arky-sdk 0.9.19 → 0.10.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/README.md +171 -105
- package/dist/admin.cjs +55 -19
- package/dist/admin.cjs.map +1 -1
- package/dist/admin.d.cts +5 -3
- package/dist/admin.d.ts +5 -3
- package/dist/admin.js +55 -19
- package/dist/admin.js.map +1 -1
- package/dist/{api-CuqZQQGv.d.cts → index-B0Pd1s-2.d.cts} +4457 -4455
- package/dist/{api-CuqZQQGv.d.ts → index-B0Pd1s-2.d.ts} +4457 -4455
- package/dist/index.cjs +2290 -524
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3828 -3
- package/dist/index.d.ts +3828 -3
- package/dist/index.js +2286 -525
- package/dist/index.js.map +1 -1
- package/dist/{inventory-DNX8Vg-N.d.ts → inventory-C2EZEnV9.d.ts} +1 -1
- package/dist/{inventory-DrVRUkE2.d.cts → inventory-t9OBR0CC.d.cts} +1 -1
- package/dist/storefront.cjs +650 -678
- package/dist/storefront.cjs.map +1 -1
- package/dist/storefront.d.cts +5 -512
- package/dist/storefront.d.ts +5 -512
- package/dist/storefront.js +650 -678
- package/dist/storefront.js.map +1 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils.d.cts +2 -2
- package/dist/utils.d.ts +2 -2
- package/package.json +1 -1
- package/dist/admin-CgER0AZn.d.cts +0 -1543
- package/dist/admin-CqasPNLl.d.ts +0 -1543
package/dist/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { atom, computed, map } from 'nanostores';
|
|
2
|
+
|
|
1
3
|
// src/types/index.ts
|
|
2
4
|
var PaymentMethodType = /* @__PURE__ */ ((PaymentMethodType2) => {
|
|
3
5
|
PaymentMethodType2["Cash"] = "cash";
|
|
@@ -45,70 +47,50 @@ var COMMON_ACTION_KEYS = [
|
|
|
45
47
|
"share",
|
|
46
48
|
"wishlist.added"
|
|
47
49
|
];
|
|
48
|
-
var createActionApi = (apiConfig) => ({
|
|
50
|
+
var createActionApi = (apiConfig, lifecycle) => ({
|
|
49
51
|
COMMON_ACTION_KEYS,
|
|
50
52
|
async track(params) {
|
|
53
|
+
await lifecycle.ensureVisitorSession();
|
|
51
54
|
try {
|
|
52
|
-
await apiConfig.httpClient.post(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
);
|
|
55
|
+
await apiConfig.httpClient.post("/v1/storefront/actions/track", {
|
|
56
|
+
key: params.key,
|
|
57
|
+
payload: params.payload
|
|
58
|
+
});
|
|
56
59
|
} catch {
|
|
57
60
|
}
|
|
58
61
|
}
|
|
59
62
|
});
|
|
60
|
-
var createStorefrontApi = (apiConfig, updateContactSession) => {
|
|
61
|
-
const base =
|
|
62
|
-
const pendingVerifications = /* @__PURE__ */ new Map();
|
|
63
|
+
var createStorefrontApi = (apiConfig, updateContactSession, lifecycle) => {
|
|
64
|
+
const base = "/v1/storefront";
|
|
63
65
|
function persistIdentification(result) {
|
|
64
|
-
const
|
|
65
|
-
if (
|
|
66
|
+
const sessionToken = result.token?.token;
|
|
67
|
+
if (sessionToken) {
|
|
66
68
|
updateContactSession(() => ({
|
|
67
|
-
|
|
68
|
-
contact: result.contact
|
|
69
|
-
store: result.store,
|
|
70
|
-
market: result.market
|
|
69
|
+
sessionToken,
|
|
70
|
+
contact: result.contact
|
|
71
71
|
}));
|
|
72
72
|
} else {
|
|
73
|
-
if (result.verification_challenge) {
|
|
74
|
-
pendingVerifications.set(result.verification_challenge.challenge_id, {
|
|
75
|
-
store: result.store,
|
|
76
|
-
market: result.market
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
73
|
updateContactSession(
|
|
80
|
-
(current) => current ? {
|
|
81
|
-
...current,
|
|
82
|
-
contact: result.contact,
|
|
83
|
-
store: result.store,
|
|
84
|
-
market: result.market
|
|
85
|
-
} : null
|
|
74
|
+
(current) => current ? { ...current, contact: result.contact } : null
|
|
86
75
|
);
|
|
87
76
|
}
|
|
88
77
|
return result;
|
|
89
78
|
}
|
|
90
79
|
async function submitIdentification(path, params, options) {
|
|
91
|
-
const store_id = apiConfig.storeId;
|
|
92
80
|
const result = await apiConfig.httpClient.post(
|
|
93
|
-
`${base
|
|
94
|
-
{
|
|
95
|
-
store_id,
|
|
96
|
-
market: params?.market || apiConfig.market || null,
|
|
97
|
-
email: params?.email
|
|
98
|
-
},
|
|
81
|
+
`${base}/account/${path}`,
|
|
82
|
+
{ email: params?.email },
|
|
99
83
|
options
|
|
100
84
|
);
|
|
101
85
|
return persistIdentification(result);
|
|
102
86
|
}
|
|
103
87
|
return {
|
|
104
88
|
store: {
|
|
105
|
-
|
|
106
|
-
return apiConfig.httpClient.get(base(), options);
|
|
107
|
-
},
|
|
89
|
+
getSetup: lifecycle.getSetup,
|
|
108
90
|
location: {
|
|
109
91
|
getCountries(options) {
|
|
110
92
|
return apiConfig.httpClient.get(
|
|
111
|
-
|
|
93
|
+
"/v1/platform/countries",
|
|
112
94
|
options
|
|
113
95
|
);
|
|
114
96
|
},
|
|
@@ -120,13 +102,13 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
|
|
|
120
102
|
},
|
|
121
103
|
list(options) {
|
|
122
104
|
return apiConfig.httpClient.get(
|
|
123
|
-
`${base
|
|
105
|
+
`${base}/locations`,
|
|
124
106
|
options
|
|
125
107
|
);
|
|
126
108
|
},
|
|
127
109
|
get(id, options) {
|
|
128
110
|
return apiConfig.httpClient.get(
|
|
129
|
-
`${base
|
|
111
|
+
`${base}/locations/${id}`,
|
|
130
112
|
options
|
|
131
113
|
);
|
|
132
114
|
}
|
|
@@ -134,13 +116,13 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
|
|
|
134
116
|
market: {
|
|
135
117
|
list(options) {
|
|
136
118
|
return apiConfig.httpClient.get(
|
|
137
|
-
`${base
|
|
119
|
+
`${base}/markets`,
|
|
138
120
|
options
|
|
139
121
|
);
|
|
140
122
|
},
|
|
141
123
|
get(id, options) {
|
|
142
124
|
return apiConfig.httpClient.get(
|
|
143
|
-
`${base
|
|
125
|
+
`${base}/markets/${id}`,
|
|
144
126
|
options
|
|
145
127
|
);
|
|
146
128
|
}
|
|
@@ -149,85 +131,59 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
|
|
|
149
131
|
cms: {
|
|
150
132
|
collection: {
|
|
151
133
|
get(params, options) {
|
|
152
|
-
const
|
|
153
|
-
const identifier = params.id !== void 0 ? params.id : `${store_id}:${params.key}`;
|
|
134
|
+
const identifier = params.id ?? params.key;
|
|
154
135
|
return apiConfig.httpClient.get(
|
|
155
|
-
`${base
|
|
136
|
+
`${base}/collections/${identifier}`,
|
|
156
137
|
options
|
|
157
138
|
);
|
|
158
139
|
}
|
|
159
140
|
},
|
|
160
141
|
entry: {
|
|
161
142
|
get(params, options) {
|
|
162
|
-
const store_id = params.store_id || apiConfig.storeId;
|
|
163
|
-
if (!params.id) {
|
|
164
|
-
throw new Error("GetEntryParams requires id");
|
|
165
|
-
}
|
|
166
143
|
return apiConfig.httpClient.get(
|
|
167
|
-
`${base
|
|
144
|
+
`${base}/entries/${params.id}`,
|
|
168
145
|
options
|
|
169
146
|
);
|
|
170
147
|
},
|
|
171
148
|
find(params, options) {
|
|
172
|
-
const { store_id, ...queryParams } = params;
|
|
173
149
|
return apiConfig.httpClient.get(
|
|
174
|
-
`${base
|
|
175
|
-
{
|
|
176
|
-
...options,
|
|
177
|
-
params: queryParams
|
|
178
|
-
}
|
|
150
|
+
`${base}/entries`,
|
|
151
|
+
{ ...options, params }
|
|
179
152
|
);
|
|
180
153
|
}
|
|
181
154
|
},
|
|
182
155
|
form: {
|
|
183
156
|
get(params, options) {
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
if (params.id) {
|
|
187
|
-
identifier = params.id;
|
|
188
|
-
} else if (params.key) {
|
|
189
|
-
identifier = `${store_id}:${params.key}`;
|
|
190
|
-
} else {
|
|
191
|
-
throw new Error("GetFormParams requires id or key");
|
|
192
|
-
}
|
|
157
|
+
const identifier = params.id ?? params.key;
|
|
158
|
+
if (!identifier) throw new Error("GetFormParams requires id or key");
|
|
193
159
|
return apiConfig.httpClient.get(
|
|
194
|
-
`${base
|
|
160
|
+
`${base}/forms/${identifier}`,
|
|
195
161
|
options
|
|
196
162
|
);
|
|
197
163
|
},
|
|
198
|
-
submit(params, options) {
|
|
199
|
-
|
|
200
|
-
const
|
|
201
|
-
if (!form_id)
|
|
202
|
-
throw new Error("SubmitFormParams requires form_id");
|
|
203
|
-
}
|
|
164
|
+
async submit(params, options) {
|
|
165
|
+
await lifecycle.ensureVisitorSession();
|
|
166
|
+
const { form_id, ...payload } = params;
|
|
167
|
+
if (!form_id) throw new Error("SubmitFormParams requires form_id");
|
|
204
168
|
return apiConfig.httpClient.post(
|
|
205
|
-
`${base
|
|
206
|
-
{ ...payload, form_id
|
|
169
|
+
`${base}/forms/${form_id}/submissions`,
|
|
170
|
+
{ ...payload, form_id },
|
|
207
171
|
options
|
|
208
172
|
);
|
|
209
173
|
}
|
|
210
174
|
},
|
|
211
175
|
taxonomy: {
|
|
212
176
|
get(params, options) {
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
if (params.id) {
|
|
216
|
-
identifier = params.id;
|
|
217
|
-
} else if (params.key) {
|
|
218
|
-
identifier = `${store_id}:${params.key}`;
|
|
219
|
-
} else {
|
|
220
|
-
throw new Error("GetTaxonomyParams requires id or key");
|
|
221
|
-
}
|
|
177
|
+
const identifier = params.id ?? params.key;
|
|
178
|
+
if (!identifier) throw new Error("GetTaxonomyParams requires id or key");
|
|
222
179
|
return apiConfig.httpClient.get(
|
|
223
|
-
`${base
|
|
180
|
+
`${base}/taxonomies/${identifier}`,
|
|
224
181
|
options
|
|
225
182
|
);
|
|
226
183
|
},
|
|
227
184
|
getChildren(params, options) {
|
|
228
|
-
const store_id = params.store_id || apiConfig.storeId;
|
|
229
185
|
return apiConfig.httpClient.get(
|
|
230
|
-
`${base
|
|
186
|
+
`${base}/taxonomies/${params.id}/children`,
|
|
231
187
|
options
|
|
232
188
|
);
|
|
233
189
|
}
|
|
@@ -236,113 +192,90 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
|
|
|
236
192
|
eshop: {
|
|
237
193
|
product: {
|
|
238
194
|
get(params, options) {
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
if (params.id) {
|
|
242
|
-
identifier = params.id;
|
|
243
|
-
} else if (params.slug) {
|
|
244
|
-
identifier = `${store_id}:${apiConfig.locale}:${params.slug}`;
|
|
245
|
-
} else {
|
|
246
|
-
throw new Error("GetProductParams requires id or slug");
|
|
247
|
-
}
|
|
195
|
+
const identifier = params.id ?? params.slug;
|
|
196
|
+
if (!identifier) throw new Error("GetProductParams requires id or slug");
|
|
248
197
|
return apiConfig.httpClient.get(
|
|
249
|
-
`${base
|
|
198
|
+
`${base}/products/${identifier}`,
|
|
250
199
|
options
|
|
251
200
|
);
|
|
252
201
|
},
|
|
253
202
|
find(params, options) {
|
|
254
|
-
const { store_id, ...queryParams } = params;
|
|
255
203
|
return apiConfig.httpClient.get(
|
|
256
|
-
`${base
|
|
257
|
-
{
|
|
258
|
-
...options,
|
|
259
|
-
params: queryParams
|
|
260
|
-
}
|
|
204
|
+
`${base}/products`,
|
|
205
|
+
{ ...options, params }
|
|
261
206
|
);
|
|
262
207
|
}
|
|
263
208
|
},
|
|
264
209
|
cart: {
|
|
265
|
-
current(
|
|
266
|
-
|
|
267
|
-
const { store_id: _store_id, ...payload } = params;
|
|
210
|
+
async current(options) {
|
|
211
|
+
await lifecycle.ensureVisitorSession();
|
|
268
212
|
return apiConfig.httpClient.post(
|
|
269
|
-
`${base
|
|
270
|
-
{
|
|
271
|
-
...payload,
|
|
272
|
-
store_id,
|
|
273
|
-
market: payload.market || apiConfig.market
|
|
274
|
-
},
|
|
213
|
+
`${base}/carts/current`,
|
|
214
|
+
{},
|
|
275
215
|
options
|
|
276
216
|
);
|
|
277
217
|
},
|
|
278
|
-
get(params, options) {
|
|
279
|
-
|
|
218
|
+
async get(params, options) {
|
|
219
|
+
await lifecycle.ensureVisitorSession();
|
|
280
220
|
return apiConfig.httpClient.get(
|
|
281
|
-
`${base
|
|
221
|
+
`${base}/carts/${params.id}`,
|
|
282
222
|
{
|
|
283
223
|
...options,
|
|
284
224
|
params: params.token ? { token: params.token } : options?.params
|
|
285
225
|
}
|
|
286
226
|
);
|
|
287
227
|
},
|
|
288
|
-
update(params, options) {
|
|
289
|
-
|
|
290
|
-
const
|
|
228
|
+
async update(params, options) {
|
|
229
|
+
await lifecycle.ensureVisitorSession();
|
|
230
|
+
const { items, ...payload } = params;
|
|
291
231
|
return apiConfig.httpClient.put(
|
|
292
|
-
`${base
|
|
232
|
+
`${base}/carts/${params.id}`,
|
|
293
233
|
{
|
|
294
234
|
...payload,
|
|
295
|
-
store_id: target,
|
|
296
235
|
...items ? { items: sanitizePublicCheckoutItems(items) } : {}
|
|
297
236
|
},
|
|
298
237
|
options
|
|
299
238
|
);
|
|
300
239
|
},
|
|
301
|
-
addItem(params, options) {
|
|
302
|
-
|
|
303
|
-
const
|
|
240
|
+
async addItem(params, options) {
|
|
241
|
+
await lifecycle.ensureVisitorSession();
|
|
242
|
+
const { item, ...payload } = params;
|
|
304
243
|
return apiConfig.httpClient.post(
|
|
305
|
-
`${base
|
|
306
|
-
{
|
|
307
|
-
...payload,
|
|
308
|
-
store_id: target,
|
|
309
|
-
item: sanitizePublicCheckoutItems([item])[0]
|
|
310
|
-
},
|
|
244
|
+
`${base}/carts/${params.id}/items`,
|
|
245
|
+
{ ...payload, item: sanitizePublicCheckoutItems([item])[0] },
|
|
311
246
|
options
|
|
312
247
|
);
|
|
313
248
|
},
|
|
314
|
-
removeItem(params, options) {
|
|
315
|
-
|
|
316
|
-
const target = store_id || apiConfig.storeId;
|
|
249
|
+
async removeItem(params, options) {
|
|
250
|
+
await lifecycle.ensureVisitorSession();
|
|
317
251
|
return apiConfig.httpClient.post(
|
|
318
|
-
`${base
|
|
319
|
-
|
|
252
|
+
`${base}/carts/${params.id}/items/remove`,
|
|
253
|
+
params,
|
|
320
254
|
options
|
|
321
255
|
);
|
|
322
256
|
},
|
|
323
|
-
clear(params, options) {
|
|
324
|
-
|
|
257
|
+
async clear(params, options) {
|
|
258
|
+
await lifecycle.ensureVisitorSession();
|
|
325
259
|
return apiConfig.httpClient.post(
|
|
326
|
-
`${base
|
|
327
|
-
{ id: params.id
|
|
260
|
+
`${base}/carts/${params.id}/clear`,
|
|
261
|
+
{ id: params.id },
|
|
328
262
|
options
|
|
329
263
|
);
|
|
330
264
|
},
|
|
331
|
-
quote(params, options) {
|
|
332
|
-
|
|
265
|
+
async quote(params, options) {
|
|
266
|
+
await lifecycle.ensureVisitorSession();
|
|
333
267
|
return apiConfig.httpClient.post(
|
|
334
|
-
`${base
|
|
335
|
-
{ id: params.id
|
|
268
|
+
`${base}/carts/${params.id}/quote`,
|
|
269
|
+
{ id: params.id },
|
|
336
270
|
options
|
|
337
271
|
);
|
|
338
272
|
},
|
|
339
|
-
checkout(params, options) {
|
|
340
|
-
|
|
273
|
+
async checkout(params, options) {
|
|
274
|
+
await lifecycle.ensureVisitorSession();
|
|
341
275
|
return apiConfig.httpClient.post(
|
|
342
|
-
`${base
|
|
276
|
+
`${base}/carts/${params.id}/checkout`,
|
|
343
277
|
{
|
|
344
278
|
id: params.id,
|
|
345
|
-
store_id,
|
|
346
279
|
payment_method_key: params.payment_method_key,
|
|
347
280
|
confirmation_token_id: params.confirmation_token_id,
|
|
348
281
|
return_url: params.return_url
|
|
@@ -352,115 +285,85 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
|
|
|
352
285
|
}
|
|
353
286
|
},
|
|
354
287
|
order: {
|
|
355
|
-
get(params, options) {
|
|
356
|
-
|
|
288
|
+
async get(params, options) {
|
|
289
|
+
await lifecycle.ensureVisitorSession();
|
|
357
290
|
return apiConfig.httpClient.get(
|
|
358
|
-
`${base
|
|
291
|
+
`${base}/orders/${params.id}`,
|
|
359
292
|
options
|
|
360
293
|
);
|
|
361
294
|
},
|
|
362
|
-
find(params, options) {
|
|
363
|
-
|
|
295
|
+
async find(params, options) {
|
|
296
|
+
await lifecycle.ensureVisitorSession();
|
|
364
297
|
return apiConfig.httpClient.get(
|
|
365
|
-
`${base
|
|
366
|
-
{
|
|
367
|
-
...options,
|
|
368
|
-
params: queryParams
|
|
369
|
-
}
|
|
298
|
+
`${base}/orders`,
|
|
299
|
+
{ ...options, params }
|
|
370
300
|
);
|
|
371
301
|
},
|
|
372
|
-
downloadDigitalAccess(params, options) {
|
|
373
|
-
|
|
302
|
+
async downloadDigitalAccess(params, options) {
|
|
303
|
+
await lifecycle.ensureVisitorSession();
|
|
374
304
|
return apiConfig.httpClient.post(
|
|
375
|
-
`${base
|
|
305
|
+
`${base}/orders/${params.order_id}/digital-access/${params.grant_id}/download`,
|
|
376
306
|
{},
|
|
377
307
|
options
|
|
378
308
|
);
|
|
379
309
|
},
|
|
380
|
-
findDigitalAccess(params, options) {
|
|
381
|
-
|
|
310
|
+
async findDigitalAccess(params, options) {
|
|
311
|
+
await lifecycle.ensureVisitorSession();
|
|
312
|
+
const { order_id, ...queryParams } = params;
|
|
382
313
|
return apiConfig.httpClient.get(
|
|
383
|
-
`${base
|
|
314
|
+
`${base}/orders/${order_id}/digital-access`,
|
|
384
315
|
{ ...options, params: queryParams }
|
|
385
316
|
);
|
|
386
317
|
},
|
|
387
|
-
getDigitalAccess(params, options) {
|
|
388
|
-
|
|
318
|
+
async getDigitalAccess(params, options) {
|
|
319
|
+
await lifecycle.ensureVisitorSession();
|
|
389
320
|
return apiConfig.httpClient.get(
|
|
390
|
-
`${base
|
|
321
|
+
`${base}/orders/${params.order_id}/digital-access/${params.grant_id}`,
|
|
391
322
|
options
|
|
392
323
|
);
|
|
393
324
|
}
|
|
394
325
|
},
|
|
395
326
|
service: {
|
|
396
327
|
get(params, options) {
|
|
397
|
-
const
|
|
398
|
-
|
|
399
|
-
if (params.id) {
|
|
400
|
-
identifier = params.id;
|
|
401
|
-
} else if (params.slug) {
|
|
402
|
-
identifier = `${store_id}:${apiConfig.locale}:${params.slug}`;
|
|
403
|
-
} else {
|
|
404
|
-
throw new Error("GetServiceParams requires id or slug");
|
|
405
|
-
}
|
|
328
|
+
const identifier = params.id ?? params.slug;
|
|
329
|
+
if (!identifier) throw new Error("GetServiceParams requires id or slug");
|
|
406
330
|
return apiConfig.httpClient.get(
|
|
407
|
-
`${base
|
|
331
|
+
`${base}/services/${identifier}`,
|
|
408
332
|
options
|
|
409
333
|
);
|
|
410
334
|
},
|
|
411
335
|
find(params, options) {
|
|
412
|
-
const { store_id, ...queryParams } = params;
|
|
413
336
|
return apiConfig.httpClient.get(
|
|
414
|
-
`${base
|
|
415
|
-
{
|
|
416
|
-
...options,
|
|
417
|
-
params: queryParams
|
|
418
|
-
}
|
|
337
|
+
`${base}/services`,
|
|
338
|
+
{ ...options, params }
|
|
419
339
|
);
|
|
420
340
|
},
|
|
421
341
|
findProviders(params, options) {
|
|
422
|
-
const { store_id, ...queryParams } = params;
|
|
423
342
|
return apiConfig.httpClient.get(
|
|
424
|
-
`${base
|
|
425
|
-
{
|
|
426
|
-
...options,
|
|
427
|
-
params: queryParams
|
|
428
|
-
}
|
|
343
|
+
`${base}/service-providers`,
|
|
344
|
+
{ ...options, params }
|
|
429
345
|
);
|
|
430
346
|
},
|
|
431
347
|
getAvailability(params, options) {
|
|
432
|
-
const { store_id, ...queryParams } = params;
|
|
433
|
-
const target_store_id = store_id || apiConfig.storeId;
|
|
434
348
|
return apiConfig.httpClient.get(
|
|
435
|
-
`${base
|
|
436
|
-
{ ...options, params
|
|
349
|
+
`${base}/services/availability`,
|
|
350
|
+
{ ...options, params }
|
|
437
351
|
);
|
|
438
352
|
}
|
|
439
353
|
},
|
|
440
354
|
provider: {
|
|
441
355
|
get(params, options) {
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
if (params.id) {
|
|
445
|
-
identifier = params.id;
|
|
446
|
-
} else if (params.slug) {
|
|
447
|
-
identifier = `${store_id}:${apiConfig.locale}:${params.slug}`;
|
|
448
|
-
} else {
|
|
449
|
-
throw new Error("GetProviderParams requires id or slug");
|
|
450
|
-
}
|
|
356
|
+
const identifier = params.id ?? params.slug;
|
|
357
|
+
if (!identifier) throw new Error("GetProviderParams requires id or slug");
|
|
451
358
|
return apiConfig.httpClient.get(
|
|
452
|
-
`${base
|
|
359
|
+
`${base}/providers/${identifier}`,
|
|
453
360
|
options
|
|
454
361
|
);
|
|
455
362
|
},
|
|
456
363
|
find(params, options) {
|
|
457
|
-
const { store_id, ...queryParams } = params;
|
|
458
364
|
return apiConfig.httpClient.get(
|
|
459
|
-
`${base
|
|
460
|
-
{
|
|
461
|
-
...options,
|
|
462
|
-
params: queryParams
|
|
463
|
-
}
|
|
365
|
+
`${base}/providers`,
|
|
366
|
+
{ ...options, params }
|
|
464
367
|
);
|
|
465
368
|
}
|
|
466
369
|
}
|
|
@@ -474,36 +377,23 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
|
|
|
474
377
|
return submitIdentification("code", params, options);
|
|
475
378
|
},
|
|
476
379
|
async verify(params, options) {
|
|
477
|
-
const store_id = apiConfig.storeId;
|
|
478
380
|
const result = await apiConfig.httpClient.post(
|
|
479
|
-
`${base
|
|
480
|
-
|
|
381
|
+
`${base}/account/verify`,
|
|
382
|
+
params,
|
|
481
383
|
options
|
|
482
384
|
);
|
|
483
|
-
if (result
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
...prev,
|
|
489
|
-
access_token: result.token.token,
|
|
490
|
-
contact: result.contact
|
|
491
|
-
} : {
|
|
492
|
-
access_token: result.token.token,
|
|
493
|
-
contact: result.contact,
|
|
494
|
-
store: identifiedStore,
|
|
495
|
-
market: pending?.market || null
|
|
496
|
-
}
|
|
497
|
-
);
|
|
498
|
-
pendingVerifications.delete(params.challenge_id);
|
|
385
|
+
if (result.token?.token) {
|
|
386
|
+
updateContactSession(() => ({
|
|
387
|
+
sessionToken: result.token.token,
|
|
388
|
+
contact: result.contact
|
|
389
|
+
}));
|
|
499
390
|
}
|
|
500
391
|
return result;
|
|
501
392
|
},
|
|
502
393
|
async logout(options) {
|
|
503
|
-
const store_id = apiConfig.storeId;
|
|
504
394
|
try {
|
|
505
395
|
await apiConfig.httpClient.post(
|
|
506
|
-
`${base
|
|
396
|
+
`${base}/account/logout`,
|
|
507
397
|
{},
|
|
508
398
|
options
|
|
509
399
|
);
|
|
@@ -513,110 +403,74 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
|
|
|
513
403
|
},
|
|
514
404
|
getMe(options) {
|
|
515
405
|
return apiConfig.httpClient.get(
|
|
516
|
-
`${base
|
|
406
|
+
`${base}/account/me`,
|
|
517
407
|
options
|
|
518
408
|
);
|
|
519
409
|
}
|
|
520
410
|
},
|
|
521
411
|
contactList: {
|
|
522
412
|
get(params, options) {
|
|
523
|
-
const store_id = params.store_id || apiConfig.storeId;
|
|
524
413
|
return apiConfig.httpClient.get(
|
|
525
|
-
`${base
|
|
414
|
+
`${base}/contact-lists/${params.id}`,
|
|
526
415
|
options
|
|
527
416
|
);
|
|
528
417
|
},
|
|
529
|
-
find(params, options) {
|
|
530
|
-
const { store_id, ...queryParams } = params || {};
|
|
418
|
+
find(params = {}, options) {
|
|
531
419
|
return apiConfig.httpClient.get(
|
|
532
|
-
`${base
|
|
533
|
-
{
|
|
534
|
-
...options,
|
|
535
|
-
params: queryParams
|
|
536
|
-
}
|
|
420
|
+
`${base}/contact-lists`,
|
|
421
|
+
{ ...options, params }
|
|
537
422
|
);
|
|
538
423
|
},
|
|
539
424
|
plans: {
|
|
540
425
|
find(params, options) {
|
|
541
|
-
const {
|
|
426
|
+
const { contact_list_id, ...queryParams } = params;
|
|
542
427
|
return apiConfig.httpClient.get(
|
|
543
|
-
`${base
|
|
428
|
+
`${base}/contact-lists/${contact_list_id}/plans`,
|
|
544
429
|
{ ...options, params: queryParams }
|
|
545
430
|
);
|
|
546
431
|
}
|
|
547
432
|
},
|
|
548
433
|
memberships: {
|
|
549
|
-
find(params, options) {
|
|
550
|
-
|
|
551
|
-
return apiConfig.httpClient.get(`${base
|
|
434
|
+
async find(params = {}, options) {
|
|
435
|
+
await lifecycle.ensureVisitorSession();
|
|
436
|
+
return apiConfig.httpClient.get(`${base}/contact-lists/memberships`, {
|
|
552
437
|
...options,
|
|
553
|
-
params
|
|
438
|
+
params
|
|
554
439
|
});
|
|
555
440
|
}
|
|
556
441
|
},
|
|
557
|
-
subscribe(params, options) {
|
|
558
|
-
|
|
442
|
+
async subscribe(params, options) {
|
|
443
|
+
await lifecycle.ensureVisitorSession();
|
|
444
|
+
const { id, ...payload } = params;
|
|
559
445
|
return apiConfig.httpClient.post(
|
|
560
|
-
`${base
|
|
446
|
+
`${base}/contact-lists/${id}/subscribe`,
|
|
561
447
|
payload,
|
|
562
448
|
options
|
|
563
449
|
);
|
|
564
450
|
},
|
|
565
|
-
checkAccess(params, options) {
|
|
566
|
-
|
|
451
|
+
async checkAccess(params, options) {
|
|
452
|
+
await lifecycle.ensureVisitorSession();
|
|
567
453
|
return apiConfig.httpClient.get(
|
|
568
|
-
`${base
|
|
569
|
-
options
|
|
570
|
-
);
|
|
571
|
-
},
|
|
572
|
-
checkContentAccess(params, options) {
|
|
573
|
-
const { store_id, ...payload } = params;
|
|
574
|
-
return apiConfig.httpClient.post(
|
|
575
|
-
`${base(store_id)}/contact-lists/access`,
|
|
576
|
-
payload,
|
|
577
|
-
options
|
|
578
|
-
);
|
|
579
|
-
},
|
|
580
|
-
manage(token, options) {
|
|
581
|
-
return apiConfig.httpClient.post(
|
|
582
|
-
`${base()}/contact-lists/manage`,
|
|
583
|
-
{ token },
|
|
454
|
+
`${base}/contact-lists/${params.id}/access`,
|
|
584
455
|
options
|
|
585
456
|
);
|
|
586
457
|
},
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
for (const name of Object.keys(headers)) {
|
|
590
|
-
if (name.toLowerCase() === "content-type") delete headers[name];
|
|
591
|
-
}
|
|
592
|
-
return apiConfig.httpClient.post(
|
|
593
|
-
`${base()}/contact-lists/unsubscribe`,
|
|
594
|
-
new URLSearchParams({ "List-Unsubscribe": "One-Click" }),
|
|
595
|
-
{
|
|
596
|
-
...options,
|
|
597
|
-
params: { ...options?.params || {}, token },
|
|
598
|
-
headers: {
|
|
599
|
-
...headers,
|
|
600
|
-
"Content-Type": "application/x-www-form-urlencoded"
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
);
|
|
604
|
-
},
|
|
605
|
-
confirm(token, options) {
|
|
458
|
+
async checkContentAccess(params, options) {
|
|
459
|
+
await lifecycle.ensureVisitorSession();
|
|
606
460
|
return apiConfig.httpClient.post(
|
|
607
|
-
`${base
|
|
608
|
-
|
|
461
|
+
`${base}/contact-lists/access`,
|
|
462
|
+
params,
|
|
609
463
|
options
|
|
610
464
|
);
|
|
611
465
|
}
|
|
612
466
|
}
|
|
613
467
|
},
|
|
614
|
-
action: createActionApi(apiConfig),
|
|
468
|
+
action: createActionApi(apiConfig, lifecycle),
|
|
615
469
|
experiments: {
|
|
616
|
-
use(params, options) {
|
|
617
|
-
|
|
470
|
+
async use(params, options) {
|
|
471
|
+
await lifecycle.ensureVisitorSession();
|
|
618
472
|
return apiConfig.httpClient.post(
|
|
619
|
-
`${base
|
|
473
|
+
`${base}/experiments/use`,
|
|
620
474
|
{ key: params.key },
|
|
621
475
|
options
|
|
622
476
|
);
|
|
@@ -776,6 +630,15 @@ function requestError(name, message, details = {}) {
|
|
|
776
630
|
function isRecord(value) {
|
|
777
631
|
return typeof value === "object" && value !== null;
|
|
778
632
|
}
|
|
633
|
+
function omitStorefrontRouting(value) {
|
|
634
|
+
if (!isRecord(value) || Array.isArray(value)) return value;
|
|
635
|
+
const prototype = Object.getPrototypeOf(value);
|
|
636
|
+
if (prototype !== Object.prototype && prototype !== null) return value;
|
|
637
|
+
const result = { ...value };
|
|
638
|
+
delete result.store_id;
|
|
639
|
+
delete result.market;
|
|
640
|
+
return result;
|
|
641
|
+
}
|
|
779
642
|
function isTokenSet(value) {
|
|
780
643
|
return isRecord(value) && typeof value.access_token === "string" && (value.refresh_token === void 0 || typeof value.refresh_token === "string") && (value.access_expires_at === void 0 || typeof value.access_expires_at === "number");
|
|
781
644
|
}
|
|
@@ -851,10 +714,25 @@ function createHttpClient(cfg) {
|
|
|
851
714
|
if (!retried && options?.transformRequest) {
|
|
852
715
|
body = options.transformRequest(body);
|
|
853
716
|
}
|
|
717
|
+
if (cfg.storefrontMode) {
|
|
718
|
+
body = omitStorefrontRouting(body);
|
|
719
|
+
}
|
|
720
|
+
const forcedHeaders = typeof cfg.forcedHeaders === "function" ? cfg.forcedHeaders() : cfg.forcedHeaders || {};
|
|
721
|
+
const callerHeaders = { ...options?.headers || {} };
|
|
722
|
+
const protectedHeaderNames = new Set(
|
|
723
|
+
Object.keys(forcedHeaders).map((name) => name.toLowerCase())
|
|
724
|
+
);
|
|
725
|
+
if (cfg.storefrontMode) protectedHeaderNames.add("authorization");
|
|
726
|
+
for (const name of Object.keys(callerHeaders)) {
|
|
727
|
+
if (protectedHeaderNames.has(name.toLowerCase())) {
|
|
728
|
+
delete callerHeaders[name];
|
|
729
|
+
}
|
|
730
|
+
}
|
|
854
731
|
const headers = {
|
|
855
732
|
Accept: "application/json",
|
|
856
733
|
"Content-Type": "application/json",
|
|
857
|
-
...
|
|
734
|
+
...callerHeaders,
|
|
735
|
+
...forcedHeaders
|
|
858
736
|
};
|
|
859
737
|
let tokens = authStorage.getTokens();
|
|
860
738
|
const nowSec = Date.now() / 1e3;
|
|
@@ -865,7 +743,8 @@ function createHttpClient(cfg) {
|
|
|
865
743
|
if (tokens?.access_token) {
|
|
866
744
|
headers["Authorization"] = `Bearer ${tokens.access_token}`;
|
|
867
745
|
}
|
|
868
|
-
const
|
|
746
|
+
const requestParams = cfg.storefrontMode ? omitStorefrontRouting(options?.params) : options?.params;
|
|
747
|
+
const finalPath = requestParams ? path + buildQueryString(requestParams) : path;
|
|
869
748
|
const fetchOptions = {
|
|
870
749
|
method,
|
|
871
750
|
headers,
|
|
@@ -896,8 +775,19 @@ function createHttpClient(cfg) {
|
|
|
896
775
|
throw err;
|
|
897
776
|
}
|
|
898
777
|
if (res.status === 401 && !retried) {
|
|
899
|
-
|
|
900
|
-
|
|
778
|
+
if (cfg.onUnauthorized) {
|
|
779
|
+
const recovered = await cfg.onUnauthorized({
|
|
780
|
+
hadAuthorization: Boolean(tokens?.access_token),
|
|
781
|
+
authorizationToken: tokens?.access_token || null,
|
|
782
|
+
path
|
|
783
|
+
});
|
|
784
|
+
if (recovered) {
|
|
785
|
+
return request(method, path, body, options, true);
|
|
786
|
+
}
|
|
787
|
+
} else {
|
|
788
|
+
await ensureFreshToken();
|
|
789
|
+
return request(method, path, body, options, true);
|
|
790
|
+
}
|
|
901
791
|
}
|
|
902
792
|
try {
|
|
903
793
|
const contentLength = res.headers.get("content-length");
|
|
@@ -1113,6 +1003,14 @@ var createStoreApi = (apiConfig, _updateSession) => {
|
|
|
1113
1003
|
params
|
|
1114
1004
|
});
|
|
1115
1005
|
},
|
|
1006
|
+
async regeneratePublishableKey(params = {}, options) {
|
|
1007
|
+
const store_id = params.store_id || apiConfig.storeId;
|
|
1008
|
+
return apiConfig.httpClient.post(
|
|
1009
|
+
`/v1/stores/${store_id}/publishable-key/regenerate`,
|
|
1010
|
+
{},
|
|
1011
|
+
options
|
|
1012
|
+
);
|
|
1013
|
+
},
|
|
1116
1014
|
async getSubscriptionPlans(_params, options) {
|
|
1117
1015
|
return apiConfig.httpClient.get("/v1/stores/plans", options);
|
|
1118
1016
|
},
|
|
@@ -2011,7 +1909,10 @@ var createMarketApi = (apiConfig) => {
|
|
|
2011
1909
|
async delete(params, options) {
|
|
2012
1910
|
return apiConfig.httpClient.delete(
|
|
2013
1911
|
`/v1/stores/${apiConfig.storeId}/markets/${params.id}`,
|
|
2014
|
-
|
|
1912
|
+
{
|
|
1913
|
+
...options,
|
|
1914
|
+
params: params.replacement_default_market_id ? { replacement_default_market_id: params.replacement_default_market_id } : options?.params
|
|
1915
|
+
}
|
|
2015
1916
|
);
|
|
2016
1917
|
}
|
|
2017
1918
|
};
|
|
@@ -2032,11 +1933,7 @@ var createActionAdminApi = (apiConfig) => ({
|
|
|
2032
1933
|
async find(params, options) {
|
|
2033
1934
|
const store_id = params.store_id || apiConfig.storeId;
|
|
2034
1935
|
const queryParams = {};
|
|
2035
|
-
if (params.query) queryParams.query = params.query;
|
|
2036
1936
|
if (params.contact_id) queryParams.contact_id = params.contact_id;
|
|
2037
|
-
if (params.types && params.types.length > 0) queryParams.types = params.types;
|
|
2038
|
-
if (params.from !== void 0) queryParams.from = params.from;
|
|
2039
|
-
if (params.to !== void 0) queryParams.to = params.to;
|
|
2040
1937
|
if (params.limit !== void 0) queryParams.limit = params.limit;
|
|
2041
1938
|
if (params.cursor) queryParams.cursor = params.cursor;
|
|
2042
1939
|
return apiConfig.httpClient.get(
|
|
@@ -2082,16 +1979,6 @@ var createContactApi = (apiConfig) => {
|
|
|
2082
1979
|
}
|
|
2083
1980
|
);
|
|
2084
1981
|
},
|
|
2085
|
-
async findActions(params, options) {
|
|
2086
|
-
const { store_id, ...queryParams } = params || {};
|
|
2087
|
-
return apiConfig.httpClient.get(
|
|
2088
|
-
`/v1/stores/${store_id || apiConfig.storeId}/actions`,
|
|
2089
|
-
{
|
|
2090
|
-
...options,
|
|
2091
|
-
params: Object.keys(queryParams).length > 0 ? queryParams : void 0
|
|
2092
|
-
}
|
|
2093
|
-
);
|
|
2094
|
-
},
|
|
2095
1982
|
async update(params, options) {
|
|
2096
1983
|
const { id, store_id, ...body } = params;
|
|
2097
1984
|
return apiConfig.httpClient.put(
|
|
@@ -2651,32 +2538,38 @@ function storefrontSupportOptions(supportToken, options) {
|
|
|
2651
2538
|
}
|
|
2652
2539
|
};
|
|
2653
2540
|
}
|
|
2654
|
-
function createStorefrontSupportApi(config) {
|
|
2541
|
+
function createStorefrontSupportApi(config, ensureVisitorSession) {
|
|
2655
2542
|
const { httpClient } = config;
|
|
2656
2543
|
return {
|
|
2657
2544
|
async startConversation(params = {}, opts) {
|
|
2658
|
-
|
|
2545
|
+
await ensureVisitorSession();
|
|
2659
2546
|
return httpClient.post(
|
|
2660
|
-
|
|
2661
|
-
|
|
2547
|
+
"/v1/storefront/support/conversations",
|
|
2548
|
+
params,
|
|
2662
2549
|
opts
|
|
2663
2550
|
);
|
|
2664
2551
|
},
|
|
2665
2552
|
async sendMessage(params, opts) {
|
|
2553
|
+
await ensureVisitorSession();
|
|
2666
2554
|
const { support_token, ...request } = params;
|
|
2667
|
-
const storeId = config.storeId;
|
|
2668
2555
|
return httpClient.post(
|
|
2669
|
-
`/v1/storefront
|
|
2670
|
-
|
|
2556
|
+
`/v1/storefront/support/conversations/${request.conversation_id}/messages`,
|
|
2557
|
+
request,
|
|
2671
2558
|
storefrontSupportOptions(support_token, opts)
|
|
2672
2559
|
);
|
|
2673
2560
|
},
|
|
2674
2561
|
async getConversation(params, opts) {
|
|
2562
|
+
await ensureVisitorSession();
|
|
2675
2563
|
const { support_token, ...request } = params;
|
|
2676
|
-
const
|
|
2677
|
-
|
|
2564
|
+
const query = new URLSearchParams();
|
|
2565
|
+
if (request.message_limit) query.set("message_limit", String(request.message_limit));
|
|
2566
|
+
if (typeof request.after_created_at === "number") {
|
|
2567
|
+
query.set("after_created_at", String(request.after_created_at));
|
|
2568
|
+
}
|
|
2569
|
+
if (request.after_id) query.set("after_id", request.after_id);
|
|
2570
|
+
const suffix = query.size ? `?${query}` : "";
|
|
2678
2571
|
return httpClient.get(
|
|
2679
|
-
`/v1/storefront
|
|
2572
|
+
`/v1/storefront/support/conversations/${request.conversation_id}${suffix}`,
|
|
2680
2573
|
storefrontSupportOptions(support_token, opts)
|
|
2681
2574
|
);
|
|
2682
2575
|
}
|
|
@@ -4127,104 +4020,1877 @@ function getFirstAvailableFCId(variant, quantity = 1) {
|
|
|
4127
4020
|
return inv?.location_id;
|
|
4128
4021
|
}
|
|
4129
4022
|
|
|
4130
|
-
// src/
|
|
4131
|
-
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4023
|
+
// src/payments/stripe.ts
|
|
4024
|
+
async function defaultStripeLoader(publishableKey, options) {
|
|
4025
|
+
const { loadStripe } = await import('@stripe/stripe-js');
|
|
4026
|
+
return loadStripe(publishableKey, options);
|
|
4027
|
+
}
|
|
4028
|
+
function normalizeCurrency(currency) {
|
|
4029
|
+
return currency.trim().toLowerCase();
|
|
4030
|
+
}
|
|
4031
|
+
async function createStripeConfirmationTokenController(config, stripeLoader = defaultStripeLoader) {
|
|
4032
|
+
const stripe = await stripeLoader(
|
|
4033
|
+
config.publishableKey,
|
|
4034
|
+
config.connectedAccountId ? { stripeAccount: config.connectedAccountId } : void 0
|
|
4035
|
+
);
|
|
4036
|
+
if (!stripe) throw new Error("Stripe failed to initialize");
|
|
4037
|
+
let elements = createElements(stripe, config);
|
|
4038
|
+
let paymentElement = elements.create("payment", {
|
|
4039
|
+
layout: "tabs"
|
|
4040
|
+
});
|
|
4140
4041
|
return {
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
|
|
4147
|
-
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
4153
|
-
|
|
4154
|
-
|
|
4155
|
-
|
|
4156
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
4162
|
-
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
|
|
4168
|
-
|
|
4169
|
-
|
|
4170
|
-
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
|
|
4042
|
+
mount(target) {
|
|
4043
|
+
if (!paymentElement) {
|
|
4044
|
+
paymentElement = elements.create("payment", { layout: "tabs" });
|
|
4045
|
+
}
|
|
4046
|
+
paymentElement.mount(target);
|
|
4047
|
+
},
|
|
4048
|
+
update(input) {
|
|
4049
|
+
elements.update({
|
|
4050
|
+
...input.amount !== void 0 ? { amount: input.amount } : {},
|
|
4051
|
+
...input.currency ? { currency: normalizeCurrency(input.currency) } : {}
|
|
4052
|
+
});
|
|
4053
|
+
},
|
|
4054
|
+
async createConfirmationToken(options = {}) {
|
|
4055
|
+
const submitResult = await elements.submit();
|
|
4056
|
+
if (submitResult.error) {
|
|
4057
|
+
throw new Error(submitResult.error.message || "Payment details are incomplete");
|
|
4058
|
+
}
|
|
4059
|
+
const tokenInput = {
|
|
4060
|
+
elements,
|
|
4061
|
+
params: {
|
|
4062
|
+
...options.return_url ? { return_url: options.return_url } : {},
|
|
4063
|
+
...options.billing_details ? { payment_method_data: { billing_details: options.billing_details } } : {}
|
|
4064
|
+
}
|
|
4065
|
+
};
|
|
4066
|
+
const result = await stripe.createConfirmationToken(tokenInput);
|
|
4067
|
+
if (result.error) {
|
|
4068
|
+
throw new Error(result.error.message || "Payment confirmation token failed");
|
|
4069
|
+
}
|
|
4070
|
+
if (!result.confirmationToken?.id) {
|
|
4071
|
+
throw new Error("Stripe did not return a confirmation token");
|
|
4072
|
+
}
|
|
4073
|
+
return {
|
|
4074
|
+
confirmation_token_id: result.confirmationToken.id,
|
|
4075
|
+
return_url: options.return_url
|
|
4076
|
+
};
|
|
4077
|
+
},
|
|
4078
|
+
async handleNextAction(clientSecret) {
|
|
4079
|
+
const result = await stripe.handleNextAction({ clientSecret });
|
|
4080
|
+
if (result.error) {
|
|
4081
|
+
throw new Error(result.error.message || "Payment authentication failed");
|
|
4082
|
+
}
|
|
4083
|
+
},
|
|
4084
|
+
destroy() {
|
|
4085
|
+
paymentElement?.destroy();
|
|
4086
|
+
paymentElement = null;
|
|
4087
|
+
}
|
|
4177
4088
|
};
|
|
4178
4089
|
}
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4090
|
+
function createElements(stripe, config) {
|
|
4091
|
+
return stripe.elements({
|
|
4092
|
+
mode: "payment",
|
|
4093
|
+
amount: config.amount,
|
|
4094
|
+
currency: normalizeCurrency(config.currency),
|
|
4095
|
+
paymentMethodCreation: "manual",
|
|
4096
|
+
...config.appearance ? { appearance: config.appearance } : {}
|
|
4097
|
+
});
|
|
4098
|
+
}
|
|
4099
|
+
|
|
4100
|
+
// src/storefrontStore/utils.ts
|
|
4101
|
+
function readErrorMessage(error, fallback) {
|
|
4102
|
+
if (error instanceof Error && error.message) return error.message;
|
|
4103
|
+
if (typeof error === "string" && error.length > 0) return error;
|
|
4104
|
+
return fallback;
|
|
4105
|
+
}
|
|
4106
|
+
function createId(prefix) {
|
|
4107
|
+
const cryptoValue = globalThis.crypto;
|
|
4108
|
+
if (cryptoValue && "randomUUID" in cryptoValue) return cryptoValue.randomUUID();
|
|
4109
|
+
return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
4110
|
+
}
|
|
4111
|
+
function firstLocalized(value, locale) {
|
|
4112
|
+
if (typeof value === "string") return value;
|
|
4113
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return "";
|
|
4114
|
+
const record = value;
|
|
4115
|
+
const localeValue = record[locale];
|
|
4116
|
+
if (typeof localeValue === "string") return localeValue;
|
|
4117
|
+
const englishValue = record.en;
|
|
4118
|
+
if (typeof englishValue === "string") return englishValue;
|
|
4119
|
+
for (const entry of Object.values(record)) {
|
|
4120
|
+
if (typeof entry === "string") return entry;
|
|
4187
4121
|
}
|
|
4122
|
+
return "";
|
|
4188
4123
|
}
|
|
4189
|
-
function
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4124
|
+
function findBlock2(blocks, keys) {
|
|
4125
|
+
return (blocks || []).find((block) => keys.includes(block.key)) || null;
|
|
4126
|
+
}
|
|
4127
|
+
function blockText(blocks, keys, locale) {
|
|
4128
|
+
const block = findBlock2(blocks, keys);
|
|
4129
|
+
if (!block) return "";
|
|
4130
|
+
return firstLocalized(block.value, locale);
|
|
4131
|
+
}
|
|
4132
|
+
function productName(product, locale) {
|
|
4133
|
+
return blockText(product.blocks, ["name", "title"], locale) || product.key || product.id;
|
|
4134
|
+
}
|
|
4135
|
+
function serviceName(service, locale) {
|
|
4136
|
+
return blockText(service.blocks, ["name", "title"], locale) || service.key || service.id;
|
|
4137
|
+
}
|
|
4138
|
+
function providerName(provider, locale) {
|
|
4139
|
+
return blockText(provider.blocks, ["name", "title"], locale) || provider.key || provider.id;
|
|
4140
|
+
}
|
|
4141
|
+
function entitySlug(entity, locale) {
|
|
4142
|
+
return entity.slug?.[locale] || entity.slug?.en || Object.values(entity.slug || {})[0] || entity.id;
|
|
4143
|
+
}
|
|
4144
|
+
function priceForMarket(prices, market, marketCurrency) {
|
|
4145
|
+
const marketKey = market.trim();
|
|
4146
|
+
if (!marketKey) throw new Error("A market is required to select a product price");
|
|
4147
|
+
const currency = marketCurrency?.trim().toUpperCase();
|
|
4148
|
+
if (!currency) throw new Error(`Market ${marketKey} does not have an authoritative currency`);
|
|
4149
|
+
const marketPrices = prices.filter((candidate) => candidate.market === marketKey);
|
|
4150
|
+
if (marketPrices.length === 0) {
|
|
4151
|
+
throw new Error(`Product is not priced for market ${marketKey}`);
|
|
4152
|
+
}
|
|
4153
|
+
if (marketPrices.some(
|
|
4154
|
+
(candidate) => !Number.isSafeInteger(candidate.amount) || candidate.amount < 0 || candidate.currency.trim().toUpperCase() !== currency
|
|
4155
|
+
)) {
|
|
4156
|
+
throw new Error(`Product has an invalid price for market ${marketKey}`);
|
|
4195
4157
|
}
|
|
4158
|
+
const authorizedPrices = marketPrices.filter((candidate) => candidate.contact_list_id);
|
|
4159
|
+
if (authorizedPrices.length > 0) {
|
|
4160
|
+
return authorizedPrices.reduce(
|
|
4161
|
+
(lowest, candidate) => candidate.amount < lowest.amount ? candidate : lowest
|
|
4162
|
+
);
|
|
4163
|
+
}
|
|
4164
|
+
const basePrices = marketPrices.filter((candidate) => !candidate.contact_list_id);
|
|
4165
|
+
if (basePrices.length !== 1) {
|
|
4166
|
+
throw new Error(`Product does not have one base price for market ${marketKey}`);
|
|
4167
|
+
}
|
|
4168
|
+
const price = basePrices[0];
|
|
4169
|
+
return price;
|
|
4196
4170
|
}
|
|
4197
|
-
function
|
|
4198
|
-
const
|
|
4199
|
-
|
|
4200
|
-
|
|
4201
|
-
|
|
4171
|
+
function availableStock(client, variant) {
|
|
4172
|
+
const fromUtility = client.utils.getAvailableStock(variant);
|
|
4173
|
+
if (Number.isFinite(fromUtility)) return fromUtility;
|
|
4174
|
+
const stock = (variant.inventory || []).reduce((total, row) => total + (row.available || 0), 0);
|
|
4175
|
+
return stock > 0 ? stock : void 0;
|
|
4176
|
+
}
|
|
4177
|
+
function locationToAddress(location) {
|
|
4178
|
+
return {
|
|
4179
|
+
country: location.country || "",
|
|
4180
|
+
state: location.state || "",
|
|
4181
|
+
city: location.city || "",
|
|
4182
|
+
postal_code: location.postal_code || "",
|
|
4183
|
+
name: "",
|
|
4184
|
+
street1: "",
|
|
4185
|
+
street2: null
|
|
4186
|
+
};
|
|
4187
|
+
}
|
|
4188
|
+
function createFormEntry(formId, fields) {
|
|
4189
|
+
if (!formId.trim()) throw new Error("formId is required");
|
|
4190
|
+
return { form_id: formId, fields };
|
|
4191
|
+
}
|
|
4192
|
+
function toProductCheckoutItems(items) {
|
|
4193
|
+
return items.map((item) => ({
|
|
4194
|
+
type: "product",
|
|
4195
|
+
id: item.id,
|
|
4196
|
+
product_id: item.product_id,
|
|
4197
|
+
variant_id: item.variant_id,
|
|
4198
|
+
quantity: item.quantity
|
|
4199
|
+
}));
|
|
4200
|
+
}
|
|
4201
|
+
function toServiceCheckoutItems(items) {
|
|
4202
|
+
return items.map((item) => ({
|
|
4203
|
+
type: "service",
|
|
4204
|
+
id: item.id,
|
|
4205
|
+
service_id: item.service_id,
|
|
4206
|
+
provider_id: item.provider_id,
|
|
4207
|
+
slots: [...item.slots].sort((a, b) => a.from - b.from),
|
|
4208
|
+
forms: item.forms
|
|
4209
|
+
}));
|
|
4210
|
+
}
|
|
4211
|
+
function formValueError(field, message) {
|
|
4212
|
+
return new Error(`Invalid value for form field '${field.key}': ${message}`);
|
|
4213
|
+
}
|
|
4214
|
+
function isValidGeoLocation(value) {
|
|
4215
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
4216
|
+
const location = value;
|
|
4217
|
+
if (location.label !== void 0 && location.label !== null && typeof location.label !== "string") {
|
|
4218
|
+
return false;
|
|
4202
4219
|
}
|
|
4203
|
-
|
|
4204
|
-
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4220
|
+
const coordinates = location.coordinates;
|
|
4221
|
+
if (!coordinates || typeof coordinates !== "object") return false;
|
|
4222
|
+
return Number.isFinite(coordinates.lat) && Number.isFinite(coordinates.lon) && coordinates.lat >= -90 && coordinates.lat <= 90 && coordinates.lon >= -180 && coordinates.lon <= 180;
|
|
4223
|
+
}
|
|
4224
|
+
function buildFormField(field, value) {
|
|
4225
|
+
const common = { id: field.id, key: field.key };
|
|
4226
|
+
switch (field.type) {
|
|
4227
|
+
case "text":
|
|
4228
|
+
if (typeof value !== "string") throw formValueError(field, "expected text");
|
|
4229
|
+
if (field.required && value.trim().length === 0) throw formValueError(field, "required text is blank");
|
|
4230
|
+
return { ...common, type: "text", value };
|
|
4231
|
+
case "number":
|
|
4232
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
4233
|
+
throw formValueError(field, "expected a finite number");
|
|
4234
|
+
}
|
|
4235
|
+
if (field.min !== null && field.min !== void 0 && value < field.min) {
|
|
4236
|
+
throw formValueError(field, `must be at least ${field.min}`);
|
|
4237
|
+
}
|
|
4238
|
+
if (field.max !== null && field.max !== void 0 && value > field.max) {
|
|
4239
|
+
throw formValueError(field, `must be at most ${field.max}`);
|
|
4240
|
+
}
|
|
4241
|
+
return { ...common, type: "number", value };
|
|
4242
|
+
case "boolean":
|
|
4243
|
+
if (typeof value !== "boolean") throw formValueError(field, "expected a boolean");
|
|
4244
|
+
return { ...common, type: "boolean", value };
|
|
4245
|
+
case "date":
|
|
4246
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value)) {
|
|
4247
|
+
throw formValueError(field, "expected an integer timestamp");
|
|
4248
|
+
}
|
|
4249
|
+
return { ...common, type: "date", value };
|
|
4250
|
+
case "geo_location":
|
|
4251
|
+
if (!isValidGeoLocation(value)) throw formValueError(field, "expected valid coordinates");
|
|
4252
|
+
return { ...common, type: "geo_location", value };
|
|
4253
|
+
case "select": {
|
|
4254
|
+
if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
|
|
4255
|
+
throw formValueError(field, "expected a list of options");
|
|
4256
|
+
}
|
|
4257
|
+
const selected = value;
|
|
4258
|
+
if (field.required && selected.length === 0) throw formValueError(field, "at least one option is required");
|
|
4259
|
+
if (new Set(selected).size !== selected.length || selected.some((item) => !field.options.includes(item))) {
|
|
4260
|
+
throw formValueError(field, "contains an unknown or duplicate option");
|
|
4261
|
+
}
|
|
4262
|
+
return { ...common, type: "select", value: selected };
|
|
4208
4263
|
}
|
|
4209
4264
|
}
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4265
|
+
}
|
|
4266
|
+
function isEmptyOptionalValue(field, value) {
|
|
4267
|
+
if (field.required) return false;
|
|
4268
|
+
if (field.type === "text") return value === "";
|
|
4269
|
+
if (field.type === "select") return Array.isArray(value) && value.length === 0;
|
|
4270
|
+
if (field.type === "geo_location") {
|
|
4271
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 0);
|
|
4272
|
+
}
|
|
4273
|
+
return false;
|
|
4274
|
+
}
|
|
4275
|
+
function buildFormFields(schema, values) {
|
|
4276
|
+
const knownKeys = new Set(schema.map((field) => field.key));
|
|
4277
|
+
const unknownKey = Object.keys(values).find((key) => !knownKeys.has(key));
|
|
4278
|
+
if (unknownKey) throw new Error(`Form field '${unknownKey}' is not defined by the form schema`);
|
|
4279
|
+
const fields = [];
|
|
4280
|
+
for (const field of schema) {
|
|
4281
|
+
const value = values[field.key];
|
|
4282
|
+
if (value === void 0) {
|
|
4283
|
+
if (field.required) throw formValueError(field, "required value is missing");
|
|
4284
|
+
continue;
|
|
4285
|
+
}
|
|
4286
|
+
if (isEmptyOptionalValue(field, value)) continue;
|
|
4287
|
+
fields.push(buildFormField(field, value));
|
|
4288
|
+
}
|
|
4289
|
+
return fields;
|
|
4290
|
+
}
|
|
4291
|
+
function createFormEntryFromValues(form, values) {
|
|
4292
|
+
return createFormEntry(form.id, buildFormFields(form.schema, values));
|
|
4293
|
+
}
|
|
4294
|
+
function getFormBlockType(field) {
|
|
4295
|
+
if (field.key === "email") return "email";
|
|
4296
|
+
if (field.key === "phone") return "phone";
|
|
4297
|
+
if (field.type === "geo_location") return "address";
|
|
4298
|
+
return field.type;
|
|
4299
|
+
}
|
|
4300
|
+
function getFormBlockValue(field) {
|
|
4301
|
+
if (field.type === "boolean") return false;
|
|
4302
|
+
if (field.type === "select") return [];
|
|
4303
|
+
if (field.type === "geo_location") return {};
|
|
4304
|
+
if (field.type === "number" || field.type === "date") return void 0;
|
|
4305
|
+
return "";
|
|
4306
|
+
}
|
|
4307
|
+
function formSchemaToBlock(field) {
|
|
4308
|
+
const min = field.type === "number" ? field.min : void 0;
|
|
4309
|
+
const max = field.type === "number" ? field.max : void 0;
|
|
4310
|
+
const options = field.type === "select" ? field.options : void 0;
|
|
4311
|
+
return {
|
|
4312
|
+
id: field.id,
|
|
4313
|
+
key: field.key,
|
|
4314
|
+
type: getFormBlockType(field),
|
|
4315
|
+
properties: {
|
|
4316
|
+
isRequired: field.required,
|
|
4317
|
+
minValues: field.required ? 1 : 0,
|
|
4318
|
+
min,
|
|
4319
|
+
max,
|
|
4320
|
+
options,
|
|
4321
|
+
pattern: field.key === "email" ? "^.+@.+\\..+$" : field.key === "phone" ? "^.{6,20}$" : void 0
|
|
4322
|
+
},
|
|
4323
|
+
value: getFormBlockValue(field)
|
|
4216
4324
|
};
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4325
|
+
}
|
|
4326
|
+
function formatServiceTime(ts, tz) {
|
|
4327
|
+
return new Date(ts * 1e3).toLocaleTimeString([], {
|
|
4328
|
+
hour: "2-digit",
|
|
4329
|
+
minute: "2-digit",
|
|
4330
|
+
timeZone: tz
|
|
4331
|
+
});
|
|
4332
|
+
}
|
|
4333
|
+
function formatServiceSlotTime(from, to, tz) {
|
|
4334
|
+
return `${formatServiceTime(from, tz)} - ${formatServiceTime(to, tz)}`;
|
|
4335
|
+
}
|
|
4336
|
+
function getSlotsForDate(availability, dateStr, providerId) {
|
|
4337
|
+
if (!availability) return [];
|
|
4338
|
+
const slots = [];
|
|
4339
|
+
for (const provider of availability.providers) {
|
|
4340
|
+
if (providerId && provider.provider_id !== providerId) continue;
|
|
4341
|
+
const day = provider.days.find((candidate) => candidate.date === dateStr);
|
|
4342
|
+
if (!day) continue;
|
|
4343
|
+
for (const slot of day.slots) {
|
|
4344
|
+
if (slot.spots > 0) slots.push({ from: slot.from, to: slot.to, providerId: provider.provider_id });
|
|
4222
4345
|
}
|
|
4223
|
-
}
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4346
|
+
}
|
|
4347
|
+
return slots.sort((a, b) => a.from - b.from);
|
|
4348
|
+
}
|
|
4349
|
+
function hasAvailableSlotsForDate(availability, dateStr, providerId) {
|
|
4350
|
+
if (!availability) return false;
|
|
4351
|
+
return availability.providers.some((provider) => {
|
|
4352
|
+
if (providerId && provider.provider_id !== providerId) return false;
|
|
4353
|
+
const day = provider.days.find((candidate) => candidate.date === dateStr);
|
|
4354
|
+
return !!day?.slots.some((slot) => slot.spots > 0);
|
|
4355
|
+
});
|
|
4356
|
+
}
|
|
4357
|
+
var SERVICE_WEEKDAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
|
4358
|
+
function createServiceInitialState() {
|
|
4359
|
+
return {
|
|
4360
|
+
service: null,
|
|
4361
|
+
availability: null,
|
|
4362
|
+
providers: [],
|
|
4363
|
+
serviceProviders: [],
|
|
4364
|
+
selectedProviderId: null,
|
|
4365
|
+
currentMonth: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), 1),
|
|
4366
|
+
calendar: [],
|
|
4367
|
+
selectedDate: null,
|
|
4368
|
+
slots: [],
|
|
4369
|
+
selectedSlot: null,
|
|
4370
|
+
timezone: typeof window !== "undefined" ? Intl.DateTimeFormat().resolvedOptions().timeZone : "UTC",
|
|
4371
|
+
tzGroups: {},
|
|
4372
|
+
loading: false,
|
|
4373
|
+
weekdays: SERVICE_WEEKDAYS,
|
|
4374
|
+
quote: null,
|
|
4375
|
+
fetchingQuote: false,
|
|
4376
|
+
quoteError: null,
|
|
4377
|
+
currency: null,
|
|
4378
|
+
dateTimeConfirmed: false,
|
|
4379
|
+
availablePaymentMethods: [],
|
|
4380
|
+
cartId: null,
|
|
4381
|
+
promoCode: null
|
|
4382
|
+
};
|
|
4383
|
+
}
|
|
4384
|
+
function normalizeTimezoneGroups(groups) {
|
|
4385
|
+
const normalized = {};
|
|
4386
|
+
for (const group of groups) {
|
|
4387
|
+
normalized[group.label] = group.zones.map((zone) => ({
|
|
4388
|
+
zone: zone.value,
|
|
4389
|
+
name: zone.label
|
|
4390
|
+
}));
|
|
4391
|
+
}
|
|
4392
|
+
return normalized;
|
|
4393
|
+
}
|
|
4394
|
+
|
|
4395
|
+
// src/storefrontStore/initialize.ts
|
|
4396
|
+
function firstFiniteNumber(...values) {
|
|
4397
|
+
return values.find((value) => typeof value === "number" && Number.isFinite(value));
|
|
4398
|
+
}
|
|
4399
|
+
function initializeStoreCore(publishableKey, config, scopedClient) {
|
|
4400
|
+
const client = scopedClient || createStorefront(publishableKey, config);
|
|
4401
|
+
const session = atom(client.session);
|
|
4402
|
+
const setup = atom(null);
|
|
4403
|
+
const locale = atom(config.locale || client.getLocale());
|
|
4404
|
+
const market_key = atom(config.market || client.getMarket());
|
|
4405
|
+
const market = computed([setup, market_key], (setupValue, marketKey) => {
|
|
4406
|
+
const resolvedKey = marketKey || setupValue?.markets.default;
|
|
4407
|
+
return setupValue?.markets.available.find((candidate) => candidate.key === resolvedKey) || null;
|
|
4408
|
+
});
|
|
4409
|
+
const currency = computed(market, (value) => value?.currency || null);
|
|
4410
|
+
const allowed_payment_methods = computed(market, (value) => value?.payment_methods || []);
|
|
4411
|
+
const payment_config = computed([setup, market], (setupValue, marketValue) => {
|
|
4412
|
+
const methods = marketValue?.payment_methods || [];
|
|
4413
|
+
const hasCreditCard = methods.some((method) => method.type === "credit_card");
|
|
4414
|
+
return {
|
|
4415
|
+
provider: setupValue?.payment || null,
|
|
4416
|
+
enabled: hasCreditCard && Boolean(setupValue?.readiness.payment)
|
|
4417
|
+
};
|
|
4418
|
+
});
|
|
4419
|
+
const cart = atom(null);
|
|
4420
|
+
const product_items = atom([]);
|
|
4421
|
+
const service_items = atom([]);
|
|
4422
|
+
const quote = atom(null);
|
|
4423
|
+
const promo_code = atom(null);
|
|
4424
|
+
const last_order = atom(null);
|
|
4425
|
+
const payment_controller = atom(null);
|
|
4426
|
+
const payment_ready = computed(payment_controller, (value) => value !== null);
|
|
4427
|
+
const cart_status = map({
|
|
4428
|
+
loading: false,
|
|
4429
|
+
syncing: false,
|
|
4430
|
+
fetching_quote: false,
|
|
4431
|
+
processing_checkout: false,
|
|
4432
|
+
error: null,
|
|
4433
|
+
quote_error: null,
|
|
4434
|
+
selected_shipping_method_id: null,
|
|
4435
|
+
user_token: null
|
|
4436
|
+
});
|
|
4437
|
+
function rawProductItemCount(value) {
|
|
4438
|
+
return (value?.items || []).reduce((total, item) => {
|
|
4439
|
+
if (item.type !== "product") return total;
|
|
4440
|
+
return total + (item.quantity || 0);
|
|
4441
|
+
}, 0);
|
|
4442
|
+
}
|
|
4443
|
+
function rawServiceItemCount(value) {
|
|
4444
|
+
return (value?.items || []).reduce((total, item) => {
|
|
4445
|
+
if (item.type !== "service") return total;
|
|
4446
|
+
return total + Math.max(1, item.slots?.length || 0);
|
|
4447
|
+
}, 0);
|
|
4448
|
+
}
|
|
4449
|
+
const product_item_count = computed(
|
|
4450
|
+
[cart, product_items],
|
|
4451
|
+
(cartValue, items) => Math.max(
|
|
4452
|
+
rawProductItemCount(cartValue),
|
|
4453
|
+
items.reduce((total, item) => total + (item.quantity || 0), 0)
|
|
4454
|
+
)
|
|
4455
|
+
);
|
|
4456
|
+
const service_item_count = computed(
|
|
4457
|
+
[cart, service_items],
|
|
4458
|
+
(cartValue, items) => Math.max(
|
|
4459
|
+
rawServiceItemCount(cartValue),
|
|
4460
|
+
items.reduce((total, item) => total + Math.max(1, item.slots.length), 0)
|
|
4461
|
+
)
|
|
4462
|
+
);
|
|
4463
|
+
const item_count = computed(
|
|
4464
|
+
[cart, product_item_count, service_item_count],
|
|
4465
|
+
(cartValue, products, services) => Math.max(cartValue?.item_count || 0, products + services)
|
|
4466
|
+
);
|
|
4467
|
+
const snapshot = computed([cart, product_items, service_items, item_count], (cartValue, products, services, count) => ({
|
|
4468
|
+
cart: cartValue,
|
|
4469
|
+
product_items: products,
|
|
4470
|
+
service_items: services,
|
|
4471
|
+
item_count: count
|
|
4472
|
+
}));
|
|
4473
|
+
let cartWriteRevision = 0;
|
|
4474
|
+
let sessionRequest = null;
|
|
4475
|
+
let cartRequest = null;
|
|
4476
|
+
function nextCartWriteRevision() {
|
|
4477
|
+
cartWriteRevision += 1;
|
|
4478
|
+
return cartWriteRevision;
|
|
4479
|
+
}
|
|
4480
|
+
const cms_state = map({
|
|
4481
|
+
entries: {},
|
|
4482
|
+
forms: {},
|
|
4483
|
+
loading: false,
|
|
4484
|
+
error: null
|
|
4485
|
+
});
|
|
4486
|
+
const eshop_state = map({
|
|
4487
|
+
products: [],
|
|
4488
|
+
services: [],
|
|
4489
|
+
providers: [],
|
|
4490
|
+
product_cursor: null,
|
|
4491
|
+
service_cursor: null,
|
|
4492
|
+
provider_cursor: null,
|
|
4493
|
+
availability: null,
|
|
4494
|
+
loading_products: false,
|
|
4495
|
+
loading_services: false,
|
|
4496
|
+
loading_providers: false,
|
|
4497
|
+
loading_availability: false,
|
|
4498
|
+
error: null
|
|
4499
|
+
});
|
|
4500
|
+
const service_state = map(createServiceInitialState());
|
|
4501
|
+
const service_form_definitions = /* @__PURE__ */ new Map();
|
|
4502
|
+
const service_form_state = map({
|
|
4503
|
+
provider_id: null,
|
|
4504
|
+
groups: [],
|
|
4505
|
+
loading: false,
|
|
4506
|
+
error: null
|
|
4507
|
+
});
|
|
4508
|
+
const service_form_groups = computed(service_form_state, (state) => state.groups);
|
|
4509
|
+
const service_form_blocks = computed(service_form_groups, (groups) => groups.flatMap((group) => group.blocks));
|
|
4510
|
+
client.onAuthStateChanged((value) => session.set(value));
|
|
4511
|
+
currency.subscribe((value) => service_state.setKey("currency", value));
|
|
4512
|
+
market.subscribe((value) => {
|
|
4513
|
+
const methods = value?.payment_methods || [];
|
|
4514
|
+
if (methods.length && service_state.get().availablePaymentMethods.length === 0) {
|
|
4515
|
+
service_state.setKey("availablePaymentMethods", methods);
|
|
4516
|
+
}
|
|
4517
|
+
});
|
|
4518
|
+
function currentMarketKey() {
|
|
4519
|
+
return market_key.get() || client.getMarket() || setup.get()?.markets.default || "";
|
|
4520
|
+
}
|
|
4521
|
+
function currentLocale() {
|
|
4522
|
+
return locale.get() || client.getLocale() || setup.get()?.languages.default || "en";
|
|
4523
|
+
}
|
|
4524
|
+
function currentStripePublishableKey() {
|
|
4525
|
+
const provider = payment_config.get()?.provider;
|
|
4526
|
+
return provider?.publishable_key || null;
|
|
4527
|
+
}
|
|
4528
|
+
function currentStripeConnectedAccountId() {
|
|
4529
|
+
const provider = payment_config.get()?.provider;
|
|
4530
|
+
return provider?.connected_account_id || null;
|
|
4531
|
+
}
|
|
4532
|
+
function currentPaymentAmount() {
|
|
4533
|
+
return Math.max(
|
|
4534
|
+
0,
|
|
4535
|
+
firstFiniteNumber(quote.get()?.charge_amount, cart.get()?.quote_snapshot?.charge_amount, cart.get()?.quote_snapshot?.total) ?? 0
|
|
4536
|
+
);
|
|
4537
|
+
}
|
|
4538
|
+
function currentPaymentCurrency() {
|
|
4539
|
+
return quote.get()?.money?.currency?.trim() || cart.get()?.quote_snapshot?.money?.currency?.trim() || null;
|
|
4540
|
+
}
|
|
4541
|
+
function setPaymentController(controller) {
|
|
4542
|
+
const current = payment_controller.get();
|
|
4543
|
+
if (current && current !== controller) {
|
|
4544
|
+
current.destroy();
|
|
4545
|
+
}
|
|
4546
|
+
payment_controller.set(controller);
|
|
4547
|
+
return controller;
|
|
4548
|
+
}
|
|
4549
|
+
function destroyPaymentController() {
|
|
4550
|
+
setPaymentController(null);
|
|
4551
|
+
}
|
|
4552
|
+
async function loadSetup() {
|
|
4553
|
+
const current = setup.get();
|
|
4554
|
+
if (current) return current;
|
|
4555
|
+
const result = await client.getSetup();
|
|
4556
|
+
setup.set(result);
|
|
4557
|
+
if (!market_key.get() && result.markets.default) {
|
|
4558
|
+
market_key.set(result.markets.default);
|
|
4559
|
+
}
|
|
4560
|
+
if (!locale.get() && result.languages.default) {
|
|
4561
|
+
locale.set(result.languages.default);
|
|
4562
|
+
}
|
|
4563
|
+
return result;
|
|
4564
|
+
}
|
|
4565
|
+
async function mountStripePayment(target, options = {}) {
|
|
4566
|
+
await loadSetup();
|
|
4567
|
+
const publishableKey2 = currentStripePublishableKey();
|
|
4568
|
+
if (!publishableKey2) {
|
|
4569
|
+
throw new Error("Stripe card payment is not configured for this Store");
|
|
4570
|
+
}
|
|
4571
|
+
const hasExplicitAmount = options.amount !== void 0;
|
|
4572
|
+
const hasExplicitCurrency = Boolean(options.currency?.trim());
|
|
4573
|
+
if (hasExplicitAmount !== hasExplicitCurrency) {
|
|
4574
|
+
throw new Error("Stripe amount and currency must be supplied together");
|
|
4575
|
+
}
|
|
4576
|
+
const amount = hasExplicitAmount ? options.amount : currentPaymentAmount();
|
|
4577
|
+
if (!Number.isSafeInteger(amount) || amount <= 0) {
|
|
4578
|
+
throw new Error("A positive minor-unit payment amount is required to mount card payment");
|
|
4579
|
+
}
|
|
4580
|
+
const paymentCurrency = hasExplicitCurrency ? options.currency.trim() : currentPaymentCurrency();
|
|
4581
|
+
if (!paymentCurrency || !/^[a-z]{3}$/i.test(paymentCurrency)) {
|
|
4582
|
+
throw new Error("An explicit three-letter payment currency is required to mount card payment");
|
|
4583
|
+
}
|
|
4584
|
+
const controller = await createStripeConfirmationTokenController({
|
|
4585
|
+
publishableKey: publishableKey2,
|
|
4586
|
+
connectedAccountId: currentStripeConnectedAccountId() || void 0,
|
|
4587
|
+
amount,
|
|
4588
|
+
currency: paymentCurrency,
|
|
4589
|
+
...options.appearance ? { appearance: options.appearance } : {}
|
|
4590
|
+
});
|
|
4591
|
+
controller.mount(target);
|
|
4592
|
+
setPaymentController(controller);
|
|
4593
|
+
return controller;
|
|
4594
|
+
}
|
|
4595
|
+
function updatePaymentController(input) {
|
|
4596
|
+
payment_controller.get()?.update(input);
|
|
4597
|
+
}
|
|
4598
|
+
async function ensureSession() {
|
|
4599
|
+
const current = session.get();
|
|
4600
|
+
if (client.isAuthenticated) return current;
|
|
4601
|
+
if (!sessionRequest) {
|
|
4602
|
+
sessionRequest = identify().finally(() => {
|
|
4603
|
+
sessionRequest = null;
|
|
4604
|
+
});
|
|
4605
|
+
}
|
|
4606
|
+
return sessionRequest;
|
|
4607
|
+
}
|
|
4608
|
+
async function identify(params = {}) {
|
|
4609
|
+
if (params.market) setMarket(params.market);
|
|
4610
|
+
const result = await client.identify({
|
|
4611
|
+
...params,
|
|
4612
|
+
market: params.market || currentMarketKey()
|
|
4613
|
+
});
|
|
4614
|
+
session.set({
|
|
4615
|
+
contact: result.contact
|
|
4616
|
+
});
|
|
4617
|
+
return result;
|
|
4618
|
+
}
|
|
4619
|
+
async function identifyContactEmailIfMissing(email) {
|
|
4620
|
+
const normalizedEmail = email.trim().toLowerCase();
|
|
4621
|
+
if (!normalizedEmail) throw new Error("Contact email is required");
|
|
4622
|
+
const current = session.get();
|
|
4623
|
+
if (current?.contact.email === normalizedEmail) return current;
|
|
4624
|
+
return identify({ email: normalizedEmail });
|
|
4625
|
+
}
|
|
4626
|
+
function setMarket(key) {
|
|
4627
|
+
const next = key.trim();
|
|
4628
|
+
const current = currentMarketKey();
|
|
4629
|
+
if (next && current && next !== current && (cart.get()?.item_count || item_count.get()) > 0) {
|
|
4630
|
+
throw Object.assign(
|
|
4631
|
+
new Error("Market cannot change while the cart contains items"),
|
|
4632
|
+
{ code: "CART_MARKET_LOCKED" }
|
|
4633
|
+
);
|
|
4634
|
+
}
|
|
4635
|
+
market_key.set(next);
|
|
4636
|
+
client.setMarket(next);
|
|
4637
|
+
}
|
|
4638
|
+
function setLocale(value) {
|
|
4639
|
+
locale.set(value);
|
|
4640
|
+
client.setLocale(value);
|
|
4641
|
+
}
|
|
4642
|
+
function setContext(context) {
|
|
4643
|
+
if (context.market !== void 0) setMarket(context.market);
|
|
4644
|
+
if (context.locale !== void 0) setLocale(context.locale);
|
|
4645
|
+
}
|
|
4646
|
+
async function ensureCart() {
|
|
4647
|
+
if (cartRequest) return cartRequest;
|
|
4648
|
+
cart_status.setKey("loading", true);
|
|
4649
|
+
cart_status.setKey("error", null);
|
|
4650
|
+
const refreshRevision = cartWriteRevision;
|
|
4651
|
+
cartRequest = (async () => {
|
|
4652
|
+
await ensureSession();
|
|
4653
|
+
const response = await client.eshop.cart.current();
|
|
4654
|
+
await applyCartResponse(response, { ifRevision: refreshRevision });
|
|
4655
|
+
return response;
|
|
4656
|
+
})();
|
|
4657
|
+
try {
|
|
4658
|
+
return await cartRequest;
|
|
4659
|
+
} catch (error) {
|
|
4660
|
+
cart_status.setKey("error", readErrorMessage(error, "Failed to load cart."));
|
|
4661
|
+
throw error;
|
|
4662
|
+
} finally {
|
|
4663
|
+
cartRequest = null;
|
|
4664
|
+
cart_status.setKey("loading", false);
|
|
4665
|
+
}
|
|
4666
|
+
}
|
|
4667
|
+
async function buildProductCartItem(item, source, productHint) {
|
|
4668
|
+
try {
|
|
4669
|
+
const product = productHint?.id === item.product_id ? productHint : await client.eshop.product.get({ id: item.product_id });
|
|
4670
|
+
const variant = product.variants.find((candidate) => candidate.id === item.variant_id);
|
|
4671
|
+
if (!variant) return null;
|
|
4672
|
+
return {
|
|
4673
|
+
id: item.id || createId("product"),
|
|
4674
|
+
product_id: product.id,
|
|
4675
|
+
variant_id: variant.id,
|
|
4676
|
+
product_name: productName(product, currentLocale()),
|
|
4677
|
+
product_slug: entitySlug(product, currentLocale()),
|
|
4678
|
+
variant_attributes: variant.attributes,
|
|
4679
|
+
requires_shipping: variant.requires_shipping !== false,
|
|
4680
|
+
price: priceForMarket(variant.prices, currentMarketKey(), market.get()?.currency),
|
|
4681
|
+
quantity: item.quantity,
|
|
4682
|
+
added_at: source.created_at ? source.created_at * 1e3 : Date.now(),
|
|
4683
|
+
max_stock: availableStock(client, variant)
|
|
4684
|
+
};
|
|
4685
|
+
} catch {
|
|
4686
|
+
return null;
|
|
4687
|
+
}
|
|
4688
|
+
}
|
|
4689
|
+
async function buildServiceCartItems(items) {
|
|
4690
|
+
const rows = [];
|
|
4691
|
+
for (const item of items) {
|
|
4692
|
+
let service = null;
|
|
4693
|
+
let provider = null;
|
|
4694
|
+
try {
|
|
4695
|
+
service = await client.eshop.service.get({ id: item.service_id });
|
|
4696
|
+
} catch {
|
|
4697
|
+
}
|
|
4698
|
+
try {
|
|
4699
|
+
provider = await client.eshop.provider.get({ id: item.provider_id });
|
|
4700
|
+
} catch {
|
|
4701
|
+
}
|
|
4702
|
+
rows.push({
|
|
4703
|
+
id: item.id || createId("service"),
|
|
4704
|
+
service_id: item.service_id,
|
|
4705
|
+
provider_id: item.provider_id,
|
|
4706
|
+
slots: item.slots,
|
|
4707
|
+
forms: item.forms || [],
|
|
4708
|
+
service_name: service ? serviceName(service, currentLocale()) : item.service_id,
|
|
4709
|
+
provider_name: provider ? providerName(provider, currentLocale()) : item.provider_id
|
|
4710
|
+
});
|
|
4711
|
+
}
|
|
4712
|
+
return rows;
|
|
4713
|
+
}
|
|
4714
|
+
async function applyCartResponse(response, options = {}) {
|
|
4715
|
+
if (options.ifRevision !== void 0 && options.ifRevision !== cartWriteRevision) {
|
|
4716
|
+
return cart.get() || response;
|
|
4717
|
+
}
|
|
4718
|
+
cart.set(response);
|
|
4719
|
+
cart_status.setKey("user_token", response.token || null);
|
|
4720
|
+
cart_status.setKey("selected_shipping_method_id", response.shipping_method_id || null);
|
|
4721
|
+
promo_code.set(response.promo_code || null);
|
|
4722
|
+
quote.set(response.quote_snapshot || null);
|
|
4723
|
+
const items = response.items || [];
|
|
4724
|
+
const products = await Promise.all(
|
|
4725
|
+
items.filter((item) => item.type === "product").map((item) => buildProductCartItem(item, response, options.productHint))
|
|
4726
|
+
);
|
|
4727
|
+
const services = await buildServiceCartItems(items.filter((item) => item.type === "service"));
|
|
4728
|
+
product_items.set(products.filter((item) => item !== null));
|
|
4729
|
+
service_items.set(services);
|
|
4730
|
+
return response;
|
|
4731
|
+
}
|
|
4732
|
+
function checkoutItems(input = {}) {
|
|
4733
|
+
return [
|
|
4734
|
+
...toProductCheckoutItems(input.product_items || product_items.get()),
|
|
4735
|
+
...toServiceCheckoutItems(input.service_items || service_items.get())
|
|
4736
|
+
];
|
|
4737
|
+
}
|
|
4738
|
+
async function syncCart(input = {}, writeRevision = nextCartWriteRevision()) {
|
|
4739
|
+
cart_status.setKey("syncing", true);
|
|
4740
|
+
cart_status.setKey("error", null);
|
|
4741
|
+
try {
|
|
4742
|
+
const current = cart.get() || await ensureCart();
|
|
4743
|
+
const response = await client.eshop.cart.update({
|
|
4744
|
+
id: current.id,
|
|
4745
|
+
items: checkoutItems(input),
|
|
4746
|
+
shipping_address: input.shipping_address,
|
|
4747
|
+
billing_address: input.billing_address,
|
|
4748
|
+
forms: input.forms,
|
|
4749
|
+
promo_code: input.promo_code === null ? "" : input.promo_code === void 0 ? promo_code.get() || void 0 : input.promo_code,
|
|
4750
|
+
payment_method_key: input.payment_method_key === null ? "" : input.payment_method_key,
|
|
4751
|
+
shipping_method_id: input.shipping_method_id === null ? "" : input.shipping_method_id === void 0 ? cart_status.get().selected_shipping_method_id || void 0 : input.shipping_method_id
|
|
4752
|
+
});
|
|
4753
|
+
await applyCartResponse(response, { ifRevision: writeRevision });
|
|
4754
|
+
return response;
|
|
4755
|
+
} catch (error) {
|
|
4756
|
+
cart_status.setKey("error", readErrorMessage(error, "Failed to sync cart."));
|
|
4757
|
+
throw error;
|
|
4758
|
+
} finally {
|
|
4759
|
+
cart_status.setKey("syncing", false);
|
|
4760
|
+
}
|
|
4761
|
+
}
|
|
4762
|
+
async function addProduct(product, variant, quantity = 1) {
|
|
4763
|
+
cart_status.setKey("error", null);
|
|
4764
|
+
const writeRevision = nextCartWriteRevision();
|
|
4765
|
+
try {
|
|
4766
|
+
const current = cart.get() || await ensureCart();
|
|
4767
|
+
const response = await client.eshop.cart.addItem({
|
|
4768
|
+
id: current.id,
|
|
4769
|
+
item: {
|
|
4770
|
+
type: "product",
|
|
4771
|
+
product_id: product.id,
|
|
4772
|
+
variant_id: variant.id,
|
|
4773
|
+
quantity
|
|
4774
|
+
}
|
|
4775
|
+
});
|
|
4776
|
+
await applyCartResponse(response, { ifRevision: writeRevision, productHint: product });
|
|
4777
|
+
return response;
|
|
4778
|
+
} catch (error) {
|
|
4779
|
+
cart_status.setKey("error", readErrorMessage(error, "Failed to add product to cart."));
|
|
4780
|
+
throw error;
|
|
4781
|
+
}
|
|
4782
|
+
}
|
|
4783
|
+
async function setProductQuantity(itemId, quantity) {
|
|
4784
|
+
const writeRevision = nextCartWriteRevision();
|
|
4785
|
+
const next = product_items.get().map((item) => {
|
|
4786
|
+
if (item.id !== itemId) return item;
|
|
4787
|
+
const bounded = item.max_stock ? Math.min(Math.max(1, quantity), item.max_stock) : Math.max(1, quantity);
|
|
4788
|
+
return { ...item, quantity: bounded };
|
|
4789
|
+
});
|
|
4790
|
+
product_items.set(next);
|
|
4791
|
+
return syncCart({ product_items: next }, writeRevision);
|
|
4792
|
+
}
|
|
4793
|
+
async function removeProduct(itemId) {
|
|
4794
|
+
const writeRevision = nextCartWriteRevision();
|
|
4795
|
+
const item = product_items.get().find((candidate) => candidate.id === itemId);
|
|
4796
|
+
product_items.set(product_items.get().filter((candidate) => candidate.id !== itemId));
|
|
4797
|
+
const current = cart.get();
|
|
4798
|
+
if (!current || !item) return null;
|
|
4799
|
+
const response = await client.eshop.cart.removeItem({
|
|
4800
|
+
id: current.id,
|
|
4801
|
+
item_id: item.id
|
|
4802
|
+
});
|
|
4803
|
+
await applyCartResponse(response, { ifRevision: writeRevision });
|
|
4804
|
+
return response;
|
|
4805
|
+
}
|
|
4806
|
+
async function addServiceItem(item) {
|
|
4807
|
+
const writeRevision = nextCartWriteRevision();
|
|
4808
|
+
const next = [...service_items.get(), item];
|
|
4809
|
+
service_items.set(next);
|
|
4810
|
+
return syncCart({ service_items: next }, writeRevision);
|
|
4811
|
+
}
|
|
4812
|
+
async function removeServiceItem(itemId) {
|
|
4813
|
+
const writeRevision = nextCartWriteRevision();
|
|
4814
|
+
const next = service_items.get().filter((item) => item.id !== itemId);
|
|
4815
|
+
service_items.set(next);
|
|
4816
|
+
return syncCart({ service_items: next }, writeRevision);
|
|
4817
|
+
}
|
|
4818
|
+
async function clearCart() {
|
|
4819
|
+
const writeRevision = nextCartWriteRevision();
|
|
4820
|
+
const current = cart.get();
|
|
4821
|
+
clearLocalCart();
|
|
4822
|
+
if (!current) return null;
|
|
4823
|
+
const response = await client.eshop.cart.clear({ id: current.id });
|
|
4824
|
+
await applyCartResponse(response, { ifRevision: writeRevision });
|
|
4825
|
+
return response;
|
|
4826
|
+
}
|
|
4827
|
+
function clearLocalCart() {
|
|
4828
|
+
product_items.set([]);
|
|
4829
|
+
service_items.set([]);
|
|
4830
|
+
cart.set(null);
|
|
4831
|
+
quote.set(null);
|
|
4832
|
+
promo_code.set(null);
|
|
4833
|
+
cart_status.setKey("selected_shipping_method_id", null);
|
|
4834
|
+
}
|
|
4835
|
+
async function fetchQuote(input = {}) {
|
|
4836
|
+
if (checkoutItems(input).length === 0) {
|
|
4837
|
+
quote.set(null);
|
|
4838
|
+
return null;
|
|
4839
|
+
}
|
|
4840
|
+
cart_status.setKey("fetching_quote", true);
|
|
4841
|
+
cart_status.setKey("quote_error", null);
|
|
4842
|
+
try {
|
|
4843
|
+
const current = await syncCart(input);
|
|
4844
|
+
const response = await client.eshop.cart.quote({ id: current.id });
|
|
4845
|
+
quote.set(response);
|
|
4846
|
+
return response;
|
|
4847
|
+
} catch (error) {
|
|
4848
|
+
quote.set(null);
|
|
4849
|
+
cart_status.setKey("quote_error", readErrorMessage(error, "Failed to fetch quote."));
|
|
4850
|
+
throw error;
|
|
4851
|
+
} finally {
|
|
4852
|
+
cart_status.setKey("fetching_quote", false);
|
|
4853
|
+
}
|
|
4854
|
+
}
|
|
4855
|
+
async function checkout(input = {}) {
|
|
4856
|
+
if (checkoutItems(input).length === 0) throw new Error("Cart is empty");
|
|
4857
|
+
cart_status.setKey("processing_checkout", true);
|
|
4858
|
+
cart_status.setKey("error", null);
|
|
4859
|
+
try {
|
|
4860
|
+
const current = await syncCart(input);
|
|
4861
|
+
const quoteValue = quote.get();
|
|
4862
|
+
const paymentMethodKey = input.payment_method_key || current.payment_method_key || quoteValue?.money?.payment_method_key || void 0;
|
|
4863
|
+
let chargeAmount = firstFiniteNumber(quoteValue?.charge_amount, current.quote_snapshot?.charge_amount);
|
|
4864
|
+
if (paymentMethodKey === "credit_card" && chargeAmount === void 0) {
|
|
4865
|
+
const latestQuote = await client.eshop.cart.quote({ id: current.id });
|
|
4866
|
+
quote.set(latestQuote);
|
|
4867
|
+
chargeAmount = firstFiniteNumber(latestQuote.charge_amount, latestQuote.total);
|
|
4868
|
+
}
|
|
4869
|
+
if (paymentMethodKey === "credit_card" && (typeof chargeAmount !== "number" || !Number.isSafeInteger(chargeAmount) || chargeAmount < 0)) {
|
|
4870
|
+
throw new Error("Card checkout requires a non-negative integer charge amount in minor units");
|
|
4871
|
+
}
|
|
4872
|
+
const needsConfirmationToken = paymentMethodKey === "credit_card" && typeof chargeAmount === "number" && chargeAmount > 0;
|
|
4873
|
+
let confirmationTokenId;
|
|
4874
|
+
let returnUrl = input.return_url;
|
|
4875
|
+
const paymentController = input.payment ?? payment_controller.get();
|
|
4876
|
+
if (needsConfirmationToken) {
|
|
4877
|
+
if (!paymentController) throw new Error("Payment controller is required for card checkout");
|
|
4878
|
+
returnUrl = returnUrl || (typeof window !== "undefined" ? window.location.href : void 0);
|
|
4879
|
+
const token = await paymentController.createConfirmationToken({
|
|
4880
|
+
return_url: returnUrl,
|
|
4881
|
+
billing_details: input.billing_details
|
|
4882
|
+
});
|
|
4883
|
+
confirmationTokenId = token.confirmation_token_id;
|
|
4884
|
+
returnUrl = token.return_url || returnUrl;
|
|
4885
|
+
}
|
|
4886
|
+
const response = await client.eshop.cart.checkout({
|
|
4887
|
+
id: current.id,
|
|
4888
|
+
payment_method_key: paymentMethodKey,
|
|
4889
|
+
confirmation_token_id: confirmationTokenId,
|
|
4890
|
+
return_url: returnUrl
|
|
4891
|
+
});
|
|
4892
|
+
if (response.payment_action.type === "handle_next_action") {
|
|
4893
|
+
if (!paymentController) throw new Error("Payment controller is required for card authentication");
|
|
4894
|
+
await paymentController.handleNextAction(response.payment_action.client_secret);
|
|
4895
|
+
}
|
|
4896
|
+
const stored = {
|
|
4897
|
+
order_id: response.order_id,
|
|
4898
|
+
number: response.number,
|
|
4899
|
+
payment_action: response.payment_action,
|
|
4900
|
+
payment: response.payment,
|
|
4901
|
+
product_items: input.product_items || product_items.get(),
|
|
4902
|
+
service_items: input.service_items || service_items.get(),
|
|
4903
|
+
shipping_address: input.shipping_address || null,
|
|
4904
|
+
billing_address: input.billing_address || null,
|
|
4905
|
+
total: response.payment.amount,
|
|
4906
|
+
currency: response.payment.currency,
|
|
4907
|
+
payment_method_key: paymentMethodKey || null,
|
|
4908
|
+
created_at: Date.now()
|
|
4909
|
+
};
|
|
4910
|
+
last_order.set(stored);
|
|
4911
|
+
if (input.clear_after_checkout !== false) {
|
|
4912
|
+
clearLocalCart();
|
|
4913
|
+
}
|
|
4914
|
+
return response;
|
|
4915
|
+
} catch (error) {
|
|
4916
|
+
cart_status.setKey("error", readErrorMessage(error, "Checkout failed."));
|
|
4917
|
+
throw error;
|
|
4918
|
+
} finally {
|
|
4919
|
+
cart_status.setKey("processing_checkout", false);
|
|
4920
|
+
}
|
|
4921
|
+
}
|
|
4922
|
+
function serviceCalendar() {
|
|
4923
|
+
const state = service_state.get();
|
|
4924
|
+
const { currentMonth, selectedDate, availability, selectedProviderId } = state;
|
|
4925
|
+
const year = currentMonth.getFullYear();
|
|
4926
|
+
const monthIndex = currentMonth.getMonth();
|
|
4927
|
+
const first = new Date(year, monthIndex, 1);
|
|
4928
|
+
const last = new Date(year, monthIndex + 1, 0);
|
|
4929
|
+
const today = /* @__PURE__ */ new Date();
|
|
4930
|
+
today.setHours(0, 0, 0, 0);
|
|
4931
|
+
const cells = [];
|
|
4932
|
+
const pad = (first.getDay() + 6) % 7;
|
|
4933
|
+
for (let i = 0; i < pad; i++) {
|
|
4934
|
+
cells.push({
|
|
4935
|
+
date: /* @__PURE__ */ new Date(0),
|
|
4936
|
+
iso: "",
|
|
4937
|
+
available: false,
|
|
4938
|
+
isSelected: false,
|
|
4939
|
+
isInRange: false,
|
|
4940
|
+
isToday: false,
|
|
4941
|
+
blank: true
|
|
4942
|
+
});
|
|
4943
|
+
}
|
|
4944
|
+
for (let day = 1; day <= last.getDate(); day++) {
|
|
4945
|
+
const date = new Date(year, monthIndex, day);
|
|
4946
|
+
const iso = `${year}-${String(monthIndex + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
|
|
4947
|
+
cells.push({
|
|
4948
|
+
date,
|
|
4949
|
+
iso,
|
|
4950
|
+
available: hasAvailableSlotsForDate(availability, iso, selectedProviderId),
|
|
4951
|
+
isSelected: iso === selectedDate,
|
|
4952
|
+
isInRange: false,
|
|
4953
|
+
isToday: date.getTime() === today.getTime(),
|
|
4954
|
+
blank: false
|
|
4955
|
+
});
|
|
4956
|
+
}
|
|
4957
|
+
const suffix = (7 - cells.length % 7) % 7;
|
|
4958
|
+
for (let i = 0; i < suffix; i++) {
|
|
4959
|
+
cells.push({
|
|
4960
|
+
date: /* @__PURE__ */ new Date(0),
|
|
4961
|
+
iso: "",
|
|
4962
|
+
available: false,
|
|
4963
|
+
isSelected: false,
|
|
4964
|
+
isInRange: false,
|
|
4965
|
+
isToday: false,
|
|
4966
|
+
blank: true
|
|
4967
|
+
});
|
|
4968
|
+
}
|
|
4969
|
+
return cells;
|
|
4970
|
+
}
|
|
4971
|
+
function computeServiceSlots(dateStr) {
|
|
4972
|
+
const state = service_state.get();
|
|
4973
|
+
const { availability, selectedProviderId, timezone, service } = state;
|
|
4974
|
+
return getSlotsForDate(availability, dateStr, selectedProviderId).map((slot, index) => ({
|
|
4975
|
+
id: `${service?.id || "service"}-${slot.from}-${index}`,
|
|
4976
|
+
serviceId: service?.id || "",
|
|
4977
|
+
providerId: slot.providerId,
|
|
4978
|
+
from: slot.from,
|
|
4979
|
+
to: slot.to,
|
|
4980
|
+
timeText: formatServiceSlotTime(slot.from, slot.to, timezone),
|
|
4981
|
+
dateText: new Date(slot.from * 1e3).toLocaleDateString([], {
|
|
4982
|
+
weekday: "short",
|
|
4983
|
+
month: "short",
|
|
4984
|
+
day: "numeric",
|
|
4985
|
+
timeZone: timezone
|
|
4986
|
+
})
|
|
4987
|
+
}));
|
|
4988
|
+
}
|
|
4989
|
+
function toServiceCartItem(slots, forms = []) {
|
|
4990
|
+
const orderedSlots = [...slots].sort((left, right) => left.from - right.from || left.to - right.to);
|
|
4991
|
+
const [first] = orderedSlots;
|
|
4992
|
+
if (!first) throw new Error("At least one service slot is required");
|
|
4993
|
+
if (orderedSlots.some((slot) => slot.serviceId !== first.serviceId || slot.providerId !== first.providerId)) {
|
|
4994
|
+
throw new Error("A booking can only contain slots for one service and provider");
|
|
4995
|
+
}
|
|
4996
|
+
if (orderedSlots.some((slot) => !Number.isSafeInteger(slot.from) || !Number.isSafeInteger(slot.to) || slot.from >= slot.to)) {
|
|
4997
|
+
throw new Error("A booking contains an invalid service slot");
|
|
4998
|
+
}
|
|
4999
|
+
for (let index = 1; index < orderedSlots.length; index += 1) {
|
|
5000
|
+
if (orderedSlots[index - 1].to !== orderedSlots[index].from) {
|
|
5001
|
+
throw new Error("A multi-slot booking must contain adjacent slots");
|
|
5002
|
+
}
|
|
5003
|
+
}
|
|
5004
|
+
return {
|
|
5005
|
+
id: createId("booking"),
|
|
5006
|
+
service_id: first.serviceId,
|
|
5007
|
+
provider_id: first.providerId,
|
|
5008
|
+
slots: orderedSlots.map(({ from, to }) => ({ from, to })),
|
|
5009
|
+
forms,
|
|
5010
|
+
service_name: first.serviceName,
|
|
5011
|
+
date_text: first.dateText,
|
|
5012
|
+
time_text: first.timeText,
|
|
5013
|
+
is_multi_day: orderedSlots.length > 1
|
|
5014
|
+
};
|
|
5015
|
+
}
|
|
5016
|
+
async function syncServiceCart(items) {
|
|
5017
|
+
try {
|
|
5018
|
+
return await syncCart({
|
|
5019
|
+
product_items: product_items.get(),
|
|
5020
|
+
service_items: items
|
|
5021
|
+
});
|
|
5022
|
+
} catch (error) {
|
|
5023
|
+
service_state.setKey("quoteError", readErrorMessage(error, "Failed to sync service cart."));
|
|
5024
|
+
throw error;
|
|
5025
|
+
}
|
|
5026
|
+
}
|
|
5027
|
+
function serviceCurrentStepName() {
|
|
5028
|
+
const state = service_state.get();
|
|
5029
|
+
if (!state.service) return "";
|
|
5030
|
+
if (!state.selectedSlot || !state.dateTimeConfirmed) return "datetime";
|
|
5031
|
+
return "review";
|
|
5032
|
+
}
|
|
5033
|
+
const service_current_step_name = computed(service_state, serviceCurrentStepName);
|
|
5034
|
+
const service_can_proceed = computed(service_state, (state) => {
|
|
5035
|
+
const step = serviceCurrentStepName();
|
|
5036
|
+
if (step === "datetime") {
|
|
5037
|
+
return !!(state.selectedDate && state.selectedSlot);
|
|
5038
|
+
}
|
|
5039
|
+
if (step === "review") return true;
|
|
5040
|
+
return false;
|
|
5041
|
+
});
|
|
5042
|
+
const service_month_year = computed(
|
|
5043
|
+
service_state,
|
|
5044
|
+
(state) => state.currentMonth.toLocaleString(void 0, {
|
|
5045
|
+
month: "long",
|
|
5046
|
+
year: "numeric"
|
|
5047
|
+
})
|
|
5048
|
+
);
|
|
5049
|
+
const service_chain_start = computed(service_items, (items) => {
|
|
5050
|
+
const slots = items.flatMap((item) => item.slots);
|
|
5051
|
+
if (!slots.length) return null;
|
|
5052
|
+
return Math.max(...slots.map((slot) => slot.to));
|
|
5053
|
+
});
|
|
5054
|
+
const service_total_steps = computed(service_state, (state) => state.service ? 2 : 0);
|
|
5055
|
+
const service_steps = computed(service_state, () => ({
|
|
5056
|
+
1: { name: "datetime" },
|
|
5057
|
+
2: { name: "review" }
|
|
5058
|
+
}));
|
|
5059
|
+
const service_current_step = computed([service_current_step_name, service_steps], (name, steps) => {
|
|
5060
|
+
for (const [idx, step] of Object.entries(steps)) {
|
|
5061
|
+
if (step.name === name) return Number(idx);
|
|
5062
|
+
}
|
|
5063
|
+
return 1;
|
|
5064
|
+
});
|
|
5065
|
+
function formatServiceDateDisplay(value) {
|
|
5066
|
+
if (!value) return "";
|
|
5067
|
+
return new Date(value).toLocaleDateString(void 0, {
|
|
5068
|
+
month: "short",
|
|
5069
|
+
day: "numeric"
|
|
5070
|
+
});
|
|
5071
|
+
}
|
|
5072
|
+
function configuredServiceFormIds(relationship) {
|
|
5073
|
+
const formIds = relationship.forms.map((entry) => entry.form_id.trim());
|
|
5074
|
+
if (formIds.some((formId) => !formId) || new Set(formIds).size !== formIds.length) {
|
|
5075
|
+
throw new Error(`Service provider ${relationship.provider_id} has blank or duplicate configured form IDs`);
|
|
5076
|
+
}
|
|
5077
|
+
return formIds;
|
|
5078
|
+
}
|
|
5079
|
+
function resolveServiceProvider(state, providerId) {
|
|
5080
|
+
const serviceId = state.service?.id;
|
|
5081
|
+
if (!serviceId) return null;
|
|
5082
|
+
const relationships = state.serviceProviders.filter((relationship) => relationship.service_id === serviceId);
|
|
5083
|
+
const targetProviderId = providerId ?? state.selectedSlot?.providerId ?? state.selectedProviderId;
|
|
5084
|
+
if (targetProviderId) {
|
|
5085
|
+
return relationships.find((relationship) => relationship.provider_id === targetProviderId) || null;
|
|
5086
|
+
}
|
|
5087
|
+
return relationships.length === 1 ? relationships[0] : null;
|
|
5088
|
+
}
|
|
5089
|
+
function clearServiceFormState(error = null, loading = false) {
|
|
5090
|
+
service_form_state.set({
|
|
5091
|
+
provider_id: null,
|
|
5092
|
+
groups: [],
|
|
5093
|
+
loading,
|
|
5094
|
+
error
|
|
5095
|
+
});
|
|
5096
|
+
}
|
|
5097
|
+
function activateServiceProviderForms(providerId, reset = false) {
|
|
5098
|
+
const relationship = resolveServiceProvider(service_state.get(), providerId);
|
|
5099
|
+
if (!relationship) {
|
|
5100
|
+
clearServiceFormState();
|
|
5101
|
+
return null;
|
|
5102
|
+
}
|
|
5103
|
+
const formIds = configuredServiceFormIds(relationship);
|
|
5104
|
+
const current = service_form_state.get();
|
|
5105
|
+
const currentFormIds = current.groups.map((group) => group.form.id);
|
|
5106
|
+
if (!reset && current.provider_id === relationship.provider_id && currentFormIds.length === formIds.length && currentFormIds.every((formId, index) => formId === formIds[index])) {
|
|
5107
|
+
if (current.error) service_form_state.setKey("error", null);
|
|
5108
|
+
return relationship;
|
|
5109
|
+
}
|
|
5110
|
+
const groups = formIds.map((formId) => {
|
|
5111
|
+
const form = service_form_definitions.get(formId);
|
|
5112
|
+
if (!form) throw new Error(`Configured booking form '${formId}' was not loaded`);
|
|
5113
|
+
return {
|
|
5114
|
+
form,
|
|
5115
|
+
blocks: form.schema.map(formSchemaToBlock)
|
|
5116
|
+
};
|
|
5117
|
+
});
|
|
5118
|
+
service_form_state.set({
|
|
5119
|
+
provider_id: relationship.provider_id,
|
|
5120
|
+
groups,
|
|
5121
|
+
loading: false,
|
|
5122
|
+
error: null
|
|
5123
|
+
});
|
|
5124
|
+
return relationship;
|
|
5125
|
+
}
|
|
5126
|
+
async function loadServiceFormDefinitions(relationships) {
|
|
5127
|
+
const formIds = [
|
|
5128
|
+
...new Set(relationships.flatMap((relationship) => configuredServiceFormIds(relationship)))
|
|
5129
|
+
];
|
|
5130
|
+
if (formIds.length === 0) return;
|
|
5131
|
+
const forms = await Promise.all(formIds.map((formId) => loadForm({ id: formId })));
|
|
5132
|
+
for (let index = 0; index < forms.length; index += 1) {
|
|
5133
|
+
const form = forms[index];
|
|
5134
|
+
const formId = formIds[index];
|
|
5135
|
+
if (form.id !== formId) {
|
|
5136
|
+
throw new Error(`Configured booking form '${formId}' resolved to '${form.id}'`);
|
|
5137
|
+
}
|
|
5138
|
+
}
|
|
5139
|
+
for (const form of forms) service_form_definitions.set(form.id, form);
|
|
5140
|
+
}
|
|
5141
|
+
function configuredServiceFormEntries(relationship) {
|
|
5142
|
+
const formIds = configuredServiceFormIds(relationship);
|
|
5143
|
+
if (formIds.length === 0) return [];
|
|
5144
|
+
activateServiceProviderForms(relationship.provider_id);
|
|
5145
|
+
const state = service_form_state.get();
|
|
5146
|
+
if (state.provider_id !== relationship.provider_id || state.groups.length !== formIds.length || state.groups.some((group, index) => group.form.id !== formIds[index])) {
|
|
5147
|
+
throw new Error(`Booking forms are not ready for provider ${relationship.provider_id}`);
|
|
5148
|
+
}
|
|
5149
|
+
return state.groups.map(
|
|
5150
|
+
(group) => createFormEntryFromValues(
|
|
5151
|
+
group.form,
|
|
5152
|
+
Object.fromEntries(group.blocks.map((block) => [block.key, block.value]))
|
|
5153
|
+
)
|
|
5154
|
+
);
|
|
5155
|
+
}
|
|
5156
|
+
const service_controller = {
|
|
5157
|
+
async initialize() {
|
|
5158
|
+
service_state.setKey("tzGroups", normalizeTimezoneGroups(client.utils.tzGroups));
|
|
5159
|
+
await ensureCart();
|
|
5160
|
+
const methods = market.get()?.payment_methods || [];
|
|
5161
|
+
if (methods.length) service_state.setKey("availablePaymentMethods", methods);
|
|
5162
|
+
},
|
|
5163
|
+
setTimezone(tz) {
|
|
5164
|
+
service_state.setKey("timezone", tz);
|
|
5165
|
+
service_state.setKey("calendar", serviceCalendar());
|
|
5166
|
+
const state = service_state.get();
|
|
5167
|
+
if (state.selectedDate) {
|
|
5168
|
+
service_state.setKey("slots", computeServiceSlots(state.selectedDate));
|
|
5169
|
+
service_state.setKey("selectedSlot", null);
|
|
5170
|
+
service_state.setKey("quote", null);
|
|
5171
|
+
service_state.setKey("quoteError", null);
|
|
5172
|
+
activateServiceProviderForms();
|
|
5173
|
+
}
|
|
5174
|
+
},
|
|
5175
|
+
async select(service) {
|
|
5176
|
+
service_form_definitions.clear();
|
|
5177
|
+
clearServiceFormState(null, true);
|
|
5178
|
+
service_state.set({
|
|
5179
|
+
...service_state.get(),
|
|
5180
|
+
service: null,
|
|
5181
|
+
serviceProviders: [],
|
|
5182
|
+
providers: [],
|
|
5183
|
+
selectedProviderId: null,
|
|
5184
|
+
availability: null,
|
|
5185
|
+
selectedDate: null,
|
|
5186
|
+
slots: [],
|
|
5187
|
+
selectedSlot: null,
|
|
5188
|
+
dateTimeConfirmed: false,
|
|
5189
|
+
quote: null,
|
|
5190
|
+
quoteError: null,
|
|
5191
|
+
loading: true
|
|
5192
|
+
});
|
|
5193
|
+
try {
|
|
5194
|
+
const [fullService, serviceProviders] = await Promise.all([
|
|
5195
|
+
client.eshop.service.get({ id: service.id }),
|
|
5196
|
+
client.eshop.service.findProviders({
|
|
5197
|
+
service_id: service.id
|
|
5198
|
+
})
|
|
5199
|
+
]);
|
|
5200
|
+
const providerIds = [...new Set(serviceProviders.map((relationship) => relationship.provider_id))];
|
|
5201
|
+
const [providerResults] = await Promise.all([
|
|
5202
|
+
Promise.all(providerIds.map((id) => client.eshop.provider.get({ id }).catch(() => null))),
|
|
5203
|
+
loadServiceFormDefinitions(serviceProviders)
|
|
5204
|
+
]);
|
|
5205
|
+
service_state.set({
|
|
5206
|
+
...service_state.get(),
|
|
5207
|
+
service: fullService,
|
|
5208
|
+
serviceProviders,
|
|
5209
|
+
providers: providerResults.filter(
|
|
5210
|
+
(provider) => provider !== null
|
|
5211
|
+
),
|
|
5212
|
+
selectedProviderId: null,
|
|
5213
|
+
availability: null,
|
|
5214
|
+
selectedDate: null,
|
|
5215
|
+
slots: [],
|
|
5216
|
+
selectedSlot: null,
|
|
5217
|
+
currentMonth: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), 1),
|
|
5218
|
+
loading: false,
|
|
5219
|
+
dateTimeConfirmed: false,
|
|
5220
|
+
quote: null,
|
|
5221
|
+
quoteError: null
|
|
5222
|
+
});
|
|
5223
|
+
activateServiceProviderForms();
|
|
5224
|
+
await service_controller.loadMonth();
|
|
5225
|
+
} catch (error) {
|
|
5226
|
+
service_form_definitions.clear();
|
|
5227
|
+
clearServiceFormState(readErrorMessage(error, "Failed to load booking forms."));
|
|
5228
|
+
service_state.setKey("loading", false);
|
|
5229
|
+
throw error;
|
|
5230
|
+
}
|
|
5231
|
+
},
|
|
5232
|
+
async loadMonth() {
|
|
5233
|
+
const state = service_state.get();
|
|
5234
|
+
if (!state.service) return;
|
|
5235
|
+
service_state.setKey("loading", true);
|
|
5236
|
+
try {
|
|
5237
|
+
const chainedStart = service_chain_start.get();
|
|
5238
|
+
let from;
|
|
5239
|
+
let to;
|
|
5240
|
+
if (chainedStart) {
|
|
5241
|
+
from = chainedStart;
|
|
5242
|
+
to = chainedStart;
|
|
5243
|
+
} else {
|
|
5244
|
+
const month = state.currentMonth;
|
|
5245
|
+
from = Math.floor(Date.UTC(month.getFullYear(), month.getMonth(), 1) / 1e3);
|
|
5246
|
+
to = Math.floor(Date.UTC(month.getFullYear(), month.getMonth() + 1, 1) / 1e3);
|
|
5247
|
+
}
|
|
5248
|
+
const availability = await loadAvailability({
|
|
5249
|
+
service_id: state.service.id,
|
|
5250
|
+
from,
|
|
5251
|
+
to
|
|
5252
|
+
});
|
|
5253
|
+
service_state.setKey("availability", availability);
|
|
5254
|
+
service_state.setKey("calendar", serviceCalendar());
|
|
5255
|
+
} finally {
|
|
5256
|
+
service_state.setKey("loading", false);
|
|
5257
|
+
}
|
|
5258
|
+
},
|
|
5259
|
+
prevMonth() {
|
|
5260
|
+
const { currentMonth } = service_state.get();
|
|
5261
|
+
service_state.setKey("currentMonth", new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1, 1));
|
|
5262
|
+
void service_controller.loadMonth();
|
|
5263
|
+
},
|
|
5264
|
+
nextMonth() {
|
|
5265
|
+
const { currentMonth } = service_state.get();
|
|
5266
|
+
service_state.setKey("currentMonth", new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1, 1));
|
|
5267
|
+
void service_controller.loadMonth();
|
|
5268
|
+
},
|
|
5269
|
+
selectProvider(providerId) {
|
|
5270
|
+
const state = service_state.get();
|
|
5271
|
+
if (providerId && !resolveServiceProvider(state, providerId)) {
|
|
5272
|
+
throw new Error(`Provider ${providerId} is not configured for the selected service`);
|
|
5273
|
+
}
|
|
5274
|
+
service_state.set({
|
|
5275
|
+
...state,
|
|
5276
|
+
selectedProviderId: providerId,
|
|
5277
|
+
selectedDate: null,
|
|
5278
|
+
slots: [],
|
|
5279
|
+
selectedSlot: null,
|
|
5280
|
+
dateTimeConfirmed: false,
|
|
5281
|
+
quote: null,
|
|
5282
|
+
quoteError: null
|
|
5283
|
+
});
|
|
5284
|
+
activateServiceProviderForms(providerId);
|
|
5285
|
+
void service_controller.loadMonth();
|
|
5286
|
+
},
|
|
5287
|
+
selectDate(cell) {
|
|
5288
|
+
if (cell.blank || !cell.available) return;
|
|
5289
|
+
const state = service_state.get();
|
|
5290
|
+
service_state.set({
|
|
5291
|
+
...state,
|
|
5292
|
+
selectedDate: cell.iso,
|
|
5293
|
+
slots: computeServiceSlots(cell.iso),
|
|
5294
|
+
selectedSlot: null,
|
|
5295
|
+
dateTimeConfirmed: false,
|
|
5296
|
+
quote: null,
|
|
5297
|
+
quoteError: null
|
|
5298
|
+
});
|
|
5299
|
+
activateServiceProviderForms();
|
|
5300
|
+
service_state.setKey("calendar", serviceCalendar());
|
|
5301
|
+
},
|
|
5302
|
+
selectTimeSlot(slot) {
|
|
5303
|
+
const state = service_state.get();
|
|
5304
|
+
if (slot) {
|
|
5305
|
+
if (!state.service || slot.serviceId !== state.service.id) {
|
|
5306
|
+
throw new Error("The selected slot does not belong to the selected service");
|
|
5307
|
+
}
|
|
5308
|
+
if (state.selectedProviderId && slot.providerId !== state.selectedProviderId) {
|
|
5309
|
+
throw new Error("The selected slot does not belong to the selected provider");
|
|
5310
|
+
}
|
|
5311
|
+
if (!resolveServiceProvider(state, slot.providerId)) {
|
|
5312
|
+
throw new Error(`Provider ${slot.providerId} is not configured for the selected service`);
|
|
5313
|
+
}
|
|
5314
|
+
}
|
|
5315
|
+
service_state.set({
|
|
5316
|
+
...state,
|
|
5317
|
+
selectedSlot: slot,
|
|
5318
|
+
dateTimeConfirmed: false,
|
|
5319
|
+
quote: null,
|
|
5320
|
+
quoteError: null
|
|
5321
|
+
});
|
|
5322
|
+
activateServiceProviderForms(slot?.providerId);
|
|
5323
|
+
},
|
|
5324
|
+
resetDateSelection() {
|
|
5325
|
+
service_state.set({
|
|
5326
|
+
...service_state.get(),
|
|
5327
|
+
selectedDate: null,
|
|
5328
|
+
slots: [],
|
|
5329
|
+
selectedSlot: null,
|
|
5330
|
+
dateTimeConfirmed: false,
|
|
5331
|
+
quote: null,
|
|
5332
|
+
quoteError: null
|
|
5333
|
+
});
|
|
5334
|
+
activateServiceProviderForms();
|
|
5335
|
+
},
|
|
5336
|
+
updateCalendar() {
|
|
5337
|
+
service_state.setKey("calendar", serviceCalendar());
|
|
5338
|
+
},
|
|
5339
|
+
findFirstAvailable() {
|
|
5340
|
+
for (const day of service_state.get().calendar) {
|
|
5341
|
+
if (!day.blank && day.available) {
|
|
5342
|
+
service_controller.selectDate(day);
|
|
5343
|
+
return;
|
|
5344
|
+
}
|
|
5345
|
+
}
|
|
5346
|
+
},
|
|
5347
|
+
async addToCart(explicitSlots) {
|
|
5348
|
+
const state = service_state.get();
|
|
5349
|
+
const slots = explicitSlots || (state.selectedSlot ? [state.selectedSlot] : []);
|
|
5350
|
+
if (slots.length === 0) return;
|
|
5351
|
+
const first = slots[0];
|
|
5352
|
+
if (!state.service || first.serviceId !== state.service.id) {
|
|
5353
|
+
throw new Error("The booking slots do not belong to the selected service");
|
|
5354
|
+
}
|
|
5355
|
+
const relationship = resolveServiceProvider(state, first.providerId);
|
|
5356
|
+
if (!relationship) {
|
|
5357
|
+
throw new Error(`Provider ${first.providerId} is not configured for the selected service`);
|
|
5358
|
+
}
|
|
5359
|
+
let forms;
|
|
5360
|
+
try {
|
|
5361
|
+
forms = configuredServiceFormEntries(relationship);
|
|
5362
|
+
} catch (error) {
|
|
5363
|
+
service_form_state.setKey("error", readErrorMessage(error, "Booking forms are invalid."));
|
|
5364
|
+
throw error;
|
|
5365
|
+
}
|
|
5366
|
+
const displayName = serviceName(state.service, currentLocale());
|
|
5367
|
+
const enriched = slots.map((slot) => ({
|
|
5368
|
+
...slot,
|
|
5369
|
+
serviceName: displayName,
|
|
5370
|
+
date: slot.dateText
|
|
5371
|
+
}));
|
|
5372
|
+
const nextItems = [...service_items.get(), toServiceCartItem(enriched, forms)];
|
|
5373
|
+
await syncServiceCart(nextItems);
|
|
5374
|
+
service_state.set({
|
|
5375
|
+
...service_state.get(),
|
|
5376
|
+
selectedDate: null,
|
|
5377
|
+
slots: [],
|
|
5378
|
+
selectedSlot: null,
|
|
5379
|
+
dateTimeConfirmed: false,
|
|
5380
|
+
quote: null,
|
|
5381
|
+
quoteError: null
|
|
5382
|
+
});
|
|
5383
|
+
activateServiceProviderForms(service_state.get().selectedProviderId, true);
|
|
5384
|
+
service_state.setKey("calendar", serviceCalendar());
|
|
5385
|
+
},
|
|
5386
|
+
async removeFromCart(bookingId) {
|
|
5387
|
+
await syncServiceCart(service_items.get().filter((item) => item.id !== bookingId));
|
|
5388
|
+
},
|
|
5389
|
+
async clearCart() {
|
|
5390
|
+
await syncServiceCart([]);
|
|
5391
|
+
},
|
|
5392
|
+
async checkout(paymentMethodId, forms = []) {
|
|
5393
|
+
const state = service_state.get();
|
|
5394
|
+
const items = service_items.get();
|
|
5395
|
+
if (!items.length) throw new Error("Cart is empty");
|
|
5396
|
+
service_state.setKey("loading", true);
|
|
5397
|
+
try {
|
|
5398
|
+
const result = await checkout({
|
|
5399
|
+
service_items: items,
|
|
5400
|
+
payment_method_key: paymentMethodId,
|
|
5401
|
+
promo_code: state.promoCode || void 0,
|
|
5402
|
+
forms
|
|
5403
|
+
});
|
|
5404
|
+
service_state.setKey("cartId", cart.get()?.id || null);
|
|
5405
|
+
return result;
|
|
5406
|
+
} finally {
|
|
5407
|
+
service_state.setKey("loading", false);
|
|
5408
|
+
}
|
|
5409
|
+
},
|
|
5410
|
+
async fetchQuote(paymentMethodId, promoCode) {
|
|
5411
|
+
service_state.get();
|
|
5412
|
+
const items = service_items.get();
|
|
5413
|
+
if (!items.length) return null;
|
|
5414
|
+
service_state.setKey("fetchingQuote", true);
|
|
5415
|
+
service_state.setKey("quoteError", null);
|
|
5416
|
+
try {
|
|
5417
|
+
service_state.setKey("promoCode", promoCode || null);
|
|
5418
|
+
const response = await fetchQuote({
|
|
5419
|
+
service_items: items,
|
|
5420
|
+
payment_method_key: paymentMethodId,
|
|
5421
|
+
promo_code: promoCode || void 0
|
|
5422
|
+
});
|
|
5423
|
+
service_state.setKey("cartId", cart.get()?.id || null);
|
|
5424
|
+
service_state.setKey("quote", response);
|
|
5425
|
+
const methods = response?.payment_methods || market.get()?.payment_methods || [];
|
|
5426
|
+
if (methods.length) service_state.setKey("availablePaymentMethods", methods);
|
|
5427
|
+
return response;
|
|
5428
|
+
} catch (error) {
|
|
5429
|
+
service_state.setKey("quoteError", readErrorMessage(error, "Failed to fetch quote."));
|
|
5430
|
+
return null;
|
|
5431
|
+
} finally {
|
|
5432
|
+
service_state.setKey("fetchingQuote", false);
|
|
5433
|
+
}
|
|
5434
|
+
},
|
|
5435
|
+
getProvidersList() {
|
|
5436
|
+
return service_state.get().providers;
|
|
5437
|
+
},
|
|
5438
|
+
prevStep() {
|
|
5439
|
+
const current = serviceCurrentStepName();
|
|
5440
|
+
if (current === "review") {
|
|
5441
|
+
service_state.setKey("dateTimeConfirmed", false);
|
|
5442
|
+
return;
|
|
5443
|
+
}
|
|
5444
|
+
if (current === "datetime") {
|
|
5445
|
+
service_state.setKey("selectedSlot", null);
|
|
5446
|
+
service_state.setKey("dateTimeConfirmed", false);
|
|
5447
|
+
service_state.setKey("quote", null);
|
|
5448
|
+
service_state.setKey("quoteError", null);
|
|
5449
|
+
activateServiceProviderForms();
|
|
5450
|
+
}
|
|
5451
|
+
},
|
|
5452
|
+
nextStep() {
|
|
5453
|
+
if (serviceCurrentStepName() === "datetime" && service_can_proceed.get()) {
|
|
5454
|
+
service_state.setKey("dateTimeConfirmed", true);
|
|
5455
|
+
}
|
|
5456
|
+
},
|
|
5457
|
+
getServicePrice() {
|
|
5458
|
+
const state = service_state.get();
|
|
5459
|
+
const relationship = resolveServiceProvider(state);
|
|
5460
|
+
if (!relationship) return "";
|
|
5461
|
+
try {
|
|
5462
|
+
const price = priceForMarket(relationship.prices, currentMarketKey(), market.get()?.currency);
|
|
5463
|
+
return client.utils.formatPrice([price]);
|
|
5464
|
+
} catch {
|
|
5465
|
+
return "";
|
|
5466
|
+
}
|
|
5467
|
+
},
|
|
5468
|
+
formatDateDisplay: formatServiceDateDisplay,
|
|
5469
|
+
serviceItemsFromSlots(slots, forms = []) {
|
|
5470
|
+
return slots.length ? [toServiceCartItem(slots, forms)] : [];
|
|
5471
|
+
}
|
|
5472
|
+
};
|
|
5473
|
+
async function loadEntry(params, options) {
|
|
5474
|
+
cms_state.setKey("loading", true);
|
|
5475
|
+
cms_state.setKey("error", null);
|
|
5476
|
+
try {
|
|
5477
|
+
const { locale: nextLocale, market: nextMarket, ...entryParams } = params;
|
|
5478
|
+
setContext({ locale: nextLocale, market: nextMarket });
|
|
5479
|
+
if (entryParams.id) {
|
|
5480
|
+
const entry2 = await client.cms.entry.get(entryParams, options);
|
|
5481
|
+
const cacheKey = entryParams.key || entryParams.id || entry2.id;
|
|
5482
|
+
cms_state.setKey("entries", {
|
|
5483
|
+
...cms_state.get().entries,
|
|
5484
|
+
[cacheKey]: entry2
|
|
5485
|
+
});
|
|
5486
|
+
return entry2;
|
|
5487
|
+
}
|
|
5488
|
+
if (!entryParams.collection_id || !entryParams.key) {
|
|
5489
|
+
throw new Error("ArkyCmsEntryParams requires id, or collection_id and key");
|
|
5490
|
+
}
|
|
5491
|
+
const result = await client.cms.entry.find(
|
|
5492
|
+
{
|
|
5493
|
+
...entryParams,
|
|
5494
|
+
collection_id: entryParams.collection_id,
|
|
5495
|
+
key: entryParams.key,
|
|
5496
|
+
limit: 1
|
|
5497
|
+
},
|
|
5498
|
+
options
|
|
5499
|
+
);
|
|
5500
|
+
const entry = result.items?.[0];
|
|
5501
|
+
if (!entry) {
|
|
5502
|
+
throw new Error("CMS entry not found");
|
|
5503
|
+
}
|
|
5504
|
+
cms_state.setKey("entries", {
|
|
5505
|
+
...cms_state.get().entries,
|
|
5506
|
+
[entryParams.key]: entry
|
|
5507
|
+
});
|
|
5508
|
+
return entry;
|
|
5509
|
+
} catch (error) {
|
|
5510
|
+
cms_state.setKey("error", readErrorMessage(error, "Failed to load CMS entry."));
|
|
5511
|
+
throw error;
|
|
5512
|
+
} finally {
|
|
5513
|
+
cms_state.setKey("loading", false);
|
|
5514
|
+
}
|
|
5515
|
+
}
|
|
5516
|
+
function formCacheKey(params) {
|
|
5517
|
+
const identifier = params.id ? `id:${params.id}` : params.key ? `key:${params.key}` : "missing";
|
|
5518
|
+
return identifier;
|
|
5519
|
+
}
|
|
5520
|
+
async function loadForm(params, options) {
|
|
5521
|
+
cms_state.setKey("loading", true);
|
|
5522
|
+
cms_state.setKey("error", null);
|
|
5523
|
+
try {
|
|
5524
|
+
const form = await client.cms.form.get(params, options);
|
|
5525
|
+
const forms = { ...cms_state.get().forms };
|
|
5526
|
+
forms[formCacheKey({ id: form.id })] = form;
|
|
5527
|
+
forms[formCacheKey({ key: form.key })] = form;
|
|
5528
|
+
cms_state.setKey("forms", forms);
|
|
5529
|
+
return form;
|
|
5530
|
+
} catch (error) {
|
|
5531
|
+
cms_state.setKey("error", readErrorMessage(error, "Failed to load CMS form."));
|
|
5532
|
+
throw error;
|
|
5533
|
+
} finally {
|
|
5534
|
+
cms_state.setKey("loading", false);
|
|
5535
|
+
}
|
|
5536
|
+
}
|
|
5537
|
+
async function submitForm(params, options) {
|
|
5538
|
+
await ensureSession();
|
|
5539
|
+
return client.cms.form.submit(params, options);
|
|
5540
|
+
}
|
|
5541
|
+
async function submitFormByKey(params, options) {
|
|
5542
|
+
const form = await loadForm({ key: params.key }, options);
|
|
5543
|
+
const entry = createFormEntryFromValues(form, params.values);
|
|
5544
|
+
return submitForm({ form_id: form.id, fields: entry.fields }, options);
|
|
5545
|
+
}
|
|
5546
|
+
async function loadProducts(params = {}, options) {
|
|
5547
|
+
eshop_state.setKey("loading_products", true);
|
|
5548
|
+
eshop_state.setKey("error", null);
|
|
5549
|
+
try {
|
|
5550
|
+
const response = await client.eshop.product.find(params, options);
|
|
5551
|
+
eshop_state.setKey("products", response.items || []);
|
|
5552
|
+
eshop_state.setKey("product_cursor", response.cursor || null);
|
|
5553
|
+
return response;
|
|
5554
|
+
} catch (error) {
|
|
5555
|
+
eshop_state.setKey("error", readErrorMessage(error, "Failed to load products."));
|
|
5556
|
+
throw error;
|
|
5557
|
+
} finally {
|
|
5558
|
+
eshop_state.setKey("loading_products", false);
|
|
5559
|
+
}
|
|
5560
|
+
}
|
|
5561
|
+
async function loadServices(params = {}, options) {
|
|
5562
|
+
eshop_state.setKey("loading_services", true);
|
|
5563
|
+
eshop_state.setKey("error", null);
|
|
5564
|
+
try {
|
|
5565
|
+
const response = await client.eshop.service.find(params, options);
|
|
5566
|
+
eshop_state.setKey("services", response.items || []);
|
|
5567
|
+
eshop_state.setKey("service_cursor", response.cursor || null);
|
|
5568
|
+
return response;
|
|
5569
|
+
} catch (error) {
|
|
5570
|
+
eshop_state.setKey("error", readErrorMessage(error, "Failed to load services."));
|
|
5571
|
+
throw error;
|
|
5572
|
+
} finally {
|
|
5573
|
+
eshop_state.setKey("loading_services", false);
|
|
5574
|
+
}
|
|
5575
|
+
}
|
|
5576
|
+
async function loadProviders(params = {}, options) {
|
|
5577
|
+
eshop_state.setKey("loading_providers", true);
|
|
5578
|
+
eshop_state.setKey("error", null);
|
|
5579
|
+
try {
|
|
5580
|
+
const response = await client.eshop.provider.find(params, options);
|
|
5581
|
+
eshop_state.setKey("providers", response.items || []);
|
|
5582
|
+
eshop_state.setKey("provider_cursor", response.cursor || null);
|
|
5583
|
+
return response;
|
|
5584
|
+
} catch (error) {
|
|
5585
|
+
eshop_state.setKey("error", readErrorMessage(error, "Failed to load providers."));
|
|
5586
|
+
throw error;
|
|
5587
|
+
} finally {
|
|
5588
|
+
eshop_state.setKey("loading_providers", false);
|
|
5589
|
+
}
|
|
5590
|
+
}
|
|
5591
|
+
async function loadAvailability(params, options) {
|
|
5592
|
+
eshop_state.setKey("loading_availability", true);
|
|
5593
|
+
eshop_state.setKey("error", null);
|
|
5594
|
+
try {
|
|
5595
|
+
const response = await client.eshop.service.getAvailability(params, options);
|
|
5596
|
+
eshop_state.setKey("availability", response);
|
|
5597
|
+
return response;
|
|
5598
|
+
} catch (error) {
|
|
5599
|
+
eshop_state.setKey("error", readErrorMessage(error, "Failed to load availability."));
|
|
5600
|
+
throw error;
|
|
5601
|
+
} finally {
|
|
5602
|
+
eshop_state.setKey("loading_availability", false);
|
|
5603
|
+
}
|
|
5604
|
+
}
|
|
5605
|
+
async function useExperiment(params) {
|
|
5606
|
+
await ensureSession();
|
|
5607
|
+
const input = typeof params === "string" ? { key: params } : params;
|
|
5608
|
+
return client.experiments.use(input);
|
|
5609
|
+
}
|
|
5610
|
+
async function trackAction(params) {
|
|
5611
|
+
await ensureSession();
|
|
5612
|
+
return client.action.track(params);
|
|
5613
|
+
}
|
|
5614
|
+
const cart_store = {
|
|
5615
|
+
cart,
|
|
5616
|
+
product_items,
|
|
5617
|
+
service_items,
|
|
5618
|
+
quote_result: quote,
|
|
5619
|
+
promo_code,
|
|
5620
|
+
last_order,
|
|
5621
|
+
status: cart_status,
|
|
5622
|
+
product_item_count,
|
|
5623
|
+
service_item_count,
|
|
5624
|
+
item_count,
|
|
5625
|
+
snapshot,
|
|
5626
|
+
load: ensureCart,
|
|
5627
|
+
refresh: syncCart,
|
|
5628
|
+
addProduct,
|
|
5629
|
+
setProductQuantity,
|
|
5630
|
+
removeProduct,
|
|
5631
|
+
addServiceItem,
|
|
5632
|
+
removeServiceItem,
|
|
5633
|
+
clear: clearCart,
|
|
5634
|
+
clearLocal: clearLocalCart,
|
|
5635
|
+
quote: fetchQuote,
|
|
5636
|
+
checkout,
|
|
5637
|
+
payment: {
|
|
5638
|
+
controller: payment_controller,
|
|
5639
|
+
ready: payment_ready,
|
|
5640
|
+
setController: setPaymentController,
|
|
5641
|
+
getController: () => payment_controller.get(),
|
|
5642
|
+
mountStripe: mountStripePayment,
|
|
5643
|
+
update: updatePaymentController,
|
|
5644
|
+
destroy: destroyPaymentController
|
|
5645
|
+
},
|
|
5646
|
+
applyPromoCode(code, input = {}) {
|
|
5647
|
+
return fetchQuote({ ...input, promo_code: code });
|
|
5648
|
+
},
|
|
5649
|
+
removePromoCode(input = {}) {
|
|
5650
|
+
return fetchQuote({ ...input, promo_code: null });
|
|
5651
|
+
},
|
|
5652
|
+
selectShippingMethod(id) {
|
|
5653
|
+
cart_status.setKey("selected_shipping_method_id", id);
|
|
5654
|
+
},
|
|
5655
|
+
locationToAddress,
|
|
5656
|
+
createFormEntry,
|
|
5657
|
+
buildItems: checkoutItems,
|
|
5658
|
+
buildProductItems: toProductCheckoutItems,
|
|
5659
|
+
buildServiceItems: toServiceCheckoutItems
|
|
5660
|
+
};
|
|
5661
|
+
const product_store = {
|
|
5662
|
+
get: (params, options) => client.eshop.product.get(params, options),
|
|
5663
|
+
list: loadProducts
|
|
5664
|
+
};
|
|
5665
|
+
const service_store = {
|
|
5666
|
+
get: (params, options) => client.eshop.service.get(params, options),
|
|
5667
|
+
list: loadServices,
|
|
5668
|
+
listProviders: (params, options) => client.eshop.service.findProviders(params, options),
|
|
5669
|
+
getAvailability: loadAvailability,
|
|
5670
|
+
state: service_state,
|
|
5671
|
+
form_state: service_form_state,
|
|
5672
|
+
form_groups: service_form_groups,
|
|
5673
|
+
form_blocks: service_form_blocks,
|
|
5674
|
+
current_step_name: service_current_step_name,
|
|
5675
|
+
can_proceed: service_can_proceed,
|
|
5676
|
+
month_year: service_month_year,
|
|
5677
|
+
chain_start: service_chain_start,
|
|
5678
|
+
total_steps: service_total_steps,
|
|
5679
|
+
steps: service_steps,
|
|
5680
|
+
current_step: service_current_step,
|
|
5681
|
+
initialize: service_controller.initialize,
|
|
5682
|
+
select: service_controller.select,
|
|
5683
|
+
setTimezone: service_controller.setTimezone,
|
|
5684
|
+
loadMonth: service_controller.loadMonth,
|
|
5685
|
+
prevMonth: service_controller.prevMonth,
|
|
5686
|
+
nextMonth: service_controller.nextMonth,
|
|
5687
|
+
selectProvider: service_controller.selectProvider,
|
|
5688
|
+
selectDate: service_controller.selectDate,
|
|
5689
|
+
selectTimeSlot: service_controller.selectTimeSlot,
|
|
5690
|
+
resetDateSelection: service_controller.resetDateSelection,
|
|
5691
|
+
updateCalendar: service_controller.updateCalendar,
|
|
5692
|
+
findFirstAvailable: service_controller.findFirstAvailable,
|
|
5693
|
+
addToCart: service_controller.addToCart,
|
|
5694
|
+
removeFromCart: service_controller.removeFromCart,
|
|
5695
|
+
clearCart: service_controller.clearCart,
|
|
5696
|
+
getProvidersList: service_controller.getProvidersList,
|
|
5697
|
+
prevStep: service_controller.prevStep,
|
|
5698
|
+
nextStep: service_controller.nextStep,
|
|
5699
|
+
getServicePrice: service_controller.getServicePrice,
|
|
5700
|
+
formatDateDisplay: service_controller.formatDateDisplay,
|
|
5701
|
+
serviceItemsFromSlots: service_controller.serviceItemsFromSlots
|
|
5702
|
+
};
|
|
5703
|
+
return {
|
|
5704
|
+
client,
|
|
5705
|
+
session,
|
|
5706
|
+
setup,
|
|
5707
|
+
market,
|
|
5708
|
+
market_key,
|
|
5709
|
+
locale,
|
|
5710
|
+
currency,
|
|
5711
|
+
allowed_payment_methods,
|
|
5712
|
+
payment_config,
|
|
5713
|
+
identify,
|
|
5714
|
+
identifyContactEmailIfMissing,
|
|
5715
|
+
verify: client.verify,
|
|
5716
|
+
me: client.me,
|
|
5717
|
+
logout: client.logout,
|
|
5718
|
+
onAuthStateChanged: client.onAuthStateChanged,
|
|
5719
|
+
get isAuthenticated() {
|
|
5720
|
+
return client.isAuthenticated;
|
|
5721
|
+
},
|
|
5722
|
+
setMarket,
|
|
5723
|
+
setLocale,
|
|
5724
|
+
setContext,
|
|
5725
|
+
getMarket: currentMarketKey,
|
|
5726
|
+
getLocale: currentLocale,
|
|
5727
|
+
cms: {
|
|
5728
|
+
state: cms_state,
|
|
5729
|
+
collection: {
|
|
5730
|
+
get: (params, options) => client.cms.collection.get(params, options)
|
|
5731
|
+
},
|
|
5732
|
+
entry: {
|
|
5733
|
+
get: loadEntry,
|
|
5734
|
+
find: (params, options) => client.cms.entry.find(params, options)
|
|
5735
|
+
},
|
|
5736
|
+
form: {
|
|
5737
|
+
get: loadForm,
|
|
5738
|
+
submit: submitForm,
|
|
5739
|
+
submitByKey: submitFormByKey
|
|
5740
|
+
},
|
|
5741
|
+
taxonomy: client.cms.taxonomy
|
|
5742
|
+
},
|
|
5743
|
+
eshop: {
|
|
5744
|
+
state: eshop_state,
|
|
5745
|
+
product: product_store,
|
|
5746
|
+
service: service_store,
|
|
5747
|
+
provider: {
|
|
5748
|
+
get: (params, options) => client.eshop.provider.get(params, options),
|
|
5749
|
+
list: loadProviders
|
|
5750
|
+
},
|
|
5751
|
+
order: client.eshop.order,
|
|
5752
|
+
cart: cart_store
|
|
5753
|
+
},
|
|
5754
|
+
crm: client.crm,
|
|
5755
|
+
action: {
|
|
5756
|
+
track(params) {
|
|
5757
|
+
return trackAction(params);
|
|
5758
|
+
},
|
|
5759
|
+
pageView(payload = {}) {
|
|
5760
|
+
return trackAction({ key: "page.view", payload });
|
|
5761
|
+
},
|
|
5762
|
+
state: atom(null)
|
|
5763
|
+
},
|
|
5764
|
+
experiments: {
|
|
5765
|
+
use: useExperiment
|
|
5766
|
+
},
|
|
5767
|
+
support: client.support,
|
|
5768
|
+
store: {
|
|
5769
|
+
...client.store,
|
|
5770
|
+
setup,
|
|
5771
|
+
load: loadSetup
|
|
5772
|
+
},
|
|
5773
|
+
utils: client.utils
|
|
5774
|
+
};
|
|
5775
|
+
}
|
|
5776
|
+
function initializeStore(publishableKey, config, scopedClient) {
|
|
5777
|
+
const store = initializeStoreCore(publishableKey, config, scopedClient);
|
|
5778
|
+
return Object.assign(store, {
|
|
5779
|
+
withContext(context) {
|
|
5780
|
+
return initializeStore(
|
|
5781
|
+
publishableKey,
|
|
5782
|
+
{
|
|
5783
|
+
...config,
|
|
5784
|
+
locale: context.locale ?? store.getLocale(),
|
|
5785
|
+
market: context.market ?? store.getMarket()
|
|
5786
|
+
},
|
|
5787
|
+
store.client.withContext(context)
|
|
5788
|
+
);
|
|
5789
|
+
}
|
|
5790
|
+
});
|
|
5791
|
+
}
|
|
5792
|
+
function initialize(publishableKey, options = {}) {
|
|
5793
|
+
return initializeStore(publishableKey, options);
|
|
5794
|
+
}
|
|
5795
|
+
|
|
5796
|
+
// src/index.ts
|
|
5797
|
+
var SDK_VERSION = "0.10.0";
|
|
5798
|
+
var SUPPORTED_FRAMEWORKS = [
|
|
5799
|
+
"astro",
|
|
5800
|
+
"react",
|
|
5801
|
+
"vue",
|
|
5802
|
+
"svelte",
|
|
5803
|
+
"vanilla"
|
|
5804
|
+
];
|
|
5805
|
+
function createUtilitySurface(apiConfig) {
|
|
5806
|
+
return {
|
|
5807
|
+
getImageUrl: (imageBlock, isBlock2 = true) => getImageUrl(imageBlock, isBlock2),
|
|
5808
|
+
getBlockValue,
|
|
5809
|
+
getBlockTextValue,
|
|
5810
|
+
getBlockContentValue,
|
|
5811
|
+
getBlockValues,
|
|
5812
|
+
getBlockLabel,
|
|
5813
|
+
getBlockObjectValues,
|
|
5814
|
+
getBlockFromArray,
|
|
5815
|
+
formatBlockValue,
|
|
5816
|
+
prepareBlocksForSubmission,
|
|
5817
|
+
extractBlockValues,
|
|
5818
|
+
formatPrice: (prices) => formatPrice(prices, apiConfig.market),
|
|
5819
|
+
getPriceAmount: (prices) => getPriceAmount(prices, apiConfig.market),
|
|
5820
|
+
formatPayment,
|
|
5821
|
+
formatMinor,
|
|
5822
|
+
getCurrencySymbol,
|
|
5823
|
+
getCurrencyName,
|
|
5824
|
+
validatePhoneNumber,
|
|
5825
|
+
tzGroups,
|
|
5826
|
+
findTimeZone,
|
|
5827
|
+
slugify,
|
|
5828
|
+
humanize,
|
|
5829
|
+
categorify,
|
|
5830
|
+
formatDate,
|
|
5831
|
+
getSvgContentForAstro,
|
|
5832
|
+
fetchSvgContent,
|
|
5833
|
+
injectSvgIntoElement,
|
|
5834
|
+
isValidKey,
|
|
5835
|
+
validateKey,
|
|
5836
|
+
toKey,
|
|
5837
|
+
nameToKey,
|
|
5838
|
+
getAvailableStock,
|
|
5839
|
+
getReservedStock,
|
|
5840
|
+
hasStock,
|
|
5841
|
+
getInventoryAt,
|
|
5842
|
+
getFirstAvailableFCId
|
|
5843
|
+
};
|
|
5844
|
+
}
|
|
5845
|
+
var ADMIN_STORAGE_KEY = "arky_admin_session";
|
|
5846
|
+
function readAdminSession() {
|
|
5847
|
+
if (typeof window === "undefined") return null;
|
|
5848
|
+
try {
|
|
5849
|
+
const raw = localStorage.getItem(ADMIN_STORAGE_KEY);
|
|
5850
|
+
return raw ? JSON.parse(raw) : null;
|
|
5851
|
+
} catch {
|
|
5852
|
+
return null;
|
|
5853
|
+
}
|
|
5854
|
+
}
|
|
5855
|
+
function writeAdminSession(s) {
|
|
5856
|
+
if (typeof window === "undefined") return;
|
|
5857
|
+
if (s) {
|
|
5858
|
+
localStorage.setItem(ADMIN_STORAGE_KEY, JSON.stringify(s));
|
|
5859
|
+
} else {
|
|
5860
|
+
localStorage.removeItem(ADMIN_STORAGE_KEY);
|
|
5861
|
+
}
|
|
5862
|
+
}
|
|
5863
|
+
function createAdmin(config) {
|
|
5864
|
+
const locale = config.locale || "en";
|
|
5865
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
5866
|
+
function toPublic(s) {
|
|
5867
|
+
return s ? { email: s.email } : null;
|
|
5868
|
+
}
|
|
5869
|
+
function emit() {
|
|
5870
|
+
const pub = toPublic(readAdminSession());
|
|
5871
|
+
for (const l of listeners) {
|
|
5872
|
+
Promise.resolve().then(() => l(pub)).catch(() => {
|
|
5873
|
+
});
|
|
5874
|
+
}
|
|
5875
|
+
}
|
|
5876
|
+
const updateSession = (updater) => {
|
|
5877
|
+
if (config.apiToken) return;
|
|
5878
|
+
const prev = readAdminSession();
|
|
5879
|
+
const next = updater(prev);
|
|
5880
|
+
writeAdminSession(next);
|
|
5881
|
+
emit();
|
|
5882
|
+
};
|
|
5883
|
+
const authStorage = config.apiToken ? {
|
|
5884
|
+
getTokens: () => ({ access_token: config.apiToken }),
|
|
5885
|
+
onTokensRefreshed: () => {
|
|
5886
|
+
},
|
|
5887
|
+
onForcedLogout: () => {
|
|
5888
|
+
}
|
|
5889
|
+
} : {
|
|
5890
|
+
getTokens() {
|
|
5891
|
+
const s = readAdminSession();
|
|
5892
|
+
if (!s) return null;
|
|
5893
|
+
return {
|
|
4228
5894
|
access_token: s.access_token,
|
|
4229
5895
|
refresh_token: s.refresh_token,
|
|
4230
5896
|
access_expires_at: s.access_expires_at
|
|
@@ -4326,6 +5992,7 @@ function createAdmin(config) {
|
|
|
4326
5992
|
update: storeApi.updateStore,
|
|
4327
5993
|
get: storeApi.getStore,
|
|
4328
5994
|
find: storeApi.getStores,
|
|
5995
|
+
regeneratePublishableKey: storeApi.regeneratePublishableKey,
|
|
4329
5996
|
subscription: {
|
|
4330
5997
|
get: storeApi.getSubscription,
|
|
4331
5998
|
getPlans: storeApi.getSubscriptionPlans,
|
|
@@ -4644,6 +6311,8 @@ function createAdmin(config) {
|
|
|
4644
6311
|
};
|
|
4645
6312
|
return sdk;
|
|
4646
6313
|
}
|
|
6314
|
+
var DEFAULT_STOREFRONT_API_URL = "https://api.arky.io";
|
|
6315
|
+
var storefrontScopeSequence = 0;
|
|
4647
6316
|
function defaultStorefrontSessionStorage() {
|
|
4648
6317
|
if (typeof window === "undefined") return null;
|
|
4649
6318
|
try {
|
|
@@ -4652,132 +6321,177 @@ function defaultStorefrontSessionStorage() {
|
|
|
4652
6321
|
return null;
|
|
4653
6322
|
}
|
|
4654
6323
|
}
|
|
4655
|
-
function
|
|
4656
|
-
const
|
|
4657
|
-
|
|
6324
|
+
function normalizeStorefrontApiUrl(value) {
|
|
6325
|
+
const input = value?.trim() || DEFAULT_STOREFRONT_API_URL;
|
|
6326
|
+
let parsed;
|
|
6327
|
+
try {
|
|
6328
|
+
parsed = new URL(input);
|
|
6329
|
+
} catch {
|
|
6330
|
+
throw new Error("Storefront apiUrl must be a valid HTTP(S) URL");
|
|
6331
|
+
}
|
|
6332
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
6333
|
+
throw new Error("Storefront apiUrl must use HTTP or HTTPS");
|
|
6334
|
+
}
|
|
6335
|
+
return input.replace(/\/+$/, "");
|
|
4658
6336
|
}
|
|
4659
|
-
function
|
|
4660
|
-
|
|
4661
|
-
|
|
6337
|
+
function validatePublishableKey(publishableKey) {
|
|
6338
|
+
if (typeof publishableKey !== "string") {
|
|
6339
|
+
throw new Error(
|
|
6340
|
+
"A valid Arky publishable key is required (arky_pk_ followed by 43 URL-safe characters)"
|
|
6341
|
+
);
|
|
6342
|
+
}
|
|
6343
|
+
const key = publishableKey.trim();
|
|
6344
|
+
if (!/^arky_pk_[A-Za-z0-9_-]{42}[AEIMQUYcgkosw048]$/.test(key)) {
|
|
6345
|
+
throw new Error(
|
|
6346
|
+
"A valid Arky publishable key is required (arky_pk_ followed by 43 URL-safe characters)"
|
|
6347
|
+
);
|
|
6348
|
+
}
|
|
6349
|
+
return key;
|
|
6350
|
+
}
|
|
6351
|
+
function publishableKeyFingerprint(publishableKey) {
|
|
6352
|
+
let hashA = 2166136261;
|
|
6353
|
+
let hashB = 2654435769;
|
|
6354
|
+
for (let index = 0; index < publishableKey.length; index += 1) {
|
|
6355
|
+
const code = publishableKey.charCodeAt(index);
|
|
6356
|
+
hashA = Math.imul(hashA ^ code, 16777619);
|
|
6357
|
+
hashB = Math.imul(hashB ^ code, 2246822507);
|
|
6358
|
+
}
|
|
6359
|
+
return `${(hashA >>> 0).toString(36)}${(hashB >>> 0).toString(36)}`;
|
|
6360
|
+
}
|
|
6361
|
+
function storefrontSessionStorageKey(apiUrl, publishableKey) {
|
|
6362
|
+
return `arky_visitor_session:${encodeURIComponent(apiUrl.toLowerCase())}:${publishableKeyFingerprint(publishableKey)}`;
|
|
6363
|
+
}
|
|
6364
|
+
function isVisitorSessionToken(value) {
|
|
6365
|
+
return Boolean(value && /^arky_vst_[0-9a-f]{64}$/.test(value));
|
|
6366
|
+
}
|
|
6367
|
+
function createStorefrontClientCore(publishableKeyInput, options = {}, isolatedSession = false) {
|
|
6368
|
+
const publishableKey = validatePublishableKey(publishableKeyInput);
|
|
6369
|
+
const apiUrl = normalizeStorefrontApiUrl(options.apiUrl);
|
|
6370
|
+
let locale = options.locale?.trim() || "";
|
|
6371
|
+
let market = options.market?.trim() || "";
|
|
4662
6372
|
const listeners = /* @__PURE__ */ new Set();
|
|
4663
|
-
let
|
|
6373
|
+
let identifyPromise = null;
|
|
4664
6374
|
let identityTail = Promise.resolve();
|
|
4665
|
-
|
|
6375
|
+
let setupPromise = null;
|
|
6376
|
+
let setupValue = null;
|
|
6377
|
+
const explicitSessionStorage = options.sessionStorage;
|
|
6378
|
+
const sessionStorage = isolatedSession ? explicitSessionStorage || null : explicitSessionStorage || defaultStorefrontSessionStorage();
|
|
6379
|
+
const canCreateVisitorSession = typeof window !== "undefined" || Boolean(explicitSessionStorage);
|
|
6380
|
+
const storageKey = `${storefrontSessionStorageKey(apiUrl, publishableKey)}${isolatedSession ? `:scope:${++storefrontScopeSequence}` : ""}`;
|
|
4666
6381
|
let memorySession = null;
|
|
4667
|
-
|
|
4668
|
-
|
|
6382
|
+
let memorySessionToken = null;
|
|
6383
|
+
function readSessionToken() {
|
|
6384
|
+
if (!sessionStorage) return memorySessionToken;
|
|
4669
6385
|
try {
|
|
4670
|
-
const
|
|
4671
|
-
|
|
4672
|
-
);
|
|
4673
|
-
return raw ? JSON.parse(raw) : null;
|
|
6386
|
+
const stored = sessionStorage.getItem(storageKey);
|
|
6387
|
+
return isVisitorSessionToken(stored) ? stored : memorySessionToken;
|
|
4674
6388
|
} catch {
|
|
4675
|
-
return
|
|
6389
|
+
return memorySessionToken;
|
|
4676
6390
|
}
|
|
4677
6391
|
}
|
|
4678
6392
|
function writeContactSession(session) {
|
|
4679
6393
|
memorySession = session;
|
|
6394
|
+
memorySessionToken = session?.sessionToken || null;
|
|
4680
6395
|
if (!sessionStorage) return;
|
|
4681
|
-
const key = storefrontSessionStorageKey(config.baseUrl, config.storeId);
|
|
4682
6396
|
try {
|
|
4683
6397
|
if (session) {
|
|
4684
|
-
sessionStorage.setItem(
|
|
6398
|
+
sessionStorage.setItem(storageKey, session.sessionToken);
|
|
4685
6399
|
} else {
|
|
4686
|
-
sessionStorage.removeItem(
|
|
6400
|
+
sessionStorage.removeItem(storageKey);
|
|
4687
6401
|
}
|
|
4688
6402
|
} catch {
|
|
4689
6403
|
}
|
|
4690
6404
|
}
|
|
4691
6405
|
function toPublic(s) {
|
|
4692
|
-
return s ? { contact: s.contact
|
|
6406
|
+
return s ? { contact: s.contact } : null;
|
|
4693
6407
|
}
|
|
4694
6408
|
function emit() {
|
|
4695
|
-
const pub = toPublic(
|
|
6409
|
+
const pub = toPublic(memorySession);
|
|
4696
6410
|
for (const l of listeners) {
|
|
4697
6411
|
Promise.resolve().then(() => l(pub)).catch(() => {
|
|
4698
6412
|
});
|
|
4699
6413
|
}
|
|
4700
6414
|
}
|
|
4701
6415
|
const updateSession = (updater) => {
|
|
4702
|
-
|
|
4703
|
-
const prev = readContactSession();
|
|
4704
|
-
const next = updater(prev);
|
|
6416
|
+
const next = updater(memorySession);
|
|
4705
6417
|
writeContactSession(next);
|
|
4706
6418
|
emit();
|
|
4707
6419
|
};
|
|
4708
|
-
const authStorage =
|
|
4709
|
-
getTokens: () => ({ access_token: config.apiToken }),
|
|
4710
|
-
onTokensRefreshed: () => {
|
|
4711
|
-
},
|
|
4712
|
-
onForcedLogout: () => {
|
|
4713
|
-
}
|
|
4714
|
-
} : {
|
|
6420
|
+
const authStorage = {
|
|
4715
6421
|
getTokens() {
|
|
4716
|
-
const
|
|
4717
|
-
return
|
|
6422
|
+
const sessionToken = readSessionToken();
|
|
6423
|
+
return sessionToken ? { access_token: sessionToken } : null;
|
|
4718
6424
|
},
|
|
4719
6425
|
onTokensRefreshed() {
|
|
4720
6426
|
},
|
|
4721
6427
|
onForcedLogout() {
|
|
4722
|
-
|
|
6428
|
+
identifyPromise = null;
|
|
4723
6429
|
updateSession(() => null);
|
|
4724
6430
|
}
|
|
4725
6431
|
};
|
|
6432
|
+
let recoverUnauthorized = async () => false;
|
|
6433
|
+
let visitorRecoveryPromise = null;
|
|
4726
6434
|
const httpClient = createHttpClient({
|
|
4727
|
-
baseUrl:
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
6435
|
+
baseUrl: apiUrl,
|
|
6436
|
+
authStorage,
|
|
6437
|
+
storefrontMode: true,
|
|
6438
|
+
forcedHeaders: () => ({
|
|
6439
|
+
"X-Arky-Publishable-Key": publishableKey,
|
|
6440
|
+
...locale ? { "X-Arky-Locale": locale } : {},
|
|
6441
|
+
...market ? { "X-Arky-Market": market } : {}
|
|
6442
|
+
}),
|
|
6443
|
+
onUnauthorized: ({ authorizationToken, path }) => recoverUnauthorized(authorizationToken, path)
|
|
4733
6444
|
});
|
|
4734
6445
|
const apiConfig = {
|
|
4735
6446
|
httpClient,
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
market
|
|
6447
|
+
apiUrl,
|
|
6448
|
+
publishableKey,
|
|
6449
|
+
market,
|
|
4739
6450
|
locale,
|
|
4740
6451
|
authStorage
|
|
4741
6452
|
};
|
|
4742
|
-
|
|
6453
|
+
function requireVisitorSessionCapability() {
|
|
6454
|
+
if (!canCreateVisitorSession) {
|
|
6455
|
+
throw new Error(
|
|
6456
|
+
"Stateful storefront operations during SSR require an explicit request-local sessionStorage adapter"
|
|
6457
|
+
);
|
|
6458
|
+
}
|
|
6459
|
+
}
|
|
6460
|
+
async function getSetup(requestOptions) {
|
|
6461
|
+
if (setupValue) return setupValue;
|
|
6462
|
+
if (setupPromise) return setupPromise;
|
|
6463
|
+
setupPromise = httpClient.get(
|
|
6464
|
+
"/v1/storefront",
|
|
6465
|
+
requestOptions
|
|
6466
|
+
).then((setup) => {
|
|
6467
|
+
setupValue = setup;
|
|
6468
|
+
return setup;
|
|
6469
|
+
}).finally(() => {
|
|
6470
|
+
setupPromise = null;
|
|
6471
|
+
});
|
|
6472
|
+
return setupPromise;
|
|
6473
|
+
}
|
|
6474
|
+
async function ensureVisitorSession() {
|
|
6475
|
+
if (readSessionToken()) return;
|
|
6476
|
+
requireVisitorSessionCapability();
|
|
6477
|
+
await identify();
|
|
6478
|
+
}
|
|
6479
|
+
const storefrontApi = createStorefrontApi(apiConfig, updateSession, {
|
|
6480
|
+
ensureVisitorSession,
|
|
6481
|
+
getSetup
|
|
6482
|
+
});
|
|
4743
6483
|
const contactApi = storefrontApi.crm.contact;
|
|
4744
6484
|
function identify(params) {
|
|
4745
|
-
|
|
4746
|
-
|
|
6485
|
+
requireVisitorSessionCapability();
|
|
6486
|
+
if (params?.market !== void 0) setMarket(params.market);
|
|
4747
6487
|
const isBareCall = !params?.email && !params?.verify;
|
|
4748
|
-
if (isBareCall &&
|
|
6488
|
+
if (isBareCall && identifyPromise) return identifyPromise;
|
|
4749
6489
|
const run = async () => {
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
market,
|
|
4756
|
-
email: params?.email
|
|
4757
|
-
}));
|
|
4758
|
-
return {
|
|
4759
|
-
contact: result.contact,
|
|
4760
|
-
store: result.store,
|
|
4761
|
-
market: result.market,
|
|
4762
|
-
verification_challenge: result.verification_challenge
|
|
4763
|
-
};
|
|
4764
|
-
} catch (err) {
|
|
4765
|
-
const e = err;
|
|
4766
|
-
const status = e?.statusCode || e?.status || e?.response?.status;
|
|
4767
|
-
if (isBareCall && status === 401) {
|
|
4768
|
-
updateSession(() => null);
|
|
4769
|
-
const result = await contactApi.identify({
|
|
4770
|
-
market
|
|
4771
|
-
});
|
|
4772
|
-
return {
|
|
4773
|
-
contact: result.contact,
|
|
4774
|
-
store: result.store,
|
|
4775
|
-
market: result.market,
|
|
4776
|
-
verification_challenge: result.verification_challenge
|
|
4777
|
-
};
|
|
4778
|
-
}
|
|
4779
|
-
throw err;
|
|
4780
|
-
}
|
|
6490
|
+
const result = await (params?.verify ? contactApi.requestCode({ email: params.email }) : contactApi.identify({ email: params?.email }));
|
|
6491
|
+
return {
|
|
6492
|
+
contact: result.contact,
|
|
6493
|
+
verification_challenge: result.verification_challenge
|
|
6494
|
+
};
|
|
4781
6495
|
};
|
|
4782
6496
|
const promise = identityTail.then(run);
|
|
4783
6497
|
identityTail = promise.then(
|
|
@@ -4785,49 +6499,88 @@ function createStorefrontClient(config) {
|
|
|
4785
6499
|
() => void 0
|
|
4786
6500
|
);
|
|
4787
6501
|
if (isBareCall) {
|
|
4788
|
-
|
|
6502
|
+
identifyPromise = promise;
|
|
4789
6503
|
void promise.then(
|
|
4790
6504
|
() => {
|
|
4791
|
-
if (
|
|
6505
|
+
if (identifyPromise === promise) identifyPromise = null;
|
|
4792
6506
|
},
|
|
4793
6507
|
() => {
|
|
4794
|
-
if (
|
|
6508
|
+
if (identifyPromise === promise) identifyPromise = null;
|
|
4795
6509
|
}
|
|
4796
6510
|
);
|
|
4797
6511
|
}
|
|
4798
6512
|
return promise;
|
|
4799
6513
|
}
|
|
4800
6514
|
async function verify(params) {
|
|
6515
|
+
requireVisitorSessionCapability();
|
|
4801
6516
|
const result = await contactApi.verify(params);
|
|
4802
|
-
|
|
4803
|
-
return result;
|
|
6517
|
+
identifyPromise = null;
|
|
6518
|
+
return { contact: result.contact };
|
|
6519
|
+
}
|
|
6520
|
+
async function me() {
|
|
6521
|
+
await ensureVisitorSession();
|
|
6522
|
+
return contactApi.getMe();
|
|
4804
6523
|
}
|
|
4805
6524
|
async function logout() {
|
|
4806
|
-
|
|
4807
|
-
|
|
6525
|
+
identifyPromise = null;
|
|
6526
|
+
if (!readSessionToken()) {
|
|
6527
|
+
updateSession(() => null);
|
|
6528
|
+
return;
|
|
6529
|
+
}
|
|
4808
6530
|
try {
|
|
4809
6531
|
await contactApi.logout();
|
|
4810
6532
|
} catch {
|
|
4811
6533
|
updateSession(() => null);
|
|
4812
6534
|
}
|
|
4813
6535
|
}
|
|
6536
|
+
function setMarket(value) {
|
|
6537
|
+
market = value.trim();
|
|
6538
|
+
apiConfig.market = market;
|
|
6539
|
+
identifyPromise = null;
|
|
6540
|
+
}
|
|
6541
|
+
function setLocale(value) {
|
|
6542
|
+
locale = value.trim();
|
|
6543
|
+
apiConfig.locale = locale;
|
|
6544
|
+
}
|
|
6545
|
+
function setContext(context) {
|
|
6546
|
+
if (context.locale !== void 0) setLocale(context.locale);
|
|
6547
|
+
if (context.market !== void 0) setMarket(context.market);
|
|
6548
|
+
}
|
|
6549
|
+
recoverUnauthorized = async (authorizationToken, path) => {
|
|
6550
|
+
if (!authorizationToken) return false;
|
|
6551
|
+
const currentToken = readSessionToken();
|
|
6552
|
+
if (currentToken !== authorizationToken) {
|
|
6553
|
+
if (currentToken) return true;
|
|
6554
|
+
if (!visitorRecoveryPromise) return false;
|
|
6555
|
+
await visitorRecoveryPromise;
|
|
6556
|
+
return Boolean(readSessionToken());
|
|
6557
|
+
}
|
|
6558
|
+
updateSession(() => null);
|
|
6559
|
+
if (/\/account\/(identify|code)$/.test(path)) return true;
|
|
6560
|
+
const recovery = ensureVisitorSession();
|
|
6561
|
+
const trackedRecovery = recovery.finally(() => {
|
|
6562
|
+
if (visitorRecoveryPromise === trackedRecovery) {
|
|
6563
|
+
visitorRecoveryPromise = null;
|
|
6564
|
+
}
|
|
6565
|
+
});
|
|
6566
|
+
visitorRecoveryPromise = trackedRecovery;
|
|
6567
|
+
await trackedRecovery;
|
|
6568
|
+
return true;
|
|
6569
|
+
};
|
|
4814
6570
|
return {
|
|
4815
6571
|
identify,
|
|
4816
6572
|
verify,
|
|
4817
6573
|
logout,
|
|
4818
|
-
me
|
|
6574
|
+
me,
|
|
4819
6575
|
get session() {
|
|
4820
|
-
|
|
4821
|
-
return toPublic(readContactSession());
|
|
6576
|
+
return toPublic(memorySession);
|
|
4822
6577
|
},
|
|
4823
6578
|
get isAuthenticated() {
|
|
4824
|
-
|
|
4825
|
-
const s = readContactSession();
|
|
4826
|
-
return s !== null && !!s.access_token;
|
|
6579
|
+
return Boolean(readSessionToken());
|
|
4827
6580
|
},
|
|
4828
6581
|
onAuthStateChanged(listener) {
|
|
4829
6582
|
listeners.add(listener);
|
|
4830
|
-
const current = toPublic(
|
|
6583
|
+
const current = toPublic(memorySession);
|
|
4831
6584
|
if (current) {
|
|
4832
6585
|
Promise.resolve().then(() => listener(current)).catch(() => {
|
|
4833
6586
|
});
|
|
@@ -4839,44 +6592,52 @@ function createStorefrontClient(config) {
|
|
|
4839
6592
|
store: storefrontApi.store,
|
|
4840
6593
|
cms: storefrontApi.cms,
|
|
4841
6594
|
eshop: storefrontApi.eshop,
|
|
4842
|
-
crm:
|
|
6595
|
+
crm: {
|
|
6596
|
+
...storefrontApi.crm,
|
|
6597
|
+
contact: {
|
|
6598
|
+
identify: (params) => identify(params),
|
|
6599
|
+
requestCode: (params) => identify({ ...params, verify: true }),
|
|
6600
|
+
verify,
|
|
6601
|
+
logout,
|
|
6602
|
+
getMe: me
|
|
6603
|
+
}
|
|
6604
|
+
},
|
|
4843
6605
|
action: storefrontApi.action,
|
|
4844
6606
|
experiments: storefrontApi.experiments,
|
|
4845
|
-
support: createStorefrontSupportApi(apiConfig),
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
setLocale: (l) => {
|
|
4853
|
-
apiConfig.locale = l;
|
|
4854
|
-
},
|
|
4855
|
-
getLocale: () => apiConfig.locale,
|
|
6607
|
+
support: createStorefrontSupportApi(apiConfig, ensureVisitorSession),
|
|
6608
|
+
getSetup,
|
|
6609
|
+
setContext,
|
|
6610
|
+
setMarket,
|
|
6611
|
+
getMarket: () => market,
|
|
6612
|
+
setLocale,
|
|
6613
|
+
getLocale: () => locale,
|
|
4856
6614
|
utils: createUtilitySurface(apiConfig)
|
|
4857
6615
|
};
|
|
4858
6616
|
}
|
|
4859
|
-
function
|
|
4860
|
-
const
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
6617
|
+
function createStorefrontClient(publishableKey, options = {}, isolatedSession = false) {
|
|
6618
|
+
const client = createStorefrontClientCore(
|
|
6619
|
+
publishableKey,
|
|
6620
|
+
options,
|
|
6621
|
+
isolatedSession
|
|
6622
|
+
);
|
|
6623
|
+
return Object.assign(client, {
|
|
6624
|
+
withContext(context) {
|
|
6625
|
+
return createStorefrontClient(
|
|
6626
|
+
publishableKey,
|
|
6627
|
+
{
|
|
6628
|
+
...options,
|
|
6629
|
+
locale: context.locale ?? client.getLocale(),
|
|
6630
|
+
market: context.market ?? client.getMarket()
|
|
6631
|
+
},
|
|
6632
|
+
true
|
|
6633
|
+
);
|
|
6634
|
+
}
|
|
4876
6635
|
});
|
|
4877
|
-
|
|
6636
|
+
}
|
|
6637
|
+
function createStorefront(publishableKey, options = {}) {
|
|
6638
|
+
return createStorefrontClient(publishableKey, options);
|
|
4878
6639
|
}
|
|
4879
6640
|
|
|
4880
|
-
export { COMMON_ACTION_KEYS, PaymentMethodType, SDK_VERSION, SUPPORTED_FRAMEWORKS, createAdmin, createCartController, createStorefront };
|
|
6641
|
+
export { COMMON_ACTION_KEYS, DEFAULT_STOREFRONT_API_URL, PaymentMethodType, SDK_VERSION, SUPPORTED_FRAMEWORKS, buildFormFields, createAdmin, createCartController, createFormEntry, createFormEntryFromValues, createStorefront, initialize };
|
|
4881
6642
|
//# sourceMappingURL=index.js.map
|
|
4882
6643
|
//# sourceMappingURL=index.js.map
|