@commercengine/storefront-sdk 0.8.2 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import createClient from "openapi-fetch";
1
+ import { BaseAPIClient, ResponseUtils, getPathnameFromUrl } from "@commercengine/sdk-core";
2
2
  import { decodeJwt } from "jose";
3
3
 
4
4
  //#region src/lib/jwt-utils.ts
@@ -85,17 +85,6 @@ function isUserAnonymous(token) {
85
85
  //#endregion
86
86
  //#region src/lib/auth-utils.ts
87
87
  /**
88
- * Extract pathname from URL
89
- */
90
- function getPathnameFromUrl(url) {
91
- try {
92
- const urlObj = new URL(url);
93
- return urlObj.pathname;
94
- } catch {
95
- return url.split("?")[0];
96
- }
97
- }
98
- /**
99
88
  * Check if a URL path is an auth endpoint that should use API key
100
89
  */
101
90
  function isAnonymousAuthEndpoint(pathname) {
@@ -413,236 +402,12 @@ function createDefaultAuthMiddleware(options) {
413
402
  }
414
403
 
415
404
  //#endregion
416
- //#region src/lib/logger-utils.ts
405
+ //#region src/lib/url-utils.ts
417
406
  /**
418
- * Response utilities for debugging and working with Response objects
407
+ * URL utility functions for the Storefront SDK
419
408
  */
420
- var ResponseUtils = class {
421
- /**
422
- * Get response headers as a plain object
423
- */
424
- static getHeaders(response) {
425
- return Object.fromEntries(response.headers.entries());
426
- }
427
- /**
428
- * Get specific header value
429
- */
430
- static getHeader(response, name) {
431
- return response.headers.get(name);
432
- }
433
- /**
434
- * Check if response was successful
435
- */
436
- static isSuccess(response) {
437
- return response.ok;
438
- }
439
- /**
440
- * Get response metadata
441
- */
442
- static getMetadata(response) {
443
- return {
444
- status: response.status,
445
- statusText: response.statusText,
446
- ok: response.ok,
447
- url: response.url,
448
- redirected: response.redirected,
449
- type: response.type,
450
- headers: Object.fromEntries(response.headers.entries())
451
- };
452
- }
453
- /**
454
- * Clone and read response as text (useful for debugging)
455
- * Note: This can only be called once per response
456
- */
457
- static async getText(response) {
458
- const cloned = response.clone();
459
- return await cloned.text();
460
- }
461
- /**
462
- * Clone and read response as JSON (useful for debugging)
463
- * Note: This can only be called once per response
464
- */
465
- static async getJSON(response) {
466
- const cloned = response.clone();
467
- return await cloned.json();
468
- }
469
- /**
470
- * Format response information for debugging
471
- */
472
- static format(response) {
473
- const metadata = this.getMetadata(response);
474
- return `${metadata.status} ${metadata.statusText} - ${metadata.url}`;
475
- }
476
- };
477
409
  /**
478
- * Debug logging utilities
479
- */
480
- var DebugLogger = class {
481
- logger;
482
- responseTextCache = /* @__PURE__ */ new Map();
483
- constructor(logger) {
484
- this.logger = logger || ((level, message, data) => {
485
- console.log(`[${level.toUpperCase()}]`, message);
486
- if (data) console.log(data);
487
- });
488
- }
489
- /**
490
- * Log debug information about API request
491
- */
492
- logRequest(request, requestBody) {
493
- this.logger("info", "API Request Debug Info", {
494
- method: request.method,
495
- url: request.url,
496
- headers: Object.fromEntries(request.headers.entries()),
497
- body: requestBody,
498
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
499
- });
500
- }
501
- /**
502
- * Log debug information about API response
503
- */
504
- async logResponse(response, responseBody) {
505
- if (responseBody && typeof responseBody === "string") this.responseTextCache.set(response.url, responseBody);
506
- this.logger("info", "API Response Debug Info", {
507
- url: response.url,
508
- status: response.status,
509
- statusText: response.statusText,
510
- ok: response.ok,
511
- headers: Object.fromEntries(response.headers.entries()),
512
- redirected: response.redirected,
513
- type: response.type,
514
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
515
- });
516
- if (responseBody) this.logger("info", "API Response Data", {
517
- data: responseBody,
518
- contentType: response.headers.get("content-type"),
519
- contentLength: response.headers.get("content-length")
520
- });
521
- }
522
- /**
523
- * Log error information
524
- */
525
- logError(message, error) {
526
- this.logger("error", message, error);
527
- }
528
- /**
529
- * Get cached response text for a URL (if available)
530
- */
531
- getCachedResponseText(url) {
532
- return this.responseTextCache.get(url) || null;
533
- }
534
- /**
535
- * Clear cached response texts
536
- */
537
- clearCache() {
538
- this.responseTextCache.clear();
539
- }
540
- };
541
- /**
542
- * Extract request body for logging
543
- */
544
- async function extractRequestBody(request) {
545
- if (request.method === "GET" || request.method === "HEAD") return null;
546
- try {
547
- const clonedRequest = request.clone();
548
- const contentType = request.headers.get("content-type")?.toLowerCase();
549
- if (contentType?.startsWith("application/json")) return await clonedRequest.json();
550
- else if (contentType?.startsWith("multipart/form-data")) return "[FormData - cannot display]";
551
- else if (contentType?.startsWith("text/")) return await clonedRequest.text();
552
- return "[Request body - unknown format]";
553
- } catch (error) {
554
- return "[Request body unavailable]";
555
- }
556
- }
557
- /**
558
- * Create debug middleware for openapi-fetch (internal use)
559
- */
560
- function createDebugMiddleware(logger) {
561
- const debugLogger = new DebugLogger(logger);
562
- return {
563
- async onRequest({ request }) {
564
- const requestBody = await extractRequestBody(request);
565
- debugLogger.logRequest(request, requestBody);
566
- return request;
567
- },
568
- async onResponse({ response }) {
569
- const cloned = response.clone();
570
- let responseBody = null;
571
- try {
572
- const contentType = response.headers.get("content-type")?.toLowerCase();
573
- if (contentType?.startsWith("application/json")) responseBody = await cloned.json();
574
- else if (contentType?.startsWith("text/")) responseBody = await cloned.text();
575
- } catch (error) {}
576
- await debugLogger.logResponse(response, responseBody);
577
- return response;
578
- },
579
- async onError({ error, request }) {
580
- debugLogger.logError("API Request Failed", {
581
- error: {
582
- name: error instanceof Error ? error.name : "Unknown",
583
- message: error instanceof Error ? error.message : String(error),
584
- stack: error instanceof Error ? error.stack : void 0
585
- },
586
- request: {
587
- method: request.method,
588
- url: request.url,
589
- headers: Object.fromEntries(request.headers.entries())
590
- },
591
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
592
- });
593
- throw error;
594
- }
595
- };
596
- }
597
-
598
- //#endregion
599
- //#region src/lib/header-utils.ts
600
- /**
601
- * Mapping of SDK header parameter names to actual HTTP header names
602
- * Only include headers that need transformation - others pass through as-is
603
- */
604
- const HEADER_TRANSFORMATIONS = { customer_group_id: "x-customer-group-id" };
605
- /**
606
- * Transform SDK header parameters to actual HTTP header names
607
- * Headers not in the transformation map are passed through unchanged
608
- *
609
- * @param headers - Headers object with SDK parameter names
610
- * @returns Headers object with actual HTTP header names
611
- */
612
- function transformHeaders(headers) {
613
- const transformed = {};
614
- for (const [key, value] of Object.entries(headers)) if (value !== void 0) {
615
- const headerName = HEADER_TRANSFORMATIONS[key] || key;
616
- transformed[headerName] = value;
617
- }
618
- return transformed;
619
- }
620
- /**
621
- * Merge default headers with method-level headers
622
- * Method-level headers take precedence over default headers
623
- * Automatically transforms SDK parameter names to HTTP header names
624
- *
625
- * @param defaultHeaders - Default headers from SDK configuration
626
- * @param methodHeaders - Headers passed to the specific method call
627
- * @returns Merged headers object with proper HTTP header names
628
- */
629
- function mergeHeaders(defaultHeaders, methodHeaders) {
630
- const merged = {};
631
- if (defaultHeaders) {
632
- const transformedDefaults = transformHeaders(defaultHeaders);
633
- Object.assign(merged, transformedDefaults);
634
- }
635
- if (methodHeaders) Object.assign(merged, methodHeaders);
636
- Object.keys(merged).forEach((key) => {
637
- if (merged[key] === void 0) delete merged[key];
638
- });
639
- return merged;
640
- }
641
-
642
- //#endregion
643
- //#region src/lib/client.ts
644
- /**
645
- * Available API environments
410
+ * Available API environments for Commerce Engine
646
411
  */
647
412
  let Environment = /* @__PURE__ */ function(Environment$1) {
648
413
  /**
@@ -656,12 +421,26 @@ let Environment = /* @__PURE__ */ function(Environment$1) {
656
421
  return Environment$1;
657
422
  }({});
658
423
  /**
659
- * Base API client for Storefront API
424
+ * Build base URL for Storefront API
425
+ */
426
+ function buildStorefrontURL(config) {
427
+ if (config.baseUrl) return config.baseUrl;
428
+ const env = config.environment || Environment.Production;
429
+ switch (env) {
430
+ case Environment.Staging: return `https://staging.api.commercengine.io/api/v1/${config.storeId}/storefront`;
431
+ case Environment.Production:
432
+ default: return `https://prod.api.commercengine.io/api/v1/${config.storeId}/storefront`;
433
+ }
434
+ }
435
+
436
+ //#endregion
437
+ //#region src/lib/client.ts
438
+ /**
439
+ * Storefront API client that extends the generic BaseAPIClient
440
+ * Adds Commerce Engine specific authentication and token management
660
441
  */
661
- var StorefrontAPIClient = class {
662
- client;
442
+ var StorefrontAPIClient = class extends BaseAPIClient {
663
443
  config;
664
- baseUrl;
665
444
  initializationPromise = null;
666
445
  /**
667
446
  * Create a new StorefrontAPIClient
@@ -669,74 +448,51 @@ var StorefrontAPIClient = class {
669
448
  * @param config - Configuration for the API client
670
449
  */
671
450
  constructor(config) {
451
+ const baseUrl = buildStorefrontURL({
452
+ storeId: config.storeId,
453
+ environment: config.environment,
454
+ baseUrl: config.baseUrl
455
+ });
456
+ const headerTransformations = { customer_group_id: "x-customer-group-id" };
457
+ super({
458
+ baseUrl,
459
+ timeout: config.timeout,
460
+ defaultHeaders: config.defaultHeaders,
461
+ debug: config.debug,
462
+ logger: config.logger
463
+ }, baseUrl, headerTransformations);
672
464
  this.config = { ...config };
673
- this.baseUrl = this.getBaseUrlFromConfig(this.config);
674
- this.client = createClient({ baseUrl: this.baseUrl });
675
- if (this.config.tokenStorage) {
465
+ this.setupStorefrontAuth();
466
+ }
467
+ /**
468
+ * Set up Storefront-specific authentication middleware
469
+ */
470
+ setupStorefrontAuth() {
471
+ const config = this.config;
472
+ if (config.tokenStorage) {
676
473
  const authMiddleware = createDefaultAuthMiddleware({
677
- apiKey: this.config.apiKey,
678
- baseUrl: this.baseUrl,
679
- tokenStorage: this.config.tokenStorage,
680
- onTokensUpdated: this.config.onTokensUpdated,
681
- onTokensCleared: this.config.onTokensCleared
474
+ apiKey: config.apiKey,
475
+ baseUrl: this.getBaseUrl(),
476
+ tokenStorage: config.tokenStorage,
477
+ onTokensUpdated: config.onTokensUpdated,
478
+ onTokensCleared: config.onTokensCleared
682
479
  });
683
480
  this.client.use(authMiddleware);
684
- if (this.config.accessToken) {
685
- this.initializationPromise = this.initializeTokens(this.config.accessToken, this.config.refreshToken);
686
- this.config.accessToken = void 0;
687
- this.config.refreshToken = void 0;
481
+ if (config.accessToken) {
482
+ this.initializationPromise = this.initializeTokens(config.accessToken, config.refreshToken);
483
+ config.accessToken = void 0;
484
+ config.refreshToken = void 0;
688
485
  }
689
486
  } else this.client.use({ onRequest: async ({ request }) => {
690
487
  const pathname = getPathnameFromUrl(request.url);
691
488
  if (isAnonymousAuthEndpoint(pathname)) {
692
- if (this.config.apiKey) request.headers.set("X-Api-Key", this.config.apiKey);
693
- if (this.config.accessToken) request.headers.set("Authorization", `Bearer ${this.config.accessToken}`);
489
+ if (config.apiKey) request.headers.set("X-Api-Key", config.apiKey);
490
+ if (config.accessToken) request.headers.set("Authorization", `Bearer ${config.accessToken}`);
694
491
  return request;
695
492
  }
696
- if (this.config.accessToken) request.headers.set("Authorization", `Bearer ${this.config.accessToken}`);
493
+ if (config.accessToken) request.headers.set("Authorization", `Bearer ${config.accessToken}`);
697
494
  return request;
698
495
  } });
699
- if (this.config.timeout) this.setupTimeoutMiddleware();
700
- if (this.config.debug) {
701
- const debugMiddleware = createDebugMiddleware(this.config.logger);
702
- this.client.use(debugMiddleware);
703
- }
704
- }
705
- /**
706
- * Set up timeout middleware
707
- */
708
- setupTimeoutMiddleware() {
709
- this.client.use({ onRequest: async ({ request }) => {
710
- const controller = new AbortController();
711
- const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
712
- if (request.signal) request.signal.addEventListener("abort", () => controller.abort());
713
- const newRequest = new Request(request, { signal: controller.signal });
714
- controller.signal.addEventListener("abort", () => clearTimeout(timeoutId));
715
- return newRequest;
716
- } });
717
- }
718
- /**
719
- * Constructs the base URL from the configuration
720
- *
721
- * @param config - The client configuration
722
- * @returns The constructed base URL
723
- */
724
- getBaseUrlFromConfig(config) {
725
- if (config.baseUrl) return config.baseUrl;
726
- const env = config.environment || Environment.Production;
727
- switch (env) {
728
- case Environment.Staging: return `https://staging.api.commercengine.io/api/v1/${config.storeId}/storefront`;
729
- case Environment.Production:
730
- default: return `https://prod.api.commercengine.io/api/v1/${config.storeId}/storefront`;
731
- }
732
- }
733
- /**
734
- * Get the base URL of the API
735
- *
736
- * @returns The base URL of the API
737
- */
738
- getBaseUrl() {
739
- return this.baseUrl;
740
496
  }
741
497
  /**
742
498
  * Get the authorization header value
@@ -798,48 +554,6 @@ var StorefrontAPIClient = class {
798
554
  this.config.apiKey = void 0;
799
555
  }
800
556
  /**
801
- * Execute a request and handle the response
802
- *
803
- * @param apiCall - Function that executes the API request
804
- * @returns Promise with the API response
805
- */
806
- async executeRequest(apiCall) {
807
- try {
808
- const { data, error, response } = await apiCall();
809
- if (error) return {
810
- data: null,
811
- error,
812
- response
813
- };
814
- if (data && data.content !== void 0) return {
815
- data: data.content,
816
- error: null,
817
- response
818
- };
819
- return {
820
- data,
821
- error: null,
822
- response
823
- };
824
- } catch (err) {
825
- const mockResponse = new Response(null, {
826
- status: 0,
827
- statusText: "Network Error"
828
- });
829
- const errorResult = {
830
- data: null,
831
- error: {
832
- success: false,
833
- code: "NETWORK_ERROR",
834
- message: "Network error occurred",
835
- error: err
836
- },
837
- response: mockResponse
838
- };
839
- return errorResult;
840
- }
841
- }
842
- /**
843
557
  * Initialize tokens in storage (private helper method)
844
558
  */
845
559
  async initializeTokens(accessToken, refreshToken) {
@@ -852,16 +566,6 @@ var StorefrontAPIClient = class {
852
566
  console.warn("Failed to initialize tokens in storage:", error);
853
567
  }
854
568
  }
855
- /**
856
- * Merge default headers with method-level headers
857
- * Method-level headers take precedence over default headers
858
- *
859
- * @param methodHeaders - Headers passed to the specific method call
860
- * @returns Merged headers object with proper HTTP header names
861
- */
862
- mergeHeaders(methodHeaders) {
863
- return mergeHeaders(this.config.defaultHeaders, methodHeaders);
864
- }
865
569
  };
866
570
 
867
571
  //#endregion
@@ -3646,11 +3350,16 @@ var StorefrontSDK = class {
3646
3350
  */
3647
3351
  store;
3648
3352
  /**
3353
+ * Centrally stored default headers for consistency
3354
+ */
3355
+ defaultHeaders;
3356
+ /**
3649
3357
  * Create a new StorefrontSDK instance
3650
3358
  *
3651
3359
  * @param options - Configuration options for the SDK
3652
3360
  */
3653
3361
  constructor(options) {
3362
+ this.defaultHeaders = options.defaultHeaders;
3654
3363
  const config = {
3655
3364
  storeId: options.storeId,
3656
3365
  environment: options.environment,
@@ -3810,26 +3519,23 @@ var StorefrontSDK = class {
3810
3519
  * @param headers - Default headers to set (only supported headers allowed)
3811
3520
  */
3812
3521
  setDefaultHeaders(headers) {
3813
- const newConfig = {
3814
- ...this.catalog["config"],
3815
- defaultHeaders: headers
3816
- };
3817
- this.catalog["config"] = newConfig;
3818
- this.cart["config"] = newConfig;
3819
- this.auth["config"] = newConfig;
3820
- this.customer["config"] = newConfig;
3821
- this.helpers["config"] = newConfig;
3822
- this.shipping["config"] = newConfig;
3823
- this.order["config"] = newConfig;
3824
- this.store["config"] = newConfig;
3522
+ this.defaultHeaders = headers;
3523
+ this.catalog.setDefaultHeaders(headers);
3524
+ this.cart.setDefaultHeaders(headers);
3525
+ this.auth.setDefaultHeaders(headers);
3526
+ this.customer.setDefaultHeaders(headers);
3527
+ this.helpers.setDefaultHeaders(headers);
3528
+ this.shipping.setDefaultHeaders(headers);
3529
+ this.order.setDefaultHeaders(headers);
3530
+ this.store.setDefaultHeaders(headers);
3825
3531
  }
3826
3532
  /**
3827
3533
  * Get current default headers
3828
3534
  *
3829
- * @returns Current default headers
3535
+ * @returns Current default headers from central storage (always consistent)
3830
3536
  */
3831
3537
  getDefaultHeaders() {
3832
- return this.catalog["config"].defaultHeaders;
3538
+ return this.defaultHeaders;
3833
3539
  }
3834
3540
  };
3835
3541
  var src_default = StorefrontSDK;