Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@commercetools/frontend-sdk

Package Overview
Dependencies
Maintainers
0
Versions
76
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@commercetools/frontend-sdk - npm Package Compare versions

Comparing version 5.0.0-alpha.0 to 6.0.1-alpha.0

lib/chunk-3X2352XW.mjs

323

CHANGELOG.md
## Version 6.0.1-alpha.0 (2024-09-26)
- Updated version
## Version 6.0.0-alpha.0 (2024-09-26)
- Alpha 2.0.0 release
## Version 1.13.2 (2024-08-29)
* Changed internal locale handling to use string instead of Intl.Locale
## Version 1.13.1 (2024-06-20)
* Erronously passed undefined values are no longer serialised into the query
## Version 1.13.0 (2024-06-19)
* Added compatability for all JSON serialisable types in query
## Version 1.12.3 (2024-06-17)
* Updated query serialisation to support square bracket syntax
## Version 1.12.2 (2024-06-13)
* Fixed array serialisation into query
* Fixed jsdocs customerHeaderValue type
## Version 1.12.1 (2024-05-16)
* Fixed edge case with URL normalisation where URL in query replaces domain
## Version 1.12.0 (2024-05-14)
* Added support to add coFE-Custom-Configuration header value globally via configure, and modularly on callAction and PageAPI methods
## Version 1.11.2 (2024-05-02)
* Fixed bug in error reporting affecting versions 1.11.0 and 1.11.1
## Version 1.11.1 (2024-04-24)
* Reverted PageAPI interfaces back to types due to unintended breaking change
## Version 1.11.0 (2024-04-24)
* Added Frontastic-Request-Id to errorCaught event trigger
* Added Frontastic-Request-Id header value to SDKResponse
* Added unnecessary project files for omission on library release
* Changed PageAPI types to interfaces
## Version 1.10.1 (2024-04-09)
* Removed httpOnly flag from cookie options as default
## Version 1.10.0 (2024-04-09)
* Set cookie options httpOnly and secure to true by default
* Added the ability to skip the action queue on the SDK.callAction method
## Version 1.9.3 (2024-03-04)
* Improved error handling
## Version 1.9.2 (2024-02-28)
* Error values are explicity set to correct loss of data
## Version 1.9.1 (2024-02-06)
* Updated README.md
## Version 1.9.0 (2023-11-28)
* Removed deprecated orderHistoryFetched event
* Added ability to override cookie handling in SDK.configure
## Version 1.8.2 (2023-08-16)
* Deprecated Extension base class, replaced with Integration to correct terminolgy
## Version 1.8.1 (2023-06-29)
* Added support for 6 digit locales
## Version 1.8.0 (2023-06-22)
* Added SSR support for rememberMeCookie helpers
* Added JSdocs for intellisense
* Update remember me cookie helpers to use new SSR compliant cookie management tools
* Separated types into their own files
* Added customization option for session lifetime
* Removed unused private methods on the EventManager
## Version 1.7.7 (2023-05-31)
* Send currency across in header for API calls
* Test for missing endpoint on protocol and add if missing
## Version 1.7.6 (2023-04-26)
* fix: include headers from IncomingMessage on SSR fetch
## Version 1.7.5 (2023-04-25)
* Added ServerOption support for getPreview and getPages methods
## Version 1.7.4 (2023-04-24)
* Added ServerOptions type to index export
## Version 1.7.3 (2023-04-24)
* Updated version
## Version 1.7.2 (2023-04-24)
* Updated version
## Version 1.7.1 (2023-04-24)
* Rework of cookie handling for better SSR support
## Version 1.7.0 (2023-04-19)
* feat: added support to pass serverSession in callAction
## Version 1.6.2 (2023-04-19)
* fix: compatibility with non-v8 browsers
## Version 1.6.1 (2023-04-19)
* Replaced esbuild and tsc for tsup build
## Version 1.6.0 (2023-04-06)
* Added optional serverSession parameter to getPage to pass server session for SSR
* Added serverSession helper to access server session cookie
## Version 1.5.0 (2023-04-03)
* Replaced automatic assigning of Commercetools-Frontend-Extension-Version header in fetcher with optional extensionVersion parameter in SDKConfig
## Version 1.4.1 (2023-03-29)
* Added throwIfNotConfigured method to Page API calls
* Added optional query param to getPage
## Version 1.4.0 (2023-03-22)
* Implemented getPages method
## Version 1.3.0 (2023-03-20)
* Added getPreview page API method
* fix: removed nested isError property from FetchError
## Version 1.2.7 (2023-03-14)
* fix: only add Commercetools-Frontend-Extension-Version header if NEXT_PUBLIC_EXT_BUILD_ID provided
## Version 1.2.6 (2023-03-14)
* fix: type generation, exclude test folder from build
## Version 1.2.5 (2023-03-14)
* fix: updated node version
## Version 1.2.4 (2023-03-14)
* fix: Use Commercetools-Frontend-Extension-Version header on SDK
* feat: SDK accepts posix and bcp47 language tags/locales
## Version 1.2.2 (2023-02-28)
* fix: included @frontastic/extension-types as dependency
## Version 1.2.1 (2023-02-28)
* Added support for arrays in action queries
## Version 1.2.0 (2023-02-28)
* Added page API with getPage method
## Version 1.1.4 (2023-02-24)
* Changed BUILD_ID to EXT_BUILD_ID
## Version 1.1.3 (2023-02-22)
* fix: added NEXT_PUBLIC prefix to BUILD_ID env variable
## Version 1.1.2 (2023-02-22)
* fix: issue with previous release process
## Version 1.1.1 (2023-02-22)
* fix: updated @types/node
## Version 1.1.0 (2023-02-22)
* feat: Added access token support for multitenancy projects
* Added node to types in tsconfig
## Version 1.0.4 (2023-01-19)
* fix: error in error event trigger after reformatting
## Version 1.0.3 (2023-01-19)
* Updated perttier config and ran fix
* Removed trailingComma:all
* Added editorconfig for github to render tabs properly
## Version 1.0.1 (2023-01-18)
* Fix up prettier config to better suit the project
## Version 1.0.0 (2023-01-16)
* Full release out of alpha/beta
* Removed getPage for later release
## Version 5.0.0-alpha.0 (2023-01-06)
* Added better names for generic types, added generic type to SDK for custom extension events
* Added generic type for CustomEvents to abstract extension class
* Exported SDKResponse type from package index
* Removed redundant StandardAction type
* Added type for Events
* Refactor getPage
* (fix): bug in SDK error handling, wrapping error in {isError: false, data: error}
* Added generic type for Events
* (fix): bug in SDK error handling, wrapping error in { isError: false, data: error }

@@ -17,4 +294,2 @@ ## Version 4.0.4-alpha.0 (2023-01-04)

* Fixed typo in .npmignore

@@ -25,3 +300,2 @@

* Added .npmignore to optimise package size

@@ -32,3 +306,2 @@

* Replaced webpack build with esbuild and tsc

@@ -38,2 +311,3 @@

* fix: export SDK class type for extension

@@ -43,2 +317,3 @@

* Removed SDK class from public export

@@ -49,2 +324,3 @@ * Changed format of params of callAction and getPage mathods

Updated and extended StandardEvents type

@@ -54,2 +330,3 @@

* Updated StandardEvents type

@@ -59,2 +336,3 @@

* Added more descriptive error types, error event triggering handled by SDK

@@ -65,2 +343,3 @@ * Updated type of dynamic event return to object with unknown keys and data

* Exposed ActionError class

@@ -70,2 +349,3 @@

* Updated data type of action error event

@@ -75,2 +355,3 @@

* Changed return type of callAction to better describe and handle errors

@@ -80,2 +361,3 @@

* Added actionError StandardEvent type

@@ -85,2 +367,3 @@

* Updated callAction return type so cannot be void

@@ -93,2 +376,3 @@ * Event names modified

* Fixed major NPM deployment issue

@@ -98,2 +382,3 @@

* Updated prepublishOnly script

@@ -103,2 +388,3 @@

* Updated name of emitter class and export type

@@ -108,2 +394,3 @@

* Added error handling

@@ -113,22 +400,8 @@

* Hide event handler modifiers to prevent extensions accessing others
## Vrersion 1.0.6-alpha.0 (2022-11-30)
## Version 1.0.0-alpha.0 (2022-11-29)
* Fixed typo in README.md
## Version 1.0.5-alpha.0 (2022-11-30)
* Updated method name
## Version 1.0.4-alpha.0 (2022-11-30)
* Added README.md
## Version 1.0.3-alpha.0 (2022-11-29)
* Initial release
## Version 1.0.0-alpha.0 (2022-11-29)
* Initial release

@@ -1,5 +0,36 @@

export declare const rememberMeCookie: {
get: () => boolean;
set: (rememberMe: boolean) => void;
remove: () => void;
import { ServerOptions } from '../types/cookieHandling/ServerOptions.js';
import 'cookie';
import 'http';
/**
* An object containing helper methods for interacting with the remember me cookie.
*/
declare const rememberMeCookie: {
/**
* Gets the remember me cookie.
*
* @param {ServerOptions} [serverOptions] - An optional {@link ServerOptions} object containing the res and req objects for ServerResponse and IncomingMessage with cookies respectively. Required for server-side rendering session management.
*
* @returns {Promise<boolean>} A boolean indicating whether or not the user is to be remembered.
*/
get: (serverOptions?: ServerOptions) => Promise<boolean>;
/**
* Sets the remember me cookie.
*
* @param {boolean} rememberMe - The value in which to set the remember me cookie.
* @param {ServerOptions} [serverOptions] - An optional {@link ServerOptions} object containing the res and req objects for ServerResponse and IncomingMessage with cookies respectively. Required for server-side rendering session management.
*
* @returns {Promise<void>} Void.
*/
set: (rememberMe: boolean, serverOptions?: ServerOptions) => Promise<void>;
/**
* Removes the remember me cookie.
*
* @param {ServerOptions} [serverOptions] - An optional {@link ServerOptions} object containing the res and req objects for ServerResponse and IncomingMessage with cookies respectively. Required for server-side rendering session management.
*
* @returns {Promise<void>} Void.
*/
remove: (serverOptions?: ServerOptions) => Promise<void>;
};
export { rememberMeCookie };

@@ -1,2 +0,16 @@

import { FetchError } from "../library/FetchError";
export declare const fetcher: <T>(url: string, options?: RequestInit) => Promise<FetchError | T>;
import { ServerOptions } from '../types/cookieHandling/ServerOptions.js';
import 'cookie';
import 'http';
type FetcherResponse<T> = {
isError: false;
frontasticRequestId: string;
data: T;
} | {
isError: true;
frontasticRequestId: string;
error: string | Error;
};
declare const fetcher: <T>(url: string, options: RequestInit, serverOptions?: ServerOptions, sessionLifetime?: number) => Promise<FetcherResponse<T>>;
export { type FetcherResponse, fetcher };

42

lib/index.d.ts

@@ -1,9 +0,33 @@

import { rememberMeCookie } from "./helpers/cookieManagement";
import { Extension } from "./library/Extension";
import { SDK } from "./library/SDK";
import { Event } from "./library/Event";
import { FetchError } from "./library/FetchError";
import { ActionError } from "./library/ActionError";
import { PageError } from "./library/PageError";
import { SDKResponse } from "./library/types";
export { SDK, Extension, Event, FetchError, ActionError, PageError, SDKResponse, rememberMeCookie };
export { rememberMeCookie } from './helpers/cookieManagement.js';
export { Integration } from './library/Integration.js';
export { SDK } from './library/SDK.js';
export { Event } from './library/Event.js';
export { FetchError } from './library/FetchError.js';
export { ActionError } from './library/ActionError.js';
export { PageError } from './library/PageError.js';
export { CookieManager } from './types/cookieHandling/CookieManager.js';
export { ServerOptions } from './types/cookieHandling/ServerOptions.js';
export { SDKResponse } from './types/sdk/SDKResponse.js';
export { CookieHandler } from './library/CookieHandler.js';
import './types/events/Events.js';
import './library/EventManager.js';
import './types/events/BaseEvents.js';
import './types/Payload.js';
import './types/Query.js';
import './types/Currency.js';
import './types/sdk/SDKConfig.js';
import './types/redactionHandling/RedactionManager.js';
import './types/redactionHandling/RedactionManagerConfig.js';
import './types/redactionHandling/RedactionRule.js';
import './types/api/page/PageApi.js';
import './types/api/page/PageFolderListResponse.js';
import '@frontastic/extension-types';
import './types/api/page/PagePreviewResponse.js';
import './types/api/page/PageResponse.js';
import './types/api/page/Page.js';
import './types/api/page/PageViewData.js';
import './types/api/page/RedirectResponse.js';
import './types/cookieHandling/CookieValueTypes.js';
import './types/cookieHandling/TmpCookiesObj.js';
import 'cookie';
import 'http';

@@ -1,3 +0,1312 @@

"use strict";var w=Object.defineProperty;var P=Object.getOwnPropertyDescriptor;var R=Object.getOwnPropertyNames;var F=Object.prototype.hasOwnProperty;var S=(r,e)=>{for(var t in e)w(r,t,{get:e[t],enumerable:!0})},k=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of R(e))!F.call(r,o)&&o!==t&&w(r,o,{get:()=>e[o],enumerable:!(n=P(e,o))||n.enumerable});return r};var A=r=>k(w({},"__esModule",{value:!0}),r);var j={};S(j,{ActionError:()=>d,Event:()=>u,Extension:()=>f,FetchError:()=>a,PageError:()=>p,SDK:()=>g,rememberMeCookie:()=>m});module.exports=A(j);var x="__rememberMe",m={get:function(){return!!(typeof window<"u"&&window.localStorage.getItem(x))},set:function(r){typeof window<"u"&&(r&&window.localStorage.setItem(x,"1"),this.remove())},remove:function(){typeof window<"u"&&window.localStorage.removeItem(x)}};var f=class{sdk;constructor(e){this.sdk=e}};function h(r){for(var e=1;e<arguments.length;e++){var t=arguments[e];for(var n in t)r[n]=t[n]}return r}var H={read:function(r){return r[0]==='"'&&(r=r.slice(1,-1)),r.replace(/(%[\dA-F]{2})+/gi,decodeURIComponent)},write:function(r){return encodeURIComponent(r).replace(/%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g,decodeURIComponent)}};function C(r,e){function t(o,s,i){if(!(typeof document>"u")){i=h({},e,i),typeof i.expires=="number"&&(i.expires=new Date(Date.now()+i.expires*864e5)),i.expires&&(i.expires=i.expires.toUTCString()),o=encodeURIComponent(o).replace(/%(2[346B]|5E|60|7C)/g,decodeURIComponent).replace(/[()]/g,escape);var c="";for(var l in i)!i[l]||(c+="; "+l,i[l]!==!0&&(c+="="+i[l].split(";")[0]));return document.cookie=o+"="+r.write(s,o)+c}}function n(o){if(!(typeof document>"u"||arguments.length&&!o)){for(var s=document.cookie?document.cookie.split("; "):[],i={},c=0;c<s.length;c++){var l=s[c].split("="),I=l.slice(1).join("=");try{var y=decodeURIComponent(l[0]);if(i[y]=r.read(I,y),o===y)break}catch{}}return o?i[o]:i}}return Object.create({set:t,get:n,remove:function(o,s){t(o,"",h({},s,{expires:-1}))},withAttributes:function(o){return C(this.converter,h({},this.attributes,o))},withConverter:function(o){return C(h({},this.converter,o),this.attributes)}},{attributes:{value:Object.freeze(e)},converter:{value:Object.freeze(r)}})}var L=C(H,{path:"/"}),b=L;var a=class extends Error{constructor(e){super(),this.isError=!0,typeof e=="string"?this.message=e:Object.keys(e).forEach(t=>{this[t]=e[t]})}isError;message};var N=b.withAttributes({path:"/"}),D=async(r,e={})=>{let t=N.get("frontastic-session");e.headers={"Content-Type":"application/json",Accept:"application/json","X-Frontastic-Access-Token":"APIKEY",...e.headers||{},...t?{"Frontastic-Session":t}:{}};let n=await fetch(r,e);if(typeof window<"u"&&n.ok&&n.headers.has("Frontastic-Session")){let s=m.get(),i;s&&(i=new Date(Date.now()+1e3*60*60*24*30*3)),N.set("frontastic-session",n.headers.get("Frontastic-Session"),{expires:i})}if(n.ok)return n.json();let o;try{o=await n.clone().json()}catch{o=await n.text()}return new a(o)};var v=class{#r=[];#e=!1;#t=!1;add(e){return new Promise((t,n)=>{this.#r.push({promise:e,resolve:t,reject:n}),this.#n()})}stop(){this.#t=!0}restart(){this.#t=!1,this.#n()}#n(){if(this.#e||this.#t)return;let e=this.#r.shift();if(!!e)try{this.#e=!0,e.promise().then(t=>this.#o(()=>e.resolve(t))).catch(t=>this.#o(()=>e.reject(t)))}catch(t){this.#o(()=>e.reject(t))}}#o(e){this.#e=!1,e(),this.#n()}};var u=class{eventName;data;isDefaultPrevented;isCancelled;isPropagationStopped;constructor(e){this.eventName=e.eventName,this.data=e.data,this.isCancelled=!1,this.isDefaultPrevented=!1,this.isPropagationStopped=!1}preventDefault(){this.isDefaultPrevented=!0}cancel(){this.isCancelled=!0}stopPropagation(){this.isPropagationStopped=!0}};var E=class{eventHandlers;constructor(){this.eventHandlers={}}getEventHandlers(e){let t=this.eventHandlers[e];return t===void 0&&(t=[],this.eventHandlers[e]=t),t}on(e,t){this.getEventHandlers(e).push(t)}offForEvent(e){this.eventHandlers[e]=[]}off(e,t){let n=this.getEventHandlers(e);n.splice(n.indexOf(t),1)}clearAllHandlers(){this.eventHandlers={}}trigger(e){for(let t of this.getEventHandlers(e.eventName))t(e)}};var d=class extends a{constructor(e,t){super(t),this.actionName=e}actionName};var p=class extends a{constructor(e,t){super(t),this.path=e}path};var g=class extends E{#r;#e;#t;#n;#o;#s;set endpoint(e){this.#e=e}get endpoint(){return this.#e}set locale(e){typeof e=="string"?this.#t=new Intl.Locale(e):this.#t=e}get locale(){return this.#t}get APILocale(){let e=this.locale.baseName.slice(0,5).replace("-","_");return this.#o?`${e}@${this.currency}`:e}set currency(e){this.#n=e}get currency(){return this.#n}constructor(){super(),this.#r=!1,this.#s=new v}#c(){if(!this.#r)throw new Error(`The SDK has not been configured.
Please call .configure before you call any other methods.`)}#i=e=>e.split("//").reduce((t,n)=>n==="http:"||n==="https:"?n+="/":`${t}/${n}`,"");configure(e){this.endpoint=e.endpoint,this.locale=new Intl.Locale(e.locale),this.currency=e.currency,this.#o=e.useCurrencyInLocale??!1,this.#r=!0}#a(e){this.trigger(new u({eventName:"errorCaught",data:{error:e}}))}async callAction(e){this.#c(),e.payload=e.payload??{};let t="";e.query&&(t=Object.keys(e.query).reduce((o,s)=>e.query[s]?o+`${s}=${e.query[s]}&`:o,"?").slice(0,t.length-1));let n;try{n=await this.#s.add(()=>D(this.#i(`${this.#e}/frontastic/action/${e.actionName}${t}`),{method:"POST",body:JSON.stringify(e.payload),headers:{"Frontastic-Locale":this.APILocale}}))}catch(o){let s=new a(o);return this.#a(new d(e.actionName,s)),{isError:!0,error:s}}return{isError:!1,data:n}}async getPage(e){let t={headers:{"Frontastic-Path":e.path,"Frontastic-Locale":this.APILocale}},n;try{n=await D(this.#i(`${this.#e}/page`),t)}catch(o){let s=new a(o);return this.#a(new p(e.path,s)),{isError:!0,error:s}}return{isError:!1,data:n}}};
/*! js-cookie v3.0.1 | MIT */
"use strict";
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
ActionError: () => ActionError,
CookieHandler: () => CookieHandler,
Event: () => Event,
FetchError: () => FetchError,
Integration: () => Integration,
PageError: () => PageError,
SDK: () => SDK,
rememberMeCookie: () => rememberMeCookie
});
module.exports = __toCommonJS(src_exports);
// src/constants/defaultSessionLifetime.ts
var DEFAULT_SESSION_LIFETIME = 7776e6;
// src/constants/rememberMeCookieKey.ts
var REMEMBER_ME_COOKIE_KEY = "__rememberMe";
// src/constants/sdkNotConfiguredErrorMessage.ts
var SDK_NOT_CONFIGURED_ERROR_MESSAGE = "The SDK has not been configured. Please call .configure on the base SDK before you call any other methods";
// src/library/DependencyContainer.ts
var DependencyContainer = class {
_hasBeenConfigured;
_cookieHandler;
_redactHandler;
constructor() {
this._hasBeenConfigured = false;
}
hasBeenConfigured() {
return this._hasBeenConfigured;
}
cookieHandler() {
return this._cookieHandler;
}
redactHandler() {
return this._redactHandler;
}
configure(config) {
this._hasBeenConfigured = true;
this._cookieHandler = config.cookieHandler;
this._redactHandler = config.redactHandler;
}
throwIfDINotConfigured = () => {
if (!this._hasBeenConfigured) {
throw new Error(SDK_NOT_CONFIGURED_ERROR_MESSAGE);
}
};
};
var Wrapper = class {
dependencyContainer;
constructor() {
this.dependencyContainer = new DependencyContainer();
}
getDependencyContainer = () => this.dependencyContainer;
};
var wrapper = new Wrapper();
var dependencyContainer = wrapper.getDependencyContainer;
// src/helpers/cookieManagement.ts
var rememberMeCookie = {
/**
* Gets the remember me cookie.
*
* @param {ServerOptions} [serverOptions] - An optional {@link ServerOptions} object containing the res and req objects for ServerResponse and IncomingMessage with cookies respectively. Required for server-side rendering session management.
*
* @returns {Promise<boolean>} A boolean indicating whether or not the user is to be remembered.
*/
get: async function(serverOptions) {
dependencyContainer().throwIfDINotConfigured();
const rememberMe = await dependencyContainer().cookieHandler().getCookie(REMEMBER_ME_COOKIE_KEY, serverOptions);
return !!rememberMe;
},
/**
* Sets the remember me cookie.
*
* @param {boolean} rememberMe - The value in which to set the remember me cookie.
* @param {ServerOptions} [serverOptions] - An optional {@link ServerOptions} object containing the res and req objects for ServerResponse and IncomingMessage with cookies respectively. Required for server-side rendering session management.
*
* @returns {Promise<void>} Void.
*/
set: async function(rememberMe, serverOptions) {
dependencyContainer().throwIfDINotConfigured();
if (rememberMe) {
await dependencyContainer().cookieHandler().setCookie(REMEMBER_ME_COOKIE_KEY, "1", serverOptions);
} else {
await this.remove();
}
},
/**
* Removes the remember me cookie.
*
* @param {ServerOptions} [serverOptions] - An optional {@link ServerOptions} object containing the res and req objects for ServerResponse and IncomingMessage with cookies respectively. Required for server-side rendering session management.
*
* @returns {Promise<void>} Void.
*/
remove: async function(serverOptions) {
dependencyContainer().throwIfDINotConfigured();
await dependencyContainer().cookieHandler().deleteCookie(REMEMBER_ME_COOKIE_KEY, serverOptions);
}
};
// src/library/Integration.ts
var Integration = class {
/**
* The sdk instance passed and assigned in the constructor.
*/
sdk;
/**
* Contructor.
*
* @param {SDK} sdk - The singleton sdk instance created within your project.
*/
constructor(sdk) {
this.sdk = sdk;
}
};
// src/helpers/fetcher.ts
var fetcher = async (url, options, serverOptions, sessionLifetime) => {
dependencyContainer().throwIfDINotConfigured();
let sessionCookie = await dependencyContainer().cookieHandler().getCookie("frontastic-session", serverOptions);
sessionCookie = sessionCookie ?? "";
const incomingHeaders = serverOptions?.req ? { ...serverOptions.req.headers } : {};
delete incomingHeaders["host"];
delete incomingHeaders["cookie"];
options.headers = {
"Content-Type": "application/json",
Accept: "application/json",
"X-Frontastic-Access-Token": "APIKEY",
...options.headers || {},
...sessionCookie ? { "Frontastic-Session": sessionCookie } : {},
...incomingHeaders
};
const response = await fetch(url, options);
const frontasticRequestId = response.headers.get("Frontastic-Request-Id") ?? "";
if (response.ok && response.headers.has("Frontastic-Session")) {
let rememberMe = await rememberMeCookie.get();
let expiryDate;
if (rememberMe) {
expiryDate = new Date(
Date.now() + (sessionLifetime ?? DEFAULT_SESSION_LIFETIME)
);
}
await dependencyContainer().cookieHandler().setCookie(
"frontastic-session",
response.headers.get("Frontastic-Session"),
{ expires: expiryDate, ...serverOptions ?? {} }
);
}
let error;
if (response.ok) {
try {
let data = await response.json();
return { frontasticRequestId, data, isError: false };
} catch (err) {
error = err;
}
} else {
try {
error = await response.clone().json();
} catch (e) {
error = await response.text();
}
}
return { frontasticRequestId, error, isError: true };
};
// src/library/Queue.ts
var Queue = class {
queue = [];
promisePending = false;
stopped = false;
add(promise) {
return new Promise((resolve, reject) => {
this.queue.push({
promise,
resolve,
reject
});
this.handle();
});
}
stop() {
this.stopped = true;
}
restart() {
this.stopped = false;
this.handle();
}
handle() {
if (this.promisePending || this.stopped) {
return;
}
const item = this.queue.shift();
if (!item) {
return;
}
try {
this.promisePending = true;
item.promise().then((value) => this.resolve(() => item.resolve(value))).catch((err) => this.resolve(() => item.reject(err)));
} catch (err) {
this.resolve(() => item.reject(err));
}
}
resolve(callback) {
this.promisePending = false;
callback();
this.handle();
}
};
// src/library/Event.ts
var Event = class {
/**
* The name of the event, will match the key of the specific event.
*/
eventName;
/**
* The data associated with the event, will match the value of the specific event and be the parameter supplied to the event handler.
*/
data;
/**
* Contructor.
*
* @param {EventName} options.eventName - The name of the event being created, will match the key of the specific event.
* @param {EventData} options.data - The data associated with the event being created, will match the value of the specific event and be the parameter supplied to the event handler.
*/
constructor(options) {
this.eventName = options.eventName;
this.data = options.data;
}
};
// src/library/EventManager.ts
var EventManager = class {
eventHandlers;
constructor() {
this.eventHandlers = {};
}
getEventHandlers(eventName) {
let eventHandlers = this.eventHandlers[eventName];
if (eventHandlers === void 0) {
eventHandlers = [];
this.eventHandlers[eventName] = eventHandlers;
}
return eventHandlers;
}
/**
* Adds an event handler for a pre-defined event.
*
* @param {EventName} eventName - The name of the event, will match the key of the specific event.
* @param {(event: Event<EventName, Events[EventName]>) => void} handler - The handler function to be called when the event is triggered.
*/
on(eventName, handler) {
let eventHandlers = this.getEventHandlers(eventName);
eventHandlers.push(handler);
}
/**
* Removes an event handler for a pre-defined event.
*
* @param {EventName} eventName - The name of the event, will match the key of the specific event.
* @param {(event: Event<EventName, Events[EventName]>) => void} handler - The handler function instance to be removed.
*/
off(eventName, handler) {
let eventHandlers = this.getEventHandlers(eventName);
eventHandlers.splice(eventHandlers.indexOf(handler), 1);
}
/**
* Triggers a pre-defined event.
*
* @param {Event<EventName, Events[EventName]>} event - The event to be triggered.
*/
trigger(event) {
for (let handler of this.getEventHandlers(event.eventName)) {
handler(event);
}
}
};
// src/library/FetchError.ts
var FetchError = class extends Error {
/**
* The message associated with the error.
*/
message;
/**
* Constructor.
*
* @param {string | Error} options.error - The error message or object detected.
*/
constructor(options) {
super();
const error = options.error;
if (typeof error === "string") {
this.message = error;
} else {
Object.getOwnPropertyNames(error).forEach((key) => {
this[key] = error[key];
});
}
}
};
// src/library/ActionError.ts
var ActionError = class extends FetchError {
/**
* Constructor.
*
* @param {string | Error} options.error - The error returned from the internal fetcher.
*/
constructor(options) {
super(options);
}
};
// src/helpers/urlHelpers.ts
var import_qs = __toESM(require("qs"));
var normaliseUrl = function(url) {
let protocolSplit = url.split("//");
let normalisedUrl = "";
if (protocolSplit[0] && (protocolSplit[0] === "http:" || protocolSplit[0] === "https:" || protocolSplit[0] === "ws:" || protocolSplit[0] === "wss:")) {
normalisedUrl = `${protocolSplit[0]}//`;
protocolSplit.splice(0, 1);
}
let query = "";
let querySplit = protocolSplit.join("//").split("?");
if (querySplit.length > 1) {
query = `?${querySplit.slice(1, querySplit.length).join("?")}`;
}
const pathSplit = querySplit[0].split("/");
for (let n = 0; n < pathSplit.length; n++) {
if (pathSplit[n]) {
normalisedUrl += `${pathSplit[n]}/`;
}
}
normalisedUrl = normalisedUrl.substring(0, normalisedUrl.length - 1);
return normalisedUrl + query;
};
var toQueryObject = function(key, value) {
let obj = {};
obj[key] = value;
return obj;
};
var toQueryString = function(obj) {
return import_qs.default.stringify(obj, {
arrayFormat: "indices",
encodeValuesOnly: true,
format: "RFC3986"
});
};
var generateQueryString = function(query) {
let queryString = "?";
Object.keys(query).forEach((key) => {
let value = query[key];
if (value !== void 0 && typeof value !== "function") {
if (value === null) {
queryString += `${key}=null&`;
} else {
queryString += `${toQueryString(toQueryObject(key, value))}&`;
}
}
});
queryString = queryString.substring(0, queryString.length - 1);
return queryString;
};
var isValidUrl = function(urlLike) {
let url;
try {
url = new URL(urlLike);
} catch (_) {
return false;
}
return true;
};
// src/library/CookieHandler.ts
var import_cookie = require("cookie");
var CookieHandler = class {
isClientSide() {
return typeof window !== "undefined";
}
stringify(value = "") {
try {
const result = JSON.stringify(value);
return /^[\{\[]/.test(result) ? result : value;
} catch (e) {
return value;
}
}
decode(str) {
return str ? str.replace(/(%[0-9A-Z]{2})+/g, decodeURIComponent) : str;
}
processValue(value) {
switch (value) {
case "true":
return true;
case "false":
return false;
case "undefined":
return void 0;
case "null":
return null;
default:
return value;
}
}
async getCookies(options) {
let req;
if (options) {
req = options.req;
}
if (!this.isClientSide()) {
if (req?.cookies) {
return req.cookies;
}
if (req?.headers?.cookie) {
return (0, import_cookie.parse)(req.headers.cookie);
}
return {};
}
const _cookies = {};
const documentCookies = document.cookie ? document.cookie.split("; ") : [];
for (let i = 0, len = documentCookies.length; i < len; i++) {
const cookieParts = documentCookies[i].split("=");
const _cookie = cookieParts.slice(1).join("=");
const name = cookieParts[0];
_cookies[name] = _cookie;
}
return _cookies;
}
async hasCookie(key, options) {
if (!key) {
return false;
}
const cookie = await this.getCookies(options);
return cookie.hasOwnProperty(key);
}
async setCookie(key, data, options) {
let _cookieOptions = {
secure: true
};
let _req;
let _res;
if (options) {
const { req, res, ..._options } = options;
_req = req;
_res = res;
_cookieOptions = Object.assign({}, _options, _cookieOptions);
}
const cookieStr = (0, import_cookie.serialize)(key, this.stringify(data), {
path: "/",
..._cookieOptions
});
if (!this.isClientSide()) {
if (_res && _req) {
let currentCookies = _res.getHeader("Set-Cookie");
if (!Array.isArray(currentCookies)) {
currentCookies = !currentCookies ? [] : [String(currentCookies)];
}
_res.setHeader("Set-Cookie", currentCookies.concat(cookieStr));
if (_req && _req.cookies) {
const _cookies = _req.cookies;
data === "" ? delete _cookies[key] : _cookies[key] = this.stringify(data);
}
if (_req && _req.headers && _req.headers.cookie) {
const _cookies = (0, import_cookie.parse)(_req.headers.cookie);
data === "" ? delete _cookies[key] : _cookies[key] = this.stringify(data);
_req.headers.cookie = Object.entries(_cookies).reduce(
(accum, item) => {
return accum.concat(`${item[0]}=${item[1]};`);
},
""
);
}
}
} else {
document.cookie = cookieStr;
}
}
async getCookie(key, options) {
const _cookies = await this.getCookies(options);
const value = _cookies[key];
if (value === void 0) {
return void 0;
}
return this.processValue(this.decode(value));
}
async deleteCookie(key, options) {
this.setCookie(key, "", { ...options, maxAge: -1 });
}
};
// src/library/Guid.ts
var Guid = class {
static NewGuid = (noHyphens) => {
var d = (/* @__PURE__ */ new Date()).getTime();
if (typeof performance !== "undefined" && typeof performance.now === "function") {
d += performance.now();
}
var shell = noHyphens ? "xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx" : "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx";
return shell.replace(/[xy]/g, function(c) {
var r = (d + Math.random() * 16) % 16 | 0;
d = Math.floor(d / 16);
return (c === "x" ? r : r & (3 | 8)).toString(16);
});
};
static Empty = "00000000-0000-0000-0000-000000000000";
static IsGuid = (guid) => /^([0-9a-fA-F]{8})-(([0-9a-fA-F]{4}-){3})([0-9a-fA-F]{12})$/i.test(guid);
};
// src/constants/defaultRedactionRules.ts
var defaultJsonRedactionText = "[REDACTED]";
var defaultUrlRedactionText = "REDACTED";
var defaultRedactionRules = {
includes: [{ value: "password" }],
properties: [
{ value: "token" },
{ value: "accessToken" },
{ value: "apiToken" },
{ value: "previewToken" },
{ value: "apiKey" },
{ value: "apiSecret" },
{ value: "clientId" },
{ value: "clientSecret" },
{ value: "secret" },
{ value: "metaData" }
],
jsonRedactionText: defaultJsonRedactionText,
urlRedactionText: defaultUrlRedactionText
};
// src/library/RedactionHandler.ts
var RedactionHandler = class {
paths;
properties;
whitelistPaths;
includes;
jsonRedactionText;
urlRedactionText;
constructor(config) {
this.paths = config.paths ?? [];
this.properties = config.properties ?? [];
this.whitelistPaths = config.whitelistPaths ?? [];
this.includes = config.includes ?? [];
this.jsonRedactionText = config.jsonRedactionText ?? defaultJsonRedactionText;
this.urlRedactionText = config.urlRedactionText ?? defaultUrlRedactionText;
}
redact(data, currentPath) {
if (typeof data === "string" || typeof data === "number" || typeof data === "boolean") {
if (!currentPath) {
console.warn(
"Value passed to redact is a basic type, only objects can be redacted."
);
return data;
} else {
if (this.shouldRedact(currentPath)) {
return this.jsonRedactionText;
}
if (typeof data === "string" && isValidUrl(data)) {
return this.redactUrl(data);
}
return data;
}
} else if (typeof data === "object") {
if (Array.isArray(data)) {
return data.map(
(datum) => this.redact(datum, currentPath)
);
} else if (data) {
Object.keys(data).forEach((key) => {
data[key] = this.redact(
data[key],
currentPath ? `${currentPath}.${key}` : key
);
});
}
}
return data;
}
redactUrl(inputUrl) {
const url = new URL(inputUrl);
const urlParams = new URLSearchParams(url.search);
for (let [key] of urlParams.entries()) {
if (this.shouldRedact(this.urlQueryKeyToObjectPath(key))) {
urlParams.set(key, this.urlRedactionText);
}
}
url.search = urlParams.toString();
return url.toString();
}
// assumes keys of string only
urlQueryKeyToObjectPath(urlQueryKey) {
let urlQueryKeyOpenBracketSplit = urlQueryKey.split("[");
if (urlQueryKeyOpenBracketSplit.length === 1) {
return urlQueryKey;
} else {
let objectPath = urlQueryKeyOpenBracketSplit.splice(
0,
1
)[0];
urlQueryKeyOpenBracketSplit.forEach((openBracketSplitValue, n) => {
const closeBracketSplit = openBracketSplitValue.split("]");
if (closeBracketSplit.length !== 2) {
console.warn(
`Object in URL params in incorrect format: [${urlQueryKeyOpenBracketSplit[n]}`
);
return;
} else {
if (closeBracketSplit[0].match(/^-?\d+$/)) {
return;
} else {
objectPath += `.${closeBracketSplit[0]}`;
}
}
});
return objectPath;
}
}
shouldRedact(path) {
let ruleIsSatisfied = this.testRedactionRules(
path,
this.whitelistPaths
);
if (ruleIsSatisfied) {
return false;
}
ruleIsSatisfied = this.testRedactionRules(path, this.paths);
if (ruleIsSatisfied) {
return true;
}
const properties = path.split(".");
const propertyName = properties[properties.length - 1];
ruleIsSatisfied = this.testRedactionRules(
propertyName,
this.properties
);
if (ruleIsSatisfied) {
return true;
}
for (let n = 0; n < this.includes.length; n++) {
if (propertyName.includes(this.includes[n].value) || !this.includes[n].caseSensitive && propertyName.toLowerCase().includes(this.includes[n].value.toLowerCase())) {
n = this.includes.length;
ruleIsSatisfied = true;
}
}
return ruleIsSatisfied;
}
testRedactionRules(path, rules) {
let ruleIsSatisfied = false;
for (let n = 0; n < rules.length; n++) {
if (path === rules[n].value || !rules[n].caseSensitive && path.toLowerCase() === rules[n].value.toLowerCase()) {
n = rules.length;
ruleIsSatisfied = true;
}
}
return ruleIsSatisfied;
}
};
// src/library/SDK.ts
var SDK = class extends EventManager {
_hasBeenConfigured;
_actionQueue;
_endpoint;
_locale;
_currency;
_extensionVersion;
_useCurrencyInLocale;
_sessionLifetime;
_customHeaderValue;
setEndpoint(url) {
url = normaliseUrl(url);
if (url.indexOf("http") === -1) {
url = `https://${url}`;
console.warn(
`Protocol not supplied to endpoint, defaulting to https - ${url}`
);
}
this._endpoint = url.split("/frontastic")[0];
}
/**
* A function returning the full url endpoint to be called, to be set within the {@link configure} method.
*/
endpoint() {
return this._endpoint;
}
/**
* A function returning the [BCP 47 language tag](http://tools.ietf.org/html/rfc5646) locale, to be set within the {@link configure} method.
*
* [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#locales_argument).
*/
locale() {
return this._locale;
}
apiHubLocale() {
const apiFormattedLocale = this._locale.replace("-", "_");
if (this._useCurrencyInLocale) {
return `${apiFormattedLocale}@${this._currency}`;
} else {
return apiFormattedLocale;
}
}
/**
* A function returning the string representing the ISO 4217 3-Letter Currency Code, to be set within the {@link configure} method.
*/
currency() {
return this._currency;
}
/**
* A function returning the string optionally set within the {@link configure} method, the value to assign to a "coFE-Custom-Configuration" header value in every API call. Overriden on single calls by explicity set customHeaderValue passed in {@link callAction} and {@link PageApi} methods.
*/
customHeaderValue() {
return this._customHeaderValue;
}
constructor() {
super();
this._hasBeenConfigured = false;
this._actionQueue = new Queue();
}
throwIfNotConfigured() {
if (!this._hasBeenConfigured) {
throw new Error(SDK_NOT_CONFIGURED_ERROR_MESSAGE);
}
}
isRedactionManager(config) {
return Boolean(
config?.redact && config?.redactUrl
);
}
/**
* The method that must be called prior to any other methods to configure the connection to the backend. An error is throw if not called prior.
*
* @param {string} config.locale - A string representing the combination of the ISO 639-1 language and ISO 3166-1 country code. For example "en-DE" or "en_DE".
* @param {string} config.currency - A string representing the ISO 4217 3-Letter Currency Code, for example EUR.
* @param {string} config.endpoint - A string representing the full URL of the endpoint to be called.
* @param {string} config.extensionVersion - A string representing the next public extension build ID, to specify the extension version in which to connect.
* @param {boolean} [config.useCurrencyInLocale=false] - An optional boolean, default false. To be set to true if currency is required in config.locale, for example en-GB@EUR.
* @param {string} [config.sessionLifetime=7776000000] - An optional number of milliseconds in which to persist the session lifeTime, to override the {@link DEFAULT_SESSION_LIFETIME} of 3 months.
* @param {boolean} [options.customHeaderValue] - An optional string, the value to assign to a "coFE-Custom-Configuration" header value in every API call. Overriden on single calls by explicity set customHeaderValue passed in {@link callAction} and {@link PageApi} methods.
* @param {CookieManager} [config.cookieHandlingOverride] - An optional cookie manager interface that contains all the cookie handling methods.
* @param {RedactionManager | RedactionManagerConfig} [config.redactionHandlingOverride] - An optional class/object implementing the {@link RedactionManager} interface or {@link RedactionManagerConfig} to replace the default {@link defaultRedactionRules} passed to the inbuilt {@link RedactionHandler}.
*
* @returns {void} Void.
*/
configure(config) {
dependencyContainer().configure({
cookieHandler: config.cookieHandlingOverride ?? new CookieHandler(),
redactHandler: this.isRedactionManager(
config.redactionHandlingOverride
) ? config.redactionHandlingOverride : new RedactionHandler(
config.redactionHandlingOverride ?? defaultRedactionRules
)
});
this.setEndpoint(config.endpoint);
this.configureLocale(config);
this._useCurrencyInLocale = config.useCurrencyInLocale ?? false;
this._extensionVersion = config.extensionVersion;
this._sessionLifetime = config.sessionLifetime ?? DEFAULT_SESSION_LIFETIME;
this._customHeaderValue = config.customHeaderValue;
this._hasBeenConfigured = true;
}
/**
* The method called to standardise the locale and currency inputs.
*
* @param {string} config.locale - A string representing the combination of the ISO 639-1 language and ISO 3166-1 country code. For example en-GB or en_GB.
* @param {string} config.currency - A string representing the ISO 3-Letter Currency Code, for example EUR.
*
* @returns {void} Void.
*/
configureLocale(config) {
const [locale, currency] = config.locale.split("@");
if (currency) {
this._currency = currency;
}
if (config.currency) {
this._currency = config.currency;
}
this._locale = locale.replace(/_/g, "-");
}
handleApiCall(options) {
[
options.type === "pageAPI" ? "pageApiMethodCalled" : "actionCalled",
"fetchCalled"
].forEach((eventName) => {
const type = eventName === "fetchCalled" ? { type: options.type } : {};
this.trigger(
// @ts-ignore
new Event({
eventName,
data: Object.assign(
options.type === "pageAPI" ? { method: options.method } : { actionName: options.actionName },
{
...type,
parameters: dependencyContainer().redactHandler().redact(options.parameters),
url: dependencyContainer().redactHandler().redactUrl(options.url),
tracing: options.tracing
}
)
})
);
});
}
handleSuccesfulCall(options) {
[
options.type === "pageAPI" ? "pageApiFetchSuccessful" : "actionFetchSuccessful",
"fetchSuccessful"
].forEach((eventName) => {
const type = eventName === "fetchSuccessful" ? { type: options.type } : {};
this.trigger(
// @ts-ignore
new Event({
eventName,
data: Object.assign(
options.type === "pageAPI" ? { method: options.method } : { actionName: options.actionName },
{
...type,
parameters: dependencyContainer().redactHandler().redact(options.parameters),
url: dependencyContainer().redactHandler().redactUrl(options.url),
dataResponse: dependencyContainer().redactHandler().redact(options.dataResponse),
tracing: options.tracing
}
)
})
);
});
}
handleError(options) {
[
options.type === "action" ? "actionErrorCaught" : "pageApiErrorCaught",
"errorCaught"
].forEach((eventName) => {
const type = eventName === "errorCaught" ? { type: options.type } : {};
this.trigger(
// @ts-ignore
new Event({
eventName,
data: Object.assign(
options.type === "action" ? { actionName: options.actionName } : { method: options.method },
{
...type,
parameters: dependencyContainer().redactHandler().redact(options.parameters),
url: dependencyContainer().redactHandler().redactUrl(options.url),
tracing: options.tracing,
error: options.error
}
)
})
);
});
return {
isError: true,
tracing: options.tracing,
error: options.error
};
}
getDefaultAPIHeaders(customHeaderValue) {
const customValue = customHeaderValue ?? this._customHeaderValue;
const customHeader = customValue ? { "coFE-Custom-Configuration": customValue } : {};
return {
"Frontastic-Locale": this.apiHubLocale(),
"Frontastic-Currency": this._currency,
"Commercetools-Frontend-Extension-Version": this._extensionVersion,
...customHeader
};
}
/**
* The method used to call extension actions.
*
* @param {string} options.actionName - The name of the action corresponding to the location of the extension, for example "product/getProduct".
* @param {unknown} [options.payload] - An optional key, value pair object payload to be serialised into the body of the request.
* @param {Object.<string, number, boolean, string[], number[], boolean[]>} [options.query] - An optional key, value pair object to be serialised into the url query.
* @param {boolean} [options.queueAction] - An optional boolean, default false indicating whether the action should be added to a queue and executed syncronously. Useful on actions you may think have race conditions.
* @param {string} [options.customHeaderValue] - An optional string, the value to assign to a "coFE-Custom-Configuration" header value. Overrides customHeaderValue passed in {@link configure}.
* @param {Object} [options.serverOptions] - An optional object containing the res and req objects for ServerResponse and IncomingMessage with cookies respectively. Required for server-side rendering session management.
*
* @returns {PromiseLike<Object>} An object with a boolean isError property, and either an error or data property for true and false respectively. Type of data will match generic argument supplied to method.
*/
async callAction(options) {
this.throwIfNotConfigured();
const params = options.query ? generateQueryString(options.query) : "";
const fetcherOptions = {
method: "POST",
body: JSON.stringify(options.payload ?? {}),
headers: this.getDefaultAPIHeaders(options.customHeaderValue)
};
let response;
const url = normaliseUrl(
`${this._endpoint}/frontastic/action/${options.actionName}${params}`
);
const action = () => fetcher(
url,
fetcherOptions,
options.serverOptions,
this._sessionLifetime
);
const frontendRequestId = Guid.NewGuid();
this.handleApiCall({
type: "action",
actionName: options.actionName,
parameters: {
query: options.query,
body: options.payload
},
url,
tracing: {
frontendRequestId
}
});
try {
if (options.queueAction) {
response = await this._actionQueue.add(action);
} else {
response = await action();
}
} catch (error) {
return this.handleError({
type: "action",
parameters: {
query: options.query,
body: options.payload
},
url,
actionName: options.actionName,
tracing: {
frontendRequestId,
frontasticRequestId: ""
},
error: new ActionError({
error
})
});
}
if (response.isError) {
return this.handleError({
type: "action",
parameters: {
query: options.query,
body: options.payload
},
url,
actionName: options.actionName,
tracing: {
frontendRequestId,
frontasticRequestId: response.frontasticRequestId
},
error: new ActionError({
error: response.error
})
});
}
this.handleSuccesfulCall({
type: "action",
actionName: options.actionName,
parameters: {
query: options.query,
body: options.payload
},
url,
dataResponse: response.data,
tracing: {
frontendRequestId,
frontasticRequestId: response.frontasticRequestId
}
});
return {
isError: false,
tracing: {
frontendRequestId,
frontasticRequestId: response.frontasticRequestId
},
data: response.data
};
}
/**
* The domain to call page methods on the API hub.
*/
page = {
getPage: async (options) => {
this.throwIfNotConfigured();
const params = options.query ? generateQueryString(options.query) : "";
const fetcherOptions = {
method: "POST",
headers: {
"Frontastic-Path": options.path,
...this.getDefaultAPIHeaders(options.customHeaderValue)
}
};
let response;
const url = normaliseUrl(
`${this._endpoint}/frontastic/page${params}`
);
const frontendRequestId = Guid.NewGuid();
this.handleApiCall({
type: "pageAPI",
method: "getPage",
parameters: {
query: options.query
},
url,
tracing: {
frontendRequestId
}
});
try {
response = await fetcher(
url,
fetcherOptions,
options.serverOptions,
this._sessionLifetime
);
} catch (error) {
return this.handleError({
type: "pageAPI",
parameters: {
query: options.query
},
url,
method: "getPage",
tracing: {
frontendRequestId,
frontasticRequestId: ""
},
error: new ActionError({
error
})
});
}
if (response.isError) {
return this.handleError({
type: "pageAPI",
parameters: {
query: options.query
},
url,
method: "getPage",
tracing: {
frontendRequestId,
frontasticRequestId: response.frontasticRequestId
},
error: new ActionError({
error: response.error
})
});
}
this.handleSuccesfulCall({
type: "pageAPI",
method: "getPage",
parameters: {
query: options.query
},
url,
dataResponse: response.data,
tracing: {
frontendRequestId,
frontasticRequestId: response.frontasticRequestId
}
});
return {
isError: false,
tracing: {
frontendRequestId,
frontasticRequestId: response.frontasticRequestId
},
data: response.data
};
},
getPreview: async (options) => {
this.throwIfNotConfigured();
const fetcherOptions = {
method: "POST",
headers: this.getDefaultAPIHeaders(options.customHeaderValue)
};
let response;
const query = {
previewId: options.previewId,
locale: this.apiHubLocale()
};
const url = normaliseUrl(
`${this._endpoint}/frontastic/preview${generateQueryString(
query
)}`
);
const frontendRequestId = Guid.NewGuid();
this.handleApiCall({
type: "pageAPI",
method: "getPreview",
parameters: {
query
},
url,
tracing: {
frontendRequestId
}
});
try {
response = await fetcher(
url,
fetcherOptions,
options.serverOptions,
this._sessionLifetime
);
} catch (error) {
return this.handleError({
type: "pageAPI",
parameters: {
query
},
url,
method: "getPreview",
tracing: {
frontendRequestId,
frontasticRequestId: ""
},
error: new ActionError({
error
})
});
}
if (response.isError) {
return this.handleError({
type: "pageAPI",
parameters: {
query
},
url,
method: "getPreview",
tracing: {
frontendRequestId,
frontasticRequestId: response.frontasticRequestId
},
error: new ActionError({
error: response.error
})
});
}
this.handleSuccesfulCall({
type: "pageAPI",
method: "getPreview",
parameters: {
query
},
url,
dataResponse: response.data,
tracing: {
frontendRequestId,
frontasticRequestId: response.frontasticRequestId
}
});
return {
isError: false,
tracing: {
frontendRequestId,
frontasticRequestId: response.frontasticRequestId
},
data: response.data
};
},
getPages: async (options = {
depth: 16,
types: "static"
}) => {
this.throwIfNotConfigured();
options.depth = options.depth ?? 16;
options.types = options.types ?? "static";
const fetcherOptions = {
method: "POST",
headers: this.getDefaultAPIHeaders(options.customHeaderValue)
};
let response;
const query = {
locale: this.apiHubLocale(),
path: options.path ?? "",
depth: options.depth
};
const url = normaliseUrl(
`${this._endpoint}/frontastic/structure${generateQueryString(
query
)}`
);
const frontendRequestId = Guid.NewGuid();
this.handleApiCall({
type: "pageAPI",
method: "getPages",
parameters: {
query
},
url,
tracing: {
frontendRequestId
}
});
try {
response = await fetcher(
url,
fetcherOptions,
options.serverOptions,
this._sessionLifetime
);
} catch (error) {
return this.handleError({
type: "pageAPI",
parameters: {
query
},
url,
method: "getPages",
tracing: {
frontendRequestId,
frontasticRequestId: ""
},
error: new ActionError({
error
})
});
}
if (response.isError) {
return this.handleError({
type: "pageAPI",
parameters: {
query
},
url,
method: "getPages",
tracing: {
frontendRequestId,
frontasticRequestId: response.frontasticRequestId
},
error: new ActionError({
error: response.error
})
});
}
this.handleSuccesfulCall({
type: "pageAPI",
method: "getPages",
parameters: {
query
},
url,
dataResponse: response.data,
tracing: {
frontendRequestId,
frontasticRequestId: response.frontasticRequestId
}
});
return {
isError: false,
tracing: {
frontendRequestId,
frontasticRequestId: response.frontasticRequestId
},
data: response.data
};
}
};
};
// src/library/PageError.ts
var PageError = class extends FetchError {
/**
* Constructor.
*
* @param {string | Error} options.error - The error returned from the internal fetcher.
*/
constructor(options) {
super(options);
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
ActionError,
CookieHandler,
Event,
FetchError,
Integration,
PageError,
SDK,
rememberMeCookie
});
//# sourceMappingURL=index.js.map

@@ -1,5 +0,17 @@

import { FetchError } from "./FetchError";
export declare class ActionError extends FetchError {
constructor(actionName: string, error: FetchError);
actionName: string;
import { FetchError } from './FetchError.js';
/**
* An error created when the action API fails, extends the {@link FetchError}.
*/
declare class ActionError extends FetchError {
/**
* Constructor.
*
* @param {string | Error} options.error - The error returned from the internal fetcher.
*/
constructor(options: {
error: string | Error;
});
}
export { ActionError };

@@ -1,7 +0,22 @@

export declare class Event<EventName extends string = string, EventData extends any = void> {
/**
* The class represeting the Event type to be passed to the EventManager's trigger function.
*
* @param {EventName} EventName - The name of the event being created, will match the key of the specific event.
* @param {EventData} EventData - The data associated with the event being created, will match the value of the specific event and be the parameter supplied to the event handler.
*/
declare class Event<EventName extends string = string, EventData extends any = void> {
/**
* The name of the event, will match the key of the specific event.
*/
eventName: EventName;
/**
* The data associated with the event, will match the value of the specific event and be the parameter supplied to the event handler.
*/
data: EventData;
isDefaultPrevented: boolean;
isCancelled: boolean;
isPropagationStopped: boolean;
/**
* Contructor.
*
* @param {EventName} options.eventName - The name of the event being created, will match the key of the specific event.
* @param {EventData} options.data - The data associated with the event being created, will match the value of the specific event and be the parameter supplied to the event handler.
*/
constructor(options: {

@@ -11,5 +26,4 @@ eventName: EventName;

});
preventDefault(): void;
cancel(): void;
stopPropagation(): void;
}
export { Event };

@@ -1,11 +0,29 @@

import { Event } from "./Event";
export declare class EventManager<Events> {
import { Event } from './Event.js';
declare class EventManager<Events> {
protected eventHandlers: Record<string, Array<(event: Event<keyof Events, Events[keyof Events]>) => void>>;
constructor();
protected getEventHandlers<EventName extends keyof Events>(eventName: EventName): Array<(event: Event<EventName, Events[EventName]>) => void>;
on<EventName extends keyof Events>(eventName: EventName, handler: (event: Event<EventName, Events[EventName]>) => void): void;
private offForEvent;
off<EventName extends keyof Events>(eventName: EventName, handler: (event: Event<EventName, Events[EventName]>) => void): void;
private clearAllHandlers;
trigger<EventName extends keyof Events>(event: Event<EventName, Events[EventName]>): void;
protected getEventHandlers<EventName extends keyof Events>(eventName: EventName & string): Array<(event: Event<EventName & string, Events[EventName]>) => void>;
/**
* Adds an event handler for a pre-defined event.
*
* @param {EventName} eventName - The name of the event, will match the key of the specific event.
* @param {(event: Event<EventName, Events[EventName]>) => void} handler - The handler function to be called when the event is triggered.
*/
on<EventName extends keyof Events>(eventName: EventName & string, handler: (event: Event<EventName & string, Events[EventName]>) => void): void;
/**
* Removes an event handler for a pre-defined event.
*
* @param {EventName} eventName - The name of the event, will match the key of the specific event.
* @param {(event: Event<EventName, Events[EventName]>) => void} handler - The handler function instance to be removed.
*/
off<EventName extends keyof Events>(eventName: EventName & string, handler: (event: Event<EventName & string, Events[EventName]>) => void): void;
/**
* Triggers a pre-defined event.
*
* @param {Event<EventName, Events[EventName]>} event - The event to be triggered.
*/
trigger<EventName extends keyof Events>(event: Event<EventName & string, Events[EventName]>): void;
}
export { EventManager };

@@ -1,6 +0,23 @@

export declare class FetchError extends Error {
constructor(error: string | Error);
/**
* An error thrown when the internal fetcher fails.
*/
declare class FetchError extends Error {
/**
* Covers any additional properties that may be added to an error.
*/
[key: string]: any;
isError: boolean;
/**
* The message associated with the error.
*/
message: string;
/**
* Constructor.
*
* @param {string | Error} options.error - The error message or object detected.
*/
constructor(options: {
error: string | Error;
});
}
export { FetchError };

@@ -1,5 +0,17 @@

import { FetchError } from "./FetchError";
export declare class PageError extends FetchError {
constructor(path: string, error: FetchError);
path: string;
import { FetchError } from './FetchError.js';
/**
* An error created when a method in the page API fails, extends the {@link FetchError}.
*/
declare class PageError extends FetchError {
/**
* Constructor.
*
* @param {string | Error} options.error - The error returned from the internal fetcher.
*/
constructor(options: {
error: string | Error;
});
}
export { PageError };

@@ -1,6 +0,17 @@

export declare class Queue {
#private;
add<T>(promise: () => Promise<T>): Promise<T>;
import { FetcherResponse } from '../helpers/fetcher.js';
import '../types/cookieHandling/ServerOptions.js';
import 'cookie';
import 'http';
declare class Queue {
private queue;
private promisePending;
private stopped;
add<T>(promise: () => Promise<FetcherResponse<T>>): Promise<FetcherResponse<T>>;
stop(): void;
restart(): void;
private handle;
private resolve;
}
export { Queue };

@@ -1,29 +0,119 @@

import { EventManager } from "./EventManager";
import { SDKResponse, Currency, StandardEvents, Events } from "./types";
export declare class SDK<ExtensionEvents extends Events> extends EventManager<StandardEvents & ExtensionEvents> {
#private;
set endpoint(url: string);
get endpoint(): string;
set locale(locale: string | Intl.Locale);
get locale(): Intl.Locale;
get APILocale(): string;
set currency(currency: Currency);
get currency(): Currency;
import { EventManager } from './EventManager.js';
import { BaseEvents } from '../types/events/BaseEvents.js';
import { Events } from '../types/events/Events.js';
import { Currency } from '../types/Currency.js';
import { AcceptedPayloadTypes } from '../types/Payload.js';
import { AcceptedQueryTypes } from '../types/Query.js';
import { SDKConfig } from '../types/sdk/SDKConfig.js';
import { SDKResponse } from '../types/sdk/SDKResponse.js';
import { PageApi } from '../types/api/page/PageApi.js';
import { ServerOptions } from '../types/cookieHandling/ServerOptions.js';
import './Event.js';
import './ActionError.js';
import './FetchError.js';
import './PageError.js';
import '../types/cookieHandling/CookieManager.js';
import '../types/cookieHandling/CookieValueTypes.js';
import '../types/cookieHandling/TmpCookiesObj.js';
import '../types/redactionHandling/RedactionManager.js';
import '../types/redactionHandling/RedactionManagerConfig.js';
import '../types/redactionHandling/RedactionRule.js';
import '../types/api/page/PageFolderListResponse.js';
import '@frontastic/extension-types';
import '../types/api/page/PagePreviewResponse.js';
import '../types/api/page/PageResponse.js';
import '../types/api/page/Page.js';
import '../types/api/page/PageViewData.js';
import '../types/api/page/RedirectResponse.js';
import 'cookie';
import 'http';
declare class SDK<ExtensionEvents extends Events> extends EventManager<BaseEvents & ExtensionEvents> {
private _hasBeenConfigured;
private _actionQueue;
private _endpoint;
private _locale;
private _currency;
private _extensionVersion;
private _useCurrencyInLocale;
private _sessionLifetime;
private _customHeaderValue?;
private setEndpoint;
/**
* A function returning the full url endpoint to be called, to be set within the {@link configure} method.
*/
endpoint(): string;
/**
* A function returning the [BCP 47 language tag](http://tools.ietf.org/html/rfc5646) locale, to be set within the {@link configure} method.
*
* [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl#locales_argument).
*/
locale(): string;
private apiHubLocale;
/**
* A function returning the string representing the ISO 4217 3-Letter Currency Code, to be set within the {@link configure} method.
*/
currency(): Currency;
/**
* A function returning the string optionally set within the {@link configure} method, the value to assign to a "coFE-Custom-Configuration" header value in every API call. Overriden on single calls by explicity set customHeaderValue passed in {@link callAction} and {@link PageApi} methods.
*/
customHeaderValue(): string | undefined;
constructor();
configure(config: {
locale: Intl.BCP47LanguageTag;
currency: Currency;
endpoint: string;
useCurrencyInLocale?: boolean;
}): void;
private throwIfNotConfigured;
private isRedactionManager;
/**
* The method that must be called prior to any other methods to configure the connection to the backend. An error is throw if not called prior.
*
* @param {string} config.locale - A string representing the combination of the ISO 639-1 language and ISO 3166-1 country code. For example "en-DE" or "en_DE".
* @param {string} config.currency - A string representing the ISO 4217 3-Letter Currency Code, for example EUR.
* @param {string} config.endpoint - A string representing the full URL of the endpoint to be called.
* @param {string} config.extensionVersion - A string representing the next public extension build ID, to specify the extension version in which to connect.
* @param {boolean} [config.useCurrencyInLocale=false] - An optional boolean, default false. To be set to true if currency is required in config.locale, for example en-GB@EUR.
* @param {string} [config.sessionLifetime=7776000000] - An optional number of milliseconds in which to persist the session lifeTime, to override the {@link DEFAULT_SESSION_LIFETIME} of 3 months.
* @param {boolean} [options.customHeaderValue] - An optional string, the value to assign to a "coFE-Custom-Configuration" header value in every API call. Overriden on single calls by explicity set customHeaderValue passed in {@link callAction} and {@link PageApi} methods.
* @param {CookieManager} [config.cookieHandlingOverride] - An optional cookie manager interface that contains all the cookie handling methods.
* @param {RedactionManager | RedactionManagerConfig} [config.redactionHandlingOverride] - An optional class/object implementing the {@link RedactionManager} interface or {@link RedactionManagerConfig} to replace the default {@link defaultRedactionRules} passed to the inbuilt {@link RedactionHandler}.
*
* @returns {void} Void.
*/
configure(config: SDKConfig): void;
/**
* The method called to standardise the locale and currency inputs.
*
* @param {string} config.locale - A string representing the combination of the ISO 639-1 language and ISO 3166-1 country code. For example en-GB or en_GB.
* @param {string} config.currency - A string representing the ISO 3-Letter Currency Code, for example EUR.
*
* @returns {void} Void.
*/
configureLocale(config: Pick<SDKConfig, "locale" | "currency">): void;
private handleApiCall;
private handleSuccesfulCall;
private handleError;
private getDefaultAPIHeaders;
/**
* The method used to call extension actions.
*
* @param {string} options.actionName - The name of the action corresponding to the location of the extension, for example "product/getProduct".
* @param {unknown} [options.payload] - An optional key, value pair object payload to be serialised into the body of the request.
* @param {Object.<string, number, boolean, string[], number[], boolean[]>} [options.query] - An optional key, value pair object to be serialised into the url query.
* @param {boolean} [options.queueAction] - An optional boolean, default false indicating whether the action should be added to a queue and executed syncronously. Useful on actions you may think have race conditions.
* @param {string} [options.customHeaderValue] - An optional string, the value to assign to a "coFE-Custom-Configuration" header value. Overrides customHeaderValue passed in {@link configure}.
* @param {Object} [options.serverOptions] - An optional object containing the res and req objects for ServerResponse and IncomingMessage with cookies respectively. Required for server-side rendering session management.
*
* @returns {PromiseLike<Object>} An object with a boolean isError property, and either an error or data property for true and false respectively. Type of data will match generic argument supplied to method.
*/
callAction<ReturnData>(options: {
actionName: string;
payload?: unknown;
query?: {
[key: string]: string | number | boolean;
};
payload?: AcceptedPayloadTypes;
query?: AcceptedQueryTypes;
queueAction?: boolean;
customHeaderValue?: string;
serverOptions?: ServerOptions;
}): Promise<SDKResponse<ReturnData>>;
getPage<ReturnData>(options: {
path: string;
}): Promise<SDKResponse<ReturnData>>;
/**
* The domain to call page methods on the API hub.
*/
page: PageApi;
}
export { SDK };
{
"name": "@commercetools/frontend-sdk",
"version": "5.0.0-alpha.0",
"license": "UNLICENSED",
"main": "lib/index.js",
"scripts": {
"dev": "concurrently -n ESBUILD,TSC -c magenta,cyan \"esbuild --outdir=lib --bundle --format=cjs --watch src/index.ts\" \"yarn dev:types --watch\"",
"build": "esbuild --outdir=lib --bundle --format=cjs src/index.ts && yarn build:types",
"release": "esbuild --outdir=lib --bundle --format=cjs --minify src/index.ts && yarn release:types",
"prepublishOnly": "yarn install && rm -rf lib && yarn release",
"dev:types": "tsc --emitDeclarationOnly --outDir lib",
"build:types": "tsc --emitDeclarationOnly --outDir lib",
"release:types": "tsc --emitDeclarationOnly --outDir lib"
},
"dependencies": {
"js-cookie": "3.0.1"
},
"devDependencies": {
"@types/core-js": "2.5.5",
"@types/js-cookie": "3.0.2",
"@types/node": "18.7.18",
"concurrently": "7.5.0",
"esbuild": "0.15.8",
"prettier": "2.7.1",
"typescript": "^4.9.3"
}
"name": "@commercetools/frontend-sdk",
"version": "6.0.1-alpha.0",
"license": "UNLICENSED",
"main": "lib/index.js",
"scripts": {
"dev": "tsup --env.NODE_ENV dev",
"build": "tsup",
"release": "tsup",
"prepublishOnly": "rm -rf lib && yarn install && yarn release",
"prettier:fix": "prettier --write .",
"test": "vitest"
},
"dependencies": {
"@frontastic/extension-types": "^0.0.11",
"cookie": "^0.5.0",
"qs": "^6.12.1"
},
"devDependencies": {
"@types/cookie": "^0.5.1",
"@types/core-js": "2.5.5",
"@types/node": "^18.14.0",
"@types/qs": "^6.9.15",
"cross-fetch": "^3.1.6",
"prettier": "2.7.1",
"tsup": "^8.2.4",
"typescript": "^5.5.4",
"vitest": "^2.0.5"
}
}

@@ -1,8 +0,30 @@

# commercetools Frontend SDK
<h2 align="center">commercetools Frontend SDK</h2>
<p align="center">
<i></i>
</p>
<p align="center">
<a href="https://github.com/FrontasticGmbH/frontend-sdk/releases"><img src="https://badgen.net/github/release/FrontasticGmbH/frontend-sdk" alt="Latest release" /></a> <a href="https://github.com/FrontasticGmbH/frontend-sdk/blob/main/LICENSE"><img src="https://badgen.net/github/FrontasticGmbH/frontend-sdk" alt="GitHub license" /></a>
</p>
**Copyright (C) commercetools GmbH - All Rights Reserved**
The commercetools Frontend SDK is a TypeScript library that simplifies the process of creating custom components and integrations in your commercetools Frontend project. This SDK enables flexible and customizable frontend development, event handling, and support for integrations to expose commercetools Frontend API extensions.
The SDK is the core component of the commercetools frontend SDK,
providing core configuration and event management. Currently only within
the alpha release stage, documentation is currently unavailable.
## Installation
To install the base SDK in your project, run:
```bash
yarn add @commercetools/frontend-sdk
```
## Features
- Event management for custom components and integrations
- Backend-agnostic and backward-compatible
- Extendable with additional SDKs and extensions
## Documentation
For more detailed information, visit the [main documentation page](https://docs.commercetools.com/frontend-development/frontend-sdk).
## License
This project is licensed under the MIT License.
## Support
For any questions or issues, please [create a GitHub issue](https://github.com/FrontasticGmbH/frontend-sdk/issues).
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc