{"version":3,"file":"index.cjs","names":[],"sources":["../src/errors/TokenError.ts","../src/errors/InvalidTokenError.ts","../src/utils/base64.ts","../src/jwtDecode.ts","../src/utils/camelCaseToSnakeCase.ts","../src/utils/extractIssuer.ts","../src/utils/mapKeys.ts","../src/revoke.ts","../src/utils/hasOwner.ts","../src/utils/snakeCaseToCamelCase.ts","../src/authenticate.ts","../src/apiCredentials/debugLog.ts","../src/apiCredentials/dedupConcurrentCalls.ts","../src/apiCredentials/apiCredentials.ts","../src/apiCredentials/storage.ts","../src/apiCredentials/index.ts","../src/errors/TokenExpiredError.ts","../src/getCoreApiBaseEndpoint.ts","../src/getProvisioningApiBaseEndpoint.ts","../src/getTokenType.ts","../src/jwtEncode.ts","../src/utils/hasExpired.ts","../src/jwtVerify.ts"],"sourcesContent":["/**\n * A token error occurred.\n */\nexport class TokenError extends Error {\n  constructor(message: string) {\n    super(message)\n    this.name = \"TokenError\"\n  }\n}\n","import { TokenError } from \"./TokenError.js\"\n\n/**\n * The token is not valid.\n */\nexport class InvalidTokenError extends TokenError {\n  constructor(message: string) {\n    super(message)\n    this.name = \"InvalidTokenError\"\n  }\n}\n","/**\n * Creates a [Base64](https://developer.mozilla.org/en-US/docs/Glossary/Base64) URL safe encoded [ASCII](https://developer.mozilla.org/en-US/docs/Glossary/ASCII)\n * string from a _binary string_ (i.e., a string in which each character in the string is treated as a byte of binary data).\n *\n * The \"Base64 URL safe\" omits the padding `=` and replaces `+/` with `-_` to avoid characters that might cause problems in URL path segments or query parameters.\n *\n * This method works both in Node.js and browsers.\n *\n * @param stringToEncode The binary string to encode.\n * @returns An ASCII string containing the Base64 URL safe representation of `stringToEncode`.\n */\nexport function encodeBase64URLSafe(\n  stringToEncode: string,\n  encoding: \"utf-8\" | \"binary\",\n): string {\n  if (typeof btoa !== \"undefined\") {\n    let utf8String = stringToEncode\n\n    if (encoding === \"utf-8\") {\n      // Encode the string as UTF-8\n      const utf8Bytes = new TextEncoder().encode(stringToEncode)\n\n      // Convert the UTF-8 bytes to a Base64 string\n      utf8String = String.fromCharCode(...utf8Bytes)\n    }\n\n    return (\n      btoa(utf8String)\n        // Remove padding equal characters\n        .replaceAll(\"=\", \"\")\n        // Replace characters according to base64url specifications\n        .replaceAll(\"+\", \"-\")\n        .replaceAll(\"/\", \"_\")\n    )\n  }\n\n  return Buffer.from(stringToEncode, encoding).toString(\"base64url\")\n}\n\n/**\n * Decodes a string of data\n * which has been encoded using [Base64](https://developer.mozilla.org/en-US/docs/Glossary/Base64) URL safe encoding.\n *\n * The \"Base64 URL safe\" omits the padding `=` and replaces `+/` with `-_` to avoid characters that might cause problems in URL path segments or query parameters.\n *\n * This method works both in Node.js and browsers.\n *\n * @param encodedData A binary string (i.e., a string in which each character in the string is treated as a byte of binary data) containing Base64 URL safe -encoded data.\n * @returns An ASCII string containing decoded data from `encodedData`.\n */\nexport function decodeBase64URLSafe(\n  encodedData: string,\n  encoding: \"utf-8\" | \"binary\",\n): string {\n  if (typeof atob !== \"undefined\") {\n    const decoded = atob(\n      encodedData\n        // Replace characters according to base64url specifications\n        .replaceAll(\"-\", \"+\")\n        .replaceAll(\"_\", \"/\")\n        // Add padding if necessary\n        .padEnd(encodedData.length + ((4 - (encodedData.length % 4)) % 4), \"=\"),\n    )\n\n    if (encoding === \"utf-8\") {\n      // Decode the Base64 string into bytes\n      const byteArray = new Uint8Array(\n        [...decoded].map((char) => char.charCodeAt(0)),\n      )\n\n      // Convert the bytes back to a UTF-8 string\n      return new TextDecoder().decode(byteArray)\n    }\n\n    return decoded\n  }\n\n  return Buffer.from(encodedData, \"base64url\").toString(encoding)\n}\n","import { InvalidTokenError } from \"./errors/InvalidTokenError.js\"\nimport { decodeBase64URLSafe } from \"./utils/base64.js\"\n\n/**\n * Decode a Commerce Layer access token without verifying if the signature is valid.\n *\n * _You should not use this for untrusted messages, since this helper method does not verify whether the signature is valid.\n * If you need to verify the access token before decoding, you can use `jwtVerify` instead._\n */\nexport function jwtDecode(accessToken: string): CommerceLayerJWT {\n  const [encodedHeader, encodedPayload, signature] = `${accessToken}`.split(\".\")\n\n  if (encodedHeader == null || encodedPayload == null || signature == null) {\n    throw new InvalidTokenError(\"Invalid token format\")\n  }\n\n  return {\n    header: JSON.parse(decodeBase64URLSafe(encodedHeader, \"binary\")),\n    payload: JSON.parse(decodeBase64URLSafe(encodedPayload, \"utf-8\")),\n    signature,\n  }\n}\n\n/**\n * A Commerce Layer JWT.\n */\nexport interface CommerceLayerJWT {\n  /** The header typically consists of two parts: the type of the token, which is JWT, and the signing algorithm being used, such as HMAC SHA256 or RSA. */\n  header: {\n    /** Signing algorithm being used (e.g. `HMAC`, `SHA256`, `RSA`, `RS512`). */\n    alg: string\n    /** Type of the token (usually `JWT`). */\n    typ?: string\n    /** Key ID */\n    kid: string\n  }\n\n  payload: Payload\n\n  signature: string\n}\n\ntype Payload =\n  | JWTDashboard\n  | JWTUser\n  | JWTSalesChannel\n  | JWTIntegration\n  | JWTWebApp\n\ninterface JWTBase {\n  /** The type of credentials you're using to authenticate to the APIs. */\n  application: {\n    id: string\n    public: boolean\n    client_id: string\n    /** Whether the application is confidential (`true` when the `client_secret` is provided). */\n    confidential: boolean\n  }\n\n  /** Scope used to restrict access to a specific active market and/or stock location. */\n  scope: string\n  /** The token expiration time, expressed as an [epoch](https://www.epoch101.com/). */\n  exp: number\n  /** The environment type (true for test mode, false for live mode). */\n  test: boolean\n  /** A randomly generated number, less than one. */\n  rand: number\n  /** Issued at (seconds since Unix epoch). */\n  iat: number\n  /** Who created and signed this token (e.g. `\"https://auth.commercelayer.io\"`). */\n  iss: string\n}\n\n/**\n * A JWT payload that represents a `user`.\n */\nexport type JWTUser = JWTBase & {\n  /** The type of credentials you're using to authenticate to the APIs. */\n  application: {\n    kind: \"user\"\n  }\n  /** The authenticated user. */\n  user: {\n    id: string\n  }\n}\n\n/**\n * A JWT payload that represents a `dashboard`.\n */\nexport type JWTDashboard = JWTBase & {\n  /** The type of credentials you're using to authenticate to the APIs. */\n  application: {\n    kind: \"dashboard\"\n  }\n  /** The authenticated user. */\n  user: {\n    id: string\n  }\n}\n\nexport type JWTOrganizationBase = JWTBase & {\n  /** The organization in scope. */\n  organization: {\n    id: string\n    slug: string\n    enterprise: boolean\n    region: string\n  }\n  /** The owner (if any) authenticating to the APIs. */\n  owner?: {\n    id: string\n    type: \"Customer\" | \"User\"\n  }\n  /**\n   * Any other information (key/value pairs) you want to enrich the token with,\n   * when using the [JWT Bearer flow](https://docs.commercelayer.io/core/authentication/jwt-bearer).\n   */\n  custom_claim?: Record<string, string>\n  /**\n   * The market(s) in scope.\n   * This is available only when the scope is defined in the request.\n   */\n  market?: {\n    id: string[]\n    stock_location_ids: string[]\n    geocoder_id: string | null\n    allows_external_prices: boolean\n  }\n}\n\n/**\n * A JWT payload that represents a `webapp`.\n */\nexport type JWTWebApp = SetRequired<JWTOrganizationBase, \"owner\"> & {\n  /** The type of credentials you're using to authenticate to the APIs. */\n  application: {\n    kind: \"webapp\"\n  }\n}\n\n/** Create a type that makes the given keys required. The remaining keys are kept as is. */\ntype SetRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] }\n\n/**\n * A JWT payload that represents a `sales_channel`.\n */\nexport type JWTSalesChannel = JWTOrganizationBase & {\n  /** The type of credentials you're using to authenticate to the APIs. */\n  application: {\n    kind: \"sales_channel\"\n  }\n}\n\n/**\n * A JWT payload that represents an `integration`.\n */\nexport type JWTIntegration = JWTOrganizationBase & {\n  /** The type of credentials you're using to authenticate to the APIs. */\n  application: {\n    kind: \"integration\"\n  }\n}\n\n/**\n * Checks if the provided payload represents a `user`.\n * @param payload The payload to be checked.\n * @returns\n */\nexport function jwtIsUser(payload: Payload): payload is JWTUser {\n  return payload.application.kind === \"user\"\n}\n\n/**\n * Checks if the provided payload represents a `dashboard`.\n * @param payload The payload to be checked.\n * @returns\n */\nexport function jwtIsDashboard(payload: Payload): payload is JWTDashboard {\n  return payload.application.kind === \"dashboard\"\n}\n\n/**\n * Checks if the provided payload represents an `integration`.\n * @param payload The payload to be checked.\n * @returns\n */\nexport function jwtIsIntegration(payload: Payload): payload is JWTIntegration {\n  return payload.application.kind === \"integration\"\n}\n\n/**\n * Checks if the provided payload represents a `sales_channel`.\n * @param payload The payload to be checked.\n * @returns\n */\nexport function jwtIsSalesChannel(\n  payload: Payload,\n): payload is JWTSalesChannel {\n  return payload.application.kind === \"sales_channel\"\n}\n\n/**\n * Checks if the provided payload represents a `webapp`.\n * @param payload The payload to be checked.\n * @returns\n */\nexport function jwtIsWebApp(payload: Payload): payload is JWTWebApp {\n  return payload.application.kind === \"webapp\"\n}\n","export type CamelCaseToSnakeCase<\n  T extends string,\n  P extends string = \"\",\n> = string extends T\n  ? string\n  : T extends `${infer C}${infer R}`\n    ? CamelCaseToSnakeCase<\n        R,\n        `${P}${C extends Lowercase<C> ? \"\" : \"_\"}${Lowercase<C>}`\n      >\n    : P\n\nexport function camelCaseToSnakeCase<S extends string>(\n  str: S,\n): CamelCaseToSnakeCase<S> {\n  return str.replace(\n    /[A-Z]/g,\n    (letter) => `_${letter.toLowerCase()}`,\n  ) as CamelCaseToSnakeCase<S>\n}\n","import type { CommerceLayerJWT } from \"src/jwtDecode.js\"\n\n/**\n * Extract the `iss` from the decoded JWT.\n *\n * This is not as simple as `decodedJWT.payload.iss` because:\n * - at the beginning the `iss` was not required.\n * - the value can be `https://commercelayer.io` is old tokens.\n */\nexport function extractIssuer(\n  decodedJWT: CommerceLayerJWT,\n): `https://auth.${string}` {\n  return decodedJWT?.payload?.iss?.startsWith(\"https://auth.\")\n    ? (decodedJWT.payload.iss as `https://auth.${string}`)\n    : \"https://auth.commercelayer.io\"\n}\n","export function mapKeys(\n  obj: Record<string, unknown>,\n  fn: (key: string) => string,\n): Record<string, unknown> {\n  return Object.keys(obj).reduce((acc: Record<string, unknown>, key) => {\n    const camelKey = fn(key)\n    acc[camelKey] = obj[key]\n    return acc\n  }, {})\n}\n","import { jwtDecode } from \"./jwtDecode.js\"\nimport type { RevokeOptions, RevokeReturn } from \"./types/index.js\"\n\nimport { camelCaseToSnakeCase } from \"./utils/camelCaseToSnakeCase.js\"\nimport { extractIssuer } from \"./utils/extractIssuer.js\"\nimport { mapKeys } from \"./utils/mapKeys.js\"\n\n/**\n * Revoke a previously generated access token (refresh tokens included) before its natural expiration date.\n *\n * @param options Revoke options\n * @returns\n * @example\n * ```ts\n * await revoke({\n *   clientId: '{{ integrationClientId }}',\n *   clientSecret: '{{ integrationClientSecret }}',\n *   token: authenticateResponse.accessToken\n * })\n * ```\n */\nexport async function revoke(options: RevokeOptions): Promise<RevokeReturn> {\n  const body = mapKeys(options, camelCaseToSnakeCase)\n  const decodedJWT = jwtDecode(options.token)\n  const issuer = extractIssuer(decodedJWT)\n\n  const response = await fetch(`${issuer}/oauth/revoke`, {\n    method: \"POST\",\n    cache: \"no-store\",\n    headers: {\n      \"Content-Type\": \"application/json\",\n      Accept: \"application/json\",\n    },\n    body: JSON.stringify(body),\n  })\n\n  return (await response.json()) as RevokeReturn\n}\n","import type { CommerceLayerJWT, JWTOrganizationBase } from \"src/jwtDecode.js\"\n\n/**\n * Checks if the payload contains an owner field, which indicates that it is a customer token.\n */\nexport function hasOwner(\n  payload: CommerceLayerJWT[\"payload\"],\n): payload is PayloadWithOwner {\n  return \"owner\" in payload && payload.owner?.id != null\n}\n\ntype PayloadWithOwner = Extract<\n  CommerceLayerJWT[\"payload\"],\n  JWTOrganizationBase\n> & {\n  owner: NonNullable<JWTOrganizationBase[\"owner\"]>\n}\n","export type SnakeCaseToCamelCase<S extends string> =\n  S extends `${infer T}_${infer U}`\n    ? `${Lowercase<T>}${Capitalize<SnakeCaseToCamelCase<U>>}`\n    : S\n\nexport function snakeCaseToCamelCase<S extends string>(\n  str: S,\n): SnakeCaseToCamelCase<S> {\n  return str.replace(/([-_][a-z])/g, (group) =>\n    group.toUpperCase().replace(\"-\", \"\").replace(\"_\", \"\"),\n  ) as SnakeCaseToCamelCase<S>\n}\n","import type {\n  AuthenticateOptions,\n  AuthenticateReturn,\n  GrantType,\n} from \"./types/index.js\"\n\nimport { camelCaseToSnakeCase } from \"./utils/camelCaseToSnakeCase.js\"\nimport { mapKeys } from \"./utils/mapKeys.js\"\nimport { snakeCaseToCamelCase } from \"./utils/snakeCaseToCamelCase.js\"\n\ninterface TokenJson {\n  errors?: unknown\n  expires: Date\n  expires_in: number\n  [key: string]: unknown\n}\n\n/**\n * Authenticate helper used to get the access token.\n *\n * _Please note that the authentication endpoint is subject to a [rate limit](https://docs.commercelayer.io/core/rate-limits)\n * of **max 30 reqs / 1 min** both in live and test mode._\n * @param grantType The type of OAuth 2.0 grant being used for authentication.\n * @param options Authenticate options\n * @returns\n * @example\n * ```ts\n * import { authenticate } from '@commercelayer/js-auth'\n *\n * const auth = await authenticate('client_credentials', {\n *   clientId: '{{ clientId }}',\n *   scope: 'market:id:DGzAouppwn'\n * })\n *\n * console.log(auth.accessToken)\n * ```\n */\nexport async function authenticate<TGrantType extends GrantType>(\n  grantType: TGrantType,\n  {\n    domain = \"commercelayer.io\",\n    headers,\n    ...options\n  }: AuthenticateOptions<TGrantType>,\n): Promise<AuthenticateReturn<TGrantType>> {\n  const body = mapKeys(\n    {\n      grant_type: grantType,\n      ...options,\n    },\n    camelCaseToSnakeCase,\n  )\n\n  const response = await fetch(`https://auth.${domain}/oauth/token`, {\n    method: \"POST\",\n    cache: \"no-store\",\n    headers: {\n      \"Content-Type\": \"application/json\",\n      Accept: \"application/json\",\n      ...headers,\n    },\n    body: JSON.stringify(body),\n  })\n\n  const json: TokenJson = await response.json()\n\n  if (json.errors == null) {\n    json.expires = new Date(Date.now() + json.expires_in * 1000)\n  }\n\n  if (json.errors != null && !Array.isArray(json.errors)) {\n    json.errors = [json.errors]\n  }\n\n  return mapKeys(\n    json,\n    snakeCaseToCamelCase,\n  ) as unknown as AuthenticateReturn<TGrantType>\n}\n","/**\n * The level of a debug log entry.\n *\n * - `\"info\"` — a meaningful event (cache miss, token refresh, authorization\n *   stored/removed, errors). Emitted when logLevel is `\"info\"` or `\"verbose\"`.\n * - `\"verbose\"` — a steady-state operation (storage read, cache hit).\n *   Emitted only when logLevel is `\"verbose\"`.\n */\ntype DebugLogLevel = \"info\" | \"verbose\"\n\n/**\n * Fine-grained debug configuration.\n *\n * Pass this as the `debug` option to `makeSalesChannel`, `makeIntegration`,\n * or `createCompositeStorage`.\n */\nexport type DebugConfig = {\n  /**\n   * The log level.\n   *\n   * - `\"info\"` — logs meaningful events only (cache miss, token refresh,\n   *   authorization stored/removed, errors). Routine cache hits are not logged,\n   *   making it suitable for setups that call `getAuthorization()` on every API request.\n   * - `\"verbose\"` — also logs steady-state operations (every storage read and cache hit).\n   */\n  logLevel: DebugLogLevel\n  /**\n   * Whether to partially mask access tokens and refresh tokens in the log output\n   * (e.g. `eyJh...A9Xz`). Useful when logs are forwarded to external services\n   * (e.g. Datadog, Sentry).\n   *\n   * @default true\n   */\n  maskToken?: boolean\n}\n\ntype DebugLog = (\n  level: DebugLogLevel,\n  storageName: string | undefined,\n  message: string,\n  ...args: unknown[]\n) => void\n\nfunction maskToken<T>(value: T): T | string {\n  if (typeof value !== \"string\" || value.length <= 8) {\n    return value\n  }\n\n  return `${value.slice(0, 4)}...${value.slice(-4)}`\n}\n\nfunction redactArgs(args: unknown[]): unknown[] {\n  return args.map((arg) => {\n    if (arg !== null && typeof arg === \"object\" && \"accessToken\" in arg) {\n      return {\n        ...arg,\n        accessToken: maskToken(arg.accessToken),\n        ...(\"refreshToken\" in arg\n          ? { refreshToken: maskToken(arg.refreshToken) }\n          : {}),\n      }\n    }\n    return arg\n  })\n}\n\n/**\n * Creates the debug logger used by `makeAuth` and `createCompositeStorage`.\n */\nexport function createDebugLog({\n  debug,\n  logPrefix,\n}: {\n  debug: DebugConfig | undefined\n  logPrefix: string\n}): DebugLog {\n  return (level, storageName, message, ...args) => {\n    if (debug == null) {\n      return\n    }\n\n    if (level === \"verbose\" && debug.logLevel !== \"verbose\") {\n      return\n    }\n\n    const prefix = `[CommerceLayer • auth.js] [${logPrefix}]${storageName != null ? ` [${storageName}]` : \"\"} ${message}`\n    const finalArgs = (debug.maskToken ?? true) ? redactArgs(args) : args\n\n    console.log(prefix, ...finalArgs)\n  }\n}\n","/**\n * Deduplicates concurrent calls to a function with the same arguments.\n * If multiple calls are made with the same arguments, only the first call\n * will be executed, and all subsequent calls will receive the same result\n * once it resolves. This is useful for preventing duplicate API calls or\n * expensive operations that are called simultaneously with the same parameters.\n */\n// biome-ignore lint/suspicious/noExplicitAny: It accepts any function type.\nexport const dedupConcurrentCalls = <T extends (...args: any[]) => any>(\n  fn: T,\n): ((...args: Parameters<T>) => Promise<Awaited<ReturnType<T>>>) => {\n  type Return = Awaited<ReturnType<T>>\n  type Args = Parameters<T>\n\n  const ongoingCalls = new Map<\n    string,\n    {\n      requestQueue: Array<{\n        resolve: (value: Return) => void\n        reject: Parameters<ConstructorParameters<typeof Promise>[0]>[1]\n      }>\n    }\n  >()\n\n  return function (this: unknown, ...args: Args): Promise<Return> {\n    return new Promise((resolve, reject) => {\n      const argsKey = JSON.stringify(args)\n      const current = ongoingCalls.get(argsKey)\n\n      if (current === undefined) {\n        ongoingCalls.set(argsKey, { requestQueue: [] })\n\n        void Promise.resolve(fn.apply(this, args))\n          .then((result) => {\n            const query = ongoingCalls.get(argsKey)?.requestQueue ?? []\n            ongoingCalls.delete(argsKey)\n\n            resolve(result)\n            for (const item of query) {\n              item.resolve(result)\n            }\n          })\n          .catch((error) => {\n            const query = ongoingCalls.get(argsKey)?.requestQueue ?? []\n            ongoingCalls.delete(argsKey)\n\n            reject(error)\n            for (const item of query) {\n              item.reject(error)\n            }\n          })\n      } else {\n        current.requestQueue.push({\n          resolve,\n          reject,\n        })\n      }\n    })\n  }\n}\n","import { authenticate } from \"../authenticate.js\"\nimport { jwtDecode } from \"../jwtDecode.js\"\nimport { hasOwner } from \"../utils/hasOwner.js\"\nimport { createDebugLog } from \"./debugLog.js\"\nimport { dedupConcurrentCalls } from \"./dedupConcurrentCalls.js\"\nimport type { StorageValue, StoreOptions } from \"./storage.js\"\nimport type {\n  ApiCredentialsAuthorization,\n  AuthOptions,\n  SetRequired,\n} from \"./types.js\"\n\ntype MakeAuthReturn = {\n  options: AuthOptions\n  getAuthorization: () => Promise<ApiCredentialsAuthorization>\n  setAuthorization: (\n    auth: SetAuthorizationOptions,\n  ) => Promise<ApiCredentialsAuthorization>\n  removeAuthorization: (\n    type: ApiCredentialsAuthorization[\"ownerType\"],\n  ) => Promise<void>\n}\n\nfunction enhanceStorage<\n  Storage extends StoreOptions[\"storage\"] | StoreOptions[\"customerStorage\"],\n>(storage?: Storage): Storage {\n  if (storage === undefined) {\n    return storage as Storage\n  }\n\n  const originalGetItem = storage.getItem.bind(storage)\n\n  return {\n    ...storage,\n    async getItem(key: string) {\n      const value = await originalGetItem(key)\n      return value != null\n        ? {\n            ...value,\n            storageName: value.storageName ?? storage.name,\n          }\n        : null\n    },\n  }\n}\n\nfunction enhanceStoreOptions(store: StoreOptions): StoreOptions {\n  return {\n    ...store,\n    storage: enhanceStorage(store.storage),\n    customerStorage: enhanceStorage(store.customerStorage),\n  }\n}\n\nexport function makeAuth(\n  options: AuthOptions,\n  _store: StoreOptions,\n  {\n    logPrefix,\n    guestOnly = false,\n  }: {\n    logPrefix: string\n    /**\n     * Whether to allow only guest authorizations.\n     * If set to `true`, the helper will only return guest authorizations\n     * and will not attempt to read or refresh customer authorizations.\n     * This is useful for integrations that do not require customer authentication,\n     * such as backend integrations or public APIs.\n     * @default false\n     */\n    guestOnly?: boolean\n  },\n): MakeAuthReturn {\n  const authOptions: SetRequired<AuthOptions, \"scope\"> = {\n    ...options,\n    scope: options.scope ?? \"market:all\",\n  }\n\n  const store = enhanceStoreOptions(_store)\n\n  const log = createDebugLog({\n    debug: authOptions.debug,\n    logPrefix,\n  })\n\n  const getStorageKey =\n    store.getKey ??\n    ((configuration, type) => {\n      return `cl_${type}-${configuration.clientId}-${configuration.scope}`\n    })\n\n  const getAuthorization: MakeAuthReturn[\"getAuthorization\"] = async () => {\n    /**\n     * Threshold duration before token expiration, in seconds.\n     *\n     * When `authorization.expiresIn` drops below this value,\n     * a new token will be requested to prevent usage of a nearly expired token.\n     *\n     * According to the Commerce Layer documentation, a new token is issued\n     * 15 minutes (900 seconds) before the previous token expires.\n     * During this overlap window, both tokens are valid.\n     *\n     * @example\n     * 600 seconds (10 minutes)\n     */\n    const expirationThreshold = 10 * 60\n\n    try {\n      if (guestOnly === false) {\n        // read `customer` authorization\n        const customerKey = await getStorageKey(\n          {\n            clientId: authOptions.clientId,\n            scope: authOptions.scope,\n          },\n          \"customer\",\n        )\n\n        const storage = store.customerStorage ?? store.storage\n\n        log(\"verbose\", storage.name, \"Checking for customer key:\", customerKey)\n\n        const storedValue = await storage.getItem(customerKey)\n        const customerAuthorization = toAuthorization(storedValue)\n\n        if (customerAuthorization?.ownerType === \"customer\") {\n          if (customerAuthorization.expiresIn >= expirationThreshold) {\n            log(\n              \"verbose\",\n              storedValue?.storageName,\n              'Found \"customer\" authorization in storage',\n              customerAuthorization,\n            )\n\n            return customerAuthorization\n          }\n\n          log(\n            \"info\",\n            storedValue?.storageName,\n            \"Customer authorization expired\",\n            customerAuthorization,\n          )\n\n          if (customerAuthorization.refreshToken != null) {\n            const refreshTokenResponse = await authenticate(\"refresh_token\", {\n              ...authOptions,\n              refreshToken: customerAuthorization.refreshToken,\n            })\n\n            const authorization = await setAuthorization({\n              accessToken: refreshTokenResponse.accessToken,\n              scope: refreshTokenResponse.scope,\n              refreshToken: refreshTokenResponse.refreshToken,\n            })\n\n            log(\n              \"info\",\n              storedValue?.storageName,\n              \"Refreshed customer authorization\",\n              authorization,\n            )\n\n            return authorization\n          }\n\n          log(\n            \"info\",\n            storedValue?.storageName,\n            \"Customer authorization expired and has no refresh token, removing stale authorization\",\n          )\n\n          await storage.removeItem(customerKey).catch((error) => {\n            log(\n              \"info\",\n              storedValue?.storageName,\n              \"Unable to remove stale customer authorization\",\n              error,\n            )\n          })\n        }\n      }\n\n      // read `guest` authorization\n      const guestKey = await getStorageKey(\n        {\n          clientId: authOptions.clientId,\n          scope: authOptions.scope,\n        },\n        \"guest\",\n      )\n\n      const storage = store.storage\n\n      log(\"verbose\", storage.name, \"Checking for guest key:\", guestKey)\n\n      const storedValue = await storage.getItem(guestKey)\n      const guestAuthorization = toAuthorization(storedValue)\n\n      if (\n        guestAuthorization?.ownerType === \"guest\" &&\n        guestAuthorization.expiresIn >= expirationThreshold\n      ) {\n        log(\n          \"verbose\",\n          storedValue?.storageName,\n          'Found \"guest\" authorization in storage',\n          guestAuthorization,\n        )\n\n        return guestAuthorization\n      }\n\n      // requesting a new token\n\n      log(\n        \"info\",\n        storage.name,\n        \"No valid authorization found, requesting a new guest token\",\n      )\n\n      // create `guest` authorization\n      const clientCredentialsResponse = await authenticate(\n        \"client_credentials\",\n        authOptions,\n      )\n\n      const authorization = await setAuthorization({\n        accessToken: clientCredentialsResponse.accessToken,\n        scope: clientCredentialsResponse.scope,\n      })\n\n      return authorization\n    } catch (error) {\n      log(\"info\", undefined, \"Error getting the authorization.\", error)\n      throw error\n    }\n  }\n\n  const setAuthorization: MakeAuthReturn[\"setAuthorization\"] = async (auth) => {\n    const authorization = toAuthorization(auth)\n\n    const key = await getStorageKey(\n      { clientId: authOptions.clientId, scope: auth.scope },\n      authorization.ownerType,\n    )\n\n    const value: StorageValue = {\n      accessToken: authorization.accessToken,\n      scope: authorization.scope,\n      refreshToken:\n        authorization.ownerType === \"customer\"\n          ? authorization.refreshToken\n          : undefined,\n    }\n\n    const storage =\n      authorization.ownerType === \"customer\"\n        ? (store.customerStorage ?? store.storage)\n        : store.storage\n\n    log(\n      \"verbose\",\n      storage.name,\n      `Storing \"${authorization.ownerType}\" authorization with key:`,\n      key,\n    )\n\n    await storage.setItem(key, value)\n\n    log(\n      \"info\",\n      storage.name,\n      `Stored \"${authorization.ownerType}\" authorization to storage`,\n      authorization,\n    )\n\n    return authorization\n  }\n\n  const removeAuthorization: MakeAuthReturn[\"removeAuthorization\"] = async (\n    type,\n  ) => {\n    const key = await getStorageKey(\n      { clientId: authOptions.clientId, scope: authOptions.scope },\n      type,\n    )\n\n    const storage =\n      type === \"customer\"\n        ? (store.customerStorage ?? store.storage)\n        : store.storage\n\n    await storage.removeItem(key)\n\n    log(\"info\", storage.name, `Removed \"${type}\" authorization with key:`, key)\n  }\n\n  return {\n    options: authOptions,\n    getAuthorization: dedupConcurrentCalls(getAuthorization),\n    setAuthorization,\n    removeAuthorization,\n  }\n}\n\n/**\n * Converts the `SetAuthorizationOptions` into an `Authorization` object.\n */\nfunction toAuthorization<Options extends SetAuthorizationOptions | null>(\n  options: Options,\n): Options extends null\n  ? ApiCredentialsAuthorization | null\n  : ApiCredentialsAuthorization {\n  if (options == null) {\n    return null as Options extends null\n      ? ApiCredentialsAuthorization | null\n      : ApiCredentialsAuthorization\n  }\n\n  const decodedJWT = jwtDecode(options.accessToken)\n\n  if (\n    hasOwner(decodedJWT.payload) &&\n    decodedJWT.payload.owner.type.toLowerCase() !== \"customer\"\n  ) {\n    throw new Error(\n      \"The provided access token does not contain a valid customer owner.\",\n    )\n  }\n\n  const type = hasOwner(decodedJWT.payload)\n    ? ({\n        ownerType: \"customer\",\n        ownerId: decodedJWT.payload.owner.id,\n        refreshToken: options.refreshToken,\n      } as const)\n    : ({ ownerType: \"guest\" } as const)\n\n  const expiresIn = Math.round(decodedJWT.payload.exp - Date.now() / 1000)\n\n  const authorization: ApiCredentialsAuthorization = {\n    ...type,\n    tokenType: \"bearer\",\n    createdAt: decodedJWT.payload.iat,\n    scope: options.scope,\n    accessToken: options.accessToken,\n    expiresIn,\n    expires: new Date(Date.now() + expiresIn * 1000),\n  }\n\n  return authorization\n}\n\ntype SetAuthorizationOptions = StorageValue\n","import type { AuthenticateReturn } from \"../types/index.js\"\nimport { createDebugLog, type DebugConfig } from \"./debugLog.js\"\nimport type { ApiCredentialsAuthorization } from \"./types.js\"\n\n/**\n * Creates a composite storage that combines multiple storages.\n *\n * When retrieving an item, it returns the first non-null value from the configured storages.\n *\n * The order of the configured storages is important, as it determines priority. The first storage in the array is checked first, followed by the second, and so on.\n *\n * Using a composite storage can be useful for reducing the load on the underlying storage.\n * For example, if you want to use Redis but avoid hitting it on every request, you can use a memory storage as the first storage and Redis as the second.\n * This way, the memory storage is checked first, and if the item is not found there, it falls back to Redis and is then saved to memory for subsequent requests.\n *\n * When setting or removing an item, the operation is performed on all configured storages to ensure consistency.\n *\n * @param options - An object containing an array of storage instances to combine and optionally a name and a debug option.\n * @returns A composite storage instance.\n *\n * @example\n * const compositeStorage = createCompositeStorage({\n *   name: \"composite-storage\",\n *   storages: [memoryStorage, redisStorage]\n * });\n *\n * const value = await compositeStorage.getItem('myKey');\n *\n * // If you set the `name` property on the individual storages,\n * // the returned value will include the `storageName` indicating which storage provided the value.\n */\nexport function createCompositeStorage(options: {\n  storages: Storage[]\n  name?: string\n  /**\n   * Whether to enable debug mode.\n   *\n   * - `\"info\"` — logs meaningful events only: a value found in a fallback\n   *   storage (i.e. a cache miss on the faster storages, with backfill).\n   * - `\"verbose\"` — also logs steady-state operations (hits on the first storage and total misses).\n   */\n  debug?: DebugConfig\n}): Storage {\n  const log = createDebugLog({\n    debug: options.debug,\n    logPrefix: \"storage\",\n  })\n\n  const compositeName = options.name\n\n  return {\n    name: compositeName,\n\n    async getItem(key: string) {\n      for (const [index, storage] of options.storages.entries()) {\n        const value = await storage.getItem(key)\n\n        if (value !== null) {\n          if (index > 0) {\n            const missedStorages = options.storages.slice(0, index)\n\n            log(\n              \"info\",\n              compositeName,\n              `Value for key \"${key}\" found in fallback storage \"${storage.name ?? `#${index}`}\" after missing in [${missedStorages.map((missed, missedIndex) => missed.name ?? `#${missedIndex}`).join(\", \")}], backfilling faster storages`,\n            )\n\n            for (const previousStorage of missedStorages) {\n              await previousStorage.setItem(key, value)\n            }\n          } else {\n            log(\n              \"verbose\",\n              compositeName,\n              `Value for key \"${key}\" found in storage \"${storage.name ?? `#${index}`}\"`,\n            )\n          }\n\n          return {\n            ...value,\n            storageName: storage.name,\n          }\n        }\n      }\n\n      log(\n        \"verbose\",\n        compositeName,\n        `Value for key \"${key}\" not found in any storage`,\n      )\n\n      return null\n    },\n\n    async setItem(key: string, value: StorageValue) {\n      await Promise.all(\n        options.storages.map((storage) => storage.setItem(key, value)),\n      )\n    },\n\n    async removeItem(key: string) {\n      await Promise.all(\n        options.storages.map((storage) => storage.removeItem(key)),\n      )\n    },\n\n    async dispose() {\n      await Promise.all(options.storages.map((storage) => storage.dispose?.()))\n    },\n  }\n}\n\nexport type StoreOptions = {\n  /**\n   * Function to get the key for storing the authorization in the storage.\n   * This function receives the configuration and the type of authorization\n   * and should return a unique key for that authorization.\n   */\n  getKey?: (\n    configuration: {\n      clientId: string\n      scope: string\n    },\n    type: ApiCredentialsAuthorization[\"ownerType\"],\n  ) => Promise<string>\n  /**\n   * Storage instance for storing authorizations.\n   * This storage will be used for both \"guest\" and \"customer\" authorizations.\n   *\n   * If you need to use a different store for \"customer\" authorizations,\n   * you can provide a different storage instance using `customerStorage` option.\n   */\n  storage: Storage\n  /**\n   * Storage instance for storing \"customer\" authorizations.\n   * If not provided, the `storage` option will be used.\n   *\n   * This is useful if you want to separate guest and customer authorizations,\n   * for example, using `localStorage` for guest authorizations\n   * and `sessionStorage` for customer authorizations.\n   */\n  customerStorage?: Storage\n}\n\n/**\n * Storage interface for managing key/value pairs.\n *\n * This interface is used to abstract the storage mechanism, allowing for different implementations (e.g., localStorage, sessionStorage, in-memory storage).\n *\n * **Naming Best Practice**: It's highly recommended to provide a `name` for your storage instances.\n * This enables better observability in debug logs, especially when using composite storages,\n * as it allows you to trace which storage layer (memory, Redis, localStorage, etc.) provided each value.\n *\n * @example\n * ```typescript\n * const memoryStorage = {\n *   name: \"memory\",\n *   async getItem(key: string) { ... },\n *   async setItem(key: string, value: StorageValue) { ... },\n *   ...\n * }\n *\n * ```\n */\nexport interface Storage {\n  /**\n   * Optional name of the storage for identification purposes.\n   *\n   * **Recommended**: Provide a descriptive name (e.g., \"memory-cache\", \"redis\", \"localStorage\")\n   * to improve debug log traceability, especially when using composite storages.\n   */\n  name?: string\n\n  /**\n   * Returns the current value associated with the given key, or null if the given key does not exist.\n   */\n  getItem: (key: string) => Promise<\n    | (StorageValue & {\n        /**\n         * The name of the storage from which the item was retrieved.\n         */\n        storageName?: string\n      })\n    | null\n  >\n\n  /**\n   * Sets the value of the pair identified by key to value, creating a new key/value pair if none existed for key previously.\n   */\n  setItem: (key: string, value: StorageValue) => Promise<void>\n\n  /**\n   * Removes the key/value pair with the given key, if a key/value pair with the given key exists.\n   */\n  removeItem: (key: string) => Promise<void>\n\n  /**\n   * Disposes all mounted storages to ensure there are no open-handles left.\n   * Call it before exiting process.\n   */\n  dispose?: () => Promise<void>\n}\n\nexport type StorageValue = Pick<\n  AuthenticateReturn<\"password\">,\n  \"accessToken\" | \"scope\"\n> & {\n  /**\n   * The refresh token. If present, it can be used to obtain a new access token.\n   */\n  refreshToken?: string\n}\n","import { jwtDecode } from \"../jwtDecode.js\"\nimport { revoke } from \"../revoke.js\"\nimport type { RevokeReturn } from \"../types/index.js\"\nimport { hasOwner } from \"../utils/hasOwner.js\"\nimport { makeAuth } from \"./apiCredentials.js\"\nimport type { StorageValue, StoreOptions } from \"./storage.js\"\nimport type {\n  ApiCredentialsAuthorization,\n  AuthOptions,\n  SetRequired,\n} from \"./types.js\"\n\nexport type { DebugConfig } from \"./debugLog.js\"\nexport {\n  createCompositeStorage,\n  type Storage,\n  type StorageValue,\n} from \"./storage.js\"\nexport type { ApiCredentialsAuthorization } from \"./types.js\"\n\n/**\n * [**Integrations**](https://docs.commercelayer.io/core/api-credentials#integration) are used\n * to develop backend integrations with any 3rd-party system.\n *\n * This helper manages the caching mechanism to avoid unnecessary API calls.\n */\nexport function makeIntegration(\n  options: SetRequired<AuthOptions, \"clientSecret\">,\n  store: Omit<StoreOptions, \"customerStorage\">,\n): {\n  options: SetRequired<AuthOptions, \"clientSecret\">\n  /**\n   * Get the current integration authorization.\n   *\n   * This will return the authorization from configured storage.\n   * If the authorization is not found or expired, it will fetch a new one.\n   */\n  getAuthorization: ReturnType<typeof makeAuth>[\"getAuthorization\"]\n  /**\n   * Remove the authorization from configured storages.\n   *\n   * This is particularly useful when you want to get a fresh authorization.\n   * **Note:** This will not revoke the token.\n   */\n  removeAuthorization: (type?: \"all\") => Promise<void>\n  /**\n   * Revoke the current integration authorization.\n   *\n   * This will remove the authorization from memory and storage, and revoke the access token.\n   */\n  revokeAuthorization: () => Promise<RevokeReturn>\n} {\n  const auth = makeAuth(options, store, {\n    logPrefix: \"integration\",\n    guestOnly: true,\n  })\n\n  return {\n    options,\n    getAuthorization: auth.getAuthorization,\n    removeAuthorization: async () => {\n      return await auth.removeAuthorization(\"guest\")\n    },\n    revokeAuthorization: async () => {\n      const { ownerType: type, accessToken } = await auth.getAuthorization()\n\n      if (type === \"guest\") {\n        await auth.removeAuthorization(\"guest\")\n\n        return await revoke({\n          clientId: options.clientId,\n          clientSecret: options.clientSecret,\n          token: accessToken,\n        })\n      }\n\n      return {}\n    },\n  }\n}\n\n/**\n * [**Sales channels**](https://docs.commercelayer.io/core/api-credentials#sales-channel) are used\n * to build any customer touchpoint (e.g. your storefront with a fully-functional shopping cart and checkout flow).\n *\n * This helper manages the caching mechanism to avoid unnecessary API calls\n * and provides methods to set and remove customer authorizations.\n */\nexport function makeSalesChannel(\n  options: Omit<AuthOptions, \"clientSecret\">,\n  store: StoreOptions,\n): {\n  options: Omit<AuthOptions, \"clientSecret\">\n  /**\n   * Get the current sales channel authorization.\n   *\n   * This will return the authorization from the configured storage.\n   * If the authorization is not found or expired, it will fetch a new one.\n   * If the customer is logged in with a `refreshToken`, it will also attempt to refresh the token when expired.\n   */\n  getAuthorization: ReturnType<typeof makeAuth>[\"getAuthorization\"]\n  /**\n   * Remove the authorization from configured storages.\n   *\n   * This is particularly useful when you want to get a fresh authorization.\n   * **Note:** This will not revoke the token.\n   */\n  removeAuthorization: (\n    type?: \"all\" | ApiCredentialsAuthorization[\"ownerType\"],\n  ) => Promise<void>\n  /**\n   * Sets the customer authorization.\n   * This will store the authorization in memory and storage.\n   * It will also validate the access token to ensure it contains a valid customer owner.\n   *\n   * The option `refreshToken` is optional and can be used to set a refresh token for the customer (e.g. \"remember me\" functionality).\n   * It will be used to refresh the access token when it expires.\n   */\n  setCustomer: (options: StorageValue) => Promise<ApiCredentialsAuthorization>\n  /**\n   * Logs out the current customer.\n   *\n   * This will remove the customer authorization from memory and storage, and revoke the access token.\n   */\n  logoutCustomer: () => Promise<RevokeReturn>\n} {\n  const auth = makeAuth(options, store, {\n    logPrefix: \"sales_channel\",\n  })\n\n  return {\n    options,\n    getAuthorization: auth.getAuthorization,\n    removeAuthorization: async (type = \"all\") => {\n      if (type === \"all\") {\n        await auth.removeAuthorization(\"customer\")\n        await auth.removeAuthorization(\"guest\")\n        return\n      }\n\n      return auth.removeAuthorization(type)\n    },\n    setCustomer: async (options) => {\n      const decodedJWT = jwtDecode(options.accessToken)\n\n      if (hasOwner(decodedJWT.payload)) {\n        return await auth.setAuthorization(options)\n      }\n\n      throw new Error(\n        \"The provided access token does not contain a valid customer owner.\",\n      )\n    },\n    logoutCustomer: async () => {\n      const { ownerType: type, accessToken } = await auth.getAuthorization()\n\n      if (type === \"customer\") {\n        await auth.removeAuthorization(\"customer\")\n\n        return await revoke({\n          clientId: options.clientId,\n          token: accessToken,\n        })\n      }\n\n      return {}\n    },\n  }\n}\n","import { TokenError } from \"./TokenError.js\"\n\n/**\n * The token expired.\n */\nexport class TokenExpiredError extends TokenError {\n  constructor() {\n    super(\"Token expired\")\n    this.name = \"TokenExpiredError\"\n  }\n}\n","import { InvalidTokenError } from \"./errors/InvalidTokenError.js\"\nimport { jwtDecode } from \"./jwtDecode.js\"\nimport { extractIssuer } from \"./utils/extractIssuer.js\"\n\n/**\n * Derives the [Core API base endpoint](https://docs.commercelayer.io/core/api-specification#base-endpoint) given a valid access token.\n *\n * @example\n * ```ts\n * getCoreApiBaseEndpoint('eyJhbGciOiJS...') //= \"https://yourdomain.commercelayer.io\"\n * ```\n *\n * The method requires a valid access token with an `organization` in the payload.\n *\n * @param accessToken - The access token to decode.\n * @param options - An options object to configure behavior.\n * @returns The core API base endpoint as a string, or `null` if the token is invalid and `shouldThrow` is `false`.\n * @throws InvalidTokenError - If the token is invalid and `shouldThrow` is true.\n */\nexport function getCoreApiBaseEndpoint(\n  accessToken: string,\n  options?: {\n    /**\n     * Whether to throw an error if the token is invalid.\n     * @default true\n     */\n    shouldThrow?: true\n  },\n): string\n\n/**\n * Derives the [Core API base endpoint](https://docs.commercelayer.io/core/api-specification#base-endpoint) given a valid access token.\n *\n * @example\n * ```ts\n * getCoreApiBaseEndpoint('eyJhbGciOiJS...') //= \"https://yourdomain.commercelayer.io\"\n * ```\n *\n * The method requires a valid access token with an `organization` in the payload.\n *\n * @param accessToken - The access token to decode.\n * @param options - An options object to configure behavior.\n * @returns The core API base endpoint as a string, or `null` if the token is invalid and `shouldThrow` is `false`.\n * @throws InvalidTokenError - If the token is invalid and `shouldThrow` is true.\n */\nexport function getCoreApiBaseEndpoint(\n  accessToken: string,\n  options: {\n    /**\n     * Whether to throw an error if the token is invalid.\n     * @default true\n     */\n    shouldThrow: false\n  },\n): string | null\n\nexport function getCoreApiBaseEndpoint(\n  accessToken: string,\n  options: {\n    shouldThrow?: boolean\n  } = {},\n): string | null {\n  const { shouldThrow = true } = options\n  const decodedJWT = jwtDecode(accessToken)\n\n  if (!(\"organization\" in decodedJWT.payload)) {\n    if (shouldThrow) {\n      throw new InvalidTokenError(\"Invalid token format\")\n    }\n\n    return null\n  }\n\n  return extractIssuer(decodedJWT).replace(\n    \"auth\",\n    decodedJWT.payload.organization.slug,\n  )\n}\n","import { InvalidTokenError } from \"./errors/InvalidTokenError.js\"\nimport { jwtDecode } from \"./jwtDecode.js\"\nimport { extractIssuer } from \"./utils/extractIssuer.js\"\n\n/**\n * Returns the [Provisioning API base endpoint](https://docs.commercelayer.io/provisioning/getting-started/api-specification#base-endpoint) given a valid access token.\n *\n * @example\n * ```ts\n * getProvisioningApiBaseEndpoint('eyJhbGciOiJS...') //= \"https://provisioning.commercelayer.io\"\n * ```\n *\n * The method requires a valid access token for Provisioning API.\n *\n * @param accessToken - The access token to decode.\n * @param options - An options object to configure behavior.\n * @returns The provisioning API base endpoint as a string, or `null` if the token is invalid and `shouldThrow` is `false`.\n * @throws InvalidTokenError - If the token is invalid and `shouldThrow` is true.\n */\nexport function getProvisioningApiBaseEndpoint(\n  accessToken: string,\n  options?: {\n    /**\n     * Whether to throw an error if the token is invalid.\n     * @default true\n     */\n    shouldThrow?: true\n  },\n): string\n\n/**\n * Returns the [Provisioning API base endpoint](https://docs.commercelayer.io/provisioning/getting-started/api-specification#base-endpoint) given a valid access token.\n *\n * @example\n * ```ts\n * getProvisioningApiBaseEndpoint('eyJhbGciOiJS...') //= \"https://provisioning.commercelayer.io\"\n * ```\n *\n * The method requires a valid access token for Provisioning API.\n *\n * @param accessToken - The access token to decode.\n * @param options - An options object to configure behavior.\n * @returns The provisioning API base endpoint as a string, or `null` if the token is invalid and `shouldThrow` is `false`.\n * @throws InvalidTokenError - If the token is invalid and `shouldThrow` is true.\n */\nexport function getProvisioningApiBaseEndpoint(\n  accessToken: string,\n  options: {\n    /**\n     * Whether to throw an error if the token is invalid.\n     * @default true\n     */\n    shouldThrow: false\n  },\n): string | null\n\nexport function getProvisioningApiBaseEndpoint(\n  accessToken: string,\n  options: {\n    shouldThrow?: boolean\n  } = {},\n): string | null {\n  const { shouldThrow = true } = options\n  const decodedJWT = jwtDecode(accessToken)\n\n  if (!decodedJWT?.payload?.scope?.includes(\"provisioning-api\")) {\n    if (shouldThrow) {\n      throw new InvalidTokenError(\"Invalid token format\")\n    }\n\n    return null\n  }\n\n  return extractIssuer(decodedJWT).replace(\"auth\", \"provisioning\")\n}\n","import type { CommerceLayerJWT } from \"./jwtDecode.js\"\n\n/**\n * Return a normalized token type from the provided payload.\n *\n * The token type is a string in the format `{applicationKind}:{ownerType}`, where:\n * - `applicationKind` is the kind of application used to authenticate (e.g. `dashboard`, `user`, `sales_channel`, `integration`, `webapp`).\n * - `ownerType` is the lowercased type of the token owner (e.g. `customer`, `user`), or `guest` when no owner is present.\n *\n * This is useful for quick authorization checks in any context, for example allowing only `integration:*`\n * tokens, or only storefront tokens such as `sales_channel:guest` and `sales_channel:customer`.\n *\n * @param payload The JWT payload to inspect.\n * @returns The token type string in the form `{applicationKind}:{ownerType}`.\n */\nexport function getTokenType(payload: CommerceLayerJWT[\"payload\"]): TokenType {\n  const applicationKind = payload.application.kind\n  const ownerType =\n    \"owner\" in payload && payload.owner != null\n      ? (payload.owner.type.toLowerCase() as Lowercase<\n          typeof payload.owner.type\n        >)\n      : \"guest\"\n\n  return `${applicationKind}:${ownerType}`\n}\n\n/**\n * Token classification string returned by {@link getTokenType}.\n *\n * It combines the application kind and owner type as `{applicationKind}:{ownerType}`\n * (for example `integration:user` or `sales_channel:guest`).\n */\nexport type TokenType = `${ApplicationKind}:${OwnerType}`\n\n/**\n * Supported application kinds extracted from Commerce Layer JWT payloads.\n */\ntype ApplicationKind = Extract<\n  CommerceLayerJWT[\"payload\"],\n  { application: { kind: string } }\n>[\"application\"][\"kind\"]\n\n/**\n * Supported owner types extracted from Commerce Layer JWT payloads.\n *\n * The value is normalized to lowercase, and includes `guest` for tokens that\n * do not have an owner.\n */\ntype OwnerType =\n  | Lowercase<\n      NonNullable<\n        Extract<\n          CommerceLayerJWT[\"payload\"],\n          { owner?: { type: string } }\n        >[\"owner\"]\n      >[\"type\"]\n    >\n  | \"guest\"\n","import { encodeBase64URLSafe } from \"./utils/base64.js\"\n\ninterface Owner {\n  type: \"User\" | \"Customer\"\n  id: string\n}\n\n/**\n * Create a JWT assertion as the first step of the [JWT bearer token authorization grant flow](https://docs.commercelayer.io/core/authentication/jwt-bearer).\n *\n * The JWT assertion is a digitally signed JSON object containing information\n * about the client and the user on whose behalf the access token is being requested.\n *\n * This JWT assertion can include information such as the issuer (typically the client),\n * the owner (the user on whose behalf the request is made), and any other relevant claims.\n *\n * @example\n * ```ts\n * const assertion = await createAssertion({\n *   payload: {\n *     'https://commercelayer.io/claims': {\n *       owner: {\n *         type: 'Customer',\n *         id: '4tepftJsT2'\n *       },\n *       custom_claim: {\n *         customer: {\n *           first_name: 'John',\n *           last_name: 'Doe'\n *         }\n *       }\n *     }\n *   }\n * })\n * ```\n */\nexport async function createAssertion({ payload }: Assertion): Promise<string> {\n  return await jwtEncode(payload, \"cl\")\n}\n\n/** RequireAtLeastOne helps create a type where at least one of the properties of an interface (can be any property) is required to exist. */\ntype RequireAtLeastOne<T> = {\n  [K in keyof T]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<keyof T, K>>>\n}[keyof T]\n\ninterface Assertion {\n  /** Assertion payload. */\n  payload: {\n    /** At least one of `owner` or `custom_claim` is required. You cannot use an empty object. */\n    \"https://commercelayer.io/claims\": RequireAtLeastOne<{\n      /** The customer or user you want to make the calls on behalf of. */\n      owner?: Owner\n      /** Any other information (key/value pairs) you want to enrich the token with. */\n      custom_claim?: Record<string, unknown>\n    }>\n  }\n}\n\nasync function jwtEncode(\n  payload: Record<string, unknown>,\n  secret: string,\n): Promise<string> {\n  const header = { alg: \"HS512\", typ: \"JWT\" }\n\n  const encodedHeader = encodeBase64URLSafe(JSON.stringify(header), \"binary\")\n\n  const encodedPayload = encodeBase64URLSafe(\n    JSON.stringify({\n      ...payload,\n      iat: Math.floor(Date.now() / 1000),\n    }),\n    \"utf-8\",\n  )\n\n  const unsignedToken = `${encodedHeader}.${encodedPayload}`\n\n  const signature = await createSignature(unsignedToken, secret)\n\n  return `${unsignedToken}.${signature}`\n}\n\nasync function createSignature(data: string, secret: string): Promise<string> {\n  const enc = new TextEncoder()\n  const algorithm = { name: \"HMAC\", hash: \"SHA-512\" }\n\n  const key = await crypto.subtle.importKey(\n    \"raw\",\n    enc.encode(secret),\n    algorithm,\n    false,\n    [\"sign\", \"verify\"],\n  )\n\n  const signature = await crypto.subtle.sign(\n    algorithm.name,\n    key,\n    enc.encode(data),\n  )\n\n  return encodeBase64URLSafe(\n    String.fromCharCode(...new Uint8Array(signature)),\n    \"binary\",\n  )\n}\n","import type { CommerceLayerJWT } from \"src/jwtDecode.js\"\n\n/**\n * Check if a Commerce Layer JWT has expired.\n *\n * @param decodedJWT - The decoded JWT to check.\n * @returns `true` if the JWT has expired, `false` otherwise.\n */\nexport function hasExpired(decodedJWT: CommerceLayerJWT): boolean {\n  return Date.now() >= decodedJWT.payload.exp * 1000\n}\n","import { InvalidTokenError } from \"./errors/InvalidTokenError.js\"\nimport { TokenError } from \"./errors/TokenError.js\"\nimport { TokenExpiredError } from \"./errors/TokenExpiredError.js\"\nimport { type CommerceLayerJWT, jwtDecode } from \"./jwtDecode.js\"\nimport { decodeBase64URLSafe } from \"./utils/base64.js\"\nimport { extractIssuer } from \"./utils/extractIssuer.js\"\nimport { hasExpired } from \"./utils/hasExpired.js\"\n\n/**\n * Validate the integrity and authenticity of the JWT.\n * It checks if the token is valid by verifying the signature against the public key used to create it.\n * This is useful to ensure that the token hasn't been tampered with and originates from Commerce Layer.\n *\n * When the verification succeeds, it resolves to the decoded access token, it rejects otherwise.\n */\nexport async function jwtVerify(\n  accessToken: string,\n  { ignoreExpiration = false, jwk }: JwtVerifyOptions = {},\n): Promise<CommerceLayerJWT> {\n  const decodedJWT = jwtDecode(accessToken)\n\n  const jsonWebKey = jwk ?? (await getJsonWebKey(decodedJWT))\n\n  if (jsonWebKey == null || jsonWebKey.kid !== decodedJWT.header.kid) {\n    throw new InvalidTokenError('Invalid token \"kid\"')\n  }\n\n  if (!ignoreExpiration && hasExpired(decodedJWT)) {\n    throw new TokenExpiredError()\n  }\n\n  const algorithm: RsaHashedImportParams = {\n    name: \"RSASSA-PKCS1-v1_5\",\n    hash: \"SHA-512\",\n  }\n\n  const publicKey = await crypto.subtle.importKey(\n    \"jwk\",\n    jsonWebKey,\n    algorithm,\n    true,\n    [\"verify\"],\n  )\n\n  const rawSignature = new Uint8Array(\n    Array.from(decodeBase64URLSafe(decodedJWT.signature, \"binary\"), (c) =>\n      c.charCodeAt(0),\n    ),\n  )\n\n  const rawData = new TextEncoder().encode(\n    accessToken.split(\".\").slice(0, 2).join(\".\"),\n  )\n\n  const isValid = await crypto.subtle.verify(\n    algorithm,\n    publicKey,\n    rawSignature,\n    rawData,\n  )\n\n  if (!isValid) {\n    throw new InvalidTokenError(\"Invalid signature\")\n  }\n\n  return decodedJWT\n}\n\ntype CommerceLayerJsonWebKey = JsonWebKey & { kid: string }\n\ninterface JwtVerifyOptions {\n  /**\n   * Do not validate the token expiration when set to `true`.\n   * @default false\n   */\n  ignoreExpiration?: boolean\n\n  /**\n   * Json Web Key used to verify the signature.\n   *\n   * The `kid` must match the `kid` from decoded accessToken.\n   *\n   * By default, we pick the jwk from https://auth.commercelayer.io/.well-known/jwks.json using the `kid` from the accessToken.\n   */\n  jwk?: CommerceLayerJsonWebKey\n}\n\n/** JWKS in-memory cache. */\nconst JWKSCache: Record<string, CommerceLayerJsonWebKey | undefined> = {}\n\n/**\n * Get the `JsonWebKey` given a key identifier.\n * @param kid Key identifier.\n * @returns\n */\nasync function getJsonWebKey(\n  jwt: CommerceLayerJWT,\n): Promise<CommerceLayerJsonWebKey | undefined> {\n  const { kid } = jwt.header\n\n  if (JWKSCache[kid] != null) {\n    return JWKSCache[kid]\n  }\n\n  const keys = await getJsonWebKeys(jwt)\n\n  JWKSCache[kid] = keys.find((key) => key.kid === kid)\n\n  return JWKSCache[kid]\n}\n\n/**\n * Retrieve RSA public keys from our JWKS (JSON Web Key Set) endpoint.\n * @returns\n */\nasync function getJsonWebKeys(\n  jwt: CommerceLayerJWT,\n): Promise<CommerceLayerJsonWebKey[]> {\n  const jwksUrl = `${extractIssuer(jwt)}/.well-known/jwks.json`\n\n  const response = await fetch(jwksUrl).then<{\n    keys: CommerceLayerJsonWebKey[] | undefined\n  }>(async (res) => await res.json())\n\n  if (response.keys == null) {\n    throw new TokenError(\n      `Invalid jwks response from \"${jwksUrl}\": ${JSON.stringify(response)}`,\n    )\n  }\n\n  return response.keys\n}\n"],"mappings":"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"}