@commercengine/storefront-sdk 0.9.0 → 0.9.2
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 +1 -0
- package/dist/index.js +413 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,6 +1,418 @@
|
|
|
1
|
-
import
|
|
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
|