@commercengine/storefront-sdk 0.13.4 → 0.14.1

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.
package/dist/index.mjs CHANGED
@@ -75,7 +75,6 @@ var ResponseUtils = class {
75
75
  */
76
76
  var DebugLogger = class {
77
77
  logger;
78
- responseTextCache = /* @__PURE__ */ new Map();
79
78
  constructor(logger) {
80
79
  this.logger = logger || ((level, message, data) => {
81
80
  console.log(`[${level.toUpperCase()}]`, message);
@@ -98,7 +97,6 @@ var DebugLogger = class {
98
97
  * Log debug information about API response
99
98
  */
100
99
  async logResponse(response, responseBody) {
101
- if (responseBody && typeof responseBody === "string") this.responseTextCache.set(response.url, responseBody);
102
100
  this.logger("info", "API Response Debug Info", {
103
101
  url: response.url,
104
102
  status: response.status,
@@ -122,17 +120,17 @@ var DebugLogger = class {
122
120
  this.logger("error", message, error);
123
121
  }
124
122
  /**
125
- * Get cached response text for a URL (if available)
123
+ * Compatibility shim retained for older internal callers.
124
+ * Response bodies are no longer cached by the debug logger.
126
125
  */
127
- getCachedResponseText(url) {
128
- return this.responseTextCache.get(url) || null;
126
+ getCachedResponseText(_url) {
127
+ return null;
129
128
  }
130
129
  /**
131
- * Clear cached response texts
130
+ * Compatibility shim retained for older internal callers.
131
+ * Response bodies are no longer cached by the debug logger.
132
132
  */
133
- clearCache() {
134
- this.responseTextCache.clear();
135
- }
133
+ clearCache() {}
136
134
  info(message, data) {
137
135
  this.logger("info", message, data);
138
136
  }
@@ -218,14 +216,33 @@ function createDebugMiddleware(logger) {
218
216
  * @returns Middleware object with onRequest handler
219
217
  */
220
218
  function createTimeoutMiddleware(timeoutMs) {
221
- return { onRequest: async ({ request }) => {
222
- const controller = new AbortController();
223
- const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
224
- if (request.signal) request.signal.addEventListener("abort", () => controller.abort());
225
- const newRequest = new Request(request, { signal: controller.signal });
226
- controller.signal.addEventListener("abort", () => clearTimeout(timeoutId));
227
- return newRequest;
228
- } };
219
+ const timeouts = /* @__PURE__ */ new WeakMap();
220
+ const clearRequestTimeout = (signal) => {
221
+ const timeoutId = timeouts.get(signal);
222
+ if (timeoutId) {
223
+ clearTimeout(timeoutId);
224
+ timeouts.delete(signal);
225
+ }
226
+ };
227
+ return {
228
+ onRequest: async ({ request }) => {
229
+ const controller = new AbortController();
230
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
231
+ if (request.signal) request.signal.addEventListener("abort", () => controller.abort(), { once: true });
232
+ const newRequest = new Request(request, { signal: controller.signal });
233
+ timeouts.set(newRequest.signal, timeoutId);
234
+ controller.signal.addEventListener("abort", () => clearRequestTimeout(newRequest.signal), { once: true });
235
+ return newRequest;
236
+ },
237
+ onResponse: async ({ request, response }) => {
238
+ clearRequestTimeout(request.signal);
239
+ return response;
240
+ },
241
+ onError: async ({ request, error }) => {
242
+ clearRequestTimeout(request.signal);
243
+ throw error;
244
+ }
245
+ };
229
246
  }
230
247
  /**
231
248
  * Transform headers using a transformation mapping
@@ -291,8 +308,8 @@ async function executeRequest(apiCall) {
291
308
  };
292
309
  } catch (err) {
293
310
  const mockResponse = new Response(null, {
294
- status: 0,
295
- statusText: "Network Error"
311
+ status: 503,
312
+ statusText: "Service Unavailable"
296
313
  });
297
314
  return {
298
315
  data: null,
@@ -412,619 +429,228 @@ function getPathnameFromUrl(url) {
412
429
  }
413
430
 
414
431
  //#endregion
415
- //#region src/lib/jwt-utils.ts
432
+ //#region src/lib/shared/url-utils.ts
416
433
  /**
417
- * Decode a JWT token payload without signature verification.
418
- * This is a lightweight replacement for jose's decodeJwt.
419
- *
420
- * @param token - The JWT token to decode
421
- * @returns The decoded payload
422
- * @throws Error if the token is malformed
434
+ * URL utility functions for the Storefront SDK
423
435
  */
424
- function decodeJwt(token) {
425
- if (typeof token !== "string") throw new Error("Invalid token: must be a string");
426
- const parts = token.split(".");
427
- if (parts.length !== 3) throw new Error("Invalid token: must have 3 parts");
428
- const base64Url = parts[1];
429
- if (!base64Url) throw new Error("Invalid token: missing payload");
430
- let base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
431
- const padding = base64.length % 4;
432
- if (padding) base64 += "=".repeat(4 - padding);
433
- const binaryStr = atob(base64);
434
- const bytes = new Uint8Array(binaryStr.length);
435
- for (let i = 0; i < binaryStr.length; i++) bytes[i] = binaryStr.charCodeAt(i);
436
- const payload = JSON.parse(new TextDecoder().decode(bytes));
437
- if (typeof payload !== "object" || payload === null) throw new Error("Invalid token: payload must be an object");
438
- return payload;
439
- }
440
436
  /**
441
- * Decode and extract user information from a JWT token
442
- *
443
- * @param token - The JWT token to decode
444
- * @returns User information or null if token is invalid
437
+ * Available API environments for Commerce Engine
445
438
  */
446
- function extractUserInfoFromToken(token) {
447
- try {
448
- const payload = decodeJwt(token);
449
- return {
450
- id: payload.ulid,
451
- email: payload.email,
452
- phone: payload.phone,
453
- username: payload.username,
454
- firstName: payload.first_name,
455
- lastName: payload.last_name,
456
- storeId: payload.store_id,
457
- isLoggedIn: payload.is_logged_in,
458
- isAnonymous: payload.is_anonymous,
459
- customerId: payload.customer_id,
460
- customerGroupId: payload.customer_group_id,
461
- anonymousId: payload.anonymous_id,
462
- channel: payload.channel,
463
- tokenExpiry: /* @__PURE__ */ new Date(payload.exp * 1e3),
464
- tokenIssuedAt: /* @__PURE__ */ new Date(payload.iat * 1e3)
465
- };
466
- } catch (error) {
467
- console.warn("Failed to decode JWT token:", error);
468
- return null;
469
- }
470
- }
439
+ let Environment = /* @__PURE__ */ function(Environment) {
440
+ /**
441
+ * Staging environment
442
+ */
443
+ Environment["Staging"] = "staging";
444
+ /**
445
+ * Production environment
446
+ */
447
+ Environment["Production"] = "production";
448
+ return Environment;
449
+ }({});
471
450
  /**
472
- * Check if a JWT token is expired
473
- *
474
- * @param token - The JWT token to check
475
- * @param bufferSeconds - Buffer time in seconds (default: 30)
476
- * @returns True if token is expired or will expire within buffer time
451
+ * Build base URL for Storefront API
477
452
  */
478
- function isTokenExpired(token, bufferSeconds = 30) {
479
- try {
480
- const payload = decodeJwt(token);
481
- if (!payload.exp) return true;
482
- return Math.floor(Date.now() / 1e3) >= payload.exp - bufferSeconds;
483
- } catch (error) {
484
- console.warn("Failed to decode JWT token:", error);
485
- return true;
453
+ function buildStorefrontURL(config) {
454
+ if (config.baseUrl) return config.baseUrl;
455
+ switch (config.environment || Environment.Production) {
456
+ case Environment.Staging: return `https://staging.api.commercengine.io/api/v1/${config.storeId}/storefront`;
457
+ case Environment.Production:
458
+ default: return `https://prod.api.commercengine.io/api/v1/${config.storeId}/storefront`;
486
459
  }
487
460
  }
488
- /**
489
- * Get the user ID from a JWT token
490
- *
491
- * @param token - The JWT token
492
- * @returns User ID (ulid) or null if token is invalid
493
- */
494
- function getUserIdFromToken(token) {
495
- return extractUserInfoFromToken(token)?.id || null;
496
- }
497
- /**
498
- * Check if user is logged in based on JWT token
499
- *
500
- * @param token - The JWT token
501
- * @returns True if user is logged in, false otherwise
502
- */
503
- function isUserLoggedIn(token) {
504
- return extractUserInfoFromToken(token)?.isLoggedIn || false;
505
- }
506
- /**
507
- * Check if user is anonymous based on JWT token
508
- *
509
- * @param token - The JWT token
510
- * @returns True if user is anonymous, false otherwise
511
- */
512
- function isUserAnonymous(token) {
513
- return extractUserInfoFromToken(token)?.isAnonymous ?? true;
514
- }
515
-
516
- //#endregion
517
- //#region src/types/api-key-endpoints.ts
518
- const API_KEY_ENDPOINTS = [
519
- "/auth/anonymous",
520
- "/store/check",
521
- "/store/config"
522
- ];
523
461
 
524
462
  //#endregion
525
- //#region src/lib/auth-utils.ts
526
- /**
527
- * Check if a URL path is an auth endpoint that should use API key
528
- */
529
- function isAnonymousAuthEndpoint(pathname) {
530
- return pathname.endsWith("/auth/anonymous");
531
- }
463
+ //#region src/lib/shared/client.ts
532
464
  /**
533
- * Check if a URL path requires API key authentication (auto-generated from OpenAPI spec)
534
- */
535
- function isApiKeyRequiredEndpoint(pathname) {
536
- return API_KEY_ENDPOINTS.some((endpoint) => pathname.endsWith(endpoint));
537
- }
538
- /**
539
- * Check if a URL path is a login/register endpoint that returns tokens
465
+ * Shared base class for Storefront API clients.
466
+ *
467
+ * This centralizes Storefront-specific URL construction, default header
468
+ * transformations, and shared API key state while leaving auth middleware
469
+ * decisions to the public and session-specific subclasses.
540
470
  */
541
- function isTokenReturningEndpoint(pathname) {
542
- return [
543
- "/auth/login/password",
544
- "/auth/register/phone",
545
- "/auth/register/email",
546
- "/auth/verify-otp",
547
- "/auth/refresh-token",
548
- "/auth/logout"
549
- ].some((endpoint) => pathname.endsWith(endpoint));
550
- }
471
+ var StorefrontAPIClientBase = class extends BaseAPIClient {
472
+ apiKey;
473
+ constructor(config) {
474
+ const baseUrl = buildStorefrontURL({
475
+ storeId: config.storeId,
476
+ environment: config.environment,
477
+ baseUrl: config.baseUrl
478
+ });
479
+ super({
480
+ baseUrl,
481
+ timeout: config.timeout,
482
+ defaultHeaders: config.defaultHeaders,
483
+ debug: config.debug,
484
+ logger: config.logger
485
+ }, baseUrl, {
486
+ customer_group_id: "x-customer-group-id",
487
+ debug_mode: "x-debug-mode"
488
+ });
489
+ this.apiKey = config.apiKey;
490
+ }
491
+ setApiKey(apiKey) {
492
+ this.apiKey = apiKey;
493
+ }
494
+ clearApiKey() {
495
+ this.apiKey = void 0;
496
+ }
497
+ useMiddleware(middleware) {
498
+ this.client.use(middleware);
499
+ }
500
+ };
551
501
 
552
502
  //#endregion
553
- //#region src/lib/middleware.ts
503
+ //#region src/lib/session/client.ts
504
+ function attachSessionAuth(client, sessionManager) {
505
+ client.useMiddleware(sessionManager.createAuthMiddleware());
506
+ }
554
507
  /**
555
- * Simple in-memory token storage implementation
508
+ * Storefront API client that extends the generic BaseAPIClient
509
+ * Adds Commerce Engine specific authentication and token management
556
510
  */
557
- var MemoryTokenStorage = class {
558
- accessToken = null;
559
- refreshToken = null;
560
- async getAccessToken() {
561
- return this.accessToken;
511
+ var SessionStorefrontAPIClient = class extends StorefrontAPIClientBase {
512
+ config;
513
+ /**
514
+ * Create a new SessionStorefrontAPIClient
515
+ *
516
+ * @param config - Configuration for the API client
517
+ */
518
+ constructor(config) {
519
+ super(config);
520
+ this.config = { ...config };
521
+ this.setupStorefrontAuth();
562
522
  }
563
- async setAccessToken(token) {
564
- this.accessToken = token;
523
+ /**
524
+ * Set up Storefront-specific authentication middleware
525
+ */
526
+ setupStorefrontAuth() {
527
+ attachSessionAuth(this, this.config.sessionManager);
565
528
  }
566
- async getRefreshToken() {
567
- return this.refreshToken;
529
+ /**
530
+ * Get the authorization header value
531
+ * without creating a new session.
532
+ *
533
+ * @returns The Authorization header value or empty string if no token is set
534
+ */
535
+ async getAuthorizationHeader() {
536
+ return this.config.sessionManager.getAuthorizationHeader();
568
537
  }
569
- async setRefreshToken(token) {
570
- this.refreshToken = token;
538
+ /**
539
+ * Set authentication tokens
540
+ *
541
+ * @param accessToken - The access token (required)
542
+ * @param refreshToken - The refresh token (optional)
543
+ *
544
+ * Behavior:
545
+ * - If tokenStorage is provided: Stores tokens for automatic management
546
+ * - If tokenStorage is not provided: Only stores access token for manual management
547
+ */
548
+ async setTokens(accessToken, refreshToken) {
549
+ await this.config.sessionManager.setTokens(accessToken, refreshToken);
571
550
  }
551
+ /**
552
+ * Clear all authentication tokens
553
+ *
554
+ * Behavior:
555
+ * - If tokenStorage is provided: Clears both access and refresh tokens from storage
556
+ * - If tokenStorage is not provided: Clears the manual access token
557
+ */
572
558
  async clearTokens() {
573
- this.accessToken = null;
574
- this.refreshToken = null;
559
+ await this.config.sessionManager.clearTokens();
575
560
  }
576
- };
577
- /**
578
- * Browser localStorage token storage implementation
579
- */
580
- var BrowserTokenStorage = class {
581
- accessTokenKey;
582
- refreshTokenKey;
583
- constructor(prefix = "storefront_") {
584
- this.accessTokenKey = `${prefix}access_token`;
585
- this.refreshTokenKey = `${prefix}refresh_token`;
561
+ /**
562
+ * Set the X-Api-Key header
563
+ *
564
+ * @param apiKey - The API key to set
565
+ */
566
+ setApiKey(apiKey) {
567
+ super.setApiKey(apiKey);
568
+ this.config.apiKey = apiKey;
569
+ this.config.sessionManager.setApiKey(apiKey);
586
570
  }
587
- async getAccessToken() {
588
- if (typeof localStorage === "undefined") return null;
589
- return localStorage.getItem(this.accessTokenKey);
571
+ /**
572
+ * Clear the X-Api-Key header
573
+ */
574
+ clearApiKey() {
575
+ super.clearApiKey();
576
+ this.config.apiKey = void 0;
577
+ this.config.sessionManager.clearApiKey();
590
578
  }
591
- async setAccessToken(token) {
592
- if (typeof localStorage !== "undefined") localStorage.setItem(this.accessTokenKey, token);
579
+ /**
580
+ * Resolve the current user ID from explicit parameters or the active session.
581
+ *
582
+ * @param explicitUserId - Optional user ID supplied by the caller
583
+ * @returns The resolved user ID
584
+ * @throws When no user ID is available
585
+ */
586
+ async resolveCurrentUserId(explicitUserId) {
587
+ if (explicitUserId) return explicitUserId;
588
+ return this.config.sessionManager.ensureUserId();
593
589
  }
594
- async getRefreshToken() {
595
- if (typeof localStorage === "undefined") return null;
596
- return localStorage.getItem(this.refreshTokenKey);
590
+ /**
591
+ * Resolve path parameters that require `user_id`.
592
+ *
593
+ * @param pathParams - Path parameters with an optional `user_id`
594
+ * @returns Path parameters with a guaranteed `user_id`
595
+ */
596
+ async resolveUserPathParams(pathParams) {
597
+ return {
598
+ ...pathParams,
599
+ user_id: await this.resolveCurrentUserId(pathParams.user_id)
600
+ };
597
601
  }
598
- async setRefreshToken(token) {
599
- if (typeof localStorage !== "undefined") localStorage.setItem(this.refreshTokenKey, token);
602
+ /**
603
+ * Resolve query parameters that require `user_id`.
604
+ *
605
+ * @param queryParams - Query parameters with an optional `user_id`
606
+ * @returns Query parameters with a guaranteed `user_id`
607
+ */
608
+ async resolveUserQueryParams(queryParams) {
609
+ return {
610
+ ...queryParams,
611
+ user_id: await this.resolveCurrentUserId(queryParams.user_id)
612
+ };
600
613
  }
601
- async clearTokens() {
602
- if (typeof localStorage !== "undefined") {
603
- localStorage.removeItem(this.accessTokenKey);
604
- localStorage.removeItem(this.refreshTokenKey);
605
- }
606
- }
607
- };
608
- /**
609
- * Cookie-based token storage implementation
610
- */
611
- var CookieTokenStorage = class {
612
- accessTokenKey;
613
- refreshTokenKey;
614
- options;
615
- constructor(options = {}) {
616
- const prefix = options.prefix || "storefront_";
617
- this.accessTokenKey = `${prefix}access_token`;
618
- this.refreshTokenKey = `${prefix}refresh_token`;
619
- this.options = {
620
- maxAge: options.maxAge || 10080 * 60,
621
- path: options.path || "/",
622
- domain: options.domain,
623
- secure: options.secure ?? (typeof window !== "undefined" && window.location?.protocol === "https:"),
624
- sameSite: options.sameSite || "Lax",
625
- httpOnly: false
626
- };
627
- }
628
- async getAccessToken() {
629
- return this.getCookie(this.accessTokenKey);
630
- }
631
- async setAccessToken(token) {
632
- this.setCookie(this.accessTokenKey, token);
633
- }
634
- async getRefreshToken() {
635
- return this.getCookie(this.refreshTokenKey);
636
- }
637
- async setRefreshToken(token) {
638
- this.setCookie(this.refreshTokenKey, token);
639
- }
640
- async clearTokens() {
641
- this.deleteCookie(this.accessTokenKey);
642
- this.deleteCookie(this.refreshTokenKey);
643
- }
644
- getCookie(name) {
645
- if (typeof document === "undefined") return null;
646
- const parts = `; ${document.cookie}`.split(`; ${name}=`);
647
- if (parts.length === 2) {
648
- const cookieValue = parts.pop()?.split(";").shift();
649
- return cookieValue ? decodeURIComponent(cookieValue) : null;
650
- }
651
- return null;
652
- }
653
- setCookie(name, value) {
654
- if (typeof document === "undefined") return;
655
- let cookieString = `${name}=${encodeURIComponent(value)}`;
656
- if (this.options.maxAge) cookieString += `; Max-Age=${this.options.maxAge}`;
657
- if (this.options.path) cookieString += `; Path=${this.options.path}`;
658
- if (this.options.domain) cookieString += `; Domain=${this.options.domain}`;
659
- if (this.options.secure) cookieString += `; Secure`;
660
- if (this.options.sameSite) cookieString += `; SameSite=${this.options.sameSite}`;
661
- document.cookie = cookieString;
662
- }
663
- deleteCookie(name) {
664
- if (typeof document === "undefined") return;
665
- let cookieString = `${name}=; Max-Age=0`;
666
- if (this.options.path) cookieString += `; Path=${this.options.path}`;
667
- if (this.options.domain) cookieString += `; Domain=${this.options.domain}`;
668
- document.cookie = cookieString;
669
- }
670
- };
671
- /**
672
- * Create authentication middleware for openapi-fetch
673
- */
674
- function createAuthMiddleware(config) {
675
- let isRefreshing = false;
676
- let refreshPromise = null;
677
- let hasAssessedTokens = false;
678
- const assessTokenStateOnce = async () => {
679
- if (hasAssessedTokens) return;
680
- hasAssessedTokens = true;
681
- try {
682
- const accessToken = await config.tokenStorage.getAccessToken();
683
- const refreshToken = await config.tokenStorage.getRefreshToken();
684
- if (!accessToken && refreshToken) {
685
- await config.tokenStorage.clearTokens();
686
- console.info("Cleaned up orphaned refresh token");
687
- }
688
- } catch (error) {
689
- console.warn("Token state assessment failed:", error);
690
- }
691
- };
692
- const refreshTokens = async () => {
693
- if (isRefreshing && refreshPromise) return refreshPromise;
694
- isRefreshing = true;
695
- refreshPromise = (async () => {
696
- try {
697
- const refreshToken = await config.tokenStorage.getRefreshToken();
698
- let newTokens;
699
- if (refreshToken && !isTokenExpired(refreshToken)) if (config.refreshTokenFn) newTokens = await config.refreshTokenFn(refreshToken);
700
- else {
701
- const response = await fetch(`${config.baseUrl}/auth/refresh-token`, {
702
- method: "POST",
703
- headers: { "Content-Type": "application/json" },
704
- body: JSON.stringify({ refresh_token: refreshToken })
705
- });
706
- if (!response.ok) throw new Error(`Token refresh failed: ${response.status}`);
707
- newTokens = (await response.json()).content;
708
- }
709
- else {
710
- const currentAccessToken = await config.tokenStorage.getAccessToken();
711
- if (!currentAccessToken) throw new Error("No tokens available for refresh");
712
- const reason = refreshToken ? "refresh token expired" : "no refresh token available";
713
- const response = await fetch(`${config.baseUrl}/auth/anonymous`, {
714
- method: "POST",
715
- headers: {
716
- "Content-Type": "application/json",
717
- ...config.apiKey && { "X-Api-Key": config.apiKey },
718
- Authorization: `Bearer ${currentAccessToken}`
719
- }
720
- });
721
- if (!response.ok) throw new Error(`Anonymous token fallback failed: ${response.status}`);
722
- newTokens = (await response.json()).content;
723
- console.info(`Token refreshed via anonymous fallback (${reason}) - user may need to re-authenticate for privileged operations`);
724
- }
725
- await config.tokenStorage.setAccessToken(newTokens.access_token);
726
- await config.tokenStorage.setRefreshToken(newTokens.refresh_token);
727
- config.onTokensUpdated?.(newTokens.access_token, newTokens.refresh_token);
728
- } catch (error) {
729
- console.error("Token refresh failed:", error);
730
- await config.tokenStorage.clearTokens();
731
- config.onTokensCleared?.();
732
- throw error;
733
- } finally {
734
- isRefreshing = false;
735
- refreshPromise = null;
736
- }
737
- })();
738
- return refreshPromise;
739
- };
740
- return {
741
- async onRequest({ request }) {
742
- const pathname = getPathnameFromUrl(request.url);
743
- await assessTokenStateOnce();
744
- if (isAnonymousAuthEndpoint(pathname)) {
745
- if (config.apiKey) request.headers.set("X-Api-Key", config.apiKey);
746
- const existingToken = await config.tokenStorage.getAccessToken();
747
- if (existingToken && !isTokenExpired(existingToken) && isUserLoggedIn(existingToken)) return new Response(JSON.stringify({
748
- message: "Cannot create anonymous session while authenticated",
749
- success: false,
750
- code: "USER_ALREADY_AUTHENTICATED"
751
- }), {
752
- status: 400,
753
- headers: { "Content-Type": "application/json" }
754
- });
755
- if (existingToken) request.headers.set("Authorization", `Bearer ${existingToken}`);
756
- return request;
757
- }
758
- if (isApiKeyRequiredEndpoint(pathname)) {
759
- if (config.apiKey) request.headers.set("X-Api-Key", config.apiKey);
760
- return request;
761
- }
762
- let accessToken = await config.tokenStorage.getAccessToken();
763
- if (!accessToken) try {
764
- const response = await fetch(`${config.baseUrl}/auth/anonymous`, {
765
- method: "POST",
766
- headers: {
767
- "Content-Type": "application/json",
768
- ...config.apiKey && { "X-Api-Key": config.apiKey }
769
- }
770
- });
771
- if (response.ok) {
772
- const tokens = (await response.json()).content;
773
- if (tokens?.access_token && tokens?.refresh_token) {
774
- await config.tokenStorage.setAccessToken(tokens.access_token);
775
- await config.tokenStorage.setRefreshToken(tokens.refresh_token);
776
- accessToken = tokens.access_token;
777
- config.onTokensUpdated?.(tokens.access_token, tokens.refresh_token);
778
- console.info("Automatically created anonymous session for first API request");
779
- }
780
- }
781
- } catch (error) {
782
- console.warn("Failed to automatically create anonymous tokens:", error);
783
- }
784
- if (accessToken && isTokenExpired(accessToken)) try {
785
- await refreshTokens();
786
- accessToken = await config.tokenStorage.getAccessToken();
787
- } catch (error) {}
788
- if (accessToken) request.headers.set("Authorization", `Bearer ${accessToken}`);
789
- return request;
790
- },
791
- async onResponse({ request, response }) {
792
- const pathname = getPathnameFromUrl(request.url);
793
- if (response.ok) {
794
- if (isTokenReturningEndpoint(pathname) || isAnonymousAuthEndpoint(pathname)) try {
795
- const content = (await response.clone().json()).content;
796
- if (content?.access_token && content?.refresh_token) {
797
- await config.tokenStorage.setAccessToken(content.access_token);
798
- await config.tokenStorage.setRefreshToken(content.refresh_token);
799
- config.onTokensUpdated?.(content.access_token, content.refresh_token);
800
- }
801
- } catch (error) {
802
- console.warn("Failed to extract tokens from response:", error);
803
- }
804
- }
805
- if (response.status === 401 && !isAnonymousAuthEndpoint(pathname)) {
806
- const currentToken = await config.tokenStorage.getAccessToken();
807
- if (currentToken && isTokenExpired(currentToken, 0)) try {
808
- await refreshTokens();
809
- const newToken = await config.tokenStorage.getAccessToken();
810
- if (newToken) {
811
- const retryRequest = request.clone();
812
- retryRequest.headers.set("Authorization", `Bearer ${newToken}`);
813
- return fetch(retryRequest);
814
- }
815
- } catch (error) {
816
- console.warn("Token refresh failed on 401 response:", error);
817
- }
818
- }
819
- return response;
820
- }
821
- };
822
- }
823
- /**
824
- * Helper function to create auth middleware with sensible defaults
825
- */
826
- function createDefaultAuthMiddleware(options) {
827
- return createAuthMiddleware({
828
- tokenStorage: options.tokenStorage || (typeof localStorage !== "undefined" ? new BrowserTokenStorage() : new MemoryTokenStorage()),
829
- apiKey: options.apiKey,
830
- baseUrl: options.baseUrl,
831
- onTokensUpdated: options.onTokensUpdated,
832
- onTokensCleared: options.onTokensCleared
833
- });
834
- }
835
-
836
- //#endregion
837
- //#region src/lib/url-utils.ts
838
- /**
839
- * URL utility functions for the Storefront SDK
840
- */
841
- /**
842
- * Available API environments for Commerce Engine
843
- */
844
- let Environment = /* @__PURE__ */ function(Environment) {
845
614
  /**
846
- * Staging environment
847
- */
848
- Environment["Staging"] = "staging";
849
- /**
850
- * Production environment
615
+ * Resolve path parameters that require `customer_id`.
616
+ *
617
+ * @param pathParams - Path parameters with an optional `customer_id`
618
+ * @returns Path parameters with a guaranteed `customer_id`
619
+ * @throws When the user is anonymous or no session can be established
851
620
  */
852
- Environment["Production"] = "production";
853
- return Environment;
854
- }({});
855
- /**
856
- * Build base URL for Storefront API
857
- */
858
- function buildStorefrontURL(config) {
859
- if (config.baseUrl) return config.baseUrl;
860
- switch (config.environment || Environment.Production) {
861
- case Environment.Staging: return `https://staging.api.commercengine.io/api/v1/${config.storeId}/storefront`;
862
- case Environment.Production:
863
- default: return `https://prod.api.commercengine.io/api/v1/${config.storeId}/storefront`;
621
+ async resolveCustomerPathParams(pathParams) {
622
+ return {
623
+ ...pathParams,
624
+ customer_id: pathParams.customer_id ?? await this.config.sessionManager.ensureCustomerId()
625
+ };
864
626
  }
865
- }
627
+ };
866
628
 
867
629
  //#endregion
868
- //#region src/lib/client.ts
630
+ //#region src/lib/shared/catalog.ts
869
631
  /**
870
- * Storefront API client that extends the generic BaseAPIClient
871
- * Adds Commerce Engine specific authentication and token management
632
+ * Client for interacting with catalog endpoints
872
633
  */
873
- var StorefrontAPIClient = class extends BaseAPIClient {
874
- config;
875
- initializationPromise = null;
876
- /**
877
- * Create a new StorefrontAPIClient
878
- *
879
- * @param config - Configuration for the API client
880
- */
881
- constructor(config) {
882
- const baseUrl = buildStorefrontURL({
883
- storeId: config.storeId,
884
- environment: config.environment,
885
- baseUrl: config.baseUrl
886
- });
887
- super({
888
- baseUrl,
889
- timeout: config.timeout,
890
- defaultHeaders: config.defaultHeaders,
891
- debug: config.debug,
892
- logger: config.logger
893
- }, baseUrl, {
894
- customer_group_id: "x-customer-group-id",
895
- debug_mode: "x-debug-mode"
896
- });
897
- this.config = { ...config };
898
- this.setupStorefrontAuth();
899
- }
900
- /**
901
- * Set up Storefront-specific authentication middleware
902
- */
903
- setupStorefrontAuth() {
904
- const config = this.config;
905
- if (config.tokenStorage) {
906
- const authMiddleware = createDefaultAuthMiddleware({
907
- apiKey: config.apiKey,
908
- baseUrl: this.getBaseUrl(),
909
- tokenStorage: config.tokenStorage,
910
- onTokensUpdated: config.onTokensUpdated,
911
- onTokensCleared: config.onTokensCleared
912
- });
913
- this.client.use(authMiddleware);
914
- if (config.accessToken) {
915
- this.initializationPromise = this.initializeTokens(config.accessToken, config.refreshToken);
916
- config.accessToken = void 0;
917
- config.refreshToken = void 0;
918
- }
919
- } else this.client.use({ onRequest: async ({ request }) => {
920
- if (isAnonymousAuthEndpoint(getPathnameFromUrl(request.url))) {
921
- if (config.apiKey) request.headers.set("X-Api-Key", config.apiKey);
922
- if (config.accessToken) request.headers.set("Authorization", `Bearer ${config.accessToken}`);
923
- return request;
924
- }
925
- if (config.accessToken) request.headers.set("Authorization", `Bearer ${config.accessToken}`);
926
- return request;
927
- } });
928
- }
634
+ var BaseCatalogClient = class extends StorefrontAPIClientBase {
929
635
  /**
930
- * Get the authorization header value
931
- * If using token storage, gets the current token from storage
932
- * Otherwise returns the manual token
636
+ * List all products
933
637
  *
934
- * @returns The Authorization header value or empty string if no token is set
935
- */
936
- async getAuthorizationHeader() {
937
- if (this.config.tokenStorage && this.initializationPromise) await this.initializationPromise;
938
- if (this.config.tokenStorage) {
939
- const token = await this.config.tokenStorage.getAccessToken();
940
- return token ? `Bearer ${token}` : "";
941
- }
942
- return this.config.accessToken ? `Bearer ${this.config.accessToken}` : "";
943
- }
944
- /**
945
- * Set authentication tokens
638
+ * @param options - Optional query parameters
639
+ * @param headers - Optional header parameters (customer_group_id, etc.)
640
+ * @returns Promise with products and pagination info
946
641
  *
947
- * @param accessToken - The access token (required)
948
- * @param refreshToken - The refresh token (optional)
642
+ * @example
643
+ * ```typescript
644
+ * // Basic product listing
645
+ * const { data, error } = await sdk.catalog.listProducts();
949
646
  *
950
- * Behavior:
951
- * - If tokenStorage is provided: Stores tokens for automatic management
952
- * - If tokenStorage is not provided: Only stores access token for manual management
953
- */
954
- async setTokens(accessToken, refreshToken) {
955
- if (this.config.tokenStorage) {
956
- await this.config.tokenStorage.setAccessToken(accessToken);
957
- if (refreshToken) await this.config.tokenStorage.setRefreshToken(refreshToken);
958
- } else {
959
- this.config.accessToken = accessToken;
960
- if (refreshToken) console.warn("Refresh token provided but ignored in manual token management mode. Use tokenStorage for automatic management.");
961
- }
962
- }
963
- /**
964
- * Clear all authentication tokens
647
+ * if (error) {
648
+ * console.error("Failed to list products:", error);
649
+ * return;
650
+ * }
965
651
  *
966
- * Behavior:
967
- * - If tokenStorage is provided: Clears both access and refresh tokens from storage
968
- * - If tokenStorage is not provided: Clears the manual access token
969
- */
970
- async clearTokens() {
971
- if (this.config.tokenStorage) await this.config.tokenStorage.clearTokens();
972
- else this.config.accessToken = void 0;
973
- }
974
- /**
975
- * Set the X-Api-Key header
976
- *
977
- * @param apiKey - The API key to set
978
- */
979
- setApiKey(apiKey) {
980
- this.config.apiKey = apiKey;
981
- }
982
- /**
983
- * Clear the X-Api-Key header
984
- */
985
- clearApiKey() {
986
- this.config.apiKey = void 0;
987
- }
988
- /**
989
- * Initialize tokens in storage (private helper method)
990
- */
991
- async initializeTokens(accessToken, refreshToken) {
992
- try {
993
- if (this.config.tokenStorage) {
994
- await this.config.tokenStorage.setAccessToken(accessToken);
995
- if (refreshToken) await this.config.tokenStorage.setRefreshToken(refreshToken);
996
- }
997
- } catch (error) {
998
- console.warn("Failed to initialize tokens in storage:", error);
999
- }
1000
- }
1001
- };
1002
-
1003
- //#endregion
1004
- //#region src/lib/catalog.ts
1005
- /**
1006
- * Client for interacting with catalog endpoints
1007
- */
1008
- var CatalogClient = class extends StorefrontAPIClient {
1009
- /**
1010
- * List all products
1011
- *
1012
- * @param options - Optional query parameters
1013
- * @param headers - Optional header parameters (customer_group_id, etc.)
1014
- * @returns Promise with products and pagination info
1015
- *
1016
- * @example
1017
- * ```typescript
1018
- * // Basic product listing
1019
- * const { data, error } = await sdk.catalog.listProducts();
1020
- *
1021
- * if (error) {
1022
- * console.error("Failed to list products:", error);
1023
- * return;
1024
- * }
1025
- *
1026
- * console.log("Products found:", data.products?.length || 0);
1027
- * console.log("Pagination:", data.pagination);
652
+ * console.log("Products found:", data.products?.length || 0);
653
+ * console.log("Pagination:", data.pagination);
1028
654
  *
1029
655
  * // With filtering and pagination
1030
656
  * const { data: filteredData, error: filteredError } = await sdk.catalog.listProducts({
@@ -1337,49 +963,6 @@ var CatalogClient = class extends StorefrontAPIClient {
1337
963
  } }));
1338
964
  }
1339
965
  /**
1340
- * Create a product review
1341
- *
1342
- * @param pathParams - The path parameters (product ID)
1343
- * @param formData - The review data including rating, comment, and optional images
1344
- * @returns Promise with review creation response
1345
- *
1346
- * @example
1347
- * ```typescript
1348
- * const { data, error } = await sdk.catalog.createProductReview(
1349
- * { product_id: "prod_123" },
1350
- * {
1351
- * user_id: "user_123",
1352
- * order_number: "ORD-001",
1353
- * rating: 5,
1354
- * review_text: "Excellent product! Highly recommended.",
1355
- * images: [
1356
- * new File(["image data"], "review1.jpg", { type: "image/jpeg" }),
1357
- * new File(["image data"], "review2.jpg", { type: "image/jpeg" })
1358
- * ]
1359
- * }
1360
- * );
1361
- *
1362
- * if (error) {
1363
- * console.error("Failed to create review:", error);
1364
- * return;
1365
- * }
1366
- *
1367
- * console.log("Review created successfully:", data.message);
1368
- * ```
1369
- */
1370
- async createProductReview(pathParams, formData) {
1371
- return this.executeRequest(() => this.client.POST("/catalog/products/{product_id}/reviews", {
1372
- params: { path: pathParams },
1373
- body: formData,
1374
- bodySerializer: (body) => {
1375
- const fd = new FormData();
1376
- for (const [key, value] of Object.entries(body)) if (value !== void 0 && value !== null) if (value instanceof File || value instanceof Blob) fd.append(key, value);
1377
- else fd.append(key, String(value));
1378
- return fd;
1379
- }
1380
- }));
1381
- }
1382
- /**
1383
966
  * Search for products
1384
967
  *
1385
968
  * @param searchData - The search query, filters, sort, and pagination options
@@ -1619,12 +1202,100 @@ var CatalogClient = class extends StorefrontAPIClient {
1619
1202
  }
1620
1203
  };
1621
1204
 
1205
+ //#endregion
1206
+ //#region src/lib/public/client.ts
1207
+ /**
1208
+ * Storefront API client that always authenticates with `X-Api-Key`.
1209
+ *
1210
+ * This client is used by the public storefront surface where no session or
1211
+ * token lifecycle should be involved.
1212
+ */
1213
+ var PublicStorefrontAPIClient = class extends StorefrontAPIClientBase {
1214
+ constructor(config) {
1215
+ super(config);
1216
+ attachPublicAuth(this);
1217
+ }
1218
+ };
1219
+ function attachPublicAuth(client) {
1220
+ client.useMiddleware({ onRequest: async ({ request }) => {
1221
+ if (client.apiKey) request.headers.set("X-Api-Key", client.apiKey);
1222
+ return request;
1223
+ } });
1224
+ }
1225
+
1226
+ //#endregion
1227
+ //#region src/lib/public/catalog.ts
1228
+ /**
1229
+ * Client for interacting with catalog endpoints
1230
+ */
1231
+ var PublicCatalogClient = class extends BaseCatalogClient {
1232
+ constructor(config) {
1233
+ super(config);
1234
+ attachPublicAuth(this);
1235
+ }
1236
+ };
1237
+
1238
+ //#endregion
1239
+ //#region src/lib/catalog.ts
1240
+ /**
1241
+ * Client for interacting with catalog endpoints
1242
+ */
1243
+ var CatalogClient = class extends BaseCatalogClient {
1244
+ constructor(config) {
1245
+ super(config);
1246
+ attachSessionAuth(this, config.sessionManager);
1247
+ }
1248
+ /**
1249
+ * Create a product review
1250
+ *
1251
+ * @param pathParams - The path parameters (product ID)
1252
+ * @param formData - The review data including rating, comment, and optional images
1253
+ * @returns Promise with review creation response
1254
+ *
1255
+ * @example
1256
+ * ```typescript
1257
+ * const { data, error } = await sdk.catalog.createProductReview(
1258
+ * { product_id: "prod_123" },
1259
+ * {
1260
+ * user_id: "user_123",
1261
+ * order_number: "ORD-001",
1262
+ * rating: 5,
1263
+ * review_text: "Excellent product! Highly recommended.",
1264
+ * images: [
1265
+ * new File(["image data"], "review1.jpg", { type: "image/jpeg" }),
1266
+ * new File(["image data"], "review2.jpg", { type: "image/jpeg" })
1267
+ * ]
1268
+ * }
1269
+ * );
1270
+ *
1271
+ * if (error) {
1272
+ * console.error("Failed to create review:", error);
1273
+ * return;
1274
+ * }
1275
+ *
1276
+ * console.log("Review created successfully:", data.message);
1277
+ * ```
1278
+ */
1279
+ async createProductReview(pathParams, formData) {
1280
+ return this.executeRequest(() => this.client.POST("/catalog/products/{product_id}/reviews", {
1281
+ params: { path: pathParams },
1282
+ body: formData,
1283
+ bodySerializer: (body) => {
1284
+ const fd = new FormData();
1285
+ for (const [key, value] of Object.entries(body)) if (value !== void 0 && value !== null) if (value instanceof File || value instanceof Blob) fd.append(key, value);
1286
+ else fd.append(key, String(value));
1287
+ return fd;
1288
+ }
1289
+ }));
1290
+ }
1291
+ };
1292
+
1622
1293
  //#endregion
1623
1294
  //#region src/lib/cart.ts
1624
1295
  /**
1625
1296
  * Client for interacting with cart endpoints
1626
1297
  */
1627
- var CartClient = class extends StorefrontAPIClient {
1298
+ var CartClient = class extends SessionStorefrontAPIClient {
1628
1299
  /**
1629
1300
  * Create a new cart
1630
1301
  *
@@ -1746,49 +1417,14 @@ var CartClient = class extends StorefrontAPIClient {
1746
1417
  body
1747
1418
  }));
1748
1419
  }
1749
- /**
1750
- * Get cart details by user ID
1751
- *
1752
- * @param userId - The ID of the user
1753
- * @returns Promise with cart details
1754
- * @example
1755
- * ```typescript
1756
- * const { data, error } = await sdk.cart.getUserCart({
1757
- * user_id: "01H9USER12345ABCDE"
1758
- * });
1759
- *
1760
- * if (error) {
1761
- * console.error("Failed to get user cart:", error.message);
1762
- * } else {
1763
- * console.log("User cart ID:", data.cart.id);
1764
- * console.log("Cart value:", data.cart.subtotal);
1765
- * }
1766
- * ```
1767
- */
1768
- async getUserCart(userId) {
1769
- return this.executeRequest(() => this.client.GET("/carts/users/{user_id}", { params: { path: userId } }));
1420
+ async getUserCart(pathParams = {}) {
1421
+ const resolvedPathParams = await this.resolveUserPathParams(pathParams);
1422
+ return this.executeRequest(() => this.client.GET("/carts/users/{user_id}", { params: { path: resolvedPathParams } }));
1770
1423
  }
1771
- /**
1772
- * Delete a cart by user ID
1773
- *
1774
- * @param userId - The ID of the user
1775
- * @returns Promise that resolves when the cart is deleted
1776
- * @example
1777
- * ```typescript
1778
- * const { data, error } = await sdk.cart.deleteUserCart({
1779
- * user_id: "01H9USER12345ABCDE"
1780
- * });
1781
- *
1782
- * if (error) {
1783
- * console.error("Failed to delete user cart:", error.message);
1784
- * } else {
1785
- * console.log("User cart cleared:", data.message);
1786
- * }
1787
- * ```
1788
- */
1789
- async deleteUserCart(userId) {
1424
+ async deleteUserCart(pathParams = {}) {
1425
+ const resolvedPathParams = await this.resolveUserPathParams(pathParams);
1790
1426
  return this.executeRequest(() => this.client.DELETE("/carts/users/{user_id}", {
1791
- params: { path: userId },
1427
+ params: { path: resolvedPathParams },
1792
1428
  body: void 0
1793
1429
  }));
1794
1430
  }
@@ -2281,92 +1917,31 @@ var CartClient = class extends StorefrontAPIClient {
2281
1917
  body: void 0
2282
1918
  }));
2283
1919
  }
2284
- /**
2285
- * Get wishlist items
2286
- *
2287
- * @param userId - The ID of the user
2288
- * @param options - Optional query parameters for filtering by seller_id (only for multi-seller marketplace stores)
2289
- * @returns Promise with wishlist items
2290
- * @example
2291
- * ```typescript
2292
- * const { data, error } = await sdk.cart.getWishlist({
2293
- * user_id: "01H9USER12345ABCDE"
2294
- * });
2295
- *
2296
- * if (error) {
2297
- * console.error("Failed to get wishlist:", error.message);
2298
- * } else {
2299
- * const products = data.products;
2300
- * console.log("Wishlist items:", products.length);
2301
- * products.forEach(product => {
2302
- * console.log("Product:", product.product_name, "Price:", product.pricing?.selling_price);
2303
- * });
2304
- * }
2305
- * ```
2306
- */
2307
- async getWishlist(userId, options) {
1920
+ async getWishlist(pathParamsOrQuery, options) {
1921
+ const hasPathParams = options !== void 0 || !!pathParamsOrQuery && typeof pathParamsOrQuery === "object" && "user_id" in pathParamsOrQuery;
1922
+ const pathParams = hasPathParams ? pathParamsOrQuery : {};
1923
+ const queryParams = hasPathParams ? options : pathParamsOrQuery;
1924
+ const resolvedPathParams = await this.resolveUserPathParams(pathParams);
2308
1925
  return this.executeRequest(() => this.client.GET("/wishlist/{user_id}", { params: {
2309
- path: userId,
2310
- query: options
1926
+ path: resolvedPathParams,
1927
+ query: queryParams
2311
1928
  } }));
2312
1929
  }
2313
- /**
2314
- * Add item to wishlist
2315
- *
2316
- * @param userId - The ID of the user
2317
- * @param itemId - The ID of the item
2318
- * @returns Promise with updated wishlist
2319
- * @example
2320
- * ```typescript
2321
- * const { data, error } = await sdk.cart.addToWishlist(
2322
- * { user_id: "01H9USER12345ABCDE" },
2323
- * {
2324
- * product_id: "01F3Z7KG06J4ACWH1C4926KJEC",
2325
- * variant_id: null
2326
- * }
2327
- * );
2328
- *
2329
- * if (error) {
2330
- * console.error("Failed to add to wishlist:", error.message);
2331
- * } else {
2332
- * const products = data.products;
2333
- * console.log("Item added to wishlist, total items:", products.length);
2334
- * }
2335
- * ```
2336
- */
2337
- async addToWishlist(userId, itemId) {
1930
+ async addToWishlist(pathParamsOrBody, maybeBody) {
1931
+ const pathParams = maybeBody ? pathParamsOrBody : {};
1932
+ const body = maybeBody ?? pathParamsOrBody;
1933
+ const resolvedPathParams = await this.resolveUserPathParams(pathParams);
2338
1934
  return this.executeRequest(() => this.client.POST("/wishlist/{user_id}", {
2339
- params: { path: userId },
2340
- body: itemId
1935
+ params: { path: resolvedPathParams },
1936
+ body
2341
1937
  }));
2342
1938
  }
2343
- /**
2344
- * Remove item from wishlist
2345
- *
2346
- * @param userId - The ID of the user
2347
- * @param body - The body containing product details to remove
2348
- * @returns Promise with updated wishlist
2349
- * @example
2350
- * ```typescript
2351
- * const { data, error } = await sdk.cart.removeFromWishlist(
2352
- * { user_id: "01H9USER12345ABCDE" },
2353
- * {
2354
- * product_id: "01F3Z7KG06J4ACWH1C4926KJEC",
2355
- * variant_id: null
2356
- * }
2357
- * );
2358
- *
2359
- * if (error) {
2360
- * console.error("Failed to remove from wishlist:", error.message);
2361
- * } else {
2362
- * const products = data.products;
2363
- * console.log("Item removed from wishlist, remaining items:", products.length);
2364
- * }
2365
- * ```
2366
- */
2367
- async removeFromWishlist(userId, body) {
1939
+ async removeFromWishlist(pathParamsOrBody, maybeBody) {
1940
+ const pathParams = maybeBody ? pathParamsOrBody : {};
1941
+ const body = maybeBody ?? pathParamsOrBody;
1942
+ const resolvedPathParams = await this.resolveUserPathParams(pathParams);
2368
1943
  return this.executeRequest(() => this.client.DELETE("/wishlist/{user_id}", {
2369
- params: { path: userId },
1944
+ params: { path: resolvedPathParams },
2370
1945
  body
2371
1946
  }));
2372
1947
  }
@@ -2377,7 +1952,7 @@ var CartClient = class extends StorefrontAPIClient {
2377
1952
  /**
2378
1953
  * Client for interacting with authentication endpoints
2379
1954
  */
2380
- var AuthClient = class extends StorefrontAPIClient {
1955
+ var AuthClient = class extends SessionStorefrontAPIClient {
2381
1956
  /**
2382
1957
  * Get anonymous token for guest users
2383
1958
  *
@@ -2510,13 +2085,14 @@ var AuthClient = class extends StorefrontAPIClient {
2510
2085
  return this.executeRequest(() => this.client.POST("/auth/login/password", { body }));
2511
2086
  }
2512
2087
  /**
2513
- * Forgot password
2088
+ * Start forgot-password OTP flow
2514
2089
  *
2515
- * @param body - Request body containing email address
2516
- * @returns Promise with password reset information
2090
+ * @param body - Request body containing email or phone details
2091
+ * @param headers - Optional header parameters (for example `x-debug-mode`)
2092
+ * @returns Promise with OTP token and action for the reset flow
2517
2093
  * @example
2518
2094
  * ```typescript
2519
- * // Send password reset email
2095
+ * // Send password reset OTP
2520
2096
  * const { data, error } = await sdk.auth.forgotPassword({
2521
2097
  * email: "customer@example.com"
2522
2098
  * });
@@ -2524,13 +2100,17 @@ var AuthClient = class extends StorefrontAPIClient {
2524
2100
  * if (error) {
2525
2101
  * console.error("Password reset failed:", error.message);
2526
2102
  * } else {
2527
- * console.log("Reset email sent successfully");
2528
- * // Show confirmation message to user
2103
+ * console.log("OTP token:", data.otp_token);
2104
+ * console.log("Action:", data.otp_action);
2529
2105
  * }
2530
2106
  * ```
2531
2107
  */
2532
- async forgotPassword(body) {
2533
- return this.executeRequest(() => this.client.POST("/auth/forgot-password", { body }));
2108
+ async forgotPassword(body, headers) {
2109
+ const mergedHeaders = this.mergeHeaders(headers);
2110
+ return this.executeRequest(() => this.client.POST("/auth/forgot-password", {
2111
+ body,
2112
+ params: { header: mergedHeaders }
2113
+ }));
2534
2114
  }
2535
2115
  /**
2536
2116
  * Reset password
@@ -2612,7 +2192,8 @@ var AuthClient = class extends StorefrontAPIClient {
2612
2192
  * Register with phone
2613
2193
  *
2614
2194
  * @param body - Registration details including phone number and user information
2615
- * @returns Promise with user info and tokens
2195
+ * @param headers - Optional header parameters (for example `x-debug-mode`)
2196
+ * @returns Promise with OTP token and action for completing registration
2616
2197
  * @example
2617
2198
  * ```typescript
2618
2199
  * // Register a new user with phone number
@@ -2627,20 +2208,24 @@ var AuthClient = class extends StorefrontAPIClient {
2627
2208
  * if (error) {
2628
2209
  * console.error("Phone registration failed:", error.message);
2629
2210
  * } else {
2630
- * console.log("Registration successful:", data.user.first_name);
2631
- * console.log("User ID:", data.user.id);
2632
- * console.log("Access token:", data.access_token);
2211
+ * console.log("OTP token:", data.otp_token);
2212
+ * console.log("Action:", data.otp_action);
2633
2213
  * }
2634
2214
  * ```
2635
2215
  */
2636
- async registerWithPhone(body) {
2637
- return this.executeRequest(() => this.client.POST("/auth/register/phone", { body }));
2216
+ async registerWithPhone(body, headers) {
2217
+ const mergedHeaders = this.mergeHeaders(headers);
2218
+ return this.executeRequest(() => this.client.POST("/auth/register/phone", {
2219
+ body,
2220
+ params: { header: mergedHeaders }
2221
+ }));
2638
2222
  }
2639
2223
  /**
2640
2224
  * Register with email
2641
2225
  *
2642
2226
  * @param body - Registration details including email and user information
2643
- * @returns Promise with user info and tokens
2227
+ * @param headers - Optional header parameters (for example `x-debug-mode`)
2228
+ * @returns Promise with OTP token and action for completing registration
2644
2229
  * @example
2645
2230
  * ```typescript
2646
2231
  * // Register a new user with email address
@@ -2654,28 +2239,92 @@ var AuthClient = class extends StorefrontAPIClient {
2654
2239
  * if (error) {
2655
2240
  * console.error("Email registration failed:", error.message);
2656
2241
  * } else {
2657
- * console.log("Registration successful:", data.user.email);
2658
- * console.log("User ID:", data.user.id);
2659
- * console.log("Access token:", data.access_token);
2242
+ * console.log("OTP token:", data.otp_token);
2243
+ * console.log("Action:", data.otp_action);
2660
2244
  * }
2661
2245
  * ```
2662
2246
  */
2663
- async registerWithEmail(body) {
2664
- return this.executeRequest(() => this.client.POST("/auth/register/email", { body }));
2247
+ async registerWithEmail(body, headers) {
2248
+ const mergedHeaders = this.mergeHeaders(headers);
2249
+ return this.executeRequest(() => this.client.POST("/auth/register/email", {
2250
+ body,
2251
+ params: { header: mergedHeaders }
2252
+ }));
2665
2253
  }
2666
2254
  /**
2667
- * Refresh the access token using a refresh token
2668
- * @param body - Request body containing the refresh token
2669
- * @returns Promise with the new access token and refresh token
2255
+ * Register with WhatsApp
2256
+ *
2257
+ * @param body - Registration details including WhatsApp number and user information
2258
+ * @param headers - Optional header parameters (for example `x-debug-mode`)
2259
+ * @returns Promise with OTP token and action for completing registration
2670
2260
  * @example
2671
2261
  * ```typescript
2672
- * // Refresh access token when it expires
2673
- * const { data, error } = await sdk.auth.refreshToken({
2674
- * refresh_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
2262
+ * // Register a new user with WhatsApp number
2263
+ * const { data, error } = await sdk.auth.registerWithWhatsapp({
2264
+ * phone: "9876543210",
2265
+ * country_code: "+91",
2266
+ * first_name: "John",
2267
+ * last_name: "Doe",
2268
+ * email: "john.doe@example.com"
2675
2269
  * });
2676
2270
  *
2677
2271
  * if (error) {
2678
- * console.error("Token refresh failed:", error.message);
2272
+ * console.error("WhatsApp registration failed:", error.message);
2273
+ * } else {
2274
+ * console.log("OTP token:", data.otp_token);
2275
+ * console.log("Action:", data.otp_action);
2276
+ * }
2277
+ * ```
2278
+ */
2279
+ async registerWithWhatsapp(body, headers) {
2280
+ const mergedHeaders = this.mergeHeaders(headers);
2281
+ return this.executeRequest(() => this.client.POST("/auth/register/whatsapp", {
2282
+ body,
2283
+ params: { header: mergedHeaders }
2284
+ }));
2285
+ }
2286
+ /**
2287
+ * Register with password
2288
+ *
2289
+ * @param body - Registration details including email or phone and password
2290
+ * @param headers - Optional header parameters (for example `x-debug-mode`)
2291
+ * @returns Promise with OTP token and action for completing registration
2292
+ * @example
2293
+ * ```typescript
2294
+ * const { data, error } = await sdk.auth.registerWithPassword({
2295
+ * email: "jane.smith@example.com",
2296
+ * password: "securePassword123",
2297
+ * confirm_password: "securePassword123",
2298
+ * });
2299
+ *
2300
+ * if (error) {
2301
+ * console.error("Password registration failed:", error.message);
2302
+ * } else {
2303
+ * console.log("OTP token:", data.otp_token);
2304
+ * console.log("Action:", data.otp_action);
2305
+ * }
2306
+ * ```
2307
+ */
2308
+ async registerWithPassword(body, headers) {
2309
+ const mergedHeaders = this.mergeHeaders(headers);
2310
+ return this.executeRequest(() => this.client.POST("/auth/register/password", {
2311
+ body,
2312
+ params: { header: mergedHeaders }
2313
+ }));
2314
+ }
2315
+ /**
2316
+ * Refresh the access token using a refresh token
2317
+ * @param body - Request body containing the refresh token
2318
+ * @returns Promise with the new access token and refresh token
2319
+ * @example
2320
+ * ```typescript
2321
+ * // Refresh access token when it expires
2322
+ * const { data, error } = await sdk.auth.refreshToken({
2323
+ * refresh_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
2324
+ * });
2325
+ *
2326
+ * if (error) {
2327
+ * console.error("Token refresh failed:", error.message);
2679
2328
  * // Redirect to login
2680
2329
  * } else {
2681
2330
  * console.log("Token refreshed successfully");
@@ -2930,90 +2579,6 @@ var AuthClient = class extends StorefrontAPIClient {
2930
2579
  return this.executeRequest(() => this.client.PUT("/auth/user/{id}/deactivate", { params: { path: pathParams } }));
2931
2580
  }
2932
2581
  /**
2933
- * Get user notification preferences
2934
- *
2935
- * @param pathParams - Path parameters containing user ID
2936
- * @returns Promise with user's notification preferences
2937
- * @example
2938
- * ```typescript
2939
- * // Get user's notification preferences
2940
- * const { data, error } = await sdk.auth.getUserNotificationPreferences({
2941
- * id: "01H9XYZ12345USERID"
2942
- * });
2943
- *
2944
- * if (error) {
2945
- * console.error("Failed to get preferences:", error.message);
2946
- * } else {
2947
- * console.log("Notification preferences:", data.notification_preferences);
2948
- * }
2949
- * ```
2950
- */
2951
- async getUserNotificationPreferences(pathParams) {
2952
- return this.executeRequest(() => this.client.GET("/auth/user/{id}/notification-preferences", { params: { path: pathParams } }));
2953
- }
2954
- /**
2955
- * Update user notification preferences
2956
- *
2957
- * @param pathParams - Path parameters containing user ID
2958
- * @param body - Updated notification preferences
2959
- * @returns Promise with updated notification preferences
2960
- * @example
2961
- * ```typescript
2962
- * // Update user's notification preferences
2963
- * const { data, error } = await sdk.auth.updateUserNotificationPreferences(
2964
- * { id: "01H9XYZ12345USERID" },
2965
- * {
2966
- * email_notifications: true,
2967
- * sms_notifications: false,
2968
- * push_notifications: true
2969
- * }
2970
- * );
2971
- *
2972
- * if (error) {
2973
- * console.error("Failed to update preferences:", error.message);
2974
- * } else {
2975
- * console.log("Preferences updated successfully");
2976
- * }
2977
- * ```
2978
- */
2979
- async updateUserNotificationPreferences(pathParams, body) {
2980
- return this.executeRequest(() => this.client.PUT("/auth/user/{id}/notification-preferences", {
2981
- params: { path: pathParams },
2982
- body
2983
- }));
2984
- }
2985
- /**
2986
- * Create user notification preference
2987
- *
2988
- * @param pathParams - Path parameters containing user ID
2989
- * @param body - Notification preferences to create
2990
- * @returns Promise with created notification preferences
2991
- * @example
2992
- * ```typescript
2993
- * // Create notification preferences for a user
2994
- * const { data, error } = await sdk.auth.createUserNotificationPreference(
2995
- * { id: "01H9XYZ12345USERID" },
2996
- * {
2997
- * email_notifications: true,
2998
- * sms_notifications: true,
2999
- * push_notifications: false
3000
- * }
3001
- * );
3002
- *
3003
- * if (error) {
3004
- * console.error("Failed to create preferences:", error.message);
3005
- * } else {
3006
- * console.log("Preferences created successfully");
3007
- * }
3008
- * ```
3009
- */
3010
- async createUserNotificationPreference(pathParams, body) {
3011
- return this.executeRequest(() => this.client.POST("/auth/user/{id}/notification-preferences", {
3012
- params: { path: pathParams },
3013
- body
3014
- }));
3015
- }
3016
- /**
3017
2582
  * Generate OTP
3018
2583
  *
3019
2584
  * @param body - OTP generation body (phone or email)
@@ -3073,7 +2638,7 @@ var AuthClient = class extends StorefrontAPIClient {
3073
2638
  /**
3074
2639
  * Client for interacting with order endpoints
3075
2640
  */
3076
- var OrderClient = class extends StorefrontAPIClient {
2641
+ var OrderClient = class extends SessionStorefrontAPIClient {
3077
2642
  /**
3078
2643
  * Get order details
3079
2644
  *
@@ -3204,41 +2769,9 @@ var OrderClient = class extends StorefrontAPIClient {
3204
2769
  async createOrder(body) {
3205
2770
  return this.executeRequest(() => this.client.POST("/orders", { body }));
3206
2771
  }
3207
- /**
3208
- * List all orders
3209
- *
3210
- * @param queryParams - Query parameters for filtering and pagination
3211
- * @returns Promise with order list
3212
- * @example
3213
- * ```typescript
3214
- * // Basic usage - only required parameter
3215
- * const { data, error } = await sdk.order.listOrders({
3216
- * user_id: "user_01H9XYZ12345ABCDE"
3217
- * });
3218
- *
3219
- * // Advanced usage with optional parameters
3220
- * const { data, error } = await sdk.order.listOrders({
3221
- * user_id: "user_01H9XYZ12345ABCDE",
3222
- * page: 1,
3223
- * limit: 20,
3224
- * sort_by: '{"created_at":"desc"}',
3225
- * status: ["confirmed", "shipped", "delivered"]
3226
- * });
3227
- *
3228
- * if (error) {
3229
- * console.error("Failed to list orders:", error.message);
3230
- * } else {
3231
- * console.log("Orders found:", data.orders?.length || 0);
3232
- * console.log("Pagination:", data.pagination);
3233
- *
3234
- * data.orders?.forEach(order => {
3235
- * console.log(`Order ${order.order_number}: ${order.status}`);
3236
- * });
3237
- * }
3238
- * ```
3239
- */
3240
- async listOrders(queryParams) {
3241
- return this.executeRequest(() => this.client.GET("/orders", { params: { query: queryParams } }));
2772
+ async listOrders(queryParams = {}) {
2773
+ const resolvedQueryParams = await this.resolveUserQueryParams(queryParams);
2774
+ return this.executeRequest(() => this.client.GET("/orders", { params: { query: resolvedQueryParams } }));
3242
2775
  }
3243
2776
  /**
3244
2777
  * Get payment status for an order
@@ -3469,7 +3002,7 @@ var OrderClient = class extends StorefrontAPIClient {
3469
3002
  /**
3470
3003
  * Client for interacting with payment endpoints
3471
3004
  */
3472
- var PaymentsClient = class extends StorefrontAPIClient {
3005
+ var PaymentsClient = class extends SessionStorefrontAPIClient {
3473
3006
  /**
3474
3007
  * List all available payment methods
3475
3008
  *
@@ -3611,474 +3144,1094 @@ var PaymentsClient = class extends StorefrontAPIClient {
3611
3144
  }
3612
3145
  };
3613
3146
 
3147
+ //#endregion
3148
+ //#region src/lib/shared/helper.ts
3149
+ /**
3150
+ * Client for interacting with helper endpoints
3151
+ */
3152
+ var BaseHelpersClient = class extends StorefrontAPIClientBase {
3153
+ /**
3154
+ * Get a list of countries
3155
+ *
3156
+ * @returns Promise with countries
3157
+ *
3158
+ * @example
3159
+ * ```typescript
3160
+ * const { data, error } = await sdk.helpers.listCountries();
3161
+ *
3162
+ * if (error) {
3163
+ * console.error("Failed to get countries:", error);
3164
+ * return;
3165
+ * }
3166
+ *
3167
+ * console.log("Countries found:", data.countries?.length || 0);
3168
+ *
3169
+ * data.countries?.forEach(country => {
3170
+ * console.log(`Country: ${country.country_name} (${country.country_iso_code})`);
3171
+ * console.log("Currency:", country.currency_code);
3172
+ * });
3173
+ * ```
3174
+ */
3175
+ async listCountries() {
3176
+ return this.executeRequest(() => this.client.GET("/common/countries", {}));
3177
+ }
3178
+ /**
3179
+ * Get a list of states for a country
3180
+ *
3181
+ * @param pathParams - Path parameters
3182
+ * @returns Promise with states
3183
+ *
3184
+ * @example
3185
+ * ```typescript
3186
+ * const { data, error } = await sdk.helpers.listCountryStates({
3187
+ * country_iso_code: "IN"
3188
+ * });
3189
+ *
3190
+ * if (error) {
3191
+ * console.error("Failed to get states:", error);
3192
+ * return;
3193
+ * }
3194
+ *
3195
+ * console.log("States found:", data.states?.length || 0);
3196
+ *
3197
+ * data.states?.forEach(state => {
3198
+ * console.log(`State: ${state.name} (${state.iso_code})`);
3199
+ * });
3200
+ *
3201
+ * // Get states for different country
3202
+ * const { data: usStates, error: usError } = await sdk.helpers.listCountryStates({
3203
+ * country_iso_code: "US"
3204
+ * });
3205
+ *
3206
+ * if (usError) {
3207
+ * console.error("Failed to get US states:", usError);
3208
+ * return;
3209
+ * }
3210
+ *
3211
+ * console.log("US States:", usStates.states?.map(s => s.name).join(", "));
3212
+ * ```
3213
+ */
3214
+ async listCountryStates(pathParams) {
3215
+ return this.executeRequest(() => this.client.GET("/common/countries/{country_iso_code}/states", { params: { path: pathParams } }));
3216
+ }
3217
+ /**
3218
+ * Get pincodes for a country
3219
+ *
3220
+ * @param pathParams - Path parameters
3221
+ * @returns Promise with pincodes
3222
+ *
3223
+ * @example
3224
+ * ```typescript
3225
+ * const { data, error } = await sdk.helpers.listCountryPincodes({
3226
+ * country_iso_code: "IN"
3227
+ * });
3228
+ *
3229
+ * if (error) {
3230
+ * console.error("Failed to get pincodes:", error);
3231
+ * return;
3232
+ * }
3233
+ *
3234
+ * console.log("Pincodes found:", data.pincodes?.length || 0);
3235
+ *
3236
+ * data.pincodes?.forEach(pincode => {
3237
+ * console.log(`Pincode: ${pincode.pincode} - ${pincode.city}, ${pincode.state_name}`);
3238
+ * });
3239
+ *
3240
+ * // Get pincodes for different country
3241
+ * const { data: usPincodes, error: usError } = await sdk.helpers.listCountryPincodes({
3242
+ * country_iso_code: "US"
3243
+ * });
3244
+ *
3245
+ * if (usError) {
3246
+ * console.error("Failed to get US pincodes:", usError);
3247
+ * return;
3248
+ * }
3249
+ *
3250
+ * console.log("US Pincodes:", usPincodes.pincodes?.map(p => p.pincode).join(", "));
3251
+ * ```
3252
+ */
3253
+ async listCountryPincodes(pathParams, queryParams) {
3254
+ return this.executeRequest(() => this.client.GET("/common/countries/{country_iso_code}/pincodes", { params: {
3255
+ path: pathParams,
3256
+ query: queryParams
3257
+ } }));
3258
+ }
3259
+ };
3260
+
3261
+ //#endregion
3262
+ //#region src/lib/public/helper.ts
3263
+ /**
3264
+ * Client for interacting with helper endpoints
3265
+ */
3266
+ var PublicHelpersClient = class extends BaseHelpersClient {
3267
+ constructor(config) {
3268
+ super(config);
3269
+ attachPublicAuth(this);
3270
+ }
3271
+ };
3272
+
3614
3273
  //#endregion
3615
3274
  //#region src/lib/helper.ts
3616
3275
  /**
3617
3276
  * Client for interacting with helper endpoints
3618
3277
  */
3619
- var HelpersClient = class extends StorefrontAPIClient {
3278
+ var HelpersClient = class extends BaseHelpersClient {
3279
+ constructor(config) {
3280
+ super(config);
3281
+ attachSessionAuth(this, config.sessionManager);
3282
+ }
3283
+ };
3284
+
3285
+ //#endregion
3286
+ //#region src/lib/customer.ts
3287
+ /**
3288
+ * Client for interacting with customer endpoints
3289
+ */
3290
+ var CustomerClient = class extends SessionStorefrontAPIClient {
3291
+ async listAddresses(pathParamsOrQuery, queryParams) {
3292
+ const hasPathParams = queryParams !== void 0 || !!pathParamsOrQuery && typeof pathParamsOrQuery === "object" && "customer_id" in pathParamsOrQuery;
3293
+ const pathParams = hasPathParams ? pathParamsOrQuery : {};
3294
+ const resolvedPathParams = await this.resolveCustomerPathParams(pathParams);
3295
+ const resolvedQueryParams = hasPathParams ? queryParams : pathParamsOrQuery;
3296
+ return this.executeRequest(() => this.client.GET("/customers/{customer_id}/addresses", { params: {
3297
+ path: resolvedPathParams,
3298
+ query: resolvedQueryParams
3299
+ } }));
3300
+ }
3301
+ async createAddress(pathParamsOrBody, maybeBody) {
3302
+ const pathParams = maybeBody ? pathParamsOrBody : {};
3303
+ const body = maybeBody ?? pathParamsOrBody;
3304
+ const resolvedPathParams = await this.resolveCustomerPathParams(pathParams);
3305
+ return this.executeRequest(() => this.client.POST("/customers/{customer_id}/addresses", {
3306
+ params: { path: resolvedPathParams },
3307
+ body
3308
+ }));
3309
+ }
3310
+ async getAddress(pathParams) {
3311
+ const resolvedPathParams = await this.resolveCustomerPathParams(pathParams);
3312
+ return this.executeRequest(() => this.client.GET("/customers/{customer_id}/addresses/{address_id}", { params: { path: resolvedPathParams } }));
3313
+ }
3314
+ async updateAddress(pathParams, body) {
3315
+ const resolvedPathParams = await this.resolveCustomerPathParams(pathParams);
3316
+ return this.executeRequest(() => this.client.PUT("/customers/{customer_id}/addresses/{address_id}", {
3317
+ params: { path: resolvedPathParams },
3318
+ body
3319
+ }));
3320
+ }
3321
+ async deleteAddress(pathParams) {
3322
+ const resolvedPathParams = await this.resolveCustomerPathParams(pathParams);
3323
+ return this.executeRequest(() => this.client.DELETE("/customers/{customer_id}/addresses/{address_id}", { params: { path: resolvedPathParams } }));
3324
+ }
3325
+ async getLoyaltyDetails(pathParams = {}) {
3326
+ const resolvedPathParams = await this.resolveCustomerPathParams(pathParams);
3327
+ return this.executeRequest(() => this.client.GET("/customers/{customer_id}/loyalty", { params: { path: resolvedPathParams } }));
3328
+ }
3329
+ async listLoyaltyPointsActivity(pathParamsOrQuery, queryParams) {
3330
+ const hasPathParams = queryParams !== void 0 || !!pathParamsOrQuery && typeof pathParamsOrQuery === "object" && "customer_id" in pathParamsOrQuery;
3331
+ const pathParams = hasPathParams ? pathParamsOrQuery : {};
3332
+ const resolvedPathParams = await this.resolveCustomerPathParams(pathParams);
3333
+ const resolvedQueryParams = hasPathParams ? queryParams : pathParamsOrQuery;
3334
+ return this.executeRequest(() => this.client.GET("/customers/{customer_id}/loyalty-points-activity", { params: {
3335
+ path: resolvedPathParams,
3336
+ query: resolvedQueryParams
3337
+ } }));
3338
+ }
3339
+ async listCustomerReviews(pathParams = {}) {
3340
+ const resolvedPathParams = await this.resolveCustomerPathParams(pathParams);
3341
+ return this.executeRequest(() => this.client.GET("/customers/{customer_id}/reviews", { params: { path: resolvedPathParams } }));
3342
+ }
3343
+ async listSavedPaymentMethods(pathParams = {}, queryParams) {
3344
+ const resolvedPathParams = await this.resolveCustomerPathParams(pathParams);
3345
+ return this.executeRequest(() => this.client.GET("/customers/{customer_id}/payment-methods", { params: {
3346
+ path: resolvedPathParams,
3347
+ query: queryParams
3348
+ } }));
3349
+ }
3350
+ async listCustomerCards(pathParams = {}) {
3351
+ const resolvedPathParams = await this.resolveCustomerPathParams(pathParams);
3352
+ return this.executeRequest(() => this.client.GET("/customers/{customer_id}/cards", { params: { path: resolvedPathParams } }));
3353
+ }
3354
+ };
3355
+
3356
+ //#endregion
3357
+ //#region src/lib/shared/store-config.ts
3358
+ /**
3359
+ * Client for interacting with store config endpoints
3360
+ */
3361
+ var BaseStoreConfigClient = class extends StorefrontAPIClientBase {
3362
+ /**
3363
+ * Check store existence
3364
+ *
3365
+ * @description Checks whether a store with the configured store ID exists.
3366
+ * Returns `success: true` if the store exists, `false` otherwise.
3367
+ *
3368
+ * @returns Promise with store existence check result
3369
+ * @example
3370
+ * ```typescript
3371
+ * const { data, error } = await sdk.store.checkStore();
3372
+ *
3373
+ * if (error) {
3374
+ * console.error("Failed to check store:", error.message);
3375
+ * } else {
3376
+ * console.log("Store exists:", data.success);
3377
+ * }
3378
+ * ```
3379
+ */
3380
+ async checkStore() {
3381
+ return this.executeRequest(() => this.client.GET("/store/check"));
3382
+ }
3383
+ /**
3384
+ * Get store config
3385
+ *
3386
+ * @returns Promise with store configuration data
3387
+ *
3388
+ * @example
3389
+ * ```typescript
3390
+ * const { data, error } = await sdk.store.getStoreConfig();
3391
+ *
3392
+ * if (error) {
3393
+ * console.error('Failed to get store config:', error.message);
3394
+ * return;
3395
+ * }
3396
+ *
3397
+ * // Access store configuration data
3398
+ * const storeConfig = data.store_config;
3399
+ * console.log('Store brand:', storeConfig.brand.name);
3400
+ * console.log('Currency:', storeConfig.currency.code);
3401
+ * console.log('KYC enabled:', storeConfig.is_kyc_enabled);
3402
+ * console.log('Customer groups enabled:', storeConfig.is_customer_group_enabled);
3403
+ * ```
3404
+ */
3405
+ async getStoreConfig() {
3406
+ return this.executeRequest(() => this.client.GET("/store/config"));
3407
+ }
3408
+ };
3409
+
3410
+ //#endregion
3411
+ //#region src/lib/public/store-config.ts
3412
+ /**
3413
+ * Client for interacting with store config endpoints
3414
+ */
3415
+ var PublicStoreConfigClient = class extends BaseStoreConfigClient {
3416
+ constructor(config) {
3417
+ super(config);
3418
+ attachPublicAuth(this);
3419
+ }
3420
+ };
3421
+
3422
+ //#endregion
3423
+ //#region src/lib/store-config.ts
3424
+ /**
3425
+ * Client for interacting with store config endpoints
3426
+ */
3427
+ var StoreConfigClient = class extends BaseStoreConfigClient {
3428
+ constructor(config) {
3429
+ super(config);
3430
+ attachSessionAuth(this, config.sessionManager);
3431
+ }
3432
+ };
3433
+
3434
+ //#endregion
3435
+ //#region src/lib/storage/token-storage.ts
3436
+ /**
3437
+ * Simple in-memory token storage implementation
3438
+ */
3439
+ var MemoryTokenStorage = class {
3440
+ accessToken = null;
3441
+ refreshToken = null;
3442
+ async getAccessToken() {
3443
+ return this.accessToken;
3444
+ }
3445
+ async setAccessToken(token) {
3446
+ this.accessToken = token;
3447
+ }
3448
+ async getRefreshToken() {
3449
+ return this.refreshToken;
3450
+ }
3451
+ async setRefreshToken(token) {
3452
+ this.refreshToken = token;
3453
+ }
3454
+ async clearTokens() {
3455
+ this.accessToken = null;
3456
+ this.refreshToken = null;
3457
+ }
3458
+ };
3459
+ /**
3460
+ * Browser localStorage token storage implementation
3461
+ */
3462
+ var BrowserTokenStorage = class {
3463
+ accessTokenKey;
3464
+ refreshTokenKey;
3465
+ constructor(prefix = "storefront_") {
3466
+ this.accessTokenKey = `${prefix}access_token`;
3467
+ this.refreshTokenKey = `${prefix}refresh_token`;
3468
+ }
3469
+ async getAccessToken() {
3470
+ if (typeof localStorage === "undefined") return null;
3471
+ return localStorage.getItem(this.accessTokenKey);
3472
+ }
3473
+ async setAccessToken(token) {
3474
+ if (typeof localStorage !== "undefined") localStorage.setItem(this.accessTokenKey, token);
3475
+ }
3476
+ async getRefreshToken() {
3477
+ if (typeof localStorage === "undefined") return null;
3478
+ return localStorage.getItem(this.refreshTokenKey);
3479
+ }
3480
+ async setRefreshToken(token) {
3481
+ if (typeof localStorage !== "undefined") localStorage.setItem(this.refreshTokenKey, token);
3482
+ }
3483
+ async clearTokens() {
3484
+ if (typeof localStorage !== "undefined") {
3485
+ localStorage.removeItem(this.accessTokenKey);
3486
+ localStorage.removeItem(this.refreshTokenKey);
3487
+ }
3488
+ }
3489
+ };
3490
+ /**
3491
+ * Cookie-based token storage implementation
3492
+ */
3493
+ var CookieTokenStorage = class {
3494
+ accessTokenKey;
3495
+ refreshTokenKey;
3496
+ options;
3497
+ constructor(options = {}) {
3498
+ const prefix = options.prefix || "storefront_";
3499
+ this.accessTokenKey = `${prefix}access_token`;
3500
+ this.refreshTokenKey = `${prefix}refresh_token`;
3501
+ this.options = {
3502
+ maxAge: options.maxAge || 10080 * 60,
3503
+ path: options.path || "/",
3504
+ domain: options.domain,
3505
+ secure: options.secure ?? (typeof window !== "undefined" && window.location?.protocol === "https:"),
3506
+ sameSite: options.sameSite || "Lax",
3507
+ httpOnly: false
3508
+ };
3509
+ }
3510
+ async getAccessToken() {
3511
+ return this.getCookie(this.accessTokenKey);
3512
+ }
3513
+ async setAccessToken(token) {
3514
+ this.setCookie(this.accessTokenKey, token);
3515
+ }
3516
+ async getRefreshToken() {
3517
+ return this.getCookie(this.refreshTokenKey);
3518
+ }
3519
+ async setRefreshToken(token) {
3520
+ this.setCookie(this.refreshTokenKey, token);
3521
+ }
3522
+ async clearTokens() {
3523
+ this.deleteCookie(this.accessTokenKey);
3524
+ this.deleteCookie(this.refreshTokenKey);
3525
+ }
3526
+ getCookie(name) {
3527
+ if (typeof document === "undefined") return null;
3528
+ const parts = `; ${document.cookie}`.split(`; ${name}=`);
3529
+ if (parts.length === 2) {
3530
+ const cookieValue = parts.pop()?.split(";").shift();
3531
+ return cookieValue ? decodeURIComponent(cookieValue) : null;
3532
+ }
3533
+ return null;
3534
+ }
3535
+ setCookie(name, value) {
3536
+ if (typeof document === "undefined") return;
3537
+ let cookieString = `${name}=${encodeURIComponent(value)}`;
3538
+ if (this.options.maxAge) cookieString += `; Max-Age=${this.options.maxAge}`;
3539
+ if (this.options.path) cookieString += `; Path=${this.options.path}`;
3540
+ if (this.options.domain) cookieString += `; Domain=${this.options.domain}`;
3541
+ if (this.options.secure) cookieString += `; Secure`;
3542
+ if (this.options.sameSite) cookieString += `; SameSite=${this.options.sameSite}`;
3543
+ document.cookie = cookieString;
3544
+ }
3545
+ deleteCookie(name) {
3546
+ if (typeof document === "undefined") return;
3547
+ let cookieString = `${name}=; Max-Age=0`;
3548
+ if (this.options.path) cookieString += `; Path=${this.options.path}`;
3549
+ if (this.options.domain) cookieString += `; Domain=${this.options.domain}`;
3550
+ document.cookie = cookieString;
3551
+ }
3552
+ };
3553
+
3554
+ //#endregion
3555
+ //#region src/lib/session/jwt-utils.ts
3556
+ /**
3557
+ * Decode a JWT token payload without signature verification.
3558
+ * This is a lightweight replacement for jose's decodeJwt.
3559
+ *
3560
+ * @param token - The JWT token to decode
3561
+ * @returns The decoded payload
3562
+ * @throws Error if the token is malformed
3563
+ */
3564
+ function decodeJwt(token) {
3565
+ if (typeof token !== "string") throw new Error("Invalid token: must be a string");
3566
+ const parts = token.split(".");
3567
+ if (parts.length !== 3) throw new Error("Invalid token: must have 3 parts");
3568
+ const base64Url = parts[1];
3569
+ if (!base64Url) throw new Error("Invalid token: missing payload");
3570
+ let base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
3571
+ const padding = base64.length % 4;
3572
+ if (padding) base64 += "=".repeat(4 - padding);
3573
+ const binaryStr = atob(base64);
3574
+ const bytes = new Uint8Array(binaryStr.length);
3575
+ for (let i = 0; i < binaryStr.length; i++) bytes[i] = binaryStr.charCodeAt(i);
3576
+ const payload = JSON.parse(new TextDecoder().decode(bytes));
3577
+ if (typeof payload !== "object" || payload === null) throw new Error("Invalid token: payload must be an object");
3578
+ return payload;
3579
+ }
3580
+ /**
3581
+ * Decode and extract user information from a JWT token
3582
+ *
3583
+ * @param token - The JWT token to decode
3584
+ * @returns User information or null if token is invalid
3585
+ */
3586
+ function extractUserInfoFromToken(token) {
3587
+ try {
3588
+ const payload = decodeJwt(token);
3589
+ return {
3590
+ id: payload.ulid,
3591
+ email: payload.email,
3592
+ phone: payload.phone,
3593
+ username: payload.username,
3594
+ firstName: payload.first_name,
3595
+ lastName: payload.last_name,
3596
+ storeId: payload.store_id,
3597
+ isLoggedIn: payload.is_logged_in,
3598
+ isAnonymous: payload.is_anonymous,
3599
+ customerId: payload.customer_id,
3600
+ customerGroupId: payload.customer_group_id,
3601
+ anonymousId: payload.anonymous_id,
3602
+ channel: payload.channel,
3603
+ tokenExpiry: /* @__PURE__ */ new Date(payload.exp * 1e3),
3604
+ tokenIssuedAt: /* @__PURE__ */ new Date(payload.iat * 1e3)
3605
+ };
3606
+ } catch (error) {
3607
+ console.warn("Failed to decode JWT token:", error);
3608
+ return null;
3609
+ }
3610
+ }
3611
+ /**
3612
+ * Check if a JWT token is expired
3613
+ *
3614
+ * @param token - The JWT token to check
3615
+ * @param bufferSeconds - Buffer time in seconds (default: 30)
3616
+ * @returns True if token is expired or will expire within buffer time
3617
+ */
3618
+ function isTokenExpired(token, bufferSeconds = 30) {
3619
+ try {
3620
+ const payload = decodeJwt(token);
3621
+ if (!payload.exp) return true;
3622
+ return Math.floor(Date.now() / 1e3) >= payload.exp - bufferSeconds;
3623
+ } catch (error) {
3624
+ console.warn("Failed to decode JWT token:", error);
3625
+ return true;
3626
+ }
3627
+ }
3628
+ /**
3629
+ * Get the user ID from a JWT token
3630
+ *
3631
+ * @param token - The JWT token
3632
+ * @returns User ID (ulid) or null if token is invalid
3633
+ */
3634
+ function getUserIdFromToken(token) {
3635
+ return extractUserInfoFromToken(token)?.id || null;
3636
+ }
3637
+ /**
3638
+ * Check if user is logged in based on JWT token
3639
+ *
3640
+ * @param token - The JWT token
3641
+ * @returns True if user is logged in, false otherwise
3642
+ */
3643
+ function isUserLoggedIn(token) {
3644
+ return extractUserInfoFromToken(token)?.isLoggedIn || false;
3645
+ }
3646
+ /**
3647
+ * Check if user is anonymous based on JWT token
3648
+ *
3649
+ * @param token - The JWT token
3650
+ * @returns True if user is anonymous, false otherwise
3651
+ */
3652
+ function isUserAnonymous(token) {
3653
+ return extractUserInfoFromToken(token)?.isAnonymous ?? true;
3654
+ }
3655
+
3656
+ //#endregion
3657
+ //#region src/types/auth-operation-metadata.ts
3658
+ const API_KEY_ONLY_OPERATIONS = [
3659
+ {
3660
+ method: "POST",
3661
+ path: "/auth/anonymous"
3662
+ },
3663
+ {
3664
+ method: "GET",
3665
+ path: "/common/countries"
3666
+ },
3667
+ {
3668
+ method: "GET",
3669
+ path: "/common/countries/{country_iso_code}/pincodes"
3670
+ },
3671
+ {
3672
+ method: "GET",
3673
+ path: "/common/countries/{country_iso_code}/states"
3674
+ },
3675
+ {
3676
+ method: "GET",
3677
+ path: "/store/check"
3678
+ },
3679
+ {
3680
+ method: "GET",
3681
+ path: "/store/config"
3682
+ },
3683
+ {
3684
+ method: "GET",
3685
+ path: "/store/kyc-document"
3686
+ },
3687
+ {
3688
+ method: "GET",
3689
+ path: "/store/sellers/{id}"
3690
+ },
3691
+ {
3692
+ method: "GET",
3693
+ path: "/store/sellers/{id}/reviews"
3694
+ }
3695
+ ];
3696
+
3697
+ //#endregion
3698
+ //#region src/lib/session/auth-utils.ts
3699
+ const TOKEN_RETURNING_OPERATIONS = [
3700
+ {
3701
+ method: "POST",
3702
+ path: "/auth/change-password"
3703
+ },
3704
+ {
3705
+ method: "POST",
3706
+ path: "/auth/login/password"
3707
+ },
3708
+ {
3709
+ method: "POST",
3710
+ path: "/auth/reset-password"
3711
+ },
3712
+ {
3713
+ method: "POST",
3714
+ path: "/auth/verify-otp"
3715
+ },
3716
+ {
3717
+ method: "POST",
3718
+ path: "/auth/refresh-token"
3719
+ },
3720
+ {
3721
+ method: "POST",
3722
+ path: "/auth/logout"
3723
+ }
3724
+ ];
3725
+ const ANONYMOUS_AUTH_OPERATION = {
3726
+ method: "POST",
3727
+ path: "/auth/anonymous"
3728
+ };
3729
+ function normalizeMethod(method) {
3730
+ return method.toUpperCase();
3731
+ }
3732
+ function normalizePathname(pathname) {
3733
+ let normalizedPathname = pathname;
3734
+ const storefrontMarkerIndex = normalizedPathname.indexOf("/storefront");
3735
+ if (storefrontMarkerIndex !== -1) normalizedPathname = normalizedPathname.slice(storefrontMarkerIndex + 11) || "/";
3736
+ if (normalizedPathname.length > 1 && normalizedPathname.endsWith("/")) return normalizedPathname.slice(0, -1);
3737
+ return normalizedPathname;
3738
+ }
3739
+ function matchesPathTemplate(pathTemplate, pathname) {
3740
+ const normalizedTemplate = normalizePathname(pathTemplate);
3741
+ const normalizedPathname = normalizePathname(pathname);
3742
+ const templateSegments = normalizedTemplate.split("/").filter(Boolean);
3743
+ const pathSegments = normalizedPathname.split("/").filter(Boolean);
3744
+ if (templateSegments.length !== pathSegments.length) return false;
3745
+ return templateSegments.every((templateSegment, index) => {
3746
+ if (templateSegment.startsWith("{") && templateSegment.endsWith("}")) return true;
3747
+ return templateSegment === pathSegments[index];
3748
+ });
3749
+ }
3750
+ function matchesOperation(method, pathname, operation) {
3751
+ return normalizeMethod(method) === operation.method && matchesPathTemplate(operation.path, pathname);
3752
+ }
3753
+ function matchesOperationList(method, pathname, operations) {
3754
+ return operations.some((operation) => matchesOperation(method, pathname, operation));
3755
+ }
3756
+ /**
3757
+ * Check if a method+path pair is the anonymous auth operation.
3758
+ */
3759
+ function isAnonymousAuthOperation(method, pathname) {
3760
+ return matchesOperation(method, pathname, ANONYMOUS_AUTH_OPERATION);
3761
+ }
3762
+ /**
3763
+ * Check if a method+path pair requires API key only authentication.
3764
+ */
3765
+ function isApiKeyOnlyOperation(method, pathname) {
3766
+ return matchesOperationList(method, pathname, API_KEY_ONLY_OPERATIONS);
3767
+ }
3768
+ /**
3769
+ * Check if a method+path pair returns tokens in a successful response.
3770
+ */
3771
+ function isTokenReturningOperation(method, pathname) {
3772
+ return matchesOperationList(method, pathname, TOKEN_RETURNING_OPERATIONS);
3773
+ }
3774
+
3775
+ //#endregion
3776
+ //#region src/lib/session/manager.ts
3777
+ /**
3778
+ * Internal per-SDK session controller.
3779
+ *
3780
+ * A single instance is shared by all API clients created from the same
3781
+ * `SessionStorefrontSDK`. This keeps refresh locks, token assessment, and session
3782
+ * bootstrap logic coordinated in one place while preserving optional manual
3783
+ * token management when `tokenStorage` is not provided.
3784
+ */
3785
+ var StorefrontSessionManager = class {
3786
+ tokenStorage;
3787
+ onTokensUpdated;
3788
+ onTokensCleared;
3789
+ baseUrl;
3790
+ refreshTokenFn;
3791
+ apiKey;
3792
+ accessToken;
3793
+ refreshToken;
3794
+ initializationPromise = null;
3795
+ refreshPromise = null;
3796
+ bootstrapPromise = null;
3797
+ hasAssessedTokens = false;
3798
+ constructor(options) {
3799
+ this.tokenStorage = options.tokenStorage;
3800
+ this.baseUrl = options.baseUrl;
3801
+ this.apiKey = options.apiKey;
3802
+ this.onTokensUpdated = options.onTokensUpdated;
3803
+ this.onTokensCleared = options.onTokensCleared;
3804
+ this.accessToken = options.accessToken ?? null;
3805
+ this.refreshToken = options.refreshToken ?? null;
3806
+ this.refreshTokenFn = options.refreshTokenFn;
3807
+ if (this.tokenStorage && this.accessToken) this.initializationPromise = this.initializeManagedTokens(this.accessToken, this.refreshToken);
3808
+ }
3809
+ /**
3810
+ * Update the API key used by session bootstrap and refresh flows.
3811
+ */
3812
+ setApiKey(apiKey) {
3813
+ this.apiKey = apiKey;
3814
+ }
3620
3815
  /**
3621
- * Get a list of countries
3622
- *
3623
- * @returns Promise with countries
3624
- *
3625
- * @example
3626
- * ```typescript
3627
- * const { data, error } = await sdk.helpers.listCountries();
3628
- *
3629
- * if (error) {
3630
- * console.error("Failed to get countries:", error);
3631
- * return;
3632
- * }
3633
- *
3634
- * console.log("Countries found:", data.countries?.length || 0);
3635
- *
3636
- * data.countries?.forEach(country => {
3637
- * console.log(`Country: ${country.country_name} (${country.country_iso_code})`);
3638
- * console.log("Currency:", country.currency_code);
3639
- * });
3640
- * ```
3816
+ * Remove the API key used by session bootstrap and refresh flows.
3641
3817
  */
3642
- async listCountries() {
3643
- return this.executeRequest(() => this.client.GET("/common/countries", {}));
3818
+ clearApiKey() {
3819
+ this.apiKey = void 0;
3644
3820
  }
3645
3821
  /**
3646
- * Get a list of states for a country
3647
- *
3648
- * @param pathParams - Path parameters
3649
- * @returns Promise with states
3650
- *
3651
- * @example
3652
- * ```typescript
3653
- * const { data, error } = await sdk.helpers.listCountryStates({
3654
- * country_iso_code: "IN"
3655
- * });
3656
- *
3657
- * if (error) {
3658
- * console.error("Failed to get states:", error);
3659
- * return;
3660
- * }
3661
- *
3662
- * console.log("States found:", data.states?.length || 0);
3663
- *
3664
- * data.states?.forEach(state => {
3665
- * console.log(`State: ${state.name} (${state.iso_code})`);
3666
- * });
3667
- *
3668
- * // Get states for different country
3669
- * const { data: usStates, error: usError } = await sdk.helpers.listCountryStates({
3670
- * country_iso_code: "US"
3671
- * });
3672
- *
3673
- * if (usError) {
3674
- * console.error("Failed to get US states:", usError);
3675
- * return;
3676
- * }
3822
+ * Create the auth middleware used by each API client.
3677
3823
  *
3678
- * console.log("US States:", usStates.states?.map(s => s.name).join(", "));
3679
- * ```
3824
+ * The returned middleware shares the same session state and refresh locking
3825
+ * across all clients attached to a single `SessionStorefrontSDK` instance.
3680
3826
  */
3681
- async listCountryStates(pathParams) {
3682
- return this.executeRequest(() => this.client.GET("/common/countries/{country_iso_code}/states", { params: { path: pathParams } }));
3827
+ createAuthMiddleware() {
3828
+ return {
3829
+ onRequest: async ({ request }) => this.handleRequest(request),
3830
+ onResponse: async ({ request, response }) => this.handleResponse(request, response)
3831
+ };
3683
3832
  }
3684
3833
  /**
3685
- * Get pincodes for a country
3686
- *
3687
- * @param pathParams - Path parameters
3688
- * @returns Promise with pincodes
3689
- *
3690
- * @example
3691
- * ```typescript
3692
- * const { data, error } = await sdk.helpers.listCountryPincodes({
3693
- * country_iso_code: "IN"
3694
- * });
3695
- *
3696
- * if (error) {
3697
- * console.error("Failed to get pincodes:", error);
3698
- * return;
3699
- * }
3700
- *
3701
- * console.log("Pincodes found:", data.pincodes?.length || 0);
3702
- *
3703
- * data.pincodes?.forEach(pincode => {
3704
- * console.log(`Pincode: ${pincode.pincode} - ${pincode.city}, ${pincode.state_name}`);
3705
- * });
3706
- *
3707
- * // Get pincodes for different country
3708
- * const { data: usPincodes, error: usError } = await sdk.helpers.listCountryPincodes({
3709
- * country_iso_code: "US"
3710
- * });
3834
+ * Read the current authorization header without creating a new session.
3711
3835
  *
3712
- * if (usError) {
3713
- * console.error("Failed to get US pincodes:", usError);
3714
- * return;
3715
- * }
3716
- *
3717
- * console.log("US Pincodes:", usPincodes.pincodes?.map(p => p.pincode).join(", "));
3718
- * ```
3836
+ * @returns A `Bearer` header value, or an empty string when no token exists
3719
3837
  */
3720
- async listCountryPincodes(pathParams, queryParams) {
3721
- return this.executeRequest(() => this.client.GET("/common/countries/{country_iso_code}/pincodes", { params: {
3722
- path: pathParams,
3723
- query: queryParams
3724
- } }));
3838
+ async getAuthorizationHeader() {
3839
+ const token = await this.peekAccessToken();
3840
+ return token ? `Bearer ${token}` : "";
3725
3841
  }
3726
- };
3727
-
3728
- //#endregion
3729
- //#region src/lib/customer.ts
3730
- /**
3731
- * Client for interacting with customer endpoints
3732
- */
3733
- var CustomerClient = class extends StorefrontAPIClient {
3734
3842
  /**
3735
- * Get all saved addresses for a customer
3736
- *
3737
- * @param pathParams - Path parameters
3738
- * @returns Promise with addresses
3739
- *
3740
- * @example
3741
- * ```typescript
3742
- * const { data, error } = await sdk.customer.listAddresses({
3743
- * user_id: "user_456"
3744
- * });
3745
- *
3746
- * if (error) {
3747
- * console.error("Failed to list addresses:", error);
3748
- * return;
3749
- * }
3750
- *
3751
- * console.log("Addresses:", data.addresses);
3752
- * console.log("Pagination:", data.pagination);
3843
+ * Persist new session tokens.
3753
3844
  *
3754
- * // With pagination
3755
- * const { data: page2, error: page2Error } = await sdk.customer.listAddresses({
3756
- * user_id: "user_456",
3757
- * page: 2,
3758
- * limit: 10
3759
- * });
3760
- * ```
3845
+ * In managed mode, tokens are written to the configured token storage. In
3846
+ * manual mode, they are stored in memory for header injection and session
3847
+ * inspection.
3761
3848
  */
3762
- async listAddresses(pathParams, queryParams) {
3763
- return this.executeRequest(() => this.client.GET("/customers/{user_id}/addresses", { params: {
3764
- path: pathParams,
3765
- query: queryParams
3766
- } }));
3849
+ async setTokens(accessToken, refreshToken) {
3850
+ const previousTokens = await this.getCurrentTokenState();
3851
+ if (this.tokenStorage) {
3852
+ await this.awaitInitialization();
3853
+ await this.storeManagedTokens(accessToken, refreshToken ?? null);
3854
+ } else {
3855
+ this.accessToken = accessToken;
3856
+ if (refreshToken) {
3857
+ this.refreshToken = refreshToken;
3858
+ console.warn("Refresh token provided but automatic refresh is disabled because tokenStorage is not configured.");
3859
+ }
3860
+ }
3861
+ const nextTokens = await this.getCurrentTokenState();
3862
+ this.notifyTokensUpdatedIfChanged(previousTokens, nextTokens);
3767
3863
  }
3768
3864
  /**
3769
- * Create a new address for a customer
3770
- *
3771
- * @param pathParams - Path parameters
3772
- * @param body - Address creation body
3773
- * @returns Promise with address details
3774
- *
3775
- * @example
3776
- * ```typescript
3777
- * const { data, error } = await sdk.customer.createAddress(
3778
- * { user_id: "user_456" },
3779
- * {
3780
- * address_line1: "123 Main Street",
3781
- * address_line2: "Apt 4B",
3782
- * city: "New York",
3783
- * state: "NY",
3784
- * country: "US",
3785
- * pincode: "10001",
3786
- * is_default_billing: true,
3787
- * is_default_shipping: false
3788
- * }
3789
- * );
3790
- *
3791
- * if (error) {
3792
- * console.error("Failed to create address:", error);
3793
- * return;
3794
- * }
3795
- *
3796
- * console.log("Address created:", data.address);
3797
- * ```
3865
+ * Clear all session tokens managed by this controller.
3798
3866
  */
3799
- async createAddress(pathParams, body) {
3800
- return this.executeRequest(() => this.client.POST("/customers/{user_id}/addresses", {
3801
- params: { path: pathParams },
3802
- body
3803
- }));
3867
+ async clearTokens() {
3868
+ if (this.tokenStorage) {
3869
+ await this.awaitInitialization();
3870
+ await this.tokenStorage.clearTokens();
3871
+ } else {
3872
+ this.accessToken = null;
3873
+ this.refreshToken = null;
3874
+ }
3875
+ this.onTokensCleared?.();
3804
3876
  }
3805
3877
  /**
3806
- * Get an address for a customer
3807
- *
3808
- * @param pathParams - Path parameters
3809
- * @returns Promise with address details
3810
- *
3811
- * @example
3812
- * ```typescript
3813
- * const { data, error } = await sdk.customer.getAddress({
3814
- * user_id: "user_456",
3815
- * address_id: "addr_789"
3816
- * });
3817
- *
3818
- * if (error) {
3819
- * console.error("Failed to get address:", error);
3820
- * return;
3821
- * }
3822
- *
3823
- * console.log("Address details:", data.address);
3824
- * ```
3878
+ * Clear all session tokens through the public session interface.
3825
3879
  */
3826
- async getAddress(pathParams) {
3827
- return this.executeRequest(() => this.client.GET("/customers/{user_id}/addresses/{address_id}", { params: { path: pathParams } }));
3880
+ async clear() {
3881
+ await this.clearTokens();
3882
+ }
3883
+ async peekAccessToken() {
3884
+ await this.awaitInitialization();
3885
+ if (this.tokenStorage) return this.tokenStorage.getAccessToken();
3886
+ return this.accessToken;
3887
+ }
3888
+ async peekRefreshToken() {
3889
+ await this.awaitInitialization();
3890
+ if (this.tokenStorage) return this.tokenStorage.getRefreshToken();
3891
+ return this.refreshToken;
3892
+ }
3893
+ async peekUserInfo() {
3894
+ const token = await this.peekAccessToken();
3895
+ if (!token) return null;
3896
+ return extractUserInfoFromToken(token);
3897
+ }
3898
+ async peekUserId() {
3899
+ const token = await this.peekAccessToken();
3900
+ if (!token) return null;
3901
+ return getUserIdFromToken(token);
3902
+ }
3903
+ async peekCustomerId() {
3904
+ return (await this.peekUserInfo())?.customerId ?? null;
3905
+ }
3906
+ async peekCustomerGroupId() {
3907
+ return (await this.peekUserInfo())?.customerGroupId ?? null;
3908
+ }
3909
+ async ensureAccessToken() {
3910
+ const token = await this.getOrCreateAccessToken();
3911
+ if (!token) throw new Error("No session available. Configure tokenStorage + apiKey for automatic session management, or call setTokens() to set tokens manually.");
3912
+ return token;
3913
+ }
3914
+ async ensureUserInfo() {
3915
+ const userInfo = extractUserInfoFromToken(await this.ensureAccessToken());
3916
+ if (!userInfo) throw new Error("Failed to extract user information from access token.");
3917
+ return userInfo;
3918
+ }
3919
+ async ensureUserId() {
3920
+ const userId = getUserIdFromToken(await this.ensureAccessToken());
3921
+ if (!userId) throw new Error("Failed to extract user ID from access token.");
3922
+ return userId;
3923
+ }
3924
+ async ensureCustomerId() {
3925
+ const userInfo = await this.ensureUserInfo();
3926
+ if (!userInfo.customerId) throw new Error("No customer_id available. User must be logged in (not anonymous) for this operation. Pass customer_id explicitly or log in first.");
3927
+ return userInfo.customerId;
3928
+ }
3929
+ async handleRequest(request) {
3930
+ const method = request.method;
3931
+ const pathname = getPathnameFromUrl(request.url);
3932
+ if (this.tokenStorage) await this.assessTokenStateOnce();
3933
+ if (isAnonymousAuthOperation(method, pathname)) return this.prepareAnonymousAuthRequest(request);
3934
+ if (isApiKeyOnlyOperation(method, pathname)) {
3935
+ this.applyApiKeyHeader(request);
3936
+ return request;
3937
+ }
3938
+ const accessToken = await this.getOrCreateAccessToken();
3939
+ if (accessToken) request.headers.set("Authorization", `Bearer ${accessToken}`);
3940
+ return request;
3941
+ }
3942
+ async handleResponse(request, response) {
3943
+ if (!this.tokenStorage) return response;
3944
+ const method = request.method;
3945
+ const pathname = getPathnameFromUrl(request.url);
3946
+ if (response.ok) {
3947
+ await this.captureTokensFromResponse(method, pathname, response);
3948
+ return response;
3949
+ }
3950
+ if (response.status === 401 && !isAnonymousAuthOperation(method, pathname) && !isApiKeyOnlyOperation(method, pathname)) {
3951
+ const currentToken = await this.peekAccessToken();
3952
+ if (currentToken && isTokenExpired(currentToken, 0)) try {
3953
+ await this.refreshTokens();
3954
+ const refreshedToken = await this.peekAccessToken();
3955
+ if (refreshedToken) {
3956
+ const retryRequest = request.clone();
3957
+ retryRequest.headers.set("Authorization", `Bearer ${refreshedToken}`);
3958
+ return fetch(retryRequest);
3959
+ }
3960
+ } catch (error) {
3961
+ console.warn("Token refresh failed on 401 response:", error);
3962
+ }
3963
+ }
3964
+ return response;
3965
+ }
3966
+ async prepareAnonymousAuthRequest(request) {
3967
+ this.applyApiKeyHeader(request);
3968
+ const existingToken = await this.peekAccessToken();
3969
+ if (this.tokenStorage && existingToken && !isTokenExpired(existingToken) && isUserLoggedIn(existingToken)) return new Response(JSON.stringify({
3970
+ message: "Cannot create anonymous session while authenticated",
3971
+ success: false,
3972
+ code: "USER_ALREADY_AUTHENTICATED"
3973
+ }), {
3974
+ status: 400,
3975
+ headers: { "Content-Type": "application/json" }
3976
+ });
3977
+ if (existingToken) request.headers.set("Authorization", `Bearer ${existingToken}`);
3978
+ return request;
3979
+ }
3980
+ applyApiKeyHeader(request) {
3981
+ if (this.apiKey) request.headers.set("X-Api-Key", this.apiKey);
3982
+ }
3983
+ /**
3984
+ * Snapshot the effective token pair from storage (managed mode) or
3985
+ * in-memory fields (manual mode). Used before and after `setTokens()` to
3986
+ * detect whether the tokens actually changed.
3987
+ */
3988
+ async getCurrentTokenState() {
3989
+ await this.awaitInitialization();
3990
+ if (this.tokenStorage) return {
3991
+ accessToken: await this.tokenStorage.getAccessToken(),
3992
+ refreshToken: await this.tokenStorage.getRefreshToken()
3993
+ };
3994
+ return {
3995
+ accessToken: this.accessToken,
3996
+ refreshToken: this.refreshToken
3997
+ };
3998
+ }
3999
+ /**
4000
+ * Fire `onTokensUpdated` only when the effective token pair differs from
4001
+ * the previous snapshot. This prevents duplicate notifications when
4002
+ * `setTokens()` is called with the same values already in storage.
4003
+ */
4004
+ notifyTokensUpdatedIfChanged(previousTokens, nextTokens) {
4005
+ if (!this.onTokensUpdated || !nextTokens.accessToken) return;
4006
+ if (previousTokens.accessToken === nextTokens.accessToken && previousTokens.refreshToken === nextTokens.refreshToken) return;
4007
+ this.onTokensUpdated(nextTokens.accessToken, nextTokens.refreshToken ?? "");
4008
+ }
4009
+ /**
4010
+ * Internal token acquisition — returns null on failure instead of throwing.
4011
+ * Used by middleware so requests degrade gracefully.
4012
+ */
4013
+ async getOrCreateAccessToken() {
4014
+ if (!this.tokenStorage) return this.peekAccessToken();
4015
+ await this.awaitInitialization();
4016
+ await this.assessTokenStateOnce();
4017
+ let accessToken = await this.tokenStorage.getAccessToken();
4018
+ if (!accessToken) accessToken = await this.bootstrapAnonymousSession();
4019
+ if (accessToken && isTokenExpired(accessToken)) try {
4020
+ await this.refreshTokens();
4021
+ accessToken = await this.tokenStorage.getAccessToken();
4022
+ } catch {
4023
+ accessToken = await this.tokenStorage.getAccessToken();
4024
+ }
4025
+ return accessToken;
4026
+ }
4027
+ async initializeManagedTokens(accessToken, refreshToken) {
4028
+ try {
4029
+ await this.storeManagedTokens(accessToken, refreshToken);
4030
+ } catch (error) {
4031
+ console.warn("Failed to initialize tokens in storage:", error);
4032
+ } finally {
4033
+ this.accessToken = null;
4034
+ this.refreshToken = null;
4035
+ }
4036
+ }
4037
+ async awaitInitialization() {
4038
+ if (this.initializationPromise) {
4039
+ await this.initializationPromise;
4040
+ this.initializationPromise = null;
4041
+ }
4042
+ }
4043
+ async assessTokenStateOnce() {
4044
+ if (!this.tokenStorage || this.hasAssessedTokens) return;
4045
+ this.hasAssessedTokens = true;
4046
+ try {
4047
+ const accessToken = await this.tokenStorage.getAccessToken();
4048
+ const refreshToken = await this.tokenStorage.getRefreshToken();
4049
+ if (!accessToken && refreshToken) {
4050
+ await this.tokenStorage.clearTokens();
4051
+ console.info("Cleaned up orphaned refresh token");
4052
+ }
4053
+ } catch (error) {
4054
+ console.warn("Token state assessment failed:", error);
4055
+ }
4056
+ }
4057
+ async bootstrapAnonymousSession() {
4058
+ if (!this.tokenStorage) return null;
4059
+ if (this.bootstrapPromise) return this.bootstrapPromise;
4060
+ this.bootstrapPromise = (async () => {
4061
+ try {
4062
+ const response = await fetch(`${this.baseUrl}/auth/anonymous`, {
4063
+ method: "POST",
4064
+ headers: {
4065
+ "Content-Type": "application/json",
4066
+ ...this.apiKey && { "X-Api-Key": this.apiKey }
4067
+ }
4068
+ });
4069
+ if (!response.ok) return null;
4070
+ const tokens = (await response.json()).content;
4071
+ if (tokens?.access_token && tokens?.refresh_token) {
4072
+ await this.storeManagedTokens(tokens.access_token, tokens.refresh_token);
4073
+ this.onTokensUpdated?.(tokens.access_token, tokens.refresh_token);
4074
+ console.info("Automatically created anonymous session for first API request");
4075
+ return tokens.access_token;
4076
+ }
4077
+ return null;
4078
+ } catch (error) {
4079
+ console.warn("Failed to automatically create anonymous tokens:", error);
4080
+ return null;
4081
+ } finally {
4082
+ this.bootstrapPromise = null;
4083
+ }
4084
+ })();
4085
+ return this.bootstrapPromise;
4086
+ }
4087
+ async refreshTokens() {
4088
+ if (!this.tokenStorage) return;
4089
+ const tokenStorage = this.tokenStorage;
4090
+ if (this.refreshPromise) return this.refreshPromise;
4091
+ this.refreshPromise = (async () => {
4092
+ try {
4093
+ const refreshToken = await tokenStorage.getRefreshToken();
4094
+ let newTokens;
4095
+ if (refreshToken && !isTokenExpired(refreshToken)) if (this.refreshTokenFn) newTokens = await this.refreshTokenFn(refreshToken);
4096
+ else {
4097
+ const response = await fetch(`${this.baseUrl}/auth/refresh-token`, {
4098
+ method: "POST",
4099
+ headers: { "Content-Type": "application/json" },
4100
+ body: JSON.stringify({ refresh_token: refreshToken })
4101
+ });
4102
+ if (!response.ok) throw new Error(`Token refresh failed: ${response.status}`);
4103
+ newTokens = (await response.json()).content;
4104
+ }
4105
+ else {
4106
+ const currentAccessToken = await tokenStorage.getAccessToken();
4107
+ if (!currentAccessToken) throw new Error("No tokens available for refresh");
4108
+ const reason = refreshToken ? "refresh token expired" : "no refresh token available";
4109
+ const response = await fetch(`${this.baseUrl}/auth/anonymous`, {
4110
+ method: "POST",
4111
+ headers: {
4112
+ "Content-Type": "application/json",
4113
+ ...this.apiKey && { "X-Api-Key": this.apiKey },
4114
+ Authorization: `Bearer ${currentAccessToken}`
4115
+ }
4116
+ });
4117
+ if (!response.ok) throw new Error(`Anonymous token fallback failed: ${response.status}`);
4118
+ newTokens = (await response.json()).content;
4119
+ console.info(`Token refreshed via anonymous fallback (${reason}) - user may need to re-authenticate for privileged operations`);
4120
+ }
4121
+ await this.storeManagedTokens(newTokens.access_token, newTokens.refresh_token);
4122
+ this.onTokensUpdated?.(newTokens.access_token, newTokens.refresh_token);
4123
+ } catch (error) {
4124
+ console.error("Token refresh failed:", error);
4125
+ await this.clearTokens();
4126
+ throw error;
4127
+ } finally {
4128
+ this.refreshPromise = null;
4129
+ }
4130
+ })();
4131
+ return this.refreshPromise;
3828
4132
  }
3829
- /**
3830
- * Update an address for a customer
3831
- *
3832
- * @param pathParams - Path parameters
3833
- * @param body - Address update body
3834
- * @returns Promise with address details
3835
- *
3836
- * @example
3837
- * ```typescript
3838
- * const { data, error } = await sdk.customer.updateAddress(
3839
- * {
3840
- * user_id: "user_456",
3841
- * address_id: "addr_789"
3842
- * },
3843
- * {
3844
- * address_line1: "456 Oak Avenue",
3845
- * city: "Los Angeles",
3846
- * state: "CA",
3847
- * pincode: "90210"
3848
- * }
3849
- * );
3850
- *
3851
- * if (error) {
3852
- * console.error("Failed to update address:", error);
3853
- * return;
3854
- * }
3855
- *
3856
- * console.log("Address updated:", data.address);
3857
- * ```
3858
- */
3859
- async updateAddress(pathParams, body) {
3860
- return this.executeRequest(() => this.client.PUT("/customers/{user_id}/addresses/{address_id}", {
3861
- params: { path: pathParams },
3862
- body
3863
- }));
4133
+ async captureTokensFromResponse(method, pathname, response) {
4134
+ if (!this.tokenStorage || !(isTokenReturningOperation(method, pathname) || isAnonymousAuthOperation(method, pathname))) return;
4135
+ try {
4136
+ const content = (await response.clone().json()).content;
4137
+ if (content?.access_token && content?.refresh_token) {
4138
+ await this.storeManagedTokens(content.access_token, content.refresh_token);
4139
+ this.onTokensUpdated?.(content.access_token, content.refresh_token);
4140
+ }
4141
+ } catch (error) {
4142
+ console.warn("Failed to extract tokens from response:", error);
4143
+ }
3864
4144
  }
4145
+ async storeManagedTokens(accessToken, refreshToken) {
4146
+ if (!this.tokenStorage) return;
4147
+ await this.tokenStorage.setAccessToken(accessToken);
4148
+ if (refreshToken) await this.tokenStorage.setRefreshToken(refreshToken);
4149
+ }
4150
+ };
4151
+
4152
+ //#endregion
4153
+ //#region src/index.ts
4154
+ /**
4155
+ * Public SDK class for the Storefront API.
4156
+ *
4157
+ * This surface is intentionally limited to API-key-backed, public read
4158
+ * operations so it can be used safely during build and prerender flows.
4159
+ */
4160
+ var PublicStorefrontSDK = class {
3865
4161
  /**
3866
- * Delete an address for a customer
3867
- *
3868
- * @param pathParams - Path parameters
3869
- * @returns Promise with deletion response
3870
- *
3871
- * @example
3872
- * ```typescript
3873
- * const { data, error } = await sdk.customer.deleteAddress({
3874
- * user_id: "user_456",
3875
- * address_id: "addr_789"
3876
- * });
3877
- *
3878
- * if (error) {
3879
- * console.error("Failed to delete address:", error);
3880
- * return;
3881
- * }
3882
- *
3883
- * console.log("Address deleted:", data.message);
3884
- * ```
4162
+ * Client for catalog-related read-only endpoints
3885
4163
  */
3886
- async deleteAddress(pathParams) {
3887
- return this.executeRequest(() => this.client.DELETE("/customers/{user_id}/addresses/{address_id}", { params: { path: pathParams } }));
3888
- }
4164
+ catalog;
3889
4165
  /**
3890
- * Get customer loyalty details
3891
- *
3892
- * @param pathParams - Path parameters
3893
- * @returns Promise with loyalty details
3894
- *
3895
- * @example
3896
- * ```typescript
3897
- * const { data, error } = await sdk.customer.getLoyaltyDetails({
3898
- * user_id: "user_456"
3899
- * });
3900
- *
3901
- * if (error) {
3902
- * console.error("Failed to get loyalty details:", error);
3903
- * return;
3904
- * }
3905
- *
3906
- * console.log("Loyalty info:", data.loyalty);
3907
- * console.log("Points balance:", data.loyalty_point_balance);
3908
- * ```
4166
+ * Client for helper-related endpoints
3909
4167
  */
3910
- async getLoyaltyDetails(pathParams) {
3911
- return this.executeRequest(() => this.client.GET("/customers/{user_id}/loyalty", { params: { path: pathParams } }));
3912
- }
4168
+ helpers;
3913
4169
  /**
3914
- * List all loyalty points activity for a customer
3915
- *
3916
- * @param pathParams - Path parameters
3917
- * @returns Promise with loyalty points activity
3918
- *
3919
- * @example
3920
- * ```typescript
3921
- * const { data, error } = await sdk.customer.listLoyaltyPointsActivity({
3922
- * user_id: "user_456"
3923
- * });
3924
- *
3925
- * if (error) {
3926
- * console.error("Failed to get loyalty activity:", error);
3927
- * return;
3928
- * }
3929
- *
3930
- * console.log("Loyalty activity:", data.loyalty_points_activity);
3931
- *
3932
- * // With pagination and sorting
3933
- * const { data: sortedData, error: sortedError } = await sdk.customer.listLoyaltyPointsActivity({
3934
- * user_id: "user_456",
3935
- * page: 1,
3936
- * limit: 20,
3937
- * sort_by: JSON.stringify({ "created_at": "desc" })
3938
- * });
3939
- * ```
4170
+ * Client for store config-related endpoints
3940
4171
  */
3941
- async listLoyaltyPointsActivity(pathParams, queryParams) {
3942
- return this.executeRequest(() => this.client.GET("/customers/{user_id}/loyalty-points-activity", { params: {
3943
- path: pathParams,
3944
- query: queryParams
3945
- } }));
3946
- }
4172
+ store;
3947
4173
  /**
3948
- * List all reviews left by a customer
3949
- *
3950
- * @param pathParams - Path parameters
3951
- * @returns Promise with reviews
3952
- *
3953
- * @example
3954
- * ```typescript
3955
- * const { data, error } = await sdk.customer.listCustomerReviews({
3956
- * user_id: "user_456"
3957
- * });
3958
- *
3959
- * if (error) {
3960
- * console.error("Failed to get customer reviews:", error);
3961
- * return;
3962
- * }
3963
- *
3964
- * console.log("Customer reviews:", data.reviews);
3965
- * console.log("Ready for review:", data.ready_for_review);
3966
- * ```
4174
+ * Centrally stored default headers for consistency
3967
4175
  */
3968
- async listCustomerReviews(pathParams) {
3969
- return this.executeRequest(() => this.client.GET("/customers/{user_id}/reviews", { params: { path: pathParams } }));
4176
+ defaultHeaders;
4177
+ constructor(options) {
4178
+ this.defaultHeaders = options.defaultHeaders;
4179
+ const config = {
4180
+ storeId: options.storeId,
4181
+ environment: options.environment,
4182
+ baseUrl: options.baseUrl,
4183
+ apiKey: options.apiKey,
4184
+ timeout: options.timeout,
4185
+ defaultHeaders: options.defaultHeaders,
4186
+ debug: options.debug,
4187
+ logger: options.logger
4188
+ };
4189
+ this.catalog = new PublicCatalogClient(config);
4190
+ this.helpers = new PublicHelpersClient(config);
4191
+ this.store = new PublicStoreConfigClient(config);
3970
4192
  }
3971
4193
  /**
3972
- * List all saved payment methods for a customer
4194
+ * Set the API key for all public clients
3973
4195
  *
3974
- * @param pathParams - Path parameters
3975
- * @returns Promise with payment methods
3976
- *
3977
- * @example
3978
- * ```typescript
3979
- * const { data, error } = await sdk.customer.listSavedPaymentMethods({
3980
- * customer_id: "customer_123"
3981
- * });
3982
- *
3983
- * if (error) {
3984
- * console.error("Failed to list saved payment methods:", error);
3985
- * return;
3986
- * }
3987
- *
3988
- * console.log("Saved payment methods:", data.saved_payment_methods);
3989
- * ```
4196
+ * @param apiKey - The API key to set
3990
4197
  */
3991
- async listSavedPaymentMethods(pathParams, queryParams) {
3992
- return this.executeRequest(() => this.client.GET("/customers/{customer_id}/payment-methods", { params: {
3993
- path: pathParams,
3994
- query: queryParams
3995
- } }));
4198
+ setApiKey(apiKey) {
4199
+ this.catalog.setApiKey(apiKey);
4200
+ this.helpers.setApiKey(apiKey);
4201
+ this.store.setApiKey(apiKey);
3996
4202
  }
3997
4203
  /**
3998
- * List all saved cards for a customer
3999
- *
4000
- * @param pathParams - Path parameters
4001
- * @returns Promise with cards
4002
- *
4003
- * @example
4004
- * ```typescript
4005
- * const { data, error } = await sdk.customer.listCustomerCards({
4006
- * customer_id: "customer_123"
4007
- * });
4008
- *
4009
- * if (error) {
4010
- * console.error("Failed to list customer cards:", error);
4011
- * return;
4012
- * }
4013
- *
4014
- * console.log("Customer cards:", data.cards);
4015
- * ```
4204
+ * Clear the API key from all public clients
4016
4205
  */
4017
- async listCustomerCards(pathParams) {
4018
- return this.executeRequest(() => this.client.GET("/customers/{customer_id}/cards", { params: { path: pathParams } }));
4206
+ clearApiKey() {
4207
+ this.catalog.clearApiKey();
4208
+ this.helpers.clearApiKey();
4209
+ this.store.clearApiKey();
4019
4210
  }
4020
- };
4021
-
4022
- //#endregion
4023
- //#region src/lib/store-config.ts
4024
- /**
4025
- * Client for interacting with store config endpoints
4026
- */
4027
- var StoreConfigClient = class extends StorefrontAPIClient {
4028
4211
  /**
4029
- * Check store existence
4030
- *
4031
- * @description Checks whether a store with the configured store ID exists.
4032
- * Returns `success: true` if the store exists, `false` otherwise.
4033
- *
4034
- * @returns Promise with store existence check result
4035
- * @example
4036
- * ```typescript
4037
- * const { data, error } = await sdk.storeConfig.checkStore();
4212
+ * Set default headers for all public clients
4038
4213
  *
4039
- * if (error) {
4040
- * console.error("Failed to check store:", error.message);
4041
- * } else {
4042
- * console.log("Store exists:", data.success);
4043
- * }
4044
- * ```
4214
+ * @param headers - Default headers to set (only supported headers allowed)
4045
4215
  */
4046
- async checkStore() {
4047
- return this.executeRequest(() => this.client.GET("/store/check"));
4216
+ setDefaultHeaders(headers) {
4217
+ this.defaultHeaders = headers;
4218
+ this.catalog.setDefaultHeaders(headers);
4219
+ this.helpers.setDefaultHeaders(headers);
4220
+ this.store.setDefaultHeaders(headers);
4048
4221
  }
4049
4222
  /**
4050
- * Get store config
4051
- *
4052
- * @returns Promise with store configuration data
4053
- *
4054
- * @example
4055
- * ```typescript
4056
- * const { data, error } = await sdk.storeConfig.getStoreConfig();
4057
- *
4058
- * if (error) {
4059
- * console.error('Failed to get store config:', error.message);
4060
- * return;
4061
- * }
4223
+ * Get current default headers
4062
4224
  *
4063
- * // Access store configuration data
4064
- * const storeConfig = data.store_config;
4065
- * console.log('Store brand:', storeConfig.brand.name);
4066
- * console.log('Currency:', storeConfig.currency.code);
4067
- * console.log('KYC enabled:', storeConfig.is_kyc_enabled);
4068
- * console.log('Customer groups enabled:', storeConfig.is_customer_group_enabled);
4069
- * ```
4225
+ * @returns Current default headers from central storage (always consistent)
4070
4226
  */
4071
- async getStoreConfig() {
4072
- return this.executeRequest(() => this.client.GET("/store/config"));
4227
+ getDefaultHeaders() {
4228
+ return this.defaultHeaders;
4073
4229
  }
4074
4230
  };
4075
-
4076
- //#endregion
4077
- //#region src/index.ts
4078
4231
  /**
4079
- * Main SDK class for the Storefront API
4232
+ * Main session-aware SDK class for the Storefront API
4080
4233
  */
4081
- var StorefrontSDK = class {
4234
+ var SessionStorefrontSDK = class {
4082
4235
  /**
4083
4236
  * Client for catalog-related endpoints (products, categories, etc.)
4084
4237
  */
@@ -4116,12 +4269,33 @@ var StorefrontSDK = class {
4116
4269
  */
4117
4270
  defaultHeaders;
4118
4271
  /**
4119
- * Create a new StorefrontSDK instance
4272
+ * Shared session helper and coordinator for all API clients in this SDK instance.
4273
+ */
4274
+ session;
4275
+ sessionManager;
4276
+ /**
4277
+ * Create a new SessionStorefrontSDK instance
4120
4278
  *
4121
4279
  * @param options - Configuration options for the SDK
4122
4280
  */
4123
4281
  constructor(options) {
4124
4282
  this.defaultHeaders = options.defaultHeaders;
4283
+ const sessionManager = new StorefrontSessionManager({
4284
+ accessToken: options.accessToken,
4285
+ apiKey: options.apiKey,
4286
+ baseUrl: buildStorefrontURL({
4287
+ storeId: options.storeId,
4288
+ environment: options.environment,
4289
+ baseUrl: options.baseUrl
4290
+ }),
4291
+ onTokensCleared: options.onTokensCleared,
4292
+ onTokensUpdated: options.onTokensUpdated,
4293
+ refreshToken: options.refreshToken,
4294
+ refreshTokenFn: options.refreshTokenFn,
4295
+ tokenStorage: options.tokenStorage
4296
+ });
4297
+ this.sessionManager = sessionManager;
4298
+ this.session = sessionManager;
4125
4299
  const config = {
4126
4300
  storeId: options.storeId,
4127
4301
  environment: options.environment,
@@ -4135,7 +4309,8 @@ var StorefrontSDK = class {
4135
4309
  onTokensCleared: options.onTokensCleared,
4136
4310
  defaultHeaders: options.defaultHeaders,
4137
4311
  debug: options.debug,
4138
- logger: options.logger
4312
+ logger: options.logger,
4313
+ sessionManager
4139
4314
  };
4140
4315
  this.catalog = new CatalogClient(config);
4141
4316
  this.cart = new CartClient(config);
@@ -4157,14 +4332,7 @@ var StorefrontSDK = class {
4157
4332
  * - If tokenStorage is not provided: Only stores access token for manual management
4158
4333
  */
4159
4334
  async setTokens(accessToken, refreshToken) {
4160
- await this.catalog.setTokens(accessToken, refreshToken);
4161
- await this.cart.setTokens(accessToken, refreshToken);
4162
- await this.auth.setTokens(accessToken, refreshToken);
4163
- await this.customer.setTokens(accessToken, refreshToken);
4164
- await this.helpers.setTokens(accessToken, refreshToken);
4165
- await this.order.setTokens(accessToken, refreshToken);
4166
- await this.payments.setTokens(accessToken, refreshToken);
4167
- await this.store.setTokens(accessToken, refreshToken);
4335
+ await this.sessionManager.setTokens(accessToken, refreshToken);
4168
4336
  }
4169
4337
  /**
4170
4338
  * Clear all authentication tokens from all clients
@@ -4174,14 +4342,7 @@ var StorefrontSDK = class {
4174
4342
  * - If tokenStorage is not provided: Clears the manual access token
4175
4343
  */
4176
4344
  async clearTokens() {
4177
- await this.catalog.clearTokens();
4178
- await this.cart.clearTokens();
4179
- await this.auth.clearTokens();
4180
- await this.customer.clearTokens();
4181
- await this.helpers.clearTokens();
4182
- await this.order.clearTokens();
4183
- await this.payments.clearTokens();
4184
- await this.store.clearTokens();
4345
+ await this.sessionManager.clearTokens();
4185
4346
  }
4186
4347
  /**
4187
4348
  * Set the API key for all clients
@@ -4189,57 +4350,58 @@ var StorefrontSDK = class {
4189
4350
  * @param apiKey - The API key to set
4190
4351
  */
4191
4352
  setApiKey(apiKey) {
4192
- this.catalog.setApiKey(apiKey);
4193
- this.cart.setApiKey(apiKey);
4194
- this.auth.setApiKey(apiKey);
4195
- this.customer.setApiKey(apiKey);
4196
- this.helpers.setApiKey(apiKey);
4197
- this.order.setApiKey(apiKey);
4198
- this.payments.setApiKey(apiKey);
4199
- this.store.setApiKey(apiKey);
4353
+ this.sessionManager.setApiKey(apiKey);
4200
4354
  }
4201
4355
  /**
4202
4356
  * Clear the API key from all clients
4203
4357
  */
4204
4358
  clearApiKey() {
4205
- this.catalog.clearApiKey();
4206
- this.cart.clearApiKey();
4207
- this.auth.clearApiKey();
4208
- this.customer.clearApiKey();
4209
- this.helpers.clearApiKey();
4210
- this.order.clearApiKey();
4211
- this.payments.clearApiKey();
4212
- this.store.clearApiKey();
4359
+ this.sessionManager.clearApiKey();
4213
4360
  }
4214
4361
  /**
4215
4362
  * Get the current access token if using token storage
4363
+ *
4364
+ * This is a passive lookup. It will not create or refresh a session.
4216
4365
  */
4217
4366
  async getAccessToken() {
4218
- return await this.auth.getAuthorizationHeader().then((header) => header.startsWith("Bearer ") ? header.substring(7) : null);
4367
+ return this.session.peekAccessToken();
4368
+ }
4369
+ /**
4370
+ * Ensure an access token is available for the current session.
4371
+ *
4372
+ * This may create or refresh a managed session when automatic token
4373
+ * management is configured.
4374
+ *
4375
+ * @returns A usable access token
4376
+ */
4377
+ async ensureAccessToken() {
4378
+ return this.session.ensureAccessToken();
4219
4379
  }
4220
4380
  /**
4221
4381
  * Get user information from the current access token
4222
4382
  *
4383
+ * This is a passive lookup. It will not create or refresh a session.
4384
+ *
4223
4385
  * @returns User information extracted from JWT token, or null if no token or invalid token
4224
4386
  */
4225
4387
  async getUserInfo() {
4226
- const token = await this.getAccessToken();
4227
- if (!token) return null;
4228
- return extractUserInfoFromToken(token);
4388
+ return this.session.peekUserInfo();
4229
4389
  }
4230
4390
  /**
4231
4391
  * Get the current user ID from the access token
4232
4392
  *
4393
+ * This is a passive lookup. It will not create or refresh a session.
4394
+ *
4233
4395
  * @returns User ID (ulid) or null if no token or invalid token
4234
4396
  */
4235
4397
  async getUserId() {
4236
- const token = await this.getAccessToken();
4237
- if (!token) return null;
4238
- return getUserIdFromToken(token);
4398
+ return this.session.peekUserId();
4239
4399
  }
4240
4400
  /**
4241
4401
  * Check if the current user is logged in (not anonymous)
4242
4402
  *
4403
+ * This is a passive lookup. It will not create or refresh a session.
4404
+ *
4243
4405
  * @returns True if user is logged in, false if anonymous or no token
4244
4406
  */
4245
4407
  async isLoggedIn() {
@@ -4250,6 +4412,8 @@ var StorefrontSDK = class {
4250
4412
  /**
4251
4413
  * Check if the current user is anonymous
4252
4414
  *
4415
+ * This is a passive lookup. It will not create or refresh a session.
4416
+ *
4253
4417
  * @returns True if user is anonymous or no token, false if logged in
4254
4418
  */
4255
4419
  async isAnonymous() {
@@ -4260,18 +4424,22 @@ var StorefrontSDK = class {
4260
4424
  /**
4261
4425
  * Get the customer ID from the current access token
4262
4426
  *
4427
+ * This is a passive lookup. It will not create or refresh a session.
4428
+ *
4263
4429
  * @returns Customer ID or null if no token, invalid token, or user has no customer ID
4264
4430
  */
4265
4431
  async getCustomerId() {
4266
- return (await this.getUserInfo())?.customerId || null;
4432
+ return this.session.peekCustomerId();
4267
4433
  }
4268
4434
  /**
4269
4435
  * Get the customer group ID from the current access token
4270
4436
  *
4437
+ * This is a passive lookup. It will not create or refresh a session.
4438
+ *
4271
4439
  * @returns Customer group ID or null if no token, invalid token, or user has no customer group
4272
4440
  */
4273
4441
  async getCustomerGroupId() {
4274
- return (await this.getUserInfo())?.customerGroupId || null;
4442
+ return this.session.peekCustomerGroupId();
4275
4443
  }
4276
4444
  /**
4277
4445
  * Set default headers for all clients
@@ -4298,7 +4466,71 @@ var StorefrontSDK = class {
4298
4466
  return this.defaultHeaders;
4299
4467
  }
4300
4468
  };
4469
+ function buildPublicStorefrontOptions(options) {
4470
+ return {
4471
+ storeId: options.storeId,
4472
+ environment: options.environment,
4473
+ baseUrl: options.baseUrl,
4474
+ apiKey: options.apiKey,
4475
+ timeout: options.timeout,
4476
+ defaultHeaders: options.defaultHeaders,
4477
+ debug: options.debug,
4478
+ logger: options.logger
4479
+ };
4480
+ }
4481
+ function buildSessionStorefrontOptions(options, overrides) {
4482
+ return {
4483
+ ...buildPublicStorefrontOptions(options),
4484
+ ...options.session,
4485
+ ...overrides
4486
+ };
4487
+ }
4488
+ /**
4489
+ * Create a configured storefront factory with explicit public and session
4490
+ * access patterns.
4491
+ *
4492
+ * @example
4493
+ * ```typescript
4494
+ * import {
4495
+ * BrowserTokenStorage,
4496
+ * createStorefront,
4497
+ * Environment,
4498
+ * } from "@commercengine/storefront-sdk";
4499
+ *
4500
+ * export const storefront = createStorefront({
4501
+ * storeId: "your-store-id",
4502
+ * apiKey: "your-api-key",
4503
+ * environment: Environment.Staging,
4504
+ * session: {
4505
+ * tokenStorage: new BrowserTokenStorage("myapp_"),
4506
+ * },
4507
+ * });
4508
+ *
4509
+ * const { data: products } = await storefront.public().catalog.listProducts();
4510
+ * const { data: cart } = await storefront.session().cart.getCart();
4511
+ * ```
4512
+ *
4513
+ * @param options - Shared public config plus optional default session config
4514
+ * @returns A storefront factory with explicit `public()` and `session()` accessors
4515
+ */
4516
+ function createStorefront(options) {
4517
+ const publicOptions = buildPublicStorefrontOptions(options);
4518
+ let publicSDK = null;
4519
+ let sessionSDK = null;
4520
+ const session = (overrides) => {
4521
+ if (overrides && Object.keys(overrides).length > 0) return new SessionStorefrontSDK(buildSessionStorefrontOptions(options, overrides));
4522
+ if (!sessionSDK) sessionSDK = new SessionStorefrontSDK(buildSessionStorefrontOptions(options));
4523
+ return sessionSDK;
4524
+ };
4525
+ return {
4526
+ public: () => {
4527
+ if (!publicSDK) publicSDK = new PublicStorefrontSDK(publicOptions);
4528
+ return publicSDK;
4529
+ },
4530
+ session
4531
+ };
4532
+ }
4301
4533
 
4302
4534
  //#endregion
4303
- export { AuthClient, BrowserTokenStorage, CartClient, CatalogClient, CookieTokenStorage, CustomerClient, Environment, HelpersClient, MemoryTokenStorage, OrderClient, PaymentsClient, ResponseUtils, StoreConfigClient, StorefrontAPIClient, StorefrontSDK, StorefrontSDK as default };
4535
+ export { AuthClient, BrowserTokenStorage, CartClient, CatalogClient, CookieTokenStorage, CustomerClient, Environment, HelpersClient, MemoryTokenStorage, OrderClient, PaymentsClient, PublicCatalogClient, PublicHelpersClient, PublicStoreConfigClient, PublicStorefrontAPIClient, PublicStorefrontSDK, ResponseUtils, SessionStorefrontAPIClient, SessionStorefrontSDK, StoreConfigClient, createStorefront };
4304
4536
  //# sourceMappingURL=index.mjs.map