@commercengine/storefront-sdk 0.9.0 → 0.9.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.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import "openapi-fetch";
1
2
  import { ApiResult, BaseAPIClient, BaseSDKOptions, DebugLoggerFn, ResponseUtils } from "@commercengine/sdk-core";
2
3
 
3
4
  //#region src/types/storefront.d.ts
package/dist/index.js CHANGED
@@ -1,6 +1,418 @@
1
- import { BaseAPIClient, ResponseUtils, getPathnameFromUrl } from "@commercengine/sdk-core";
1
+ import createClient from "openapi-fetch";
2
2
  import { decodeJwt } from "jose";
3
3
 
4
+ //#region ../sdk-core/dist/index.js
5
+ /**
6
+ * Response utilities for debugging and working with Response objects
7
+ */
8
+ var ResponseUtils = class {
9
+ /**
10
+ * Get response headers as a plain object
11
+ */
12
+ static getHeaders(response) {
13
+ return Object.fromEntries(response.headers.entries());
14
+ }
15
+ /**
16
+ * Get specific header value
17
+ */
18
+ static getHeader(response, name) {
19
+ return response.headers.get(name);
20
+ }
21
+ /**
22
+ * Check if response was successful
23
+ */
24
+ static isSuccess(response) {
25
+ return response.ok;
26
+ }
27
+ /**
28
+ * Get response metadata
29
+ */
30
+ static getMetadata(response) {
31
+ return {
32
+ status: response.status,
33
+ statusText: response.statusText,
34
+ ok: response.ok,
35
+ url: response.url,
36
+ redirected: response.redirected,
37
+ type: response.type,
38
+ headers: Object.fromEntries(response.headers.entries())
39
+ };
40
+ }
41
+ /**
42
+ * Clone and read response as text (useful for debugging)
43
+ * Note: This can only be called once per response
44
+ */
45
+ static async getText(response) {
46
+ const cloned = response.clone();
47
+ return await cloned.text();
48
+ }
49
+ /**
50
+ * Clone and read response as JSON (useful for debugging)
51
+ * Note: This can only be called once per response
52
+ */
53
+ static async getJSON(response) {
54
+ const cloned = response.clone();
55
+ return await cloned.json();
56
+ }
57
+ /**
58
+ * Format response information for debugging
59
+ */
60
+ static format(response) {
61
+ const metadata = this.getMetadata(response);
62
+ return `${metadata.status} ${metadata.statusText} - ${metadata.url}`;
63
+ }
64
+ /**
65
+ * Format response for logging purposes (enhanced version)
66
+ */
67
+ static formatResponse(response) {
68
+ return {
69
+ status: response.status,
70
+ statusText: response.statusText,
71
+ url: response.url,
72
+ ok: response.ok
73
+ };
74
+ }
75
+ };
76
+ /**
77
+ * Debug logging utilities
78
+ */
79
+ var DebugLogger = class {
80
+ logger;
81
+ responseTextCache = /* @__PURE__ */ new Map();
82
+ constructor(logger) {
83
+ this.logger = logger || ((level, message, data) => {
84
+ console.log(`[${level.toUpperCase()}]`, message);
85
+ if (data) console.log(data);
86
+ });
87
+ }
88
+ /**
89
+ * Log debug information about API request
90
+ */
91
+ logRequest(request, requestBody) {
92
+ this.logger("info", "API Request Debug Info", {
93
+ method: request.method,
94
+ url: request.url,
95
+ headers: Object.fromEntries(request.headers.entries()),
96
+ body: requestBody,
97
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
98
+ });
99
+ }
100
+ /**
101
+ * Log debug information about API response
102
+ */
103
+ async logResponse(response, responseBody) {
104
+ if (responseBody && typeof responseBody === "string") this.responseTextCache.set(response.url, responseBody);
105
+ this.logger("info", "API Response Debug Info", {
106
+ url: response.url,
107
+ status: response.status,
108
+ statusText: response.statusText,
109
+ ok: response.ok,
110
+ headers: Object.fromEntries(response.headers.entries()),
111
+ redirected: response.redirected,
112
+ type: response.type,
113
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
114
+ });
115
+ if (responseBody) this.logger("info", "API Response Data", {
116
+ data: responseBody,
117
+ contentType: response.headers.get("content-type"),
118
+ contentLength: response.headers.get("content-length")
119
+ });
120
+ }
121
+ /**
122
+ * Log error information
123
+ */
124
+ logError(message, error) {
125
+ this.logger("error", message, error);
126
+ }
127
+ /**
128
+ * Get cached response text for a URL (if available)
129
+ */
130
+ getCachedResponseText(url) {
131
+ return this.responseTextCache.get(url) || null;
132
+ }
133
+ /**
134
+ * Clear cached response texts
135
+ */
136
+ clearCache() {
137
+ this.responseTextCache.clear();
138
+ }
139
+ info(message, data) {
140
+ this.logger("info", message, data);
141
+ }
142
+ warn(message, data) {
143
+ this.logger("warn", message, data);
144
+ }
145
+ error(message, data) {
146
+ this.logger("error", message, data);
147
+ }
148
+ };
149
+ /**
150
+ * Extract request body for logging
151
+ */
152
+ async function extractRequestBody(request) {
153
+ if (request.method === "GET" || request.method === "HEAD") return null;
154
+ try {
155
+ const clonedRequest = request.clone();
156
+ const contentType = request.headers.get("content-type")?.toLowerCase();
157
+ if (contentType?.startsWith("application/json")) return await clonedRequest.json();
158
+ else if (contentType?.startsWith("multipart/form-data")) return "[FormData - cannot display]";
159
+ else if (contentType?.startsWith("text/")) return await clonedRequest.text();
160
+ return "[Request body - unknown format]";
161
+ } catch (error) {
162
+ return "[Request body unavailable]";
163
+ }
164
+ }
165
+ /**
166
+ * Create debug middleware for openapi-fetch (internal use)
167
+ * Enhanced version that combines original functionality with duration tracking
168
+ */
169
+ function createDebugMiddleware(logger) {
170
+ const debugLogger = new DebugLogger(logger);
171
+ return {
172
+ async onRequest({ request }) {
173
+ request.__debugStartTime = Date.now();
174
+ const requestBody = await extractRequestBody(request);
175
+ debugLogger.logRequest(request, requestBody);
176
+ return request;
177
+ },
178
+ async onResponse({ request, response }) {
179
+ const startTime = request.__debugStartTime;
180
+ const duration = startTime ? Date.now() - startTime : 0;
181
+ const cloned = response.clone();
182
+ let responseBody = null;
183
+ try {
184
+ const contentType = response.headers.get("content-type")?.toLowerCase();
185
+ if (contentType?.startsWith("application/json")) responseBody = await cloned.json();
186
+ else if (contentType?.startsWith("text/")) responseBody = await cloned.text();
187
+ } catch (error) {}
188
+ await debugLogger.logResponse(response, responseBody);
189
+ if (duration > 0) debugLogger.info(`Request completed in ${duration}ms`, {
190
+ url: request.url,
191
+ method: request.method
192
+ });
193
+ return response;
194
+ },
195
+ async onError({ error, request }) {
196
+ debugLogger.logError("API Request Failed", {
197
+ error: {
198
+ name: error instanceof Error ? error.name : "Unknown",
199
+ message: error instanceof Error ? error.message : String(error),
200
+ stack: error instanceof Error ? error.stack : void 0
201
+ },
202
+ request: {
203
+ method: request.method,
204
+ url: request.url,
205
+ headers: Object.fromEntries(request.headers.entries())
206
+ },
207
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
208
+ });
209
+ throw error;
210
+ }
211
+ };
212
+ }
213
+ /**
214
+ * Timeout middleware for Commerce Engine SDKs
215
+ */
216
+ /**
217
+ * Create timeout middleware for openapi-fetch
218
+ * Adds configurable request timeout functionality
219
+ *
220
+ * @param timeoutMs - Timeout duration in milliseconds
221
+ * @returns Middleware object with onRequest handler
222
+ */
223
+ function createTimeoutMiddleware(timeoutMs) {
224
+ return { onRequest: async ({ request }) => {
225
+ const controller = new AbortController();
226
+ const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
227
+ if (request.signal) request.signal.addEventListener("abort", () => controller.abort());
228
+ const newRequest = new Request(request, { signal: controller.signal });
229
+ controller.signal.addEventListener("abort", () => clearTimeout(timeoutId));
230
+ return newRequest;
231
+ } };
232
+ }
233
+ /**
234
+ * Transform headers using a transformation mapping
235
+ * Headers not in the transformation map are passed through unchanged
236
+ *
237
+ * @param headers - Headers object with original names
238
+ * @param transformations - Mapping of original names to transformed names
239
+ * @returns Headers object with transformed names
240
+ */
241
+ function transformHeaders(headers, transformations) {
242
+ const transformed = {};
243
+ for (const [key, value] of Object.entries(headers)) if (value !== void 0) {
244
+ const headerName = transformations[key] || key;
245
+ transformed[headerName] = value;
246
+ }
247
+ return transformed;
248
+ }
249
+ /**
250
+ * Merge headers with transformation support
251
+ * Transforms default headers, then merges with method headers
252
+ *
253
+ * @param defaultHeaders - Default headers from SDK configuration
254
+ * @param methodHeaders - Headers passed to the specific method call
255
+ * @param transformations - Mapping for header name transformations
256
+ * @returns Merged headers object with transformations applied
257
+ */
258
+ function mergeAndTransformHeaders(defaultHeaders, methodHeaders, transformations) {
259
+ const merged = {};
260
+ if (defaultHeaders && transformations) {
261
+ const transformedDefaults = transformHeaders(defaultHeaders, transformations);
262
+ Object.assign(merged, transformedDefaults);
263
+ } else if (defaultHeaders) Object.assign(merged, defaultHeaders);
264
+ if (methodHeaders) Object.assign(merged, methodHeaders);
265
+ Object.keys(merged).forEach((key) => {
266
+ if (merged[key] === void 0) delete merged[key];
267
+ });
268
+ return merged;
269
+ }
270
+ /**
271
+ * Execute a request and handle the response consistently
272
+ * This provides unified error handling and response processing across all SDKs
273
+ *
274
+ * @param apiCall - Function that executes the API request
275
+ * @returns Promise with the API response in standardized format
276
+ */
277
+ async function executeRequest(apiCall) {
278
+ try {
279
+ const { data, error, response } = await apiCall();
280
+ if (error) return {
281
+ data: null,
282
+ error,
283
+ response
284
+ };
285
+ if (data && data.content !== void 0) return {
286
+ data: data.content,
287
+ error: null,
288
+ response
289
+ };
290
+ return {
291
+ data,
292
+ error: null,
293
+ response
294
+ };
295
+ } catch (err) {
296
+ const mockResponse = new Response(null, {
297
+ status: 0,
298
+ statusText: "Network Error"
299
+ });
300
+ const errorResult = {
301
+ data: null,
302
+ error: {
303
+ success: false,
304
+ code: "NETWORK_ERROR",
305
+ message: "Network error occurred",
306
+ error: err
307
+ },
308
+ response: mockResponse
309
+ };
310
+ return errorResult;
311
+ }
312
+ }
313
+ /**
314
+ * Generic base API client that all Commerce Engine SDKs can extend
315
+ * Handles common functionality like middleware setup, request execution, and header management
316
+ * Does NOT include token management - that's SDK-specific
317
+ *
318
+ * @template TPaths - OpenAPI paths type
319
+ * @template THeaders - Supported default headers type
320
+ */
321
+ var BaseAPIClient = class {
322
+ client;
323
+ config;
324
+ baseUrl;
325
+ headerTransformations;
326
+ /**
327
+ * Create a new BaseAPIClient
328
+ *
329
+ * @param config - Configuration for the API client
330
+ * @param baseUrl - The base URL for the API (must be provided by subclass)
331
+ * @param headerTransformations - Header name transformations for this SDK
332
+ */
333
+ constructor(config, baseUrl, headerTransformations = {}) {
334
+ this.config = { ...config };
335
+ this.headerTransformations = headerTransformations;
336
+ this.baseUrl = baseUrl;
337
+ this.client = createClient({ baseUrl: this.baseUrl });
338
+ this.setupMiddleware();
339
+ }
340
+ /**
341
+ * Set up all middleware for the client
342
+ */
343
+ setupMiddleware() {
344
+ if (this.config.timeout) {
345
+ const timeoutMiddleware = createTimeoutMiddleware(this.config.timeout);
346
+ this.client.use(timeoutMiddleware);
347
+ }
348
+ if (this.config.debug) {
349
+ const debugMiddleware = createDebugMiddleware(this.config.logger);
350
+ this.client.use(debugMiddleware);
351
+ }
352
+ }
353
+ /**
354
+ * Get the base URL of the API
355
+ *
356
+ * @returns The base URL of the API
357
+ */
358
+ getBaseUrl() {
359
+ return this.baseUrl;
360
+ }
361
+ /**
362
+ * Execute a request and handle the response consistently
363
+ * This provides unified error handling and response processing
364
+ *
365
+ * @param apiCall - Function that executes the API request
366
+ * @returns Promise with the API response in standardized format
367
+ */
368
+ async executeRequest(apiCall) {
369
+ return executeRequest(apiCall);
370
+ }
371
+ /**
372
+ * Merge default headers with method-level headers
373
+ * Method-level headers take precedence over default headers
374
+ * Automatically applies SDK-specific header transformations
375
+ *
376
+ * @param methodHeaders - Headers passed to the specific method call
377
+ * @returns Merged headers object with proper HTTP header names
378
+ */
379
+ mergeHeaders(methodHeaders) {
380
+ return mergeAndTransformHeaders(this.config.defaultHeaders, methodHeaders, this.headerTransformations);
381
+ }
382
+ /**
383
+ * Set default headers for the client
384
+ *
385
+ * @param headers - Default headers to set
386
+ */
387
+ setDefaultHeaders(headers) {
388
+ this.config.defaultHeaders = headers;
389
+ }
390
+ /**
391
+ * Get current default headers
392
+ *
393
+ * @returns Current default headers
394
+ */
395
+ getDefaultHeaders() {
396
+ return this.config.defaultHeaders;
397
+ }
398
+ };
399
+ /**
400
+ * Generic URL utility functions for any SDK
401
+ */
402
+ /**
403
+ * Extract pathname from URL
404
+ * Useful for middleware that needs to inspect request paths
405
+ */
406
+ function getPathnameFromUrl(url) {
407
+ try {
408
+ const urlObj = new URL(url);
409
+ return urlObj.pathname;
410
+ } catch {
411
+ return url.split("?")[0] || url;
412
+ }
413
+ }
414
+
415
+ //#endregion
4
416
  //#region src/lib/jwt-utils.ts
5
417
  /**
6
418
  * Decode and extract user information from a JWT token