@commercengine/storefront-sdk 0.3.9 → 0.3.10
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 +14 -1
- package/dist/index.js +4 -1
- package/dist/lib/client.js +32 -5
- package/dist/lib/logger-utils.d.ts +88 -0
- package/dist/lib/logger-utils.js +211 -0
- package/dist/types/storefront-api-types.d.ts +2 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { HelpersClient } from "./lib/helper";
|
|
|
8
8
|
import { CustomerClient } from "./lib/customer";
|
|
9
9
|
import { TokenStorage, MemoryTokenStorage, BrowserTokenStorage, CookieTokenStorage } from "./lib/middleware";
|
|
10
10
|
import { type UserInfo } from "./lib/jwt-utils";
|
|
11
|
+
import { ResponseUtils, type DebugLoggerFn } from "./lib/logger-utils";
|
|
11
12
|
/**
|
|
12
13
|
* Supported default headers that can be set at the SDK level
|
|
13
14
|
* Only includes headers that are actually supported by API endpoints
|
|
@@ -75,6 +76,17 @@ export interface StorefrontSDKOptions {
|
|
|
75
76
|
* Only supports headers that are actually available in the API
|
|
76
77
|
*/
|
|
77
78
|
defaultHeaders?: SupportedDefaultHeaders;
|
|
79
|
+
/**
|
|
80
|
+
* Enable debug mode for detailed request/response logging
|
|
81
|
+
* When enabled, detailed debug information will be logged via the logger
|
|
82
|
+
* Note: Response objects are always included in ApiResult regardless of debug mode
|
|
83
|
+
*/
|
|
84
|
+
debug?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Custom logger function for debug information
|
|
87
|
+
* If not provided and debug is enabled, will use console.log
|
|
88
|
+
*/
|
|
89
|
+
logger?: DebugLoggerFn;
|
|
78
90
|
}
|
|
79
91
|
/**
|
|
80
92
|
* Main SDK class for the Storefront API
|
|
@@ -197,10 +209,11 @@ export declare class StorefrontSDK {
|
|
|
197
209
|
getDefaultHeaders(): SupportedDefaultHeaders | undefined;
|
|
198
210
|
}
|
|
199
211
|
export default StorefrontSDK;
|
|
200
|
-
export { StorefrontAPIClient, AuthClient, CartClient, CatalogClient, CustomerClient, HelpersClient, ShippingClient, OrderClient, };
|
|
212
|
+
export { StorefrontAPIClient, AuthClient, CartClient, CatalogClient, CustomerClient, HelpersClient, ShippingClient, OrderClient, ResponseUtils, };
|
|
201
213
|
export { Environment };
|
|
202
214
|
export { TokenStorage, MemoryTokenStorage, BrowserTokenStorage, CookieTokenStorage, };
|
|
203
215
|
export type { CookieTokenStorageOptions } from "./lib/middleware";
|
|
204
216
|
export type { UserInfo } from "./lib/jwt-utils";
|
|
217
|
+
export type { DebugLoggerFn } from "./lib/logger-utils";
|
|
205
218
|
export type { components, operations, paths } from "./types/storefront";
|
|
206
219
|
export type * from "./types/storefront-api-types";
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import { HelpersClient } from "./lib/helper";
|
|
|
8
8
|
import { CustomerClient } from "./lib/customer";
|
|
9
9
|
import { MemoryTokenStorage, BrowserTokenStorage, CookieTokenStorage, } from "./lib/middleware";
|
|
10
10
|
import { extractUserInfoFromToken, getUserIdFromToken, isUserLoggedIn, isUserAnonymous, } from "./lib/jwt-utils";
|
|
11
|
+
import { ResponseUtils, } from "./lib/logger-utils";
|
|
11
12
|
/**
|
|
12
13
|
* Main SDK class for the Storefront API
|
|
13
14
|
*/
|
|
@@ -59,6 +60,8 @@ export class StorefrontSDK {
|
|
|
59
60
|
onTokensUpdated: options.onTokensUpdated,
|
|
60
61
|
onTokensCleared: options.onTokensCleared,
|
|
61
62
|
defaultHeaders: options.defaultHeaders,
|
|
63
|
+
debug: options.debug,
|
|
64
|
+
logger: options.logger,
|
|
62
65
|
};
|
|
63
66
|
this.catalog = new CatalogClient(config);
|
|
64
67
|
this.cart = new CartClient(config);
|
|
@@ -227,7 +230,7 @@ export class StorefrontSDK {
|
|
|
227
230
|
// Export the main SDK class
|
|
228
231
|
export default StorefrontSDK;
|
|
229
232
|
// Export individual clients for advanced usage
|
|
230
|
-
export { StorefrontAPIClient, AuthClient, CartClient, CatalogClient, CustomerClient, HelpersClient, ShippingClient, OrderClient, };
|
|
233
|
+
export { StorefrontAPIClient, AuthClient, CartClient, CatalogClient, CustomerClient, HelpersClient, ShippingClient, OrderClient, ResponseUtils, };
|
|
231
234
|
// Export environment enum
|
|
232
235
|
export { Environment };
|
|
233
236
|
// Export token storage types
|
package/dist/lib/client.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import createClient from "openapi-fetch";
|
|
2
2
|
import { createDefaultAuthMiddleware } from "./middleware";
|
|
3
3
|
import { getPathnameFromUrl, isAnonymousAuthEndpoint } from "./auth-utils";
|
|
4
|
+
import { createDebugMiddleware } from "./logger-utils";
|
|
4
5
|
/**
|
|
5
6
|
* Available API environments
|
|
6
7
|
*/
|
|
@@ -81,6 +82,11 @@ export class StorefrontAPIClient {
|
|
|
81
82
|
if (this.config.timeout) {
|
|
82
83
|
this.setupTimeoutMiddleware();
|
|
83
84
|
}
|
|
85
|
+
// Set up debug middleware if enabled
|
|
86
|
+
if (this.config.debug) {
|
|
87
|
+
const debugMiddleware = createDebugMiddleware(this.config.logger);
|
|
88
|
+
this.client.use(debugMiddleware);
|
|
89
|
+
}
|
|
84
90
|
}
|
|
85
91
|
/**
|
|
86
92
|
* Set up timeout middleware
|
|
@@ -215,21 +221,39 @@ export class StorefrontAPIClient {
|
|
|
215
221
|
*/
|
|
216
222
|
async executeRequest(apiCall) {
|
|
217
223
|
try {
|
|
218
|
-
const { data, error } = await apiCall();
|
|
224
|
+
const { data, error, response } = await apiCall();
|
|
225
|
+
// Debug logging is now handled by middleware
|
|
219
226
|
// openapi-fetch returns error for 4xx/5xx, data for 2xx
|
|
220
227
|
if (error) {
|
|
221
|
-
return {
|
|
228
|
+
return {
|
|
229
|
+
data: null,
|
|
230
|
+
error,
|
|
231
|
+
response,
|
|
232
|
+
};
|
|
222
233
|
}
|
|
223
234
|
// If response has content, return it directly
|
|
224
235
|
if (data && data.content !== undefined) {
|
|
225
|
-
return {
|
|
236
|
+
return {
|
|
237
|
+
data: data.content,
|
|
238
|
+
error: null,
|
|
239
|
+
response,
|
|
240
|
+
};
|
|
226
241
|
}
|
|
227
242
|
// If no content, return the response object (with message, success, etc.)
|
|
228
|
-
return {
|
|
243
|
+
return {
|
|
244
|
+
data: data,
|
|
245
|
+
error: null,
|
|
246
|
+
response,
|
|
247
|
+
};
|
|
229
248
|
}
|
|
230
249
|
catch (err) {
|
|
231
250
|
// This handles network errors or other unexpected errors
|
|
232
|
-
|
|
251
|
+
// Network errors don't have Response objects, so we create a mock response
|
|
252
|
+
const mockResponse = new Response(null, {
|
|
253
|
+
status: 0,
|
|
254
|
+
statusText: "Network Error",
|
|
255
|
+
});
|
|
256
|
+
const errorResult = {
|
|
233
257
|
data: null,
|
|
234
258
|
error: {
|
|
235
259
|
success: false,
|
|
@@ -237,7 +261,10 @@ export class StorefrontAPIClient {
|
|
|
237
261
|
message: "Network error occurred",
|
|
238
262
|
error: err,
|
|
239
263
|
},
|
|
264
|
+
response: mockResponse,
|
|
240
265
|
};
|
|
266
|
+
// Network errors are logged by middleware if debug is enabled
|
|
267
|
+
return errorResult;
|
|
241
268
|
}
|
|
242
269
|
}
|
|
243
270
|
/**
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { Middleware } from "openapi-fetch";
|
|
2
|
+
/**
|
|
3
|
+
* Debug logger function interface
|
|
4
|
+
*/
|
|
5
|
+
export interface DebugLoggerFn {
|
|
6
|
+
(level: "info" | "warn" | "error", message: string, data?: any): void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Response utilities for debugging and working with Response objects
|
|
10
|
+
*/
|
|
11
|
+
export declare class ResponseUtils {
|
|
12
|
+
/**
|
|
13
|
+
* Get response headers as a plain object
|
|
14
|
+
*/
|
|
15
|
+
static getHeaders(response: Response): Record<string, string>;
|
|
16
|
+
/**
|
|
17
|
+
* Get specific header value
|
|
18
|
+
*/
|
|
19
|
+
static getHeader(response: Response, name: string): string | null;
|
|
20
|
+
/**
|
|
21
|
+
* Check if response was successful
|
|
22
|
+
*/
|
|
23
|
+
static isSuccess(response: Response): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Get response metadata
|
|
26
|
+
*/
|
|
27
|
+
static getMetadata(response: Response): {
|
|
28
|
+
status: number;
|
|
29
|
+
statusText: string;
|
|
30
|
+
ok: boolean;
|
|
31
|
+
url: string;
|
|
32
|
+
redirected: boolean;
|
|
33
|
+
type: ResponseType;
|
|
34
|
+
headers: {
|
|
35
|
+
[k: string]: string;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* Clone and read response as text (useful for debugging)
|
|
40
|
+
* Note: This can only be called once per response
|
|
41
|
+
*/
|
|
42
|
+
static getText(response: Response): Promise<string>;
|
|
43
|
+
/**
|
|
44
|
+
* Clone and read response as JSON (useful for debugging)
|
|
45
|
+
* Note: This can only be called once per response
|
|
46
|
+
*/
|
|
47
|
+
static getJSON(response: Response): Promise<any>;
|
|
48
|
+
/**
|
|
49
|
+
* Format response information for debugging
|
|
50
|
+
*/
|
|
51
|
+
static format(response: Response): string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Debug logging utilities
|
|
55
|
+
*/
|
|
56
|
+
export declare class DebugLogger {
|
|
57
|
+
private logger;
|
|
58
|
+
private responseTextCache;
|
|
59
|
+
constructor(logger?: DebugLoggerFn);
|
|
60
|
+
/**
|
|
61
|
+
* Log debug information about API request
|
|
62
|
+
*/
|
|
63
|
+
logRequest(request: Request, requestBody?: any): void;
|
|
64
|
+
/**
|
|
65
|
+
* Log debug information about API response
|
|
66
|
+
*/
|
|
67
|
+
logResponse(response: Response, responseBody?: any): Promise<void>;
|
|
68
|
+
/**
|
|
69
|
+
* Log error information
|
|
70
|
+
*/
|
|
71
|
+
logError(message: string, error: any): void;
|
|
72
|
+
/**
|
|
73
|
+
* Get cached response text for a URL (if available)
|
|
74
|
+
*/
|
|
75
|
+
getCachedResponseText(url: string): string | null;
|
|
76
|
+
/**
|
|
77
|
+
* Clear cached response texts
|
|
78
|
+
*/
|
|
79
|
+
clearCache(): void;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Extract request body for logging
|
|
83
|
+
*/
|
|
84
|
+
export declare function extractRequestBody(request: Request): Promise<any>;
|
|
85
|
+
/**
|
|
86
|
+
* Create debug middleware for openapi-fetch (internal use)
|
|
87
|
+
*/
|
|
88
|
+
export declare function createDebugMiddleware(logger?: DebugLoggerFn): Middleware;
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response utilities for debugging and working with Response objects
|
|
3
|
+
*/
|
|
4
|
+
export class ResponseUtils {
|
|
5
|
+
/**
|
|
6
|
+
* Get response headers as a plain object
|
|
7
|
+
*/
|
|
8
|
+
static getHeaders(response) {
|
|
9
|
+
return Object.fromEntries(response.headers.entries());
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Get specific header value
|
|
13
|
+
*/
|
|
14
|
+
static getHeader(response, name) {
|
|
15
|
+
return response.headers.get(name);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Check if response was successful
|
|
19
|
+
*/
|
|
20
|
+
static isSuccess(response) {
|
|
21
|
+
return response.ok;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Get response metadata
|
|
25
|
+
*/
|
|
26
|
+
static getMetadata(response) {
|
|
27
|
+
return {
|
|
28
|
+
status: response.status,
|
|
29
|
+
statusText: response.statusText,
|
|
30
|
+
ok: response.ok,
|
|
31
|
+
url: response.url,
|
|
32
|
+
redirected: response.redirected,
|
|
33
|
+
type: response.type,
|
|
34
|
+
headers: Object.fromEntries(response.headers.entries()),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Clone and read response as text (useful for debugging)
|
|
39
|
+
* Note: This can only be called once per response
|
|
40
|
+
*/
|
|
41
|
+
static async getText(response) {
|
|
42
|
+
const cloned = response.clone();
|
|
43
|
+
return await cloned.text();
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Clone and read response as JSON (useful for debugging)
|
|
47
|
+
* Note: This can only be called once per response
|
|
48
|
+
*/
|
|
49
|
+
static async getJSON(response) {
|
|
50
|
+
const cloned = response.clone();
|
|
51
|
+
return await cloned.json();
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Format response information for debugging
|
|
55
|
+
*/
|
|
56
|
+
static format(response) {
|
|
57
|
+
const metadata = this.getMetadata(response);
|
|
58
|
+
return `${metadata.status} ${metadata.statusText} - ${metadata.url}`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Debug logging utilities
|
|
63
|
+
*/
|
|
64
|
+
export class DebugLogger {
|
|
65
|
+
logger;
|
|
66
|
+
responseTextCache = new Map();
|
|
67
|
+
constructor(logger) {
|
|
68
|
+
this.logger =
|
|
69
|
+
logger ||
|
|
70
|
+
((level, message, data) => {
|
|
71
|
+
console.log(`[${level.toUpperCase()}]`, message);
|
|
72
|
+
if (data)
|
|
73
|
+
console.log(data);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Log debug information about API request
|
|
78
|
+
*/
|
|
79
|
+
logRequest(request, requestBody) {
|
|
80
|
+
this.logger("info", "API Request Debug Info", {
|
|
81
|
+
method: request.method,
|
|
82
|
+
url: request.url,
|
|
83
|
+
headers: Object.fromEntries(request.headers.entries()),
|
|
84
|
+
body: requestBody,
|
|
85
|
+
timestamp: new Date().toISOString(),
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Log debug information about API response
|
|
90
|
+
*/
|
|
91
|
+
async logResponse(response, responseBody) {
|
|
92
|
+
// Cache response text for later use by ResponseUtils
|
|
93
|
+
if (responseBody && typeof responseBody === "string") {
|
|
94
|
+
this.responseTextCache.set(response.url, responseBody);
|
|
95
|
+
}
|
|
96
|
+
// Log response metadata
|
|
97
|
+
this.logger("info", "API Response Debug Info", {
|
|
98
|
+
url: response.url,
|
|
99
|
+
status: response.status,
|
|
100
|
+
statusText: response.statusText,
|
|
101
|
+
ok: response.ok,
|
|
102
|
+
headers: Object.fromEntries(response.headers.entries()),
|
|
103
|
+
redirected: response.redirected,
|
|
104
|
+
type: response.type,
|
|
105
|
+
timestamp: new Date().toISOString(),
|
|
106
|
+
});
|
|
107
|
+
// Log response body if available
|
|
108
|
+
if (responseBody) {
|
|
109
|
+
this.logger("info", "API Response Data", {
|
|
110
|
+
data: responseBody,
|
|
111
|
+
contentType: response.headers.get("content-type"),
|
|
112
|
+
contentLength: response.headers.get("content-length"),
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Log error information
|
|
118
|
+
*/
|
|
119
|
+
logError(message, error) {
|
|
120
|
+
this.logger("error", message, error);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Get cached response text for a URL (if available)
|
|
124
|
+
*/
|
|
125
|
+
getCachedResponseText(url) {
|
|
126
|
+
return this.responseTextCache.get(url) || null;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Clear cached response texts
|
|
130
|
+
*/
|
|
131
|
+
clearCache() {
|
|
132
|
+
this.responseTextCache.clear();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Extract request body for logging
|
|
137
|
+
*/
|
|
138
|
+
export async function extractRequestBody(request) {
|
|
139
|
+
if (request.method === "GET" || request.method === "HEAD") {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
const clonedRequest = request.clone();
|
|
144
|
+
const contentType = request.headers.get("content-type")?.toLowerCase();
|
|
145
|
+
if (contentType?.startsWith("application/json")) {
|
|
146
|
+
return await clonedRequest.json();
|
|
147
|
+
}
|
|
148
|
+
else if (contentType?.startsWith("multipart/form-data")) {
|
|
149
|
+
return "[FormData - cannot display]";
|
|
150
|
+
}
|
|
151
|
+
else if (contentType?.startsWith("text/")) {
|
|
152
|
+
return await clonedRequest.text();
|
|
153
|
+
}
|
|
154
|
+
return "[Request body - unknown format]";
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
return "[Request body unavailable]";
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Create debug middleware for openapi-fetch (internal use)
|
|
162
|
+
*/
|
|
163
|
+
export function createDebugMiddleware(logger) {
|
|
164
|
+
const debugLogger = new DebugLogger(logger);
|
|
165
|
+
return {
|
|
166
|
+
async onRequest({ request }) {
|
|
167
|
+
// Log request information
|
|
168
|
+
const requestBody = await extractRequestBody(request);
|
|
169
|
+
debugLogger.logRequest(request, requestBody);
|
|
170
|
+
return request;
|
|
171
|
+
},
|
|
172
|
+
async onResponse({ response }) {
|
|
173
|
+
// Clone response to read body without consuming it
|
|
174
|
+
const cloned = response.clone();
|
|
175
|
+
let responseBody = null;
|
|
176
|
+
try {
|
|
177
|
+
const contentType = response.headers.get("content-type")?.toLowerCase();
|
|
178
|
+
if (contentType?.startsWith("application/json")) {
|
|
179
|
+
responseBody = await cloned.json();
|
|
180
|
+
}
|
|
181
|
+
else if (contentType?.startsWith("text/")) {
|
|
182
|
+
responseBody = await cloned.text();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
// Ignore body reading errors
|
|
187
|
+
}
|
|
188
|
+
// Log response information
|
|
189
|
+
await debugLogger.logResponse(response, responseBody);
|
|
190
|
+
return response;
|
|
191
|
+
},
|
|
192
|
+
async onError({ error, request }) {
|
|
193
|
+
// Log error information with request context
|
|
194
|
+
debugLogger.logError("API Request Failed", {
|
|
195
|
+
error: {
|
|
196
|
+
name: error instanceof Error ? error.name : "Unknown",
|
|
197
|
+
message: error instanceof Error ? error.message : String(error),
|
|
198
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
199
|
+
},
|
|
200
|
+
request: {
|
|
201
|
+
method: request.method,
|
|
202
|
+
url: request.url,
|
|
203
|
+
headers: Object.fromEntries(request.headers.entries()),
|
|
204
|
+
},
|
|
205
|
+
timestamp: new Date().toISOString(),
|
|
206
|
+
});
|
|
207
|
+
// Re-throw the error to maintain normal error handling
|
|
208
|
+
throw error;
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
}
|