@commercengine/storefront-sdk 0.8.2 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -24,8 +24,8 @@ function createClient(clientOptions) {
24
24
  ...typeof globalQuerySerializer === "object" ? globalQuerySerializer : {},
25
25
  ...requestQuerySerializer
26
26
  });
27
- const serializedBody = body === void 0 ? void 0 : bodySerializer(body, mergeHeaders$1(baseHeaders, headers, params.header));
28
- const finalHeaders = mergeHeaders$1(serializedBody === void 0 || serializedBody instanceof FormData ? {} : { "Content-Type": "application/json" }, baseHeaders, headers, params.header);
27
+ const serializedBody = body === void 0 ? void 0 : bodySerializer(body, mergeHeaders(baseHeaders, headers, params.header));
28
+ const finalHeaders = mergeHeaders(serializedBody === void 0 || serializedBody instanceof FormData ? {} : { "Content-Type": "application/json" }, baseHeaders, headers, params.header);
29
29
  const requestInit = {
30
30
  redirect: "follow",
31
31
  ...baseOptions,
@@ -355,7 +355,7 @@ function createFinalURL(pathname, options) {
355
355
  if (search) finalURL += `?${search}`;
356
356
  return finalURL;
357
357
  }
358
- function mergeHeaders$1(...allHeaders) {
358
+ function mergeHeaders(...allHeaders) {
359
359
  const finalHeaders = new Headers();
360
360
  for (const h of allHeaders) {
361
361
  if (!h || typeof h !== "object") continue;
@@ -372,739 +372,916 @@ function removeTrailingSlash(url) {
372
372
  }
373
373
 
374
374
  //#endregion
375
- //#region ../../node_modules/.pnpm/jose@6.0.13/node_modules/jose/dist/webapi/lib/buffer_utils.js
376
- const encoder = new TextEncoder();
377
- const decoder = new TextDecoder();
378
- const MAX_INT32 = 2 ** 32;
379
-
380
- //#endregion
381
- //#region ../../node_modules/.pnpm/jose@6.0.13/node_modules/jose/dist/webapi/lib/base64.js
382
- function decodeBase64(encoded) {
383
- if (Uint8Array.fromBase64) return Uint8Array.fromBase64(encoded);
384
- const binary = atob(encoded);
385
- const bytes = new Uint8Array(binary.length);
386
- for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
387
- return bytes;
388
- }
389
-
390
- //#endregion
391
- //#region ../../node_modules/.pnpm/jose@6.0.13/node_modules/jose/dist/webapi/util/base64url.js
392
- function decode(input) {
393
- if (Uint8Array.fromBase64) return Uint8Array.fromBase64(typeof input === "string" ? input : decoder.decode(input), { alphabet: "base64url" });
394
- let encoded = input;
395
- if (encoded instanceof Uint8Array) encoded = decoder.decode(encoded);
396
- encoded = encoded.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, "");
397
- try {
398
- return decodeBase64(encoded);
399
- } catch {
400
- throw new TypeError("The input to be decoded is not correctly encoded.");
375
+ //#region ../sdk-core/dist/index.js
376
+ /**
377
+ * Response utilities for debugging and working with Response objects
378
+ */
379
+ var ResponseUtils = class {
380
+ /**
381
+ * Get response headers as a plain object
382
+ */
383
+ static getHeaders(response) {
384
+ return Object.fromEntries(response.headers.entries());
401
385
  }
402
- }
403
-
404
- //#endregion
405
- //#region ../../node_modules/.pnpm/jose@6.0.13/node_modules/jose/dist/webapi/util/errors.js
406
- var JOSEError = class extends Error {
407
- static code = "ERR_JOSE_GENERIC";
408
- code = "ERR_JOSE_GENERIC";
409
- constructor(message, options) {
410
- super(message, options);
411
- this.name = this.constructor.name;
412
- Error.captureStackTrace?.(this, this.constructor);
386
+ /**
387
+ * Get specific header value
388
+ */
389
+ static getHeader(response, name) {
390
+ return response.headers.get(name);
413
391
  }
414
- };
415
- var JWTInvalid = class extends JOSEError {
416
- static code = "ERR_JWT_INVALID";
417
- code = "ERR_JWT_INVALID";
418
- };
419
- var JWKSMultipleMatchingKeys = class extends JOSEError {
420
- [Symbol.asyncIterator];
421
- static code = "ERR_JWKS_MULTIPLE_MATCHING_KEYS";
422
- code = "ERR_JWKS_MULTIPLE_MATCHING_KEYS";
423
- constructor(message = "multiple matching keys found in the JSON Web Key Set", options) {
424
- super(message, options);
392
+ /**
393
+ * Check if response was successful
394
+ */
395
+ static isSuccess(response) {
396
+ return response.ok;
397
+ }
398
+ /**
399
+ * Get response metadata
400
+ */
401
+ static getMetadata(response) {
402
+ return {
403
+ status: response.status,
404
+ statusText: response.statusText,
405
+ ok: response.ok,
406
+ url: response.url,
407
+ redirected: response.redirected,
408
+ type: response.type,
409
+ headers: Object.fromEntries(response.headers.entries())
410
+ };
411
+ }
412
+ /**
413
+ * Clone and read response as text (useful for debugging)
414
+ * Note: This can only be called once per response
415
+ */
416
+ static async getText(response) {
417
+ const cloned = response.clone();
418
+ return await cloned.text();
419
+ }
420
+ /**
421
+ * Clone and read response as JSON (useful for debugging)
422
+ * Note: This can only be called once per response
423
+ */
424
+ static async getJSON(response) {
425
+ const cloned = response.clone();
426
+ return await cloned.json();
427
+ }
428
+ /**
429
+ * Format response information for debugging
430
+ */
431
+ static format(response) {
432
+ const metadata = this.getMetadata(response);
433
+ return `${metadata.status} ${metadata.statusText} - ${metadata.url}`;
434
+ }
435
+ /**
436
+ * Format response for logging purposes (enhanced version)
437
+ */
438
+ static formatResponse(response) {
439
+ return {
440
+ status: response.status,
441
+ statusText: response.statusText,
442
+ url: response.url,
443
+ ok: response.ok
444
+ };
425
445
  }
426
446
  };
427
-
428
- //#endregion
429
- //#region ../../node_modules/.pnpm/jose@6.0.13/node_modules/jose/dist/webapi/lib/is_object.js
430
- function isObjectLike(value) {
431
- return typeof value === "object" && value !== null;
432
- }
433
- var is_object_default = (input) => {
434
- if (!isObjectLike(input) || Object.prototype.toString.call(input) !== "[object Object]") return false;
435
- if (Object.getPrototypeOf(input) === null) return true;
436
- let proto = input;
437
- while (Object.getPrototypeOf(proto) !== null) proto = Object.getPrototypeOf(proto);
438
- return Object.getPrototypeOf(input) === proto;
439
- };
440
-
441
- //#endregion
442
- //#region ../../node_modules/.pnpm/jose@6.0.13/node_modules/jose/dist/webapi/util/decode_jwt.js
443
- function decodeJwt(jwt) {
444
- if (typeof jwt !== "string") throw new JWTInvalid("JWTs must use Compact JWS serialization, JWT must be a string");
445
- const { 1: payload, length } = jwt.split(".");
446
- if (length === 5) throw new JWTInvalid("Only JWTs using Compact JWS serialization can be decoded");
447
- if (length !== 3) throw new JWTInvalid("Invalid JWT");
448
- if (!payload) throw new JWTInvalid("JWTs must contain a payload");
449
- let decoded;
450
- try {
451
- decoded = decode(payload);
452
- } catch {
453
- throw new JWTInvalid("Failed to base64url decode the payload");
447
+ /**
448
+ * Debug logging utilities
449
+ */
450
+ var DebugLogger = class {
451
+ logger;
452
+ responseTextCache = /* @__PURE__ */ new Map();
453
+ constructor(logger) {
454
+ this.logger = logger || ((level, message, data) => {
455
+ console.log(`[${level.toUpperCase()}]`, message);
456
+ if (data) console.log(data);
457
+ });
454
458
  }
455
- let result;
456
- try {
457
- result = JSON.parse(decoder.decode(decoded));
458
- } catch {
459
- throw new JWTInvalid("Failed to parse the decoded payload as JSON");
459
+ /**
460
+ * Log debug information about API request
461
+ */
462
+ logRequest(request, requestBody) {
463
+ this.logger("info", "API Request Debug Info", {
464
+ method: request.method,
465
+ url: request.url,
466
+ headers: Object.fromEntries(request.headers.entries()),
467
+ body: requestBody,
468
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
469
+ });
460
470
  }
461
- if (!is_object_default(result)) throw new JWTInvalid("Invalid JWT Claims Set");
462
- return result;
463
- }
464
-
465
- //#endregion
466
- //#region src/lib/jwt-utils.ts
471
+ /**
472
+ * Log debug information about API response
473
+ */
474
+ async logResponse(response, responseBody) {
475
+ if (responseBody && typeof responseBody === "string") this.responseTextCache.set(response.url, responseBody);
476
+ this.logger("info", "API Response Debug Info", {
477
+ url: response.url,
478
+ status: response.status,
479
+ statusText: response.statusText,
480
+ ok: response.ok,
481
+ headers: Object.fromEntries(response.headers.entries()),
482
+ redirected: response.redirected,
483
+ type: response.type,
484
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
485
+ });
486
+ if (responseBody) this.logger("info", "API Response Data", {
487
+ data: responseBody,
488
+ contentType: response.headers.get("content-type"),
489
+ contentLength: response.headers.get("content-length")
490
+ });
491
+ }
492
+ /**
493
+ * Log error information
494
+ */
495
+ logError(message, error) {
496
+ this.logger("error", message, error);
497
+ }
498
+ /**
499
+ * Get cached response text for a URL (if available)
500
+ */
501
+ getCachedResponseText(url) {
502
+ return this.responseTextCache.get(url) || null;
503
+ }
504
+ /**
505
+ * Clear cached response texts
506
+ */
507
+ clearCache() {
508
+ this.responseTextCache.clear();
509
+ }
510
+ info(message, data) {
511
+ this.logger("info", message, data);
512
+ }
513
+ warn(message, data) {
514
+ this.logger("warn", message, data);
515
+ }
516
+ error(message, data) {
517
+ this.logger("error", message, data);
518
+ }
519
+ };
467
520
  /**
468
- * Decode and extract user information from a JWT token
469
- *
470
- * @param token - The JWT token to decode
471
- * @returns User information or null if token is invalid
521
+ * Extract request body for logging
472
522
  */
473
- function extractUserInfoFromToken(token) {
523
+ async function extractRequestBody(request) {
524
+ if (request.method === "GET" || request.method === "HEAD") return null;
474
525
  try {
475
- const payload = decodeJwt(token);
476
- return {
477
- id: payload.ulid,
478
- email: payload.email,
479
- phone: payload.phone,
480
- username: payload.username,
481
- firstName: payload.first_name,
482
- lastName: payload.last_name,
483
- storeId: payload.store_id,
484
- isLoggedIn: payload.is_logged_in,
485
- isAnonymous: !payload.is_logged_in,
486
- customerId: payload.customer_id,
487
- customerGroupId: payload.customer_group_id,
488
- anonymousId: payload.anonymous_id,
489
- tokenExpiry: /* @__PURE__ */ new Date(payload.exp * 1e3),
490
- tokenIssuedAt: /* @__PURE__ */ new Date(payload.iat * 1e3)
491
- };
526
+ const clonedRequest = request.clone();
527
+ const contentType = request.headers.get("content-type")?.toLowerCase();
528
+ if (contentType?.startsWith("application/json")) return await clonedRequest.json();
529
+ else if (contentType?.startsWith("multipart/form-data")) return "[FormData - cannot display]";
530
+ else if (contentType?.startsWith("text/")) return await clonedRequest.text();
531
+ return "[Request body - unknown format]";
492
532
  } catch (error) {
493
- console.warn("Failed to decode JWT token:", error);
494
- return null;
533
+ return "[Request body unavailable]";
495
534
  }
496
535
  }
497
536
  /**
498
- * Check if a JWT token is expired
499
- *
500
- * @param token - The JWT token to check
501
- * @param bufferSeconds - Buffer time in seconds (default: 30)
502
- * @returns True if token is expired or will expire within buffer time
537
+ * Create debug middleware for openapi-fetch (internal use)
538
+ * Enhanced version that combines original functionality with duration tracking
503
539
  */
504
- function isTokenExpired(token, bufferSeconds = 30) {
505
- try {
506
- const payload = decodeJwt(token);
507
- if (!payload.exp) return true;
508
- const currentTime = Math.floor(Date.now() / 1e3);
509
- const expiryTime = payload.exp;
510
- return currentTime >= expiryTime - bufferSeconds;
511
- } catch (error) {
512
- console.warn("Failed to decode JWT token:", error);
513
- return true;
514
- }
515
- }
516
- /**
517
- * Get the user ID from a JWT token
518
- *
519
- * @param token - The JWT token
520
- * @returns User ID (ulid) or null if token is invalid
521
- */
522
- function getUserIdFromToken(token) {
523
- const userInfo = extractUserInfoFromToken(token);
524
- return userInfo?.id || null;
540
+ function createDebugMiddleware(logger) {
541
+ const debugLogger = new DebugLogger(logger);
542
+ return {
543
+ async onRequest({ request }) {
544
+ request.__debugStartTime = Date.now();
545
+ const requestBody = await extractRequestBody(request);
546
+ debugLogger.logRequest(request, requestBody);
547
+ return request;
548
+ },
549
+ async onResponse({ request, response }) {
550
+ const startTime = request.__debugStartTime;
551
+ const duration = startTime ? Date.now() - startTime : 0;
552
+ const cloned = response.clone();
553
+ let responseBody = null;
554
+ try {
555
+ const contentType = response.headers.get("content-type")?.toLowerCase();
556
+ if (contentType?.startsWith("application/json")) responseBody = await cloned.json();
557
+ else if (contentType?.startsWith("text/")) responseBody = await cloned.text();
558
+ } catch (error) {}
559
+ await debugLogger.logResponse(response, responseBody);
560
+ if (duration > 0) debugLogger.info(`Request completed in ${duration}ms`, {
561
+ url: request.url,
562
+ method: request.method
563
+ });
564
+ return response;
565
+ },
566
+ async onError({ error, request }) {
567
+ debugLogger.logError("API Request Failed", {
568
+ error: {
569
+ name: error instanceof Error ? error.name : "Unknown",
570
+ message: error instanceof Error ? error.message : String(error),
571
+ stack: error instanceof Error ? error.stack : void 0
572
+ },
573
+ request: {
574
+ method: request.method,
575
+ url: request.url,
576
+ headers: Object.fromEntries(request.headers.entries())
577
+ },
578
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
579
+ });
580
+ throw error;
581
+ }
582
+ };
525
583
  }
526
584
  /**
527
- * Check if user is logged in based on JWT token
528
- *
529
- * @param token - The JWT token
530
- * @returns True if user is logged in, false otherwise
585
+ * Timeout middleware for Commerce Engine SDKs
531
586
  */
532
- function isUserLoggedIn(token) {
533
- const userInfo = extractUserInfoFromToken(token);
534
- return userInfo?.isLoggedIn || false;
535
- }
536
587
  /**
537
- * Check if user is anonymous based on JWT token
588
+ * Create timeout middleware for openapi-fetch
589
+ * Adds configurable request timeout functionality
538
590
  *
539
- * @param token - The JWT token
540
- * @returns True if user is anonymous, false otherwise
591
+ * @param timeoutMs - Timeout duration in milliseconds
592
+ * @returns Middleware object with onRequest handler
541
593
  */
542
- function isUserAnonymous(token) {
543
- const userInfo = extractUserInfoFromToken(token);
544
- return userInfo?.isAnonymous || true;
594
+ function createTimeoutMiddleware(timeoutMs) {
595
+ return { onRequest: async ({ request }) => {
596
+ const controller = new AbortController();
597
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
598
+ if (request.signal) request.signal.addEventListener("abort", () => controller.abort());
599
+ const newRequest = new Request(request, { signal: controller.signal });
600
+ controller.signal.addEventListener("abort", () => clearTimeout(timeoutId));
601
+ return newRequest;
602
+ } };
545
603
  }
546
-
547
- //#endregion
548
- //#region src/lib/auth-utils.ts
549
604
  /**
550
- * Extract pathname from URL
605
+ * Transform headers using a transformation mapping
606
+ * Headers not in the transformation map are passed through unchanged
607
+ *
608
+ * @param headers - Headers object with original names
609
+ * @param transformations - Mapping of original names to transformed names
610
+ * @returns Headers object with transformed names
551
611
  */
552
- function getPathnameFromUrl(url) {
553
- try {
554
- const urlObj = new URL(url);
555
- return urlObj.pathname;
556
- } catch {
557
- return url.split("?")[0];
612
+ function transformHeaders(headers, transformations) {
613
+ const transformed = {};
614
+ for (const [key, value] of Object.entries(headers)) if (value !== void 0) {
615
+ const headerName = transformations[key] || key;
616
+ transformed[headerName] = value;
558
617
  }
618
+ return transformed;
559
619
  }
560
620
  /**
561
- * Check if a URL path is an auth endpoint that should use API key
562
- */
563
- function isAnonymousAuthEndpoint(pathname) {
564
- return pathname.endsWith("/auth/anonymous");
565
- }
566
- /**
567
- * Check if a URL path is a login/register endpoint that returns tokens
621
+ * Merge headers with transformation support
622
+ * Transforms default headers, then merges with method headers
623
+ *
624
+ * @param defaultHeaders - Default headers from SDK configuration
625
+ * @param methodHeaders - Headers passed to the specific method call
626
+ * @param transformations - Mapping for header name transformations
627
+ * @returns Merged headers object with transformations applied
568
628
  */
569
- function isTokenReturningEndpoint(pathname) {
570
- const tokenEndpoints = [
571
- "/auth/login/password",
572
- "/auth/register/phone",
573
- "/auth/register/email",
574
- "/auth/verify-otp",
575
- "/auth/refresh-token"
576
- ];
577
- return tokenEndpoints.some((endpoint) => pathname.endsWith(endpoint));
629
+ function mergeAndTransformHeaders(defaultHeaders, methodHeaders, transformations) {
630
+ const merged = {};
631
+ if (defaultHeaders && transformations) {
632
+ const transformedDefaults = transformHeaders(defaultHeaders, transformations);
633
+ Object.assign(merged, transformedDefaults);
634
+ } else if (defaultHeaders) Object.assign(merged, defaultHeaders);
635
+ if (methodHeaders) Object.assign(merged, methodHeaders);
636
+ Object.keys(merged).forEach((key) => {
637
+ if (merged[key] === void 0) delete merged[key];
638
+ });
639
+ return merged;
578
640
  }
579
641
  /**
580
- * Check if a URL path is the logout endpoint
642
+ * Execute a request and handle the response consistently
643
+ * This provides unified error handling and response processing across all SDKs
644
+ *
645
+ * @param apiCall - Function that executes the API request
646
+ * @returns Promise with the API response in standardized format
581
647
  */
582
- function isLogoutEndpoint(pathname) {
583
- return pathname.endsWith("/auth/logout");
648
+ async function executeRequest(apiCall) {
649
+ try {
650
+ const { data, error, response } = await apiCall();
651
+ if (error) return {
652
+ data: null,
653
+ error,
654
+ response
655
+ };
656
+ if (data && data.content !== void 0) return {
657
+ data: data.content,
658
+ error: null,
659
+ response
660
+ };
661
+ return {
662
+ data,
663
+ error: null,
664
+ response
665
+ };
666
+ } catch (err) {
667
+ const mockResponse = new Response(null, {
668
+ status: 0,
669
+ statusText: "Network Error"
670
+ });
671
+ const errorResult = {
672
+ data: null,
673
+ error: {
674
+ success: false,
675
+ code: "NETWORK_ERROR",
676
+ message: "Network error occurred",
677
+ error: err
678
+ },
679
+ response: mockResponse
680
+ };
681
+ return errorResult;
682
+ }
584
683
  }
585
-
586
- //#endregion
587
- //#region src/lib/middleware.ts
588
684
  /**
589
- * Simple in-memory token storage implementation
685
+ * Generic base API client that all Commerce Engine SDKs can extend
686
+ * Handles common functionality like middleware setup, request execution, and header management
687
+ * Does NOT include token management - that's SDK-specific
688
+ *
689
+ * @template TPaths - OpenAPI paths type
690
+ * @template THeaders - Supported default headers type
590
691
  */
591
- var MemoryTokenStorage = class {
592
- accessToken = null;
593
- refreshToken = null;
594
- async getAccessToken() {
595
- return this.accessToken;
596
- }
597
- async setAccessToken(token) {
598
- this.accessToken = token;
599
- }
600
- async getRefreshToken() {
601
- return this.refreshToken;
602
- }
603
- async setRefreshToken(token) {
604
- this.refreshToken = token;
605
- }
606
- async clearTokens() {
607
- this.accessToken = null;
608
- this.refreshToken = null;
692
+ var BaseAPIClient = class {
693
+ client;
694
+ config;
695
+ baseUrl;
696
+ headerTransformations;
697
+ /**
698
+ * Create a new BaseAPIClient
699
+ *
700
+ * @param config - Configuration for the API client
701
+ * @param baseUrl - The base URL for the API (must be provided by subclass)
702
+ * @param headerTransformations - Header name transformations for this SDK
703
+ */
704
+ constructor(config, baseUrl, headerTransformations = {}) {
705
+ this.config = { ...config };
706
+ this.headerTransformations = headerTransformations;
707
+ this.baseUrl = baseUrl;
708
+ this.client = createClient({ baseUrl: this.baseUrl });
709
+ this.setupMiddleware();
609
710
  }
610
- };
611
- /**
612
- * Browser localStorage token storage implementation
613
- */
614
- var BrowserTokenStorage = class {
615
- accessTokenKey;
616
- refreshTokenKey;
617
- constructor(prefix = "storefront_") {
618
- this.accessTokenKey = `${prefix}access_token`;
619
- this.refreshTokenKey = `${prefix}refresh_token`;
711
+ /**
712
+ * Set up all middleware for the client
713
+ */
714
+ setupMiddleware() {
715
+ if (this.config.timeout) {
716
+ const timeoutMiddleware = createTimeoutMiddleware(this.config.timeout);
717
+ this.client.use(timeoutMiddleware);
718
+ }
719
+ if (this.config.debug) {
720
+ const debugMiddleware = createDebugMiddleware(this.config.logger);
721
+ this.client.use(debugMiddleware);
722
+ }
620
723
  }
621
- async getAccessToken() {
622
- if (typeof localStorage === "undefined") return null;
623
- return localStorage.getItem(this.accessTokenKey);
724
+ /**
725
+ * Get the base URL of the API
726
+ *
727
+ * @returns The base URL of the API
728
+ */
729
+ getBaseUrl() {
730
+ return this.baseUrl;
624
731
  }
625
- async setAccessToken(token) {
626
- if (typeof localStorage !== "undefined") localStorage.setItem(this.accessTokenKey, token);
732
+ /**
733
+ * Execute a request and handle the response consistently
734
+ * This provides unified error handling and response processing
735
+ *
736
+ * @param apiCall - Function that executes the API request
737
+ * @returns Promise with the API response in standardized format
738
+ */
739
+ async executeRequest(apiCall) {
740
+ return executeRequest(apiCall);
627
741
  }
628
- async getRefreshToken() {
629
- if (typeof localStorage === "undefined") return null;
630
- return localStorage.getItem(this.refreshTokenKey);
742
+ /**
743
+ * Merge default headers with method-level headers
744
+ * Method-level headers take precedence over default headers
745
+ * Automatically applies SDK-specific header transformations
746
+ *
747
+ * @param methodHeaders - Headers passed to the specific method call
748
+ * @returns Merged headers object with proper HTTP header names
749
+ */
750
+ mergeHeaders(methodHeaders) {
751
+ return mergeAndTransformHeaders(this.config.defaultHeaders, methodHeaders, this.headerTransformations);
631
752
  }
632
- async setRefreshToken(token) {
633
- if (typeof localStorage !== "undefined") localStorage.setItem(this.refreshTokenKey, token);
753
+ /**
754
+ * Set default headers for the client
755
+ *
756
+ * @param headers - Default headers to set
757
+ */
758
+ setDefaultHeaders(headers) {
759
+ this.config.defaultHeaders = headers;
634
760
  }
635
- async clearTokens() {
636
- if (typeof localStorage !== "undefined") {
637
- localStorage.removeItem(this.accessTokenKey);
638
- localStorage.removeItem(this.refreshTokenKey);
639
- }
761
+ /**
762
+ * Get current default headers
763
+ *
764
+ * @returns Current default headers
765
+ */
766
+ getDefaultHeaders() {
767
+ return this.config.defaultHeaders;
640
768
  }
641
769
  };
642
770
  /**
643
- * Cookie-based token storage implementation
771
+ * Generic URL utility functions for any SDK
644
772
  */
645
- var CookieTokenStorage = class {
646
- accessTokenKey;
647
- refreshTokenKey;
648
- options;
649
- constructor(options = {}) {
650
- const prefix = options.prefix || "storefront_";
651
- this.accessTokenKey = `${prefix}access_token`;
652
- this.refreshTokenKey = `${prefix}refresh_token`;
653
- this.options = {
654
- maxAge: options.maxAge || 10080 * 60,
655
- path: options.path || "/",
656
- domain: options.domain,
657
- secure: options.secure ?? (typeof window !== "undefined" && window.location?.protocol === "https:"),
658
- sameSite: options.sameSite || "Lax",
659
- httpOnly: false
660
- };
661
- }
662
- async getAccessToken() {
663
- return this.getCookie(this.accessTokenKey);
664
- }
665
- async setAccessToken(token) {
666
- this.setCookie(this.accessTokenKey, token);
667
- }
668
- async getRefreshToken() {
669
- return this.getCookie(this.refreshTokenKey);
773
+ /**
774
+ * Extract pathname from URL
775
+ * Useful for middleware that needs to inspect request paths
776
+ */
777
+ function getPathnameFromUrl(url) {
778
+ try {
779
+ const urlObj = new URL(url);
780
+ return urlObj.pathname;
781
+ } catch {
782
+ return url.split("?")[0] || url;
670
783
  }
671
- async setRefreshToken(token) {
672
- this.setCookie(this.refreshTokenKey, token);
784
+ }
785
+
786
+ //#endregion
787
+ //#region ../../node_modules/.pnpm/jose@6.0.13/node_modules/jose/dist/webapi/lib/buffer_utils.js
788
+ const encoder = new TextEncoder();
789
+ const decoder = new TextDecoder();
790
+ const MAX_INT32 = 2 ** 32;
791
+
792
+ //#endregion
793
+ //#region ../../node_modules/.pnpm/jose@6.0.13/node_modules/jose/dist/webapi/lib/base64.js
794
+ function decodeBase64(encoded) {
795
+ if (Uint8Array.fromBase64) return Uint8Array.fromBase64(encoded);
796
+ const binary = atob(encoded);
797
+ const bytes = new Uint8Array(binary.length);
798
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
799
+ return bytes;
800
+ }
801
+
802
+ //#endregion
803
+ //#region ../../node_modules/.pnpm/jose@6.0.13/node_modules/jose/dist/webapi/util/base64url.js
804
+ function decode(input) {
805
+ if (Uint8Array.fromBase64) return Uint8Array.fromBase64(typeof input === "string" ? input : decoder.decode(input), { alphabet: "base64url" });
806
+ let encoded = input;
807
+ if (encoded instanceof Uint8Array) encoded = decoder.decode(encoded);
808
+ encoded = encoded.replace(/-/g, "+").replace(/_/g, "/").replace(/\s/g, "");
809
+ try {
810
+ return decodeBase64(encoded);
811
+ } catch {
812
+ throw new TypeError("The input to be decoded is not correctly encoded.");
673
813
  }
674
- async clearTokens() {
675
- this.deleteCookie(this.accessTokenKey);
676
- this.deleteCookie(this.refreshTokenKey);
814
+ }
815
+
816
+ //#endregion
817
+ //#region ../../node_modules/.pnpm/jose@6.0.13/node_modules/jose/dist/webapi/util/errors.js
818
+ var JOSEError = class extends Error {
819
+ static code = "ERR_JOSE_GENERIC";
820
+ code = "ERR_JOSE_GENERIC";
821
+ constructor(message, options) {
822
+ super(message, options);
823
+ this.name = this.constructor.name;
824
+ Error.captureStackTrace?.(this, this.constructor);
677
825
  }
678
- getCookie(name) {
679
- if (typeof document === "undefined") return null;
680
- const value = `; ${document.cookie}`;
681
- const parts = value.split(`; ${name}=`);
682
- if (parts.length === 2) {
683
- const cookieValue = parts.pop()?.split(";").shift();
684
- return cookieValue ? decodeURIComponent(cookieValue) : null;
685
- }
686
- return null;
826
+ };
827
+ var JWTInvalid = class extends JOSEError {
828
+ static code = "ERR_JWT_INVALID";
829
+ code = "ERR_JWT_INVALID";
830
+ };
831
+ var JWKSMultipleMatchingKeys = class extends JOSEError {
832
+ [Symbol.asyncIterator];
833
+ static code = "ERR_JWKS_MULTIPLE_MATCHING_KEYS";
834
+ code = "ERR_JWKS_MULTIPLE_MATCHING_KEYS";
835
+ constructor(message = "multiple matching keys found in the JSON Web Key Set", options) {
836
+ super(message, options);
687
837
  }
688
- setCookie(name, value) {
689
- if (typeof document === "undefined") return;
690
- const encodedValue = encodeURIComponent(value);
691
- let cookieString = `${name}=${encodedValue}`;
692
- if (this.options.maxAge) cookieString += `; Max-Age=${this.options.maxAge}`;
693
- if (this.options.path) cookieString += `; Path=${this.options.path}`;
694
- if (this.options.domain) cookieString += `; Domain=${this.options.domain}`;
695
- if (this.options.secure) cookieString += `; Secure`;
696
- if (this.options.sameSite) cookieString += `; SameSite=${this.options.sameSite}`;
697
- document.cookie = cookieString;
838
+ };
839
+
840
+ //#endregion
841
+ //#region ../../node_modules/.pnpm/jose@6.0.13/node_modules/jose/dist/webapi/lib/is_object.js
842
+ function isObjectLike(value) {
843
+ return typeof value === "object" && value !== null;
844
+ }
845
+ var is_object_default = (input) => {
846
+ if (!isObjectLike(input) || Object.prototype.toString.call(input) !== "[object Object]") return false;
847
+ if (Object.getPrototypeOf(input) === null) return true;
848
+ let proto = input;
849
+ while (Object.getPrototypeOf(proto) !== null) proto = Object.getPrototypeOf(proto);
850
+ return Object.getPrototypeOf(input) === proto;
851
+ };
852
+
853
+ //#endregion
854
+ //#region ../../node_modules/.pnpm/jose@6.0.13/node_modules/jose/dist/webapi/util/decode_jwt.js
855
+ function decodeJwt(jwt) {
856
+ if (typeof jwt !== "string") throw new JWTInvalid("JWTs must use Compact JWS serialization, JWT must be a string");
857
+ const { 1: payload, length } = jwt.split(".");
858
+ if (length === 5) throw new JWTInvalid("Only JWTs using Compact JWS serialization can be decoded");
859
+ if (length !== 3) throw new JWTInvalid("Invalid JWT");
860
+ if (!payload) throw new JWTInvalid("JWTs must contain a payload");
861
+ let decoded;
862
+ try {
863
+ decoded = decode(payload);
864
+ } catch {
865
+ throw new JWTInvalid("Failed to base64url decode the payload");
698
866
  }
699
- deleteCookie(name) {
700
- if (typeof document === "undefined") return;
701
- let cookieString = `${name}=; Max-Age=0`;
702
- if (this.options.path) cookieString += `; Path=${this.options.path}`;
703
- if (this.options.domain) cookieString += `; Domain=${this.options.domain}`;
704
- document.cookie = cookieString;
867
+ let result;
868
+ try {
869
+ result = JSON.parse(decoder.decode(decoded));
870
+ } catch {
871
+ throw new JWTInvalid("Failed to parse the decoded payload as JSON");
705
872
  }
706
- };
873
+ if (!is_object_default(result)) throw new JWTInvalid("Invalid JWT Claims Set");
874
+ return result;
875
+ }
876
+
877
+ //#endregion
878
+ //#region src/lib/jwt-utils.ts
707
879
  /**
708
- * Create authentication middleware for openapi-fetch
880
+ * Decode and extract user information from a JWT token
881
+ *
882
+ * @param token - The JWT token to decode
883
+ * @returns User information or null if token is invalid
709
884
  */
710
- function createAuthMiddleware(config) {
711
- let isRefreshing = false;
712
- let refreshPromise = null;
713
- let hasAssessedTokens = false;
714
- const assessTokenStateOnce = async () => {
715
- if (hasAssessedTokens) return;
716
- hasAssessedTokens = true;
717
- try {
718
- const accessToken = await config.tokenStorage.getAccessToken();
719
- const refreshToken = await config.tokenStorage.getRefreshToken();
720
- if (!accessToken && refreshToken) {
721
- await config.tokenStorage.clearTokens();
722
- console.info("Cleaned up orphaned refresh token");
723
- }
724
- } catch (error) {
725
- console.warn("Token state assessment failed:", error);
726
- }
727
- };
728
- const refreshTokens = async () => {
729
- if (isRefreshing && refreshPromise) return refreshPromise;
730
- isRefreshing = true;
731
- refreshPromise = (async () => {
732
- try {
733
- const refreshToken = await config.tokenStorage.getRefreshToken();
734
- let newTokens;
735
- if (refreshToken && !isTokenExpired(refreshToken)) if (config.refreshTokenFn) newTokens = await config.refreshTokenFn(refreshToken);
736
- else {
737
- const response = await fetch(`${config.baseUrl}/auth/refresh-token`, {
738
- method: "POST",
739
- headers: { "Content-Type": "application/json" },
740
- body: JSON.stringify({ refresh_token: refreshToken })
741
- });
742
- if (!response.ok) throw new Error(`Token refresh failed: ${response.status}`);
743
- const data = await response.json();
744
- newTokens = data.content;
745
- }
746
- else {
747
- const currentAccessToken = await config.tokenStorage.getAccessToken();
748
- if (!currentAccessToken) throw new Error("No tokens available for refresh");
749
- const reason = refreshToken ? "refresh token expired" : "no refresh token available";
750
- const response = await fetch(`${config.baseUrl}/auth/anonymous`, {
751
- method: "POST",
752
- headers: {
753
- "Content-Type": "application/json",
754
- ...config.apiKey && { "X-Api-Key": config.apiKey },
755
- Authorization: `Bearer ${currentAccessToken}`
756
- }
757
- });
758
- if (!response.ok) throw new Error(`Anonymous token fallback failed: ${response.status}`);
759
- const data = await response.json();
760
- newTokens = data.content;
761
- console.info(`Token refreshed via anonymous fallback (${reason}) - user may need to re-authenticate for privileged operations`);
762
- }
763
- await config.tokenStorage.setAccessToken(newTokens.access_token);
764
- await config.tokenStorage.setRefreshToken(newTokens.refresh_token);
765
- config.onTokensUpdated?.(newTokens.access_token, newTokens.refresh_token);
766
- } catch (error) {
767
- console.error("Token refresh failed:", error);
768
- await config.tokenStorage.clearTokens();
769
- config.onTokensCleared?.();
770
- throw error;
771
- } finally {
772
- isRefreshing = false;
773
- refreshPromise = null;
774
- }
775
- })();
776
- return refreshPromise;
777
- };
778
- return {
779
- async onRequest({ request }) {
780
- const pathname = getPathnameFromUrl(request.url);
781
- await assessTokenStateOnce();
782
- if (isAnonymousAuthEndpoint(pathname)) {
783
- if (config.apiKey) request.headers.set("X-Api-Key", config.apiKey);
784
- const existingToken = await config.tokenStorage.getAccessToken();
785
- if (existingToken && !isTokenExpired(existingToken) && isUserLoggedIn(existingToken)) return new Response(JSON.stringify({
786
- message: "Cannot create anonymous session while authenticated",
787
- success: false,
788
- code: "USER_ALREADY_AUTHENTICATED"
789
- }), {
790
- status: 400,
791
- headers: { "Content-Type": "application/json" }
792
- });
793
- if (existingToken) request.headers.set("Authorization", `Bearer ${existingToken}`);
794
- return request;
795
- }
796
- let accessToken = await config.tokenStorage.getAccessToken();
797
- if (!accessToken) try {
798
- const response = await fetch(`${config.baseUrl}/auth/anonymous`, {
799
- method: "POST",
800
- headers: {
801
- "Content-Type": "application/json",
802
- ...config.apiKey && { "X-Api-Key": config.apiKey }
803
- }
804
- });
805
- if (response.ok) {
806
- const data = await response.json();
807
- const tokens = data.content;
808
- if (tokens?.access_token && tokens?.refresh_token) {
809
- await config.tokenStorage.setAccessToken(tokens.access_token);
810
- await config.tokenStorage.setRefreshToken(tokens.refresh_token);
811
- accessToken = tokens.access_token;
812
- config.onTokensUpdated?.(tokens.access_token, tokens.refresh_token);
813
- console.info("Automatically created anonymous session for first API request");
814
- }
815
- }
816
- } catch (error) {
817
- console.warn("Failed to automatically create anonymous tokens:", error);
818
- }
819
- if (accessToken && isTokenExpired(accessToken)) try {
820
- await refreshTokens();
821
- accessToken = await config.tokenStorage.getAccessToken();
822
- } catch (error) {}
823
- if (accessToken) request.headers.set("Authorization", `Bearer ${accessToken}`);
824
- return request;
825
- },
826
- async onResponse({ request, response }) {
827
- const pathname = getPathnameFromUrl(request.url);
828
- if (response.ok) {
829
- if (isTokenReturningEndpoint(pathname) || isAnonymousAuthEndpoint(pathname)) try {
830
- const data = await response.clone().json();
831
- const content = data.content;
832
- if (content?.access_token && content?.refresh_token) {
833
- await config.tokenStorage.setAccessToken(content.access_token);
834
- await config.tokenStorage.setRefreshToken(content.refresh_token);
835
- config.onTokensUpdated?.(content.access_token, content.refresh_token);
836
- }
837
- } catch (error) {
838
- console.warn("Failed to extract tokens from response:", error);
839
- }
840
- else if (isLogoutEndpoint(pathname)) {
841
- await config.tokenStorage.clearTokens();
842
- config.onTokensCleared?.();
843
- }
844
- }
845
- if (response.status === 401 && !isAnonymousAuthEndpoint(pathname)) {
846
- const currentToken = await config.tokenStorage.getAccessToken();
847
- if (currentToken && isTokenExpired(currentToken, 0)) try {
848
- await refreshTokens();
849
- const newToken = await config.tokenStorage.getAccessToken();
850
- if (newToken) {
851
- const retryRequest = request.clone();
852
- retryRequest.headers.set("Authorization", `Bearer ${newToken}`);
853
- return fetch(retryRequest);
854
- }
855
- } catch (error) {
856
- console.warn("Token refresh failed on 401 response:", error);
857
- }
858
- }
859
- return response;
860
- }
861
- };
885
+ function extractUserInfoFromToken(token) {
886
+ try {
887
+ const payload = decodeJwt(token);
888
+ return {
889
+ id: payload.ulid,
890
+ email: payload.email,
891
+ phone: payload.phone,
892
+ username: payload.username,
893
+ firstName: payload.first_name,
894
+ lastName: payload.last_name,
895
+ storeId: payload.store_id,
896
+ isLoggedIn: payload.is_logged_in,
897
+ isAnonymous: !payload.is_logged_in,
898
+ customerId: payload.customer_id,
899
+ customerGroupId: payload.customer_group_id,
900
+ anonymousId: payload.anonymous_id,
901
+ tokenExpiry: /* @__PURE__ */ new Date(payload.exp * 1e3),
902
+ tokenIssuedAt: /* @__PURE__ */ new Date(payload.iat * 1e3)
903
+ };
904
+ } catch (error) {
905
+ console.warn("Failed to decode JWT token:", error);
906
+ return null;
907
+ }
862
908
  }
863
909
  /**
864
- * Helper function to create auth middleware with sensible defaults
910
+ * Check if a JWT token is expired
911
+ *
912
+ * @param token - The JWT token to check
913
+ * @param bufferSeconds - Buffer time in seconds (default: 30)
914
+ * @returns True if token is expired or will expire within buffer time
915
+ */
916
+ function isTokenExpired(token, bufferSeconds = 30) {
917
+ try {
918
+ const payload = decodeJwt(token);
919
+ if (!payload.exp) return true;
920
+ const currentTime = Math.floor(Date.now() / 1e3);
921
+ const expiryTime = payload.exp;
922
+ return currentTime >= expiryTime - bufferSeconds;
923
+ } catch (error) {
924
+ console.warn("Failed to decode JWT token:", error);
925
+ return true;
926
+ }
927
+ }
928
+ /**
929
+ * Get the user ID from a JWT token
930
+ *
931
+ * @param token - The JWT token
932
+ * @returns User ID (ulid) or null if token is invalid
933
+ */
934
+ function getUserIdFromToken(token) {
935
+ const userInfo = extractUserInfoFromToken(token);
936
+ return userInfo?.id || null;
937
+ }
938
+ /**
939
+ * Check if user is logged in based on JWT token
940
+ *
941
+ * @param token - The JWT token
942
+ * @returns True if user is logged in, false otherwise
943
+ */
944
+ function isUserLoggedIn(token) {
945
+ const userInfo = extractUserInfoFromToken(token);
946
+ return userInfo?.isLoggedIn || false;
947
+ }
948
+ /**
949
+ * Check if user is anonymous based on JWT token
950
+ *
951
+ * @param token - The JWT token
952
+ * @returns True if user is anonymous, false otherwise
953
+ */
954
+ function isUserAnonymous(token) {
955
+ const userInfo = extractUserInfoFromToken(token);
956
+ return userInfo?.isAnonymous || true;
957
+ }
958
+
959
+ //#endregion
960
+ //#region src/lib/auth-utils.ts
961
+ /**
962
+ * Check if a URL path is an auth endpoint that should use API key
963
+ */
964
+ function isAnonymousAuthEndpoint(pathname) {
965
+ return pathname.endsWith("/auth/anonymous");
966
+ }
967
+ /**
968
+ * Check if a URL path is a login/register endpoint that returns tokens
969
+ */
970
+ function isTokenReturningEndpoint(pathname) {
971
+ const tokenEndpoints = [
972
+ "/auth/login/password",
973
+ "/auth/register/phone",
974
+ "/auth/register/email",
975
+ "/auth/verify-otp",
976
+ "/auth/refresh-token"
977
+ ];
978
+ return tokenEndpoints.some((endpoint) => pathname.endsWith(endpoint));
979
+ }
980
+ /**
981
+ * Check if a URL path is the logout endpoint
865
982
  */
866
- function createDefaultAuthMiddleware(options) {
867
- const tokenStorage = options.tokenStorage || (typeof localStorage !== "undefined" ? new BrowserTokenStorage() : new MemoryTokenStorage());
868
- return createAuthMiddleware({
869
- tokenStorage,
870
- apiKey: options.apiKey,
871
- baseUrl: options.baseUrl,
872
- onTokensUpdated: options.onTokensUpdated,
873
- onTokensCleared: options.onTokensCleared
874
- });
983
+ function isLogoutEndpoint(pathname) {
984
+ return pathname.endsWith("/auth/logout");
875
985
  }
876
986
 
877
987
  //#endregion
878
- //#region src/lib/logger-utils.ts
988
+ //#region src/lib/middleware.ts
879
989
  /**
880
- * Response utilities for debugging and working with Response objects
990
+ * Simple in-memory token storage implementation
881
991
  */
882
- var ResponseUtils = class {
883
- /**
884
- * Get response headers as a plain object
885
- */
886
- static getHeaders(response) {
887
- return Object.fromEntries(response.headers.entries());
888
- }
889
- /**
890
- * Get specific header value
891
- */
892
- static getHeader(response, name) {
893
- return response.headers.get(name);
894
- }
895
- /**
896
- * Check if response was successful
897
- */
898
- static isSuccess(response) {
899
- return response.ok;
992
+ var MemoryTokenStorage = class {
993
+ accessToken = null;
994
+ refreshToken = null;
995
+ async getAccessToken() {
996
+ return this.accessToken;
900
997
  }
901
- /**
902
- * Get response metadata
903
- */
904
- static getMetadata(response) {
905
- return {
906
- status: response.status,
907
- statusText: response.statusText,
908
- ok: response.ok,
909
- url: response.url,
910
- redirected: response.redirected,
911
- type: response.type,
912
- headers: Object.fromEntries(response.headers.entries())
913
- };
998
+ async setAccessToken(token) {
999
+ this.accessToken = token;
914
1000
  }
915
- /**
916
- * Clone and read response as text (useful for debugging)
917
- * Note: This can only be called once per response
918
- */
919
- static async getText(response) {
920
- const cloned = response.clone();
921
- return await cloned.text();
1001
+ async getRefreshToken() {
1002
+ return this.refreshToken;
922
1003
  }
923
- /**
924
- * Clone and read response as JSON (useful for debugging)
925
- * Note: This can only be called once per response
926
- */
927
- static async getJSON(response) {
928
- const cloned = response.clone();
929
- return await cloned.json();
1004
+ async setRefreshToken(token) {
1005
+ this.refreshToken = token;
930
1006
  }
931
- /**
932
- * Format response information for debugging
933
- */
934
- static format(response) {
935
- const metadata = this.getMetadata(response);
936
- return `${metadata.status} ${metadata.statusText} - ${metadata.url}`;
1007
+ async clearTokens() {
1008
+ this.accessToken = null;
1009
+ this.refreshToken = null;
937
1010
  }
938
1011
  };
939
1012
  /**
940
- * Debug logging utilities
1013
+ * Browser localStorage token storage implementation
941
1014
  */
942
- var DebugLogger = class {
943
- logger;
944
- responseTextCache = /* @__PURE__ */ new Map();
945
- constructor(logger) {
946
- this.logger = logger || ((level, message, data) => {
947
- console.log(`[${level.toUpperCase()}]`, message);
948
- if (data) console.log(data);
949
- });
1015
+ var BrowserTokenStorage = class {
1016
+ accessTokenKey;
1017
+ refreshTokenKey;
1018
+ constructor(prefix = "storefront_") {
1019
+ this.accessTokenKey = `${prefix}access_token`;
1020
+ this.refreshTokenKey = `${prefix}refresh_token`;
950
1021
  }
951
- /**
952
- * Log debug information about API request
953
- */
954
- logRequest(request, requestBody) {
955
- this.logger("info", "API Request Debug Info", {
956
- method: request.method,
957
- url: request.url,
958
- headers: Object.fromEntries(request.headers.entries()),
959
- body: requestBody,
960
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
961
- });
1022
+ async getAccessToken() {
1023
+ if (typeof localStorage === "undefined") return null;
1024
+ return localStorage.getItem(this.accessTokenKey);
962
1025
  }
963
- /**
964
- * Log debug information about API response
965
- */
966
- async logResponse(response, responseBody) {
967
- if (responseBody && typeof responseBody === "string") this.responseTextCache.set(response.url, responseBody);
968
- this.logger("info", "API Response Debug Info", {
969
- url: response.url,
970
- status: response.status,
971
- statusText: response.statusText,
972
- ok: response.ok,
973
- headers: Object.fromEntries(response.headers.entries()),
974
- redirected: response.redirected,
975
- type: response.type,
976
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
977
- });
978
- if (responseBody) this.logger("info", "API Response Data", {
979
- data: responseBody,
980
- contentType: response.headers.get("content-type"),
981
- contentLength: response.headers.get("content-length")
982
- });
1026
+ async setAccessToken(token) {
1027
+ if (typeof localStorage !== "undefined") localStorage.setItem(this.accessTokenKey, token);
983
1028
  }
984
- /**
985
- * Log error information
986
- */
987
- logError(message, error) {
988
- this.logger("error", message, error);
1029
+ async getRefreshToken() {
1030
+ if (typeof localStorage === "undefined") return null;
1031
+ return localStorage.getItem(this.refreshTokenKey);
989
1032
  }
990
- /**
991
- * Get cached response text for a URL (if available)
992
- */
993
- getCachedResponseText(url) {
994
- return this.responseTextCache.get(url) || null;
1033
+ async setRefreshToken(token) {
1034
+ if (typeof localStorage !== "undefined") localStorage.setItem(this.refreshTokenKey, token);
995
1035
  }
996
- /**
997
- * Clear cached response texts
998
- */
999
- clearCache() {
1000
- this.responseTextCache.clear();
1036
+ async clearTokens() {
1037
+ if (typeof localStorage !== "undefined") {
1038
+ localStorage.removeItem(this.accessTokenKey);
1039
+ localStorage.removeItem(this.refreshTokenKey);
1040
+ }
1001
1041
  }
1002
1042
  };
1003
1043
  /**
1004
- * Extract request body for logging
1044
+ * Cookie-based token storage implementation
1005
1045
  */
1006
- async function extractRequestBody(request) {
1007
- if (request.method === "GET" || request.method === "HEAD") return null;
1008
- try {
1009
- const clonedRequest = request.clone();
1010
- const contentType = request.headers.get("content-type")?.toLowerCase();
1011
- if (contentType?.startsWith("application/json")) return await clonedRequest.json();
1012
- else if (contentType?.startsWith("multipart/form-data")) return "[FormData - cannot display]";
1013
- else if (contentType?.startsWith("text/")) return await clonedRequest.text();
1014
- return "[Request body - unknown format]";
1015
- } catch (error) {
1016
- return "[Request body unavailable]";
1046
+ var CookieTokenStorage = class {
1047
+ accessTokenKey;
1048
+ refreshTokenKey;
1049
+ options;
1050
+ constructor(options = {}) {
1051
+ const prefix = options.prefix || "storefront_";
1052
+ this.accessTokenKey = `${prefix}access_token`;
1053
+ this.refreshTokenKey = `${prefix}refresh_token`;
1054
+ this.options = {
1055
+ maxAge: options.maxAge || 10080 * 60,
1056
+ path: options.path || "/",
1057
+ domain: options.domain,
1058
+ secure: options.secure ?? (typeof window !== "undefined" && window.location?.protocol === "https:"),
1059
+ sameSite: options.sameSite || "Lax",
1060
+ httpOnly: false
1061
+ };
1017
1062
  }
1018
- }
1063
+ async getAccessToken() {
1064
+ return this.getCookie(this.accessTokenKey);
1065
+ }
1066
+ async setAccessToken(token) {
1067
+ this.setCookie(this.accessTokenKey, token);
1068
+ }
1069
+ async getRefreshToken() {
1070
+ return this.getCookie(this.refreshTokenKey);
1071
+ }
1072
+ async setRefreshToken(token) {
1073
+ this.setCookie(this.refreshTokenKey, token);
1074
+ }
1075
+ async clearTokens() {
1076
+ this.deleteCookie(this.accessTokenKey);
1077
+ this.deleteCookie(this.refreshTokenKey);
1078
+ }
1079
+ getCookie(name) {
1080
+ if (typeof document === "undefined") return null;
1081
+ const value = `; ${document.cookie}`;
1082
+ const parts = value.split(`; ${name}=`);
1083
+ if (parts.length === 2) {
1084
+ const cookieValue = parts.pop()?.split(";").shift();
1085
+ return cookieValue ? decodeURIComponent(cookieValue) : null;
1086
+ }
1087
+ return null;
1088
+ }
1089
+ setCookie(name, value) {
1090
+ if (typeof document === "undefined") return;
1091
+ const encodedValue = encodeURIComponent(value);
1092
+ let cookieString = `${name}=${encodedValue}`;
1093
+ if (this.options.maxAge) cookieString += `; Max-Age=${this.options.maxAge}`;
1094
+ if (this.options.path) cookieString += `; Path=${this.options.path}`;
1095
+ if (this.options.domain) cookieString += `; Domain=${this.options.domain}`;
1096
+ if (this.options.secure) cookieString += `; Secure`;
1097
+ if (this.options.sameSite) cookieString += `; SameSite=${this.options.sameSite}`;
1098
+ document.cookie = cookieString;
1099
+ }
1100
+ deleteCookie(name) {
1101
+ if (typeof document === "undefined") return;
1102
+ let cookieString = `${name}=; Max-Age=0`;
1103
+ if (this.options.path) cookieString += `; Path=${this.options.path}`;
1104
+ if (this.options.domain) cookieString += `; Domain=${this.options.domain}`;
1105
+ document.cookie = cookieString;
1106
+ }
1107
+ };
1019
1108
  /**
1020
- * Create debug middleware for openapi-fetch (internal use)
1109
+ * Create authentication middleware for openapi-fetch
1021
1110
  */
1022
- function createDebugMiddleware(logger) {
1023
- const debugLogger = new DebugLogger(logger);
1111
+ function createAuthMiddleware(config) {
1112
+ let isRefreshing = false;
1113
+ let refreshPromise = null;
1114
+ let hasAssessedTokens = false;
1115
+ const assessTokenStateOnce = async () => {
1116
+ if (hasAssessedTokens) return;
1117
+ hasAssessedTokens = true;
1118
+ try {
1119
+ const accessToken = await config.tokenStorage.getAccessToken();
1120
+ const refreshToken = await config.tokenStorage.getRefreshToken();
1121
+ if (!accessToken && refreshToken) {
1122
+ await config.tokenStorage.clearTokens();
1123
+ console.info("Cleaned up orphaned refresh token");
1124
+ }
1125
+ } catch (error) {
1126
+ console.warn("Token state assessment failed:", error);
1127
+ }
1128
+ };
1129
+ const refreshTokens = async () => {
1130
+ if (isRefreshing && refreshPromise) return refreshPromise;
1131
+ isRefreshing = true;
1132
+ refreshPromise = (async () => {
1133
+ try {
1134
+ const refreshToken = await config.tokenStorage.getRefreshToken();
1135
+ let newTokens;
1136
+ if (refreshToken && !isTokenExpired(refreshToken)) if (config.refreshTokenFn) newTokens = await config.refreshTokenFn(refreshToken);
1137
+ else {
1138
+ const response = await fetch(`${config.baseUrl}/auth/refresh-token`, {
1139
+ method: "POST",
1140
+ headers: { "Content-Type": "application/json" },
1141
+ body: JSON.stringify({ refresh_token: refreshToken })
1142
+ });
1143
+ if (!response.ok) throw new Error(`Token refresh failed: ${response.status}`);
1144
+ const data = await response.json();
1145
+ newTokens = data.content;
1146
+ }
1147
+ else {
1148
+ const currentAccessToken = await config.tokenStorage.getAccessToken();
1149
+ if (!currentAccessToken) throw new Error("No tokens available for refresh");
1150
+ const reason = refreshToken ? "refresh token expired" : "no refresh token available";
1151
+ const response = await fetch(`${config.baseUrl}/auth/anonymous`, {
1152
+ method: "POST",
1153
+ headers: {
1154
+ "Content-Type": "application/json",
1155
+ ...config.apiKey && { "X-Api-Key": config.apiKey },
1156
+ Authorization: `Bearer ${currentAccessToken}`
1157
+ }
1158
+ });
1159
+ if (!response.ok) throw new Error(`Anonymous token fallback failed: ${response.status}`);
1160
+ const data = await response.json();
1161
+ newTokens = data.content;
1162
+ console.info(`Token refreshed via anonymous fallback (${reason}) - user may need to re-authenticate for privileged operations`);
1163
+ }
1164
+ await config.tokenStorage.setAccessToken(newTokens.access_token);
1165
+ await config.tokenStorage.setRefreshToken(newTokens.refresh_token);
1166
+ config.onTokensUpdated?.(newTokens.access_token, newTokens.refresh_token);
1167
+ } catch (error) {
1168
+ console.error("Token refresh failed:", error);
1169
+ await config.tokenStorage.clearTokens();
1170
+ config.onTokensCleared?.();
1171
+ throw error;
1172
+ } finally {
1173
+ isRefreshing = false;
1174
+ refreshPromise = null;
1175
+ }
1176
+ })();
1177
+ return refreshPromise;
1178
+ };
1024
1179
  return {
1025
1180
  async onRequest({ request }) {
1026
- const requestBody = await extractRequestBody(request);
1027
- debugLogger.logRequest(request, requestBody);
1181
+ const pathname = getPathnameFromUrl(request.url);
1182
+ await assessTokenStateOnce();
1183
+ if (isAnonymousAuthEndpoint(pathname)) {
1184
+ if (config.apiKey) request.headers.set("X-Api-Key", config.apiKey);
1185
+ const existingToken = await config.tokenStorage.getAccessToken();
1186
+ if (existingToken && !isTokenExpired(existingToken) && isUserLoggedIn(existingToken)) return new Response(JSON.stringify({
1187
+ message: "Cannot create anonymous session while authenticated",
1188
+ success: false,
1189
+ code: "USER_ALREADY_AUTHENTICATED"
1190
+ }), {
1191
+ status: 400,
1192
+ headers: { "Content-Type": "application/json" }
1193
+ });
1194
+ if (existingToken) request.headers.set("Authorization", `Bearer ${existingToken}`);
1195
+ return request;
1196
+ }
1197
+ let accessToken = await config.tokenStorage.getAccessToken();
1198
+ if (!accessToken) try {
1199
+ const response = await fetch(`${config.baseUrl}/auth/anonymous`, {
1200
+ method: "POST",
1201
+ headers: {
1202
+ "Content-Type": "application/json",
1203
+ ...config.apiKey && { "X-Api-Key": config.apiKey }
1204
+ }
1205
+ });
1206
+ if (response.ok) {
1207
+ const data = await response.json();
1208
+ const tokens = data.content;
1209
+ if (tokens?.access_token && tokens?.refresh_token) {
1210
+ await config.tokenStorage.setAccessToken(tokens.access_token);
1211
+ await config.tokenStorage.setRefreshToken(tokens.refresh_token);
1212
+ accessToken = tokens.access_token;
1213
+ config.onTokensUpdated?.(tokens.access_token, tokens.refresh_token);
1214
+ console.info("Automatically created anonymous session for first API request");
1215
+ }
1216
+ }
1217
+ } catch (error) {
1218
+ console.warn("Failed to automatically create anonymous tokens:", error);
1219
+ }
1220
+ if (accessToken && isTokenExpired(accessToken)) try {
1221
+ await refreshTokens();
1222
+ accessToken = await config.tokenStorage.getAccessToken();
1223
+ } catch (error) {}
1224
+ if (accessToken) request.headers.set("Authorization", `Bearer ${accessToken}`);
1028
1225
  return request;
1029
1226
  },
1030
- async onResponse({ response }) {
1031
- const cloned = response.clone();
1032
- let responseBody = null;
1033
- try {
1034
- const contentType = response.headers.get("content-type")?.toLowerCase();
1035
- if (contentType?.startsWith("application/json")) responseBody = await cloned.json();
1036
- else if (contentType?.startsWith("text/")) responseBody = await cloned.text();
1037
- } catch (error) {}
1038
- await debugLogger.logResponse(response, responseBody);
1227
+ async onResponse({ request, response }) {
1228
+ const pathname = getPathnameFromUrl(request.url);
1229
+ if (response.ok) {
1230
+ if (isTokenReturningEndpoint(pathname) || isAnonymousAuthEndpoint(pathname)) try {
1231
+ const data = await response.clone().json();
1232
+ const content = data.content;
1233
+ if (content?.access_token && content?.refresh_token) {
1234
+ await config.tokenStorage.setAccessToken(content.access_token);
1235
+ await config.tokenStorage.setRefreshToken(content.refresh_token);
1236
+ config.onTokensUpdated?.(content.access_token, content.refresh_token);
1237
+ }
1238
+ } catch (error) {
1239
+ console.warn("Failed to extract tokens from response:", error);
1240
+ }
1241
+ else if (isLogoutEndpoint(pathname)) {
1242
+ await config.tokenStorage.clearTokens();
1243
+ config.onTokensCleared?.();
1244
+ }
1245
+ }
1246
+ if (response.status === 401 && !isAnonymousAuthEndpoint(pathname)) {
1247
+ const currentToken = await config.tokenStorage.getAccessToken();
1248
+ if (currentToken && isTokenExpired(currentToken, 0)) try {
1249
+ await refreshTokens();
1250
+ const newToken = await config.tokenStorage.getAccessToken();
1251
+ if (newToken) {
1252
+ const retryRequest = request.clone();
1253
+ retryRequest.headers.set("Authorization", `Bearer ${newToken}`);
1254
+ return fetch(retryRequest);
1255
+ }
1256
+ } catch (error) {
1257
+ console.warn("Token refresh failed on 401 response:", error);
1258
+ }
1259
+ }
1039
1260
  return response;
1040
- },
1041
- async onError({ error, request }) {
1042
- debugLogger.logError("API Request Failed", {
1043
- error: {
1044
- name: error instanceof Error ? error.name : "Unknown",
1045
- message: error instanceof Error ? error.message : String(error),
1046
- stack: error instanceof Error ? error.stack : void 0
1047
- },
1048
- request: {
1049
- method: request.method,
1050
- url: request.url,
1051
- headers: Object.fromEntries(request.headers.entries())
1052
- },
1053
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1054
- });
1055
- throw error;
1056
1261
  }
1057
1262
  };
1058
1263
  }
1059
-
1060
- //#endregion
1061
- //#region src/lib/header-utils.ts
1062
- /**
1063
- * Mapping of SDK header parameter names to actual HTTP header names
1064
- * Only include headers that need transformation - others pass through as-is
1065
- */
1066
- const HEADER_TRANSFORMATIONS = { customer_group_id: "x-customer-group-id" };
1067
- /**
1068
- * Transform SDK header parameters to actual HTTP header names
1069
- * Headers not in the transformation map are passed through unchanged
1070
- *
1071
- * @param headers - Headers object with SDK parameter names
1072
- * @returns Headers object with actual HTTP header names
1073
- */
1074
- function transformHeaders(headers) {
1075
- const transformed = {};
1076
- for (const [key, value] of Object.entries(headers)) if (value !== void 0) {
1077
- const headerName = HEADER_TRANSFORMATIONS[key] || key;
1078
- transformed[headerName] = value;
1079
- }
1080
- return transformed;
1081
- }
1082
1264
  /**
1083
- * Merge default headers with method-level headers
1084
- * Method-level headers take precedence over default headers
1085
- * Automatically transforms SDK parameter names to HTTP header names
1086
- *
1087
- * @param defaultHeaders - Default headers from SDK configuration
1088
- * @param methodHeaders - Headers passed to the specific method call
1089
- * @returns Merged headers object with proper HTTP header names
1265
+ * Helper function to create auth middleware with sensible defaults
1090
1266
  */
1091
- function mergeHeaders(defaultHeaders, methodHeaders) {
1092
- const merged = {};
1093
- if (defaultHeaders) {
1094
- const transformedDefaults = transformHeaders(defaultHeaders);
1095
- Object.assign(merged, transformedDefaults);
1096
- }
1097
- if (methodHeaders) Object.assign(merged, methodHeaders);
1098
- Object.keys(merged).forEach((key) => {
1099
- if (merged[key] === void 0) delete merged[key];
1267
+ function createDefaultAuthMiddleware(options) {
1268
+ const tokenStorage = options.tokenStorage || (typeof localStorage !== "undefined" ? new BrowserTokenStorage() : new MemoryTokenStorage());
1269
+ return createAuthMiddleware({
1270
+ tokenStorage,
1271
+ apiKey: options.apiKey,
1272
+ baseUrl: options.baseUrl,
1273
+ onTokensUpdated: options.onTokensUpdated,
1274
+ onTokensCleared: options.onTokensCleared
1100
1275
  });
1101
- return merged;
1102
1276
  }
1103
1277
 
1104
1278
  //#endregion
1105
- //#region src/lib/client.ts
1279
+ //#region src/lib/url-utils.ts
1106
1280
  /**
1107
- * Available API environments
1281
+ * URL utility functions for the Storefront SDK
1282
+ */
1283
+ /**
1284
+ * Available API environments for Commerce Engine
1108
1285
  */
1109
1286
  let Environment = /* @__PURE__ */ function(Environment$1) {
1110
1287
  /**
@@ -1118,12 +1295,26 @@ let Environment = /* @__PURE__ */ function(Environment$1) {
1118
1295
  return Environment$1;
1119
1296
  }({});
1120
1297
  /**
1121
- * Base API client for Storefront API
1298
+ * Build base URL for Storefront API
1122
1299
  */
1123
- var StorefrontAPIClient = class {
1124
- client;
1300
+ function buildStorefrontURL(config) {
1301
+ if (config.baseUrl) return config.baseUrl;
1302
+ const env = config.environment || Environment.Production;
1303
+ switch (env) {
1304
+ case Environment.Staging: return `https://staging.api.commercengine.io/api/v1/${config.storeId}/storefront`;
1305
+ case Environment.Production:
1306
+ default: return `https://prod.api.commercengine.io/api/v1/${config.storeId}/storefront`;
1307
+ }
1308
+ }
1309
+
1310
+ //#endregion
1311
+ //#region src/lib/client.ts
1312
+ /**
1313
+ * Storefront API client that extends the generic BaseAPIClient
1314
+ * Adds Commerce Engine specific authentication and token management
1315
+ */
1316
+ var StorefrontAPIClient = class extends BaseAPIClient {
1125
1317
  config;
1126
- baseUrl;
1127
1318
  initializationPromise = null;
1128
1319
  /**
1129
1320
  * Create a new StorefrontAPIClient
@@ -1131,74 +1322,51 @@ var StorefrontAPIClient = class {
1131
1322
  * @param config - Configuration for the API client
1132
1323
  */
1133
1324
  constructor(config) {
1325
+ const baseUrl = buildStorefrontURL({
1326
+ storeId: config.storeId,
1327
+ environment: config.environment,
1328
+ baseUrl: config.baseUrl
1329
+ });
1330
+ const headerTransformations = { customer_group_id: "x-customer-group-id" };
1331
+ super({
1332
+ baseUrl,
1333
+ timeout: config.timeout,
1334
+ defaultHeaders: config.defaultHeaders,
1335
+ debug: config.debug,
1336
+ logger: config.logger
1337
+ }, baseUrl, headerTransformations);
1134
1338
  this.config = { ...config };
1135
- this.baseUrl = this.getBaseUrlFromConfig(this.config);
1136
- this.client = createClient({ baseUrl: this.baseUrl });
1137
- if (this.config.tokenStorage) {
1339
+ this.setupStorefrontAuth();
1340
+ }
1341
+ /**
1342
+ * Set up Storefront-specific authentication middleware
1343
+ */
1344
+ setupStorefrontAuth() {
1345
+ const config = this.config;
1346
+ if (config.tokenStorage) {
1138
1347
  const authMiddleware = createDefaultAuthMiddleware({
1139
- apiKey: this.config.apiKey,
1140
- baseUrl: this.baseUrl,
1141
- tokenStorage: this.config.tokenStorage,
1142
- onTokensUpdated: this.config.onTokensUpdated,
1143
- onTokensCleared: this.config.onTokensCleared
1348
+ apiKey: config.apiKey,
1349
+ baseUrl: this.getBaseUrl(),
1350
+ tokenStorage: config.tokenStorage,
1351
+ onTokensUpdated: config.onTokensUpdated,
1352
+ onTokensCleared: config.onTokensCleared
1144
1353
  });
1145
1354
  this.client.use(authMiddleware);
1146
- if (this.config.accessToken) {
1147
- this.initializationPromise = this.initializeTokens(this.config.accessToken, this.config.refreshToken);
1148
- this.config.accessToken = void 0;
1149
- this.config.refreshToken = void 0;
1355
+ if (config.accessToken) {
1356
+ this.initializationPromise = this.initializeTokens(config.accessToken, config.refreshToken);
1357
+ config.accessToken = void 0;
1358
+ config.refreshToken = void 0;
1150
1359
  }
1151
1360
  } else this.client.use({ onRequest: async ({ request }) => {
1152
1361
  const pathname = getPathnameFromUrl(request.url);
1153
1362
  if (isAnonymousAuthEndpoint(pathname)) {
1154
- if (this.config.apiKey) request.headers.set("X-Api-Key", this.config.apiKey);
1155
- if (this.config.accessToken) request.headers.set("Authorization", `Bearer ${this.config.accessToken}`);
1363
+ if (config.apiKey) request.headers.set("X-Api-Key", config.apiKey);
1364
+ if (config.accessToken) request.headers.set("Authorization", `Bearer ${config.accessToken}`);
1156
1365
  return request;
1157
1366
  }
1158
- if (this.config.accessToken) request.headers.set("Authorization", `Bearer ${this.config.accessToken}`);
1367
+ if (config.accessToken) request.headers.set("Authorization", `Bearer ${config.accessToken}`);
1159
1368
  return request;
1160
1369
  } });
1161
- if (this.config.timeout) this.setupTimeoutMiddleware();
1162
- if (this.config.debug) {
1163
- const debugMiddleware = createDebugMiddleware(this.config.logger);
1164
- this.client.use(debugMiddleware);
1165
- }
1166
- }
1167
- /**
1168
- * Set up timeout middleware
1169
- */
1170
- setupTimeoutMiddleware() {
1171
- this.client.use({ onRequest: async ({ request }) => {
1172
- const controller = new AbortController();
1173
- const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
1174
- if (request.signal) request.signal.addEventListener("abort", () => controller.abort());
1175
- const newRequest = new Request(request, { signal: controller.signal });
1176
- controller.signal.addEventListener("abort", () => clearTimeout(timeoutId));
1177
- return newRequest;
1178
- } });
1179
- }
1180
- /**
1181
- * Constructs the base URL from the configuration
1182
- *
1183
- * @param config - The client configuration
1184
- * @returns The constructed base URL
1185
- */
1186
- getBaseUrlFromConfig(config) {
1187
- if (config.baseUrl) return config.baseUrl;
1188
- const env = config.environment || Environment.Production;
1189
- switch (env) {
1190
- case Environment.Staging: return `https://staging.api.commercengine.io/api/v1/${config.storeId}/storefront`;
1191
- case Environment.Production:
1192
- default: return `https://prod.api.commercengine.io/api/v1/${config.storeId}/storefront`;
1193
- }
1194
- }
1195
- /**
1196
- * Get the base URL of the API
1197
- *
1198
- * @returns The base URL of the API
1199
- */
1200
- getBaseUrl() {
1201
- return this.baseUrl;
1202
1370
  }
1203
1371
  /**
1204
1372
  * Get the authorization header value
@@ -1260,48 +1428,6 @@ var StorefrontAPIClient = class {
1260
1428
  this.config.apiKey = void 0;
1261
1429
  }
1262
1430
  /**
1263
- * Execute a request and handle the response
1264
- *
1265
- * @param apiCall - Function that executes the API request
1266
- * @returns Promise with the API response
1267
- */
1268
- async executeRequest(apiCall) {
1269
- try {
1270
- const { data, error, response } = await apiCall();
1271
- if (error) return {
1272
- data: null,
1273
- error,
1274
- response
1275
- };
1276
- if (data && data.content !== void 0) return {
1277
- data: data.content,
1278
- error: null,
1279
- response
1280
- };
1281
- return {
1282
- data,
1283
- error: null,
1284
- response
1285
- };
1286
- } catch (err) {
1287
- const mockResponse = new Response(null, {
1288
- status: 0,
1289
- statusText: "Network Error"
1290
- });
1291
- const errorResult = {
1292
- data: null,
1293
- error: {
1294
- success: false,
1295
- code: "NETWORK_ERROR",
1296
- message: "Network error occurred",
1297
- error: err
1298
- },
1299
- response: mockResponse
1300
- };
1301
- return errorResult;
1302
- }
1303
- }
1304
- /**
1305
1431
  * Initialize tokens in storage (private helper method)
1306
1432
  */
1307
1433
  async initializeTokens(accessToken, refreshToken) {
@@ -1314,16 +1440,6 @@ var StorefrontAPIClient = class {
1314
1440
  console.warn("Failed to initialize tokens in storage:", error);
1315
1441
  }
1316
1442
  }
1317
- /**
1318
- * Merge default headers with method-level headers
1319
- * Method-level headers take precedence over default headers
1320
- *
1321
- * @param methodHeaders - Headers passed to the specific method call
1322
- * @returns Merged headers object with proper HTTP header names
1323
- */
1324
- mergeHeaders(methodHeaders) {
1325
- return mergeHeaders(this.config.defaultHeaders, methodHeaders);
1326
- }
1327
1443
  };
1328
1444
 
1329
1445
  //#endregion
@@ -4108,11 +4224,16 @@ var StorefrontSDK = class {
4108
4224
  */
4109
4225
  store;
4110
4226
  /**
4227
+ * Centrally stored default headers for consistency
4228
+ */
4229
+ defaultHeaders;
4230
+ /**
4111
4231
  * Create a new StorefrontSDK instance
4112
4232
  *
4113
4233
  * @param options - Configuration options for the SDK
4114
4234
  */
4115
4235
  constructor(options) {
4236
+ this.defaultHeaders = options.defaultHeaders;
4116
4237
  const config = {
4117
4238
  storeId: options.storeId,
4118
4239
  environment: options.environment,
@@ -4272,26 +4393,23 @@ var StorefrontSDK = class {
4272
4393
  * @param headers - Default headers to set (only supported headers allowed)
4273
4394
  */
4274
4395
  setDefaultHeaders(headers) {
4275
- const newConfig = {
4276
- ...this.catalog["config"],
4277
- defaultHeaders: headers
4278
- };
4279
- this.catalog["config"] = newConfig;
4280
- this.cart["config"] = newConfig;
4281
- this.auth["config"] = newConfig;
4282
- this.customer["config"] = newConfig;
4283
- this.helpers["config"] = newConfig;
4284
- this.shipping["config"] = newConfig;
4285
- this.order["config"] = newConfig;
4286
- this.store["config"] = newConfig;
4396
+ this.defaultHeaders = headers;
4397
+ this.catalog.setDefaultHeaders(headers);
4398
+ this.cart.setDefaultHeaders(headers);
4399
+ this.auth.setDefaultHeaders(headers);
4400
+ this.customer.setDefaultHeaders(headers);
4401
+ this.helpers.setDefaultHeaders(headers);
4402
+ this.shipping.setDefaultHeaders(headers);
4403
+ this.order.setDefaultHeaders(headers);
4404
+ this.store.setDefaultHeaders(headers);
4287
4405
  }
4288
4406
  /**
4289
4407
  * Get current default headers
4290
4408
  *
4291
- * @returns Current default headers
4409
+ * @returns Current default headers from central storage (always consistent)
4292
4410
  */
4293
4411
  getDefaultHeaders() {
4294
- return this.catalog["config"].defaultHeaders;
4412
+ return this.defaultHeaders;
4295
4413
  }
4296
4414
  };
4297
4415
  var src_default = StorefrontSDK;