@01.software/sdk 0.28.0 → 0.30.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/README.md +273 -73
- package/dist/analytics/react.cjs +4 -1
- package/dist/analytics/react.cjs.map +1 -1
- package/dist/analytics/react.js +4 -1
- package/dist/analytics/react.js.map +1 -1
- package/dist/analytics.cjs +4 -1
- package/dist/analytics.cjs.map +1 -1
- package/dist/analytics.js +4 -1
- package/dist/analytics.js.map +1 -1
- package/dist/client.cjs +1476 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +28 -0
- package/dist/client.d.ts +28 -0
- package/dist/client.js +1453 -0
- package/dist/client.js.map +1 -0
- package/dist/collection-client-B9d9kr1d.d.ts +218 -0
- package/dist/collection-client-QPbwimkU.d.cts +218 -0
- package/dist/{const-mdQQtIOz.d.ts → const-B75IFDRi.d.ts} +2 -4
- package/dist/{const-Cz9Ki_I7.d.cts → const-VZuk2tWc.d.cts} +2 -4
- package/dist/index-B2WbhEgT.d.cts +106 -0
- package/dist/index-B2WbhEgT.d.ts +106 -0
- package/dist/index.cjs +1291 -1501
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -115
- package/dist/index.d.ts +11 -115
- package/dist/index.js +1292 -1520
- package/dist/index.js.map +1 -1
- package/dist/metadata.cjs +91 -0
- package/dist/metadata.cjs.map +1 -0
- package/dist/metadata.d.cts +58 -0
- package/dist/metadata.d.ts +58 -0
- package/dist/metadata.js +68 -0
- package/dist/metadata.js.map +1 -0
- package/dist/{payload-types-BrSYb-sh.d.cts → payload-types-DPjO_IbQ.d.cts} +17 -6
- package/dist/{payload-types-BrSYb-sh.d.ts → payload-types-DPjO_IbQ.d.ts} +17 -6
- package/dist/query.cjs +1791 -0
- package/dist/query.cjs.map +1 -0
- package/dist/query.d.cts +244 -0
- package/dist/query.d.ts +244 -0
- package/dist/query.js +1786 -0
- package/dist/query.js.map +1 -0
- package/dist/realtime.cjs +4 -1
- package/dist/realtime.cjs.map +1 -1
- package/dist/realtime.d.cts +2 -2
- package/dist/realtime.d.ts +2 -2
- package/dist/realtime.js +4 -1
- package/dist/realtime.js.map +1 -1
- package/dist/{server-BINWywT8.d.cts → server-CrsPyqEc.d.cts} +14 -31
- package/dist/{server-BINWywT8.d.ts → server-CrsPyqEc.d.ts} +14 -31
- package/dist/server.cjs +300 -844
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +112 -7
- package/dist/server.d.ts +112 -7
- package/dist/server.js +300 -862
- package/dist/server.js.map +1 -1
- package/dist/{types-BLUb4cYq.d.ts → types-1fBLrYU7.d.ts} +1 -1
- package/dist/{types-CW4PaIL7.d.cts → types-BwT0eeaz.d.cts} +1 -1
- package/dist/types-Dlb2mwpX.d.cts +1249 -0
- package/dist/types-DuSKPiY5.d.ts +1249 -0
- package/dist/ui/canvas/server.cjs +7 -6
- package/dist/ui/canvas/server.cjs.map +1 -1
- package/dist/ui/canvas/server.d.cts +1 -3
- package/dist/ui/canvas/server.d.ts +1 -3
- package/dist/ui/canvas/server.js +7 -6
- package/dist/ui/canvas/server.js.map +1 -1
- package/dist/ui/canvas.cjs +11 -10
- package/dist/ui/canvas.cjs.map +1 -1
- package/dist/ui/canvas.d.cts +29 -6
- package/dist/ui/canvas.d.ts +29 -6
- package/dist/ui/canvas.js +11 -10
- package/dist/ui/canvas.js.map +1 -1
- package/dist/ui/form.d.cts +1 -1
- package/dist/ui/form.d.ts +1 -1
- package/dist/ui/video.d.cts +1 -1
- package/dist/ui/video.d.ts +1 -1
- package/dist/webhook.d.cts +3 -3
- package/dist/webhook.d.ts +3 -3
- package/package.json +82 -13
- package/dist/server-C2Q9R-Lu.d.ts +0 -1662
- package/dist/server-D369bCVJ.d.cts +0 -1662
package/dist/client.cjs
ADDED
|
@@ -0,0 +1,1476 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/client.ts
|
|
21
|
+
var client_exports = {};
|
|
22
|
+
__export(client_exports, {
|
|
23
|
+
Client: () => Client,
|
|
24
|
+
createClient: () => createClient
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(client_exports);
|
|
27
|
+
|
|
28
|
+
// src/core/collection/http-client.ts
|
|
29
|
+
var import_qs_esm = require("qs-esm");
|
|
30
|
+
|
|
31
|
+
// src/core/internal/errors/index.ts
|
|
32
|
+
var SDKError = class extends Error {
|
|
33
|
+
constructor(code, message, status, details, userMessage, suggestion, requestId) {
|
|
34
|
+
super(message);
|
|
35
|
+
this.name = "SDKError";
|
|
36
|
+
this.code = code;
|
|
37
|
+
this.status = status;
|
|
38
|
+
this.details = details;
|
|
39
|
+
this.userMessage = userMessage;
|
|
40
|
+
this.suggestion = suggestion;
|
|
41
|
+
this.requestId = requestId;
|
|
42
|
+
if (Error.captureStackTrace) {
|
|
43
|
+
Error.captureStackTrace(this, new.target);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
getUserMessage() {
|
|
47
|
+
return this.userMessage || this.message;
|
|
48
|
+
}
|
|
49
|
+
toJSON() {
|
|
50
|
+
return {
|
|
51
|
+
name: this.name,
|
|
52
|
+
code: this.code,
|
|
53
|
+
message: this.message,
|
|
54
|
+
status: this.status,
|
|
55
|
+
details: this.details,
|
|
56
|
+
userMessage: this.userMessage,
|
|
57
|
+
suggestion: this.suggestion,
|
|
58
|
+
...this.requestId !== void 0 && { requestId: this.requestId }
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
var NetworkError = class extends SDKError {
|
|
63
|
+
constructor(message, status, details, userMessage, suggestion) {
|
|
64
|
+
super("NETWORK_ERROR", message, status, details, userMessage, suggestion);
|
|
65
|
+
this.name = "NetworkError";
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
var ValidationError = class extends SDKError {
|
|
69
|
+
constructor(message, details, userMessage, suggestion, status = 400) {
|
|
70
|
+
super("VALIDATION_ERROR", message, status, details, userMessage, suggestion);
|
|
71
|
+
this.name = "ValidationError";
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var ApiError = class extends SDKError {
|
|
75
|
+
constructor(message, status, details, userMessage, suggestion) {
|
|
76
|
+
super("API_ERROR", message, status, details, userMessage, suggestion);
|
|
77
|
+
this.name = "ApiError";
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
var ConfigError = class extends SDKError {
|
|
81
|
+
constructor(message, details, userMessage, suggestion) {
|
|
82
|
+
super("CONFIG_ERROR", message, void 0, details, userMessage, suggestion);
|
|
83
|
+
this.name = "ConfigError";
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
var TimeoutError = class extends SDKError {
|
|
87
|
+
constructor(message = "Request timed out.", details, userMessage, suggestion) {
|
|
88
|
+
super("TIMEOUT_ERROR", message, 408, details, userMessage, suggestion);
|
|
89
|
+
this.name = "TimeoutError";
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
var UsageLimitError = class extends SDKError {
|
|
93
|
+
constructor(message, usage, details, userMessage, suggestion) {
|
|
94
|
+
super("USAGE_LIMIT_ERROR", message, 429, details, userMessage, suggestion);
|
|
95
|
+
this.name = "UsageLimitError";
|
|
96
|
+
this.usage = usage;
|
|
97
|
+
}
|
|
98
|
+
toJSON() {
|
|
99
|
+
return {
|
|
100
|
+
...super.toJSON(),
|
|
101
|
+
usage: this.usage
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
var AuthError = class extends SDKError {
|
|
106
|
+
constructor(message, details, userMessage, suggestion, requestId) {
|
|
107
|
+
super("auth_error", message, 401, details, userMessage, suggestion, requestId);
|
|
108
|
+
this.name = "AuthError";
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
var PermissionError = class extends SDKError {
|
|
112
|
+
constructor(message, details, userMessage, suggestion, requestId) {
|
|
113
|
+
super("permission_error", message, 403, details, userMessage, suggestion, requestId);
|
|
114
|
+
this.name = "PermissionError";
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
var NotFoundError = class extends SDKError {
|
|
118
|
+
constructor(message, details, userMessage, suggestion, requestId) {
|
|
119
|
+
super("not_found", message, 404, details, userMessage, suggestion, requestId);
|
|
120
|
+
this.name = "NotFoundError";
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
var ConflictError = class extends SDKError {
|
|
124
|
+
constructor(message, details, userMessage, suggestion, requestId) {
|
|
125
|
+
super("conflict", message, 409, details, userMessage, suggestion, requestId);
|
|
126
|
+
this.name = "ConflictError";
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
var RateLimitError = class extends SDKError {
|
|
130
|
+
constructor(message, retryAfter, details, userMessage, suggestion, requestId) {
|
|
131
|
+
super("rate_limit_exceeded", message, 429, details, userMessage, suggestion, requestId);
|
|
132
|
+
this.name = "RateLimitError";
|
|
133
|
+
this.retryAfter = retryAfter;
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
var createNetworkError = (message, status, details, userMessage, suggestion) => new NetworkError(message, status, details, userMessage, suggestion);
|
|
137
|
+
var createValidationError = (message, details, userMessage, suggestion, status) => new ValidationError(message, details, userMessage, suggestion, status);
|
|
138
|
+
var createApiError = (message, status, details, userMessage, suggestion) => new ApiError(message, status, details, userMessage, suggestion);
|
|
139
|
+
var createConfigError = (message, details, userMessage, suggestion) => new ConfigError(message, details, userMessage, suggestion);
|
|
140
|
+
var createTimeoutError = (message, details, userMessage, suggestion) => new TimeoutError(message, details, userMessage, suggestion);
|
|
141
|
+
var createUsageLimitError = (message, usage, details, userMessage, suggestion) => new UsageLimitError(message, usage, details, userMessage, suggestion);
|
|
142
|
+
var createAuthError = (message, details, userMessage, suggestion, requestId) => new AuthError(message, details, userMessage, suggestion, requestId);
|
|
143
|
+
var createPermissionError = (message, details, userMessage, suggestion, requestId) => new PermissionError(message, details, userMessage, suggestion, requestId);
|
|
144
|
+
var createNotFoundError = (message, details, userMessage, suggestion, requestId) => new NotFoundError(message, details, userMessage, suggestion, requestId);
|
|
145
|
+
var createConflictError = (message, details, userMessage, suggestion, requestId) => new ConflictError(message, details, userMessage, suggestion, requestId);
|
|
146
|
+
var createRateLimitError = (message, retryAfter, details, userMessage, suggestion, requestId) => new RateLimitError(message, retryAfter, details, userMessage, suggestion, requestId);
|
|
147
|
+
|
|
148
|
+
// src/core/internal/utils/credentials.ts
|
|
149
|
+
function requirePublishableKeyForSecret(apiName, publishableKey, secretKey) {
|
|
150
|
+
if (secretKey && !publishableKey) {
|
|
151
|
+
throw createConfigError(
|
|
152
|
+
`publishableKey is required for ${apiName} when secretKey is used. It is sent as X-Publishable-Key for tenant routing, rate limiting, and quota enforcement.`
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
return publishableKey ?? "";
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// src/core/client/types.ts
|
|
159
|
+
function resolveApiUrl(apiUrl) {
|
|
160
|
+
if (apiUrl) {
|
|
161
|
+
return apiUrl.replace(/\/$/, "");
|
|
162
|
+
}
|
|
163
|
+
if (typeof process !== "undefined" && process.env) {
|
|
164
|
+
const envUrl = process.env.SOFTWARE_API_URL || process.env.NEXT_PUBLIC_SOFTWARE_API_URL;
|
|
165
|
+
if (envUrl) {
|
|
166
|
+
return envUrl.replace(/\/$/, "");
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return "https://api.01.software";
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// src/core/internal/utils/http.ts
|
|
173
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
174
|
+
var DEFAULT_RETRYABLE_STATUSES = [408, 429, 500, 502, 503, 504];
|
|
175
|
+
var NON_RETRYABLE_STATUSES = [400, 401, 403, 404, 409, 422];
|
|
176
|
+
var SAFE_METHODS = ["GET", "HEAD", "OPTIONS"];
|
|
177
|
+
function debugLog(debug, type, message, data) {
|
|
178
|
+
if (!debug) return;
|
|
179
|
+
const shouldLog = debug === true || type === "request" && debug.logRequests || type === "response" && debug.logResponses || type === "error" && debug.logErrors;
|
|
180
|
+
if (shouldLog) {
|
|
181
|
+
console.group(`[SDK ${type.toUpperCase()}] ${message}`);
|
|
182
|
+
if (data) console.log(data);
|
|
183
|
+
console.groupEnd();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function getErrorSuggestion(status) {
|
|
187
|
+
if (status === 400)
|
|
188
|
+
return "The request data failed validation. Check field values and types.";
|
|
189
|
+
if (status === 401) return "Please check your authentication credentials.";
|
|
190
|
+
if (status === 403)
|
|
191
|
+
return "Access denied. Check your credentials or permissions.";
|
|
192
|
+
if (status === 404) return "The requested resource was not found.";
|
|
193
|
+
if (status === 422) return "The request data failed validation.";
|
|
194
|
+
if (status >= 500) return "A server error occurred. Please try again later.";
|
|
195
|
+
return void 0;
|
|
196
|
+
}
|
|
197
|
+
function isUsageLimitExceededResponse(response) {
|
|
198
|
+
if (response.status !== 429) return false;
|
|
199
|
+
const limit = parseInt(response.headers.get("X-Usage-Limit") || "", 10);
|
|
200
|
+
const current = parseInt(response.headers.get("X-Usage-Current") || "", 10);
|
|
201
|
+
if (!Number.isFinite(limit) || !Number.isFinite(current)) return false;
|
|
202
|
+
return response.headers.get("X-Usage-Exceeded") === "true" || current > limit;
|
|
203
|
+
}
|
|
204
|
+
async function parseErrorBody(response) {
|
|
205
|
+
const fallback = {
|
|
206
|
+
errorMessage: `HTTP ${response.status}: ${response.statusText}`,
|
|
207
|
+
userMessage: `Request failed (status: ${response.status})`
|
|
208
|
+
};
|
|
209
|
+
try {
|
|
210
|
+
const body = await response.json();
|
|
211
|
+
const reason = typeof body.reason === "string" ? body.reason : typeof body.code === "string" ? body.code : void 0;
|
|
212
|
+
if (body.errors && Array.isArray(body.errors)) {
|
|
213
|
+
const fieldErrors = [];
|
|
214
|
+
for (const e of body.errors) {
|
|
215
|
+
if (e.data?.errors && Array.isArray(e.data.errors) && e.data.errors.length > 0) {
|
|
216
|
+
for (const fe of e.data.errors) {
|
|
217
|
+
fieldErrors.push({
|
|
218
|
+
field: fe.path || fe.field,
|
|
219
|
+
message: fe.message
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
} else if (e.field || e.message) {
|
|
223
|
+
fieldErrors.push({ field: e.field, message: e.message });
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const details = (fieldErrors.length > 0 ? fieldErrors : body.errors).map(
|
|
227
|
+
(e) => e.field ? `${e.field}: ${e.message}` : e.message
|
|
228
|
+
).filter(Boolean).join("; ");
|
|
229
|
+
if (details) {
|
|
230
|
+
return {
|
|
231
|
+
errorMessage: `HTTP ${response.status}: ${details}`,
|
|
232
|
+
userMessage: details,
|
|
233
|
+
reason,
|
|
234
|
+
body,
|
|
235
|
+
errors: fieldErrors.length > 0 ? fieldErrors : body.errors
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (typeof body.error === "string") {
|
|
240
|
+
return {
|
|
241
|
+
errorMessage: `HTTP ${response.status}: ${body.error}`,
|
|
242
|
+
userMessage: body.error,
|
|
243
|
+
reason,
|
|
244
|
+
body
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
if (body.message) {
|
|
248
|
+
return {
|
|
249
|
+
errorMessage: `HTTP ${response.status}: ${body.message}`,
|
|
250
|
+
userMessage: body.message,
|
|
251
|
+
reason,
|
|
252
|
+
body
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
return { ...fallback, reason, body };
|
|
256
|
+
} catch {
|
|
257
|
+
return fallback;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
async function delay(ms) {
|
|
261
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
262
|
+
}
|
|
263
|
+
function attachRequestId(err, id) {
|
|
264
|
+
if (id) err.requestId = id;
|
|
265
|
+
return err;
|
|
266
|
+
}
|
|
267
|
+
function createHttpStatusError(status, parsed, details, requestId) {
|
|
268
|
+
const errorDetails = {
|
|
269
|
+
...details,
|
|
270
|
+
...parsed.errors && { errors: parsed.errors },
|
|
271
|
+
...parsed.body && { body: parsed.body }
|
|
272
|
+
};
|
|
273
|
+
const suggestion = getErrorSuggestion(status);
|
|
274
|
+
if (status === 400 || status === 422) {
|
|
275
|
+
return attachRequestId(
|
|
276
|
+
createValidationError(
|
|
277
|
+
parsed.errorMessage,
|
|
278
|
+
errorDetails,
|
|
279
|
+
parsed.userMessage,
|
|
280
|
+
suggestion,
|
|
281
|
+
status
|
|
282
|
+
),
|
|
283
|
+
requestId
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
if (status === 401) {
|
|
287
|
+
return attachRequestId(
|
|
288
|
+
createAuthError(
|
|
289
|
+
parsed.errorMessage,
|
|
290
|
+
errorDetails,
|
|
291
|
+
parsed.userMessage,
|
|
292
|
+
suggestion
|
|
293
|
+
),
|
|
294
|
+
requestId
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
if (status === 403) {
|
|
298
|
+
return attachRequestId(
|
|
299
|
+
createPermissionError(
|
|
300
|
+
parsed.errorMessage,
|
|
301
|
+
errorDetails,
|
|
302
|
+
parsed.userMessage,
|
|
303
|
+
suggestion
|
|
304
|
+
),
|
|
305
|
+
requestId
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
if (status === 404) {
|
|
309
|
+
return attachRequestId(
|
|
310
|
+
createNotFoundError(
|
|
311
|
+
parsed.errorMessage,
|
|
312
|
+
errorDetails,
|
|
313
|
+
parsed.userMessage,
|
|
314
|
+
suggestion
|
|
315
|
+
),
|
|
316
|
+
requestId
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
if (status === 409) {
|
|
320
|
+
return attachRequestId(
|
|
321
|
+
createConflictError(
|
|
322
|
+
parsed.errorMessage,
|
|
323
|
+
errorDetails,
|
|
324
|
+
parsed.userMessage,
|
|
325
|
+
suggestion
|
|
326
|
+
),
|
|
327
|
+
requestId
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
return attachRequestId(
|
|
331
|
+
createNetworkError(
|
|
332
|
+
parsed.errorMessage,
|
|
333
|
+
status,
|
|
334
|
+
errorDetails,
|
|
335
|
+
parsed.userMessage,
|
|
336
|
+
suggestion
|
|
337
|
+
),
|
|
338
|
+
requestId
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
async function httpFetch(url, options) {
|
|
342
|
+
const {
|
|
343
|
+
apiUrl,
|
|
344
|
+
publishableKey,
|
|
345
|
+
secretKey,
|
|
346
|
+
customerToken,
|
|
347
|
+
timeout = DEFAULT_TIMEOUT,
|
|
348
|
+
debug,
|
|
349
|
+
retry,
|
|
350
|
+
onUnauthorized,
|
|
351
|
+
...requestInit
|
|
352
|
+
} = options || {};
|
|
353
|
+
const baseUrl = resolveApiUrl(apiUrl);
|
|
354
|
+
const retryConfig = {
|
|
355
|
+
maxRetries: retry?.maxRetries ?? 3,
|
|
356
|
+
retryableStatuses: retry?.retryableStatuses ?? DEFAULT_RETRYABLE_STATUSES,
|
|
357
|
+
retryDelay: retry?.retryDelay ?? ((attempt) => Math.min(1e3 * 2 ** attempt, 1e4))
|
|
358
|
+
};
|
|
359
|
+
let authToken;
|
|
360
|
+
if (secretKey) {
|
|
361
|
+
authToken = secretKey;
|
|
362
|
+
} else if (customerToken) {
|
|
363
|
+
authToken = customerToken;
|
|
364
|
+
}
|
|
365
|
+
let lastError;
|
|
366
|
+
let hasRetried401 = false;
|
|
367
|
+
for (let attempt = 0; attempt <= retryConfig.maxRetries; attempt++) {
|
|
368
|
+
try {
|
|
369
|
+
const headers = new Headers(requestInit.headers);
|
|
370
|
+
if (publishableKey) {
|
|
371
|
+
headers.set("X-Publishable-Key", publishableKey);
|
|
372
|
+
}
|
|
373
|
+
if (authToken) {
|
|
374
|
+
headers.set("Authorization", `Bearer ${authToken}`);
|
|
375
|
+
}
|
|
376
|
+
if (!headers.has("Content-Type") && requestInit.body && !(requestInit.body instanceof FormData)) {
|
|
377
|
+
headers.set("Content-Type", "application/json");
|
|
378
|
+
}
|
|
379
|
+
const redactedHeaders = Object.fromEntries(headers.entries());
|
|
380
|
+
if (redactedHeaders["authorization"]) {
|
|
381
|
+
const token = redactedHeaders["authorization"];
|
|
382
|
+
redactedHeaders["authorization"] = token.length > 20 ? `Bearer ...****${token.slice(-8)}` : "****";
|
|
383
|
+
}
|
|
384
|
+
debugLog(debug, "request", url, {
|
|
385
|
+
method: requestInit.method || "GET",
|
|
386
|
+
headers: redactedHeaders,
|
|
387
|
+
attempt: attempt + 1
|
|
388
|
+
});
|
|
389
|
+
const controller = new AbortController();
|
|
390
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
391
|
+
const response = await fetch(`${baseUrl}${url}`, {
|
|
392
|
+
...requestInit,
|
|
393
|
+
headers,
|
|
394
|
+
signal: controller.signal
|
|
395
|
+
});
|
|
396
|
+
clearTimeout(timeoutId);
|
|
397
|
+
const requestId = response.headers.get("x-request-id") ?? void 0;
|
|
398
|
+
debugLog(debug, "response", url, {
|
|
399
|
+
status: response.status,
|
|
400
|
+
statusText: response.statusText,
|
|
401
|
+
headers: Object.fromEntries(response.headers.entries())
|
|
402
|
+
});
|
|
403
|
+
if (!response.ok) {
|
|
404
|
+
if (isUsageLimitExceededResponse(response)) {
|
|
405
|
+
const limit = parseInt(
|
|
406
|
+
response.headers.get("X-Usage-Limit") || "0",
|
|
407
|
+
10
|
|
408
|
+
);
|
|
409
|
+
const current = parseInt(
|
|
410
|
+
response.headers.get("X-Usage-Current") || "0",
|
|
411
|
+
10
|
|
412
|
+
);
|
|
413
|
+
const remaining = parseInt(
|
|
414
|
+
response.headers.get("X-Usage-Remaining") || "0",
|
|
415
|
+
10
|
|
416
|
+
);
|
|
417
|
+
throw attachRequestId(
|
|
418
|
+
createUsageLimitError(
|
|
419
|
+
`Monthly API usage limit exceeded (${current.toLocaleString()}/${limit.toLocaleString()})`,
|
|
420
|
+
{ limit, current, remaining },
|
|
421
|
+
{
|
|
422
|
+
url,
|
|
423
|
+
method: requestInit.method || "GET",
|
|
424
|
+
attempt: attempt + 1
|
|
425
|
+
},
|
|
426
|
+
"Monthly API call limit exceeded. Please upgrade your plan.",
|
|
427
|
+
"Upgrade your tenant plan to increase the monthly API call limit."
|
|
428
|
+
),
|
|
429
|
+
requestId
|
|
430
|
+
);
|
|
431
|
+
}
|
|
432
|
+
const parsed = await parseErrorBody(response);
|
|
433
|
+
if (response.status === 401 && onUnauthorized && customerToken && !hasRetried401 && parsed.reason === "token_expired") {
|
|
434
|
+
hasRetried401 = true;
|
|
435
|
+
try {
|
|
436
|
+
const newToken = await onUnauthorized();
|
|
437
|
+
if (newToken) {
|
|
438
|
+
authToken = newToken;
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
} catch {
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
const details = {
|
|
445
|
+
url,
|
|
446
|
+
method: requestInit.method || "GET",
|
|
447
|
+
attempt: attempt + 1
|
|
448
|
+
};
|
|
449
|
+
if (NON_RETRYABLE_STATUSES.includes(response.status)) {
|
|
450
|
+
throw createHttpStatusError(
|
|
451
|
+
response.status,
|
|
452
|
+
parsed,
|
|
453
|
+
details,
|
|
454
|
+
requestId
|
|
455
|
+
);
|
|
456
|
+
}
|
|
457
|
+
const error = attachRequestId(
|
|
458
|
+
createNetworkError(
|
|
459
|
+
parsed.errorMessage,
|
|
460
|
+
response.status,
|
|
461
|
+
details,
|
|
462
|
+
parsed.userMessage,
|
|
463
|
+
getErrorSuggestion(response.status)
|
|
464
|
+
),
|
|
465
|
+
requestId
|
|
466
|
+
);
|
|
467
|
+
const method = (requestInit.method || "GET").toUpperCase();
|
|
468
|
+
if (attempt < retryConfig.maxRetries && SAFE_METHODS.includes(method) && retryConfig.retryableStatuses.includes(response.status)) {
|
|
469
|
+
lastError = error;
|
|
470
|
+
const retryDelay = retryConfig.retryDelay(attempt);
|
|
471
|
+
debugLog(debug, "error", `Retrying in ${retryDelay}ms...`, error);
|
|
472
|
+
await delay(retryDelay);
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
throw error;
|
|
476
|
+
}
|
|
477
|
+
return response;
|
|
478
|
+
} catch (error) {
|
|
479
|
+
debugLog(debug, "error", url, error);
|
|
480
|
+
const method = (requestInit.method || "GET").toUpperCase();
|
|
481
|
+
const isSafe = SAFE_METHODS.includes(method);
|
|
482
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
483
|
+
const timeoutError = createTimeoutError(
|
|
484
|
+
`Request timed out after ${timeout}ms.`,
|
|
485
|
+
{ url, timeout, attempt: attempt + 1 },
|
|
486
|
+
"The request timed out.",
|
|
487
|
+
"Please check your network connection or try again later."
|
|
488
|
+
);
|
|
489
|
+
if (isSafe && attempt < retryConfig.maxRetries) {
|
|
490
|
+
lastError = timeoutError;
|
|
491
|
+
await delay(retryConfig.retryDelay(attempt));
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
throw timeoutError;
|
|
495
|
+
}
|
|
496
|
+
if (error instanceof TypeError) {
|
|
497
|
+
const networkError = createNetworkError(
|
|
498
|
+
"Network connection failed.",
|
|
499
|
+
void 0,
|
|
500
|
+
{ url, originalError: error.message, attempt: attempt + 1 },
|
|
501
|
+
"Network connection failed.",
|
|
502
|
+
"Please check your internet connection and try again."
|
|
503
|
+
);
|
|
504
|
+
if (isSafe && attempt < retryConfig.maxRetries) {
|
|
505
|
+
lastError = networkError;
|
|
506
|
+
await delay(retryConfig.retryDelay(attempt));
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
throw networkError;
|
|
510
|
+
}
|
|
511
|
+
if (error instanceof NetworkError || error instanceof TimeoutError) {
|
|
512
|
+
if (isSafe && attempt < retryConfig.maxRetries && error.status && !NON_RETRYABLE_STATUSES.includes(error.status) && retryConfig.retryableStatuses.includes(error.status)) {
|
|
513
|
+
lastError = error;
|
|
514
|
+
await delay(retryConfig.retryDelay(attempt));
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
throw error;
|
|
518
|
+
}
|
|
519
|
+
if (error instanceof SDKError) {
|
|
520
|
+
throw error;
|
|
521
|
+
}
|
|
522
|
+
const unknownError = createNetworkError(
|
|
523
|
+
error instanceof Error ? error.message : "An unknown network error occurred.",
|
|
524
|
+
void 0,
|
|
525
|
+
{ url, originalError: error, attempt: attempt + 1 },
|
|
526
|
+
"An unknown error occurred.",
|
|
527
|
+
"Please try again later."
|
|
528
|
+
);
|
|
529
|
+
if (isSafe && attempt < retryConfig.maxRetries) {
|
|
530
|
+
lastError = unknownError;
|
|
531
|
+
await delay(retryConfig.retryDelay(attempt));
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
throw unknownError;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
throw lastError ?? new NetworkError("Request failed after retries");
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// src/core/collection/http-client.ts
|
|
541
|
+
var HttpClient = class {
|
|
542
|
+
constructor(publishableKey, secretKey, getCustomerToken, onUnauthorized, onRequestId, apiUrl) {
|
|
543
|
+
this.publishableKey = requirePublishableKeyForSecret(
|
|
544
|
+
"CollectionClient",
|
|
545
|
+
publishableKey,
|
|
546
|
+
secretKey
|
|
547
|
+
);
|
|
548
|
+
this.secretKey = secretKey;
|
|
549
|
+
this.getCustomerToken = getCustomerToken;
|
|
550
|
+
this.onUnauthorized = onUnauthorized;
|
|
551
|
+
this.onRequestId = onRequestId;
|
|
552
|
+
this.apiUrl = apiUrl;
|
|
553
|
+
}
|
|
554
|
+
get defaultOptions() {
|
|
555
|
+
const opts = {
|
|
556
|
+
apiUrl: this.apiUrl,
|
|
557
|
+
publishableKey: this.publishableKey,
|
|
558
|
+
secretKey: this.secretKey
|
|
559
|
+
};
|
|
560
|
+
const token = this.getCustomerToken?.();
|
|
561
|
+
if (token) {
|
|
562
|
+
opts.customerToken = token;
|
|
563
|
+
if (this.onUnauthorized) {
|
|
564
|
+
opts.onUnauthorized = this.onUnauthorized;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
return opts;
|
|
568
|
+
}
|
|
569
|
+
async fetchWithTracking(url, opts) {
|
|
570
|
+
try {
|
|
571
|
+
const response = await httpFetch(url, opts);
|
|
572
|
+
this.onRequestId?.(response.headers.get("x-request-id") ?? null);
|
|
573
|
+
return response;
|
|
574
|
+
} catch (err) {
|
|
575
|
+
const id = err instanceof SDKError ? err.requestId ?? null : null;
|
|
576
|
+
this.onRequestId?.(id);
|
|
577
|
+
throw err;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
buildUrl(endpoint, options) {
|
|
581
|
+
if (!options) return endpoint;
|
|
582
|
+
const queryString = (0, import_qs_esm.stringify)(options, { addQueryPrefix: true });
|
|
583
|
+
return queryString ? `${endpoint}${queryString}` : endpoint;
|
|
584
|
+
}
|
|
585
|
+
assertJsonResponse(response) {
|
|
586
|
+
const contentType = response.headers.get("content-type");
|
|
587
|
+
if (!contentType?.includes("application/json")) {
|
|
588
|
+
throw createApiError("Response is not in JSON format.", response.status, {
|
|
589
|
+
contentType
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
/**
|
|
594
|
+
* Parse Payload CMS find response (list query)
|
|
595
|
+
* Returns native Payload response structure
|
|
596
|
+
*/
|
|
597
|
+
async parseFindResponse(response) {
|
|
598
|
+
const contentType = response.headers.get("content-type");
|
|
599
|
+
try {
|
|
600
|
+
this.assertJsonResponse(response);
|
|
601
|
+
const jsonData = await response.json();
|
|
602
|
+
if (jsonData.docs === void 0) {
|
|
603
|
+
throw createApiError("Invalid find response.", response.status, {
|
|
604
|
+
jsonData
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
return {
|
|
608
|
+
docs: jsonData.docs,
|
|
609
|
+
totalDocs: jsonData.totalDocs ?? 0,
|
|
610
|
+
limit: jsonData.limit || 20,
|
|
611
|
+
totalPages: jsonData.totalPages ?? 0,
|
|
612
|
+
page: jsonData.page || 1,
|
|
613
|
+
pagingCounter: jsonData.pagingCounter || 1,
|
|
614
|
+
hasPrevPage: jsonData.hasPrevPage ?? false,
|
|
615
|
+
hasNextPage: jsonData.hasNextPage ?? false,
|
|
616
|
+
prevPage: jsonData.prevPage ?? null,
|
|
617
|
+
nextPage: jsonData.nextPage ?? null
|
|
618
|
+
};
|
|
619
|
+
} catch (error) {
|
|
620
|
+
if (error instanceof SDKError) throw error;
|
|
621
|
+
throw createApiError("Failed to parse response.", response.status, {
|
|
622
|
+
contentType,
|
|
623
|
+
error: error instanceof Error ? error.message : error
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Parse Payload CMS mutation response (create/update)
|
|
629
|
+
* Returns native Payload response structure
|
|
630
|
+
*/
|
|
631
|
+
async parseMutationResponse(response) {
|
|
632
|
+
const contentType = response.headers.get("content-type");
|
|
633
|
+
try {
|
|
634
|
+
this.assertJsonResponse(response);
|
|
635
|
+
const jsonData = await response.json();
|
|
636
|
+
if (jsonData.doc === void 0) {
|
|
637
|
+
throw createApiError("Invalid mutation response.", response.status, {
|
|
638
|
+
jsonData
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
return {
|
|
642
|
+
message: jsonData.message || "",
|
|
643
|
+
doc: jsonData.doc,
|
|
644
|
+
errors: jsonData.errors
|
|
645
|
+
};
|
|
646
|
+
} catch (error) {
|
|
647
|
+
if (error instanceof SDKError) throw error;
|
|
648
|
+
throw createApiError("Failed to parse response.", response.status, {
|
|
649
|
+
contentType,
|
|
650
|
+
error: error instanceof Error ? error.message : error
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Parse Payload CMS document response (findById/delete)
|
|
656
|
+
* Returns document directly without wrapper
|
|
657
|
+
*/
|
|
658
|
+
async parseDocumentResponse(response) {
|
|
659
|
+
const contentType = response.headers.get("content-type");
|
|
660
|
+
try {
|
|
661
|
+
this.assertJsonResponse(response);
|
|
662
|
+
const jsonData = await response.json();
|
|
663
|
+
return jsonData;
|
|
664
|
+
} catch (error) {
|
|
665
|
+
if (error instanceof SDKError) throw error;
|
|
666
|
+
throw createApiError("Failed to parse response.", response.status, {
|
|
667
|
+
contentType,
|
|
668
|
+
error: error instanceof Error ? error.message : error
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
|
|
674
|
+
// src/utils/types.ts
|
|
675
|
+
var resolveRelation = (ref) => {
|
|
676
|
+
if (typeof ref === "string" || typeof ref === "number" || ref === null || ref === void 0)
|
|
677
|
+
return null;
|
|
678
|
+
return ref;
|
|
679
|
+
};
|
|
680
|
+
|
|
681
|
+
// src/core/metadata/index.ts
|
|
682
|
+
function extractSeo(doc) {
|
|
683
|
+
const seo = doc.seo ?? {};
|
|
684
|
+
const og = seo.openGraph ?? {};
|
|
685
|
+
return {
|
|
686
|
+
title: seo.title ?? doc.title ?? null,
|
|
687
|
+
description: seo.description ?? null,
|
|
688
|
+
noIndex: seo.noIndex ?? null,
|
|
689
|
+
canonical: seo.canonical ?? null,
|
|
690
|
+
openGraph: {
|
|
691
|
+
title: og.title ?? null,
|
|
692
|
+
description: og.description ?? null,
|
|
693
|
+
image: og.image ?? null
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
function generateMetadata(input, options) {
|
|
698
|
+
const title = input.title ?? void 0;
|
|
699
|
+
const description = input.description ?? void 0;
|
|
700
|
+
const ogTitle = input.openGraph?.title ?? title;
|
|
701
|
+
const ogDescription = input.openGraph?.description ?? description;
|
|
702
|
+
const image = resolveMetaImage(input.openGraph?.image);
|
|
703
|
+
return {
|
|
704
|
+
title,
|
|
705
|
+
description,
|
|
706
|
+
...input.noIndex && { robots: { index: false, follow: false } },
|
|
707
|
+
...input.canonical && { alternates: { canonical: input.canonical } },
|
|
708
|
+
openGraph: {
|
|
709
|
+
...ogTitle && { title: ogTitle },
|
|
710
|
+
...ogDescription && { description: ogDescription },
|
|
711
|
+
...options?.siteName && { siteName: options.siteName },
|
|
712
|
+
...image && { images: [image] }
|
|
713
|
+
},
|
|
714
|
+
twitter: {
|
|
715
|
+
card: image ? "summary_large_image" : "summary",
|
|
716
|
+
...ogTitle && { title: ogTitle },
|
|
717
|
+
...ogDescription && { description: ogDescription },
|
|
718
|
+
...image && { images: [image.url] }
|
|
719
|
+
}
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
function resolveMetaImage(ref) {
|
|
723
|
+
const image = resolveRelation(ref);
|
|
724
|
+
if (!image) return null;
|
|
725
|
+
const sized = image.sizes?.["1536"];
|
|
726
|
+
const url = sized?.url || image.url;
|
|
727
|
+
if (!url) return null;
|
|
728
|
+
const width = sized?.url ? sized.width : image.width;
|
|
729
|
+
const height = sized?.url ? sized.height : image.height;
|
|
730
|
+
return {
|
|
731
|
+
url,
|
|
732
|
+
...width && { width },
|
|
733
|
+
...height && { height },
|
|
734
|
+
...image.alt && { alt: image.alt }
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// src/core/collection/query-builder.ts
|
|
739
|
+
var ReadOnlyCollectionQueryBuilder = class {
|
|
740
|
+
constructor(api, collection) {
|
|
741
|
+
this.api = api;
|
|
742
|
+
this.collection = collection;
|
|
743
|
+
}
|
|
744
|
+
async find(options) {
|
|
745
|
+
return this.api.requestFind(
|
|
746
|
+
`/api/${String(this.collection)}`,
|
|
747
|
+
options
|
|
748
|
+
);
|
|
749
|
+
}
|
|
750
|
+
async findById(id, options) {
|
|
751
|
+
return this.api.requestFindById(
|
|
752
|
+
`/api/${String(this.collection)}/${String(id)}`,
|
|
753
|
+
options
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
async count(options) {
|
|
757
|
+
return this.api.requestCount(
|
|
758
|
+
`/api/${String(this.collection)}/count`,
|
|
759
|
+
options
|
|
760
|
+
);
|
|
761
|
+
}
|
|
762
|
+
async findMetadata(options, metadataOptions) {
|
|
763
|
+
const { docs } = await this.find({ ...options, limit: 1, depth: 1 });
|
|
764
|
+
const doc = docs[0];
|
|
765
|
+
if (!doc) return null;
|
|
766
|
+
return generateMetadata(
|
|
767
|
+
extractSeo(doc),
|
|
768
|
+
metadataOptions
|
|
769
|
+
);
|
|
770
|
+
}
|
|
771
|
+
async findMetadataById(id, metadataOptions) {
|
|
772
|
+
const doc = await this.findById(id, { depth: 1 });
|
|
773
|
+
return generateMetadata(
|
|
774
|
+
extractSeo(doc),
|
|
775
|
+
metadataOptions
|
|
776
|
+
);
|
|
777
|
+
}
|
|
778
|
+
};
|
|
779
|
+
|
|
780
|
+
// src/core/collection/collection-client.ts
|
|
781
|
+
var ReadOnlyCollectionClient = class extends HttpClient {
|
|
782
|
+
from(collection) {
|
|
783
|
+
return new ReadOnlyCollectionQueryBuilder(this, collection);
|
|
784
|
+
}
|
|
785
|
+
async requestFind(endpoint, options) {
|
|
786
|
+
const url = this.buildUrl(endpoint, options);
|
|
787
|
+
const response = await this.fetchWithTracking(url, {
|
|
788
|
+
...this.defaultOptions,
|
|
789
|
+
method: "GET"
|
|
790
|
+
});
|
|
791
|
+
return this.parseFindResponse(response);
|
|
792
|
+
}
|
|
793
|
+
async requestFindById(endpoint, options) {
|
|
794
|
+
const url = this.buildUrl(endpoint, options);
|
|
795
|
+
const response = await this.fetchWithTracking(url, {
|
|
796
|
+
...this.defaultOptions,
|
|
797
|
+
method: "GET"
|
|
798
|
+
});
|
|
799
|
+
return this.parseDocumentResponse(response);
|
|
800
|
+
}
|
|
801
|
+
async requestCount(endpoint, options) {
|
|
802
|
+
const url = this.buildUrl(endpoint, options);
|
|
803
|
+
const response = await this.fetchWithTracking(url, {
|
|
804
|
+
...this.defaultOptions,
|
|
805
|
+
method: "GET"
|
|
806
|
+
});
|
|
807
|
+
return this.parseDocumentResponse(response);
|
|
808
|
+
}
|
|
809
|
+
};
|
|
810
|
+
|
|
811
|
+
// src/core/api/parse-response.ts
|
|
812
|
+
async function parseApiResponse(response, endpoint) {
|
|
813
|
+
let data;
|
|
814
|
+
try {
|
|
815
|
+
data = await response.json();
|
|
816
|
+
} catch {
|
|
817
|
+
throw createApiError(
|
|
818
|
+
`Invalid JSON response from ${endpoint}`,
|
|
819
|
+
response.status,
|
|
820
|
+
void 0,
|
|
821
|
+
"Server returned an invalid response.",
|
|
822
|
+
"Check if the API endpoint is available."
|
|
823
|
+
);
|
|
824
|
+
}
|
|
825
|
+
if (data.error) {
|
|
826
|
+
const errorMessage = typeof data.error === "string" ? data.error : "Unknown API error";
|
|
827
|
+
const reason = typeof data.reason === "string" ? data.reason : void 0;
|
|
828
|
+
const requestId = response.headers.get("x-request-id") ?? void 0;
|
|
829
|
+
const retryAfterRaw = response.headers.get("Retry-After");
|
|
830
|
+
const retryAfter = retryAfterRaw ? parseInt(retryAfterRaw, 10) || void 0 : void 0;
|
|
831
|
+
if (reason === "validation_failed") {
|
|
832
|
+
throw attachRequestId(createValidationError(errorMessage, data, errorMessage), requestId);
|
|
833
|
+
}
|
|
834
|
+
if (reason === "token_expired" || reason === "token_invalid" || reason === "key_invalid" || reason === "key_revoked") {
|
|
835
|
+
throw attachRequestId(createAuthError(errorMessage, data, errorMessage), requestId);
|
|
836
|
+
}
|
|
837
|
+
if (reason === "forbidden") {
|
|
838
|
+
throw attachRequestId(createPermissionError(errorMessage, data, errorMessage), requestId);
|
|
839
|
+
}
|
|
840
|
+
if (reason === "rate_limit_exceeded") {
|
|
841
|
+
throw attachRequestId(createRateLimitError(errorMessage, retryAfter, data, errorMessage), requestId);
|
|
842
|
+
}
|
|
843
|
+
if (reason === "not_found") {
|
|
844
|
+
throw attachRequestId(createNotFoundError(errorMessage, data, errorMessage), requestId);
|
|
845
|
+
}
|
|
846
|
+
if (reason === "conflict") {
|
|
847
|
+
throw attachRequestId(createConflictError(errorMessage, data, errorMessage), requestId);
|
|
848
|
+
}
|
|
849
|
+
throw attachRequestId(
|
|
850
|
+
createApiError(errorMessage, response.status, data, errorMessage, "An error occurred while processing the request."),
|
|
851
|
+
requestId
|
|
852
|
+
);
|
|
853
|
+
}
|
|
854
|
+
return data;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// src/core/community/community-client.ts
|
|
858
|
+
var CommunityClient = class {
|
|
859
|
+
constructor(options) {
|
|
860
|
+
this.publishableKey = requirePublishableKeyForSecret(
|
|
861
|
+
"CommunityClient",
|
|
862
|
+
options.publishableKey,
|
|
863
|
+
options.secretKey
|
|
864
|
+
);
|
|
865
|
+
this.secretKey = options.secretKey;
|
|
866
|
+
this.apiUrl = options.apiUrl;
|
|
867
|
+
this.customerToken = options.customerToken;
|
|
868
|
+
this.onUnauthorized = options.onUnauthorized;
|
|
869
|
+
this.onRequestId = options.onRequestId;
|
|
870
|
+
}
|
|
871
|
+
buildQuery(params) {
|
|
872
|
+
if (!params) return "";
|
|
873
|
+
const entries = Object.entries(params).filter((e) => e[1] !== void 0).map(([k, v]) => [k, String(v)]);
|
|
874
|
+
return entries.length ? `?${new URLSearchParams(entries).toString()}` : "";
|
|
875
|
+
}
|
|
876
|
+
async execute(endpoint, method, body) {
|
|
877
|
+
const token = typeof this.customerToken === "function" ? this.customerToken() : this.customerToken;
|
|
878
|
+
try {
|
|
879
|
+
const response = await httpFetch(endpoint, {
|
|
880
|
+
method,
|
|
881
|
+
apiUrl: this.apiUrl,
|
|
882
|
+
publishableKey: this.publishableKey,
|
|
883
|
+
secretKey: this.secretKey,
|
|
884
|
+
customerToken: token ?? void 0,
|
|
885
|
+
...token && this.onUnauthorized && { onUnauthorized: this.onUnauthorized },
|
|
886
|
+
...body !== void 0 && { body: JSON.stringify(body) }
|
|
887
|
+
});
|
|
888
|
+
this.onRequestId?.(response.headers.get("x-request-id") ?? null);
|
|
889
|
+
return parseApiResponse(response, endpoint);
|
|
890
|
+
} catch (err) {
|
|
891
|
+
const id = err instanceof SDKError ? err.requestId ?? null : null;
|
|
892
|
+
this.onRequestId?.(id);
|
|
893
|
+
throw err;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
createPost(params) {
|
|
897
|
+
return this.execute("/api/posts", "POST", params);
|
|
898
|
+
}
|
|
899
|
+
getMyPosts(params) {
|
|
900
|
+
return this.execute(
|
|
901
|
+
`/api/posts/my${this.buildQuery(params)}`,
|
|
902
|
+
"GET"
|
|
903
|
+
);
|
|
904
|
+
}
|
|
905
|
+
getTrending(params) {
|
|
906
|
+
return this.execute(
|
|
907
|
+
`/api/posts/trending${this.buildQuery(params)}`,
|
|
908
|
+
"GET"
|
|
909
|
+
);
|
|
910
|
+
}
|
|
911
|
+
incrementView(params) {
|
|
912
|
+
return this.execute(
|
|
913
|
+
`/api/posts/${params.postId}/view`,
|
|
914
|
+
"POST"
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
reportPost(params) {
|
|
918
|
+
const { postId, ...body } = params;
|
|
919
|
+
return this.execute(
|
|
920
|
+
`/api/posts/${postId}/report`,
|
|
921
|
+
"POST",
|
|
922
|
+
body
|
|
923
|
+
);
|
|
924
|
+
}
|
|
925
|
+
// Comments
|
|
926
|
+
createComment(params) {
|
|
927
|
+
const { postId, parentId, body: commentBody } = params;
|
|
928
|
+
const body = { post: postId, body: commentBody };
|
|
929
|
+
if (parentId !== void 0) {
|
|
930
|
+
body.parent = parentId;
|
|
931
|
+
}
|
|
932
|
+
return this.execute("/api/comments", "POST", body);
|
|
933
|
+
}
|
|
934
|
+
listComments(params) {
|
|
935
|
+
const { postId, page, limit, rootComment } = params;
|
|
936
|
+
const urlParams = new URLSearchParams();
|
|
937
|
+
urlParams.set("where[post][equals]", postId);
|
|
938
|
+
urlParams.set("sort", "-createdAt");
|
|
939
|
+
if (limit !== void 0) urlParams.set("limit", String(limit));
|
|
940
|
+
if (page !== void 0) urlParams.set("page", String(page));
|
|
941
|
+
if (rootComment !== void 0) urlParams.set("where[rootComment][equals]", rootComment);
|
|
942
|
+
return this.execute(
|
|
943
|
+
`/api/comments?${urlParams.toString()}`,
|
|
944
|
+
"GET"
|
|
945
|
+
);
|
|
946
|
+
}
|
|
947
|
+
updateComment(params) {
|
|
948
|
+
const { commentId, body } = params;
|
|
949
|
+
return this.execute(
|
|
950
|
+
`/api/comments/${commentId}`,
|
|
951
|
+
"PATCH",
|
|
952
|
+
{ body }
|
|
953
|
+
);
|
|
954
|
+
}
|
|
955
|
+
deleteComment(params) {
|
|
956
|
+
return this.execute(
|
|
957
|
+
`/api/comments/${params.commentId}`,
|
|
958
|
+
"DELETE"
|
|
959
|
+
);
|
|
960
|
+
}
|
|
961
|
+
reportComment(params) {
|
|
962
|
+
const { commentId, ...body } = params;
|
|
963
|
+
return this.execute(
|
|
964
|
+
`/api/comments/${commentId}/report`,
|
|
965
|
+
"POST",
|
|
966
|
+
body
|
|
967
|
+
);
|
|
968
|
+
}
|
|
969
|
+
// Reactions
|
|
970
|
+
addReaction(params) {
|
|
971
|
+
const { postId, type } = params;
|
|
972
|
+
return this.execute("/api/reactions", "POST", {
|
|
973
|
+
post: postId,
|
|
974
|
+
type
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
removeReaction(params) {
|
|
978
|
+
const { postId, type } = params;
|
|
979
|
+
return this.execute(
|
|
980
|
+
`/api/posts/${postId}/react?type=${encodeURIComponent(type)}`,
|
|
981
|
+
"DELETE"
|
|
982
|
+
);
|
|
983
|
+
}
|
|
984
|
+
addCommentReaction(params) {
|
|
985
|
+
const { commentId, type } = params;
|
|
986
|
+
return this.execute("/api/reactions", "POST", {
|
|
987
|
+
comment: commentId,
|
|
988
|
+
type
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
removeCommentReaction(params) {
|
|
992
|
+
const { commentId, type } = params;
|
|
993
|
+
return this.execute(
|
|
994
|
+
`/api/comments/${commentId}/react?type=${encodeURIComponent(type)}`,
|
|
995
|
+
"DELETE"
|
|
996
|
+
);
|
|
997
|
+
}
|
|
998
|
+
getReactionSummary(params) {
|
|
999
|
+
return this.execute(
|
|
1000
|
+
`/api/posts/${params.postId}/reactions`,
|
|
1001
|
+
"GET"
|
|
1002
|
+
);
|
|
1003
|
+
}
|
|
1004
|
+
getReactionTypes() {
|
|
1005
|
+
return this.execute(
|
|
1006
|
+
"/api/reaction-types?limit=100",
|
|
1007
|
+
"GET"
|
|
1008
|
+
);
|
|
1009
|
+
}
|
|
1010
|
+
// Bookmarks
|
|
1011
|
+
addBookmark(params) {
|
|
1012
|
+
return this.execute("/api/bookmarks", "POST", {
|
|
1013
|
+
post: params.postId
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
removeBookmark(params) {
|
|
1017
|
+
return this.execute(
|
|
1018
|
+
`/api/posts/${params.postId}/bookmark`,
|
|
1019
|
+
"DELETE"
|
|
1020
|
+
);
|
|
1021
|
+
}
|
|
1022
|
+
getMyBookmarks(params) {
|
|
1023
|
+
return this.execute(
|
|
1024
|
+
`/api/bookmarks/my${this.buildQuery(params)}`,
|
|
1025
|
+
"GET"
|
|
1026
|
+
);
|
|
1027
|
+
}
|
|
1028
|
+
};
|
|
1029
|
+
|
|
1030
|
+
// src/core/customer/customer-auth.ts
|
|
1031
|
+
var DEFAULT_TIMEOUT2 = 15e3;
|
|
1032
|
+
function safeGetItem(key) {
|
|
1033
|
+
try {
|
|
1034
|
+
return localStorage.getItem(key);
|
|
1035
|
+
} catch {
|
|
1036
|
+
return null;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
var CustomerAuth = class {
|
|
1040
|
+
constructor(publishableKey, options, apiUrl) {
|
|
1041
|
+
this.refreshPromise = null;
|
|
1042
|
+
this.publishableKey = publishableKey;
|
|
1043
|
+
this.baseUrl = resolveApiUrl(apiUrl);
|
|
1044
|
+
const persist = options?.persist ?? true;
|
|
1045
|
+
if (persist) {
|
|
1046
|
+
const key = typeof persist === "string" ? persist : "customer-token";
|
|
1047
|
+
const isBrowser = typeof window !== "undefined";
|
|
1048
|
+
this.token = isBrowser ? safeGetItem(key) : null;
|
|
1049
|
+
this.onTokenChange = isBrowser ? (token) => {
|
|
1050
|
+
try {
|
|
1051
|
+
if (token) localStorage.setItem(key, token);
|
|
1052
|
+
else localStorage.removeItem(key);
|
|
1053
|
+
} catch {
|
|
1054
|
+
}
|
|
1055
|
+
} : void 0;
|
|
1056
|
+
} else {
|
|
1057
|
+
this.token = options?.token ?? null;
|
|
1058
|
+
this.onTokenChange = options?.onTokenChange;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
/**
|
|
1062
|
+
* Register a new customer account
|
|
1063
|
+
*/
|
|
1064
|
+
async register(data) {
|
|
1065
|
+
return this.requestJson("/api/customers/register", {
|
|
1066
|
+
method: "POST",
|
|
1067
|
+
body: JSON.stringify(data)
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Login with email and password. Stores the token internally.
|
|
1072
|
+
*/
|
|
1073
|
+
async login(data) {
|
|
1074
|
+
const result = await this.requestJson(
|
|
1075
|
+
"/api/customers/login",
|
|
1076
|
+
{
|
|
1077
|
+
method: "POST",
|
|
1078
|
+
body: JSON.stringify(data)
|
|
1079
|
+
}
|
|
1080
|
+
);
|
|
1081
|
+
this.setToken(result.token);
|
|
1082
|
+
return result;
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* Refresh the current token. Requires a valid (non-expired) token.
|
|
1086
|
+
*/
|
|
1087
|
+
async refreshToken() {
|
|
1088
|
+
if (!this.token) throw new ApiError("Not authenticated", 401);
|
|
1089
|
+
if (this.refreshPromise) return this.refreshPromise;
|
|
1090
|
+
this.refreshPromise = this._doRefreshToken();
|
|
1091
|
+
try {
|
|
1092
|
+
return await this.refreshPromise;
|
|
1093
|
+
} finally {
|
|
1094
|
+
this.refreshPromise = null;
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
async _doRefreshToken() {
|
|
1098
|
+
const result = await this.requestJson(
|
|
1099
|
+
"/api/customers/refresh",
|
|
1100
|
+
{
|
|
1101
|
+
method: "POST",
|
|
1102
|
+
headers: { Authorization: `Bearer ${this.token}` }
|
|
1103
|
+
}
|
|
1104
|
+
);
|
|
1105
|
+
this.setToken(result.token);
|
|
1106
|
+
return result;
|
|
1107
|
+
}
|
|
1108
|
+
/**
|
|
1109
|
+
* Clear the stored token
|
|
1110
|
+
*/
|
|
1111
|
+
logout() {
|
|
1112
|
+
this.setToken(null);
|
|
1113
|
+
}
|
|
1114
|
+
/**
|
|
1115
|
+
* Get the current authenticated customer's profile
|
|
1116
|
+
*/
|
|
1117
|
+
async me() {
|
|
1118
|
+
if (!this.token) return null;
|
|
1119
|
+
try {
|
|
1120
|
+
const data = await this.requestJson(
|
|
1121
|
+
"/api/customers/me",
|
|
1122
|
+
{
|
|
1123
|
+
method: "GET",
|
|
1124
|
+
headers: { Authorization: `Bearer ${this.token}` }
|
|
1125
|
+
}
|
|
1126
|
+
);
|
|
1127
|
+
return data.customer ?? null;
|
|
1128
|
+
} catch (error) {
|
|
1129
|
+
if (error instanceof ApiError && error.status === 401) {
|
|
1130
|
+
this.setToken(null);
|
|
1131
|
+
return null;
|
|
1132
|
+
}
|
|
1133
|
+
throw error;
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
/**
|
|
1137
|
+
* Request a password reset email
|
|
1138
|
+
*/
|
|
1139
|
+
async forgotPassword(email) {
|
|
1140
|
+
await this.requestJson("/api/customers/forgot-password", {
|
|
1141
|
+
method: "POST",
|
|
1142
|
+
body: JSON.stringify({ email })
|
|
1143
|
+
});
|
|
1144
|
+
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Reset password using a token from the reset email
|
|
1147
|
+
*/
|
|
1148
|
+
async resetPassword(token, password) {
|
|
1149
|
+
await this.requestJson("/api/customers/reset-password", {
|
|
1150
|
+
method: "POST",
|
|
1151
|
+
body: JSON.stringify({ token, password })
|
|
1152
|
+
});
|
|
1153
|
+
}
|
|
1154
|
+
/**
|
|
1155
|
+
* Update the authenticated customer's profile (name, phone, marketingConsent)
|
|
1156
|
+
*/
|
|
1157
|
+
async updateProfile(data) {
|
|
1158
|
+
if (!this.token) throw new ApiError("Not authenticated", 401);
|
|
1159
|
+
const result = await this.requestJson(
|
|
1160
|
+
"/api/customers/me",
|
|
1161
|
+
{
|
|
1162
|
+
method: "PATCH",
|
|
1163
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
1164
|
+
body: JSON.stringify(data)
|
|
1165
|
+
}
|
|
1166
|
+
);
|
|
1167
|
+
return result.customer;
|
|
1168
|
+
}
|
|
1169
|
+
/**
|
|
1170
|
+
* Change the password of the currently authenticated customer
|
|
1171
|
+
*/
|
|
1172
|
+
async changePassword(currentPassword, newPassword) {
|
|
1173
|
+
if (!this.token) throw new ApiError("Not authenticated", 401);
|
|
1174
|
+
await this.requestJson("/api/customers/change-password", {
|
|
1175
|
+
method: "POST",
|
|
1176
|
+
headers: { Authorization: `Bearer ${this.token}` },
|
|
1177
|
+
body: JSON.stringify({ currentPassword, newPassword })
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
/**
|
|
1181
|
+
* Get the authenticated customer's orders with pagination and optional status filter
|
|
1182
|
+
*/
|
|
1183
|
+
async getMyOrders(options) {
|
|
1184
|
+
if (!this.token) throw new ApiError("Not authenticated", 401);
|
|
1185
|
+
const params = new URLSearchParams();
|
|
1186
|
+
if (options?.page) params.set("page", String(options.page));
|
|
1187
|
+
if (options?.limit) params.set("limit", String(options.limit));
|
|
1188
|
+
if (options?.status) params.set("status", options.status);
|
|
1189
|
+
const qs = params.toString();
|
|
1190
|
+
return this.requestJson(`/api/customers/me/orders${qs ? `?${qs}` : ""}`, {
|
|
1191
|
+
method: "GET",
|
|
1192
|
+
headers: { Authorization: `Bearer ${this.token}` }
|
|
1193
|
+
});
|
|
1194
|
+
}
|
|
1195
|
+
/**
|
|
1196
|
+
* Get the current token (or null if not authenticated)
|
|
1197
|
+
*/
|
|
1198
|
+
getToken() {
|
|
1199
|
+
return this.token;
|
|
1200
|
+
}
|
|
1201
|
+
/**
|
|
1202
|
+
* Set the token manually (e.g. from SSR)
|
|
1203
|
+
*/
|
|
1204
|
+
setToken(token) {
|
|
1205
|
+
this.token = token;
|
|
1206
|
+
this.onTokenChange?.(token);
|
|
1207
|
+
}
|
|
1208
|
+
/**
|
|
1209
|
+
* Check if the customer is currently authenticated
|
|
1210
|
+
*/
|
|
1211
|
+
isAuthenticated() {
|
|
1212
|
+
return this.token !== null;
|
|
1213
|
+
}
|
|
1214
|
+
/**
|
|
1215
|
+
* Internal: make a request with timeout and error handling.
|
|
1216
|
+
* Auth endpoints don't retry — failures are final.
|
|
1217
|
+
*/
|
|
1218
|
+
async requestJson(path, init) {
|
|
1219
|
+
const headers = new Headers(init.headers);
|
|
1220
|
+
headers.set("X-Publishable-Key", this.publishableKey);
|
|
1221
|
+
if (!headers.has("Content-Type") && init.body) {
|
|
1222
|
+
headers.set("Content-Type", "application/json");
|
|
1223
|
+
}
|
|
1224
|
+
const controller = new AbortController();
|
|
1225
|
+
const timeoutId = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT2);
|
|
1226
|
+
let res;
|
|
1227
|
+
try {
|
|
1228
|
+
res = await fetch(`${this.baseUrl}${path}`, {
|
|
1229
|
+
...init,
|
|
1230
|
+
headers,
|
|
1231
|
+
signal: controller.signal
|
|
1232
|
+
});
|
|
1233
|
+
} catch (error) {
|
|
1234
|
+
clearTimeout(timeoutId);
|
|
1235
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
1236
|
+
throw new TimeoutError(`Request timed out after ${DEFAULT_TIMEOUT2}ms`, {
|
|
1237
|
+
url: path,
|
|
1238
|
+
timeout: DEFAULT_TIMEOUT2
|
|
1239
|
+
});
|
|
1240
|
+
}
|
|
1241
|
+
throw new NetworkError(
|
|
1242
|
+
error instanceof Error ? error.message : "Network request failed",
|
|
1243
|
+
void 0,
|
|
1244
|
+
{ url: path },
|
|
1245
|
+
"Network connection failed.",
|
|
1246
|
+
"Please check your internet connection and try again."
|
|
1247
|
+
);
|
|
1248
|
+
}
|
|
1249
|
+
clearTimeout(timeoutId);
|
|
1250
|
+
if (!res.ok) {
|
|
1251
|
+
const body = await res.json().catch(() => ({}));
|
|
1252
|
+
throw new ApiError(
|
|
1253
|
+
body.error || `HTTP ${res.status}`,
|
|
1254
|
+
res.status,
|
|
1255
|
+
body.details,
|
|
1256
|
+
body.error
|
|
1257
|
+
);
|
|
1258
|
+
}
|
|
1259
|
+
try {
|
|
1260
|
+
return await res.json();
|
|
1261
|
+
} catch {
|
|
1262
|
+
throw new ApiError(
|
|
1263
|
+
"Invalid JSON response from server",
|
|
1264
|
+
res.status,
|
|
1265
|
+
void 0,
|
|
1266
|
+
"INVALID_RESPONSE"
|
|
1267
|
+
);
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
};
|
|
1271
|
+
|
|
1272
|
+
// src/core/customer/customer-namespace.ts
|
|
1273
|
+
var CustomerNamespace = class {
|
|
1274
|
+
constructor(publishableKey, options, apiUrl) {
|
|
1275
|
+
this.auth = new CustomerAuth(publishableKey, options, apiUrl);
|
|
1276
|
+
}
|
|
1277
|
+
};
|
|
1278
|
+
|
|
1279
|
+
// src/core/api/cart-api.ts
|
|
1280
|
+
var CartApi = class {
|
|
1281
|
+
constructor(options) {
|
|
1282
|
+
if (!options.secretKey && !options.customerToken) {
|
|
1283
|
+
throw createConfigError(
|
|
1284
|
+
"Either secretKey or customerToken is required for CartApi."
|
|
1285
|
+
);
|
|
1286
|
+
}
|
|
1287
|
+
this.publishableKey = requirePublishableKeyForSecret(
|
|
1288
|
+
"CartApi",
|
|
1289
|
+
options.publishableKey,
|
|
1290
|
+
options.secretKey
|
|
1291
|
+
);
|
|
1292
|
+
this.secretKey = options.secretKey;
|
|
1293
|
+
this.apiUrl = options.apiUrl;
|
|
1294
|
+
this.customerToken = options.customerToken;
|
|
1295
|
+
this.onUnauthorized = options.onUnauthorized;
|
|
1296
|
+
this.onRequestId = options.onRequestId;
|
|
1297
|
+
}
|
|
1298
|
+
async execute(endpoint, method, body) {
|
|
1299
|
+
const token = typeof this.customerToken === "function" ? this.customerToken() : this.customerToken;
|
|
1300
|
+
try {
|
|
1301
|
+
const response = await httpFetch(endpoint, {
|
|
1302
|
+
method,
|
|
1303
|
+
apiUrl: this.apiUrl,
|
|
1304
|
+
publishableKey: this.publishableKey,
|
|
1305
|
+
secretKey: this.secretKey,
|
|
1306
|
+
customerToken: token ?? void 0,
|
|
1307
|
+
...token && this.onUnauthorized && { onUnauthorized: this.onUnauthorized },
|
|
1308
|
+
...body !== void 0 && { body: JSON.stringify(body) }
|
|
1309
|
+
});
|
|
1310
|
+
this.onRequestId?.(response.headers.get("x-request-id") ?? null);
|
|
1311
|
+
return parseApiResponse(response, endpoint);
|
|
1312
|
+
} catch (err) {
|
|
1313
|
+
const id = err instanceof SDKError ? err.requestId ?? null : null;
|
|
1314
|
+
this.onRequestId?.(id);
|
|
1315
|
+
throw err;
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
getCart(cartId) {
|
|
1319
|
+
return this.execute(`/api/carts/${cartId}`, "GET");
|
|
1320
|
+
}
|
|
1321
|
+
addItem(params) {
|
|
1322
|
+
return this.execute("/api/carts/add-item", "POST", params);
|
|
1323
|
+
}
|
|
1324
|
+
updateItem(params) {
|
|
1325
|
+
return this.execute("/api/carts/update-item", "POST", params);
|
|
1326
|
+
}
|
|
1327
|
+
removeItem(params) {
|
|
1328
|
+
return this.execute(
|
|
1329
|
+
"/api/carts/remove-item",
|
|
1330
|
+
"POST",
|
|
1331
|
+
params
|
|
1332
|
+
);
|
|
1333
|
+
}
|
|
1334
|
+
applyDiscount(params) {
|
|
1335
|
+
return this.execute("/api/carts/apply-discount", "POST", params);
|
|
1336
|
+
}
|
|
1337
|
+
removeDiscount(params) {
|
|
1338
|
+
return this.execute("/api/carts/remove-discount", "POST", params);
|
|
1339
|
+
}
|
|
1340
|
+
clearCart(params) {
|
|
1341
|
+
return this.execute(
|
|
1342
|
+
"/api/carts/clear",
|
|
1343
|
+
"POST",
|
|
1344
|
+
params
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
};
|
|
1348
|
+
|
|
1349
|
+
// src/core/commerce/commerce-client.ts
|
|
1350
|
+
var CommerceClient = class {
|
|
1351
|
+
constructor(options) {
|
|
1352
|
+
const cartApi = new CartApi({
|
|
1353
|
+
publishableKey: options.publishableKey,
|
|
1354
|
+
apiUrl: options.apiUrl,
|
|
1355
|
+
customerToken: options.customerToken,
|
|
1356
|
+
onUnauthorized: options.onUnauthorized,
|
|
1357
|
+
onRequestId: options.onRequestId
|
|
1358
|
+
});
|
|
1359
|
+
const execute = async (endpoint, body) => {
|
|
1360
|
+
const token = options.customerToken();
|
|
1361
|
+
try {
|
|
1362
|
+
const response = await httpFetch(endpoint, {
|
|
1363
|
+
method: "POST",
|
|
1364
|
+
apiUrl: options.apiUrl,
|
|
1365
|
+
publishableKey: options.publishableKey,
|
|
1366
|
+
customerToken: token ?? void 0,
|
|
1367
|
+
...token && options.onUnauthorized && { onUnauthorized: options.onUnauthorized },
|
|
1368
|
+
body: JSON.stringify(body)
|
|
1369
|
+
});
|
|
1370
|
+
options.onRequestId?.(response.headers.get("x-request-id") ?? null);
|
|
1371
|
+
return parseApiResponse(response, endpoint);
|
|
1372
|
+
} catch (err) {
|
|
1373
|
+
const id = err instanceof SDKError ? err.requestId ?? null : null;
|
|
1374
|
+
options.onRequestId?.(id);
|
|
1375
|
+
throw err;
|
|
1376
|
+
}
|
|
1377
|
+
};
|
|
1378
|
+
this.product = {
|
|
1379
|
+
stockCheck: (params) => execute("/api/products/stock-check", params),
|
|
1380
|
+
listingGroups: (params) => execute("/api/products/listing-groups", params),
|
|
1381
|
+
detail: async (params) => {
|
|
1382
|
+
try {
|
|
1383
|
+
return await execute("/api/products/detail", params);
|
|
1384
|
+
} catch (err) {
|
|
1385
|
+
if (err instanceof NotFoundError) return null;
|
|
1386
|
+
throw err;
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
};
|
|
1390
|
+
this.cart = {
|
|
1391
|
+
get: cartApi.getCart.bind(cartApi),
|
|
1392
|
+
addItem: cartApi.addItem.bind(cartApi),
|
|
1393
|
+
updateItem: cartApi.updateItem.bind(cartApi),
|
|
1394
|
+
removeItem: cartApi.removeItem.bind(cartApi),
|
|
1395
|
+
applyDiscount: cartApi.applyDiscount.bind(cartApi),
|
|
1396
|
+
removeDiscount: cartApi.removeDiscount.bind(cartApi),
|
|
1397
|
+
clear: cartApi.clearCart.bind(cartApi)
|
|
1398
|
+
};
|
|
1399
|
+
this.orders = {
|
|
1400
|
+
checkout: (params) => execute("/api/orders/checkout", params),
|
|
1401
|
+
listMine: (params) => options.customerAuth.getMyOrders(params)
|
|
1402
|
+
};
|
|
1403
|
+
this.discounts = {
|
|
1404
|
+
validate: (params) => execute("/api/discounts/validate", params)
|
|
1405
|
+
};
|
|
1406
|
+
this.shipping = {
|
|
1407
|
+
calculate: (params) => execute("/api/shipping-policies/calculate", params)
|
|
1408
|
+
};
|
|
1409
|
+
}
|
|
1410
|
+
};
|
|
1411
|
+
|
|
1412
|
+
// src/core/client/client.ts
|
|
1413
|
+
var Client = class {
|
|
1414
|
+
constructor(options) {
|
|
1415
|
+
this.lastRequestId = null;
|
|
1416
|
+
const publishableKey = options.publishableKey;
|
|
1417
|
+
if (!publishableKey) {
|
|
1418
|
+
throw createConfigError("publishableKey is required.");
|
|
1419
|
+
}
|
|
1420
|
+
this.config = { ...options, publishableKey };
|
|
1421
|
+
const metadata = {
|
|
1422
|
+
timestamp: Date.now(),
|
|
1423
|
+
userAgent: typeof window !== "undefined" ? window.navigator?.userAgent : "Node.js"
|
|
1424
|
+
};
|
|
1425
|
+
this.state = { metadata };
|
|
1426
|
+
this.customer = new CustomerNamespace(
|
|
1427
|
+
this.config.publishableKey,
|
|
1428
|
+
options.customer,
|
|
1429
|
+
this.config.apiUrl
|
|
1430
|
+
);
|
|
1431
|
+
const onUnauthorized = async () => {
|
|
1432
|
+
try {
|
|
1433
|
+
const result = await this.customer.auth.refreshToken();
|
|
1434
|
+
return result.token ?? null;
|
|
1435
|
+
} catch {
|
|
1436
|
+
return null;
|
|
1437
|
+
}
|
|
1438
|
+
};
|
|
1439
|
+
const onRequestId = (id) => {
|
|
1440
|
+
this.lastRequestId = id;
|
|
1441
|
+
};
|
|
1442
|
+
this.commerce = new CommerceClient({
|
|
1443
|
+
publishableKey: this.config.publishableKey,
|
|
1444
|
+
apiUrl: this.config.apiUrl,
|
|
1445
|
+
customerToken: () => this.customer.auth.getToken(),
|
|
1446
|
+
onUnauthorized,
|
|
1447
|
+
onRequestId,
|
|
1448
|
+
customerAuth: this.customer.auth
|
|
1449
|
+
});
|
|
1450
|
+
this.community = new CommunityClient({
|
|
1451
|
+
publishableKey: this.config.publishableKey,
|
|
1452
|
+
apiUrl: this.config.apiUrl,
|
|
1453
|
+
customerToken: () => this.customer.auth.getToken(),
|
|
1454
|
+
onUnauthorized,
|
|
1455
|
+
onRequestId
|
|
1456
|
+
});
|
|
1457
|
+
this.collections = new ReadOnlyCollectionClient(
|
|
1458
|
+
this.config.publishableKey,
|
|
1459
|
+
void 0,
|
|
1460
|
+
() => this.customer.auth.getToken(),
|
|
1461
|
+
onUnauthorized,
|
|
1462
|
+
onRequestId,
|
|
1463
|
+
this.config.apiUrl
|
|
1464
|
+
);
|
|
1465
|
+
}
|
|
1466
|
+
getState() {
|
|
1467
|
+
return { ...this.state };
|
|
1468
|
+
}
|
|
1469
|
+
getConfig() {
|
|
1470
|
+
return { ...this.config };
|
|
1471
|
+
}
|
|
1472
|
+
};
|
|
1473
|
+
function createClient(options) {
|
|
1474
|
+
return new Client(options);
|
|
1475
|
+
}
|
|
1476
|
+
//# sourceMappingURL=client.cjs.map
|