arky-sdk 0.9.13 → 0.9.16

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.
Files changed (40) hide show
  1. package/README.md +16 -12
  2. package/dist/admin-C-ZTxvz3.d.ts +1544 -0
  3. package/dist/admin-Dm2WRN6q.d.cts +1544 -0
  4. package/dist/admin.cjs +982 -425
  5. package/dist/admin.cjs.map +1 -1
  6. package/dist/admin.d.cts +3 -3
  7. package/dist/admin.d.ts +3 -3
  8. package/dist/admin.js +982 -425
  9. package/dist/admin.js.map +1 -1
  10. package/dist/{api-DvsFdOaF.d.cts → api-D37IpMSq.d.cts} +1417 -665
  11. package/dist/{api-DvsFdOaF.d.ts → api-D37IpMSq.d.ts} +1417 -665
  12. package/dist/index.cjs +1317 -539
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +3 -3
  15. package/dist/index.d.ts +3 -3
  16. package/dist/index.js +1317 -539
  17. package/dist/index.js.map +1 -1
  18. package/dist/{index-CQd9b_7n.d.ts → inventory-DdN96PX3.d.cts} +6 -4
  19. package/dist/{index-BC06yiuv.d.cts → inventory-Dh1RevEb.d.ts} +6 -4
  20. package/dist/storefront.cjs +1210 -658
  21. package/dist/storefront.cjs.map +1 -1
  22. package/dist/storefront.d.cts +88 -285
  23. package/dist/storefront.d.ts +88 -285
  24. package/dist/storefront.js +1207 -659
  25. package/dist/storefront.js.map +1 -1
  26. package/dist/types.cjs.map +1 -1
  27. package/dist/types.d.cts +1 -1
  28. package/dist/types.d.ts +1 -1
  29. package/dist/types.js.map +1 -1
  30. package/dist/utils.cjs +198 -16
  31. package/dist/utils.cjs.map +1 -1
  32. package/dist/utils.d.cts +18 -2
  33. package/dist/utils.d.ts +18 -2
  34. package/dist/utils.js +191 -17
  35. package/dist/utils.js.map +1 -1
  36. package/package.json +4 -5
  37. package/dist/admin-Q9MBFwCb.d.cts +0 -1496
  38. package/dist/admin-ZLXD4_en.d.ts +0 -1496
  39. package/scripts/contract-admin.mjs +0 -120
  40. package/scripts/contract-storefront.mjs +0 -296
@@ -1,21 +1,25 @@
1
1
  import { atom, computed, map } from 'nanostores';
2
2
 
3
3
  // src/utils/orderItems.ts
4
- function normalizeOrderCheckoutItems(items) {
4
+ function sanitizePublicCheckoutItems(items) {
5
5
  return items.map((item) => {
6
- if ("type" in item) {
7
- return item;
8
- }
9
- if ("product_id" in item) {
10
- return { type: "product", ...item };
6
+ if (item.type === "product") {
7
+ return {
8
+ type: "product",
9
+ ...item.id ? { id: item.id } : {},
10
+ product_id: item.product_id,
11
+ variant_id: item.variant_id,
12
+ quantity: item.quantity
13
+ };
11
14
  }
12
- return { type: "service", ...item };
13
- });
14
- }
15
- function normalizePublicCheckoutItems(items) {
16
- return normalizeOrderCheckoutItems(items).map((item) => {
17
- const { price: _price, ...publicItem } = item;
18
- return publicItem;
15
+ return {
16
+ type: "service",
17
+ ...item.id ? { id: item.id } : {},
18
+ service_id: item.service_id,
19
+ provider_id: item.provider_id,
20
+ slots: item.slots,
21
+ ...item.forms ? { forms: item.forms } : {}
22
+ };
19
23
  });
20
24
  }
21
25
 
@@ -40,10 +44,9 @@ var createActionApi = (apiConfig) => ({
40
44
  COMMON_ACTION_KEYS,
41
45
  async track(params) {
42
46
  try {
43
- const key = "key" in params && params.key ? params.key : params.type;
44
47
  await apiConfig.httpClient.post(
45
48
  `/v1/storefront/${apiConfig.storeId}/actions/track`,
46
- { key, payload: params.payload }
49
+ { key: params.key, payload: params.payload }
47
50
  );
48
51
  } catch {
49
52
  }
@@ -51,6 +54,47 @@ var createActionApi = (apiConfig) => ({
51
54
  });
52
55
  var createStorefrontApi = (apiConfig, updateContactSession) => {
53
56
  const base = (storeId = apiConfig.storeId) => `/v1/storefront/${storeId}`;
57
+ const pendingVerifications = /* @__PURE__ */ new Map();
58
+ function persistIdentification(result) {
59
+ const issued = result.token;
60
+ if (issued?.token) {
61
+ updateContactSession(() => ({
62
+ access_token: issued.token,
63
+ contact: result.contact,
64
+ store: result.store,
65
+ market: result.market
66
+ }));
67
+ } else {
68
+ if (result.verification_challenge) {
69
+ pendingVerifications.set(result.verification_challenge.challenge_id, {
70
+ store: result.store,
71
+ market: result.market
72
+ });
73
+ }
74
+ updateContactSession(
75
+ (current) => current ? {
76
+ ...current,
77
+ contact: result.contact,
78
+ store: result.store,
79
+ market: result.market
80
+ } : null
81
+ );
82
+ }
83
+ return result;
84
+ }
85
+ async function submitIdentification(path, params, options) {
86
+ const store_id = apiConfig.storeId;
87
+ const result = await apiConfig.httpClient.post(
88
+ `${base(store_id)}/account/${path}`,
89
+ {
90
+ store_id,
91
+ market: params?.market || apiConfig.market || null,
92
+ email: params?.email
93
+ },
94
+ options
95
+ );
96
+ return persistIdentification(result);
97
+ }
54
98
  return {
55
99
  store: {
56
100
  getStore(options) {
@@ -58,7 +102,10 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
58
102
  },
59
103
  location: {
60
104
  getCountries(options) {
61
- return apiConfig.httpClient.get(`/v1/platform/countries`, options);
105
+ return apiConfig.httpClient.get(
106
+ `/v1/platform/countries`,
107
+ options
108
+ );
62
109
  },
63
110
  getCountry(countryCode, options) {
64
111
  return apiConfig.httpClient.get(
@@ -67,18 +114,30 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
67
114
  );
68
115
  },
69
116
  list(options) {
70
- return apiConfig.httpClient.get(`${base()}/locations`, options);
117
+ return apiConfig.httpClient.get(
118
+ `${base()}/locations`,
119
+ options
120
+ );
71
121
  },
72
122
  get(id, options) {
73
- return apiConfig.httpClient.get(`${base()}/locations/${id}`, options);
123
+ return apiConfig.httpClient.get(
124
+ `${base()}/locations/${id}`,
125
+ options
126
+ );
74
127
  }
75
128
  },
76
129
  market: {
77
130
  list(options) {
78
- return apiConfig.httpClient.get(`${base()}/markets`, options);
131
+ return apiConfig.httpClient.get(
132
+ `${base()}/markets`,
133
+ options
134
+ );
79
135
  },
80
136
  get(id, options) {
81
- return apiConfig.httpClient.get(`${base()}/markets/${id}`, options);
137
+ return apiConfig.httpClient.get(
138
+ `${base()}/markets/${id}`,
139
+ options
140
+ );
82
141
  }
83
142
  }
84
143
  },
@@ -86,11 +145,9 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
86
145
  collection: {
87
146
  get(params, options) {
88
147
  const store_id = params.store_id || apiConfig.storeId;
89
- if (!params.id) {
90
- throw new Error("GetCollectionParams requires id");
91
- }
148
+ const identifier = params.id !== void 0 ? params.id : `${store_id}:${params.key}`;
92
149
  return apiConfig.httpClient.get(
93
- `${base(store_id)}/collections/${params.id}`,
150
+ `${base(store_id)}/collections/${identifier}`,
94
151
  options
95
152
  );
96
153
  }
@@ -108,10 +165,13 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
108
165
  },
109
166
  find(params, options) {
110
167
  const { store_id, ...queryParams } = params;
111
- return apiConfig.httpClient.get(`${base(store_id)}/entries`, {
112
- ...options,
113
- params: queryParams
114
- });
168
+ return apiConfig.httpClient.get(
169
+ `${base(store_id)}/entries`,
170
+ {
171
+ ...options,
172
+ params: queryParams
173
+ }
174
+ );
115
175
  }
116
176
  },
117
177
  form: {
@@ -187,10 +247,13 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
187
247
  },
188
248
  find(params, options) {
189
249
  const { store_id, ...queryParams } = params;
190
- return apiConfig.httpClient.get(`${base(store_id)}/products`, {
191
- ...options,
192
- params: queryParams
193
- });
250
+ return apiConfig.httpClient.get(
251
+ `${base(store_id)}/products`,
252
+ {
253
+ ...options,
254
+ params: queryParams
255
+ }
256
+ );
194
257
  }
195
258
  },
196
259
  cart: {
@@ -199,16 +262,23 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
199
262
  const { store_id: _store_id, ...payload } = params;
200
263
  return apiConfig.httpClient.post(
201
264
  `${base(store_id)}/carts/current`,
202
- { ...payload, store_id, market: payload.market || apiConfig.market },
265
+ {
266
+ ...payload,
267
+ store_id,
268
+ market: payload.market || apiConfig.market
269
+ },
203
270
  options
204
271
  );
205
272
  },
206
273
  get(params, options) {
207
274
  const store_id = params.store_id || apiConfig.storeId;
208
- return apiConfig.httpClient.get(`${base(store_id)}/carts/${params.id}`, {
209
- ...options,
210
- params: params.token ? { token: params.token } : options?.params
211
- });
275
+ return apiConfig.httpClient.get(
276
+ `${base(store_id)}/carts/${params.id}`,
277
+ {
278
+ ...options,
279
+ params: params.token ? { token: params.token } : options?.params
280
+ }
281
+ );
212
282
  },
213
283
  update(params, options) {
214
284
  const { store_id, items, ...payload } = params;
@@ -218,7 +288,7 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
218
288
  {
219
289
  ...payload,
220
290
  store_id: target,
221
- ...items ? { items: normalizePublicCheckoutItems(items) } : {}
291
+ ...items ? { items: sanitizePublicCheckoutItems(items) } : {}
222
292
  },
223
293
  options
224
294
  );
@@ -231,7 +301,7 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
231
301
  {
232
302
  ...payload,
233
303
  store_id: target,
234
- item: normalizePublicCheckoutItems([item])[0]
304
+ item: sanitizePublicCheckoutItems([item])[0]
235
305
  },
236
306
  options
237
307
  );
@@ -286,18 +356,35 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
286
356
  },
287
357
  find(params, options) {
288
358
  const { store_id, ...queryParams } = params;
289
- return apiConfig.httpClient.get(`${base(store_id)}/orders`, {
290
- ...options,
291
- params: queryParams
292
- });
359
+ return apiConfig.httpClient.get(
360
+ `${base(store_id)}/orders`,
361
+ {
362
+ ...options,
363
+ params: queryParams
364
+ }
365
+ );
293
366
  },
294
367
  downloadDigitalAccess(params, options) {
295
368
  const store_id = params.store_id || apiConfig.storeId;
296
369
  return apiConfig.httpClient.post(
297
- `${base(store_id)}/orders/${params.id}/digital-access/${params.grant_id}/download`,
370
+ `${base(store_id)}/orders/${params.order_id}/digital-access/${params.grant_id}/download`,
298
371
  {},
299
372
  options
300
373
  );
374
+ },
375
+ findDigitalAccess(params, options) {
376
+ const { order_id, store_id, ...queryParams } = params;
377
+ return apiConfig.httpClient.get(
378
+ `${base(store_id)}/orders/${order_id}/digital-access`,
379
+ { ...options, params: queryParams }
380
+ );
381
+ },
382
+ getDigitalAccess(params, options) {
383
+ const store_id = params.store_id || apiConfig.storeId;
384
+ return apiConfig.httpClient.get(
385
+ `${base(store_id)}/orders/${params.order_id}/digital-access/${params.grant_id}`,
386
+ options
387
+ );
301
388
  }
302
389
  },
303
390
  service: {
@@ -318,17 +405,23 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
318
405
  },
319
406
  find(params, options) {
320
407
  const { store_id, ...queryParams } = params;
321
- return apiConfig.httpClient.get(`${base(store_id)}/services`, {
322
- ...options,
323
- params: queryParams
324
- });
408
+ return apiConfig.httpClient.get(
409
+ `${base(store_id)}/services`,
410
+ {
411
+ ...options,
412
+ params: queryParams
413
+ }
414
+ );
325
415
  },
326
416
  findProviders(params, options) {
327
417
  const { store_id, ...queryParams } = params;
328
- return apiConfig.httpClient.get(`${base(store_id)}/service-providers`, {
329
- ...options,
330
- params: queryParams
331
- });
418
+ return apiConfig.httpClient.get(
419
+ `${base(store_id)}/service-providers`,
420
+ {
421
+ ...options,
422
+ params: queryParams
423
+ }
424
+ );
332
425
  },
333
426
  getAvailability(params, options) {
334
427
  const { store_id, ...queryParams } = params;
@@ -357,48 +450,47 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
357
450
  },
358
451
  find(params, options) {
359
452
  const { store_id, ...queryParams } = params;
360
- return apiConfig.httpClient.get(`${base(store_id)}/providers`, {
361
- ...options,
362
- params: queryParams
363
- });
453
+ return apiConfig.httpClient.get(
454
+ `${base(store_id)}/providers`,
455
+ {
456
+ ...options,
457
+ params: queryParams
458
+ }
459
+ );
364
460
  }
365
461
  }
366
462
  },
367
463
  crm: {
368
464
  contact: {
369
- async identify(params, options) {
370
- const store_id = apiConfig.storeId;
371
- const result = await apiConfig.httpClient.post(
372
- `${base(store_id)}/account/identify`,
373
- {
374
- store_id,
375
- market: params?.market || apiConfig.market || null,
376
- email: params?.email,
377
- verify: params?.verify ?? false
378
- },
379
- options
380
- );
381
- if (result?.token?.token) {
382
- updateContactSession(() => ({
383
- access_token: result.token.token,
384
- contact: result.contact,
385
- store: result.store,
386
- market: result.market
387
- }));
388
- }
389
- return result;
465
+ identify(params, options) {
466
+ return submitIdentification("identify", params, options);
467
+ },
468
+ requestCode(params, options) {
469
+ return submitIdentification("code", params, options);
390
470
  },
391
471
  async verify(params, options) {
392
472
  const store_id = apiConfig.storeId;
393
473
  const result = await apiConfig.httpClient.post(
394
474
  `${base(store_id)}/account/verify`,
395
- { store_id, code: params.code },
475
+ { store_id, challenge_id: params.challenge_id, code: params.code },
396
476
  options
397
477
  );
398
- if (result?.token) {
478
+ if (result?.token?.token) {
479
+ const pending = pendingVerifications.get(params.challenge_id);
480
+ const identifiedStore = pending?.store || await apiConfig.httpClient.get(base(store_id), options);
399
481
  updateContactSession(
400
- (prev) => prev ? { ...prev, access_token: result.token } : null
482
+ (prev) => prev ? {
483
+ ...prev,
484
+ access_token: result.token.token,
485
+ contact: result.contact
486
+ } : {
487
+ access_token: result.token.token,
488
+ contact: result.contact,
489
+ store: identifiedStore,
490
+ market: pending?.market || null
491
+ }
401
492
  );
493
+ pendingVerifications.delete(params.challenge_id);
402
494
  }
403
495
  return result;
404
496
  },
@@ -415,7 +507,10 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
415
507
  }
416
508
  },
417
509
  getMe(options) {
418
- return apiConfig.httpClient.get(`${base()}/account/me`, options);
510
+ return apiConfig.httpClient.get(
511
+ `${base()}/account/me`,
512
+ options
513
+ );
419
514
  }
420
515
  },
421
516
  contactList: {
@@ -428,10 +523,31 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
428
523
  },
429
524
  find(params, options) {
430
525
  const { store_id, ...queryParams } = params || {};
431
- return apiConfig.httpClient.get(`${base(store_id)}/contact-lists`, {
432
- ...options,
433
- params: queryParams
434
- });
526
+ return apiConfig.httpClient.get(
527
+ `${base(store_id)}/contact-lists`,
528
+ {
529
+ ...options,
530
+ params: queryParams
531
+ }
532
+ );
533
+ },
534
+ plans: {
535
+ find(params, options) {
536
+ const { store_id, contact_list_id, ...queryParams } = params;
537
+ return apiConfig.httpClient.get(
538
+ `${base(store_id)}/contact-lists/${contact_list_id}/plans`,
539
+ { ...options, params: queryParams }
540
+ );
541
+ }
542
+ },
543
+ memberships: {
544
+ find(params, options) {
545
+ const { store_id, ...queryParams } = params || {};
546
+ return apiConfig.httpClient.get(`${base(store_id)}/contact-lists/memberships`, {
547
+ ...options,
548
+ params: queryParams
549
+ });
550
+ }
435
551
  },
436
552
  subscribe(params, options) {
437
553
  const { store_id, id, ...payload } = params;
@@ -456,13 +572,31 @@ var createStorefrontApi = (apiConfig, updateContactSession) => {
456
572
  options
457
573
  );
458
574
  },
459
- unsubscribe(token, options) {
575
+ manage(token, options) {
460
576
  return apiConfig.httpClient.post(
461
- `${base()}/contact-lists/unsubscribe`,
577
+ `${base()}/contact-lists/manage`,
462
578
  { token },
463
579
  options
464
580
  );
465
581
  },
582
+ unsubscribe(token, options) {
583
+ const headers = { ...options?.headers };
584
+ for (const name of Object.keys(headers)) {
585
+ if (name.toLowerCase() === "content-type") delete headers[name];
586
+ }
587
+ return apiConfig.httpClient.post(
588
+ `${base()}/contact-lists/unsubscribe`,
589
+ new URLSearchParams({ "List-Unsubscribe": "One-Click" }),
590
+ {
591
+ ...options,
592
+ params: { ...options?.params || {}, token },
593
+ headers: {
594
+ ...headers,
595
+ "Content-Type": "application/x-www-form-urlencoded"
596
+ }
597
+ }
598
+ );
599
+ },
466
600
  confirm(token, options) {
467
601
  return apiConfig.httpClient.post(
468
602
  `${base()}/contact-lists/confirm`,
@@ -627,27 +761,47 @@ var convertServerErrorToRequestError = (serverError, renameRules) => {
627
761
 
628
762
  // src/utils/queryParams.ts
629
763
  function buildQueryString(params) {
630
- const queryParts = [];
631
- Object.entries(params).forEach(([key, value]) => {
632
- if (value === null || value === void 0) {
633
- return;
634
- }
635
- if (Array.isArray(value)) {
636
- const jsonString = JSON.stringify(value);
637
- queryParts.push(`${key}=${encodeURIComponent(jsonString)}`);
638
- } else if (typeof value === "string") {
639
- queryParts.push(`${key}=${encodeURIComponent(value)}`);
640
- } else if (typeof value === "number" || typeof value === "boolean") {
641
- queryParts.push(`${key}=${value}`);
642
- } else if (typeof value === "object") {
643
- const jsonString = JSON.stringify(value);
644
- queryParts.push(`${key}=${encodeURIComponent(jsonString)}`);
764
+ const queryParts = Object.entries(params).flatMap(
765
+ ([key, value]) => {
766
+ if (value === null || value === void 0) return [];
767
+ if (typeof value === "string") {
768
+ return [`${key}=${encodeURIComponent(value)}`];
769
+ }
770
+ if (typeof value === "number" || typeof value === "boolean") {
771
+ return [`${key}=${value}`];
772
+ }
773
+ if (Array.isArray(value) || typeof value === "object") {
774
+ return [`${key}=${encodeURIComponent(JSON.stringify(value))}`];
775
+ }
776
+ return [];
645
777
  }
646
- });
778
+ );
647
779
  return queryParts.length > 0 ? `?${queryParts.join("&")}` : "";
648
780
  }
649
781
 
650
782
  // src/services/createHttpClient.ts
783
+ function requestError(name, message, details = {}) {
784
+ return Object.assign(new Error(message), { name }, details);
785
+ }
786
+ function isRecord(value) {
787
+ return typeof value === "object" && value !== null;
788
+ }
789
+ function isTokenSet(value) {
790
+ 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");
791
+ }
792
+ function isValidationError(value) {
793
+ return isRecord(value) && typeof value.field === "string" && typeof value.error === "string";
794
+ }
795
+ function toServerError(value, statusCode) {
796
+ const payload = isRecord(value) ? value : {};
797
+ const validationErrors = Array.isArray(payload.validationErrors) ? payload.validationErrors.filter(isValidationError) : [];
798
+ return {
799
+ message: typeof payload.message === "string" ? payload.message : "Request failed",
800
+ error: typeof payload.error === "string" ? payload.error : "REQUEST_FAILED",
801
+ statusCode: typeof payload.statusCode === "number" ? payload.statusCode : statusCode,
802
+ validationErrors
803
+ };
804
+ }
651
805
  function createHttpClient(cfg) {
652
806
  const { authStorage } = cfg;
653
807
  let refreshPromise = null;
@@ -664,32 +818,41 @@ function createHttpClient(cfg) {
664
818
  const refresh_token = tokens?.refresh_token;
665
819
  if (!refresh_token) {
666
820
  authStorage.onForcedLogout();
667
- const err = new Error("No refresh token available");
668
- err.name = "ApiError";
669
- err.statusCode = 401;
670
- throw err;
821
+ throw requestError("ApiError", "No refresh token available", {
822
+ statusCode: 401
823
+ });
671
824
  }
672
825
  const refRes = await fetch(getRefreshEndpoint(), {
673
826
  method: "POST",
674
- headers: { Accept: "application/json", "Content-Type": "application/json" },
827
+ headers: {
828
+ Accept: "application/json",
829
+ "Content-Type": "application/json"
830
+ },
675
831
  body: JSON.stringify({ refresh_token })
676
832
  });
677
833
  if (!refRes.ok) {
678
834
  authStorage.onForcedLogout();
679
- const err = new Error("Token refresh failed");
680
- err.name = "ApiError";
681
- err.statusCode = 401;
682
- throw err;
835
+ throw requestError("ApiError", "Token refresh failed", {
836
+ statusCode: 401
837
+ });
683
838
  }
684
839
  const data = await refRes.json();
840
+ if (!isTokenSet(data)) {
841
+ authStorage.onForcedLogout();
842
+ throw requestError(
843
+ "ParseError",
844
+ "Token refresh returned an invalid response",
845
+ { statusCode: refRes.status }
846
+ );
847
+ }
685
848
  authStorage.onTokensRefreshed(data);
686
849
  })().finally(() => {
687
850
  refreshPromise = null;
688
851
  });
689
852
  return refreshPromise;
690
853
  }
691
- async function request(method, path, body, options) {
692
- if (options?.transformRequest) {
854
+ async function request(method, path, body, options, retried = false) {
855
+ if (!retried && options?.transformRequest) {
693
856
  body = options.transformRequest(body);
694
857
  }
695
858
  const headers = {
@@ -707,23 +870,27 @@ function createHttpClient(cfg) {
707
870
  headers["Authorization"] = `Bearer ${tokens.access_token}`;
708
871
  }
709
872
  const finalPath = options?.params ? path + buildQueryString(options.params) : path;
710
- const fetchOpts = { method, headers, signal: options?.signal };
873
+ const fetchOptions = {
874
+ method,
875
+ headers,
876
+ signal: options?.signal
877
+ };
711
878
  if (!["GET", "DELETE"].includes(method) && body !== void 0) {
712
- fetchOpts.body = JSON.stringify(body);
879
+ fetchOptions.body = body instanceof URLSearchParams ? body.toString() : JSON.stringify(body);
713
880
  }
714
881
  const fullUrl = `${cfg.baseUrl}${finalPath}`;
715
882
  let res;
716
883
  let data;
717
884
  const startedAt = Date.now();
718
885
  try {
719
- res = await fetch(fullUrl, fetchOpts);
886
+ res = await fetch(fullUrl, fetchOptions);
720
887
  } catch (error) {
721
888
  const aborted = options?.signal?.aborted || error instanceof Error && error.name === "AbortError";
722
- const err = new Error(error instanceof Error ? error.message : "Network request failed");
723
- err.name = aborted ? "AbortError" : "NetworkError";
724
- err.method = method;
725
- err.url = fullUrl;
726
- err.aborted = aborted;
889
+ const err = requestError(
890
+ aborted ? "AbortError" : "NetworkError",
891
+ error instanceof Error ? error.message : "Network request failed",
892
+ { method, url: fullUrl, aborted }
893
+ );
727
894
  if (options?.onError && method !== "GET") {
728
895
  Promise.resolve(
729
896
  options.onError({ error: err, method, url: fullUrl, aborted })
@@ -732,18 +899,9 @@ function createHttpClient(cfg) {
732
899
  }
733
900
  throw err;
734
901
  }
735
- if (res.status === 401 && !options?.["_retried"]) {
736
- try {
737
- await ensureFreshToken();
738
- const refreshed = authStorage.getTokens();
739
- if (refreshed?.access_token) {
740
- headers["Authorization"] = `Bearer ${refreshed.access_token}`;
741
- fetchOpts.headers = headers;
742
- }
743
- return request(method, path, body, { ...options, _retried: true });
744
- } catch (refreshError) {
745
- throw refreshError;
746
- }
902
+ if (res.status === 401 && !retried) {
903
+ await ensureFreshToken();
904
+ return request(method, path, body, options, true);
747
905
  }
748
906
  try {
749
907
  const contentLength = res.headers.get("content-length");
@@ -754,30 +912,35 @@ function createHttpClient(cfg) {
754
912
  data = await res.json();
755
913
  }
756
914
  } catch (error) {
757
- const err = new Error("Failed to parse response");
758
- err.name = "ParseError";
759
- err.method = method;
760
- err.url = fullUrl;
761
- err.status = res.status;
915
+ const err = requestError("ParseError", "Failed to parse response", {
916
+ method,
917
+ url: fullUrl,
918
+ statusCode: res.status
919
+ });
762
920
  if (options?.onError && method !== "GET") {
763
921
  Promise.resolve(
764
- options.onError({ error: err, method, url: fullUrl, status: res.status })
922
+ options.onError({
923
+ error: err,
924
+ method,
925
+ url: fullUrl,
926
+ status: res.status
927
+ })
765
928
  ).catch(() => {
766
929
  });
767
930
  }
768
931
  throw err;
769
932
  }
770
933
  if (!res.ok) {
771
- const serverErr = data;
934
+ const serverErr = toServerError(data, res.status);
772
935
  const reqErr = convertServerErrorToRequestError(serverErr);
773
- const err = new Error(serverErr.message || "Request failed");
774
- err.name = "ApiError";
775
- err.statusCode = serverErr.statusCode || res.status;
776
- err.validationErrors = reqErr.validationErrors;
777
- err.method = method;
778
- err.url = fullUrl;
779
936
  const requestId = res.headers.get("x-request-id") || res.headers.get("request-id");
780
- if (requestId) err.requestId = requestId;
937
+ const err = requestError("ApiError", serverErr.message, {
938
+ statusCode: serverErr.statusCode,
939
+ validationErrors: reqErr.validationErrors,
940
+ method,
941
+ url: fullUrl,
942
+ requestId: requestId || void 0
943
+ });
781
944
  if (options?.onError && method !== "GET") {
782
945
  Promise.resolve(
783
946
  options.onError({
@@ -827,183 +990,279 @@ function supportConversationQuery(params) {
827
990
  if (params.after_id) qs.set("after_id", params.after_id);
828
991
  return qs.toString();
829
992
  }
993
+ function storefrontSupportOptions(supportToken, options) {
994
+ if (!/^[0-9a-f]{64}$/.test(supportToken)) {
995
+ throw new Error("support_token must be a 64-character lowercase hexadecimal token");
996
+ }
997
+ const headers = { ...options?.headers };
998
+ for (const name of Object.keys(headers)) {
999
+ if (name.toLowerCase() === "x-arky-support-token") delete headers[name];
1000
+ }
1001
+ return {
1002
+ ...options,
1003
+ headers: {
1004
+ ...headers,
1005
+ "X-Arky-Support-Token": supportToken
1006
+ }
1007
+ };
1008
+ }
830
1009
  function createStorefrontSupportApi(config) {
831
- const { httpClient, storeId } = config;
1010
+ const { httpClient } = config;
832
1011
  return {
833
1012
  async startConversation(params = {}, opts) {
1013
+ const storeId = config.storeId;
834
1014
  return httpClient.post(
835
1015
  `/v1/storefront/${storeId}/support/conversations`,
836
- { store_id: storeId, ...params },
1016
+ { ...params, store_id: storeId },
837
1017
  opts
838
1018
  );
839
1019
  },
840
1020
  async sendMessage(params, opts) {
1021
+ const { support_token, ...request } = params;
1022
+ const storeId = config.storeId;
841
1023
  return httpClient.post(
842
- `/v1/storefront/${storeId}/support/conversations/${params.conversation_id}/messages`,
843
- { store_id: storeId, ...params },
844
- opts
1024
+ `/v1/storefront/${storeId}/support/conversations/${request.conversation_id}/messages`,
1025
+ { ...request, store_id: storeId },
1026
+ storefrontSupportOptions(support_token, opts)
845
1027
  );
846
1028
  },
847
1029
  async getConversation(params, opts) {
848
- const qs = supportConversationQuery({ store_id: storeId, ...params });
1030
+ const { support_token, ...request } = params;
1031
+ const storeId = config.storeId;
1032
+ const qs = supportConversationQuery({ ...request, store_id: storeId });
849
1033
  return httpClient.get(
850
- `/v1/storefront/${storeId}/support/conversations/${params.conversation_id}?${qs}`,
851
- opts
1034
+ `/v1/storefront/${storeId}/support/conversations/${request.conversation_id}?${qs}`,
1035
+ storefrontSupportOptions(support_token, opts)
852
1036
  );
853
1037
  }
854
1038
  };
855
1039
  }
856
1040
 
857
1041
  // src/utils/blocks.ts
1042
+ function isRecord2(value) {
1043
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1044
+ }
1045
+ function isBlock(value) {
1046
+ return isRecord2(value) && typeof value.key === "string" && typeof value.type === "string";
1047
+ }
1048
+ function recordFromBlocks(values, locale) {
1049
+ const result = {};
1050
+ for (const value of values) {
1051
+ if (!isBlock(value)) continue;
1052
+ result[value.key] = unwrapBlock(value, locale);
1053
+ }
1054
+ return result;
1055
+ }
1056
+ function localizedValue(value, locale) {
1057
+ if (typeof value === "string") return value;
1058
+ if (!isRecord2(value)) return "";
1059
+ const selected = value[locale] ?? value.en;
1060
+ return typeof selected === "string" ? selected : "";
1061
+ }
1062
+ function unwrapBlock(value, locale) {
1063
+ if (!isBlock(value)) return value;
1064
+ if (value.type === "localized_text" || value.type === "markdown") {
1065
+ return localizedValue(value.value, locale);
1066
+ }
1067
+ if (value.type === "array") {
1068
+ if (!Array.isArray(value.value)) return [];
1069
+ return value.value.map((item) => {
1070
+ if (isBlock(item)) return unwrapBlock(item, locale);
1071
+ if (isRecord2(item) && Array.isArray(item.value)) {
1072
+ return recordFromBlocks(item.value, locale);
1073
+ }
1074
+ if (!isRecord2(item)) return item;
1075
+ return Object.fromEntries(
1076
+ Object.entries(item).map(([key, nested]) => [key, unwrapBlock(nested, locale)])
1077
+ );
1078
+ });
1079
+ }
1080
+ if (value.type === "object") {
1081
+ if (Array.isArray(value.value)) return recordFromBlocks(value.value, locale);
1082
+ if (!isRecord2(value.value)) return {};
1083
+ return Object.fromEntries(
1084
+ Object.entries(value.value).map(([key, nested]) => [key, unwrapBlock(nested, locale)])
1085
+ );
1086
+ }
1087
+ return value.value;
1088
+ }
1089
+ function blockContentArray(values, locale) {
1090
+ const blocks = values.filter(isBlock);
1091
+ if (blocks.length === 0) return [];
1092
+ if (blocks.every((block) => block.key === blocks[0].key)) {
1093
+ return blocks.map((block) => blockContentValue(block, locale));
1094
+ }
1095
+ return Object.fromEntries(
1096
+ blocks.map((block) => [block.key, blockContentValue(block, locale)])
1097
+ );
1098
+ }
1099
+ function blockContentValue(block, locale) {
1100
+ if (block.type === "localized_text" || block.type === "markdown") {
1101
+ return localizedValue(block.value, locale);
1102
+ }
1103
+ if (block.type === "media") return block.value ?? null;
1104
+ if (block.type === "array") {
1105
+ return Array.isArray(block.value) ? blockContentArray(block.value, locale) : [];
1106
+ }
1107
+ if (block.type === "object") {
1108
+ if (Array.isArray(block.value)) return blockContentArray(block.value, locale);
1109
+ if (!isRecord2(block.value)) return {};
1110
+ return Object.fromEntries(
1111
+ Object.entries(block.value).map(([key, value]) => [
1112
+ key,
1113
+ isBlock(value) ? blockContentValue(value, locale) : value
1114
+ ])
1115
+ );
1116
+ }
1117
+ return block.value;
1118
+ }
1119
+ function findBlock(entry, key) {
1120
+ return entry?.blocks?.find((block) => block.key === key);
1121
+ }
858
1122
  function getBlockLabel(block) {
859
- if (!block) return "";
860
- return block.key?.replace(/_/g, " ").replace(/\b\w/g, (l) => l.toUpperCase()) ?? "";
1123
+ return block?.key.replace(/_/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase()) ?? "";
861
1124
  }
862
1125
  function formatBlockValue(block) {
863
- if (!block || block.value === null || block.value === void 0) return "";
864
- const value = block.value;
865
- switch (block.type) {
866
- case "boolean":
867
- return value ? "Yes" : "No";
868
- case "number":
869
- if (block.properties?.variant === "DATE" || block.properties?.variant === "DATE_TIME") {
870
- return new Date(value).toLocaleDateString();
871
- }
872
- return String(value);
873
- case "media":
874
- if (value && typeof value === "object") {
875
- return value.mime_type ? value.name || value.id : value.title || value.name || value.id;
876
- }
877
- return String(value);
878
- default:
879
- return String(value);
1126
+ if (block?.value === null || block?.value === void 0) return "";
1127
+ if (block.type === "boolean") return block.value ? "Yes" : "No";
1128
+ if (block.type === "number") {
1129
+ const properties = isRecord2(block.properties) ? block.properties : {};
1130
+ if (properties.variant === "DATE" || properties.variant === "DATE_TIME") {
1131
+ return new Date(Number(block.value)).toLocaleDateString();
1132
+ }
880
1133
  }
1134
+ if (block.type === "media" && isRecord2(block.value)) {
1135
+ const label = block.value.name ?? block.value.title ?? block.value.id;
1136
+ return typeof label === "string" ? label : "";
1137
+ }
1138
+ return String(block.value);
881
1139
  }
882
- function prepareBlocksForSubmission(formData, blockTypes) {
883
- return Object.keys(formData).filter((key) => formData[key] !== null && formData[key] !== void 0).map((key) => ({
1140
+ function prepareBlocksForSubmission(formData, blockTypes = {}) {
1141
+ return Object.entries(formData).filter(([, value]) => value !== null && value !== void 0).map(([key, value]) => ({
884
1142
  key,
885
- value: (blockTypes?.[key] || "text") === "array" && !Array.isArray(formData[key]) ? [formData[key]] : formData[key]
1143
+ value: blockTypes[key] === "array" && !Array.isArray(value) ? [value] : value
886
1144
  }));
887
1145
  }
888
1146
  function extractBlockValues(blocks) {
889
- const values = {};
890
- blocks.forEach((block) => {
891
- values[block.key] = block.value ?? null;
892
- });
893
- return values;
1147
+ return Object.fromEntries(blocks.map((block) => [block.key, block.value ?? null]));
894
1148
  }
895
- var getBlockValue = (entry, blockKey) => {
896
- const block = entry?.blocks?.find((f) => f.key === blockKey);
897
- return block?.value ?? null;
898
- };
899
- var getBlockTextValue = (block, locale = "en") => {
1149
+ function getBlockValue(entry, key) {
1150
+ return findBlock(entry, key)?.value ?? null;
1151
+ }
1152
+ function getBlockTextValue(block, locale = "en") {
900
1153
  if (!block || block.value === null || block.value === void 0) return "";
901
- const blockType = block.type;
902
- if (blockType === "localized_text" || blockType === "markdown") {
903
- if (typeof block.value === "object" && block.value !== null) {
904
- return block.value[locale] ?? block.value["en"] ?? "";
905
- }
906
- }
907
- if (typeof block.value === "string") return block.value;
908
- return String(block.value ?? "");
909
- };
910
- var getBlockValues = (entry, blockKey) => {
911
- const block = entry?.blocks?.find((f) => f.key === blockKey);
912
- if (!block) return [];
913
- if (block.type === "array" && Array.isArray(block.value)) {
914
- return block.value;
915
- }
916
- return [];
917
- };
918
- function unwrapBlock(block, locale) {
919
- if (!block?.type || block.value === void 0) return block;
920
- const blockType = block.type;
921
- if (blockType === "array") {
922
- return block.value.map((obj) => {
923
- const parsed = {};
924
- for (const [k, v] of Object.entries(obj)) {
925
- parsed[k] = unwrapBlock(v, locale);
926
- }
927
- return parsed;
928
- });
929
- }
930
- if (blockType === "object") {
931
- const parsed = {};
932
- for (const [k, v] of Object.entries(block.value || {})) {
933
- parsed[k] = unwrapBlock(v, locale);
934
- }
935
- return parsed;
1154
+ if (block.type === "localized_text" || block.type === "markdown") {
1155
+ return localizedValue(block.value, locale);
936
1156
  }
937
- if (blockType === "localized_text" || blockType === "markdown") {
938
- return block.value?.[locale];
939
- }
940
- return block.value;
1157
+ return typeof block.value === "string" ? block.value : String(block.value);
1158
+ }
1159
+ function getBlockContentValue(entry, key, locale = "en") {
1160
+ const block = findBlock(entry, key);
1161
+ return block ? blockContentValue(block, locale) : null;
941
1162
  }
942
- var getBlockObjectValues = (entry, blockKey, locale = "en") => {
943
- const block = entry?.blocks?.find((f) => f.key === blockKey);
944
- if (!block || block.type !== "array" || !Array.isArray(block.value)) return [];
945
- return block.value.map((obj) => {
946
- if (!obj?.value || !Array.isArray(obj.value)) return {};
947
- return obj.value.reduce((acc, current) => {
948
- acc[current.key] = unwrapBlock(current, locale);
949
- return acc;
950
- }, {});
1163
+ function getBlockValues(entry, key) {
1164
+ const block = findBlock(entry, key);
1165
+ return block?.type === "array" && Array.isArray(block.value) ? block.value : [];
1166
+ }
1167
+ function getBlockObjectValues(entry, key, locale = "en") {
1168
+ return getBlockValues(entry, key).map((value) => {
1169
+ if (isRecord2(value) && Array.isArray(value.value)) {
1170
+ return recordFromBlocks(value.value, locale);
1171
+ }
1172
+ return isRecord2(value) ? Object.fromEntries(
1173
+ Object.entries(value).map(([field, nested]) => [field, unwrapBlock(nested, locale)])
1174
+ ) : {};
951
1175
  });
952
- };
953
- var getBlockFromArray = (entry, blockKey, locale = "en") => {
954
- const block = entry?.blocks?.find((f) => f.key === blockKey);
1176
+ }
1177
+ function getBlockFromArray(entry, key, locale = "en") {
1178
+ const block = findBlock(entry, key);
955
1179
  if (!block) return {};
956
- if (block.type === "array" && Array.isArray(block.value)) {
957
- return block.value.reduce((acc, current) => {
958
- acc[current.key] = unwrapBlock(current, locale);
959
- return acc;
960
- }, {});
961
- }
962
- if (block.type === "object" && block.value && typeof block.value === "object") {
963
- const result = {};
964
- for (const [k, v] of Object.entries(block.value)) {
965
- result[k] = unwrapBlock(v, locale);
966
- }
967
- return result;
968
- }
969
- return { [block.key]: unwrapBlock(block, locale) };
970
- };
971
- var getImageUrl = (imageBlock, isBlock = true) => {
972
- if (!imageBlock) return null;
973
- if (imageBlock.type === "media") {
974
- const mediaValue = imageBlock.value;
975
- return mediaValue?.resolutions?.original?.url || mediaValue?.url || null;
976
- }
977
- if (isBlock) {
978
- if (typeof imageBlock === "string") return imageBlock;
979
- if (imageBlock.url) return imageBlock.url;
980
- }
981
- return imageBlock.resolutions?.original?.url || null;
982
- };
1180
+ const value = unwrapBlock(block, locale);
1181
+ if (isRecord2(value)) return value;
1182
+ if (Array.isArray(block.value)) return recordFromBlocks(block.value, locale);
1183
+ return { [block.key]: value };
1184
+ }
1185
+ function nestedUrl(value) {
1186
+ const resolutions = isRecord2(value.resolutions) ? value.resolutions : null;
1187
+ const original = resolutions && isRecord2(resolutions.original) ? resolutions.original : null;
1188
+ if (typeof original?.url === "string") return original.url;
1189
+ return typeof value.url === "string" ? value.url : null;
1190
+ }
1191
+ function getImageUrl(value, isBlock2 = true) {
1192
+ if (typeof value === "string") return value;
1193
+ if (!isRecord2(value)) return null;
1194
+ if (value.type === "media" && isRecord2(value.value)) return nestedUrl(value.value);
1195
+ if (isBlock2 && typeof value.url === "string") return value.url;
1196
+ return nestedUrl(value);
1197
+ }
983
1198
 
984
1199
  // src/utils/price.ts
1200
+ var SUPPORTED_STORE_CURRENCIES = Object.freeze([
1201
+ "USD",
1202
+ "EUR",
1203
+ "GBP",
1204
+ "JPY",
1205
+ "CNY",
1206
+ "CHF",
1207
+ "AUD",
1208
+ "CAD",
1209
+ "HKD",
1210
+ "SGD",
1211
+ "NZD",
1212
+ "KRW",
1213
+ "SEK",
1214
+ "NOK",
1215
+ "DKK",
1216
+ "INR",
1217
+ "MXN",
1218
+ "BRL",
1219
+ "ZAR",
1220
+ "RUB",
1221
+ "TRY",
1222
+ "PLN",
1223
+ "THB",
1224
+ "IDR",
1225
+ "MYR",
1226
+ "PHP",
1227
+ "CZK",
1228
+ "ILS",
1229
+ "AED",
1230
+ "SAR",
1231
+ "HUF",
1232
+ "RON",
1233
+ "BGN",
1234
+ "HRK",
1235
+ "BAM",
1236
+ "RSD",
1237
+ "MKD",
1238
+ "ALL"
1239
+ ]);
1240
+ var SUPPORTED_STORE_CURRENCY_SET = Object.freeze(
1241
+ new Set(SUPPORTED_STORE_CURRENCIES)
1242
+ );
1243
+ var ZERO_MINOR_UNIT_STORE_CURRENCIES = Object.freeze(
1244
+ /* @__PURE__ */ new Set(["JPY", "KRW"])
1245
+ );
985
1246
  function formatCurrency(amount, currencyCode, locale = "en") {
986
- if (!currencyCode) return "";
1247
+ const normalized = currencyCode.trim().toUpperCase();
1248
+ if (!normalized) return "";
1249
+ const minorUnits = getCurrencyMinorUnits(normalized);
987
1250
  return new Intl.NumberFormat(locale, {
988
1251
  style: "currency",
989
- currency: currencyCode.toUpperCase()
1252
+ currency: normalized,
1253
+ minimumFractionDigits: minorUnits,
1254
+ maximumFractionDigits: minorUnits
990
1255
  }).format(amount);
991
1256
  }
992
- function getMinorUnits(currency) {
993
- try {
994
- const formatter = new Intl.NumberFormat("en", {
995
- style: "currency",
996
- currency: currency.toUpperCase()
997
- });
998
- const parts = formatter.formatToParts(1.11);
999
- const fractionPart = parts.find((p) => p.type === "fraction");
1000
- return fractionPart?.value.length ?? 2;
1001
- } catch {
1002
- return 2;
1257
+ function getCurrencyMinorUnits(currency) {
1258
+ const normalized = currency.trim().toUpperCase();
1259
+ if (!SUPPORTED_STORE_CURRENCY_SET.has(normalized)) {
1260
+ throw new RangeError(`Unsupported currency '${currency}'`);
1003
1261
  }
1262
+ return ZERO_MINOR_UNIT_STORE_CURRENCIES.has(normalized) ? 0 : 2;
1004
1263
  }
1005
1264
  function convertToMajor(minorAmount, currency) {
1006
- const units = getMinorUnits(currency);
1265
+ const units = getCurrencyMinorUnits(currency);
1007
1266
  return minorAmount / Math.pow(10, units);
1008
1267
  }
1009
1268
  function getCurrencySymbol(currency) {
@@ -1025,22 +1284,24 @@ function getCurrencyName(currency) {
1025
1284
  }
1026
1285
  }
1027
1286
  function formatMinor(amountMinor, currency) {
1028
- if (!currency) return "";
1287
+ if (!Number.isSafeInteger(amountMinor)) {
1288
+ throw new RangeError("Minor-unit amount must be a safe integer");
1289
+ }
1029
1290
  return formatCurrency(convertToMajor(amountMinor, currency), currency);
1030
1291
  }
1031
1292
  function formatPayment(payment) {
1032
1293
  return formatMinor(payment.total, payment.currency);
1033
1294
  }
1034
1295
  function formatPrice(prices, marketId) {
1035
- if (!prices || prices.length === 0) return "";
1036
- const price = marketId ? prices.find((p) => p.market === marketId) || prices[0] : prices[0];
1037
- if (!price) return "";
1296
+ if (!prices || prices.length === 0 || !marketId) return "";
1297
+ const price = prices.find((p) => p.market === marketId);
1298
+ if (!price || !Number.isSafeInteger(price.amount) || price.amount < 0 || !price.currency) return "";
1038
1299
  return formatMinor(price.amount, price.currency);
1039
1300
  }
1040
1301
  function getPriceAmount(prices, marketId) {
1041
- if (!prices || prices.length === 0) return 0;
1042
- const price = prices.find((p) => p.market === marketId) || prices[0];
1043
- return price?.amount || 0;
1302
+ if (!prices || prices.length === 0 || !marketId) return null;
1303
+ const price = prices.find((p) => p.market === marketId);
1304
+ return price && Number.isSafeInteger(price.amount) && price.amount >= 0 ? price.amount : null;
1044
1305
  }
1045
1306
 
1046
1307
  // src/utils/validation.ts
@@ -1141,6 +1402,7 @@ function formatDate(date, locale) {
1141
1402
  async function fetchSvgContent(mediaObject) {
1142
1403
  if (!mediaObject) return null;
1143
1404
  const svgUrl = getImageUrl(mediaObject, false);
1405
+ if (!svgUrl) return null;
1144
1406
  try {
1145
1407
  const response = await fetch(svgUrl);
1146
1408
  if (!response.ok) {
@@ -1233,9 +1495,10 @@ function getFirstAvailableFCId(variant, quantity = 1) {
1233
1495
  // src/index.ts
1234
1496
  function createUtilitySurface(apiConfig) {
1235
1497
  return {
1236
- getImageUrl: (imageBlock, isBlock = true) => getImageUrl(imageBlock, isBlock),
1498
+ getImageUrl: (imageBlock, isBlock2 = true) => getImageUrl(imageBlock, isBlock2),
1237
1499
  getBlockValue,
1238
1500
  getBlockTextValue,
1501
+ getBlockContentValue,
1239
1502
  getBlockValues,
1240
1503
  getBlockLabel,
1241
1504
  getBlockObjectValues,
@@ -1270,29 +1533,50 @@ function createUtilitySurface(apiConfig) {
1270
1533
  getFirstAvailableFCId
1271
1534
  };
1272
1535
  }
1273
- var CONTACT_STORAGE_KEY = "arky_contact_session";
1274
- function readContactSession() {
1536
+ function defaultStorefrontSessionStorage() {
1275
1537
  if (typeof window === "undefined") return null;
1276
1538
  try {
1277
- const raw = localStorage.getItem(CONTACT_STORAGE_KEY);
1278
- return raw ? JSON.parse(raw) : null;
1539
+ return window.localStorage;
1279
1540
  } catch {
1280
1541
  return null;
1281
1542
  }
1282
1543
  }
1283
- function writeContactSession(s) {
1284
- if (typeof window === "undefined") return;
1285
- if (s) {
1286
- localStorage.setItem(CONTACT_STORAGE_KEY, JSON.stringify(s));
1287
- } else {
1288
- localStorage.removeItem(CONTACT_STORAGE_KEY);
1289
- }
1544
+ function storefrontSessionStorageKey(baseUrl, storeId) {
1545
+ const normalizedBaseUrl = baseUrl.trim().replace(/\/+$/, "").toLowerCase();
1546
+ return `arky_contact_session:${encodeURIComponent(normalizedBaseUrl)}:${encodeURIComponent(storeId)}`;
1290
1547
  }
1291
- function createStorefront(config) {
1548
+ function createStorefrontClient(config) {
1292
1549
  const locale = config.locale || "en";
1293
1550
  const initialMarket = config.market || "";
1294
1551
  const listeners = /* @__PURE__ */ new Set();
1295
1552
  let bareIdentifyPromise = null;
1553
+ let identityTail = Promise.resolve();
1554
+ const sessionStorage = config.sessionStorage || defaultStorefrontSessionStorage();
1555
+ let memorySession = null;
1556
+ function readContactSession() {
1557
+ if (!sessionStorage) return memorySession;
1558
+ try {
1559
+ const raw = sessionStorage.getItem(
1560
+ storefrontSessionStorageKey(config.baseUrl, config.storeId)
1561
+ );
1562
+ return raw ? JSON.parse(raw) : null;
1563
+ } catch {
1564
+ return memorySession;
1565
+ }
1566
+ }
1567
+ function writeContactSession(session) {
1568
+ memorySession = session;
1569
+ if (!sessionStorage) return;
1570
+ const key = storefrontSessionStorageKey(config.baseUrl, config.storeId);
1571
+ try {
1572
+ if (session) {
1573
+ sessionStorage.setItem(key, JSON.stringify(session));
1574
+ } else {
1575
+ sessionStorage.removeItem(key);
1576
+ }
1577
+ } catch {
1578
+ }
1579
+ }
1296
1580
  function toPublic(s) {
1297
1581
  return s ? { contact: s.contact, store: s.store, market: s.market } : null;
1298
1582
  }
@@ -1348,19 +1632,23 @@ function createStorefront(config) {
1348
1632
  const contactApi = storefrontApi.crm.contact;
1349
1633
  function identify(params) {
1350
1634
  if (params?.market !== void 0) apiConfig.market = params.market;
1635
+ const market = apiConfig.market;
1351
1636
  const isBareCall = !params?.email && !params?.verify;
1352
1637
  if (isBareCall && bareIdentifyPromise) return bareIdentifyPromise;
1353
- const promise = (async () => {
1638
+ const run = async () => {
1354
1639
  try {
1355
- const result = await contactApi.identify({
1356
- market: apiConfig.market,
1357
- email: params?.email,
1358
- verify: params?.verify
1359
- });
1640
+ const result = await (params?.verify ? contactApi.requestCode({
1641
+ market,
1642
+ email: params.email
1643
+ }) : contactApi.identify({
1644
+ market,
1645
+ email: params?.email
1646
+ }));
1360
1647
  return {
1361
1648
  contact: result.contact,
1362
1649
  store: result.store,
1363
- market: result.market
1650
+ market: result.market,
1651
+ verification_challenge: result.verification_challenge
1364
1652
  };
1365
1653
  } catch (err) {
1366
1654
  const e = err;
@@ -1368,21 +1656,34 @@ function createStorefront(config) {
1368
1656
  if (isBareCall && status === 401) {
1369
1657
  updateSession(() => null);
1370
1658
  const result = await contactApi.identify({
1371
- market: apiConfig.market
1659
+ market
1372
1660
  });
1373
1661
  return {
1374
1662
  contact: result.contact,
1375
1663
  store: result.store,
1376
- market: result.market
1664
+ market: result.market,
1665
+ verification_challenge: result.verification_challenge
1377
1666
  };
1378
1667
  }
1379
1668
  throw err;
1380
1669
  }
1381
- })().catch((err) => {
1382
- if (isBareCall) bareIdentifyPromise = null;
1383
- throw err;
1384
- });
1385
- if (isBareCall) bareIdentifyPromise = promise;
1670
+ };
1671
+ const promise = identityTail.then(run);
1672
+ identityTail = promise.then(
1673
+ () => void 0,
1674
+ () => void 0
1675
+ );
1676
+ if (isBareCall) {
1677
+ bareIdentifyPromise = promise;
1678
+ void promise.then(
1679
+ () => {
1680
+ if (bareIdentifyPromise === promise) bareIdentifyPromise = null;
1681
+ },
1682
+ () => {
1683
+ if (bareIdentifyPromise === promise) bareIdentifyPromise = null;
1684
+ }
1685
+ );
1686
+ }
1386
1687
  return promise;
1387
1688
  }
1388
1689
  async function verify(params) {
@@ -1425,17 +1726,12 @@ function createStorefront(config) {
1425
1726
  };
1426
1727
  },
1427
1728
  store: storefrontApi.store,
1428
- cart: createCartController(storefrontApi.eshop.cart),
1429
1729
  cms: storefrontApi.cms,
1430
1730
  eshop: storefrontApi.eshop,
1431
1731
  crm: storefrontApi.crm,
1432
1732
  action: storefrontApi.action,
1433
1733
  experiments: storefrontApi.experiments,
1434
1734
  support: createStorefrontSupportApi(apiConfig),
1435
- setStoreId: (storeId) => {
1436
- apiConfig.storeId = storeId;
1437
- bareIdentifyPromise = null;
1438
- },
1439
1735
  getStoreId: () => apiConfig.storeId,
1440
1736
  setMarket: (key) => {
1441
1737
  apiConfig.market = key;
@@ -1449,14 +1745,40 @@ function createStorefront(config) {
1449
1745
  utils: createUtilitySurface(apiConfig)
1450
1746
  };
1451
1747
  }
1748
+ function createStorefront(config) {
1749
+ const sessionStorage = config.sessionStorage || defaultStorefrontSessionStorage() || void 0;
1750
+ const scopedConfig = {
1751
+ ...config,
1752
+ sessionStorage
1753
+ };
1754
+ const client = createStorefrontClient(scopedConfig);
1755
+ Object.defineProperty(client, "forStore", {
1756
+ enumerable: true,
1757
+ configurable: false,
1758
+ writable: false,
1759
+ value: (storeId) => createStorefront({
1760
+ ...scopedConfig,
1761
+ storeId,
1762
+ market: client.getMarket(),
1763
+ locale: client.getLocale()
1764
+ })
1765
+ });
1766
+ return client;
1767
+ }
1452
1768
 
1453
1769
  // src/payments/stripe.ts
1770
+ async function defaultStripeLoader(publishableKey, options) {
1771
+ const { loadStripe } = await import('@stripe/stripe-js');
1772
+ return loadStripe(publishableKey, options);
1773
+ }
1454
1774
  function normalizeCurrency(currency) {
1455
1775
  return currency.trim().toLowerCase();
1456
1776
  }
1457
- async function createStripeConfirmationTokenController(config) {
1458
- const { loadStripe } = await import('@stripe/stripe-js');
1459
- const stripe = await loadStripe(config.publishableKey);
1777
+ async function createStripeConfirmationTokenController(config, stripeLoader = defaultStripeLoader) {
1778
+ const stripe = await stripeLoader(
1779
+ config.publishableKey,
1780
+ config.connectedAccountId ? { stripeAccount: config.connectedAccountId } : void 0
1781
+ );
1460
1782
  if (!stripe) throw new Error("Stripe failed to initialize");
1461
1783
  let elements = createElements(stripe, config);
1462
1784
  let paymentElement = elements.create("payment", {
@@ -1545,11 +1867,11 @@ function firstLocalized(value, locale) {
1545
1867
  }
1546
1868
  return "";
1547
1869
  }
1548
- function findBlock(blocks, keys) {
1870
+ function findBlock2(blocks, keys) {
1549
1871
  return (blocks || []).find((block) => keys.includes(block.key)) || null;
1550
1872
  }
1551
1873
  function blockText(blocks, keys, locale) {
1552
- const block = findBlock(blocks, keys);
1874
+ const block = findBlock2(blocks, keys);
1553
1875
  if (!block) return "";
1554
1876
  return firstLocalized(block.value, locale);
1555
1877
  }
@@ -1565,15 +1887,32 @@ function providerName(provider, locale) {
1565
1887
  function entitySlug(entity, locale) {
1566
1888
  return entity.slug?.[locale] || entity.slug?.en || Object.values(entity.slug || {})[0] || entity.id;
1567
1889
  }
1568
- function priceForMarket(prices, market, fallbackCurrency) {
1569
- const price = prices.find((candidate) => candidate.market === market) || prices[0];
1570
- return {
1571
- amount: price?.amount || 0,
1572
- market: price?.market || market,
1573
- currency: price?.currency || fallbackCurrency || "",
1574
- compare_at: price?.compare_at,
1575
- contact_list_id: price?.contact_list_id
1576
- };
1890
+ function priceForMarket(prices, market, marketCurrency) {
1891
+ const marketKey = market.trim();
1892
+ if (!marketKey) throw new Error("A market is required to select a product price");
1893
+ const currency = marketCurrency?.trim().toUpperCase();
1894
+ if (!currency) throw new Error(`Market ${marketKey} does not have an authoritative currency`);
1895
+ const marketPrices = prices.filter((candidate) => candidate.market === marketKey);
1896
+ if (marketPrices.length === 0) {
1897
+ throw new Error(`Product is not priced for market ${marketKey}`);
1898
+ }
1899
+ if (marketPrices.some(
1900
+ (candidate) => !Number.isSafeInteger(candidate.amount) || candidate.amount < 0 || candidate.currency.trim().toUpperCase() !== currency
1901
+ )) {
1902
+ throw new Error(`Product has an invalid price for market ${marketKey}`);
1903
+ }
1904
+ const authorizedPrices = marketPrices.filter((candidate) => candidate.contact_list_id);
1905
+ if (authorizedPrices.length > 0) {
1906
+ return authorizedPrices.reduce(
1907
+ (lowest, candidate) => candidate.amount < lowest.amount ? candidate : lowest
1908
+ );
1909
+ }
1910
+ const basePrices = marketPrices.filter((candidate) => !candidate.contact_list_id);
1911
+ if (basePrices.length !== 1) {
1912
+ throw new Error(`Product does not have one base price for market ${marketKey}`);
1913
+ }
1914
+ const price = basePrices[0];
1915
+ return price;
1577
1916
  }
1578
1917
  function availableStock(client, variant) {
1579
1918
  const fromUtility = client.utils.getAvailableStock(variant);
@@ -1592,8 +1931,9 @@ function locationToAddress(location) {
1592
1931
  street2: null
1593
1932
  };
1594
1933
  }
1595
- function normalizeForms(forms) {
1596
- return forms;
1934
+ function createFormEntry(formId, fields) {
1935
+ if (!formId.trim()) throw new Error("formId is required");
1936
+ return { form_id: formId, fields };
1597
1937
  }
1598
1938
  function toProductCheckoutItems(items) {
1599
1939
  return items.map((item) => ({
@@ -1605,36 +1945,97 @@ function toProductCheckoutItems(items) {
1605
1945
  }));
1606
1946
  }
1607
1947
  function toServiceCheckoutItems(items) {
1608
- const groups = /* @__PURE__ */ new Map();
1609
- for (const item of items) {
1610
- const key = `${item.service_id}:${item.provider_id}`;
1611
- const slot = { from: item.from, to: item.to };
1612
- const existing = groups.get(key);
1613
- if (existing) {
1614
- existing.slots.push(slot);
1948
+ return items.map((item) => ({
1949
+ type: "service",
1950
+ id: item.id,
1951
+ service_id: item.service_id,
1952
+ provider_id: item.provider_id,
1953
+ slots: [...item.slots].sort((a, b) => a.from - b.from),
1954
+ forms: item.forms
1955
+ }));
1956
+ }
1957
+ function formValueError(field, message) {
1958
+ return new Error(`Invalid value for form field '${field.key}': ${message}`);
1959
+ }
1960
+ function isValidGeoLocation(value) {
1961
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
1962
+ const location = value;
1963
+ if (location.label !== void 0 && location.label !== null && typeof location.label !== "string") {
1964
+ return false;
1965
+ }
1966
+ const coordinates = location.coordinates;
1967
+ if (!coordinates || typeof coordinates !== "object") return false;
1968
+ return Number.isFinite(coordinates.lat) && Number.isFinite(coordinates.lon) && coordinates.lat >= -90 && coordinates.lat <= 90 && coordinates.lon >= -180 && coordinates.lon <= 180;
1969
+ }
1970
+ function buildFormField(field, value) {
1971
+ const common = { id: field.id, key: field.key };
1972
+ switch (field.type) {
1973
+ case "text":
1974
+ if (typeof value !== "string") throw formValueError(field, "expected text");
1975
+ if (field.required && value.trim().length === 0) throw formValueError(field, "required text is blank");
1976
+ return { ...common, type: "text", value };
1977
+ case "number":
1978
+ if (typeof value !== "number" || !Number.isFinite(value)) {
1979
+ throw formValueError(field, "expected a finite number");
1980
+ }
1981
+ if (field.min !== null && field.min !== void 0 && value < field.min) {
1982
+ throw formValueError(field, `must be at least ${field.min}`);
1983
+ }
1984
+ if (field.max !== null && field.max !== void 0 && value > field.max) {
1985
+ throw formValueError(field, `must be at most ${field.max}`);
1986
+ }
1987
+ return { ...common, type: "number", value };
1988
+ case "boolean":
1989
+ if (typeof value !== "boolean") throw formValueError(field, "expected a boolean");
1990
+ return { ...common, type: "boolean", value };
1991
+ case "date":
1992
+ if (typeof value !== "number" || !Number.isSafeInteger(value)) {
1993
+ throw formValueError(field, "expected an integer timestamp");
1994
+ }
1995
+ return { ...common, type: "date", value };
1996
+ case "geo_location":
1997
+ if (!isValidGeoLocation(value)) throw formValueError(field, "expected valid coordinates");
1998
+ return { ...common, type: "geo_location", value };
1999
+ case "select": {
2000
+ if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
2001
+ throw formValueError(field, "expected a list of options");
2002
+ }
2003
+ const selected = value;
2004
+ if (field.required && selected.length === 0) throw formValueError(field, "at least one option is required");
2005
+ if (new Set(selected).size !== selected.length || selected.some((item) => !field.options.includes(item))) {
2006
+ throw formValueError(field, "contains an unknown or duplicate option");
2007
+ }
2008
+ return { ...common, type: "select", value: selected };
2009
+ }
2010
+ }
2011
+ }
2012
+ function isEmptyOptionalValue(field, value) {
2013
+ if (field.required) return false;
2014
+ if (field.type === "text") return value === "";
2015
+ if (field.type === "select") return Array.isArray(value) && value.length === 0;
2016
+ if (field.type === "geo_location") {
2017
+ return Boolean(value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).length === 0);
2018
+ }
2019
+ return false;
2020
+ }
2021
+ function buildFormFields(schema, values) {
2022
+ const knownKeys = new Set(schema.map((field) => field.key));
2023
+ const unknownKey = Object.keys(values).find((key) => !knownKeys.has(key));
2024
+ if (unknownKey) throw new Error(`Form field '${unknownKey}' is not defined by the form schema`);
2025
+ const fields = [];
2026
+ for (const field of schema) {
2027
+ const value = values[field.key];
2028
+ if (value === void 0) {
2029
+ if (field.required) throw formValueError(field, "required value is missing");
1615
2030
  continue;
1616
2031
  }
1617
- groups.set(key, {
1618
- type: "service",
1619
- id: item.id,
1620
- service_id: item.service_id,
1621
- provider_id: item.provider_id,
1622
- slots: [slot],
1623
- forms: item.forms || []
1624
- });
2032
+ if (isEmptyOptionalValue(field, value)) continue;
2033
+ fields.push(buildFormField(field, value));
1625
2034
  }
1626
- return [...groups.values()].map((item) => ({
1627
- ...item,
1628
- slots: [...item.slots].sort((a, b) => a.from - b.from)
1629
- }));
2035
+ return fields;
1630
2036
  }
1631
- function formFieldsFromBlocks(blocks) {
1632
- return blocks.map((block) => ({
1633
- id: block.id,
1634
- key: block.key,
1635
- type: block.type,
1636
- value: block.value
1637
- }));
2037
+ function createFormEntryFromValues(form, values) {
2038
+ return createFormEntry(form.id, buildFormFields(form.schema, values));
1638
2039
  }
1639
2040
  function getFormBlockType(field) {
1640
2041
  if (field.key === "email") return "email";
@@ -1644,11 +2045,15 @@ function getFormBlockType(field) {
1644
2045
  }
1645
2046
  function getFormBlockValue(field) {
1646
2047
  if (field.type === "boolean") return false;
1647
- if (field.type === "number") return field.min ?? 0;
2048
+ if (field.type === "select") return [];
1648
2049
  if (field.type === "geo_location") return {};
2050
+ if (field.type === "number" || field.type === "date") return void 0;
1649
2051
  return "";
1650
2052
  }
1651
2053
  function formSchemaToBlock(field) {
2054
+ const min = field.type === "number" ? field.min : void 0;
2055
+ const max = field.type === "number" ? field.max : void 0;
2056
+ const options = field.type === "select" ? field.options : void 0;
1652
2057
  return {
1653
2058
  id: field.id,
1654
2059
  key: field.key,
@@ -1656,9 +2061,9 @@ function formSchemaToBlock(field) {
1656
2061
  properties: {
1657
2062
  isRequired: field.required,
1658
2063
  minValues: field.required ? 1 : 0,
1659
- min: field.min,
1660
- max: field.max,
1661
- options: field.options,
2064
+ min,
2065
+ max,
2066
+ options,
1662
2067
  pattern: field.key === "email" ? "^.+@.+\\..+$" : field.key === "phone" ? "^.{6,20}$" : void 0
1663
2068
  },
1664
2069
  value: getFormBlockValue(field)
@@ -1701,15 +2106,13 @@ function createServiceInitialState() {
1701
2106
  service: null,
1702
2107
  availability: null,
1703
2108
  providers: [],
2109
+ serviceProviders: [],
1704
2110
  selectedProviderId: null,
1705
2111
  currentMonth: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), 1),
1706
2112
  calendar: [],
1707
2113
  selectedDate: null,
1708
- startDate: null,
1709
- endDate: null,
1710
2114
  slots: [],
1711
2115
  selectedSlot: null,
1712
- cart: [],
1713
2116
  timezone: typeof window !== "undefined" ? Intl.DateTimeFormat().resolvedOptions().timeZone : "UTC",
1714
2117
  tzGroups: {},
1715
2118
  loading: false,
@@ -1719,7 +2122,6 @@ function createServiceInitialState() {
1719
2122
  quoteError: null,
1720
2123
  currency: null,
1721
2124
  dateTimeConfirmed: false,
1722
- isMultiDay: false,
1723
2125
  availablePaymentMethods: [],
1724
2126
  cartId: null,
1725
2127
  promoCode: null
@@ -1740,8 +2142,9 @@ function normalizeTimezoneGroups(groups) {
1740
2142
  function firstFiniteNumber(...values) {
1741
2143
  return values.find((value) => typeof value === "number" && Number.isFinite(value));
1742
2144
  }
1743
- function initialize(config) {
2145
+ function initializeStore(config) {
1744
2146
  const client = createStorefront(config);
2147
+ const formClients = /* @__PURE__ */ new Map([[client.getStoreId(), client]]);
1745
2148
  const session = atom(client.session);
1746
2149
  const locale = atom(config.locale || client.getLocale());
1747
2150
  const market_key = atom(config.market || client.getMarket());
@@ -1752,7 +2155,10 @@ function initialize(config) {
1752
2155
  const store = value?.store;
1753
2156
  const methods = value?.market?.payment_methods || [];
1754
2157
  const hasCreditCard = methods.some((method) => method.type === "credit_card");
1755
- return { provider: store?.payment || null, enabled: hasCreditCard && !!store?.payment };
2158
+ return {
2159
+ provider: store?.payment || null,
2160
+ enabled: hasCreditCard && !!store?.payment
2161
+ };
1756
2162
  });
1757
2163
  const cart = atom(null);
1758
2164
  const product_items = atom([]);
@@ -1793,7 +2199,10 @@ function initialize(config) {
1793
2199
  );
1794
2200
  const service_item_count = computed(
1795
2201
  [cart, service_items],
1796
- (cartValue, items) => Math.max(rawServiceItemCount(cartValue), items.length)
2202
+ (cartValue, items) => Math.max(
2203
+ rawServiceItemCount(cartValue),
2204
+ items.reduce((total, item) => total + Math.max(1, item.slots.length), 0)
2205
+ )
1797
2206
  );
1798
2207
  const item_count = computed(
1799
2208
  [cart, product_item_count, service_item_count],
@@ -1833,8 +2242,15 @@ function initialize(config) {
1833
2242
  error: null
1834
2243
  });
1835
2244
  const service_state = map(createServiceInitialState());
1836
- const service_form_node = atom(null);
1837
- const service_form_blocks = computed(service_form_node, (node) => node?.blocks || []);
2245
+ const service_form_definitions = /* @__PURE__ */ new Map();
2246
+ const service_form_state = map({
2247
+ provider_id: null,
2248
+ groups: [],
2249
+ loading: false,
2250
+ error: null
2251
+ });
2252
+ const service_form_groups = computed(service_form_state, (state) => state.groups);
2253
+ const service_form_blocks = computed(service_form_groups, (groups) => groups.flatMap((group) => group.blocks));
1838
2254
  client.onAuthStateChanged((value) => session.set(value));
1839
2255
  currency.subscribe((value) => service_state.setKey("currency", value));
1840
2256
  session.subscribe((value) => {
@@ -1849,25 +2265,30 @@ function initialize(config) {
1849
2265
  function currentLocale() {
1850
2266
  return locale.get() || client.getLocale() || "en";
1851
2267
  }
1852
- function currentCurrency() {
1853
- return currency.get() || market.get()?.currency || null;
2268
+ function clientForStore(storeId) {
2269
+ const existing = formClients.get(storeId);
2270
+ if (existing) return existing;
2271
+ const scoped = client.forStore(storeId);
2272
+ formClients.set(storeId, scoped);
2273
+ return scoped;
1854
2274
  }
1855
2275
  function currentStripePublishableKey() {
1856
2276
  const provider = payment_config.get()?.provider;
1857
- return provider?.publishable_key || provider?.publishableKey || provider?.publicKey || null;
2277
+ return provider?.publishable_key || null;
2278
+ }
2279
+ function currentStripeConnectedAccountId() {
2280
+ const provider = payment_config.get()?.provider;
2281
+ return provider?.connected_account_id || null;
1858
2282
  }
1859
2283
  function currentPaymentAmount() {
1860
2284
  return Math.max(
1861
2285
  0,
1862
- firstFiniteNumber(
1863
- quote.get()?.charge_amount,
1864
- quote.get()?.payment?.total,
1865
- cart.get()?.quote_snapshot?.charge_amount,
1866
- cart.get()?.quote_snapshot?.payment?.total,
1867
- cart.get()?.quote_snapshot?.total
1868
- ) ?? 0
2286
+ firstFiniteNumber(quote.get()?.charge_amount, cart.get()?.quote_snapshot?.charge_amount, cart.get()?.quote_snapshot?.total) ?? 0
1869
2287
  );
1870
2288
  }
2289
+ function currentPaymentCurrency() {
2290
+ return quote.get()?.money?.currency?.trim() || cart.get()?.quote_snapshot?.money?.currency?.trim() || null;
2291
+ }
1871
2292
  function setPaymentController(controller) {
1872
2293
  const current = payment_controller.get();
1873
2294
  if (current && current !== controller) {
@@ -1884,10 +2305,24 @@ function initialize(config) {
1884
2305
  if (!publishableKey) {
1885
2306
  throw new Error("Stripe publishable key is required to mount card payment");
1886
2307
  }
2308
+ const hasExplicitAmount = options.amount !== void 0;
2309
+ const hasExplicitCurrency = Boolean(options.currency?.trim());
2310
+ if (hasExplicitAmount !== hasExplicitCurrency) {
2311
+ throw new Error("Stripe amount and currency must be supplied together");
2312
+ }
2313
+ const amount = hasExplicitAmount ? options.amount : currentPaymentAmount();
2314
+ if (!Number.isSafeInteger(amount) || amount <= 0) {
2315
+ throw new Error("A positive minor-unit payment amount is required to mount card payment");
2316
+ }
2317
+ const paymentCurrency = hasExplicitCurrency ? options.currency.trim() : currentPaymentCurrency();
2318
+ if (!paymentCurrency || !/^[a-z]{3}$/i.test(paymentCurrency)) {
2319
+ throw new Error("An explicit three-letter payment currency is required to mount card payment");
2320
+ }
1887
2321
  const controller = await createStripeConfirmationTokenController({
1888
2322
  publishableKey,
1889
- amount: Math.max(0, options.amount ?? currentPaymentAmount()),
1890
- currency: options.currency || currentCurrency() || "usd",
2323
+ connectedAccountId: options.connectedAccountId || currentStripeConnectedAccountId() || void 0,
2324
+ amount,
2325
+ currency: paymentCurrency,
1891
2326
  ...options.appearance ? { appearance: options.appearance } : {}
1892
2327
  });
1893
2328
  controller.mount(target);
@@ -1913,10 +2348,24 @@ function initialize(config) {
1913
2348
  }
1914
2349
  async function identify(params = {}) {
1915
2350
  if (params.market) setMarket(params.market);
1916
- const result = await client.identify({ ...params, market: params.market || currentMarketKey() });
1917
- session.set(result);
2351
+ const result = await client.identify({
2352
+ ...params,
2353
+ market: params.market || currentMarketKey()
2354
+ });
2355
+ session.set({
2356
+ contact: result.contact,
2357
+ store: result.store,
2358
+ market: result.market
2359
+ });
1918
2360
  return result;
1919
2361
  }
2362
+ async function identifyContactEmailIfMissing(email) {
2363
+ const normalizedEmail = email.trim().toLowerCase();
2364
+ if (!normalizedEmail) throw new Error("Contact email is required");
2365
+ const current = session.get();
2366
+ if (current?.contact.email === normalizedEmail) return current;
2367
+ return identify({ email: normalizedEmail });
2368
+ }
1920
2369
  function setMarket(key) {
1921
2370
  market_key.set(key);
1922
2371
  client.setMarket(key);
@@ -1941,7 +2390,9 @@ function initialize(config) {
1941
2390
  const refreshRevision = cartWriteRevision;
1942
2391
  cartRequest = (async () => {
1943
2392
  await ensureSession();
1944
- const response = await client.cart.refresh({ market: currentMarketKey() });
2393
+ const response = await client.eshop.cart.current({
2394
+ market: currentMarketKey()
2395
+ });
1945
2396
  await applyCartResponse(response, { ifRevision: refreshRevision });
1946
2397
  return response;
1947
2398
  })();
@@ -1955,9 +2406,9 @@ function initialize(config) {
1955
2406
  cart_status.setKey("loading", false);
1956
2407
  }
1957
2408
  }
1958
- async function buildProductCartItem(item, source) {
2409
+ async function buildProductCartItem(item, source, productHint) {
1959
2410
  try {
1960
- const product = await client.eshop.product.get({ id: item.product_id });
2411
+ const product = productHint?.id === item.product_id ? productHint : await client.eshop.product.get({ id: item.product_id });
1961
2412
  const variant = product.variants.find((candidate) => candidate.id === item.variant_id);
1962
2413
  if (!variant) return null;
1963
2414
  return {
@@ -1968,7 +2419,7 @@ function initialize(config) {
1968
2419
  product_slug: entitySlug(product, currentLocale()),
1969
2420
  variant_attributes: variant.attributes,
1970
2421
  requires_shipping: variant.requires_shipping !== false,
1971
- price: priceForMarket(variant.prices, currentMarketKey(), currentCurrency()),
2422
+ price: priceForMarket(variant.prices, currentMarketKey(), market.get()?.currency),
1972
2423
  quantity: item.quantity,
1973
2424
  added_at: source.created_at ? source.created_at * 1e3 : Date.now(),
1974
2425
  max_stock: availableStock(client, variant)
@@ -1990,18 +2441,15 @@ function initialize(config) {
1990
2441
  provider = await client.eshop.provider.get({ id: item.provider_id });
1991
2442
  } catch {
1992
2443
  }
1993
- for (const [index, slot] of item.slots.entries()) {
1994
- rows.push({
1995
- id: item.id || createId(`service_${index}`),
1996
- service_id: item.service_id,
1997
- provider_id: item.provider_id,
1998
- from: slot.from,
1999
- to: slot.to,
2000
- forms: item.forms || [],
2001
- service_name: service ? serviceName(service, currentLocale()) : item.service_id,
2002
- provider_name: provider ? providerName(provider, currentLocale()) : item.provider_id
2003
- });
2004
- }
2444
+ rows.push({
2445
+ id: item.id || createId("service"),
2446
+ service_id: item.service_id,
2447
+ provider_id: item.provider_id,
2448
+ slots: item.slots,
2449
+ forms: item.forms || [],
2450
+ service_name: service ? serviceName(service, currentLocale()) : item.service_id,
2451
+ provider_name: provider ? providerName(provider, currentLocale()) : item.provider_id
2452
+ });
2005
2453
  }
2006
2454
  return rows;
2007
2455
  }
@@ -2016,11 +2464,9 @@ function initialize(config) {
2016
2464
  quote.set(response.quote_snapshot || null);
2017
2465
  const items = response.items || [];
2018
2466
  const products = await Promise.all(
2019
- items.filter((item) => item.type === "product").map((item) => buildProductCartItem(item, response))
2020
- );
2021
- const services = await buildServiceCartItems(
2022
- items.filter((item) => item.type === "service")
2467
+ items.filter((item) => item.type === "product").map((item) => buildProductCartItem(item, response, options.productHint))
2023
2468
  );
2469
+ const services = await buildServiceCartItems(items.filter((item) => item.type === "service"));
2024
2470
  product_items.set(products.filter((item) => item !== null));
2025
2471
  service_items.set(services);
2026
2472
  return response;
@@ -2036,21 +2482,17 @@ function initialize(config) {
2036
2482
  cart_status.setKey("error", null);
2037
2483
  try {
2038
2484
  const current = cart.get() || await ensureCart();
2039
- const response = await client.cart.update({
2485
+ const response = await client.eshop.cart.update({
2040
2486
  id: current.id,
2041
2487
  market: currentMarketKey(),
2042
2488
  items: checkoutItems(input),
2043
- shipping_address: input.shipping_address || void 0,
2044
- billing_address: input.billing_address || void 0,
2045
- forms: normalizeForms(input.forms),
2046
- promo_code: input.promo_code === void 0 ? promo_code.get() || void 0 : input.promo_code || void 0,
2047
- payment_method_key: input.payment_method_key || void 0,
2048
- shipping_method_id: input.shipping_method_id || cart_status.get().selected_shipping_method_id || void 0
2489
+ shipping_address: input.shipping_address,
2490
+ billing_address: input.billing_address,
2491
+ forms: input.forms,
2492
+ promo_code: input.promo_code === null ? "" : input.promo_code === void 0 ? promo_code.get() || void 0 : input.promo_code,
2493
+ payment_method_key: input.payment_method_key === null ? "" : input.payment_method_key,
2494
+ 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
2049
2495
  });
2050
- if (input.promo_code !== void 0) promo_code.set(input.promo_code);
2051
- if (input.shipping_method_id !== void 0) {
2052
- cart_status.setKey("selected_shipping_method_id", input.shipping_method_id);
2053
- }
2054
2496
  await applyCartResponse(response, { ifRevision: writeRevision });
2055
2497
  return response;
2056
2498
  } catch (error) {
@@ -2065,7 +2507,7 @@ function initialize(config) {
2065
2507
  const writeRevision = nextCartWriteRevision();
2066
2508
  try {
2067
2509
  const current = cart.get() || await ensureCart();
2068
- const response = await client.cart.addItem({
2510
+ const response = await client.eshop.cart.addItem({
2069
2511
  id: current.id,
2070
2512
  item: {
2071
2513
  type: "product",
@@ -2074,7 +2516,7 @@ function initialize(config) {
2074
2516
  quantity
2075
2517
  }
2076
2518
  });
2077
- await applyCartResponse(response, { ifRevision: writeRevision });
2519
+ await applyCartResponse(response, { ifRevision: writeRevision, productHint: product });
2078
2520
  return response;
2079
2521
  } catch (error) {
2080
2522
  cart_status.setKey("error", readErrorMessage(error, "Failed to add product to cart."));
@@ -2097,11 +2539,9 @@ function initialize(config) {
2097
2539
  product_items.set(product_items.get().filter((candidate) => candidate.id !== itemId));
2098
2540
  const current = cart.get();
2099
2541
  if (!current || !item) return null;
2100
- const response = await client.cart.removeItem({
2542
+ const response = await client.eshop.cart.removeItem({
2101
2543
  id: current.id,
2102
- item_id: item.id,
2103
- product_id: item.product_id,
2104
- variant_id: item.variant_id
2544
+ item_id: item.id
2105
2545
  });
2106
2546
  await applyCartResponse(response, { ifRevision: writeRevision });
2107
2547
  return response;
@@ -2123,7 +2563,7 @@ function initialize(config) {
2123
2563
  const current = cart.get();
2124
2564
  clearLocalCart();
2125
2565
  if (!current) return null;
2126
- const response = await client.cart.clear({ id: current.id });
2566
+ const response = await client.eshop.cart.clear({ id: current.id });
2127
2567
  await applyCartResponse(response, { ifRevision: writeRevision });
2128
2568
  return response;
2129
2569
  }
@@ -2144,7 +2584,7 @@ function initialize(config) {
2144
2584
  cart_status.setKey("quote_error", null);
2145
2585
  try {
2146
2586
  const current = await syncCart(input);
2147
- const response = await client.cart.quote({ id: current.id });
2587
+ const response = await client.eshop.cart.quote({ id: current.id });
2148
2588
  quote.set(response);
2149
2589
  return response;
2150
2590
  } catch (error) {
@@ -2162,23 +2602,17 @@ function initialize(config) {
2162
2602
  try {
2163
2603
  const current = await syncCart(input);
2164
2604
  const quoteValue = quote.get();
2165
- const paymentMethodKey = input.payment_method_key || current.payment_method_key || quoteValue?.payment?.payment_method_key || void 0;
2166
- let chargeAmount = firstFiniteNumber(
2167
- quoteValue?.charge_amount,
2168
- quoteValue?.payment?.total,
2169
- current.quote_snapshot?.charge_amount,
2170
- current.quote_snapshot?.payment?.total
2171
- );
2605
+ const paymentMethodKey = input.payment_method_key || current.payment_method_key || quoteValue?.money?.payment_method_key || void 0;
2606
+ let chargeAmount = firstFiniteNumber(quoteValue?.charge_amount, current.quote_snapshot?.charge_amount);
2172
2607
  if (paymentMethodKey === "credit_card" && chargeAmount === void 0) {
2173
- const latestQuote = await client.cart.quote({ id: current.id });
2608
+ const latestQuote = await client.eshop.cart.quote({ id: current.id });
2174
2609
  quote.set(latestQuote);
2175
- chargeAmount = firstFiniteNumber(
2176
- latestQuote.charge_amount,
2177
- latestQuote.payment?.total,
2178
- latestQuote.total
2179
- );
2610
+ chargeAmount = firstFiniteNumber(latestQuote.charge_amount, latestQuote.total);
2611
+ }
2612
+ if (paymentMethodKey === "credit_card" && (typeof chargeAmount !== "number" || !Number.isSafeInteger(chargeAmount) || chargeAmount < 0)) {
2613
+ throw new Error("Card checkout requires a non-negative integer charge amount in minor units");
2180
2614
  }
2181
- const needsConfirmationToken = paymentMethodKey === "credit_card" && (chargeAmount === void 0 || chargeAmount > 0);
2615
+ const needsConfirmationToken = paymentMethodKey === "credit_card" && typeof chargeAmount === "number" && chargeAmount > 0;
2182
2616
  let confirmationTokenId;
2183
2617
  let returnUrl = input.return_url;
2184
2618
  const paymentController = input.payment ?? payment_controller.get();
@@ -2192,7 +2626,7 @@ function initialize(config) {
2192
2626
  confirmationTokenId = token.confirmation_token_id;
2193
2627
  returnUrl = token.return_url || returnUrl;
2194
2628
  }
2195
- const response = await client.cart.checkout({
2629
+ const response = await client.eshop.cart.checkout({
2196
2630
  id: current.id,
2197
2631
  payment_method_key: paymentMethodKey,
2198
2632
  confirmation_token_id: confirmationTokenId,
@@ -2211,8 +2645,8 @@ function initialize(config) {
2211
2645
  service_items: input.service_items || service_items.get(),
2212
2646
  shipping_address: input.shipping_address || null,
2213
2647
  billing_address: input.billing_address || null,
2214
- total: quoteValue?.payment?.total || quoteValue?.total || response.payment?.total,
2215
- currency: quoteValue?.payment?.currency || currentCurrency(),
2648
+ total: response.payment.amount,
2649
+ currency: response.payment.currency,
2216
2650
  payment_method_key: paymentMethodKey || null,
2217
2651
  created_at: Date.now()
2218
2652
  };
@@ -2230,7 +2664,7 @@ function initialize(config) {
2230
2664
  }
2231
2665
  function serviceCalendar() {
2232
2666
  const state = service_state.get();
2233
- const { currentMonth, selectedDate, startDate, endDate, availability, selectedProviderId } = state;
2667
+ const { currentMonth, selectedDate, availability, selectedProviderId } = state;
2234
2668
  const year = currentMonth.getFullYear();
2235
2669
  const monthIndex = currentMonth.getMonth();
2236
2670
  const first = new Date(year, monthIndex, 1);
@@ -2253,18 +2687,12 @@ function initialize(config) {
2253
2687
  for (let day = 1; day <= last.getDate(); day++) {
2254
2688
  const date = new Date(year, monthIndex, day);
2255
2689
  const iso = `${year}-${String(monthIndex + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
2256
- const isSelected = iso === selectedDate || iso === startDate || iso === endDate;
2257
- let isInRange = false;
2258
- if (startDate && endDate) {
2259
- const time = date.getTime();
2260
- isInRange = time > new Date(startDate).getTime() && time < new Date(endDate).getTime();
2261
- }
2262
2690
  cells.push({
2263
2691
  date,
2264
2692
  iso,
2265
2693
  available: hasAvailableSlotsForDate(availability, iso, selectedProviderId),
2266
- isSelected,
2267
- isInRange,
2694
+ isSelected: iso === selectedDate,
2695
+ isInRange: false,
2268
2696
  isToday: date.getTime() === today.getTime(),
2269
2697
  blank: false
2270
2698
  });
@@ -2301,46 +2729,38 @@ function initialize(config) {
2301
2729
  })
2302
2730
  }));
2303
2731
  }
2304
- function toServiceCartItem(slot) {
2305
- return {
2306
- id: slot.id,
2307
- service_id: slot.serviceId,
2308
- provider_id: slot.providerId,
2309
- from: slot.from,
2310
- to: slot.to,
2311
- forms: [],
2312
- service_name: slot.serviceName,
2313
- date_text: slot.dateText,
2314
- time_text: slot.timeText,
2315
- is_multi_day: slot.isMultiDay
2316
- };
2317
- }
2318
- function fromServiceCartItem(item) {
2732
+ function toServiceCartItem(slots, forms = []) {
2733
+ const orderedSlots = [...slots].sort((left, right) => left.from - right.from || left.to - right.to);
2734
+ const [first] = orderedSlots;
2735
+ if (!first) throw new Error("At least one service slot is required");
2736
+ if (orderedSlots.some((slot) => slot.serviceId !== first.serviceId || slot.providerId !== first.providerId)) {
2737
+ throw new Error("A booking can only contain slots for one service and provider");
2738
+ }
2739
+ if (orderedSlots.some((slot) => !Number.isSafeInteger(slot.from) || !Number.isSafeInteger(slot.to) || slot.from >= slot.to)) {
2740
+ throw new Error("A booking contains an invalid service slot");
2741
+ }
2742
+ for (let index = 1; index < orderedSlots.length; index += 1) {
2743
+ if (orderedSlots[index - 1].to !== orderedSlots[index].from) {
2744
+ throw new Error("A multi-slot booking must contain adjacent slots");
2745
+ }
2746
+ }
2319
2747
  return {
2320
- id: item.id,
2321
- serviceId: item.service_id,
2322
- providerId: item.provider_id,
2323
- from: item.from,
2324
- to: item.to,
2325
- serviceName: item.service_name || "",
2326
- date: item.date_text || "",
2327
- dateText: item.date_text || "",
2328
- timeText: item.time_text || formatServiceSlotTime(item.from, item.to, service_state.get().timezone),
2329
- isMultiDay: item.is_multi_day
2748
+ id: createId("booking"),
2749
+ service_id: first.serviceId,
2750
+ provider_id: first.providerId,
2751
+ slots: orderedSlots.map(({ from, to }) => ({ from, to })),
2752
+ forms,
2753
+ service_name: first.serviceName,
2754
+ date_text: first.dateText,
2755
+ time_text: first.timeText,
2756
+ is_multi_day: orderedSlots.length > 1
2330
2757
  };
2331
2758
  }
2332
- function setServiceCartFromServiceItems(items) {
2333
- const next = items.map(fromServiceCartItem);
2334
- const current = service_state.get().cart;
2335
- if (JSON.stringify(current) !== JSON.stringify(next)) {
2336
- service_state.setKey("cart", next);
2337
- }
2338
- }
2339
- async function syncServiceCart(slots) {
2759
+ async function syncServiceCart(items) {
2340
2760
  try {
2341
2761
  return await syncCart({
2342
2762
  product_items: product_items.get(),
2343
- service_items: slots.map(toServiceCartItem)
2763
+ service_items: items
2344
2764
  });
2345
2765
  } catch (error) {
2346
2766
  service_state.setKey("quoteError", readErrorMessage(error, "Failed to sync service cart."));
@@ -2357,18 +2777,22 @@ function initialize(config) {
2357
2777
  const service_can_proceed = computed(service_state, (state) => {
2358
2778
  const step = serviceCurrentStepName();
2359
2779
  if (step === "datetime") {
2360
- return state.isMultiDay ? !!(state.startDate && state.endDate && state.selectedSlot) : !!(state.selectedDate && state.selectedSlot);
2780
+ return !!(state.selectedDate && state.selectedSlot);
2361
2781
  }
2362
2782
  if (step === "review") return true;
2363
2783
  return false;
2364
2784
  });
2365
2785
  const service_month_year = computed(
2366
2786
  service_state,
2367
- (state) => state.currentMonth.toLocaleString(void 0, { month: "long", year: "numeric" })
2787
+ (state) => state.currentMonth.toLocaleString(void 0, {
2788
+ month: "long",
2789
+ year: "numeric"
2790
+ })
2368
2791
  );
2369
- const service_chain_start = computed(service_state, (state) => {
2370
- if (!state.cart.length) return null;
2371
- return Math.max(...state.cart.map((slot) => slot.to));
2792
+ const service_chain_start = computed(service_items, (items) => {
2793
+ const slots = items.flatMap((item) => item.slots);
2794
+ if (!slots.length) return null;
2795
+ return Math.max(...slots.map((slot) => slot.to));
2372
2796
  });
2373
2797
  const service_total_steps = computed(service_state, (state) => state.service ? 2 : 0);
2374
2798
  const service_steps = computed(service_state, () => ({
@@ -2383,40 +2807,101 @@ function initialize(config) {
2383
2807
  });
2384
2808
  function formatServiceDateDisplay(value) {
2385
2809
  if (!value) return "";
2386
- return new Date(value).toLocaleDateString(void 0, { month: "short", day: "numeric" });
2810
+ return new Date(value).toLocaleDateString(void 0, {
2811
+ month: "short",
2812
+ day: "numeric"
2813
+ });
2387
2814
  }
2388
- function serviceProviderId(provider) {
2389
- return "provider_id" in provider ? provider.provider_id : provider.id;
2815
+ function configuredServiceFormIds(relationship) {
2816
+ const formIds = relationship.forms.map((entry) => entry.form_id.trim());
2817
+ if (formIds.some((formId) => !formId) || new Set(formIds).size !== formIds.length) {
2818
+ throw new Error(`Service provider ${relationship.provider_id} has blank or duplicate configured form IDs`);
2819
+ }
2820
+ return formIds;
2821
+ }
2822
+ function resolveServiceProvider(state, providerId) {
2823
+ const serviceId = state.service?.id;
2824
+ if (!serviceId) return null;
2825
+ const relationships = state.serviceProviders.filter((relationship) => relationship.service_id === serviceId);
2826
+ const targetProviderId = providerId ?? state.selectedSlot?.providerId ?? state.selectedProviderId;
2827
+ if (targetProviderId) {
2828
+ return relationships.find((relationship) => relationship.provider_id === targetProviderId) || null;
2829
+ }
2830
+ return relationships.length === 1 ? relationships[0] : null;
2831
+ }
2832
+ function clearServiceFormState(error = null, loading = false) {
2833
+ service_form_state.set({
2834
+ provider_id: null,
2835
+ groups: [],
2836
+ loading,
2837
+ error
2838
+ });
2390
2839
  }
2391
- function getFirstServiceProviderEntry(state) {
2392
- const serviceWithProviders = state.service;
2393
- const providers = serviceWithProviders?.providers;
2394
- if (!providers?.length) return null;
2395
- if (state.selectedProviderId) {
2396
- const match = providers.find((provider) => provider.provider_id === state.selectedProviderId);
2397
- if (match) return match;
2840
+ function activateServiceProviderForms(providerId, reset = false) {
2841
+ const relationship = resolveServiceProvider(service_state.get(), providerId);
2842
+ if (!relationship) {
2843
+ clearServiceFormState();
2844
+ return null;
2845
+ }
2846
+ const formIds = configuredServiceFormIds(relationship);
2847
+ const current = service_form_state.get();
2848
+ const currentFormIds = current.groups.map((group) => group.form.id);
2849
+ if (!reset && current.provider_id === relationship.provider_id && currentFormIds.length === formIds.length && currentFormIds.every((formId, index) => formId === formIds[index])) {
2850
+ if (current.error) service_form_state.setKey("error", null);
2851
+ return relationship;
2398
2852
  }
2399
- return providers[0];
2853
+ const groups = formIds.map((formId) => {
2854
+ const form = service_form_definitions.get(formId);
2855
+ if (!form) throw new Error(`Configured booking form '${formId}' was not loaded`);
2856
+ return {
2857
+ form,
2858
+ blocks: form.schema.map(formSchemaToBlock)
2859
+ };
2860
+ });
2861
+ service_form_state.set({
2862
+ provider_id: relationship.provider_id,
2863
+ groups,
2864
+ loading: false,
2865
+ error: null
2866
+ });
2867
+ return relationship;
2400
2868
  }
2401
- async function loadServiceForm() {
2402
- try {
2403
- const form = await loadForm({ key: "order-form" });
2404
- const blocks = (form.schema || []).map(formSchemaToBlock);
2405
- service_form_node.set({ blocks });
2406
- return blocks;
2407
- } catch {
2408
- service_form_node.set({ blocks: [] });
2409
- return [];
2869
+ async function loadServiceFormDefinitions(relationships) {
2870
+ const formIds = [
2871
+ ...new Set(relationships.flatMap((relationship) => configuredServiceFormIds(relationship)))
2872
+ ];
2873
+ if (formIds.length === 0) return;
2874
+ const forms = await Promise.all(formIds.map((formId) => loadForm({ id: formId })));
2875
+ for (let index = 0; index < forms.length; index += 1) {
2876
+ const form = forms[index];
2877
+ const formId = formIds[index];
2878
+ if (form.id !== formId) {
2879
+ throw new Error(`Configured booking form '${formId}' resolved to '${form.id}'`);
2880
+ }
2410
2881
  }
2882
+ for (const form of forms) service_form_definitions.set(form.id, form);
2883
+ }
2884
+ function configuredServiceFormEntries(relationship) {
2885
+ const formIds = configuredServiceFormIds(relationship);
2886
+ if (formIds.length === 0) return [];
2887
+ activateServiceProviderForms(relationship.provider_id);
2888
+ const state = service_form_state.get();
2889
+ if (state.provider_id !== relationship.provider_id || state.groups.length !== formIds.length || state.groups.some((group, index) => group.form.id !== formIds[index])) {
2890
+ throw new Error(`Booking forms are not ready for provider ${relationship.provider_id}`);
2891
+ }
2892
+ return state.groups.map(
2893
+ (group) => createFormEntryFromValues(
2894
+ group.form,
2895
+ Object.fromEntries(group.blocks.map((block) => [block.key, block.value]))
2896
+ )
2897
+ );
2411
2898
  }
2412
2899
  const service_controller = {
2413
2900
  async initialize() {
2414
2901
  service_state.setKey("tzGroups", normalizeTimezoneGroups(client.utils.tzGroups));
2415
2902
  await ensureCart();
2416
- setServiceCartFromServiceItems(service_items.get());
2417
2903
  const methods = session.get()?.market?.payment_methods || [];
2418
2904
  if (methods.length) service_state.setKey("availablePaymentMethods", methods);
2419
- await loadServiceForm();
2420
2905
  },
2421
2906
  setTimezone(tz) {
2422
2907
  service_state.setKey("timezone", tz);
@@ -2425,39 +2910,62 @@ function initialize(config) {
2425
2910
  if (state.selectedDate) {
2426
2911
  service_state.setKey("slots", computeServiceSlots(state.selectedDate));
2427
2912
  service_state.setKey("selectedSlot", null);
2913
+ service_state.setKey("quote", null);
2914
+ service_state.setKey("quoteError", null);
2915
+ activateServiceProviderForms();
2428
2916
  }
2429
2917
  },
2430
2918
  async select(service) {
2431
- service_state.setKey("loading", true);
2919
+ service_form_definitions.clear();
2920
+ clearServiceFormState(null, true);
2921
+ service_state.set({
2922
+ ...service_state.get(),
2923
+ service: null,
2924
+ serviceProviders: [],
2925
+ providers: [],
2926
+ selectedProviderId: null,
2927
+ availability: null,
2928
+ selectedDate: null,
2929
+ slots: [],
2930
+ selectedSlot: null,
2931
+ dateTimeConfirmed: false,
2932
+ quote: null,
2933
+ quoteError: null,
2934
+ loading: true
2935
+ });
2432
2936
  try {
2433
- const isMultiDayBlock = service.blocks?.find((block) => block.key === "isMultiDay");
2434
- const blockValue = isMultiDayBlock?.value;
2435
- const isMultiDay = Array.isArray(blockValue) ? blockValue[0] === true : blockValue === true;
2436
2937
  const [fullService, serviceProviders] = await Promise.all([
2437
2938
  client.eshop.service.get({ id: service.id }),
2438
- client.eshop.service.findProviders({ service_id: service.id })
2939
+ client.eshop.service.findProviders({
2940
+ service_id: service.id
2941
+ })
2942
+ ]);
2943
+ const providerIds = [...new Set(serviceProviders.map((relationship) => relationship.provider_id))];
2944
+ const [providerResults] = await Promise.all([
2945
+ Promise.all(providerIds.map((id) => client.eshop.provider.get({ id }).catch(() => null))),
2946
+ loadServiceFormDefinitions(serviceProviders)
2439
2947
  ]);
2440
- const providerIds = [...new Set(serviceProviders.map(serviceProviderId))];
2441
- const providerResults = await Promise.all(
2442
- providerIds.map((id) => client.eshop.provider.get({ id }).catch(() => null))
2443
- );
2444
2948
  service_state.set({
2445
2949
  ...service_state.get(),
2446
2950
  service: fullService,
2951
+ serviceProviders,
2447
2952
  providers: providerResults.filter((provider) => provider !== null),
2448
2953
  selectedProviderId: null,
2449
2954
  availability: null,
2450
2955
  selectedDate: null,
2451
- startDate: null,
2452
- endDate: null,
2453
2956
  slots: [],
2454
2957
  selectedSlot: null,
2455
2958
  currentMonth: new Date((/* @__PURE__ */ new Date()).getFullYear(), (/* @__PURE__ */ new Date()).getMonth(), 1),
2456
2959
  loading: false,
2457
- isMultiDay
2960
+ dateTimeConfirmed: false,
2961
+ quote: null,
2962
+ quoteError: null
2458
2963
  });
2964
+ activateServiceProviderForms();
2459
2965
  await service_controller.loadMonth();
2460
2966
  } catch (error) {
2967
+ service_form_definitions.clear();
2968
+ clearServiceFormState(readErrorMessage(error, "Failed to load booking forms."));
2461
2969
  service_state.setKey("loading", false);
2462
2970
  throw error;
2463
2971
  }
@@ -2500,90 +3008,71 @@ function initialize(config) {
2500
3008
  void service_controller.loadMonth();
2501
3009
  },
2502
3010
  selectProvider(providerId) {
3011
+ const state = service_state.get();
3012
+ if (providerId && !resolveServiceProvider(state, providerId)) {
3013
+ throw new Error(`Provider ${providerId} is not configured for the selected service`);
3014
+ }
2503
3015
  service_state.set({
2504
- ...service_state.get(),
3016
+ ...state,
2505
3017
  selectedProviderId: providerId,
2506
3018
  selectedDate: null,
2507
- startDate: null,
2508
- endDate: null,
2509
3019
  slots: [],
2510
- selectedSlot: null
3020
+ selectedSlot: null,
3021
+ dateTimeConfirmed: false,
3022
+ quote: null,
3023
+ quoteError: null
2511
3024
  });
3025
+ activateServiceProviderForms(providerId);
2512
3026
  void service_controller.loadMonth();
2513
3027
  },
2514
3028
  selectDate(cell) {
2515
3029
  if (cell.blank || !cell.available) return;
2516
- service_state.setKey("dateTimeConfirmed", false);
2517
3030
  const state = service_state.get();
2518
- if (state.isMultiDay) {
2519
- if (!state.startDate) {
2520
- service_state.setKey("startDate", cell.iso);
2521
- service_state.setKey("selectedDate", cell.iso);
2522
- service_state.setKey("endDate", null);
2523
- service_state.setKey("selectedSlot", null);
2524
- } else if (!state.endDate) {
2525
- if (cell.date.getTime() < new Date(state.startDate).getTime()) {
2526
- service_state.setKey("startDate", cell.iso);
2527
- service_state.setKey("endDate", state.startDate);
2528
- } else {
2529
- service_state.setKey("endDate", cell.iso);
2530
- }
2531
- service_controller.createMultiDaySlots();
2532
- } else {
2533
- service_state.setKey("startDate", cell.iso);
2534
- service_state.setKey("selectedDate", cell.iso);
2535
- service_state.setKey("endDate", null);
2536
- service_state.setKey("selectedSlot", null);
2537
- }
2538
- service_controller.updateCalendar();
2539
- } else {
2540
- service_state.set({
2541
- ...state,
2542
- selectedDate: cell.iso,
2543
- slots: computeServiceSlots(cell.iso),
2544
- selectedSlot: null
2545
- });
2546
- service_state.setKey("calendar", serviceCalendar());
2547
- }
3031
+ service_state.set({
3032
+ ...state,
3033
+ selectedDate: cell.iso,
3034
+ slots: computeServiceSlots(cell.iso),
3035
+ selectedSlot: null,
3036
+ dateTimeConfirmed: false,
3037
+ quote: null,
3038
+ quoteError: null
3039
+ });
3040
+ activateServiceProviderForms();
3041
+ service_state.setKey("calendar", serviceCalendar());
2548
3042
  },
2549
- createMultiDaySlots() {
3043
+ selectTimeSlot(slot) {
2550
3044
  const state = service_state.get();
2551
- if (!state.startDate || !state.endDate || !state.availability) return;
2552
- const slots = [];
2553
- for (let day = new Date(state.startDate); day <= new Date(state.endDate); day.setDate(day.getDate() + 1)) {
2554
- const iso = day.toISOString().slice(0, 10);
2555
- for (const slot of getSlotsForDate(state.availability, iso, state.selectedProviderId)) {
2556
- slots.push({
2557
- id: `${state.service?.id || "service"}-${slot.from}-${slots.length}`,
2558
- serviceId: state.service?.id || "",
2559
- providerId: slot.providerId,
2560
- from: slot.from,
2561
- to: slot.to,
2562
- timeText: formatServiceSlotTime(slot.from, slot.to, state.timezone),
2563
- dateText: new Date(slot.from * 1e3).toLocaleDateString([], {
2564
- weekday: "short",
2565
- month: "short",
2566
- day: "numeric",
2567
- timeZone: state.timezone
2568
- }),
2569
- isMultiDay: true
2570
- });
3045
+ if (slot) {
3046
+ if (!state.service || slot.serviceId !== state.service.id) {
3047
+ throw new Error("The selected slot does not belong to the selected service");
3048
+ }
3049
+ if (state.selectedProviderId && slot.providerId !== state.selectedProviderId) {
3050
+ throw new Error("The selected slot does not belong to the selected provider");
3051
+ }
3052
+ if (!resolveServiceProvider(state, slot.providerId)) {
3053
+ throw new Error(`Provider ${slot.providerId} is not configured for the selected service`);
2571
3054
  }
2572
3055
  }
2573
- service_state.setKey("slots", slots);
2574
- service_state.setKey("selectedSlot", slots.length === 1 ? slots[0] : null);
2575
- },
2576
- selectTimeSlot(slot) {
2577
- service_state.setKey("dateTimeConfirmed", false);
2578
- service_state.setKey("selectedSlot", slot);
3056
+ service_state.set({
3057
+ ...state,
3058
+ selectedSlot: slot,
3059
+ dateTimeConfirmed: false,
3060
+ quote: null,
3061
+ quoteError: null
3062
+ });
3063
+ activateServiceProviderForms(slot?.providerId);
2579
3064
  },
2580
3065
  resetDateSelection() {
2581
- service_state.setKey("selectedDate", null);
2582
- service_state.setKey("startDate", null);
2583
- service_state.setKey("endDate", null);
2584
- service_state.setKey("slots", []);
2585
- service_state.setKey("selectedSlot", null);
2586
- service_state.setKey("dateTimeConfirmed", false);
3066
+ service_state.set({
3067
+ ...service_state.get(),
3068
+ selectedDate: null,
3069
+ slots: [],
3070
+ selectedSlot: null,
3071
+ dateTimeConfirmed: false,
3072
+ quote: null,
3073
+ quoteError: null
3074
+ });
3075
+ activateServiceProviderForms();
2587
3076
  },
2588
3077
  updateCalendar() {
2589
3078
  service_state.setKey("calendar", serviceCalendar());
@@ -2596,70 +3085,79 @@ function initialize(config) {
2596
3085
  }
2597
3086
  }
2598
3087
  },
2599
- async addToCart() {
3088
+ async addToCart(explicitSlots) {
2600
3089
  const state = service_state.get();
2601
- const serviceBlocks = state.service?.forms || [];
2602
- const enrich = (slot) => ({
3090
+ const slots = explicitSlots || (state.selectedSlot ? [state.selectedSlot] : []);
3091
+ if (slots.length === 0) return;
3092
+ const first = slots[0];
3093
+ if (!state.service || first.serviceId !== state.service.id) {
3094
+ throw new Error("The booking slots do not belong to the selected service");
3095
+ }
3096
+ const relationship = resolveServiceProvider(state, first.providerId);
3097
+ if (!relationship) {
3098
+ throw new Error(`Provider ${first.providerId} is not configured for the selected service`);
3099
+ }
3100
+ let forms;
3101
+ try {
3102
+ forms = configuredServiceFormEntries(relationship);
3103
+ } catch (error) {
3104
+ service_form_state.setKey("error", readErrorMessage(error, "Booking forms are invalid."));
3105
+ throw error;
3106
+ }
3107
+ const displayName = serviceName(state.service, currentLocale());
3108
+ const enriched = slots.map((slot) => ({
2603
3109
  ...slot,
2604
- serviceName: state.service ? serviceName(state.service, currentLocale()) : "",
2605
- date: slot.dateText,
2606
- serviceBlocks
2607
- });
2608
- const selected = state.isMultiDay && state.slots.length > 0 ? state.slots.map(enrich) : state.selectedSlot ? [enrich(state.selectedSlot)] : [];
2609
- if (!selected.length) return;
2610
- const nextCart = [...state.cart, ...selected];
3110
+ serviceName: displayName,
3111
+ date: slot.dateText
3112
+ }));
3113
+ const nextItems = [...service_items.get(), toServiceCartItem(enriched, forms)];
3114
+ await syncServiceCart(nextItems);
2611
3115
  service_state.set({
2612
- ...state,
2613
- cart: nextCart,
3116
+ ...service_state.get(),
2614
3117
  selectedDate: null,
2615
- startDate: null,
2616
- endDate: null,
2617
3118
  slots: [],
2618
- selectedSlot: null
3119
+ selectedSlot: null,
3120
+ dateTimeConfirmed: false,
3121
+ quote: null,
3122
+ quoteError: null
2619
3123
  });
2620
- await syncServiceCart(nextCart);
3124
+ activateServiceProviderForms(service_state.get().selectedProviderId, true);
2621
3125
  service_state.setKey("calendar", serviceCalendar());
2622
3126
  },
2623
- async removeFromCart(slotId) {
2624
- const nextCart = service_state.get().cart.filter((slot) => slot.id !== slotId);
2625
- service_state.setKey("cart", nextCart);
2626
- await syncServiceCart(nextCart);
3127
+ async removeFromCart(bookingId) {
3128
+ await syncServiceCart(service_items.get().filter((item) => item.id !== bookingId));
2627
3129
  },
2628
3130
  async clearCart() {
2629
- service_state.setKey("cart", []);
2630
3131
  await syncServiceCart([]);
2631
3132
  },
2632
3133
  async checkout(paymentMethodId, forms = []) {
2633
3134
  const state = service_state.get();
2634
- if (!state.cart.length) return { success: false, error: "Cart is empty" };
3135
+ const items = service_items.get();
3136
+ if (!items.length) throw new Error("Cart is empty");
2635
3137
  service_state.setKey("loading", true);
2636
3138
  try {
2637
3139
  const result = await checkout({
2638
- service_items: state.cart.map((slot) => ({
2639
- ...toServiceCartItem(slot),
2640
- forms: []
2641
- })),
3140
+ service_items: items,
2642
3141
  payment_method_key: paymentMethodId,
2643
3142
  promo_code: state.promoCode || void 0,
2644
3143
  forms
2645
3144
  });
2646
3145
  service_state.setKey("cartId", cart.get()?.id || null);
2647
- return { success: true, data: result };
2648
- } catch (error) {
2649
- return { success: false, error: readErrorMessage(error, "Checkout failed.") };
3146
+ return result;
2650
3147
  } finally {
2651
3148
  service_state.setKey("loading", false);
2652
3149
  }
2653
3150
  },
2654
3151
  async fetchQuote(paymentMethodId, promoCode) {
2655
- const state = service_state.get();
2656
- if (!state.cart.length) return null;
3152
+ service_state.get();
3153
+ const items = service_items.get();
3154
+ if (!items.length) return null;
2657
3155
  service_state.setKey("fetchingQuote", true);
2658
3156
  service_state.setKey("quoteError", null);
2659
3157
  try {
2660
3158
  service_state.setKey("promoCode", promoCode || null);
2661
3159
  const response = await fetchQuote({
2662
- service_items: state.cart.map(toServiceCartItem),
3160
+ service_items: items,
2663
3161
  payment_method_key: paymentMethodId,
2664
3162
  promo_code: promoCode || void 0
2665
3163
  });
@@ -2687,6 +3185,9 @@ function initialize(config) {
2687
3185
  if (current === "datetime") {
2688
3186
  service_state.setKey("selectedSlot", null);
2689
3187
  service_state.setKey("dateTimeConfirmed", false);
3188
+ service_state.setKey("quote", null);
3189
+ service_state.setKey("quoteError", null);
3190
+ activateServiceProviderForms();
2690
3191
  }
2691
3192
  },
2692
3193
  nextStep() {
@@ -2696,17 +3197,20 @@ function initialize(config) {
2696
3197
  },
2697
3198
  getServicePrice() {
2698
3199
  const state = service_state.get();
2699
- if (state.quote?.total !== void 0) return String(state.quote.total);
2700
- const provider = getFirstServiceProviderEntry(state);
2701
- if (!provider?.prices) return "";
2702
- return client.utils.formatPrice(provider.prices) || "0";
3200
+ const relationship = resolveServiceProvider(state);
3201
+ if (!relationship) return "";
3202
+ try {
3203
+ const price = priceForMarket(relationship.prices, currentMarketKey(), market.get()?.currency);
3204
+ return client.utils.formatPrice([price]);
3205
+ } catch {
3206
+ return "";
3207
+ }
2703
3208
  },
2704
3209
  formatDateDisplay: formatServiceDateDisplay,
2705
- serviceItemsFromSlots(slots) {
2706
- return slots.map(toServiceCartItem);
3210
+ serviceItemsFromSlots(slots, forms = []) {
3211
+ return slots.length ? [toServiceCartItem(slots, forms)] : [];
2707
3212
  }
2708
3213
  };
2709
- service_items.subscribe((items) => setServiceCartFromServiceItems(items));
2710
3214
  async function loadEntry(params, options) {
2711
3215
  cms_state.setKey("loading", true);
2712
3216
  cms_state.setKey("error", null);
@@ -2716,7 +3220,10 @@ function initialize(config) {
2716
3220
  if (entryParams.id) {
2717
3221
  const entry2 = await client.cms.entry.get(entryParams, options);
2718
3222
  const cacheKey = entryParams.key || entryParams.id || entry2.id;
2719
- cms_state.setKey("entries", { ...cms_state.get().entries, [cacheKey]: entry2 });
3223
+ cms_state.setKey("entries", {
3224
+ ...cms_state.get().entries,
3225
+ [cacheKey]: entry2
3226
+ });
2720
3227
  return entry2;
2721
3228
  }
2722
3229
  if (!entryParams.collection_id || !entryParams.key) {
@@ -2735,7 +3242,10 @@ function initialize(config) {
2735
3242
  if (!entry) {
2736
3243
  throw new Error("CMS entry not found");
2737
3244
  }
2738
- cms_state.setKey("entries", { ...cms_state.get().entries, [entryParams.key]: entry });
3245
+ cms_state.setKey("entries", {
3246
+ ...cms_state.get().entries,
3247
+ [entryParams.key]: entry
3248
+ });
2739
3249
  return entry;
2740
3250
  } catch (error) {
2741
3251
  cms_state.setKey("error", readErrorMessage(error, "Failed to load CMS entry."));
@@ -2744,13 +3254,22 @@ function initialize(config) {
2744
3254
  cms_state.setKey("loading", false);
2745
3255
  }
2746
3256
  }
3257
+ function formCacheKey(params) {
3258
+ const storeId = params.store_id || client.getStoreId();
3259
+ const identifier = params.id ? `id:${params.id}` : params.key ? `key:${params.key}` : "missing";
3260
+ return `${storeId}:${identifier}`;
3261
+ }
2747
3262
  async function loadForm(params, options) {
2748
3263
  cms_state.setKey("loading", true);
2749
3264
  cms_state.setKey("error", null);
2750
3265
  try {
2751
- const form = await client.cms.form.get(params, options);
2752
- const key = params.key || params.id || form.key || form.id;
2753
- cms_state.setKey("forms", { ...cms_state.get().forms, [key]: form });
3266
+ const storeId = params.store_id || client.getStoreId();
3267
+ const formClient = clientForStore(storeId);
3268
+ const form = await formClient.cms.form.get({ ...params, store_id: storeId }, options);
3269
+ const forms = { ...cms_state.get().forms };
3270
+ forms[formCacheKey({ id: form.id, store_id: storeId })] = form;
3271
+ forms[formCacheKey({ key: form.key, store_id: storeId })] = form;
3272
+ cms_state.setKey("forms", forms);
2754
3273
  return form;
2755
3274
  } catch (error) {
2756
3275
  cms_state.setKey("error", readErrorMessage(error, "Failed to load CMS form."));
@@ -2759,12 +3278,26 @@ function initialize(config) {
2759
3278
  cms_state.setKey("loading", false);
2760
3279
  }
2761
3280
  }
2762
- async function submitFormByKey(key, fieldsOrBlocks, options) {
2763
- const forms = cms_state.get().forms;
2764
- const form = forms[key] || await loadForm({ key });
2765
- const fields = fieldsOrBlocks.length > 0 && "properties" in fieldsOrBlocks[0] ? formFieldsFromBlocks(fieldsOrBlocks) : fieldsOrBlocks;
2766
- const payload = { form_id: form.id, fields };
2767
- return client.cms.form.submit(payload, options);
3281
+ async function ensureFormClient(storeId) {
3282
+ const formClient = clientForStore(storeId);
3283
+ const marketKey = currentMarketKey();
3284
+ if (formClient === client) {
3285
+ await ensureSession();
3286
+ } else if (!formClient.session || marketKey && formClient.session.market?.key !== marketKey) {
3287
+ await formClient.identify({ market: marketKey });
3288
+ }
3289
+ return formClient;
3290
+ }
3291
+ async function submitForm(params, options) {
3292
+ const storeId = params.store_id || client.getStoreId();
3293
+ const formClient = await ensureFormClient(storeId);
3294
+ return formClient.cms.form.submit({ ...params, store_id: storeId }, options);
3295
+ }
3296
+ async function submitFormByKey(params, options) {
3297
+ const storeId = params.store_id || client.getStoreId();
3298
+ const form = await loadForm({ key: params.key, store_id: storeId }, options);
3299
+ const entry = createFormEntryFromValues(form, params.values);
3300
+ return submitForm({ store_id: storeId, form_id: form.id, fields: entry.fields }, options);
2768
3301
  }
2769
3302
  async function loadProducts(params = {}, options) {
2770
3303
  eshop_state.setKey("loading_products", true);
@@ -2867,38 +3400,32 @@ function initialize(config) {
2867
3400
  destroy: destroyPaymentController
2868
3401
  },
2869
3402
  applyPromoCode(code, input = {}) {
2870
- promo_code.set(code);
2871
3403
  return fetchQuote({ ...input, promo_code: code });
2872
3404
  },
2873
3405
  removePromoCode(input = {}) {
2874
- promo_code.set(null);
2875
3406
  return fetchQuote({ ...input, promo_code: null });
2876
3407
  },
2877
3408
  selectShippingMethod(id) {
2878
3409
  cart_status.setKey("selected_shipping_method_id", id);
2879
3410
  },
2880
3411
  locationToAddress,
3412
+ createFormEntry,
2881
3413
  buildItems: checkoutItems,
2882
3414
  buildProductItems: toProductCheckoutItems,
2883
3415
  buildServiceItems: toServiceCheckoutItems
2884
3416
  };
2885
3417
  const product_store = {
2886
3418
  get: (params, options) => client.eshop.product.get(params, options),
2887
- find: loadProducts,
2888
- list: loadProducts,
2889
- loadListing: loadProducts,
2890
- loadDetail: (params, options) => client.eshop.product.get(params, options)
3419
+ list: loadProducts
2891
3420
  };
2892
3421
  const service_store = {
2893
3422
  get: (params, options) => client.eshop.service.get(params, options),
2894
- find: loadServices,
2895
3423
  list: loadServices,
2896
- loadListing: loadServices,
2897
- loadDetail: (params, options) => client.eshop.service.get(params, options),
2898
3424
  listProviders: (params, options) => client.eshop.service.findProviders(params, options),
2899
- findProviders: (params, options) => client.eshop.service.findProviders(params, options),
2900
3425
  getAvailability: loadAvailability,
2901
3426
  state: service_state,
3427
+ form_state: service_form_state,
3428
+ form_groups: service_form_groups,
2902
3429
  form_blocks: service_form_blocks,
2903
3430
  current_step_name: service_current_step_name,
2904
3431
  can_proceed: service_can_proceed,
@@ -2915,7 +3442,6 @@ function initialize(config) {
2915
3442
  nextMonth: service_controller.nextMonth,
2916
3443
  selectProvider: service_controller.selectProvider,
2917
3444
  selectDate: service_controller.selectDate,
2918
- createMultiDaySlots: service_controller.createMultiDaySlots,
2919
3445
  selectTimeSlot: service_controller.selectTimeSlot,
2920
3446
  resetDateSelection: service_controller.resetDateSelection,
2921
3447
  updateCalendar: service_controller.updateCalendar,
@@ -2939,8 +3465,8 @@ function initialize(config) {
2939
3465
  currency,
2940
3466
  allowed_payment_methods,
2941
3467
  payment_config,
2942
- cart: cart_store,
2943
3468
  identify,
3469
+ identifyContactEmailIfMissing,
2944
3470
  verify: client.verify,
2945
3471
  me: client.me,
2946
3472
  logout: client.logout,
@@ -2965,7 +3491,7 @@ function initialize(config) {
2965
3491
  },
2966
3492
  form: {
2967
3493
  get: loadForm,
2968
- submit: (params, options) => client.cms.form.submit(params, options),
3494
+ submit: submitForm,
2969
3495
  submitByKey: submitFormByKey
2970
3496
  },
2971
3497
  taxonomy: client.cms.taxonomy
@@ -2976,7 +3502,7 @@ function initialize(config) {
2976
3502
  service: service_store,
2977
3503
  provider: {
2978
3504
  get: (params, options) => client.eshop.provider.get(params, options),
2979
- find: loadProviders
3505
+ list: loadProviders
2980
3506
  },
2981
3507
  order: client.eshop.order,
2982
3508
  cart: cart_store
@@ -2999,7 +3525,29 @@ function initialize(config) {
2999
3525
  utils: client.utils
3000
3526
  };
3001
3527
  }
3528
+ function initialize(config) {
3529
+ const stores = /* @__PURE__ */ new Map();
3530
+ const createScope = (scopeConfig) => {
3531
+ const existing = stores.get(scopeConfig.storeId);
3532
+ if (existing) return existing;
3533
+ const store = initializeStore(scopeConfig);
3534
+ stores.set(scopeConfig.storeId, store);
3535
+ Object.defineProperty(store, "forStore", {
3536
+ enumerable: true,
3537
+ configurable: false,
3538
+ writable: false,
3539
+ value: (storeId) => createScope({
3540
+ ...config,
3541
+ storeId,
3542
+ market: store.getMarket(),
3543
+ locale: store.getLocale()
3544
+ })
3545
+ });
3546
+ return store;
3547
+ };
3548
+ return createScope(config);
3549
+ }
3002
3550
 
3003
- export { COMMON_ACTION_KEYS, createCartController, createStorefront, createStripeConfirmationTokenController, initialize };
3551
+ export { COMMON_ACTION_KEYS, buildFormFields, createCartController, createFormEntry, createFormEntryFromValues, createStorefront, createStripeConfirmationTokenController, getBlockContentValue, initialize };
3004
3552
  //# sourceMappingURL=storefront.js.map
3005
3553
  //# sourceMappingURL=storefront.js.map