arky-sdk 0.7.93 → 0.7.95
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 +19 -19
- package/dist/admin.cjs +2324 -0
- package/dist/admin.cjs.map +1 -0
- package/dist/admin.d.cts +3 -0
- package/dist/admin.d.ts +3 -0
- package/dist/admin.js +2322 -0
- package/dist/admin.js.map +1 -0
- package/dist/index.cjs +518 -523
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +55 -86
- package/dist/index.d.ts +55 -86
- package/dist/index.js +518 -523
- package/dist/index.js.map +1 -1
- package/dist/storefront.cjs +1359 -0
- package/dist/storefront.cjs.map +1 -0
- package/dist/storefront.d.cts +3 -0
- package/dist/storefront.d.ts +3 -0
- package/dist/storefront.js +1356 -0
- package/dist/storefront.js.map +1 -0
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +164 -164
- package/dist/types.d.ts +164 -164
- package/dist/types.js.map +1 -1
- package/package.json +12 -5
|
@@ -0,0 +1,1356 @@
|
|
|
1
|
+
// src/utils/blocks.ts
|
|
2
|
+
function getBlockLabel(block) {
|
|
3
|
+
if (!block) return "";
|
|
4
|
+
return block.key?.replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()) ?? "";
|
|
5
|
+
}
|
|
6
|
+
function formatBlockValue(block) {
|
|
7
|
+
if (!block || block.value === null || block.value === void 0) return "";
|
|
8
|
+
const value = block.value;
|
|
9
|
+
switch (block.type) {
|
|
10
|
+
case "boolean":
|
|
11
|
+
return value ? "Yes" : "No";
|
|
12
|
+
case "number":
|
|
13
|
+
if (block.properties?.variant === "DATE" || block.properties?.variant === "DATE_TIME") {
|
|
14
|
+
return new Date(value).toLocaleDateString();
|
|
15
|
+
}
|
|
16
|
+
return String(value);
|
|
17
|
+
case "relationship_entry":
|
|
18
|
+
case "relationship_media":
|
|
19
|
+
if (value && typeof value === "object") {
|
|
20
|
+
return value.mime_type ? value.name || value.id : value.title || value.name || value.id;
|
|
21
|
+
}
|
|
22
|
+
return String(value);
|
|
23
|
+
default:
|
|
24
|
+
return String(value);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function prepareBlocksForSubmission(formData, blockTypes) {
|
|
28
|
+
return Object.keys(formData).filter((key) => formData[key] !== null && formData[key] !== void 0).map((key) => ({
|
|
29
|
+
key,
|
|
30
|
+
value: blockTypes?.[key] === "list" && !Array.isArray(formData[key]) ? [formData[key]] : formData[key]
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
function extractBlockValues(blocks) {
|
|
34
|
+
const values = {};
|
|
35
|
+
blocks.forEach((block) => {
|
|
36
|
+
values[block.key] = block.value ?? null;
|
|
37
|
+
});
|
|
38
|
+
return values;
|
|
39
|
+
}
|
|
40
|
+
var getBlockValue = (entry, blockKey) => {
|
|
41
|
+
const block = entry?.blocks?.find((f) => f.key === blockKey);
|
|
42
|
+
return block?.value ?? null;
|
|
43
|
+
};
|
|
44
|
+
var getBlockTextValue = (block, locale = "en") => {
|
|
45
|
+
if (!block || block.value === null || block.value === void 0) return "";
|
|
46
|
+
if (block.type === "localized_text" || block.type === "markdown") {
|
|
47
|
+
if (typeof block.value === "object" && block.value !== null) {
|
|
48
|
+
return block.value[locale] ?? block.value["en"] ?? "";
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (typeof block.value === "string") return block.value;
|
|
52
|
+
return String(block.value ?? "");
|
|
53
|
+
};
|
|
54
|
+
var getBlockValues = (entry, blockKey) => {
|
|
55
|
+
const block = entry?.blocks?.find((f) => f.key === blockKey);
|
|
56
|
+
if (!block) return [];
|
|
57
|
+
if (block.type === "list" && Array.isArray(block.value)) {
|
|
58
|
+
return block.value;
|
|
59
|
+
}
|
|
60
|
+
return [];
|
|
61
|
+
};
|
|
62
|
+
function unwrapBlock(block, locale) {
|
|
63
|
+
if (!block?.type || block.value === void 0) return block;
|
|
64
|
+
if (block.type === "list") {
|
|
65
|
+
return block.value.map((obj) => {
|
|
66
|
+
const parsed = {};
|
|
67
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
68
|
+
parsed[k] = unwrapBlock(v, locale);
|
|
69
|
+
}
|
|
70
|
+
return parsed;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
if (block.type === "map") {
|
|
74
|
+
const parsed = {};
|
|
75
|
+
for (const [k, v] of Object.entries(block.value || {})) {
|
|
76
|
+
parsed[k] = unwrapBlock(v, locale);
|
|
77
|
+
}
|
|
78
|
+
return parsed;
|
|
79
|
+
}
|
|
80
|
+
if (block.type === "localized_text" || block.type === "markdown") {
|
|
81
|
+
return block.value?.[locale];
|
|
82
|
+
}
|
|
83
|
+
return block.value;
|
|
84
|
+
}
|
|
85
|
+
var getBlockObjectValues = (entry, blockKey, locale = "en") => {
|
|
86
|
+
const block = entry?.blocks?.find((f) => f.key === blockKey);
|
|
87
|
+
if (!block || block.type !== "list" || !Array.isArray(block.value)) return [];
|
|
88
|
+
return block.value.map((obj) => {
|
|
89
|
+
if (!obj?.value || !Array.isArray(obj.value)) return {};
|
|
90
|
+
return obj.value.reduce((acc, current) => {
|
|
91
|
+
acc[current.key] = unwrapBlock(current, locale);
|
|
92
|
+
return acc;
|
|
93
|
+
}, {});
|
|
94
|
+
});
|
|
95
|
+
};
|
|
96
|
+
var getBlockFromArray = (entry, blockKey, locale = "en") => {
|
|
97
|
+
const block = entry?.blocks?.find((f) => f.key === blockKey);
|
|
98
|
+
if (!block) return {};
|
|
99
|
+
if (block.type === "list" && Array.isArray(block.value)) {
|
|
100
|
+
return block.value.reduce((acc, current) => {
|
|
101
|
+
acc[current.key] = unwrapBlock(current, locale);
|
|
102
|
+
return acc;
|
|
103
|
+
}, {});
|
|
104
|
+
}
|
|
105
|
+
if (block.type === "map" && block.value && typeof block.value === "object") {
|
|
106
|
+
const result = {};
|
|
107
|
+
for (const [k, v] of Object.entries(block.value)) {
|
|
108
|
+
result[k] = unwrapBlock(v, locale);
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
return { [block.key]: unwrapBlock(block, locale) };
|
|
113
|
+
};
|
|
114
|
+
var getImageUrl = (imageBlock, isBlock = true) => {
|
|
115
|
+
if (!imageBlock) return null;
|
|
116
|
+
if (imageBlock.type === "relationship_media") {
|
|
117
|
+
const mediaValue = imageBlock.value;
|
|
118
|
+
return mediaValue?.resolutions?.original?.url || mediaValue?.url || null;
|
|
119
|
+
}
|
|
120
|
+
if (isBlock) {
|
|
121
|
+
if (typeof imageBlock === "string") return imageBlock;
|
|
122
|
+
if (imageBlock.url) return imageBlock.url;
|
|
123
|
+
}
|
|
124
|
+
return imageBlock.resolutions?.original?.url || null;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// src/api/storefront.ts
|
|
128
|
+
var COMMON_ACTIVITY_TYPES = [
|
|
129
|
+
"page_view",
|
|
130
|
+
"product_view",
|
|
131
|
+
"service_view",
|
|
132
|
+
"provider_view",
|
|
133
|
+
"cart_added",
|
|
134
|
+
"cart_removed",
|
|
135
|
+
"checkout_started",
|
|
136
|
+
"purchase",
|
|
137
|
+
"booking_created",
|
|
138
|
+
"signin",
|
|
139
|
+
"signup",
|
|
140
|
+
"verified_email",
|
|
141
|
+
"search",
|
|
142
|
+
"share",
|
|
143
|
+
"wishlist_added"
|
|
144
|
+
];
|
|
145
|
+
var createActivityApi = (apiConfig) => ({
|
|
146
|
+
COMMON_ACTIVITY_TYPES,
|
|
147
|
+
async track(params) {
|
|
148
|
+
try {
|
|
149
|
+
await apiConfig.httpClient.post(
|
|
150
|
+
`/v1/storefront/${apiConfig.storeId}/activities/track`,
|
|
151
|
+
{ type: params.type, payload: params.payload }
|
|
152
|
+
);
|
|
153
|
+
} catch {
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
function groupCartToItems(cart) {
|
|
158
|
+
const groups = /* @__PURE__ */ new Map();
|
|
159
|
+
for (const slot of cart) {
|
|
160
|
+
const key = `${slot.service_id}:${slot.provider_id}`;
|
|
161
|
+
if (!groups.has(key)) {
|
|
162
|
+
groups.set(key, {
|
|
163
|
+
service_id: slot.service_id,
|
|
164
|
+
provider_id: slot.provider_id,
|
|
165
|
+
slots: []
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
groups.get(key).slots.push({ from: slot.from, to: slot.to });
|
|
169
|
+
}
|
|
170
|
+
return [...groups.values()];
|
|
171
|
+
}
|
|
172
|
+
var createStorefrontApi = (apiConfig) => {
|
|
173
|
+
const base = (storeId = apiConfig.storeId) => `/v1/storefront/${storeId}`;
|
|
174
|
+
let cart = [];
|
|
175
|
+
return {
|
|
176
|
+
store: {
|
|
177
|
+
getStore(options) {
|
|
178
|
+
return apiConfig.httpClient.get(base(), options);
|
|
179
|
+
},
|
|
180
|
+
location: {
|
|
181
|
+
getCountries(options) {
|
|
182
|
+
return apiConfig.httpClient.get(`/v1/platform/countries`, options);
|
|
183
|
+
},
|
|
184
|
+
getCountry(countryCode, options) {
|
|
185
|
+
return apiConfig.httpClient.get(
|
|
186
|
+
`/v1/platform/countries/${countryCode}`,
|
|
187
|
+
options
|
|
188
|
+
);
|
|
189
|
+
},
|
|
190
|
+
list(options) {
|
|
191
|
+
return apiConfig.httpClient.get(`${base()}/locations`, options);
|
|
192
|
+
},
|
|
193
|
+
get(id, options) {
|
|
194
|
+
return apiConfig.httpClient.get(`${base()}/locations/${id}`, options);
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
market: {
|
|
198
|
+
list(options) {
|
|
199
|
+
return apiConfig.httpClient.get(`${base()}/markets`, options);
|
|
200
|
+
},
|
|
201
|
+
get(id, options) {
|
|
202
|
+
return apiConfig.httpClient.get(`${base()}/markets/${id}`, options);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
cms: {
|
|
207
|
+
node: {
|
|
208
|
+
async get(params, options) {
|
|
209
|
+
const store_id = params.store_id || apiConfig.storeId;
|
|
210
|
+
let identifier;
|
|
211
|
+
if (params.id) {
|
|
212
|
+
identifier = params.id;
|
|
213
|
+
} else if (params.slug) {
|
|
214
|
+
identifier = `${store_id}:${apiConfig.locale}:${params.slug}`;
|
|
215
|
+
} else if (params.key) {
|
|
216
|
+
identifier = `${store_id}:${params.key}`;
|
|
217
|
+
} else {
|
|
218
|
+
throw new Error("GetNodeParams requires id, slug, or key");
|
|
219
|
+
}
|
|
220
|
+
const response = await apiConfig.httpClient.get(
|
|
221
|
+
`${base(store_id)}/nodes/${identifier}`,
|
|
222
|
+
options
|
|
223
|
+
);
|
|
224
|
+
return {
|
|
225
|
+
...response,
|
|
226
|
+
getBlock(key) {
|
|
227
|
+
return getBlockFromArray(response, key, apiConfig.locale);
|
|
228
|
+
},
|
|
229
|
+
getBlockValues(key) {
|
|
230
|
+
return getBlockObjectValues(response, key, apiConfig.locale);
|
|
231
|
+
},
|
|
232
|
+
getImage(key) {
|
|
233
|
+
const block = getBlockFromArray(response, key, apiConfig.locale);
|
|
234
|
+
return getImageUrl(block, true);
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
},
|
|
238
|
+
find(params, options) {
|
|
239
|
+
const { store_id, ...queryParams } = params;
|
|
240
|
+
return apiConfig.httpClient.get(`${base(store_id)}/nodes`, {
|
|
241
|
+
...options,
|
|
242
|
+
params: queryParams
|
|
243
|
+
});
|
|
244
|
+
},
|
|
245
|
+
getChildren(params, options) {
|
|
246
|
+
const { id, store_id, ...queryParams } = params;
|
|
247
|
+
return apiConfig.httpClient.get(`${base(store_id)}/nodes/${id}/children`, {
|
|
248
|
+
...options,
|
|
249
|
+
params: queryParams
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
form: {
|
|
254
|
+
get(params, options) {
|
|
255
|
+
const store_id = params.store_id || apiConfig.storeId;
|
|
256
|
+
let identifier;
|
|
257
|
+
if (params.id) {
|
|
258
|
+
identifier = params.id;
|
|
259
|
+
} else if (params.key) {
|
|
260
|
+
identifier = `${store_id}:${params.key}`;
|
|
261
|
+
} else {
|
|
262
|
+
throw new Error("GetFormParams requires id or key");
|
|
263
|
+
}
|
|
264
|
+
return apiConfig.httpClient.get(
|
|
265
|
+
`${base(store_id)}/forms/${identifier}`,
|
|
266
|
+
options
|
|
267
|
+
);
|
|
268
|
+
},
|
|
269
|
+
submit(params, options) {
|
|
270
|
+
const { store_id, form_id, ...payload } = params;
|
|
271
|
+
const target_store_id = store_id || apiConfig.storeId;
|
|
272
|
+
if (!form_id) {
|
|
273
|
+
throw new Error("SubmitFormParams requires form_id");
|
|
274
|
+
}
|
|
275
|
+
return apiConfig.httpClient.post(
|
|
276
|
+
`${base(target_store_id)}/forms/${form_id}/submissions`,
|
|
277
|
+
{ ...payload, form_id, store_id: target_store_id },
|
|
278
|
+
options
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
taxonomy: {
|
|
283
|
+
get(params, options) {
|
|
284
|
+
const store_id = params.store_id || apiConfig.storeId;
|
|
285
|
+
let identifier;
|
|
286
|
+
if (params.id) {
|
|
287
|
+
identifier = params.id;
|
|
288
|
+
} else if (params.key) {
|
|
289
|
+
identifier = `${store_id}:${params.key}`;
|
|
290
|
+
} else {
|
|
291
|
+
throw new Error("GetTaxonomyParams requires id or key");
|
|
292
|
+
}
|
|
293
|
+
return apiConfig.httpClient.get(
|
|
294
|
+
`${base(store_id)}/taxonomies/${identifier}`,
|
|
295
|
+
options
|
|
296
|
+
);
|
|
297
|
+
},
|
|
298
|
+
getChildren(params, options) {
|
|
299
|
+
const store_id = params.store_id || apiConfig.storeId;
|
|
300
|
+
return apiConfig.httpClient.get(
|
|
301
|
+
`${base(store_id)}/taxonomies/${params.id}/children`,
|
|
302
|
+
options
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
},
|
|
307
|
+
eshop: {
|
|
308
|
+
product: {
|
|
309
|
+
get(params, options) {
|
|
310
|
+
const store_id = params.store_id || apiConfig.storeId;
|
|
311
|
+
let identifier;
|
|
312
|
+
if (params.id) {
|
|
313
|
+
identifier = params.id;
|
|
314
|
+
} else if (params.slug) {
|
|
315
|
+
identifier = `${store_id}:${apiConfig.locale}:${params.slug}`;
|
|
316
|
+
} else {
|
|
317
|
+
throw new Error("GetProductParams requires id or slug");
|
|
318
|
+
}
|
|
319
|
+
return apiConfig.httpClient.get(
|
|
320
|
+
`${base(store_id)}/products/${identifier}`,
|
|
321
|
+
options
|
|
322
|
+
);
|
|
323
|
+
},
|
|
324
|
+
find(params, options) {
|
|
325
|
+
const { store_id, ...queryParams } = params;
|
|
326
|
+
return apiConfig.httpClient.get(`${base(store_id)}/products`, {
|
|
327
|
+
...options,
|
|
328
|
+
params: queryParams
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
},
|
|
332
|
+
order: {
|
|
333
|
+
getQuote(params, options) {
|
|
334
|
+
const store_id = params.store_id || apiConfig.storeId;
|
|
335
|
+
const { location, store_id: _store_id, ...rest } = params;
|
|
336
|
+
const shipping_address = location ? {
|
|
337
|
+
country: location.country || "",
|
|
338
|
+
state: location.state || "",
|
|
339
|
+
city: location.city || "",
|
|
340
|
+
postal_code: location.postal_code || "",
|
|
341
|
+
name: "",
|
|
342
|
+
street1: "",
|
|
343
|
+
street2: null
|
|
344
|
+
} : void 0;
|
|
345
|
+
return apiConfig.httpClient.post(
|
|
346
|
+
`${base(store_id)}/orders/quote`,
|
|
347
|
+
{ ...rest, shipping_address, market: apiConfig.market },
|
|
348
|
+
options
|
|
349
|
+
);
|
|
350
|
+
},
|
|
351
|
+
checkout(params, options) {
|
|
352
|
+
const { store_id, ...rest } = params;
|
|
353
|
+
const target = store_id || apiConfig.storeId;
|
|
354
|
+
return apiConfig.httpClient.post(
|
|
355
|
+
`${base(target)}/orders/checkout`,
|
|
356
|
+
{ ...rest, store_id: target, market: apiConfig.market },
|
|
357
|
+
options
|
|
358
|
+
);
|
|
359
|
+
},
|
|
360
|
+
get(params, options) {
|
|
361
|
+
const store_id = params.store_id || apiConfig.storeId;
|
|
362
|
+
return apiConfig.httpClient.get(
|
|
363
|
+
`${base(store_id)}/orders/${params.id}`,
|
|
364
|
+
options
|
|
365
|
+
);
|
|
366
|
+
},
|
|
367
|
+
find(params, options) {
|
|
368
|
+
const { store_id, ...queryParams } = params;
|
|
369
|
+
return apiConfig.httpClient.get(`${base(store_id)}/orders`, {
|
|
370
|
+
...options,
|
|
371
|
+
params: queryParams
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
},
|
|
376
|
+
booking: {
|
|
377
|
+
addToCart(slot) {
|
|
378
|
+
cart.push(slot);
|
|
379
|
+
},
|
|
380
|
+
removeFromCart(slotId) {
|
|
381
|
+
cart = cart.filter((slot) => slot.id !== slotId);
|
|
382
|
+
},
|
|
383
|
+
getCart() {
|
|
384
|
+
return [...cart];
|
|
385
|
+
},
|
|
386
|
+
clearCart() {
|
|
387
|
+
cart = [];
|
|
388
|
+
},
|
|
389
|
+
checkout(params, options) {
|
|
390
|
+
const { store_id, items: paramItems, ...payload } = params || {};
|
|
391
|
+
const target_store_id = store_id || apiConfig.storeId;
|
|
392
|
+
const items = paramItems || groupCartToItems(cart);
|
|
393
|
+
return apiConfig.httpClient.post(
|
|
394
|
+
`${base(target_store_id)}/bookings/checkout`,
|
|
395
|
+
{ market: "booking", ...payload, items },
|
|
396
|
+
options
|
|
397
|
+
);
|
|
398
|
+
},
|
|
399
|
+
get(params, options) {
|
|
400
|
+
const store_id = params.store_id || apiConfig.storeId;
|
|
401
|
+
return apiConfig.httpClient.get(
|
|
402
|
+
`${base(store_id)}/bookings/${params.id}`,
|
|
403
|
+
options
|
|
404
|
+
);
|
|
405
|
+
},
|
|
406
|
+
find(params, options) {
|
|
407
|
+
const { store_id, ...queryParams } = params;
|
|
408
|
+
return apiConfig.httpClient.get(`${base(store_id)}/bookings`, {
|
|
409
|
+
...options,
|
|
410
|
+
params: queryParams
|
|
411
|
+
});
|
|
412
|
+
},
|
|
413
|
+
getQuote(params, options) {
|
|
414
|
+
const { store_id, ...payload } = params;
|
|
415
|
+
const target_store_id = store_id || apiConfig.storeId;
|
|
416
|
+
return apiConfig.httpClient.post(
|
|
417
|
+
`${base(target_store_id)}/bookings/quote`,
|
|
418
|
+
{ market: "booking", ...payload },
|
|
419
|
+
options
|
|
420
|
+
);
|
|
421
|
+
},
|
|
422
|
+
getAvailability(params, options) {
|
|
423
|
+
const { store_id, ...queryParams } = params;
|
|
424
|
+
const target_store_id = store_id || apiConfig.storeId;
|
|
425
|
+
return apiConfig.httpClient.get(
|
|
426
|
+
`${base(target_store_id)}/bookings/availability`,
|
|
427
|
+
{ ...options, params: queryParams }
|
|
428
|
+
);
|
|
429
|
+
},
|
|
430
|
+
cancelItem(params, options) {
|
|
431
|
+
const { store_id, booking_id, item_id, ...payload } = params;
|
|
432
|
+
const target_store_id = store_id || apiConfig.storeId;
|
|
433
|
+
return apiConfig.httpClient.post(
|
|
434
|
+
`${base(target_store_id)}/bookings/${booking_id}/items/${item_id}/cancel`,
|
|
435
|
+
payload,
|
|
436
|
+
options
|
|
437
|
+
);
|
|
438
|
+
},
|
|
439
|
+
service: {
|
|
440
|
+
get(params, options) {
|
|
441
|
+
const store_id = params.store_id || apiConfig.storeId;
|
|
442
|
+
let identifier;
|
|
443
|
+
if (params.id) {
|
|
444
|
+
identifier = params.id;
|
|
445
|
+
} else if (params.slug) {
|
|
446
|
+
identifier = `${store_id}:${apiConfig.locale}:${params.slug}`;
|
|
447
|
+
} else {
|
|
448
|
+
throw new Error("GetServiceParams requires id or slug");
|
|
449
|
+
}
|
|
450
|
+
return apiConfig.httpClient.get(
|
|
451
|
+
`${base(store_id)}/services/${identifier}`,
|
|
452
|
+
options
|
|
453
|
+
);
|
|
454
|
+
},
|
|
455
|
+
find(params, options) {
|
|
456
|
+
const { store_id, ...queryParams } = params;
|
|
457
|
+
return apiConfig.httpClient.get(`${base(store_id)}/services`, {
|
|
458
|
+
...options,
|
|
459
|
+
params: queryParams
|
|
460
|
+
});
|
|
461
|
+
},
|
|
462
|
+
findProviders(params, options) {
|
|
463
|
+
const { store_id, ...queryParams } = params;
|
|
464
|
+
return apiConfig.httpClient.get(`${base(store_id)}/service-providers`, {
|
|
465
|
+
...options,
|
|
466
|
+
params: queryParams
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
},
|
|
470
|
+
provider: {
|
|
471
|
+
get(params, options) {
|
|
472
|
+
const store_id = params.store_id || apiConfig.storeId;
|
|
473
|
+
let identifier;
|
|
474
|
+
if (params.id) {
|
|
475
|
+
identifier = params.id;
|
|
476
|
+
} else if (params.slug) {
|
|
477
|
+
identifier = `${store_id}:${apiConfig.locale}:${params.slug}`;
|
|
478
|
+
} else {
|
|
479
|
+
throw new Error("GetProviderParams requires id or slug");
|
|
480
|
+
}
|
|
481
|
+
return apiConfig.httpClient.get(
|
|
482
|
+
`${base(store_id)}/providers/${identifier}`,
|
|
483
|
+
options
|
|
484
|
+
);
|
|
485
|
+
},
|
|
486
|
+
find(params, options) {
|
|
487
|
+
const { store_id, ...queryParams } = params;
|
|
488
|
+
return apiConfig.httpClient.get(`${base(store_id)}/providers`, {
|
|
489
|
+
...options,
|
|
490
|
+
params: queryParams
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
},
|
|
495
|
+
crm: {
|
|
496
|
+
customer: {
|
|
497
|
+
async session(params, options) {
|
|
498
|
+
const store_id = params?.store_id || apiConfig.storeId;
|
|
499
|
+
const result = await apiConfig.httpClient.post(
|
|
500
|
+
`${base(store_id)}/customers/session`,
|
|
501
|
+
{ store_id, market: params?.market || apiConfig.market || null },
|
|
502
|
+
options
|
|
503
|
+
);
|
|
504
|
+
if (result?.token?.access_token) {
|
|
505
|
+
apiConfig.setToken(result.token);
|
|
506
|
+
}
|
|
507
|
+
return result;
|
|
508
|
+
},
|
|
509
|
+
async connect(params, options) {
|
|
510
|
+
const store_id = params.store_id || apiConfig.storeId;
|
|
511
|
+
const result = await apiConfig.httpClient.post(
|
|
512
|
+
`${base(store_id)}/customers/connect`,
|
|
513
|
+
{ email: params.email, store_id },
|
|
514
|
+
options
|
|
515
|
+
);
|
|
516
|
+
if (result?.access_token) {
|
|
517
|
+
apiConfig.setToken(result);
|
|
518
|
+
}
|
|
519
|
+
return result;
|
|
520
|
+
},
|
|
521
|
+
requestCode(params, options) {
|
|
522
|
+
const store_id = params.store_id || apiConfig.storeId;
|
|
523
|
+
return apiConfig.httpClient.post(
|
|
524
|
+
`${base(store_id)}/customers/auth/code`,
|
|
525
|
+
{ email: params.email, store_id },
|
|
526
|
+
options
|
|
527
|
+
);
|
|
528
|
+
},
|
|
529
|
+
async verify(params, options) {
|
|
530
|
+
const store_id = params.store_id || apiConfig.storeId;
|
|
531
|
+
const result = await apiConfig.httpClient.post(
|
|
532
|
+
`${base(store_id)}/customers/auth/verify`,
|
|
533
|
+
{ email: params.email, code: params.code, store_id },
|
|
534
|
+
options
|
|
535
|
+
);
|
|
536
|
+
if (result?.access_token) {
|
|
537
|
+
apiConfig.setToken(result);
|
|
538
|
+
}
|
|
539
|
+
return result;
|
|
540
|
+
},
|
|
541
|
+
async refreshToken(params, options) {
|
|
542
|
+
const store_id = params.store_id || apiConfig.storeId;
|
|
543
|
+
const result = await apiConfig.httpClient.post(
|
|
544
|
+
`${base(store_id)}/customers/auth/refresh`,
|
|
545
|
+
{ refresh_token: params.refresh_token },
|
|
546
|
+
options
|
|
547
|
+
);
|
|
548
|
+
if (result?.access_token) {
|
|
549
|
+
apiConfig.setToken(result);
|
|
550
|
+
}
|
|
551
|
+
return result;
|
|
552
|
+
},
|
|
553
|
+
getMe(options) {
|
|
554
|
+
return apiConfig.httpClient.get(`${base()}/customers/me`, options);
|
|
555
|
+
}
|
|
556
|
+
},
|
|
557
|
+
audience: {
|
|
558
|
+
get(params, options) {
|
|
559
|
+
let identifier;
|
|
560
|
+
if (params.id) {
|
|
561
|
+
identifier = params.id;
|
|
562
|
+
} else if (params.key) {
|
|
563
|
+
identifier = `${apiConfig.storeId}:${params.key}`;
|
|
564
|
+
} else {
|
|
565
|
+
throw new Error("GetAudienceParams requires id or key");
|
|
566
|
+
}
|
|
567
|
+
return apiConfig.httpClient.get(
|
|
568
|
+
`${base(apiConfig.storeId)}/audiences/${identifier}`,
|
|
569
|
+
options
|
|
570
|
+
);
|
|
571
|
+
},
|
|
572
|
+
find(params, options) {
|
|
573
|
+
return apiConfig.httpClient.get(`${base()}/audiences`, {
|
|
574
|
+
...options,
|
|
575
|
+
params
|
|
576
|
+
});
|
|
577
|
+
},
|
|
578
|
+
subscribe(params, options) {
|
|
579
|
+
return apiConfig.httpClient.post(
|
|
580
|
+
`${base()}/audiences/${params.id}/subscribe`,
|
|
581
|
+
{
|
|
582
|
+
customer_id: params.customer_id,
|
|
583
|
+
price_id: params.price_id,
|
|
584
|
+
success_url: params.success_url,
|
|
585
|
+
cancel_url: params.cancel_url,
|
|
586
|
+
confirm_url: params.confirm_url
|
|
587
|
+
},
|
|
588
|
+
options
|
|
589
|
+
);
|
|
590
|
+
},
|
|
591
|
+
checkAccess(params, options) {
|
|
592
|
+
return apiConfig.httpClient.get(
|
|
593
|
+
`${base()}/audiences/${params.id}/access`,
|
|
594
|
+
options
|
|
595
|
+
);
|
|
596
|
+
},
|
|
597
|
+
unsubscribe(token, options) {
|
|
598
|
+
return apiConfig.httpClient.get(`${base()}/audiences/unsubscribe`, {
|
|
599
|
+
...options,
|
|
600
|
+
params: { token }
|
|
601
|
+
});
|
|
602
|
+
},
|
|
603
|
+
confirm(token, options) {
|
|
604
|
+
return apiConfig.httpClient.get(`${base()}/audiences/confirm`, {
|
|
605
|
+
...options,
|
|
606
|
+
params: { token }
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
},
|
|
611
|
+
activity: createActivityApi(apiConfig),
|
|
612
|
+
automation: {
|
|
613
|
+
agent: {
|
|
614
|
+
getAgents(params, options) {
|
|
615
|
+
const store_id = params?.store_id || apiConfig.storeId;
|
|
616
|
+
const queryParams = { ...params || {} };
|
|
617
|
+
delete queryParams.store_id;
|
|
618
|
+
return apiConfig.httpClient.get(`${base(store_id)}/agents`, {
|
|
619
|
+
...options,
|
|
620
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : void 0
|
|
621
|
+
});
|
|
622
|
+
},
|
|
623
|
+
getAgent(params, options) {
|
|
624
|
+
const store_id = params.store_id || apiConfig.storeId;
|
|
625
|
+
return apiConfig.httpClient.get(
|
|
626
|
+
`${base(store_id)}/agents/${params.id}`,
|
|
627
|
+
options
|
|
628
|
+
);
|
|
629
|
+
},
|
|
630
|
+
sendMessage(params, options) {
|
|
631
|
+
const store_id = params.store_id || apiConfig.storeId;
|
|
632
|
+
const body = { message: params.message };
|
|
633
|
+
if (params.chat_id) body.chat_id = params.chat_id;
|
|
634
|
+
return apiConfig.httpClient.post(
|
|
635
|
+
`${base(store_id)}/agents/${params.id}/chats/messages`,
|
|
636
|
+
body,
|
|
637
|
+
options
|
|
638
|
+
);
|
|
639
|
+
},
|
|
640
|
+
getChat(params, options) {
|
|
641
|
+
const store_id = params.store_id || apiConfig.storeId;
|
|
642
|
+
return apiConfig.httpClient.get(
|
|
643
|
+
`${base(store_id)}/agents/${params.id}/chats/${params.chat_id}`,
|
|
644
|
+
options
|
|
645
|
+
);
|
|
646
|
+
},
|
|
647
|
+
getChatMessages(params, options) {
|
|
648
|
+
const store_id = params.store_id || apiConfig.storeId;
|
|
649
|
+
const queryParams = {};
|
|
650
|
+
if (params.limit) queryParams.limit = String(params.limit);
|
|
651
|
+
return apiConfig.httpClient.get(
|
|
652
|
+
`${base(store_id)}/agents/${params.id}/chats/${params.chat_id}/messages`,
|
|
653
|
+
{
|
|
654
|
+
...options,
|
|
655
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : void 0
|
|
656
|
+
}
|
|
657
|
+
);
|
|
658
|
+
},
|
|
659
|
+
rateChat(params, options) {
|
|
660
|
+
const store_id = params.store_id || apiConfig.storeId;
|
|
661
|
+
const body = { rating: params.rating };
|
|
662
|
+
if (params.comment) body.comment = params.comment;
|
|
663
|
+
return apiConfig.httpClient.post(
|
|
664
|
+
`${base(store_id)}/agents/${params.id}/chats/${params.chat_id}/rate`,
|
|
665
|
+
body,
|
|
666
|
+
options
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
};
|
|
672
|
+
};
|
|
673
|
+
|
|
674
|
+
// src/utils/errors.ts
|
|
675
|
+
var convertServerErrorToRequestError = (serverError, renameRules) => {
|
|
676
|
+
const validationErrors = serverError?.validationErrors ?? [];
|
|
677
|
+
return {
|
|
678
|
+
...serverError,
|
|
679
|
+
validationErrors: validationErrors.map((validationError) => {
|
|
680
|
+
const field = validationError.field;
|
|
681
|
+
return {
|
|
682
|
+
field,
|
|
683
|
+
error: validationError.error || "GENERAL.VALIDATION_ERROR"
|
|
684
|
+
};
|
|
685
|
+
})
|
|
686
|
+
};
|
|
687
|
+
};
|
|
688
|
+
|
|
689
|
+
// src/utils/queryParams.ts
|
|
690
|
+
function buildQueryString(params) {
|
|
691
|
+
const queryParts = [];
|
|
692
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
693
|
+
if (value === null || value === void 0) {
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
if (Array.isArray(value)) {
|
|
697
|
+
const jsonString = JSON.stringify(value);
|
|
698
|
+
queryParts.push(`${key}=${encodeURIComponent(jsonString)}`);
|
|
699
|
+
} else if (typeof value === "string") {
|
|
700
|
+
queryParts.push(`${key}=${encodeURIComponent(value)}`);
|
|
701
|
+
} else if (typeof value === "number" || typeof value === "boolean") {
|
|
702
|
+
queryParts.push(`${key}=${value}`);
|
|
703
|
+
} else if (typeof value === "object") {
|
|
704
|
+
const jsonString = JSON.stringify(value);
|
|
705
|
+
queryParts.push(`${key}=${encodeURIComponent(jsonString)}`);
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
return queryParts.length > 0 ? `?${queryParts.join("&")}` : "";
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// src/services/createHttpClient.ts
|
|
712
|
+
var STORAGE_KEYS = {
|
|
713
|
+
access_token: "arky_token",
|
|
714
|
+
refresh_token: "arky_refresh",
|
|
715
|
+
access_expires_at: "arky_expires_at"
|
|
716
|
+
};
|
|
717
|
+
function defaultGetToken() {
|
|
718
|
+
if (typeof window === "undefined") return { access_token: "" };
|
|
719
|
+
return {
|
|
720
|
+
access_token: localStorage.getItem(STORAGE_KEYS.access_token) || "",
|
|
721
|
+
refresh_token: localStorage.getItem(STORAGE_KEYS.refresh_token) || "",
|
|
722
|
+
access_expires_at: parseInt(localStorage.getItem(STORAGE_KEYS.access_expires_at) || "0", 10)
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
function defaultSetToken(tokens) {
|
|
726
|
+
if (typeof window === "undefined") return;
|
|
727
|
+
if (tokens.access_token) {
|
|
728
|
+
localStorage.setItem(STORAGE_KEYS.access_token, tokens.access_token);
|
|
729
|
+
localStorage.setItem(STORAGE_KEYS.refresh_token, tokens.refresh_token || "");
|
|
730
|
+
localStorage.setItem(STORAGE_KEYS.access_expires_at, (tokens.access_expires_at || 0).toString());
|
|
731
|
+
} else {
|
|
732
|
+
Object.values(STORAGE_KEYS).forEach((key) => localStorage.removeItem(key));
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
function defaultLogout() {
|
|
736
|
+
if (typeof window === "undefined") return;
|
|
737
|
+
Object.values(STORAGE_KEYS).forEach((key) => localStorage.removeItem(key));
|
|
738
|
+
}
|
|
739
|
+
function defaultIsAuthenticated() {
|
|
740
|
+
if (typeof window === "undefined") return false;
|
|
741
|
+
const token = localStorage.getItem(STORAGE_KEYS.access_token) || "";
|
|
742
|
+
return token.startsWith("customer_") || token.startsWith("account_");
|
|
743
|
+
}
|
|
744
|
+
function createHttpClient(cfg) {
|
|
745
|
+
const getToken = cfg.getToken || defaultGetToken;
|
|
746
|
+
const setToken = cfg.setToken || defaultSetToken;
|
|
747
|
+
const logout = cfg.logout || defaultLogout;
|
|
748
|
+
let refreshPromise = null;
|
|
749
|
+
function getRefreshEndpoint() {
|
|
750
|
+
const refreshPath = typeof cfg.refreshPath === "function" ? cfg.refreshPath() : cfg.refreshPath || "/v1/auth/refresh";
|
|
751
|
+
return `${cfg.baseUrl}${refreshPath}`;
|
|
752
|
+
}
|
|
753
|
+
async function ensureFreshToken() {
|
|
754
|
+
if (refreshPromise) {
|
|
755
|
+
return refreshPromise;
|
|
756
|
+
}
|
|
757
|
+
refreshPromise = (async () => {
|
|
758
|
+
const { refresh_token } = await getToken();
|
|
759
|
+
if (!refresh_token) {
|
|
760
|
+
logout();
|
|
761
|
+
const err = new Error("No refresh token available");
|
|
762
|
+
err.name = "ApiError";
|
|
763
|
+
err.statusCode = 401;
|
|
764
|
+
throw err;
|
|
765
|
+
}
|
|
766
|
+
const refRes = await fetch(getRefreshEndpoint(), {
|
|
767
|
+
method: "POST",
|
|
768
|
+
headers: { Accept: "application/json", "Content-Type": "application/json" },
|
|
769
|
+
body: JSON.stringify({ refresh_token })
|
|
770
|
+
});
|
|
771
|
+
if (!refRes.ok) {
|
|
772
|
+
logout();
|
|
773
|
+
const err = new Error("Token refresh failed");
|
|
774
|
+
err.name = "ApiError";
|
|
775
|
+
err.statusCode = 401;
|
|
776
|
+
throw err;
|
|
777
|
+
}
|
|
778
|
+
const data = await refRes.json();
|
|
779
|
+
setToken(data);
|
|
780
|
+
})().finally(() => {
|
|
781
|
+
refreshPromise = null;
|
|
782
|
+
});
|
|
783
|
+
return refreshPromise;
|
|
784
|
+
}
|
|
785
|
+
async function request(method, path, body, options) {
|
|
786
|
+
if (options?.transformRequest) {
|
|
787
|
+
body = options.transformRequest(body);
|
|
788
|
+
}
|
|
789
|
+
const headers = {
|
|
790
|
+
Accept: "application/json",
|
|
791
|
+
"Content-Type": "application/json",
|
|
792
|
+
...options?.headers || {}
|
|
793
|
+
};
|
|
794
|
+
let { access_token, access_expires_at } = await getToken();
|
|
795
|
+
const nowSec = Date.now() / 1e3;
|
|
796
|
+
if (access_expires_at && nowSec > access_expires_at) {
|
|
797
|
+
await ensureFreshToken();
|
|
798
|
+
const tokens = await getToken();
|
|
799
|
+
access_token = tokens.access_token;
|
|
800
|
+
}
|
|
801
|
+
if (access_token) {
|
|
802
|
+
headers["Authorization"] = `Bearer ${access_token}`;
|
|
803
|
+
}
|
|
804
|
+
const finalPath = options?.params ? path + buildQueryString(options.params) : path;
|
|
805
|
+
const fetchOpts = { method, headers };
|
|
806
|
+
if (!["GET", "DELETE"].includes(method) && body !== void 0) {
|
|
807
|
+
fetchOpts.body = JSON.stringify(body);
|
|
808
|
+
}
|
|
809
|
+
const fullUrl = `${cfg.baseUrl}${finalPath}`;
|
|
810
|
+
let res;
|
|
811
|
+
let data;
|
|
812
|
+
const startedAt = Date.now();
|
|
813
|
+
try {
|
|
814
|
+
res = await fetch(fullUrl, fetchOpts);
|
|
815
|
+
} catch (error) {
|
|
816
|
+
const err = new Error(error instanceof Error ? error.message : "Network request failed");
|
|
817
|
+
err.name = "NetworkError";
|
|
818
|
+
err.method = method;
|
|
819
|
+
err.url = fullUrl;
|
|
820
|
+
if (options?.onError && method !== "GET") {
|
|
821
|
+
Promise.resolve(
|
|
822
|
+
options.onError({ error: err, method, url: fullUrl, aborted: false })
|
|
823
|
+
).catch(() => {
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
throw err;
|
|
827
|
+
}
|
|
828
|
+
if (res.status === 401 && !options?.["_retried"]) {
|
|
829
|
+
try {
|
|
830
|
+
await ensureFreshToken();
|
|
831
|
+
const tokens = await getToken();
|
|
832
|
+
headers["Authorization"] = `Bearer ${tokens.access_token}`;
|
|
833
|
+
fetchOpts.headers = headers;
|
|
834
|
+
return request(method, path, body, { ...options, _retried: true });
|
|
835
|
+
} catch (refreshError) {
|
|
836
|
+
throw refreshError;
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
try {
|
|
840
|
+
const contentLength = res.headers.get("content-length");
|
|
841
|
+
const contentType = res.headers.get("content-type");
|
|
842
|
+
if (res.status === 204 || contentLength === "0" || !contentType?.includes("application/json")) {
|
|
843
|
+
data = {};
|
|
844
|
+
} else {
|
|
845
|
+
data = await res.json();
|
|
846
|
+
}
|
|
847
|
+
} catch (error) {
|
|
848
|
+
const err = new Error("Failed to parse response");
|
|
849
|
+
err.name = "ParseError";
|
|
850
|
+
err.method = method;
|
|
851
|
+
err.url = fullUrl;
|
|
852
|
+
err.status = res.status;
|
|
853
|
+
if (options?.onError && method !== "GET") {
|
|
854
|
+
Promise.resolve(
|
|
855
|
+
options.onError({ error: err, method, url: fullUrl, status: res.status })
|
|
856
|
+
).catch(() => {
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
throw err;
|
|
860
|
+
}
|
|
861
|
+
if (!res.ok) {
|
|
862
|
+
const serverErr = data;
|
|
863
|
+
const reqErr = convertServerErrorToRequestError(serverErr);
|
|
864
|
+
const err = new Error(serverErr.message || "Request failed");
|
|
865
|
+
err.name = "ApiError";
|
|
866
|
+
err.statusCode = serverErr.statusCode || res.status;
|
|
867
|
+
err.validationErrors = reqErr.validationErrors;
|
|
868
|
+
err.method = method;
|
|
869
|
+
err.url = fullUrl;
|
|
870
|
+
const requestId = res.headers.get("x-request-id") || res.headers.get("request-id");
|
|
871
|
+
if (requestId) err.requestId = requestId;
|
|
872
|
+
if (options?.onError && method !== "GET") {
|
|
873
|
+
Promise.resolve(
|
|
874
|
+
options.onError({
|
|
875
|
+
error: err,
|
|
876
|
+
method,
|
|
877
|
+
url: fullUrl,
|
|
878
|
+
status: res.status,
|
|
879
|
+
response: serverErr,
|
|
880
|
+
request_id: requestId || null,
|
|
881
|
+
duration_ms: Date.now() - startedAt
|
|
882
|
+
})
|
|
883
|
+
).catch(() => {
|
|
884
|
+
});
|
|
885
|
+
}
|
|
886
|
+
throw err;
|
|
887
|
+
}
|
|
888
|
+
if (options?.onSuccess && method !== "GET") {
|
|
889
|
+
const requestId = res.headers.get("x-request-id") || res.headers.get("request-id");
|
|
890
|
+
Promise.resolve(
|
|
891
|
+
options.onSuccess({
|
|
892
|
+
data,
|
|
893
|
+
method,
|
|
894
|
+
url: fullUrl,
|
|
895
|
+
status: res.status,
|
|
896
|
+
request_id: requestId || null,
|
|
897
|
+
duration_ms: Date.now() - startedAt
|
|
898
|
+
})
|
|
899
|
+
).catch(() => {
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
return data;
|
|
903
|
+
}
|
|
904
|
+
return {
|
|
905
|
+
get: (path, opts) => request("GET", path, void 0, opts),
|
|
906
|
+
post: (path, body, opts) => request("POST", path, body, opts),
|
|
907
|
+
put: (path, body, opts) => request("PUT", path, body, opts),
|
|
908
|
+
patch: (path, body, opts) => request("PATCH", path, body, opts),
|
|
909
|
+
delete: (path, opts) => request("DELETE", path, void 0, opts)
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
// src/utils/price.ts
|
|
914
|
+
function formatCurrency(amount, currencyCode, locale = "en") {
|
|
915
|
+
if (!currencyCode) return "";
|
|
916
|
+
return new Intl.NumberFormat(locale, {
|
|
917
|
+
style: "currency",
|
|
918
|
+
currency: currencyCode.toUpperCase()
|
|
919
|
+
}).format(amount);
|
|
920
|
+
}
|
|
921
|
+
function getMinorUnits(currency) {
|
|
922
|
+
try {
|
|
923
|
+
const formatter = new Intl.NumberFormat("en", {
|
|
924
|
+
style: "currency",
|
|
925
|
+
currency: currency.toUpperCase()
|
|
926
|
+
});
|
|
927
|
+
const parts = formatter.formatToParts(1.11);
|
|
928
|
+
const fractionPart = parts.find((p) => p.type === "fraction");
|
|
929
|
+
return fractionPart?.value.length ?? 2;
|
|
930
|
+
} catch {
|
|
931
|
+
return 2;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
function convertToMajor(minorAmount, currency) {
|
|
935
|
+
const units = getMinorUnits(currency);
|
|
936
|
+
return minorAmount / Math.pow(10, units);
|
|
937
|
+
}
|
|
938
|
+
function getCurrencySymbol(currency) {
|
|
939
|
+
try {
|
|
940
|
+
return new Intl.NumberFormat("en", {
|
|
941
|
+
style: "currency",
|
|
942
|
+
currency: currency.toUpperCase(),
|
|
943
|
+
currencyDisplay: "narrowSymbol"
|
|
944
|
+
}).formatToParts(0).find((p) => p.type === "currency")?.value || currency.toUpperCase();
|
|
945
|
+
} catch {
|
|
946
|
+
return currency.toUpperCase();
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
function getCurrencyName(currency) {
|
|
950
|
+
try {
|
|
951
|
+
return new Intl.DisplayNames(["en"], { type: "currency" }).of(currency.toUpperCase()) || currency.toUpperCase();
|
|
952
|
+
} catch {
|
|
953
|
+
return currency.toUpperCase();
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
function formatMinor(amountMinor, currency) {
|
|
957
|
+
if (!currency) return "";
|
|
958
|
+
return formatCurrency(convertToMajor(amountMinor, currency), currency);
|
|
959
|
+
}
|
|
960
|
+
function formatPayment(payment) {
|
|
961
|
+
return formatMinor(payment.total, payment.currency);
|
|
962
|
+
}
|
|
963
|
+
function formatPrice(prices, marketId) {
|
|
964
|
+
if (!prices || prices.length === 0) return "";
|
|
965
|
+
const price = marketId ? prices.find((p) => p.market === marketId) || prices[0] : prices[0];
|
|
966
|
+
if (!price) return "";
|
|
967
|
+
return formatMinor(price.amount, price.currency);
|
|
968
|
+
}
|
|
969
|
+
function getPriceAmount(prices, marketId) {
|
|
970
|
+
if (!prices || prices.length === 0) return 0;
|
|
971
|
+
const price = prices.find((p) => p.market === marketId) || prices[0];
|
|
972
|
+
return price?.amount || 0;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
// src/utils/validation.ts
|
|
976
|
+
function validatePhoneNumber(phone) {
|
|
977
|
+
if (!phone) {
|
|
978
|
+
return { isValid: false, error: "Phone number is required" };
|
|
979
|
+
}
|
|
980
|
+
const cleaned = phone.replace(/\D/g, "");
|
|
981
|
+
if (cleaned.length < 8) {
|
|
982
|
+
return { isValid: false, error: "Phone number is too short" };
|
|
983
|
+
}
|
|
984
|
+
if (cleaned.length > 15) {
|
|
985
|
+
return { isValid: false, error: "Phone number is too long" };
|
|
986
|
+
}
|
|
987
|
+
return { isValid: true };
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
// src/utils/timezone.ts
|
|
991
|
+
var tzGroups = [
|
|
992
|
+
{
|
|
993
|
+
label: "US",
|
|
994
|
+
zones: [
|
|
995
|
+
{ label: "Eastern Time", value: "America/New_York" },
|
|
996
|
+
{ label: "Central Time", value: "America/Chicago" },
|
|
997
|
+
{ label: "Mountain Time", value: "America/Denver" },
|
|
998
|
+
{ label: "Pacific Time", value: "America/Los_Angeles" }
|
|
999
|
+
]
|
|
1000
|
+
},
|
|
1001
|
+
{
|
|
1002
|
+
label: "Europe",
|
|
1003
|
+
zones: [
|
|
1004
|
+
{ label: "London", value: "Europe/London" },
|
|
1005
|
+
{ label: "Paris", value: "Europe/Paris" },
|
|
1006
|
+
{ label: "Berlin", value: "Europe/Berlin" },
|
|
1007
|
+
{ label: "Rome", value: "Europe/Rome" }
|
|
1008
|
+
]
|
|
1009
|
+
},
|
|
1010
|
+
{
|
|
1011
|
+
label: "Asia",
|
|
1012
|
+
zones: [
|
|
1013
|
+
{ label: "Tokyo", value: "Asia/Tokyo" },
|
|
1014
|
+
{ label: "Shanghai", value: "Asia/Shanghai" },
|
|
1015
|
+
{ label: "Mumbai", value: "Asia/Kolkata" },
|
|
1016
|
+
{ label: "Dubai", value: "Asia/Dubai" }
|
|
1017
|
+
]
|
|
1018
|
+
}
|
|
1019
|
+
];
|
|
1020
|
+
function findTimeZone(groups) {
|
|
1021
|
+
try {
|
|
1022
|
+
const detected = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
1023
|
+
for (const group of groups) {
|
|
1024
|
+
for (const zone of group.zones) {
|
|
1025
|
+
if (zone.value === detected) {
|
|
1026
|
+
return detected;
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
return "UTC";
|
|
1031
|
+
} catch (e) {
|
|
1032
|
+
return "UTC";
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
// src/utils/text.ts
|
|
1037
|
+
var locales = ["en", "sr-latn"];
|
|
1038
|
+
var localeMap = {
|
|
1039
|
+
"en": "en-US",
|
|
1040
|
+
"sr-latn": "sr-RS"
|
|
1041
|
+
};
|
|
1042
|
+
function slugify(text) {
|
|
1043
|
+
return text.toString().toLowerCase().replace(/\s+/g, "-").replace(/[^\w-]+/g, "").replace(/--+/g, "-").replace(/^-+/, "").replace(/-+$/, "");
|
|
1044
|
+
}
|
|
1045
|
+
function humanize(text) {
|
|
1046
|
+
const slugifiedText = slugify(text);
|
|
1047
|
+
return slugifiedText.replace(/-/g, " ").replace(
|
|
1048
|
+
/\w\S*/g,
|
|
1049
|
+
(w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()
|
|
1050
|
+
);
|
|
1051
|
+
}
|
|
1052
|
+
function categorify(text) {
|
|
1053
|
+
const slugifiedText = slugify(text);
|
|
1054
|
+
return slugifiedText.replace(/-/g, " ").toUpperCase();
|
|
1055
|
+
}
|
|
1056
|
+
function formatDate(date, locale) {
|
|
1057
|
+
let localeString = "en-US";
|
|
1058
|
+
if (locales.includes(locale)) {
|
|
1059
|
+
localeString = localeMap[locale];
|
|
1060
|
+
}
|
|
1061
|
+
return new Date(date).toLocaleDateString(localeString, {
|
|
1062
|
+
timeZone: "UTC",
|
|
1063
|
+
year: "numeric",
|
|
1064
|
+
month: "short",
|
|
1065
|
+
day: "numeric"
|
|
1066
|
+
});
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
// src/utils/svg.ts
|
|
1070
|
+
async function fetchSvgContent(mediaObject) {
|
|
1071
|
+
if (!mediaObject) return null;
|
|
1072
|
+
const svgUrl = getImageUrl(mediaObject, false);
|
|
1073
|
+
try {
|
|
1074
|
+
const response = await fetch(svgUrl);
|
|
1075
|
+
if (!response.ok) {
|
|
1076
|
+
console.error(`Failed to fetch SVG: ${response.status} ${response.statusText}`);
|
|
1077
|
+
return null;
|
|
1078
|
+
}
|
|
1079
|
+
const svgContent = await response.text();
|
|
1080
|
+
return svgContent;
|
|
1081
|
+
} catch (error) {
|
|
1082
|
+
console.error("Error fetching SVG:", error);
|
|
1083
|
+
return null;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
async function getSvgContentForAstro(mediaObject) {
|
|
1087
|
+
try {
|
|
1088
|
+
const svgContent = await fetchSvgContent(mediaObject);
|
|
1089
|
+
return svgContent || "";
|
|
1090
|
+
} catch (error) {
|
|
1091
|
+
console.error("Error getting SVG content for Astro:", error);
|
|
1092
|
+
return "";
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
async function injectSvgIntoElement(mediaObject, targetElement, className) {
|
|
1096
|
+
if (!targetElement) return;
|
|
1097
|
+
try {
|
|
1098
|
+
const svgContent = await fetchSvgContent(mediaObject);
|
|
1099
|
+
if (svgContent) {
|
|
1100
|
+
targetElement.innerHTML = svgContent;
|
|
1101
|
+
if (className) {
|
|
1102
|
+
const svgElement = targetElement.querySelector("svg");
|
|
1103
|
+
if (svgElement) {
|
|
1104
|
+
svgElement.classList.add(...className.split(" "));
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
} catch (error) {
|
|
1109
|
+
console.error("Error injecting SVG:", error);
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
// src/utils/keyValidation.ts
|
|
1114
|
+
var KEY_PATTERN = /^[a-zA-Z0-9_-]+$/;
|
|
1115
|
+
function isValidKey(key) {
|
|
1116
|
+
if (!key || key.length === 0) return false;
|
|
1117
|
+
return KEY_PATTERN.test(key);
|
|
1118
|
+
}
|
|
1119
|
+
function validateKey(key) {
|
|
1120
|
+
if (!key || key.length === 0) {
|
|
1121
|
+
return { valid: false, error: "Key is required" };
|
|
1122
|
+
}
|
|
1123
|
+
if (key.length > 255) {
|
|
1124
|
+
return { valid: false, error: "Key must be 255 characters or less" };
|
|
1125
|
+
}
|
|
1126
|
+
if (!KEY_PATTERN.test(key)) {
|
|
1127
|
+
return {
|
|
1128
|
+
valid: false,
|
|
1129
|
+
error: "Key can only contain letters, numbers, underscores (_) and hyphens (-)"
|
|
1130
|
+
};
|
|
1131
|
+
}
|
|
1132
|
+
return { valid: true };
|
|
1133
|
+
}
|
|
1134
|
+
function toKey(input) {
|
|
1135
|
+
if (!input) return "";
|
|
1136
|
+
return input.toLowerCase().trim().replace(/\s+/g, "_").replace(/[^a-z0-9_-]/g, "").replace(/^[-_]+|[-_]+$/g, "").replace(/[-_]{2,}/g, "_");
|
|
1137
|
+
}
|
|
1138
|
+
function nameToKey(name) {
|
|
1139
|
+
return toKey(name);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
// src/utils/inventory.ts
|
|
1143
|
+
function getAvailableStock(variant) {
|
|
1144
|
+
if (!variant?.inventory) return 0;
|
|
1145
|
+
return variant.inventory.reduce((sum, inv) => sum + inv.available, 0);
|
|
1146
|
+
}
|
|
1147
|
+
function getReservedStock(variant) {
|
|
1148
|
+
if (!variant?.inventory) return 0;
|
|
1149
|
+
return variant.inventory.reduce((sum, inv) => sum + inv.reserved, 0);
|
|
1150
|
+
}
|
|
1151
|
+
function hasStock(variant, quantity = 1) {
|
|
1152
|
+
return getAvailableStock(variant) >= quantity;
|
|
1153
|
+
}
|
|
1154
|
+
function getInventoryAt(variant, locationId) {
|
|
1155
|
+
return variant?.inventory?.find((inv) => inv.location_id === locationId);
|
|
1156
|
+
}
|
|
1157
|
+
function getFirstAvailableFCId(variant, quantity = 1) {
|
|
1158
|
+
const inv = variant?.inventory?.find((i) => i.available >= quantity);
|
|
1159
|
+
return inv?.location_id;
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
// src/index.ts
|
|
1163
|
+
function createUtilitySurface(apiConfig) {
|
|
1164
|
+
return {
|
|
1165
|
+
getImageUrl: (imageBlock, isBlock = true) => getImageUrl(imageBlock, isBlock),
|
|
1166
|
+
getBlockValue,
|
|
1167
|
+
getBlockTextValue,
|
|
1168
|
+
getBlockValues,
|
|
1169
|
+
getBlockLabel,
|
|
1170
|
+
getBlockObjectValues,
|
|
1171
|
+
getBlockFromArray,
|
|
1172
|
+
formatBlockValue,
|
|
1173
|
+
prepareBlocksForSubmission,
|
|
1174
|
+
extractBlockValues,
|
|
1175
|
+
formatPrice: (prices) => formatPrice(prices, apiConfig.market),
|
|
1176
|
+
getPriceAmount: (prices) => getPriceAmount(prices, apiConfig.market),
|
|
1177
|
+
formatPayment,
|
|
1178
|
+
formatMinor,
|
|
1179
|
+
getCurrencySymbol,
|
|
1180
|
+
getCurrencyName,
|
|
1181
|
+
validatePhoneNumber,
|
|
1182
|
+
tzGroups,
|
|
1183
|
+
findTimeZone,
|
|
1184
|
+
slugify,
|
|
1185
|
+
humanize,
|
|
1186
|
+
categorify,
|
|
1187
|
+
formatDate,
|
|
1188
|
+
getSvgContentForAstro,
|
|
1189
|
+
fetchSvgContent,
|
|
1190
|
+
injectSvgIntoElement,
|
|
1191
|
+
isValidKey,
|
|
1192
|
+
validateKey,
|
|
1193
|
+
toKey,
|
|
1194
|
+
nameToKey,
|
|
1195
|
+
getAvailableStock,
|
|
1196
|
+
getReservedStock,
|
|
1197
|
+
hasStock,
|
|
1198
|
+
getInventoryAt,
|
|
1199
|
+
getFirstAvailableFCId
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
function createStorefront(config) {
|
|
1203
|
+
const locale = config.locale || "en";
|
|
1204
|
+
const market = config.market || "";
|
|
1205
|
+
const getToken = config.getToken || defaultGetToken;
|
|
1206
|
+
const setToken = config.setToken || defaultSetToken;
|
|
1207
|
+
const logout = config.logout || defaultLogout;
|
|
1208
|
+
const isAuthenticated = config.isAuthenticated || defaultIsAuthenticated;
|
|
1209
|
+
let refresh_store_id = config.storeId;
|
|
1210
|
+
const httpClient = createHttpClient({
|
|
1211
|
+
...config,
|
|
1212
|
+
getToken,
|
|
1213
|
+
setToken,
|
|
1214
|
+
logout,
|
|
1215
|
+
refreshPath: () => `/v1/storefront/${refresh_store_id}/customers/auth/refresh`
|
|
1216
|
+
});
|
|
1217
|
+
const apiConfig = {
|
|
1218
|
+
httpClient,
|
|
1219
|
+
storeId: config.storeId,
|
|
1220
|
+
baseUrl: config.baseUrl,
|
|
1221
|
+
market,
|
|
1222
|
+
locale,
|
|
1223
|
+
setToken,
|
|
1224
|
+
getToken
|
|
1225
|
+
};
|
|
1226
|
+
const storefrontApi = createStorefrontApi(apiConfig);
|
|
1227
|
+
let sessionPromise = null;
|
|
1228
|
+
let currentSession = null;
|
|
1229
|
+
const sessionListeners = /* @__PURE__ */ new Set();
|
|
1230
|
+
const customerApi = storefrontApi.crm.customer;
|
|
1231
|
+
function emitSessionChange(session2) {
|
|
1232
|
+
for (const listener of sessionListeners) {
|
|
1233
|
+
Promise.resolve().then(() => listener(session2)).catch(() => {
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
function setCurrentSessionFromResult(result) {
|
|
1238
|
+
const s = {
|
|
1239
|
+
customer: result.customer,
|
|
1240
|
+
store: result.store,
|
|
1241
|
+
market: result.market || null
|
|
1242
|
+
};
|
|
1243
|
+
currentSession = s;
|
|
1244
|
+
emitSessionChange(s);
|
|
1245
|
+
return s;
|
|
1246
|
+
}
|
|
1247
|
+
function clearSession() {
|
|
1248
|
+
sessionPromise = null;
|
|
1249
|
+
currentSession = null;
|
|
1250
|
+
emitSessionChange(null);
|
|
1251
|
+
}
|
|
1252
|
+
async function invalidateAfterAuth(promise) {
|
|
1253
|
+
const result = await promise;
|
|
1254
|
+
clearSession();
|
|
1255
|
+
return result;
|
|
1256
|
+
}
|
|
1257
|
+
function session(market2) {
|
|
1258
|
+
if (market2 !== void 0) apiConfig.market = market2;
|
|
1259
|
+
if (sessionPromise) return sessionPromise;
|
|
1260
|
+
sessionPromise = (async () => {
|
|
1261
|
+
try {
|
|
1262
|
+
const result = await customerApi.session({
|
|
1263
|
+
market: apiConfig.market
|
|
1264
|
+
});
|
|
1265
|
+
return setCurrentSessionFromResult(result);
|
|
1266
|
+
} catch (err) {
|
|
1267
|
+
const status = err?.statusCode || err?.status || err?.response?.status;
|
|
1268
|
+
if (status === 401) {
|
|
1269
|
+
currentSession = null;
|
|
1270
|
+
emitSessionChange(null);
|
|
1271
|
+
await setToken({ access_token: "", refresh_token: "", access_expires_at: 0 });
|
|
1272
|
+
const result = await customerApi.session({
|
|
1273
|
+
market: apiConfig.market
|
|
1274
|
+
});
|
|
1275
|
+
return setCurrentSessionFromResult(result);
|
|
1276
|
+
}
|
|
1277
|
+
throw err;
|
|
1278
|
+
}
|
|
1279
|
+
})().catch((err) => {
|
|
1280
|
+
sessionPromise = null;
|
|
1281
|
+
throw err;
|
|
1282
|
+
});
|
|
1283
|
+
return sessionPromise;
|
|
1284
|
+
}
|
|
1285
|
+
function setMarket(key) {
|
|
1286
|
+
apiConfig.market = key;
|
|
1287
|
+
clearSession();
|
|
1288
|
+
}
|
|
1289
|
+
function onSessionChange(listener) {
|
|
1290
|
+
sessionListeners.add(listener);
|
|
1291
|
+
if (currentSession) {
|
|
1292
|
+
Promise.resolve().then(() => listener(currentSession)).catch(() => {
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
return () => {
|
|
1296
|
+
sessionListeners.delete(listener);
|
|
1297
|
+
};
|
|
1298
|
+
}
|
|
1299
|
+
function getSession() {
|
|
1300
|
+
return currentSession;
|
|
1301
|
+
}
|
|
1302
|
+
function logoutAndClearSession() {
|
|
1303
|
+
clearSession();
|
|
1304
|
+
return logout();
|
|
1305
|
+
}
|
|
1306
|
+
function setTokenAndClearSession(tokens) {
|
|
1307
|
+
setToken(tokens);
|
|
1308
|
+
clearSession();
|
|
1309
|
+
}
|
|
1310
|
+
const crm = {
|
|
1311
|
+
...storefrontApi.crm,
|
|
1312
|
+
customer: {
|
|
1313
|
+
...customerApi,
|
|
1314
|
+
async session(params, options) {
|
|
1315
|
+
const result = await customerApi.session(params, options);
|
|
1316
|
+
setCurrentSessionFromResult(result);
|
|
1317
|
+
return result;
|
|
1318
|
+
},
|
|
1319
|
+
connect: (params, options) => invalidateAfterAuth(customerApi.connect(params, options)),
|
|
1320
|
+
verify: (params, options) => invalidateAfterAuth(customerApi.verify(params, options))
|
|
1321
|
+
}
|
|
1322
|
+
};
|
|
1323
|
+
return {
|
|
1324
|
+
session,
|
|
1325
|
+
getSession,
|
|
1326
|
+
onSessionChange,
|
|
1327
|
+
store: storefrontApi.store,
|
|
1328
|
+
cms: storefrontApi.cms,
|
|
1329
|
+
eshop: storefrontApi.eshop,
|
|
1330
|
+
booking: storefrontApi.booking,
|
|
1331
|
+
crm,
|
|
1332
|
+
activity: storefrontApi.activity,
|
|
1333
|
+
automation: storefrontApi.automation,
|
|
1334
|
+
setStoreId: (storeId) => {
|
|
1335
|
+
refresh_store_id = storeId;
|
|
1336
|
+
apiConfig.storeId = storeId;
|
|
1337
|
+
clearSession();
|
|
1338
|
+
},
|
|
1339
|
+
getStoreId: () => apiConfig.storeId,
|
|
1340
|
+
setMarket,
|
|
1341
|
+
getMarket: () => apiConfig.market,
|
|
1342
|
+
setLocale: (locale2) => {
|
|
1343
|
+
apiConfig.locale = locale2;
|
|
1344
|
+
},
|
|
1345
|
+
getLocale: () => apiConfig.locale,
|
|
1346
|
+
isAuthenticated,
|
|
1347
|
+
logout: logoutAndClearSession,
|
|
1348
|
+
setToken: setTokenAndClearSession,
|
|
1349
|
+
extractBlockValues,
|
|
1350
|
+
utils: createUtilitySurface(apiConfig)
|
|
1351
|
+
};
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
export { COMMON_ACTIVITY_TYPES, createStorefront };
|
|
1355
|
+
//# sourceMappingURL=storefront.js.map
|
|
1356
|
+
//# sourceMappingURL=storefront.js.map
|