arky-sdk 0.9.20 → 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 -5
- 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 -5
- package/dist/admin.js.map +1 -1
- package/dist/{api-C_2AMzmc.d.cts → index-B0Pd1s-2.d.cts} +4457 -4451
- package/dist/{api-C_2AMzmc.d.ts → index-B0Pd1s-2.d.ts} +4457 -4451
- package/dist/index.cjs +2331 -551
- 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 +2327 -552
- package/dist/index.js.map +1 -1
- package/dist/{inventory-DdgZ5MI_.d.ts → inventory-C2EZEnV9.d.ts} +1 -1
- package/dist/{inventory-DgPpeRWU.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-CTtJoarO.d.ts +0 -1543
- package/dist/admin-DB_GYW2b.d.cts +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
|
};
|
|
@@ -2637,32 +2538,38 @@ function storefrontSupportOptions(supportToken, options) {
|
|
|
2637
2538
|
}
|
|
2638
2539
|
};
|
|
2639
2540
|
}
|
|
2640
|
-
function createStorefrontSupportApi(config) {
|
|
2541
|
+
function createStorefrontSupportApi(config, ensureVisitorSession) {
|
|
2641
2542
|
const { httpClient } = config;
|
|
2642
2543
|
return {
|
|
2643
2544
|
async startConversation(params = {}, opts) {
|
|
2644
|
-
|
|
2545
|
+
await ensureVisitorSession();
|
|
2645
2546
|
return httpClient.post(
|
|
2646
|
-
|
|
2647
|
-
|
|
2547
|
+
"/v1/storefront/support/conversations",
|
|
2548
|
+
params,
|
|
2648
2549
|
opts
|
|
2649
2550
|
);
|
|
2650
2551
|
},
|
|
2651
2552
|
async sendMessage(params, opts) {
|
|
2553
|
+
await ensureVisitorSession();
|
|
2652
2554
|
const { support_token, ...request } = params;
|
|
2653
|
-
const storeId = config.storeId;
|
|
2654
2555
|
return httpClient.post(
|
|
2655
|
-
`/v1/storefront
|
|
2656
|
-
|
|
2556
|
+
`/v1/storefront/support/conversations/${request.conversation_id}/messages`,
|
|
2557
|
+
request,
|
|
2657
2558
|
storefrontSupportOptions(support_token, opts)
|
|
2658
2559
|
);
|
|
2659
2560
|
},
|
|
2660
2561
|
async getConversation(params, opts) {
|
|
2562
|
+
await ensureVisitorSession();
|
|
2661
2563
|
const { support_token, ...request } = params;
|
|
2662
|
-
const
|
|
2663
|
-
|
|
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}` : "";
|
|
2664
2571
|
return httpClient.get(
|
|
2665
|
-
`/v1/storefront
|
|
2572
|
+
`/v1/storefront/support/conversations/${request.conversation_id}${suffix}`,
|
|
2666
2573
|
storefrontSupportOptions(support_token, opts)
|
|
2667
2574
|
);
|
|
2668
2575
|
}
|
|
@@ -4113,145 +4020,1918 @@ function getFirstAvailableFCId(variant, quantity = 1) {
|
|
|
4113
4020
|
return inv?.location_id;
|
|
4114
4021
|
}
|
|
4115
4022
|
|
|
4116
|
-
// src/
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
"react",
|
|
4121
|
-
"vue",
|
|
4122
|
-
"svelte",
|
|
4123
|
-
"vanilla"
|
|
4124
|
-
];
|
|
4125
|
-
function createUtilitySurface(apiConfig) {
|
|
4126
|
-
return {
|
|
4127
|
-
getImageUrl: (imageBlock, isBlock2 = true) => getImageUrl(imageBlock, isBlock2),
|
|
4128
|
-
getBlockValue,
|
|
4129
|
-
getBlockTextValue,
|
|
4130
|
-
getBlockContentValue,
|
|
4131
|
-
getBlockValues,
|
|
4132
|
-
getBlockLabel,
|
|
4133
|
-
getBlockObjectValues,
|
|
4134
|
-
getBlockFromArray,
|
|
4135
|
-
formatBlockValue,
|
|
4136
|
-
prepareBlocksForSubmission,
|
|
4137
|
-
extractBlockValues,
|
|
4138
|
-
formatPrice: (prices) => formatPrice(prices, apiConfig.market),
|
|
4139
|
-
getPriceAmount: (prices) => getPriceAmount(prices, apiConfig.market),
|
|
4140
|
-
formatPayment,
|
|
4141
|
-
formatMinor,
|
|
4142
|
-
getCurrencySymbol,
|
|
4143
|
-
getCurrencyName,
|
|
4144
|
-
validatePhoneNumber,
|
|
4145
|
-
tzGroups,
|
|
4146
|
-
findTimeZone,
|
|
4147
|
-
slugify,
|
|
4148
|
-
humanize,
|
|
4149
|
-
categorify,
|
|
4150
|
-
formatDate,
|
|
4151
|
-
getSvgContentForAstro,
|
|
4152
|
-
fetchSvgContent,
|
|
4153
|
-
injectSvgIntoElement,
|
|
4154
|
-
isValidKey,
|
|
4155
|
-
validateKey,
|
|
4156
|
-
toKey,
|
|
4157
|
-
nameToKey,
|
|
4158
|
-
getAvailableStock,
|
|
4159
|
-
getReservedStock,
|
|
4160
|
-
hasStock,
|
|
4161
|
-
getInventoryAt,
|
|
4162
|
-
getFirstAvailableFCId
|
|
4163
|
-
};
|
|
4023
|
+
// src/payments/stripe.ts
|
|
4024
|
+
async function defaultStripeLoader(publishableKey, options) {
|
|
4025
|
+
const { loadStripe } = await import('@stripe/stripe-js');
|
|
4026
|
+
return loadStripe(publishableKey, options);
|
|
4164
4027
|
}
|
|
4165
|
-
|
|
4166
|
-
|
|
4167
|
-
if (typeof window === "undefined") return null;
|
|
4168
|
-
try {
|
|
4169
|
-
const raw = localStorage.getItem(ADMIN_STORAGE_KEY);
|
|
4170
|
-
return raw ? JSON.parse(raw) : null;
|
|
4171
|
-
} catch {
|
|
4172
|
-
return null;
|
|
4173
|
-
}
|
|
4174
|
-
}
|
|
4175
|
-
function writeAdminSession(s) {
|
|
4176
|
-
if (typeof window === "undefined") return;
|
|
4177
|
-
if (s) {
|
|
4178
|
-
localStorage.setItem(ADMIN_STORAGE_KEY, JSON.stringify(s));
|
|
4179
|
-
} else {
|
|
4180
|
-
localStorage.removeItem(ADMIN_STORAGE_KEY);
|
|
4181
|
-
}
|
|
4028
|
+
function normalizeCurrency(currency) {
|
|
4029
|
+
return currency.trim().toLowerCase();
|
|
4182
4030
|
}
|
|
4183
|
-
function
|
|
4184
|
-
const
|
|
4185
|
-
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
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
|
+
});
|
|
4041
|
+
return {
|
|
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) } : {}
|
|
4193
4052
|
});
|
|
4194
|
-
}
|
|
4195
|
-
}
|
|
4196
|
-
const updateSession = (updater) => {
|
|
4197
|
-
if (config.apiToken) return;
|
|
4198
|
-
const prev = readAdminSession();
|
|
4199
|
-
const next = updater(prev);
|
|
4200
|
-
writeAdminSession(next);
|
|
4201
|
-
emit();
|
|
4202
|
-
};
|
|
4203
|
-
const authStorage = config.apiToken ? {
|
|
4204
|
-
getTokens: () => ({ access_token: config.apiToken }),
|
|
4205
|
-
onTokensRefreshed: () => {
|
|
4206
4053
|
},
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
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
|
+
}
|
|
4213
4073
|
return {
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
access_expires_at: s.access_expires_at
|
|
4074
|
+
confirmation_token_id: result.confirmationToken.id,
|
|
4075
|
+
return_url: options.return_url
|
|
4217
4076
|
};
|
|
4218
4077
|
},
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
refresh_token: tokens.refresh_token ?? prev.refresh_token,
|
|
4225
|
-
access_expires_at: tokens.access_expires_at ?? prev.access_expires_at
|
|
4226
|
-
} : null
|
|
4227
|
-
);
|
|
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
|
+
}
|
|
4228
4083
|
},
|
|
4229
|
-
|
|
4230
|
-
|
|
4084
|
+
destroy() {
|
|
4085
|
+
paymentElement?.destroy();
|
|
4086
|
+
paymentElement = null;
|
|
4231
4087
|
}
|
|
4232
4088
|
};
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4089
|
+
}
|
|
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 } : {}
|
|
4240
4097
|
});
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
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;
|
|
4121
|
+
}
|
|
4122
|
+
return "";
|
|
4123
|
+
}
|
|
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}`);
|
|
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;
|
|
4170
|
+
}
|
|
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
|
|
4248
4186
|
};
|
|
4249
|
-
|
|
4250
|
-
|
|
4251
|
-
|
|
4252
|
-
|
|
4253
|
-
|
|
4254
|
-
|
|
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;
|
|
4219
|
+
}
|
|
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 };
|
|
4263
|
+
}
|
|
4264
|
+
}
|
|
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)
|
|
4324
|
+
};
|
|
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 });
|
|
4345
|
+
}
|
|
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 {
|
|
5894
|
+
access_token: s.access_token,
|
|
5895
|
+
refresh_token: s.refresh_token,
|
|
5896
|
+
access_expires_at: s.access_expires_at
|
|
5897
|
+
};
|
|
5898
|
+
},
|
|
5899
|
+
onTokensRefreshed(tokens) {
|
|
5900
|
+
updateSession(
|
|
5901
|
+
(prev) => prev ? {
|
|
5902
|
+
...prev,
|
|
5903
|
+
access_token: tokens.access_token,
|
|
5904
|
+
refresh_token: tokens.refresh_token ?? prev.refresh_token,
|
|
5905
|
+
access_expires_at: tokens.access_expires_at ?? prev.access_expires_at
|
|
5906
|
+
} : null
|
|
5907
|
+
);
|
|
5908
|
+
},
|
|
5909
|
+
onForcedLogout() {
|
|
5910
|
+
updateSession(() => null);
|
|
5911
|
+
}
|
|
5912
|
+
};
|
|
5913
|
+
const httpClient = createHttpClient({
|
|
5914
|
+
baseUrl: config.baseUrl,
|
|
5915
|
+
storeId: config.storeId,
|
|
5916
|
+
refreshPath: config.refreshPath,
|
|
5917
|
+
navigate: config.navigate,
|
|
5918
|
+
loginFallbackPath: config.loginFallbackPath,
|
|
5919
|
+
authStorage
|
|
5920
|
+
});
|
|
5921
|
+
const apiConfig = {
|
|
5922
|
+
httpClient,
|
|
5923
|
+
storeId: config.storeId,
|
|
5924
|
+
baseUrl: config.baseUrl,
|
|
5925
|
+
market: config.market,
|
|
5926
|
+
locale,
|
|
5927
|
+
authStorage
|
|
5928
|
+
};
|
|
5929
|
+
const accountApi = createAccountApi(apiConfig);
|
|
5930
|
+
const authApi = createAuthApi(apiConfig, updateSession);
|
|
5931
|
+
const storeApi = createStoreApi(apiConfig);
|
|
5932
|
+
const platformApi = createPlatformApi(apiConfig);
|
|
5933
|
+
const cmsApi = createCmsApi(apiConfig);
|
|
5934
|
+
const eshopApi = createEshopApi(apiConfig);
|
|
4255
5935
|
const promoCodeApi = createPromoCodeApi(apiConfig);
|
|
4256
5936
|
const crmApi = createContactApi(apiConfig);
|
|
4257
5937
|
const supportApi = createAdminSupportApi(apiConfig);
|
|
@@ -4312,6 +5992,7 @@ function createAdmin(config) {
|
|
|
4312
5992
|
update: storeApi.updateStore,
|
|
4313
5993
|
get: storeApi.getStore,
|
|
4314
5994
|
find: storeApi.getStores,
|
|
5995
|
+
regeneratePublishableKey: storeApi.regeneratePublishableKey,
|
|
4315
5996
|
subscription: {
|
|
4316
5997
|
get: storeApi.getSubscription,
|
|
4317
5998
|
getPlans: storeApi.getSubscriptionPlans,
|
|
@@ -4630,6 +6311,8 @@ function createAdmin(config) {
|
|
|
4630
6311
|
};
|
|
4631
6312
|
return sdk;
|
|
4632
6313
|
}
|
|
6314
|
+
var DEFAULT_STOREFRONT_API_URL = "https://api.arky.io";
|
|
6315
|
+
var storefrontScopeSequence = 0;
|
|
4633
6316
|
function defaultStorefrontSessionStorage() {
|
|
4634
6317
|
if (typeof window === "undefined") return null;
|
|
4635
6318
|
try {
|
|
@@ -4638,132 +6321,177 @@ function defaultStorefrontSessionStorage() {
|
|
|
4638
6321
|
return null;
|
|
4639
6322
|
}
|
|
4640
6323
|
}
|
|
4641
|
-
function
|
|
4642
|
-
const
|
|
4643
|
-
|
|
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(/\/+$/, "");
|
|
4644
6336
|
}
|
|
4645
|
-
function
|
|
4646
|
-
|
|
4647
|
-
|
|
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() || "";
|
|
4648
6372
|
const listeners = /* @__PURE__ */ new Set();
|
|
4649
|
-
let
|
|
6373
|
+
let identifyPromise = null;
|
|
4650
6374
|
let identityTail = Promise.resolve();
|
|
4651
|
-
|
|
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}` : ""}`;
|
|
4652
6381
|
let memorySession = null;
|
|
4653
|
-
|
|
4654
|
-
|
|
6382
|
+
let memorySessionToken = null;
|
|
6383
|
+
function readSessionToken() {
|
|
6384
|
+
if (!sessionStorage) return memorySessionToken;
|
|
4655
6385
|
try {
|
|
4656
|
-
const
|
|
4657
|
-
|
|
4658
|
-
);
|
|
4659
|
-
return raw ? JSON.parse(raw) : null;
|
|
6386
|
+
const stored = sessionStorage.getItem(storageKey);
|
|
6387
|
+
return isVisitorSessionToken(stored) ? stored : memorySessionToken;
|
|
4660
6388
|
} catch {
|
|
4661
|
-
return
|
|
6389
|
+
return memorySessionToken;
|
|
4662
6390
|
}
|
|
4663
6391
|
}
|
|
4664
6392
|
function writeContactSession(session) {
|
|
4665
6393
|
memorySession = session;
|
|
6394
|
+
memorySessionToken = session?.sessionToken || null;
|
|
4666
6395
|
if (!sessionStorage) return;
|
|
4667
|
-
const key = storefrontSessionStorageKey(config.baseUrl, config.storeId);
|
|
4668
6396
|
try {
|
|
4669
6397
|
if (session) {
|
|
4670
|
-
sessionStorage.setItem(
|
|
6398
|
+
sessionStorage.setItem(storageKey, session.sessionToken);
|
|
4671
6399
|
} else {
|
|
4672
|
-
sessionStorage.removeItem(
|
|
6400
|
+
sessionStorage.removeItem(storageKey);
|
|
4673
6401
|
}
|
|
4674
6402
|
} catch {
|
|
4675
6403
|
}
|
|
4676
6404
|
}
|
|
4677
6405
|
function toPublic(s) {
|
|
4678
|
-
return s ? { contact: s.contact
|
|
6406
|
+
return s ? { contact: s.contact } : null;
|
|
4679
6407
|
}
|
|
4680
6408
|
function emit() {
|
|
4681
|
-
const pub = toPublic(
|
|
6409
|
+
const pub = toPublic(memorySession);
|
|
4682
6410
|
for (const l of listeners) {
|
|
4683
6411
|
Promise.resolve().then(() => l(pub)).catch(() => {
|
|
4684
6412
|
});
|
|
4685
6413
|
}
|
|
4686
6414
|
}
|
|
4687
6415
|
const updateSession = (updater) => {
|
|
4688
|
-
|
|
4689
|
-
const prev = readContactSession();
|
|
4690
|
-
const next = updater(prev);
|
|
6416
|
+
const next = updater(memorySession);
|
|
4691
6417
|
writeContactSession(next);
|
|
4692
6418
|
emit();
|
|
4693
6419
|
};
|
|
4694
|
-
const authStorage =
|
|
4695
|
-
getTokens: () => ({ access_token: config.apiToken }),
|
|
4696
|
-
onTokensRefreshed: () => {
|
|
4697
|
-
},
|
|
4698
|
-
onForcedLogout: () => {
|
|
4699
|
-
}
|
|
4700
|
-
} : {
|
|
6420
|
+
const authStorage = {
|
|
4701
6421
|
getTokens() {
|
|
4702
|
-
const
|
|
4703
|
-
return
|
|
6422
|
+
const sessionToken = readSessionToken();
|
|
6423
|
+
return sessionToken ? { access_token: sessionToken } : null;
|
|
4704
6424
|
},
|
|
4705
6425
|
onTokensRefreshed() {
|
|
4706
6426
|
},
|
|
4707
6427
|
onForcedLogout() {
|
|
4708
|
-
|
|
6428
|
+
identifyPromise = null;
|
|
4709
6429
|
updateSession(() => null);
|
|
4710
6430
|
}
|
|
4711
6431
|
};
|
|
6432
|
+
let recoverUnauthorized = async () => false;
|
|
6433
|
+
let visitorRecoveryPromise = null;
|
|
4712
6434
|
const httpClient = createHttpClient({
|
|
4713
|
-
baseUrl:
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
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)
|
|
4719
6444
|
});
|
|
4720
6445
|
const apiConfig = {
|
|
4721
6446
|
httpClient,
|
|
4722
|
-
|
|
4723
|
-
|
|
4724
|
-
market
|
|
6447
|
+
apiUrl,
|
|
6448
|
+
publishableKey,
|
|
6449
|
+
market,
|
|
4725
6450
|
locale,
|
|
4726
6451
|
authStorage
|
|
4727
6452
|
};
|
|
4728
|
-
|
|
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
|
+
});
|
|
4729
6483
|
const contactApi = storefrontApi.crm.contact;
|
|
4730
6484
|
function identify(params) {
|
|
4731
|
-
|
|
4732
|
-
|
|
6485
|
+
requireVisitorSessionCapability();
|
|
6486
|
+
if (params?.market !== void 0) setMarket(params.market);
|
|
4733
6487
|
const isBareCall = !params?.email && !params?.verify;
|
|
4734
|
-
if (isBareCall &&
|
|
6488
|
+
if (isBareCall && identifyPromise) return identifyPromise;
|
|
4735
6489
|
const run = async () => {
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
market,
|
|
4742
|
-
email: params?.email
|
|
4743
|
-
}));
|
|
4744
|
-
return {
|
|
4745
|
-
contact: result.contact,
|
|
4746
|
-
store: result.store,
|
|
4747
|
-
market: result.market,
|
|
4748
|
-
verification_challenge: result.verification_challenge
|
|
4749
|
-
};
|
|
4750
|
-
} catch (err) {
|
|
4751
|
-
const e = err;
|
|
4752
|
-
const status = e?.statusCode || e?.status || e?.response?.status;
|
|
4753
|
-
if (isBareCall && status === 401) {
|
|
4754
|
-
updateSession(() => null);
|
|
4755
|
-
const result = await contactApi.identify({
|
|
4756
|
-
market
|
|
4757
|
-
});
|
|
4758
|
-
return {
|
|
4759
|
-
contact: result.contact,
|
|
4760
|
-
store: result.store,
|
|
4761
|
-
market: result.market,
|
|
4762
|
-
verification_challenge: result.verification_challenge
|
|
4763
|
-
};
|
|
4764
|
-
}
|
|
4765
|
-
throw err;
|
|
4766
|
-
}
|
|
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
|
+
};
|
|
4767
6495
|
};
|
|
4768
6496
|
const promise = identityTail.then(run);
|
|
4769
6497
|
identityTail = promise.then(
|
|
@@ -4771,49 +6499,88 @@ function createStorefrontClient(config) {
|
|
|
4771
6499
|
() => void 0
|
|
4772
6500
|
);
|
|
4773
6501
|
if (isBareCall) {
|
|
4774
|
-
|
|
6502
|
+
identifyPromise = promise;
|
|
4775
6503
|
void promise.then(
|
|
4776
6504
|
() => {
|
|
4777
|
-
if (
|
|
6505
|
+
if (identifyPromise === promise) identifyPromise = null;
|
|
4778
6506
|
},
|
|
4779
6507
|
() => {
|
|
4780
|
-
if (
|
|
6508
|
+
if (identifyPromise === promise) identifyPromise = null;
|
|
4781
6509
|
}
|
|
4782
6510
|
);
|
|
4783
6511
|
}
|
|
4784
6512
|
return promise;
|
|
4785
6513
|
}
|
|
4786
6514
|
async function verify(params) {
|
|
6515
|
+
requireVisitorSessionCapability();
|
|
4787
6516
|
const result = await contactApi.verify(params);
|
|
4788
|
-
|
|
4789
|
-
return result;
|
|
6517
|
+
identifyPromise = null;
|
|
6518
|
+
return { contact: result.contact };
|
|
6519
|
+
}
|
|
6520
|
+
async function me() {
|
|
6521
|
+
await ensureVisitorSession();
|
|
6522
|
+
return contactApi.getMe();
|
|
4790
6523
|
}
|
|
4791
6524
|
async function logout() {
|
|
4792
|
-
|
|
4793
|
-
|
|
6525
|
+
identifyPromise = null;
|
|
6526
|
+
if (!readSessionToken()) {
|
|
6527
|
+
updateSession(() => null);
|
|
6528
|
+
return;
|
|
6529
|
+
}
|
|
4794
6530
|
try {
|
|
4795
6531
|
await contactApi.logout();
|
|
4796
6532
|
} catch {
|
|
4797
6533
|
updateSession(() => null);
|
|
4798
6534
|
}
|
|
4799
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
|
+
};
|
|
4800
6570
|
return {
|
|
4801
6571
|
identify,
|
|
4802
6572
|
verify,
|
|
4803
6573
|
logout,
|
|
4804
|
-
me
|
|
6574
|
+
me,
|
|
4805
6575
|
get session() {
|
|
4806
|
-
|
|
4807
|
-
return toPublic(readContactSession());
|
|
6576
|
+
return toPublic(memorySession);
|
|
4808
6577
|
},
|
|
4809
6578
|
get isAuthenticated() {
|
|
4810
|
-
|
|
4811
|
-
const s = readContactSession();
|
|
4812
|
-
return s !== null && !!s.access_token;
|
|
6579
|
+
return Boolean(readSessionToken());
|
|
4813
6580
|
},
|
|
4814
6581
|
onAuthStateChanged(listener) {
|
|
4815
6582
|
listeners.add(listener);
|
|
4816
|
-
const current = toPublic(
|
|
6583
|
+
const current = toPublic(memorySession);
|
|
4817
6584
|
if (current) {
|
|
4818
6585
|
Promise.resolve().then(() => listener(current)).catch(() => {
|
|
4819
6586
|
});
|
|
@@ -4825,44 +6592,52 @@ function createStorefrontClient(config) {
|
|
|
4825
6592
|
store: storefrontApi.store,
|
|
4826
6593
|
cms: storefrontApi.cms,
|
|
4827
6594
|
eshop: storefrontApi.eshop,
|
|
4828
|
-
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
|
+
},
|
|
4829
6605
|
action: storefrontApi.action,
|
|
4830
6606
|
experiments: storefrontApi.experiments,
|
|
4831
|
-
support: createStorefrontSupportApi(apiConfig),
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
setLocale: (l) => {
|
|
4839
|
-
apiConfig.locale = l;
|
|
4840
|
-
},
|
|
4841
|
-
getLocale: () => apiConfig.locale,
|
|
6607
|
+
support: createStorefrontSupportApi(apiConfig, ensureVisitorSession),
|
|
6608
|
+
getSetup,
|
|
6609
|
+
setContext,
|
|
6610
|
+
setMarket,
|
|
6611
|
+
getMarket: () => market,
|
|
6612
|
+
setLocale,
|
|
6613
|
+
getLocale: () => locale,
|
|
4842
6614
|
utils: createUtilitySurface(apiConfig)
|
|
4843
6615
|
};
|
|
4844
6616
|
}
|
|
4845
|
-
function
|
|
4846
|
-
const
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
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
|
+
}
|
|
4862
6635
|
});
|
|
4863
|
-
|
|
6636
|
+
}
|
|
6637
|
+
function createStorefront(publishableKey, options = {}) {
|
|
6638
|
+
return createStorefrontClient(publishableKey, options);
|
|
4864
6639
|
}
|
|
4865
6640
|
|
|
4866
|
-
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 };
|
|
4867
6642
|
//# sourceMappingURL=index.js.map
|
|
4868
6643
|
//# sourceMappingURL=index.js.map
|