@cimplify/sdk 0.16.2 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/advanced.d.mts +2 -2
- package/dist/advanced.d.ts +2 -2
- package/dist/advanced.js +90 -2906
- package/dist/advanced.mjs +3 -2886
- package/dist/chunk-FLPI2VVJ.js +72 -0
- package/dist/chunk-G6KNFYJD.mjs +687 -0
- package/dist/chunk-GFKBH7R2.mjs +2759 -0
- package/dist/chunk-J65U5SED.js +714 -0
- package/dist/chunk-LM3CJ4L7.mjs +69 -0
- package/dist/chunk-OITG2SEN.js +692 -0
- package/dist/chunk-OVM5YWM5.mjs +674 -0
- package/dist/chunk-REPTMTQ4.js +2810 -0
- package/dist/chunk-S6TU55KP.js +170 -0
- package/dist/chunk-WDIMISRC.mjs +166 -0
- package/dist/{client-DTTtSSmQ.d.ts → client-BaolRdB4.d.mts} +3 -3
- package/dist/{client-Cb7r8u6t.d.mts → client-C59R79gx.d.ts} +3 -3
- package/dist/index.d.mts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +401 -4412
- package/dist/index.mjs +5 -4317
- package/dist/{payment-Dvd7nRmk.d.mts → payment-C6ULw3K-.d.mts} +1 -1
- package/dist/{payment-Dvd7nRmk.d.ts → payment-C6ULw3K-.d.ts} +1 -1
- package/dist/{price-DrfhwHpw.d.ts → price-Bd6JBV8W.d.ts} +1 -1
- package/dist/{price-BDZNXiLS.d.mts → price-CbR6DCt0.d.mts} +1 -1
- package/dist/react.d.mts +12 -7
- package/dist/react.d.ts +12 -7
- package/dist/react.js +377 -3961
- package/dist/react.mjs +326 -3913
- package/dist/utils.d.mts +2 -2
- package/dist/utils.d.ts +2 -2
- package/dist/utils.js +122 -720
- package/dist/utils.mjs +2 -694
- package/package.json +1 -1
- package/registry/add-on-selector.json +1 -1
- package/registry/checkout-page.json +1 -1
- package/registry/composite-selector.json +1 -1
- package/registry/product-grid.json +1 -1
|
@@ -0,0 +1,687 @@
|
|
|
1
|
+
import { CatalogueQueries, CartOperations, CheckoutService, OrderQueries, LinkService, AuthService, BusinessService, InventoryService, SchedulingService, LiteService, FxService, createElements } from './chunk-GFKBH7R2.mjs';
|
|
2
|
+
import { CimplifyError, ErrorCode, enrichError } from './chunk-OVM5YWM5.mjs';
|
|
3
|
+
|
|
4
|
+
// src/activity.ts
|
|
5
|
+
var ActivityService = class {
|
|
6
|
+
constructor(client) {
|
|
7
|
+
this.client = client;
|
|
8
|
+
}
|
|
9
|
+
trackProductView(productId, options = {}) {
|
|
10
|
+
const event = {
|
|
11
|
+
type: "product_view",
|
|
12
|
+
product_id: productId,
|
|
13
|
+
product_name: options.productName,
|
|
14
|
+
category_id: options.categoryId,
|
|
15
|
+
price: options.price,
|
|
16
|
+
page_path: options.pagePath ?? currentPath()
|
|
17
|
+
};
|
|
18
|
+
this.sendEvents([event]);
|
|
19
|
+
}
|
|
20
|
+
trackCategoryView(categoryId, options = {}) {
|
|
21
|
+
const event = {
|
|
22
|
+
type: "category_view",
|
|
23
|
+
category_id: categoryId,
|
|
24
|
+
category_name: options.categoryName,
|
|
25
|
+
page_path: options.pagePath ?? currentPath()
|
|
26
|
+
};
|
|
27
|
+
this.sendEvents([event]);
|
|
28
|
+
}
|
|
29
|
+
sendEvents(events) {
|
|
30
|
+
try {
|
|
31
|
+
this.client.post("/api/v1/activity/events", { events }).catch(() => {
|
|
32
|
+
});
|
|
33
|
+
} catch {
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
function currentPath() {
|
|
38
|
+
if (typeof window !== "undefined") {
|
|
39
|
+
return window.location.pathname;
|
|
40
|
+
}
|
|
41
|
+
return void 0;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/client.ts
|
|
45
|
+
var ACCESS_TOKEN_STORAGE_KEY = "cimplify_access_token";
|
|
46
|
+
var SESSION_TOKEN_STORAGE_KEY = "cimplify_session_token";
|
|
47
|
+
var ORDER_TOKEN_PREFIX = "cimplify_ot_";
|
|
48
|
+
var SESSION_TOKEN_HEADER = "x-session-token";
|
|
49
|
+
var ORDER_TOKEN_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
50
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
51
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
52
|
+
var DEFAULT_RETRY_DELAY_MS = 1e3;
|
|
53
|
+
function sleep(ms) {
|
|
54
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
55
|
+
}
|
|
56
|
+
function isRetryable(error) {
|
|
57
|
+
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
if (error instanceof CimplifyError) {
|
|
64
|
+
return error.retryable;
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
function toNetworkError(error, isTestMode) {
|
|
69
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
70
|
+
return enrichError(
|
|
71
|
+
new CimplifyError(
|
|
72
|
+
ErrorCode.TIMEOUT,
|
|
73
|
+
"Request timed out. Please check your connection and try again.",
|
|
74
|
+
true
|
|
75
|
+
),
|
|
76
|
+
{ isTestMode }
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
if (error instanceof TypeError && error.message.includes("fetch")) {
|
|
80
|
+
return enrichError(
|
|
81
|
+
new CimplifyError(
|
|
82
|
+
ErrorCode.NETWORK_ERROR,
|
|
83
|
+
"Network error. Please check your internet connection.",
|
|
84
|
+
true
|
|
85
|
+
),
|
|
86
|
+
{ isTestMode }
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
if (error instanceof CimplifyError) {
|
|
90
|
+
return enrichError(error, { isTestMode });
|
|
91
|
+
}
|
|
92
|
+
return enrichError(
|
|
93
|
+
new CimplifyError(
|
|
94
|
+
ErrorCode.UNKNOWN_ERROR,
|
|
95
|
+
error instanceof Error ? error.message : "An unknown error occurred",
|
|
96
|
+
false
|
|
97
|
+
),
|
|
98
|
+
{ isTestMode }
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
function deriveUrls() {
|
|
102
|
+
const hostname = typeof window !== "undefined" ? window.location.hostname : "";
|
|
103
|
+
if (hostname === "localhost" || hostname === "127.0.0.1") {
|
|
104
|
+
const protocol = window.location.protocol;
|
|
105
|
+
return {
|
|
106
|
+
baseUrl: `${protocol}//${hostname}:8082`,
|
|
107
|
+
linkApiUrl: `${protocol}//${hostname}:8080`
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
baseUrl: "https://storefronts.cimplify.io",
|
|
112
|
+
linkApiUrl: "https://api.cimplify.io"
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function normalizeUrl(url) {
|
|
116
|
+
const trimmed = url.trim();
|
|
117
|
+
if (!trimmed) return trimmed;
|
|
118
|
+
return trimmed.replace(/\/+$/, "");
|
|
119
|
+
}
|
|
120
|
+
function getEnvPublicKey() {
|
|
121
|
+
try {
|
|
122
|
+
const env = globalThis.process;
|
|
123
|
+
return env?.env.NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY || void 0;
|
|
124
|
+
} catch {
|
|
125
|
+
return void 0;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
var CimplifyClient = class {
|
|
129
|
+
constructor(config = {}) {
|
|
130
|
+
this.accessToken = null;
|
|
131
|
+
this.sessionToken = null;
|
|
132
|
+
this.context = {};
|
|
133
|
+
this.businessId = null;
|
|
134
|
+
this.businessIdResolvePromise = null;
|
|
135
|
+
this.inflightRequests = /* @__PURE__ */ new Map();
|
|
136
|
+
this.publicKey = config.publicKey || getEnvPublicKey() || "";
|
|
137
|
+
const urls = deriveUrls();
|
|
138
|
+
this.baseUrl = config.baseUrl ? normalizeUrl(config.baseUrl) : urls.baseUrl;
|
|
139
|
+
this.linkApiUrl = config.linkApiUrl ? normalizeUrl(config.linkApiUrl) : urls.linkApiUrl;
|
|
140
|
+
this.credentials = config.credentials || "include";
|
|
141
|
+
this.timeout = config.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
142
|
+
this.maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
|
|
143
|
+
this.retryDelay = config.retryDelay ?? DEFAULT_RETRY_DELAY_MS;
|
|
144
|
+
this.hooks = config.hooks ?? {};
|
|
145
|
+
this.accessToken = this.loadAccessToken();
|
|
146
|
+
this.sessionToken = this.loadSessionToken();
|
|
147
|
+
if (!this.publicKey && !config.suppressPublicKeyWarning) {
|
|
148
|
+
console.warn(
|
|
149
|
+
'[Cimplify] No public key found. Set NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY in your environment, or pass { publicKey: "pk_..." } to createCimplifyClient().'
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
buildUrl(base, path) {
|
|
154
|
+
const url = `${base}${path}`;
|
|
155
|
+
const locationId = this.context.location_id;
|
|
156
|
+
if (!locationId) return url;
|
|
157
|
+
const separator = url.includes("?") ? "&" : "?";
|
|
158
|
+
return `${url}${separator}location_id=${encodeURIComponent(locationId)}`;
|
|
159
|
+
}
|
|
160
|
+
getAccessToken() {
|
|
161
|
+
return this.accessToken;
|
|
162
|
+
}
|
|
163
|
+
getPublicKey() {
|
|
164
|
+
return this.publicKey;
|
|
165
|
+
}
|
|
166
|
+
isTestMode() {
|
|
167
|
+
return this.publicKey.trim().startsWith("pk_test_");
|
|
168
|
+
}
|
|
169
|
+
setAccessToken(token) {
|
|
170
|
+
const previous = this.accessToken;
|
|
171
|
+
this.accessToken = token;
|
|
172
|
+
this.saveAccessToken(token);
|
|
173
|
+
this.hooks.onSessionChange?.({
|
|
174
|
+
previousToken: previous,
|
|
175
|
+
newToken: token,
|
|
176
|
+
source: "manual"
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
clearSession() {
|
|
180
|
+
const previous = this.accessToken;
|
|
181
|
+
this.accessToken = null;
|
|
182
|
+
this.sessionToken = null;
|
|
183
|
+
this.saveAccessToken(null);
|
|
184
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
185
|
+
localStorage.removeItem(SESSION_TOKEN_STORAGE_KEY);
|
|
186
|
+
}
|
|
187
|
+
this.hooks.onSessionChange?.({
|
|
188
|
+
previousToken: previous,
|
|
189
|
+
newToken: null,
|
|
190
|
+
source: "clear"
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
setOrderToken(orderId, token) {
|
|
194
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
195
|
+
try {
|
|
196
|
+
const entry = JSON.stringify({ token, storedAt: Date.now() });
|
|
197
|
+
localStorage.setItem(`${ORDER_TOKEN_PREFIX}${orderId}`, entry);
|
|
198
|
+
} catch {
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
getOrderToken(orderId) {
|
|
203
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
204
|
+
try {
|
|
205
|
+
const raw = localStorage.getItem(`${ORDER_TOKEN_PREFIX}${orderId}`);
|
|
206
|
+
if (!raw) return null;
|
|
207
|
+
const entry = JSON.parse(raw);
|
|
208
|
+
if (Date.now() - entry.storedAt > ORDER_TOKEN_TTL_MS) {
|
|
209
|
+
localStorage.removeItem(`${ORDER_TOKEN_PREFIX}${orderId}`);
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
return entry.token;
|
|
213
|
+
} catch {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
clearOrderTokens() {
|
|
220
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
221
|
+
const keysToRemove = [];
|
|
222
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
223
|
+
const key = localStorage.key(i);
|
|
224
|
+
if (key?.startsWith(ORDER_TOKEN_PREFIX)) {
|
|
225
|
+
keysToRemove.push(key);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
keysToRemove.forEach((k) => localStorage.removeItem(k));
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
setLocationId(locationId) {
|
|
232
|
+
if (locationId) {
|
|
233
|
+
this.context.location_id = locationId;
|
|
234
|
+
} else {
|
|
235
|
+
delete this.context.location_id;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
getLocationId() {
|
|
239
|
+
return this.context.location_id ?? null;
|
|
240
|
+
}
|
|
241
|
+
setBusinessId(businessId) {
|
|
242
|
+
const normalized = businessId.trim();
|
|
243
|
+
if (!normalized) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
this.businessId = normalized;
|
|
247
|
+
}
|
|
248
|
+
getBusinessId() {
|
|
249
|
+
return this.businessId;
|
|
250
|
+
}
|
|
251
|
+
async resolveBusinessId() {
|
|
252
|
+
if (this.businessId) {
|
|
253
|
+
return this.businessId;
|
|
254
|
+
}
|
|
255
|
+
if (this.businessIdResolvePromise) {
|
|
256
|
+
return this.businessIdResolvePromise;
|
|
257
|
+
}
|
|
258
|
+
this.businessIdResolvePromise = (async () => {
|
|
259
|
+
const result = await this.business.getInfo();
|
|
260
|
+
if (!result.ok || !result.value?.id) {
|
|
261
|
+
throw new CimplifyError(
|
|
262
|
+
ErrorCode.NOT_FOUND,
|
|
263
|
+
"Unable to resolve business ID from the current public key.",
|
|
264
|
+
true
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
this.businessId = result.value.id;
|
|
268
|
+
return result.value.id;
|
|
269
|
+
})();
|
|
270
|
+
try {
|
|
271
|
+
return await this.businessIdResolvePromise;
|
|
272
|
+
} finally {
|
|
273
|
+
this.businessIdResolvePromise = null;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
loadAccessToken() {
|
|
277
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
278
|
+
return localStorage.getItem(ACCESS_TOKEN_STORAGE_KEY);
|
|
279
|
+
}
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
saveAccessToken(token) {
|
|
283
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
284
|
+
try {
|
|
285
|
+
if (token) {
|
|
286
|
+
localStorage.setItem(ACCESS_TOKEN_STORAGE_KEY, token);
|
|
287
|
+
} else {
|
|
288
|
+
localStorage.removeItem(ACCESS_TOKEN_STORAGE_KEY);
|
|
289
|
+
}
|
|
290
|
+
} catch {
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
loadSessionToken() {
|
|
295
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
296
|
+
return localStorage.getItem(SESSION_TOKEN_STORAGE_KEY);
|
|
297
|
+
}
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
saveSessionToken(token) {
|
|
301
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
302
|
+
try {
|
|
303
|
+
localStorage.setItem(SESSION_TOKEN_STORAGE_KEY, token);
|
|
304
|
+
} catch {
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
captureSessionToken(response) {
|
|
309
|
+
const token = response.headers.get(SESSION_TOKEN_HEADER);
|
|
310
|
+
if (token && token !== this.sessionToken) {
|
|
311
|
+
this.sessionToken = token;
|
|
312
|
+
this.saveSessionToken(token);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
getHeaders() {
|
|
316
|
+
const headers = {
|
|
317
|
+
"Content-Type": "application/json"
|
|
318
|
+
};
|
|
319
|
+
const trimmedPublicKey = this.publicKey.trim();
|
|
320
|
+
if (trimmedPublicKey) {
|
|
321
|
+
headers["X-API-Key"] = trimmedPublicKey;
|
|
322
|
+
}
|
|
323
|
+
if (this.accessToken) {
|
|
324
|
+
headers["Authorization"] = `Bearer ${this.accessToken}`;
|
|
325
|
+
}
|
|
326
|
+
if (this.sessionToken) {
|
|
327
|
+
headers[SESSION_TOKEN_HEADER] = this.sessionToken;
|
|
328
|
+
}
|
|
329
|
+
return headers;
|
|
330
|
+
}
|
|
331
|
+
async resilientFetch(url, options) {
|
|
332
|
+
const method = options.method || "GET";
|
|
333
|
+
const path = url.replace(this.baseUrl, "").replace(this.linkApiUrl, "");
|
|
334
|
+
const startTime = Date.now();
|
|
335
|
+
let retryCount = 0;
|
|
336
|
+
const context = {
|
|
337
|
+
method,
|
|
338
|
+
path,
|
|
339
|
+
url,
|
|
340
|
+
body: options.body ? JSON.parse(options.body) : void 0,
|
|
341
|
+
startTime
|
|
342
|
+
};
|
|
343
|
+
this.hooks.onRequestStart?.(context);
|
|
344
|
+
let lastError;
|
|
345
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
346
|
+
try {
|
|
347
|
+
const controller = new AbortController();
|
|
348
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
349
|
+
const response = await fetch(url, {
|
|
350
|
+
...options,
|
|
351
|
+
signal: controller.signal
|
|
352
|
+
});
|
|
353
|
+
clearTimeout(timeoutId);
|
|
354
|
+
this.captureSessionToken(response);
|
|
355
|
+
if (response.ok) {
|
|
356
|
+
this.hooks.onRequestSuccess?.({
|
|
357
|
+
...context,
|
|
358
|
+
status: response.status,
|
|
359
|
+
durationMs: Date.now() - startTime
|
|
360
|
+
});
|
|
361
|
+
return response;
|
|
362
|
+
}
|
|
363
|
+
if (response.status === 429 && attempt < this.maxRetries) {
|
|
364
|
+
retryCount++;
|
|
365
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
366
|
+
const delay = retryAfter ? Math.min(parseInt(retryAfter, 10) * 1e3, 3e4) || this.retryDelay * Math.pow(2, attempt) : this.retryDelay * Math.pow(2, attempt);
|
|
367
|
+
this.hooks.onRetry?.({
|
|
368
|
+
...context,
|
|
369
|
+
attempt: retryCount,
|
|
370
|
+
delayMs: delay,
|
|
371
|
+
error: new Error(`Rate limited: ${response.status}`)
|
|
372
|
+
});
|
|
373
|
+
await sleep(delay);
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
if (response.status >= 400 && response.status < 500) {
|
|
377
|
+
this.hooks.onRequestError?.({
|
|
378
|
+
...context,
|
|
379
|
+
error: new CimplifyError(
|
|
380
|
+
`HTTP_${response.status}`,
|
|
381
|
+
`Request failed with status ${response.status}`,
|
|
382
|
+
false
|
|
383
|
+
),
|
|
384
|
+
status: response.status,
|
|
385
|
+
durationMs: Date.now() - startTime,
|
|
386
|
+
retryCount,
|
|
387
|
+
retryable: false
|
|
388
|
+
});
|
|
389
|
+
return response;
|
|
390
|
+
}
|
|
391
|
+
if (response.status >= 500 && attempt < this.maxRetries) {
|
|
392
|
+
retryCount++;
|
|
393
|
+
const delay = this.retryDelay * Math.pow(2, attempt);
|
|
394
|
+
this.hooks.onRetry?.({
|
|
395
|
+
...context,
|
|
396
|
+
attempt: retryCount,
|
|
397
|
+
delayMs: delay,
|
|
398
|
+
error: new Error(`Server error: ${response.status}`)
|
|
399
|
+
});
|
|
400
|
+
await sleep(delay);
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
this.hooks.onRequestError?.({
|
|
404
|
+
...context,
|
|
405
|
+
error: new CimplifyError(
|
|
406
|
+
"SERVER_ERROR",
|
|
407
|
+
`Server error ${response.status} after ${retryCount} retries`,
|
|
408
|
+
false
|
|
409
|
+
),
|
|
410
|
+
status: response.status,
|
|
411
|
+
durationMs: Date.now() - startTime,
|
|
412
|
+
retryCount,
|
|
413
|
+
retryable: false
|
|
414
|
+
});
|
|
415
|
+
return response;
|
|
416
|
+
} catch (error) {
|
|
417
|
+
lastError = error;
|
|
418
|
+
const networkError = toNetworkError(error, this.isTestMode());
|
|
419
|
+
const errorRetryable = isRetryable(error);
|
|
420
|
+
if (!errorRetryable || attempt >= this.maxRetries) {
|
|
421
|
+
this.hooks.onRequestError?.({
|
|
422
|
+
...context,
|
|
423
|
+
error: networkError,
|
|
424
|
+
durationMs: Date.now() - startTime,
|
|
425
|
+
retryCount,
|
|
426
|
+
retryable: errorRetryable
|
|
427
|
+
});
|
|
428
|
+
throw networkError;
|
|
429
|
+
}
|
|
430
|
+
retryCount++;
|
|
431
|
+
const delay = this.retryDelay * Math.pow(2, attempt);
|
|
432
|
+
this.hooks.onRetry?.({
|
|
433
|
+
...context,
|
|
434
|
+
attempt: retryCount,
|
|
435
|
+
delayMs: delay,
|
|
436
|
+
error: networkError
|
|
437
|
+
});
|
|
438
|
+
await sleep(delay);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
const finalError = toNetworkError(lastError, this.isTestMode());
|
|
442
|
+
this.hooks.onRequestError?.({
|
|
443
|
+
...context,
|
|
444
|
+
error: finalError,
|
|
445
|
+
durationMs: Date.now() - startTime,
|
|
446
|
+
retryCount,
|
|
447
|
+
retryable: false
|
|
448
|
+
});
|
|
449
|
+
throw finalError;
|
|
450
|
+
}
|
|
451
|
+
getDedupeKey(type, payload) {
|
|
452
|
+
return `${type}:${JSON.stringify(payload)}`;
|
|
453
|
+
}
|
|
454
|
+
async deduplicatedRequest(key, requestFn) {
|
|
455
|
+
const existing = this.inflightRequests.get(key);
|
|
456
|
+
if (existing) {
|
|
457
|
+
return existing;
|
|
458
|
+
}
|
|
459
|
+
const request = requestFn().finally(() => {
|
|
460
|
+
this.inflightRequests.delete(key);
|
|
461
|
+
});
|
|
462
|
+
this.inflightRequests.set(key, request);
|
|
463
|
+
return request;
|
|
464
|
+
}
|
|
465
|
+
async get(path) {
|
|
466
|
+
const key = this.getDedupeKey("get", path);
|
|
467
|
+
return this.deduplicatedRequest(key, async () => {
|
|
468
|
+
const response = await this.resilientFetch(this.buildUrl(this.baseUrl, path), {
|
|
469
|
+
method: "GET",
|
|
470
|
+
credentials: this.credentials,
|
|
471
|
+
headers: this.getHeaders()
|
|
472
|
+
});
|
|
473
|
+
return this.handleRestResponse(response);
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
async post(path, body) {
|
|
477
|
+
const response = await this.resilientFetch(this.buildUrl(this.baseUrl, path), {
|
|
478
|
+
method: "POST",
|
|
479
|
+
credentials: this.credentials,
|
|
480
|
+
headers: this.getHeaders(),
|
|
481
|
+
body: body ? JSON.stringify(body) : void 0
|
|
482
|
+
});
|
|
483
|
+
return this.handleRestResponse(response);
|
|
484
|
+
}
|
|
485
|
+
async patch(path, body) {
|
|
486
|
+
const response = await this.resilientFetch(this.buildUrl(this.baseUrl, path), {
|
|
487
|
+
method: "PATCH",
|
|
488
|
+
credentials: this.credentials,
|
|
489
|
+
headers: this.getHeaders(),
|
|
490
|
+
body: body ? JSON.stringify(body) : void 0
|
|
491
|
+
});
|
|
492
|
+
return this.handleRestResponse(response);
|
|
493
|
+
}
|
|
494
|
+
async delete(path) {
|
|
495
|
+
const response = await this.resilientFetch(this.buildUrl(this.baseUrl, path), {
|
|
496
|
+
method: "DELETE",
|
|
497
|
+
credentials: this.credentials,
|
|
498
|
+
headers: this.getHeaders()
|
|
499
|
+
});
|
|
500
|
+
return this.handleRestResponse(response);
|
|
501
|
+
}
|
|
502
|
+
async linkGet(path) {
|
|
503
|
+
const key = this.getDedupeKey("linkGet", path);
|
|
504
|
+
return this.deduplicatedRequest(key, async () => {
|
|
505
|
+
const response = await this.resilientFetch(`${this.linkApiUrl}${path}`, {
|
|
506
|
+
method: "GET",
|
|
507
|
+
credentials: this.credentials,
|
|
508
|
+
headers: this.getHeaders()
|
|
509
|
+
});
|
|
510
|
+
return this.handleRestResponse(response);
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
async linkPost(path, body) {
|
|
514
|
+
const response = await this.resilientFetch(`${this.linkApiUrl}${path}`, {
|
|
515
|
+
method: "POST",
|
|
516
|
+
credentials: this.credentials,
|
|
517
|
+
headers: this.getHeaders(),
|
|
518
|
+
body: body ? JSON.stringify(body) : void 0
|
|
519
|
+
});
|
|
520
|
+
return this.handleRestResponse(response);
|
|
521
|
+
}
|
|
522
|
+
async linkDelete(path) {
|
|
523
|
+
const response = await this.resilientFetch(`${this.linkApiUrl}${path}`, {
|
|
524
|
+
method: "DELETE",
|
|
525
|
+
credentials: this.credentials,
|
|
526
|
+
headers: this.getHeaders()
|
|
527
|
+
});
|
|
528
|
+
return this.handleRestResponse(response);
|
|
529
|
+
}
|
|
530
|
+
async handleRestResponse(response) {
|
|
531
|
+
const json = await response.json();
|
|
532
|
+
if (!response.ok) {
|
|
533
|
+
const errorCode = typeof json.error === "object" && json.error?.error_code || typeof json.error === "object" && json.error?.code || "API_ERROR";
|
|
534
|
+
const errorMessage = typeof json.error === "object" && json.error?.error_message || typeof json.error === "object" && json.error?.message || typeof json.error === "string" && json.error || typeof json.message === "string" && json.message || "An error occurred";
|
|
535
|
+
const retryable = typeof json.error === "object" && typeof json.error?.retryable === "boolean" ? json.error.retryable : false;
|
|
536
|
+
const error = enrichError(
|
|
537
|
+
new CimplifyError(
|
|
538
|
+
errorCode,
|
|
539
|
+
errorMessage,
|
|
540
|
+
retryable
|
|
541
|
+
),
|
|
542
|
+
{ isTestMode: this.isTestMode() }
|
|
543
|
+
);
|
|
544
|
+
if (response.status === 401 || error.code === ErrorCode.UNAUTHORIZED) {
|
|
545
|
+
console.warn(
|
|
546
|
+
"[Cimplify] Received 401 Unauthorized. Access token may be missing/expired. Refresh authentication and retry the request."
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
throw error;
|
|
550
|
+
}
|
|
551
|
+
if (json?.success === false || json?.error?.code || json?.error?.message) {
|
|
552
|
+
const error = enrichError(
|
|
553
|
+
new CimplifyError(
|
|
554
|
+
json.error?.code || "API_ERROR",
|
|
555
|
+
json.error?.message || "An error occurred",
|
|
556
|
+
json.error?.retryable || false
|
|
557
|
+
),
|
|
558
|
+
{ isTestMode: this.isTestMode() }
|
|
559
|
+
);
|
|
560
|
+
throw error;
|
|
561
|
+
}
|
|
562
|
+
if (json?.data !== void 0) {
|
|
563
|
+
return json.data;
|
|
564
|
+
}
|
|
565
|
+
return json;
|
|
566
|
+
}
|
|
567
|
+
get catalogue() {
|
|
568
|
+
if (!this._catalogue) {
|
|
569
|
+
this._catalogue = new CatalogueQueries(this);
|
|
570
|
+
}
|
|
571
|
+
return this._catalogue;
|
|
572
|
+
}
|
|
573
|
+
get cart() {
|
|
574
|
+
if (!this._cart) {
|
|
575
|
+
this._cart = new CartOperations(this);
|
|
576
|
+
}
|
|
577
|
+
return this._cart;
|
|
578
|
+
}
|
|
579
|
+
get checkout() {
|
|
580
|
+
if (!this._checkout) {
|
|
581
|
+
this._checkout = new CheckoutService(this);
|
|
582
|
+
}
|
|
583
|
+
return this._checkout;
|
|
584
|
+
}
|
|
585
|
+
get orders() {
|
|
586
|
+
if (!this._orders) {
|
|
587
|
+
this._orders = new OrderQueries(this);
|
|
588
|
+
}
|
|
589
|
+
return this._orders;
|
|
590
|
+
}
|
|
591
|
+
get link() {
|
|
592
|
+
if (!this._link) {
|
|
593
|
+
this._link = new LinkService(this);
|
|
594
|
+
}
|
|
595
|
+
return this._link;
|
|
596
|
+
}
|
|
597
|
+
get auth() {
|
|
598
|
+
if (!this._auth) {
|
|
599
|
+
this._auth = new AuthService(this);
|
|
600
|
+
}
|
|
601
|
+
return this._auth;
|
|
602
|
+
}
|
|
603
|
+
get business() {
|
|
604
|
+
if (!this._business) {
|
|
605
|
+
this._business = new BusinessService(this);
|
|
606
|
+
}
|
|
607
|
+
return this._business;
|
|
608
|
+
}
|
|
609
|
+
get inventory() {
|
|
610
|
+
if (!this._inventory) {
|
|
611
|
+
this._inventory = new InventoryService(this);
|
|
612
|
+
}
|
|
613
|
+
return this._inventory;
|
|
614
|
+
}
|
|
615
|
+
get scheduling() {
|
|
616
|
+
if (!this._scheduling) {
|
|
617
|
+
this._scheduling = new SchedulingService(this);
|
|
618
|
+
}
|
|
619
|
+
return this._scheduling;
|
|
620
|
+
}
|
|
621
|
+
get lite() {
|
|
622
|
+
if (!this._lite) {
|
|
623
|
+
this._lite = new LiteService(this);
|
|
624
|
+
}
|
|
625
|
+
return this._lite;
|
|
626
|
+
}
|
|
627
|
+
get fx() {
|
|
628
|
+
if (!this._fx) {
|
|
629
|
+
this._fx = new FxService(this);
|
|
630
|
+
}
|
|
631
|
+
return this._fx;
|
|
632
|
+
}
|
|
633
|
+
get activity() {
|
|
634
|
+
if (!this._activity) {
|
|
635
|
+
this._activity = new ActivityService(this);
|
|
636
|
+
}
|
|
637
|
+
return this._activity;
|
|
638
|
+
}
|
|
639
|
+
elements(businessId, options) {
|
|
640
|
+
if (businessId) {
|
|
641
|
+
this.setBusinessId(businessId);
|
|
642
|
+
}
|
|
643
|
+
return createElements(this, businessId ?? this.businessId ?? void 0, options);
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
function createCimplifyClient(config = {}) {
|
|
647
|
+
return new CimplifyClient(config);
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// src/utils/variant.ts
|
|
651
|
+
function stripProductPrefix(variantName, productName) {
|
|
652
|
+
if (!variantName || !productName) return variantName || productName;
|
|
653
|
+
const lower = variantName.toLowerCase().trim();
|
|
654
|
+
if (lower === "default" || lower === "standard") return productName;
|
|
655
|
+
if (lower.endsWith("-default") || lower.endsWith("-standard")) return productName;
|
|
656
|
+
if (variantName.startsWith(productName)) {
|
|
657
|
+
const rest = variantName.slice(productName.length);
|
|
658
|
+
const match = rest.match(/^\s*[-–—]\s*(.*)/);
|
|
659
|
+
if (match && match[1].length > 0) return match[1];
|
|
660
|
+
}
|
|
661
|
+
const lowerProduct = productName.toLowerCase().trim();
|
|
662
|
+
if (lower.startsWith(lowerProduct)) {
|
|
663
|
+
const rest = variantName.slice(productName.length);
|
|
664
|
+
const match = rest.match(/^\s*[-–—]\s*(.*)/);
|
|
665
|
+
if (match && match[1].length > 0) return match[1];
|
|
666
|
+
}
|
|
667
|
+
const lastDash = variantName.lastIndexOf("-");
|
|
668
|
+
if (lastDash > 0 && lastDash < variantName.length - 1) {
|
|
669
|
+
const prefix = variantName.slice(0, lastDash).trim();
|
|
670
|
+
const suffix = variantName.slice(lastDash + 1).trim();
|
|
671
|
+
if (suffix.length > 0 && prefix.length > suffix.length * 2) {
|
|
672
|
+
return suffix;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
return variantName;
|
|
676
|
+
}
|
|
677
|
+
function getVariantDisplayName(variant, productName) {
|
|
678
|
+
if (variant.display_attributes?.length) {
|
|
679
|
+
return variant.display_attributes.map((a) => a.value_name).join(" / ");
|
|
680
|
+
}
|
|
681
|
+
if (variant.name) {
|
|
682
|
+
return productName ? stripProductPrefix(variant.name, productName) : variant.name;
|
|
683
|
+
}
|
|
684
|
+
return productName || (variant.is_default ? "Default" : "Option");
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
export { ActivityService, CimplifyClient, createCimplifyClient, getVariantDisplayName };
|