@juo/blocks 1.15.0 → 1.16.1

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 (104) hide show
  1. package/dist/{ExtensionRoot-C3lVJEBl.js → ExtensionRoot-DfpvN94s.js} +1 -1
  2. package/dist/{block-VhmfGB3N.js → block-dVP4Rqgz.js} +9 -0
  3. package/dist/core.js +2297 -381
  4. package/dist/{css-runtime-ClH0MEYr.js → css-runtime-DTiXcYSs.js} +23 -17
  5. package/dist/editor/handler.d.ts.map +1 -1
  6. package/dist/editor.js +4 -1
  7. package/dist/{gridstack-adapter-CDWbSpbt.js → gridstack-adapter-BZWybRw1.js} +2 -2
  8. package/dist/preact.js +2 -2
  9. package/dist/{project-gridstack-CM783oKI.js → project-gridstack-C2cSYRMx.js} +1 -1
  10. package/dist/react.js +2 -2
  11. package/dist/services/customer/adapter.d.ts +8 -0
  12. package/dist/services/customer/adapter.d.ts.map +1 -0
  13. package/dist/services/customer/customerService.d.ts +3 -2
  14. package/dist/services/customer/customerService.d.ts.map +1 -1
  15. package/dist/services/customer/index.d.ts +1 -0
  16. package/dist/services/customer/index.d.ts.map +1 -1
  17. package/dist/services/customer/types.d.ts +4 -1
  18. package/dist/services/customer/types.d.ts.map +1 -1
  19. package/dist/services/index.d.ts +3 -0
  20. package/dist/services/index.d.ts.map +1 -1
  21. package/dist/services/login/adapter.d.ts +33 -0
  22. package/dist/services/login/adapter.d.ts.map +1 -0
  23. package/dist/services/login/index.d.ts +3 -1
  24. package/dist/services/login/index.d.ts.map +1 -1
  25. package/dist/services/login/loginService.d.ts +6 -2
  26. package/dist/services/login/loginService.d.ts.map +1 -1
  27. package/dist/services/order/adapter.d.ts +9 -0
  28. package/dist/services/order/adapter.d.ts.map +1 -0
  29. package/dist/services/order/index.d.ts +5 -0
  30. package/dist/services/order/index.d.ts.map +1 -0
  31. package/dist/services/order/orderService.d.ts +7 -0
  32. package/dist/services/order/orderService.d.ts.map +1 -0
  33. package/dist/services/order/types.d.ts +21 -0
  34. package/dist/services/order/types.d.ts.map +1 -0
  35. package/dist/services/orders/adapter.d.ts +2 -0
  36. package/dist/services/orders/adapter.d.ts.map +1 -1
  37. package/dist/services/orders/index.d.ts +1 -1
  38. package/dist/services/orders/index.d.ts.map +1 -1
  39. package/dist/services/product/adapter.d.ts +27 -2
  40. package/dist/services/product/adapter.d.ts.map +1 -1
  41. package/dist/services/product/adapter.test.d.ts +2 -0
  42. package/dist/services/product/adapter.test.d.ts.map +1 -0
  43. package/dist/services/product/index.d.ts +1 -1
  44. package/dist/services/product/index.d.ts.map +1 -1
  45. package/dist/services/product/productService.d.ts.map +1 -1
  46. package/dist/services/product/types.d.ts +68 -0
  47. package/dist/services/product/types.d.ts.map +1 -1
  48. package/dist/services/schedules/adapter.d.ts +94 -0
  49. package/dist/services/schedules/adapter.d.ts.map +1 -0
  50. package/dist/services/schedules/adapter.test.d.ts +2 -0
  51. package/dist/services/schedules/adapter.test.d.ts.map +1 -0
  52. package/dist/services/schedules/apiSchedulesAdapter.test.d.ts +2 -0
  53. package/dist/services/schedules/apiSchedulesAdapter.test.d.ts.map +1 -0
  54. package/dist/services/schedules/index.d.ts +5 -0
  55. package/dist/services/schedules/index.d.ts.map +1 -0
  56. package/dist/services/schedules/schedulesService.d.ts +11 -0
  57. package/dist/services/schedules/schedulesService.d.ts.map +1 -0
  58. package/dist/services/schedules/schedulesService.test.d.ts +2 -0
  59. package/dist/services/schedules/schedulesService.test.d.ts.map +1 -0
  60. package/dist/services/schedules/types.d.ts +290 -0
  61. package/dist/services/schedules/types.d.ts.map +1 -0
  62. package/dist/services/settings/index.d.ts +3 -0
  63. package/dist/services/settings/index.d.ts.map +1 -0
  64. package/dist/services/settings/settingsService.d.ts +7 -0
  65. package/dist/services/settings/settingsService.d.ts.map +1 -0
  66. package/dist/services/settings/settingsService.test.d.ts +2 -0
  67. package/dist/services/settings/settingsService.test.d.ts.map +1 -0
  68. package/dist/services/settings/types.d.ts +35 -0
  69. package/dist/services/settings/types.d.ts.map +1 -0
  70. package/dist/services/shop/index.d.ts +3 -0
  71. package/dist/services/shop/index.d.ts.map +1 -0
  72. package/dist/services/shop/shopService.d.ts +12 -0
  73. package/dist/services/shop/shopService.d.ts.map +1 -0
  74. package/dist/services/shop/shopService.test.d.ts +2 -0
  75. package/dist/services/shop/shopService.test.d.ts.map +1 -0
  76. package/dist/services/shop/types.d.ts +7 -0
  77. package/dist/services/shop/types.d.ts.map +1 -0
  78. package/dist/services/subscription/adapter.d.ts +77 -2
  79. package/dist/services/subscription/adapter.d.ts.map +1 -1
  80. package/dist/services/subscription/adapter.test.d.ts +2 -0
  81. package/dist/services/subscription/adapter.test.d.ts.map +1 -0
  82. package/dist/services/subscription/index.d.ts +1 -1
  83. package/dist/services/subscription/index.d.ts.map +1 -1
  84. package/dist/services/subscription/subscriptionService.d.ts +1 -6
  85. package/dist/services/subscription/subscriptionService.d.ts.map +1 -1
  86. package/dist/services/subscription/types.d.ts +220 -9
  87. package/dist/services/subscription/types.d.ts.map +1 -1
  88. package/dist/services/subscription/types.test.d.ts +2 -0
  89. package/dist/services/subscription/types.test.d.ts.map +1 -0
  90. package/dist/shared/context.d.ts.map +1 -1
  91. package/dist/theme-state.d.ts +10 -1
  92. package/dist/theme-state.d.ts.map +1 -1
  93. package/dist/utils/index.d.ts +6 -0
  94. package/dist/utils/index.d.ts.map +1 -1
  95. package/dist/vue/context.d.ts +26 -1
  96. package/dist/vue/context.d.ts.map +1 -1
  97. package/dist/vue/useTranslatedProp.test.d.ts +2 -0
  98. package/dist/vue/useTranslatedProp.test.d.ts.map +1 -0
  99. package/dist/vue.js +48 -4
  100. package/dist/web-components/editor/InlineText.d.ts +1 -0
  101. package/dist/web-components/editor/InlineText.d.ts.map +1 -1
  102. package/dist/web-components-editor.js +13 -5
  103. package/dist/web-components-runtime.js +2 -2
  104. package/package.json +1 -1
package/dist/core.js CHANGED
@@ -1,11 +1,11 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import { f as createContext, g as d, h as w, j as registerBlock, E, u, k as getDefinedBlocks, l as createBlockInstance, m as defineBlock, s as subscribeContext, G as GRID_V1_ENGINE_ID, n as clampRect, p as applyFillExpansion, q as compactItems, t as findFreeRect, D as DEFAULT_GRID } from "./css-runtime-ClH0MEYr.js";
5
- import { B, v, x, y, z, L, P, S, c, T, A, d as d2, b, F, H, I, J, K, N, O, Q, R, U, V, W, X, Y, Z, i, _, a, $, a0, o, a1, a2, a3, e, a4, r, a5, a6, a7, w as w2 } from "./css-runtime-ClH0MEYr.js";
4
+ import { f as createContext, g as d, h as w, j as registerBlock, E, u, k as getDefinedBlocks, l as createBlockInstance, m as defineBlock, s as subscribeContext, G as GRID_V1_ENGINE_ID, n as clampRect, p as applyFillExpansion, q as compactItems, t as findFreeRect, D as DEFAULT_GRID } from "./css-runtime-DTiXcYSs.js";
5
+ import { B, v, x, y, z, L, P, S, c, T, A, d as d2, b, F, H, I, J, K, N, O, Q, R, U, V, W, X, Y, Z, i, _, a, $, a0, o, a1, a2, a3, e, a4, r, a5, a6, a7, w as w2 } from "./css-runtime-DTiXcYSs.js";
6
6
  import { reportBlockWarning, reportBlockError } from "./observability.js";
7
7
  import { a as createEditorBridge, D as createSelectBlockMessage } from "./bridge-xiKEzCGR.js";
8
- import { p } from "./project-gridstack-CM783oKI.js";
8
+ import { p } from "./project-gridstack-C2cSYRMx.js";
9
9
  import { clearLayoutModeAdapter, getLayoutModeAdapter, registerLayoutModeAdapter } from "./layout-mode-adapter-BBd4JRQd.js";
10
10
  function createState() {
11
11
  const discountCode = d(null);
@@ -159,13 +159,43 @@ async function loadBlockRegistry(options = {}) {
159
159
  function createCustomerService(options) {
160
160
  const current = d(null);
161
161
  const accountOrdersUrl = d(options.getAccountOrdersUrl());
162
- const service = {
162
+ return {
163
163
  current,
164
- accountOrdersUrl
164
+ accountOrdersUrl,
165
+ async getCurrent() {
166
+ const result = await options.adapter.getCurrent();
167
+ if (result._tag === "Success") current.value = result.data;
168
+ return result;
169
+ }
165
170
  };
166
- return service;
167
171
  }
168
172
  const CustomerServiceContext = createContext("services/customer");
173
+ function createMockCustomerAdapter() {
174
+ const customer = { id: "customer-1", name: "Customer 1" };
175
+ return {
176
+ async getCurrent() {
177
+ return { _tag: "Success", data: customer };
178
+ }
179
+ };
180
+ }
181
+ function createApiCustomerAdapter(fetcher) {
182
+ return {
183
+ async getCurrent() {
184
+ try {
185
+ const res = await fetcher("/customer/info");
186
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
187
+ const data = await res.json();
188
+ const name = [data.firstName, data.lastName].filter(Boolean).join(" ") || data.email || "";
189
+ return {
190
+ _tag: "Success",
191
+ data: { id: data.id, name, email: data.email }
192
+ };
193
+ } catch (e2) {
194
+ return { _tag: "Failure", error: e2 };
195
+ }
196
+ }
197
+ };
198
+ }
169
199
  const PARAMS = {
170
200
  DELETGATED_TOKEN: "delegated-token",
171
201
  REDIRECT_TO: "redirect-to",
@@ -182,6 +212,123 @@ function uuid() {
182
212
  (c2) => (c2 ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c2 / 4).toString(16)
183
213
  );
184
214
  }
215
+ function createMockLoginAdapter(opts = {}) {
216
+ const appUrl = opts.appUrl ?? "https://admin.juo.io";
217
+ return {
218
+ async passwordlessStart() {
219
+ return { _tag: "Success", data: void 0 };
220
+ },
221
+ async authenticate() {
222
+ return {
223
+ _tag: "Success",
224
+ data: {
225
+ access_token: "mock-access-token",
226
+ refresh_token: "mock-refresh-token",
227
+ expires_in: 3600
228
+ }
229
+ };
230
+ },
231
+ getAuthorizeUrl({ connection, state, shop }) {
232
+ const url = new URL(`${appUrl}/auth/customer/authorize`);
233
+ url.searchParams.set("connection", connection);
234
+ url.searchParams.set("state", state);
235
+ url.searchParams.set("shop", shop);
236
+ return url.toString();
237
+ }
238
+ };
239
+ }
240
+ function createApiLoginAdapter(fetcher, opts) {
241
+ const { shopDomain, appUrl } = opts;
242
+ async function callAuthEndpoint(path, body, lang) {
243
+ const url = lang ? `${path}?lang=${lang}` : path;
244
+ const res = await fetcher(url, {
245
+ method: "POST",
246
+ headers: {
247
+ Accept: "application/json",
248
+ "Content-Type": "application/json",
249
+ "X-Shopify-Shop-Domain": shopDomain
250
+ },
251
+ body: JSON.stringify(body)
252
+ });
253
+ if (res.status === 403) {
254
+ const data = await res.json();
255
+ if ("error_description" in data) {
256
+ if (data.error_description === "Wrong email or verification code.") {
257
+ throw new AuthenticationError(AuthenticationErrorReason.IncorrectCode);
258
+ } else if (data.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
259
+ throw new AuthenticationError(AuthenticationErrorReason.TooManyTries);
260
+ } else if (data.error_description === "The verification code has expired. Please try to login again.") {
261
+ throw new AuthenticationError(AuthenticationErrorReason.Expired);
262
+ }
263
+ } else if ("code" in data && data.code === "AUTH_ERROR") {
264
+ throw new AuthenticationError(AuthenticationErrorReason.NoEmail, {
265
+ connection: data.connection
266
+ });
267
+ }
268
+ }
269
+ if (!res.ok) {
270
+ throw new Error(`Unexpected response: ${res.statusText}`);
271
+ }
272
+ return { _tag: "Success", data: void 0 };
273
+ }
274
+ return {
275
+ async passwordlessStart({ email, lang }) {
276
+ try {
277
+ await callAuthEndpoint(
278
+ "/auth/customer/passwordless/start",
279
+ { email },
280
+ lang
281
+ );
282
+ return { _tag: "Success", data: void 0 };
283
+ } catch (e2) {
284
+ return { _tag: "Failure", error: e2 };
285
+ }
286
+ },
287
+ async authenticate(params) {
288
+ try {
289
+ const res = await fetcher("/auth/customer/token", {
290
+ method: "POST",
291
+ headers: {
292
+ Accept: "application/json",
293
+ "Content-Type": "application/json",
294
+ "X-Shopify-Shop-Domain": shopDomain
295
+ },
296
+ body: JSON.stringify(params)
297
+ });
298
+ if (res.status === 403) {
299
+ const data = await res.json();
300
+ if ("error_description" in data) {
301
+ if (data.error_description === "Wrong email or verification code.") {
302
+ throw new AuthenticationError(AuthenticationErrorReason.IncorrectCode);
303
+ } else if (data.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
304
+ throw new AuthenticationError(AuthenticationErrorReason.TooManyTries);
305
+ } else if (data.error_description === "The verification code has expired. Please try to login again.") {
306
+ throw new AuthenticationError(AuthenticationErrorReason.Expired);
307
+ }
308
+ } else if ("code" in data && data.code === "AUTH_ERROR") {
309
+ throw new AuthenticationError(AuthenticationErrorReason.NoEmail, {
310
+ connection: data.connection
311
+ });
312
+ }
313
+ }
314
+ if (!res.ok) {
315
+ throw new Error(`Unexpected response: ${res.statusText}`);
316
+ }
317
+ return { _tag: "Success", data: await res.json() };
318
+ } catch (e2) {
319
+ return { _tag: "Failure", error: e2 };
320
+ }
321
+ },
322
+ getAuthorizeUrl({ connection, state, shop }) {
323
+ const url = new URL(`${appUrl}/auth/customer/authorize`);
324
+ url.searchParams.set("connection", connection);
325
+ url.searchParams.set("state", state);
326
+ url.searchParams.set("shop", shop);
327
+ return url.toString();
328
+ }
329
+ };
330
+ }
331
+ const __vite_import_meta_env__ = {};
185
332
  var AuthenticationErrorReason = /* @__PURE__ */ ((AuthenticationErrorReason2) => {
186
333
  AuthenticationErrorReason2["Expired"] = "expired";
187
334
  AuthenticationErrorReason2["IncorrectCode"] = "wrong-code";
@@ -200,6 +347,9 @@ class AuthenticationError extends Error {
200
347
  this.data = data ?? {};
201
348
  }
202
349
  }
350
+ const STORAGE_DELEGATED_TOKEN = "delegated-token";
351
+ const STORAGE_REFRESH_TOKEN_KEY = "refresh-token";
352
+ const STORAGE_STATE_KEY = "state";
203
353
  function storage(key, value) {
204
354
  const storageKey = `auth:${key}`;
205
355
  try {
@@ -215,83 +365,33 @@ function storage(key, value) {
215
365
  }
216
366
  return null;
217
367
  }
218
- const STORAGE_DELEGATED_TOKEN = "delegated-token";
219
- const STORAGE_REFRESH_TOKEN_KEY = "refresh-token";
220
- const STORAGE_STATE_KEY = "state";
221
- function createAuthFunctions(basePath, shop) {
222
- async function start(email, lang) {
223
- const response = await fetch(
224
- `${basePath}/auth/customer/passwordless/start?lang=${lang}`,
225
- {
226
- method: "POST",
227
- headers: {
228
- Accept: "application/json",
229
- "Content-Type": "application/json",
230
- "X-Shopify-Shop-Domain": shop
231
- },
232
- body: JSON.stringify({ email })
233
- }
234
- );
235
- if (response.status !== 200) {
236
- throw new Error(`Unexpected response: ${response.statusText}`);
237
- }
238
- }
239
- async function authenticate(data) {
240
- const response = await fetch(`${basePath}/auth/customer/token`, {
241
- method: "POST",
242
- headers: {
243
- Accept: "application/json",
244
- "Content-Type": "application/json",
245
- "X-Shopify-Shop-Domain": shop
246
- },
247
- body: JSON.stringify(data)
248
- });
249
- if (response.status === 403) {
250
- const data2 = await response.json();
251
- if ("error_description" in data2) {
252
- if (data2.error_description === "Wrong email or verification code.") {
253
- throw new AuthenticationError(
254
- "wrong-code"
255
- /* IncorrectCode */
256
- );
257
- } else if (data2.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
258
- throw new AuthenticationError(
259
- "too-many-tries"
260
- /* TooManyTries */
261
- );
262
- } else if (data2.error_description === "The verification code has expired. Please try to login again.") {
263
- throw new AuthenticationError(
264
- "expired"
265
- /* Expired */
266
- );
267
- }
268
- } else if ("code" in data2) {
269
- if (data2.code === "AUTH_ERROR") {
270
- throw new AuthenticationError("no-email", {
271
- connection: data2.connection
272
- });
273
- }
274
- }
275
- }
276
- if (response.status !== 200) {
277
- throw new Error(`Unexpected response: ${response.statusText}`);
278
- }
279
- return await response.json();
280
- }
281
- return { start, authenticate };
282
- }
283
368
  function nonce(len = 10) {
284
369
  return uuid().replace(/-/g, "").slice(0, len);
285
370
  }
286
- function createAuthService(apiUrl, shopDomain) {
287
- const { start: authStart, authenticate: authAuthenticate } = createAuthFunctions(apiUrl, shopDomain);
371
+ function loginService(deps) {
372
+ const apiBase = deps.shop.rootUrl.replace(/\/$/, "") + "/api";
373
+ const appUrl = (typeof import.meta !== "undefined" ? __vite_import_meta_env__ == null ? void 0 : __vite_import_meta_env__.VITE_APP_URL : void 0) ?? "https://admin.juo.io";
374
+ const fetcher = (path, init) => fetch(`${apiBase}${path}`, init);
375
+ const adapter = createApiLoginAdapter(fetcher, {
376
+ shopDomain: deps.shop.domain,
377
+ appUrl
378
+ });
379
+ return createLoginService({
380
+ adapter,
381
+ router: deps.router,
382
+ formatError: deps.formatError,
383
+ shop: {
384
+ domain: deps.shop.domain,
385
+ locale: w(() => deps.getLocale())
386
+ },
387
+ onLoginSuccess: deps.onLoginSuccess
388
+ });
389
+ }
390
+ function createLoginService(init) {
391
+ const { adapter, formatError, router, shop, onLoginSuccess } = init;
288
392
  const accessToken = d(null);
289
- const delegatedToken = d(
290
- storage(STORAGE_DELEGATED_TOKEN)
291
- );
292
- const refreshToken = d(
293
- storage(STORAGE_REFRESH_TOKEN_KEY)
294
- );
393
+ const delegatedToken = d(storage(STORAGE_DELEGATED_TOKEN));
394
+ const refreshToken = d(storage(STORAGE_REFRESH_TOKEN_KEY));
295
395
  const expires = d(null);
296
396
  const state = d(storage(STORAGE_STATE_KEY));
297
397
  E(() => {
@@ -303,67 +403,28 @@ function createAuthService(apiUrl, shopDomain) {
303
403
  E(() => {
304
404
  storage(STORAGE_STATE_KEY, state.value);
305
405
  });
306
- function store(data) {
406
+ function storeTokens(data) {
307
407
  const { access_token, refresh_token, expires_in } = data;
308
408
  accessToken.value = access_token;
309
409
  refreshToken.value = refresh_token;
310
410
  expires.value = new Date(Date.now() + expires_in);
311
411
  }
312
- return {
313
- state,
314
- async start(email, lang) {
315
- await authStart(email, lang);
316
- },
317
- async authenticate(data) {
318
- store(await authAuthenticate(data));
319
- },
320
- getDelegatedToken() {
321
- return delegatedToken.value;
322
- },
323
- setDelegatedToken(token) {
324
- delegatedToken.value = token;
325
- },
326
- async getToken() {
327
- if (refreshToken.value == null) {
328
- return null;
329
- }
330
- if (accessToken.value == null || typeof accessToken.value === "string" && (expires.value == null || expires.value < /* @__PURE__ */ new Date())) {
331
- accessToken.value = new Promise(
332
- (resolve, reject) => authAuthenticate({ refreshToken: refreshToken.value }).then((data) => data).then((data) => {
333
- store(data);
334
- resolve(data.access_token);
412
+ async function getToken() {
413
+ if (refreshToken.value == null) return null;
414
+ if (accessToken.value == null || typeof accessToken.value === "string" && (expires.value == null || expires.value < /* @__PURE__ */ new Date())) {
415
+ accessToken.value = new Promise(
416
+ (resolve, reject) => adapter.authenticate({ refreshToken: refreshToken.value }).then((result) => {
417
+ if (result._tag === "Failure") {
418
+ reject(result.error);
335
419
  return;
336
- }).catch(reject)
337
- );
338
- }
339
- return Promise.resolve(accessToken.value);
340
- },
341
- logout() {
342
- accessToken.value = null;
343
- delegatedToken.value = null;
344
- refreshToken.value = null;
345
- expires.value = null;
346
- },
347
- isAuthenticated: w(
348
- () => delegatedToken.value != null || refreshToken.value != null
349
- )
350
- };
351
- }
352
- function loginService(deps) {
353
- return createLoginService({
354
- apiUrl: `${deps.shop.rootUrl.replace(/\/$/, "")}/api`,
355
- router: deps.router,
356
- formatError: deps.formatError,
357
- shop: {
358
- domain: deps.shop.domain,
359
- locale: w(() => deps.getLocale())
360
- },
361
- onLoginSuccess: deps.onLoginSuccess
362
- });
363
- }
364
- function createLoginService(init) {
365
- const { formatError, router, shop, onLoginSuccess } = init;
366
- const auth = createAuthService(init.apiUrl.replace(/\/$/, ""), shop.domain);
420
+ }
421
+ storeTokens(result.data);
422
+ resolve(result.data.access_token);
423
+ }).catch(reject)
424
+ );
425
+ }
426
+ return Promise.resolve(accessToken.value);
427
+ }
367
428
  const userEmail = d(null);
368
429
  async function onSuccess(query) {
369
430
  await (onLoginSuccess == null ? void 0 : onLoginSuccess());
@@ -376,13 +437,9 @@ function createLoginService(init) {
376
437
  queryParams[key] = value;
377
438
  }
378
439
  });
379
- router.push({
380
- path: decodeURIComponent(redirect),
381
- query: queryParams
382
- });
440
+ router.push({ path: decodeURIComponent(redirect), query: queryParams });
383
441
  } else {
384
- const redirect = query.get(PARAMS.REDIRECT_TO);
385
- router.replace(decodeURIComponent(redirect));
442
+ router.replace(decodeURIComponent(query.get(PARAMS.REDIRECT_TO)));
386
443
  }
387
444
  } else {
388
445
  router.replace("/");
@@ -394,16 +451,17 @@ function createLoginService(init) {
394
451
  async start(email) {
395
452
  try {
396
453
  userEmail.value = email;
397
- await auth.start(
398
- userEmail.value,
399
- shop.locale instanceof u ? shop.locale.value : shop.locale
400
- );
454
+ const lang = shop.locale instanceof u ? shop.locale.value : shop.locale;
455
+ const result = await adapter.passwordlessStart({
456
+ email: userEmail.value,
457
+ lang
458
+ });
459
+ if (result._tag === "Failure") {
460
+ return { _tag: "Failure", error: formatError(result.error) };
461
+ }
401
462
  return { _tag: "Success", data: void 0 };
402
463
  } catch (error) {
403
- return {
404
- _tag: "Failure",
405
- error: formatError(error)
406
- };
464
+ return { _tag: "Failure", error: formatError(error) };
407
465
  }
408
466
  },
409
467
  async verify(email, code) {
@@ -414,63 +472,57 @@ function createLoginService(init) {
414
472
  };
415
473
  }
416
474
  try {
417
- const result = await auth.authenticate({
475
+ const result = await adapter.authenticate({
418
476
  email: userEmail.value,
419
477
  code
420
478
  });
479
+ if (result._tag === "Failure") {
480
+ return { _tag: "Failure", error: formatError(result.error) };
481
+ }
482
+ storeTokens(result.data);
421
483
  await onSuccess();
422
- return { _tag: "Success", data: result };
484
+ return { _tag: "Success", data: void 0 };
423
485
  } catch (error) {
424
- return {
425
- _tag: "Failure",
426
- error: formatError(error)
427
- };
486
+ return { _tag: "Failure", error: formatError(error) };
428
487
  }
429
488
  }
430
489
  }
431
490
  },
432
491
  social: {
433
492
  async start(provider) {
434
- function socialUrl(key, shop2) {
435
- auth.state.value = nonce();
436
- const url = new URL(
437
- // TODO: double check admin url
438
- `${"https://admin.juo.io"}/auth/customer/authorize`
439
- );
440
- url.searchParams.append("connection", key);
441
- url.searchParams.append("state", auth.state.value ?? "");
442
- url.searchParams.append("shop", shop2);
443
- return url.toString();
444
- }
445
- window.location.href = socialUrl(
446
- provider === "Facebook" ? "facebook" : "google-oauth2",
447
- shop.domain
448
- );
449
- return {
450
- _tag: "Success",
451
- data: void 0
452
- };
493
+ state.value = nonce();
494
+ const connection = provider === "Facebook" ? "facebook" : "google-oauth2";
495
+ window.location.href = adapter.getAuthorizeUrl({
496
+ connection,
497
+ state: state.value ?? "",
498
+ shop: shop.domain
499
+ });
500
+ return { _tag: "Success", data: void 0 };
453
501
  },
454
502
  async verify(code) {
455
503
  try {
456
- const result = await auth.authenticate({ code });
504
+ const result = await adapter.authenticate({ code });
505
+ if (result._tag === "Failure") {
506
+ return { _tag: "Failure", error: formatError(result.error) };
507
+ }
508
+ storeTokens(result.data);
457
509
  await onSuccess();
458
- return { _tag: "Success", data: result };
510
+ return { _tag: "Success", data: void 0 };
459
511
  } catch (error) {
460
- return {
461
- _tag: "Failure",
462
- error: formatError(error)
463
- };
512
+ return { _tag: "Failure", error: formatError(error) };
464
513
  }
465
514
  }
466
515
  },
467
516
  async logout() {
468
- await auth.logout();
517
+ accessToken.value = null;
518
+ delegatedToken.value = null;
519
+ refreshToken.value = null;
520
+ expires.value = null;
469
521
  return { _tag: "Success", data: void 0 };
470
522
  },
471
523
  async handleQueryParams(query) {
472
524
  if (query.has(PARAMS.DELETGATED_TOKEN)) {
473
- auth.setDelegatedToken(query.get(PARAMS.DELETGATED_TOKEN));
525
+ delegatedToken.value = query.get(PARAMS.DELETGATED_TOKEN);
474
526
  await onSuccess(query);
475
527
  return { _tag: "Success", data: void 0 };
476
528
  }
@@ -482,16 +534,13 @@ function createLoginService(init) {
482
534
  }
483
535
  const code = query.get(PARAMS.CODE);
484
536
  if (code != null) {
485
- if (query.get(PARAMS.STATE) !== auth.state.value) {
486
- return {
487
- _tag: "Failure",
488
- error: formatError("Invalid state")
489
- };
537
+ if (query.get(PARAMS.STATE) !== state.value) {
538
+ return { _tag: "Failure", error: formatError("Invalid state") };
490
539
  } else {
491
540
  return service.social.verify(code);
492
541
  }
493
542
  }
494
- if (query.get(PARAMS.REFRESH) === "1" && await auth.getToken() != null) {
543
+ if (query.get(PARAMS.REFRESH) === "1" && await getToken() != null) {
495
544
  await onSuccess();
496
545
  return { _tag: "Success", data: void 0 };
497
546
  }
@@ -508,8 +557,20 @@ function createLoginService(init) {
508
557
  }
509
558
  return { _tag: "Success", data: void 0 };
510
559
  },
511
- isAuthenticated: auth.isAuthenticated,
512
- getToken: () => auth.getToken()
560
+ isAuthenticated: w(
561
+ () => delegatedToken.value != null || refreshToken.value != null
562
+ ),
563
+ getToken,
564
+ async getAuthHeaders() {
565
+ if (delegatedToken.value) {
566
+ return { "X-Delegated-Token": delegatedToken.value };
567
+ }
568
+ const token = await getToken();
569
+ if (token) {
570
+ return { Authorization: `Bearer ${token}` };
571
+ }
572
+ return {};
573
+ }
513
574
  };
514
575
  return service;
515
576
  }
@@ -575,6 +636,55 @@ function createMockOrdersAdapter() {
575
636
  }
576
637
  };
577
638
  }
639
+ function mapOrderLine(item) {
640
+ var _a;
641
+ return {
642
+ id: item.id,
643
+ title: item.title,
644
+ quantity: item.quantity,
645
+ variantImage: ((_a = item.variantImage) == null ? void 0 : _a.url) ? { url: item.variantImage.url, altText: item.variantImage.altText ?? void 0 } : null,
646
+ customAttributes: item.customAttributes
647
+ };
648
+ }
649
+ function mapOrder(order) {
650
+ return {
651
+ id: order.id,
652
+ createdAt: order.createdAt,
653
+ placedAt: order.placedAt,
654
+ currencyCode: order.currencyCode,
655
+ totalPrice: order.totalPrice,
656
+ financialStatus: order.financialStatus,
657
+ fulfillmentStatus: order.fulfillmentStatus,
658
+ lines: order.items.map(mapOrderLine),
659
+ customAttributes: order.customAttributes
660
+ };
661
+ }
662
+ function createApiOrdersAdapter(fetcher) {
663
+ return {
664
+ async getOrders(params) {
665
+ const query = new URLSearchParams();
666
+ if ((params == null ? void 0 : params.limit) != null) query.set("limit", String(params.limit));
667
+ if ((params == null ? void 0 : params.after) != null) query.set("after", params.after);
668
+ if ((params == null ? void 0 : params.before) != null) query.set("before", params.before);
669
+ if ((params == null ? void 0 : params.query) != null) query.set("query", params.query);
670
+ if ((params == null ? void 0 : params.includeOneTime) != null) query.set("includeOneTime", String(params.includeOneTime));
671
+ if ((params == null ? void 0 : params.sort) != null) query.set("sort", params.sort);
672
+ const qs = query.toString();
673
+ const res = await fetcher(`/customer/v1/orders${qs ? `?${qs}` : ""}`);
674
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
675
+ const body = await res.json();
676
+ return {
677
+ items: body.data.map(mapOrder),
678
+ pageInfo: {
679
+ hasNextPage: body.hasNextPage,
680
+ hasPrevPage: body.hasPrevPage,
681
+ startCursor: body.startCursor,
682
+ endCursor: body.endCursor
683
+ }
684
+ };
685
+ }
686
+ };
687
+ }
578
688
  function createOrdersService(adapter) {
579
689
  return {
580
690
  async getOrders(params) {
@@ -594,7 +704,7 @@ function createMockProductAdapter() {
594
704
  id: "sample-1",
595
705
  title: "Hydrating Gel Cleanser for Face and Eye Area",
596
706
  featuredImage: {
597
- url: "https://via.placeholder.com/400x400?text=Product+1"
707
+ url: "https://placehold.co/400x400?text=Product+1"
598
708
  },
599
709
  hasOnlyDefaultVariant: true,
600
710
  variants: [
@@ -602,7 +712,7 @@ function createMockProductAdapter() {
602
712
  id: "sample-1-1",
603
713
  title: "150 ml bottle",
604
714
  image: {
605
- url: "https://via.placeholder.com/400x400?text=Product+1"
715
+ url: "https://placehold.co/400x400?text=Product+1"
606
716
  },
607
717
  price: {
608
718
  amount: "12.49",
@@ -628,7 +738,7 @@ function createMockProductAdapter() {
628
738
  id: "sample-2",
629
739
  title: "More COLLAGEN",
630
740
  featuredImage: {
631
- url: "https://via.placeholder.com/400x400?text=Product+2"
741
+ url: "https://placehold.co/400x400?text=Product+2"
632
742
  },
633
743
  hasOnlyDefaultVariant: true,
634
744
  variants: [
@@ -636,7 +746,7 @@ function createMockProductAdapter() {
636
746
  id: "sample-2-1",
637
747
  title: "blackcurrant",
638
748
  image: {
639
- url: "https://via.placeholder.com/400x400?text=Product+2"
749
+ url: "https://placehold.co/400x400?text=Product+2"
640
750
  },
641
751
  price: {
642
752
  amount: "12.49",
@@ -657,7 +767,7 @@ function createMockProductAdapter() {
657
767
  id: "sample-3",
658
768
  title: "Gentle deodorant for underarms and bust",
659
769
  featuredImage: {
660
- url: "https://via.placeholder.com/400x400?text=Product+3"
770
+ url: "https://placehold.co/400x400?text=Product+3"
661
771
  },
662
772
  hasOnlyDefaultVariant: false,
663
773
  variants: [
@@ -665,7 +775,7 @@ function createMockProductAdapter() {
665
775
  id: "sample-3-1",
666
776
  title: "Green tea and jasmine",
667
777
  image: {
668
- url: "https://via.placeholder.com/400x400?text=Product+3a"
778
+ url: "https://placehold.co/400x400?text=Product+3a"
669
779
  },
670
780
  price: {
671
781
  amount: "6.7",
@@ -683,7 +793,7 @@ function createMockProductAdapter() {
683
793
  id: "sample-3-2",
684
794
  title: "Lavender and mint",
685
795
  image: {
686
- url: "https://via.placeholder.com/400x400?text=Product+3b"
796
+ url: "https://placehold.co/400x400?text=Product+3b"
687
797
  },
688
798
  price: {
689
799
  amount: "7.5",
@@ -715,6 +825,48 @@ function createMockProductAdapter() {
715
825
  }
716
826
  };
717
827
  }
828
+ function toAvailableProduct(product) {
829
+ return {
830
+ id: product.id,
831
+ title: product.title,
832
+ description: `Sample description for ${product.title}.`,
833
+ featuredImage: product.featuredImage ?? void 0,
834
+ hasOnlyDefaultVariant: product.hasOnlyDefaultVariant,
835
+ variants: product.variants.map((variant) => {
836
+ var _a, _b, _c;
837
+ const base = Number.parseFloat(variant.price.amount);
838
+ const sub = (_b = (_a = variant.purchaseOptions) == null ? void 0 : _a[0]) == null ? void 0 : _b.price.amount;
839
+ const current = sub == null ? base : Number.parseFloat(sub);
840
+ return {
841
+ id: variant.id,
842
+ availableForSale: true,
843
+ title: variant.title,
844
+ currentPrice: { amount: current, currencyCode: variant.price.currencyCode },
845
+ basePrice: { amount: base, currencyCode: variant.price.currencyCode },
846
+ image: variant.image ?? void 0,
847
+ recurring: (((_c = variant.purchaseOptions) == null ? void 0 : _c.length) ?? 0) > 0
848
+ };
849
+ })
850
+ };
851
+ }
852
+ const sampleBox = {
853
+ id: "sample-box-1",
854
+ title: "Build Your Own Box",
855
+ minProductsCount: 3,
856
+ parentProduct: "sample-1",
857
+ tiers: [
858
+ { value: 5, threshold: 3 },
859
+ { value: 10, threshold: 5 }
860
+ ],
861
+ variants: products.map((product) => ({
862
+ id: product.id,
863
+ variants: product.variants.map((variant) => ({ id: variant.id }))
864
+ })),
865
+ freeVariants: [],
866
+ thresholdType: "LINES_COUNT",
867
+ type: "PERCENTAGE",
868
+ restrictQuantityToTiers: false
869
+ };
718
870
  return {
719
871
  async getById(id) {
720
872
  await new Promise((resolve) => setTimeout(resolve, 100));
@@ -728,6 +880,27 @@ function createMockProductAdapter() {
728
880
  );
729
881
  return product == null ? null : processProduct(product);
730
882
  },
883
+ async getProductsByIds({ ids }) {
884
+ await new Promise((resolve) => setTimeout(resolve, 100));
885
+ return products.filter((product) => ids.includes(product.id)).map(({ variants: _variants, ...product }) => product);
886
+ },
887
+ async getBoxVariants({ ids }) {
888
+ await new Promise((resolve) => setTimeout(resolve, 100));
889
+ return products.filter((product) => ids.includes(product.id)).map(toAvailableProduct);
890
+ },
891
+ async getAvailableProducts({ q }) {
892
+ await new Promise((resolve) => setTimeout(resolve, 100));
893
+ const queryLower = q == null ? void 0 : q.toLowerCase();
894
+ return products.filter(
895
+ (product) => !queryLower || product.title.toLowerCase().includes(queryLower) || product.variants.some(
896
+ (variant) => variant.title.toLowerCase().includes(queryLower)
897
+ )
898
+ ).map(toAvailableProduct);
899
+ },
900
+ async getBox({ id }) {
901
+ await new Promise((resolve) => setTimeout(resolve, 100));
902
+ return id === sampleBox.id ? sampleBox : null;
903
+ },
731
904
  async search(opts) {
732
905
  await new Promise((resolve) => setTimeout(resolve, 100));
733
906
  let filteredProducts = [...products];
@@ -768,6 +941,185 @@ function createMockProductAdapter() {
768
941
  }
769
942
  };
770
943
  }
944
+ function mapVariant(variant, currencyCode) {
945
+ var _a;
946
+ return {
947
+ id: variant.id,
948
+ title: variant.title,
949
+ image: variant.image == null ? null : { url: variant.image.url, altText: variant.image.altText ?? void 0 },
950
+ price: { amount: variant.price.toFixed(2), currencyCode },
951
+ ...((_a = variant.purchaseOptions) == null ? void 0 : _a.length) ? {
952
+ purchaseOptions: variant.purchaseOptions.map((opt) => ({
953
+ name: opt.name,
954
+ price: { amount: opt.price.toFixed(2), currencyCode },
955
+ interval: opt.interval
956
+ }))
957
+ } : {}
958
+ };
959
+ }
960
+ function mapProduct(product, currencyCode) {
961
+ return {
962
+ id: product.id,
963
+ title: product.title,
964
+ featuredImage: product.image == null ? null : { url: product.image.url, altText: product.image.altText ?? void 0 },
965
+ hasOnlyDefaultVariant: product.variants.length <= 1,
966
+ variants: {
967
+ items: product.variants.map((v2) => mapVariant(v2, currencyCode)),
968
+ pageInfo: {
969
+ hasNextPage: false,
970
+ hasPrevPage: false,
971
+ startCursor: null,
972
+ endCursor: null
973
+ }
974
+ },
975
+ collections: product.collections
976
+ };
977
+ }
978
+ function createApiProductAdapter(fetcher, opts = { currencyCode: "USD" }) {
979
+ const { currencyCode } = opts;
980
+ async function fetchProducts(params) {
981
+ const query = params.toString() ? `?${params.toString()}` : "";
982
+ const res = await fetcher(`/customer/v1/products${query}`);
983
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
984
+ return res.json();
985
+ }
986
+ return {
987
+ async getById(id) {
988
+ try {
989
+ const params = new URLSearchParams({ query: `id:"${id}"`, limit: "1" });
990
+ const body = await fetchProducts(params);
991
+ return body.data.length > 0 ? mapProduct(body.data[0], currencyCode) : null;
992
+ } catch {
993
+ return null;
994
+ }
995
+ },
996
+ async getByVariantId(variantId) {
997
+ try {
998
+ const params = new URLSearchParams({
999
+ query: `variants.id:"${variantId}"`,
1000
+ limit: "1"
1001
+ });
1002
+ const body = await fetchProducts(params);
1003
+ return body.data.length > 0 ? mapProduct(body.data[0], currencyCode) : null;
1004
+ } catch {
1005
+ return null;
1006
+ }
1007
+ },
1008
+ async search(opts2) {
1009
+ try {
1010
+ const params = new URLSearchParams();
1011
+ if (opts2 == null ? void 0 : opts2.query) {
1012
+ params.set("query", `"${opts2.query.trim().replace(/"/g, '\\"')}"`);
1013
+ }
1014
+ if (opts2 == null ? void 0 : opts2.pagination) {
1015
+ if ("after" in opts2.pagination)
1016
+ params.set("after", opts2.pagination.after);
1017
+ else if ("before" in opts2.pagination)
1018
+ params.set("before", opts2.pagination.before);
1019
+ }
1020
+ const body = await fetchProducts(params);
1021
+ return {
1022
+ items: body.data.map((p2) => mapProduct(p2, currencyCode)),
1023
+ pageInfo: {
1024
+ hasNextPage: body.hasNextPage,
1025
+ hasPrevPage: body.hasPrevPage,
1026
+ startCursor: body.startCursor,
1027
+ endCursor: body.endCursor
1028
+ }
1029
+ };
1030
+ } catch {
1031
+ return {
1032
+ items: [],
1033
+ pageInfo: {
1034
+ hasNextPage: false,
1035
+ hasPrevPage: false,
1036
+ startCursor: null,
1037
+ endCursor: null
1038
+ }
1039
+ };
1040
+ }
1041
+ },
1042
+ async getProductsByIds({ ids }) {
1043
+ const params = new URLSearchParams();
1044
+ for (const id of ids) params.append("ids", id);
1045
+ const res = await fetcher(`/customer/products/by-ids?${params.toString()}`);
1046
+ if (!res.ok) return [];
1047
+ const data = await res.json();
1048
+ return (data ?? []).map((p2) => ({
1049
+ id: p2.id,
1050
+ title: p2.title ?? "",
1051
+ featuredImage: p2.featuredImage ? { url: p2.featuredImage.url, altText: p2.featuredImage.altText } : null,
1052
+ hasOnlyDefaultVariant: true,
1053
+ collections: []
1054
+ }));
1055
+ },
1056
+ async getBoxVariants({ ids, subscriptionId }) {
1057
+ const params = new URLSearchParams();
1058
+ for (const id of ids) params.append("ids", id);
1059
+ params.set("subscriptionId", subscriptionId);
1060
+ const res = await fetcher(`/customer/products/variants?${params.toString()}`);
1061
+ if (!res.ok) return [];
1062
+ const data = await res.json();
1063
+ return (data ?? []).map((p2) => {
1064
+ var _a;
1065
+ return {
1066
+ id: p2.id,
1067
+ title: p2.title,
1068
+ description: p2.description ?? "",
1069
+ featuredImage: p2.featuredImage,
1070
+ hasOnlyDefaultVariant: p2.hasOnlyDefaultVariant ?? (((_a = p2.variants) == null ? void 0 : _a.length) ?? 0) <= 1,
1071
+ variants: (p2.variants ?? []).map((v2) => ({
1072
+ id: v2.id,
1073
+ availableForSale: v2.availableForSale,
1074
+ title: v2.title,
1075
+ currentPrice: v2.currentPrice,
1076
+ basePrice: v2.basePrice,
1077
+ image: v2.image,
1078
+ recurring: v2.recurring
1079
+ }))
1080
+ };
1081
+ });
1082
+ },
1083
+ async getAvailableProducts({ subscriptionId, lineId, q, mode, action, pagination }) {
1084
+ const params = new URLSearchParams();
1085
+ params.set("action", action);
1086
+ if (q) params.set("q", q);
1087
+ if (mode) params.set("mode", mode);
1088
+ if (pagination && "after" in pagination) params.set("after", pagination.after);
1089
+ if (pagination && "before" in pagination) params.set("before", pagination.before);
1090
+ const res = await fetcher(
1091
+ `/customer/subscriptions/${subscriptionId}/lines/${lineId}/available-products?${params.toString()}`
1092
+ );
1093
+ if (!res.ok) return [];
1094
+ const data = await res.json();
1095
+ return (data ?? []).map((p2) => {
1096
+ var _a;
1097
+ return {
1098
+ id: p2.id,
1099
+ title: p2.title,
1100
+ description: p2.description ?? "",
1101
+ featuredImage: p2.featuredImage,
1102
+ hasOnlyDefaultVariant: p2.hasOnlyDefaultVariant ?? (((_a = p2.variants) == null ? void 0 : _a.length) ?? 0) <= 1,
1103
+ variants: (p2.variants ?? []).map((v2) => ({
1104
+ id: v2.id,
1105
+ availableForSale: v2.availableForSale,
1106
+ title: v2.title,
1107
+ currentPrice: v2.currentPrice,
1108
+ basePrice: v2.basePrice,
1109
+ image: v2.image,
1110
+ recurring: v2.recurring
1111
+ }))
1112
+ };
1113
+ });
1114
+ },
1115
+ async getBox({ id }) {
1116
+ const res = await fetcher(`/customer/boxes/${id}`);
1117
+ if (!res.ok) return null;
1118
+ const data = await res.json();
1119
+ return { id, ...data };
1120
+ }
1121
+ };
1122
+ }
771
1123
  const ProductServiceContext = createContext("services/product");
772
1124
  function createProductService(adapter) {
773
1125
  return {
@@ -779,7 +1131,11 @@ function createProductService(adapter) {
779
1131
  },
780
1132
  async search(opts) {
781
1133
  return adapter.search(opts);
782
- }
1134
+ },
1135
+ getProductsByIds: (ids) => adapter.getProductsByIds({ ids }),
1136
+ getBoxVariants: (ids, subscriptionId) => adapter.getBoxVariants({ ids, subscriptionId }),
1137
+ getAvailableProducts: (params) => adapter.getAvailableProducts(params),
1138
+ getBox: (id) => adapter.getBox({ id })
783
1139
  };
784
1140
  }
785
1141
  function routeNameTableFromPages(pages) {
@@ -833,7 +1189,85 @@ function createMockRouterService() {
833
1189
  }
834
1190
  }).routerService;
835
1191
  }
836
- function createMockSubscriptionAdapter(options = {}) {
1192
+ const DISCOUNT_REJECTION_REASONS = [
1193
+ "INVALID_CODE",
1194
+ "INACTIVE",
1195
+ "NOT_APPLICABLE_TO_CUSTOMER",
1196
+ "ALREADY_ADDED",
1197
+ "ALREADY_USED",
1198
+ "NOT_APPLICABLE_TO_PRODUCTS",
1199
+ "NOT_APPLICABLE_TO_SUBSCRIPTION",
1200
+ "INVALID_COMBINATION",
1201
+ "QUANTITY_REQUIREMENT_NOT_MET"
1202
+ ];
1203
+ function toAttributeArray(next) {
1204
+ return Object.entries(next).map(([key, value]) => ({ key, value }));
1205
+ }
1206
+ function createMockSubscriptionAdapter(options = {}) {
1207
+ const richBase = (id, itemId, renewalId) => ({
1208
+ id,
1209
+ customerId: "customer_1",
1210
+ items: [
1211
+ {
1212
+ id: itemId,
1213
+ // Aligned with the mock product catalog (createMockProductAdapter):
1214
+ // sample-3 "Gentle deodorant", variant sample-3-1 "Green tea and jasmine",
1215
+ // recurring price 5.36 EUR (10% off → 4.82 with SAVE10).
1216
+ productId: "sample-3",
1217
+ variantId: "sample-3-1",
1218
+ quantity: 2,
1219
+ price: { amount: "5.36", currencyCode: "EUR" },
1220
+ nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15"),
1221
+ isUpsell: false,
1222
+ isVirtual: false,
1223
+ nextBillingDate: "2025-11-15T00:00:00Z",
1224
+ currentPrice: { amount: "5.36", currencyCode: "EUR" },
1225
+ lineDiscountedPrice: { amount: "4.82", currencyCode: "EUR" },
1226
+ variantAvailableForSale: true,
1227
+ title: "Gentle deodorant for underarms and bust",
1228
+ variantTitle: "Green tea and jasmine",
1229
+ variantImage: { url: "https://placehold.co/400x400?text=Product+3a" },
1230
+ billingPolicy: { intervalCount: 1, interval: "MONTH" },
1231
+ deliveryPolicy: { intervalCount: 1, interval: "MONTH" }
1232
+ }
1233
+ ],
1234
+ discounts: [
1235
+ {
1236
+ id: "discount_1",
1237
+ type: "percentage",
1238
+ value: 10,
1239
+ code: "SAVE10",
1240
+ description: "10% off subscription"
1241
+ }
1242
+ ],
1243
+ status: "active",
1244
+ nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15"),
1245
+ frequency: "1 MONTH",
1246
+ createdAt: "2024-10-01T00:00:00Z",
1247
+ updatedAt: "2024-10-14T00:00:00Z",
1248
+ billingPolicy: { intervalCount: 1, interval: "MONTH", minCycles: 6 },
1249
+ deliveryPolicy: {
1250
+ intervalCount: 1,
1251
+ interval: "MONTH",
1252
+ anchors: [{ day: 15, type: "MONTHDAY" }]
1253
+ },
1254
+ nextBillingDate: "2025-11-15T00:00:00Z",
1255
+ nextBillingAttempt: { date: "2025-11-15T00:00:00Z", attempt: 1, ready: true },
1256
+ lastBillingAttempt: { date: "2025-10-15T00:00:00Z", ready: true },
1257
+ nextFulfillment: "2025-11-17T00:00:00Z",
1258
+ estimatedOrderDate: "2025-11-13T00:00:00Z",
1259
+ customerPaymentMethod: {
1260
+ id: "pm_1",
1261
+ instrument: { type: "credit_card", brand: "visa", maskedNumber: "************4242" },
1262
+ active: true,
1263
+ isRecurring: true
1264
+ },
1265
+ requiredBillingCyclesLeft: 3,
1266
+ renewal: { id: renewalId, isInActiveProcessState: false },
1267
+ lastPaymentStatus: "succeeded",
1268
+ pausePeriod: null,
1269
+ isEditable: true
1270
+ });
837
1271
  const mockSubscriptions = [
838
1272
  {
839
1273
  id: "sub_1",
@@ -841,14 +1275,27 @@ function createMockSubscriptionAdapter(options = {}) {
841
1275
  items: [
842
1276
  {
843
1277
  id: "item_1",
844
- productId: "product_1",
845
- variantId: "variant_1",
1278
+ // Aligned with the mock product catalog (createMockProductAdapter):
1279
+ // sample-1 "Hydrating Gel Cleanser" (default variant), recurring price
1280
+ // 9.99 EUR (10% off → 8.99 with SAVE10).
1281
+ productId: "sample-1",
1282
+ variantId: "sample-1-1",
846
1283
  quantity: 2,
847
- price: { amount: "29.99", currencyCode: "USD" },
1284
+ price: { amount: "9.99", currencyCode: "EUR" },
848
1285
  nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15"),
1286
+ isUpsell: false,
1287
+ isVirtual: false,
1288
+ nextBillingDate: "2025-11-15T00:00:00Z",
1289
+ currentPrice: { amount: "9.99", currencyCode: "EUR" },
1290
+ lineDiscountedPrice: { amount: "8.99", currencyCode: "EUR" },
1291
+ variantAvailableForSale: true,
1292
+ title: "Hydrating Gel Cleanser for Face and Eye Area",
1293
+ variantImage: { url: "https://placehold.co/400x400?text=Product+1" },
1294
+ billingPolicy: { intervalCount: 1, interval: "MONTH" },
1295
+ deliveryPolicy: { intervalCount: 1, interval: "MONTH" },
849
1296
  customAttributes: [
850
- { key: "roast_level", value: "medium" },
851
- { key: "grind_type", value: "whole_bean" }
1297
+ { key: "skin_type", value: "combination" },
1298
+ { key: "fragrance", value: "fragrance_free" }
852
1299
  ]
853
1300
  }
854
1301
  ],
@@ -866,9 +1313,31 @@ function createMockSubscriptionAdapter(options = {}) {
866
1313
  frequency: "1 MONTH",
867
1314
  createdAt: "2024-10-01T00:00:00Z",
868
1315
  updatedAt: "2024-10-14T00:00:00Z",
1316
+ billingPolicy: { intervalCount: 1, interval: "MONTH", minCycles: 6 },
1317
+ deliveryPolicy: {
1318
+ intervalCount: 1,
1319
+ interval: "MONTH",
1320
+ anchors: [{ day: 15, type: "MONTHDAY" }]
1321
+ },
1322
+ nextBillingDate: "2025-11-15T00:00:00Z",
1323
+ nextBillingAttempt: { date: "2025-11-15T00:00:00Z", attempt: 1, ready: true },
1324
+ lastBillingAttempt: { date: "2025-10-15T00:00:00Z", ready: true },
1325
+ nextFulfillment: "2025-11-17T00:00:00Z",
1326
+ estimatedOrderDate: "2025-11-13T00:00:00Z",
1327
+ customerPaymentMethod: {
1328
+ id: "pm_1",
1329
+ instrument: { type: "credit_card", brand: "visa", maskedNumber: "************4242" },
1330
+ active: true,
1331
+ isRecurring: true
1332
+ },
1333
+ requiredBillingCyclesLeft: 3,
1334
+ renewal: { id: "ren_1", isInActiveProcessState: false },
1335
+ lastPaymentStatus: "succeeded",
1336
+ pausePeriod: null,
1337
+ isEditable: true,
869
1338
  customAttributes: [
870
1339
  { key: "subscription_tier", value: "premium" },
871
- { key: "gift_message", value: "Enjoy your coffee!" }
1340
+ { key: "gift_message", value: "Enjoy your skincare routine!" }
872
1341
  ]
873
1342
  },
874
1343
  {
@@ -877,11 +1346,17 @@ function createMockSubscriptionAdapter(options = {}) {
877
1346
  items: [
878
1347
  {
879
1348
  id: "item_2",
880
- productId: "product_2",
881
- variantId: "variant_2",
1349
+ // Aligned with the mock product catalog (createMockProductAdapter):
1350
+ // sample-2 "More COLLAGEN" (default variant), recurring price 10.99 EUR.
1351
+ productId: "sample-2",
1352
+ variantId: "sample-2-1",
882
1353
  quantity: 1,
883
- price: { amount: "49.99", currencyCode: "USD" },
1354
+ price: { amount: "10.99", currencyCode: "EUR" },
884
1355
  nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-20"),
1356
+ title: "More COLLAGEN",
1357
+ variantImage: { url: "https://placehold.co/400x400?text=Product+2" },
1358
+ billingPolicy: { intervalCount: 2, interval: "WEEK" },
1359
+ deliveryPolicy: { intervalCount: 2, interval: "WEEK" },
885
1360
  customAttributes: []
886
1361
  }
887
1362
  ],
@@ -892,17 +1367,66 @@ function createMockSubscriptionAdapter(options = {}) {
892
1367
  createdAt: "2024-09-15T00:00:00Z",
893
1368
  updatedAt: "2024-10-10T00:00:00Z",
894
1369
  customAttributes: []
1370
+ },
1371
+ // Per-status fixtures: groundwork for the deferred editor preview picker
1372
+ // (docs/superpowers/specs/2026-06-17-renewalsummary-parity-and-mock-groundwork-design.md §9).
1373
+ // Not consumed yet — the editor still seeds the active sub_1 via getDefaultSubscriptionId().
1374
+ {
1375
+ ...richBase("sub_paused", "item_paused", "ren_paused"),
1376
+ status: "paused",
1377
+ pausePeriod: { start: "2025-11-01T00:00:00Z", end: "2026-01-01T00:00:00Z" }
1378
+ },
1379
+ {
1380
+ ...richBase("sub_canceled", "item_canceled", "ren_canceled"),
1381
+ status: "canceled",
1382
+ pausePeriod: null
1383
+ },
1384
+ {
1385
+ ...richBase("sub_failed", "item_failed", "ren_failed"),
1386
+ status: "failed",
1387
+ lastPaymentStatus: "failed",
1388
+ pausePeriod: null
1389
+ },
1390
+ {
1391
+ ...richBase("sub_expired", "item_expired", "ren_expired"),
1392
+ status: "expired",
1393
+ pausePeriod: null
1394
+ }
1395
+ ];
1396
+ const mockUpsellProducts = [
1397
+ {
1398
+ id: "upsell-1",
1399
+ variantId: "upsell-v-1",
1400
+ title: "Retinol Night Cream",
1401
+ imageUrl: "https://images.unsplash.com/photo-1617897903246-719242758050?w=200",
1402
+ price: { amount: "38.00", currencyCode: "USD" },
1403
+ description: "Anti-aging night treatment"
1404
+ },
1405
+ {
1406
+ id: "upsell-2",
1407
+ variantId: "upsell-v-2",
1408
+ title: "SPF 50 Day Shield",
1409
+ imageUrl: "https://images.unsplash.com/photo-1556228578-8c89e6adf883?w=200",
1410
+ price: { amount: "24.00", currencyCode: "USD" },
1411
+ description: "Lightweight daily sun protection"
895
1412
  }
896
1413
  ];
897
1414
  return {
898
1415
  async getById(id) {
899
1416
  await new Promise((resolve) => setTimeout(resolve, 300));
900
- const subscription = mockSubscriptions.find((sub) => sub.id === id) || null;
901
- return { _tag: "Success", data: subscription };
1417
+ const subscription = mockSubscriptions.find((sub) => sub.id === id);
1418
+ return {
1419
+ _tag: "Success",
1420
+ data: subscription ? {
1421
+ ...subscription,
1422
+ items: subscription.items.map((item) => ({ ...item })),
1423
+ discounts: subscription.discounts.map((d3) => ({ ...d3 }))
1424
+ } : null
1425
+ };
902
1426
  },
903
1427
  async search(options2 = {}) {
904
1428
  await new Promise((resolve) => setTimeout(resolve, 300));
905
- const pageSize = 1;
1429
+ const pageSize = 2;
906
1430
  const allItems = mockSubscriptions;
907
1431
  let startIndex = 0;
908
1432
  let endIndex = pageSize;
@@ -918,29 +1442,108 @@ function createMockSubscriptionAdapter(options = {}) {
918
1442
  }
919
1443
  }
920
1444
  const pageItems = allItems.slice(startIndex, endIndex);
921
- const paginatedList = {
922
- items: pageItems.map((sub) => ({
923
- ...sub,
924
- items: sub.items.map((item) => ({ ...item })),
925
- discounts: sub.discounts.map((discount) => ({ ...discount }))
926
- })),
927
- pageInfo: {
928
- hasNextPage: endIndex < allItems.length,
929
- hasPrevPage: startIndex > 0,
930
- startCursor: pageItems.length > 0 ? startIndex.toString() : null,
931
- endCursor: pageItems.length > 0 ? (endIndex - 1).toString() : null
1445
+ return {
1446
+ _tag: "Success",
1447
+ data: {
1448
+ items: pageItems.map((sub) => ({
1449
+ ...sub,
1450
+ items: sub.items.map((item) => ({ ...item })),
1451
+ discounts: sub.discounts.map((discount) => ({ ...discount }))
1452
+ })),
1453
+ pageInfo: {
1454
+ hasNextPage: endIndex < allItems.length,
1455
+ hasPrevPage: startIndex > 0,
1456
+ startCursor: pageItems.length > 0 ? startIndex.toString() : null,
1457
+ endCursor: pageItems.length > 0 ? (endIndex - 1).toString() : null
1458
+ }
932
1459
  }
933
1460
  };
934
- return { _tag: "Success", data: paginatedList };
935
1461
  },
936
- async addItem() {
1462
+ async addItem({ subscriptionId, item }) {
1463
+ var _a, _b, _c, _d, _e, _f, _g;
1464
+ await new Promise((resolve) => setTimeout(resolve, 300));
1465
+ const subscription = mockSubscriptions.find((sub) => sub.id === subscriptionId);
1466
+ if (!subscription) {
1467
+ return { _tag: "Failure", error: "Subscription not found" };
1468
+ }
1469
+ const currencyCode = ((_a = subscription.items[0]) == null ? void 0 : _a.price.currencyCode) ?? "USD";
1470
+ const product = await ((_b = options.productAdapter) == null ? void 0 : _b.getByVariantId(item.variantId)) ?? await ((_c = options.productAdapter) == null ? void 0 : _c.getById(item.variantId)) ?? null;
1471
+ const variant = (product == null ? void 0 : product.variants.items.find((v2) => v2.id === item.variantId)) ?? (product == null ? void 0 : product.variants.items[0]);
1472
+ const linePrice = ((_e = (_d = variant == null ? void 0 : variant.purchaseOptions) == null ? void 0 : _d[0]) == null ? void 0 : _e.price) ?? (variant == null ? void 0 : variant.price);
1473
+ subscription.items.push({
1474
+ id: `item_${Date.now()}_${subscription.items.length}`,
1475
+ productId: (product == null ? void 0 : product.id) ?? "",
1476
+ variantId: item.variantId,
1477
+ quantity: item.quantity,
1478
+ price: linePrice ? { ...linePrice } : { amount: "0.00", currencyCode },
1479
+ ...linePrice ? { currentPrice: { ...linePrice } } : {},
1480
+ title: (product == null ? void 0 : product.title) ?? `Product ${item.variantId}`,
1481
+ ...variant && product && !product.hasOnlyDefaultVariant ? { variantTitle: variant.title } : {},
1482
+ ...(variant == null ? void 0 : variant.image) ? { variantImage: variant.image } : {},
1483
+ variantAvailableForSale: true,
1484
+ billingPolicy: ((_f = subscription.items[0]) == null ? void 0 : _f.billingPolicy) ?? subscription.billingPolicy ?? null,
1485
+ deliveryPolicy: ((_g = subscription.items[0]) == null ? void 0 : _g.deliveryPolicy) ?? subscription.deliveryPolicy ?? null
1486
+ });
1487
+ subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1488
+ return { _tag: "Success", data: void 0 };
1489
+ },
1490
+ async removeItem({ subscriptionId, itemId }) {
1491
+ await new Promise((resolve) => setTimeout(resolve, 300));
1492
+ const subscription = mockSubscriptions.find((sub) => sub.id === subscriptionId);
1493
+ if (!subscription) {
1494
+ return { _tag: "Failure", error: "Subscription not found" };
1495
+ }
1496
+ subscription.items = subscription.items.filter((i2) => i2.id !== itemId);
1497
+ subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1498
+ return { _tag: "Success", data: void 0 };
1499
+ },
1500
+ async updateItem({ subscriptionId, lineId, update }) {
1501
+ await new Promise((resolve) => setTimeout(resolve, 300));
1502
+ const subscription = mockSubscriptions.find((sub) => sub.id === subscriptionId);
1503
+ if (!subscription) {
1504
+ return { _tag: "Failure", error: "Subscription not found" };
1505
+ }
1506
+ const item = subscription.items.find((i2) => i2.id === lineId);
1507
+ if (!item) {
1508
+ return { _tag: "Failure", error: "Line item not found" };
1509
+ }
1510
+ if (update.variantId !== void 0) item.variantId = update.variantId;
1511
+ if (update.quantity !== void 0) item.quantity = update.quantity;
1512
+ if (update.billingPolicy !== void 0) item.billingPolicy = update.billingPolicy;
1513
+ subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1514
+ return { _tag: "Success", data: void 0 };
1515
+ },
1516
+ async updateItemAttributes({ subscriptionId, lineId, customAttributes }) {
1517
+ await new Promise((resolve) => setTimeout(resolve, 300));
1518
+ const subscription = mockSubscriptions.find((sub) => sub.id === subscriptionId);
1519
+ if (!subscription) {
1520
+ return { _tag: "Failure", error: "Subscription not found" };
1521
+ }
1522
+ const item = subscription.items.find((i2) => i2.id === lineId);
1523
+ if (!item) {
1524
+ return { _tag: "Failure", error: "Line item not found" };
1525
+ }
1526
+ item.customAttributes = toAttributeArray(customAttributes);
1527
+ subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1528
+ return { _tag: "Success", data: void 0 };
1529
+ },
1530
+ async updateAttributes({ subscriptionId, customAttributes }) {
937
1531
  await new Promise((resolve) => setTimeout(resolve, 300));
1532
+ const subscription = mockSubscriptions.find((sub) => sub.id === subscriptionId);
1533
+ if (!subscription) {
1534
+ return { _tag: "Failure", error: "Subscription not found" };
1535
+ }
1536
+ subscription.customAttributes = toAttributeArray(customAttributes);
1537
+ subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
938
1538
  return { _tag: "Success", data: void 0 };
939
1539
  },
940
- async removeItem() {
1540
+ async skipLineItem() {
941
1541
  await new Promise((resolve) => setTimeout(resolve, 300));
942
1542
  return { _tag: "Success", data: void 0 };
943
1543
  },
1544
+ async updateBoxItems() {
1545
+ return { _tag: "Success", data: void 0 };
1546
+ },
944
1547
  async addDiscount() {
945
1548
  await new Promise((resolve) => setTimeout(resolve, 300));
946
1549
  const behavior = options.addDiscountBehavior ?? { kind: "success" };
@@ -969,24 +1572,29 @@ function createMockSubscriptionAdapter(options = {}) {
969
1572
  (sub) => sub.id === params.subscriptionId
970
1573
  );
971
1574
  if (!subscription) {
972
- return {
973
- _tag: "Failure",
974
- error: "Subscription not found"
975
- };
1575
+ return { _tag: "Failure", error: "Subscription not found" };
976
1576
  }
977
1577
  subscription.nextDeliveryDate = new Date(params.newDate);
978
1578
  subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
979
- subscription.items.forEach((item) => {
980
- item.nextDeliveryDate = new Date(params.newDate);
981
- });
982
- return {
983
- _tag: "Success",
984
- data: {
985
- ...subscription,
986
- items: subscription.items.map((item) => ({ ...item })),
987
- discounts: subscription.discounts.map((discount) => ({ ...discount }))
988
- }
989
- };
1579
+ return { _tag: "Success", data: { ...subscription } };
1580
+ },
1581
+ async updateCustomAttributes(params) {
1582
+ await new Promise((resolve) => setTimeout(resolve, 300));
1583
+ const subscription = mockSubscriptions.find(
1584
+ (sub) => sub.id === params.subscriptionId
1585
+ );
1586
+ if (!subscription) {
1587
+ return { _tag: "Failure", error: "Subscription not found" };
1588
+ }
1589
+ const preserved = (subscription.customAttributes ?? []).filter(
1590
+ (attr) => attr.key.startsWith("_")
1591
+ );
1592
+ const next = Object.entries(params.customAttributes).map(
1593
+ ([key, value]) => ({ key, value })
1594
+ );
1595
+ subscription.customAttributes = [...preserved, ...next];
1596
+ subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1597
+ return { _tag: "Success", data: { ...subscription } };
990
1598
  },
991
1599
  async resume(params) {
992
1600
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -994,21 +1602,11 @@ function createMockSubscriptionAdapter(options = {}) {
994
1602
  (sub) => sub.id === params.subscriptionId
995
1603
  );
996
1604
  if (!subscription) {
997
- return {
998
- _tag: "Failure",
999
- error: "Subscription not found"
1000
- };
1605
+ return { _tag: "Failure", error: "Subscription not found" };
1001
1606
  }
1002
1607
  subscription.status = "active";
1003
1608
  subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1004
- return {
1005
- _tag: "Success",
1006
- data: {
1007
- ...subscription,
1008
- items: subscription.items.map((item) => ({ ...item })),
1009
- discounts: subscription.discounts.map((discount) => ({ ...discount }))
1010
- }
1011
- };
1609
+ return { _tag: "Success", data: { ...subscription } };
1012
1610
  },
1013
1611
  async pause(params) {
1014
1612
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -1016,21 +1614,11 @@ function createMockSubscriptionAdapter(options = {}) {
1016
1614
  (sub) => sub.id === params.subscriptionId
1017
1615
  );
1018
1616
  if (!subscription) {
1019
- return {
1020
- _tag: "Failure",
1021
- error: "Subscription not found"
1022
- };
1617
+ return { _tag: "Failure", error: "Subscription not found" };
1023
1618
  }
1024
1619
  subscription.status = "paused";
1025
1620
  subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1026
- return {
1027
- _tag: "Success",
1028
- data: {
1029
- ...subscription,
1030
- items: subscription.items.map((item) => ({ ...item })),
1031
- discounts: subscription.discounts.map((discount) => ({ ...discount }))
1032
- }
1033
- };
1621
+ return { _tag: "Success", data: { ...subscription } };
1034
1622
  },
1035
1623
  async reactivate(params) {
1036
1624
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -1038,21 +1626,11 @@ function createMockSubscriptionAdapter(options = {}) {
1038
1626
  (sub) => sub.id === params.subscriptionId
1039
1627
  );
1040
1628
  if (!subscription) {
1041
- return {
1042
- _tag: "Failure",
1043
- error: "Subscription not found"
1044
- };
1629
+ return { _tag: "Failure", error: "Subscription not found" };
1045
1630
  }
1046
1631
  subscription.status = "active";
1047
1632
  subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1048
- return {
1049
- _tag: "Success",
1050
- data: {
1051
- ...subscription,
1052
- items: subscription.items.map((item) => ({ ...item })),
1053
- discounts: subscription.discounts.map((discount) => ({ ...discount }))
1054
- }
1055
- };
1633
+ return { _tag: "Success", data: { ...subscription } };
1056
1634
  },
1057
1635
  async cancel(params) {
1058
1636
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -1060,21 +1638,11 @@ function createMockSubscriptionAdapter(options = {}) {
1060
1638
  (sub) => sub.id === params.subscriptionId
1061
1639
  );
1062
1640
  if (!subscription) {
1063
- return {
1064
- _tag: "Failure",
1065
- error: "Subscription not found"
1066
- };
1641
+ return { _tag: "Failure", error: "Subscription not found" };
1067
1642
  }
1068
1643
  subscription.status = "canceled";
1069
1644
  subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1070
- return {
1071
- _tag: "Success",
1072
- data: {
1073
- ...subscription,
1074
- items: subscription.items.map((item) => ({ ...item })),
1075
- discounts: subscription.discounts.map((discount) => ({ ...discount }))
1076
- }
1077
- };
1645
+ return { _tag: "Success", data: { ...subscription } };
1078
1646
  },
1079
1647
  async getAvailablePlans() {
1080
1648
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -1102,128 +1670,1460 @@ function createMockSubscriptionAdapter(options = {}) {
1102
1670
  ]
1103
1671
  };
1104
1672
  },
1673
+ async getLastOrder(params) {
1674
+ await new Promise((resolve) => setTimeout(resolve, 200));
1675
+ const subscription = mockSubscriptions.find((sub) => sub.id === params.subscriptionId);
1676
+ if (!subscription) return { _tag: "Success", data: null };
1677
+ return {
1678
+ _tag: "Success",
1679
+ data: {
1680
+ id: `mock_last_order_${params.subscriptionId}`,
1681
+ createdAt: "2026-04-22T12:00:00Z"
1682
+ }
1683
+ };
1684
+ },
1685
+ async renewNow(_params) {
1686
+ await new Promise((resolve) => setTimeout(resolve, 300));
1687
+ return { _tag: "Success", data: { result: "success" } };
1688
+ },
1689
+ async changeFrequency(params) {
1690
+ await new Promise((resolve) => setTimeout(resolve, 300));
1691
+ const subscription = mockSubscriptions.find((sub) => sub.id === params.subscriptionId);
1692
+ if (!subscription) {
1693
+ return { _tag: "Failure", error: "Subscription not found" };
1694
+ }
1695
+ if (typeof params.plan === "string") {
1696
+ subscription.frequency = `1 ${params.plan}`;
1697
+ } else {
1698
+ subscription.frequency = `${params.plan.intervalCount} ${params.plan.interval}`;
1699
+ if (subscription.billingPolicy) {
1700
+ subscription.billingPolicy = { ...subscription.billingPolicy, ...params.plan };
1701
+ }
1702
+ if (subscription.deliveryPolicy) {
1703
+ subscription.deliveryPolicy = { ...subscription.deliveryPolicy, ...params.plan };
1704
+ }
1705
+ }
1706
+ subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1707
+ return {
1708
+ _tag: "Success",
1709
+ data: {
1710
+ ...subscription,
1711
+ items: subscription.items.map((item) => ({ ...item })),
1712
+ discounts: subscription.discounts.map((d3) => ({ ...d3 }))
1713
+ }
1714
+ };
1715
+ },
1105
1716
  async getDefaultSubscriptionId() {
1106
1717
  var _a;
1107
1718
  await new Promise((resolve) => setTimeout(resolve, 300));
1108
1719
  return { _tag: "Success", data: ((_a = mockSubscriptions[0]) == null ? void 0 : _a.id) ?? null };
1720
+ },
1721
+ async getUpsellProducts() {
1722
+ await new Promise((resolve) => setTimeout(resolve, 300));
1723
+ return { _tag: "Success", data: [...mockUpsellProducts] };
1724
+ },
1725
+ async addUpsellProduct() {
1726
+ await new Promise((resolve) => setTimeout(resolve, 400));
1727
+ return { _tag: "Success", data: void 0 };
1109
1728
  }
1110
1729
  };
1111
1730
  }
1112
- const SubscriptionServiceContext = createContext("services/subscription");
1113
- function getCurrentSubscriptionId() {
1114
- if (typeof window === "undefined") {
1115
- return null;
1116
- }
1117
- const pathname = window.location.pathname;
1118
- const segments = pathname.split("/").filter(Boolean);
1119
- const subscriptionIndex = segments.findIndex(
1120
- (segment) => segment === "subscriptions" || segment === "subscription"
1121
- );
1122
- if (subscriptionIndex !== -1 && segments[subscriptionIndex + 1]) {
1123
- return segments[subscriptionIndex + 1];
1124
- }
1125
- const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1126
- const lastSegment = segments[segments.length - 1];
1127
- if (lastSegment && uuidPattern.test(lastSegment)) {
1128
- return lastSegment;
1129
- }
1130
- return null;
1731
+ function normalizeInstrumentType(type) {
1732
+ return type.replace(/-/g, "_");
1131
1733
  }
1132
- function createSubscriptionService(adapter) {
1133
- const current = d(null);
1134
- const discountsChangedAtSignal = d(0);
1135
- const itemsChangedAtSignal = d(0);
1136
- async function refreshCurrent(subscriptionId) {
1137
- const result = await adapter.getById(subscriptionId);
1138
- if (result._tag === "Success" && result.data != null) {
1139
- current.value = result.data;
1140
- }
1734
+ function mapPaymentMethod(rawPm) {
1735
+ if (!rawPm) return null;
1736
+ if (typeof rawPm === "string") {
1737
+ return { id: rawPm };
1141
1738
  }
1142
- async function refreshAfterDiscountChange(subscriptionId) {
1143
- await refreshCurrent(subscriptionId);
1144
- discountsChangedAtSignal.value = discountsChangedAtSignal.value + 1;
1145
- }
1146
- async function refreshAfterItemChange(subscriptionId) {
1147
- await refreshCurrent(subscriptionId);
1148
- itemsChangedAtSignal.value = itemsChangedAtSignal.value + 1;
1149
- }
1150
- function subscriptionIdFromCurrent() {
1151
- var _a;
1152
- return ((_a = current.value) == null ? void 0 : _a.id) ?? null;
1153
- }
1154
- const service = {
1155
- search: adapter.search,
1156
- getById: adapter.getById,
1157
- getCurrentSubscriptionId,
1158
- addItem: async (subscriptionId, item) => {
1159
- const result = await adapter.addItem({ subscriptionId, item });
1160
- if (result._tag === "Success") {
1161
- await refreshAfterItemChange(subscriptionId);
1162
- }
1163
- return result;
1164
- },
1165
- removeItem: async (subscriptionId, itemId) => {
1166
- const result = await adapter.removeItem({ subscriptionId, itemId });
1167
- if (result._tag === "Success") {
1168
- await refreshAfterItemChange(subscriptionId);
1739
+ return {
1740
+ id: rawPm.id ?? "",
1741
+ ...rawPm.instrument != null ? {
1742
+ instrument: {
1743
+ ...rawPm.instrument,
1744
+ type: normalizeInstrumentType(rawPm.instrument.type)
1169
1745
  }
1170
- return result;
1171
- },
1172
- addDiscount: async (code, subscriptionId) => {
1173
- const id = subscriptionId ?? subscriptionIdFromCurrent();
1174
- if (id == null) {
1746
+ } : {},
1747
+ ...rawPm.active != null ? { active: rawPm.active } : {},
1748
+ ...rawPm.isRecurring != null ? { isRecurring: rawPm.isRecurring } : {}
1749
+ };
1750
+ }
1751
+ function mapSubscription(raw) {
1752
+ var _a, _b;
1753
+ const status = raw.status === "merged" ? "canceled" : raw.status;
1754
+ const deliveryPolicy = raw.deliveryPolicy ?? ((_b = (_a = raw.items) == null ? void 0 : _a[0]) == null ? void 0 : _b.deliveryPolicy);
1755
+ const frequency = deliveryPolicy ? `${deliveryPolicy.intervalCount} ${deliveryPolicy.interval}` : "";
1756
+ return {
1757
+ id: raw.id,
1758
+ customerId: "",
1759
+ serial: raw.serial,
1760
+ items: (raw.items ?? []).map(
1761
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1762
+ (item) => ({
1763
+ id: item.id,
1764
+ productId: item.productId ?? item.product ?? "",
1765
+ variantId: item.variantId ?? item.variant ?? "",
1766
+ quantity: item.quantity,
1767
+ price: item.price ?? {
1768
+ amount: String(item.totalPrice ?? 0),
1769
+ currencyCode: raw.currencyCode ?? ""
1770
+ },
1771
+ nextDeliveryDate: item.nextDeliveryDate ? new Date(item.nextDeliveryDate) : null,
1772
+ billingPolicy: item.billingPolicy ?? raw.billingPolicy ?? null,
1773
+ deliveryPolicy: item.deliveryPolicy ?? raw.deliveryPolicy ?? null,
1774
+ // Map human-readable titles from the v1 SubscriptionItemResource
1775
+ title: item.title,
1776
+ variantTitle: item.subtitle ?? void 0,
1777
+ // recurringCycleLimit === 1 means the item charges exactly once (one-time upsell)
1778
+ recurringCycleLimit: item.recurringCycleLimit ?? null,
1779
+ customAttributes: Array.isArray(item.customAttributes) ? item.customAttributes : []
1780
+ })
1781
+ ),
1782
+ discounts: (raw.discounts ?? []).map(
1783
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1784
+ (d3) => {
1785
+ var _a2, _b2, _c;
1175
1786
  return {
1176
- _tag: "Failure",
1177
- error: { kind: "SUBSCRIPTION_NOT_FOUND" }
1787
+ id: d3.id,
1788
+ type: ((_a2 = d3.value) == null ? void 0 : _a2.type) === "fixed-amount" || d3.type === "fixed_amount" ? "fixed_amount" : "percentage",
1789
+ value: ((_b2 = d3.value) == null ? void 0 : _b2.amount) ?? ((_c = d3.value) == null ? void 0 : _c.percentage) ?? d3.value ?? 0,
1790
+ code: d3.title ?? d3.code ?? "",
1791
+ description: d3.description ?? ""
1178
1792
  };
1179
1793
  }
1180
- const result = await adapter.addDiscount({ subscriptionId: id, code });
1181
- if (result._tag === "Success") {
1182
- await refreshAfterDiscountChange(id);
1183
- }
1184
- return result;
1185
- },
1186
- removeDiscount: async (discountId, subscriptionId) => {
1187
- const id = subscriptionId ?? subscriptionIdFromCurrent();
1188
- if (id == null) {
1794
+ ),
1795
+ status,
1796
+ nextDeliveryDate: raw.nextBillingDate ? new Date(raw.nextBillingDate) : raw.nextDeliveryDate ? new Date(raw.nextDeliveryDate) : null,
1797
+ // Map nextBillingDate as the raw ISO string for ProductSelect skip() and other consumers
1798
+ nextBillingDate: raw.nextBillingDate ?? null,
1799
+ // Map customerPaymentMethod — expanded to an object (id + instrument) via the
1800
+ // adapter's ?expand=paymentMethod request; mapPaymentMethod tolerates a bare id too.
1801
+ customerPaymentMethod: mapPaymentMethod(raw.paymentMethod),
1802
+ frequency,
1803
+ createdAt: raw.createdAt,
1804
+ updatedAt: raw.updatedAt,
1805
+ billingPolicy: raw.billingPolicy ?? null,
1806
+ deliveryPolicy: raw.deliveryPolicy ?? null,
1807
+ customAttributes: Array.isArray(raw.customAttributes) ? raw.customAttributes : []
1808
+ };
1809
+ }
1810
+ async function readErrorBody(res) {
1811
+ try {
1812
+ const body = await res.json();
1813
+ return body != null && typeof body === "object" ? body : {};
1814
+ } catch {
1815
+ return {};
1816
+ }
1817
+ }
1818
+ function createApiSubscriptionAdapter(fetcher) {
1819
+ const adapter = {
1820
+ async search(options) {
1821
+ var _a;
1822
+ try {
1823
+ const params = new URLSearchParams();
1824
+ if ((_a = options == null ? void 0 : options.filters) == null ? void 0 : _a.length) {
1825
+ const groups = options.filters.filter((f) => Object.keys(f).length > 0).map((f) => Object.entries(f).filter(([, v2]) => Boolean(v2))).filter((entries) => entries.length > 0).map(
1826
+ (entries) => "(" + entries.map(([k, v2]) => `${k}:'${v2}'`).join(" AND ") + ")"
1827
+ );
1828
+ if (groups.length) params.set("query", groups.join(" OR "));
1829
+ }
1830
+ if (options == null ? void 0 : options.includeCanceledItems) {
1831
+ params.set("includeCanceledItems", "true");
1832
+ }
1833
+ params.set("expand", "paymentMethod");
1834
+ if (options == null ? void 0 : options.pagination) {
1835
+ if ("after" in options.pagination)
1836
+ params.set("after", options.pagination.after);
1837
+ else if ("before" in options.pagination)
1838
+ params.set("before", options.pagination.before);
1839
+ }
1840
+ const query = params.toString() ? `?${params.toString()}` : "";
1841
+ const res = await fetcher(`/customer/v1/subscriptions${query}`);
1842
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1843
+ const body = await res.json();
1189
1844
  return {
1190
- _tag: "Failure",
1191
- error: { kind: "SUBSCRIPTION_NOT_FOUND" }
1845
+ _tag: "Success",
1846
+ data: {
1847
+ items: body.data.map(mapSubscription),
1848
+ pageInfo: {
1849
+ hasNextPage: body.hasNextPage,
1850
+ hasPrevPage: body.hasPrevPage,
1851
+ startCursor: body.startCursor,
1852
+ endCursor: body.endCursor
1853
+ }
1854
+ }
1192
1855
  };
1856
+ } catch (e2) {
1857
+ return { _tag: "Failure", error: e2 };
1858
+ }
1859
+ },
1860
+ async getById(id) {
1861
+ try {
1862
+ const res = await fetcher(
1863
+ `/customer/v1/subscriptions?query=id:"${id}"&limit=1&expand=paymentMethod`
1864
+ );
1865
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1866
+ const body = await res.json();
1867
+ const raw = body.data[0];
1868
+ return { _tag: "Success", data: raw ? mapSubscription(raw) : null };
1869
+ } catch (e2) {
1870
+ return { _tag: "Failure", error: e2 };
1871
+ }
1872
+ },
1873
+ async addItem({ subscriptionId, item }) {
1874
+ try {
1875
+ const res = await fetcher(
1876
+ `/customer/v1/subscriptions/${subscriptionId}/items`,
1877
+ {
1878
+ method: "POST",
1879
+ headers: { "Content-Type": "application/json" },
1880
+ body: JSON.stringify({ variant: item.variantId, quantity: item.quantity })
1881
+ }
1882
+ );
1883
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1884
+ return { _tag: "Success", data: void 0 };
1885
+ } catch (e2) {
1886
+ return { _tag: "Failure", error: e2 };
1887
+ }
1888
+ },
1889
+ async removeItem({ subscriptionId, itemId }) {
1890
+ try {
1891
+ const res = await fetcher(
1892
+ `/customer/v1/subscriptions/${subscriptionId}/items/${itemId}`,
1893
+ { method: "DELETE" }
1894
+ );
1895
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1896
+ return { _tag: "Success", data: void 0 };
1897
+ } catch (e2) {
1898
+ return { _tag: "Failure", error: e2 };
1899
+ }
1900
+ },
1901
+ async updateItem({ subscriptionId, lineId, update }) {
1902
+ try {
1903
+ const body = {
1904
+ ...update.variantId !== void 0 ? { productVariantId: update.variantId } : {},
1905
+ ...update.quantity !== void 0 ? { quantity: update.quantity } : {},
1906
+ ...update.billingPolicy !== void 0 ? { billingPolicy: update.billingPolicy } : {},
1907
+ ...update.shippingMethodHandle !== void 0 ? { shippingMethodHandle: update.shippingMethodHandle } : {}
1908
+ };
1909
+ const res = await fetcher(
1910
+ `/customer/subscriptions/${subscriptionId}/lines/${lineId}`,
1911
+ {
1912
+ method: "PUT",
1913
+ headers: { "Content-Type": "application/json" },
1914
+ body: JSON.stringify(body)
1915
+ }
1916
+ );
1917
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1918
+ return { _tag: "Success", data: void 0 };
1919
+ } catch (e2) {
1920
+ return { _tag: "Failure", error: e2 };
1921
+ }
1922
+ },
1923
+ async updateItemAttributes({ subscriptionId, lineId, customAttributes }) {
1924
+ try {
1925
+ const res = await fetcher(
1926
+ `/customer/v1/subscriptions/${subscriptionId}/items/${lineId}`,
1927
+ {
1928
+ method: "PATCH",
1929
+ headers: { "Content-Type": "application/json" },
1930
+ body: JSON.stringify({ customAttributes })
1931
+ }
1932
+ );
1933
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1934
+ return { _tag: "Success", data: void 0 };
1935
+ } catch (e2) {
1936
+ return { _tag: "Failure", error: e2 };
1937
+ }
1938
+ },
1939
+ async updateAttributes({ subscriptionId, customAttributes }) {
1940
+ try {
1941
+ const res = await fetcher(
1942
+ `/customer/v1/subscriptions/${subscriptionId}`,
1943
+ {
1944
+ method: "PATCH",
1945
+ headers: { "Content-Type": "application/json" },
1946
+ body: JSON.stringify({ customAttributes })
1947
+ }
1948
+ );
1949
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1950
+ return { _tag: "Success", data: void 0 };
1951
+ } catch (e2) {
1952
+ return { _tag: "Failure", error: e2 };
1953
+ }
1954
+ },
1955
+ async skipLineItem({ subscriptionId, lineId, nextBillingDate }) {
1956
+ try {
1957
+ const res = await fetcher(
1958
+ `/customer/subscriptions/${subscriptionId}/lines/${lineId}`,
1959
+ {
1960
+ method: "PUT",
1961
+ headers: { "Content-Type": "application/json" },
1962
+ body: JSON.stringify({ nextBillingDate })
1963
+ }
1964
+ );
1965
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1966
+ return { _tag: "Success", data: void 0 };
1967
+ } catch (e2) {
1968
+ return { _tag: "Failure", error: e2 };
1969
+ }
1970
+ },
1971
+ async updateBoxItems({ subscriptionId, parentId, items, shippingMethodHandle }) {
1972
+ try {
1973
+ const res = await fetcher(
1974
+ `/customer/subscriptions/${subscriptionId}/update-box-products`,
1975
+ {
1976
+ method: "POST",
1977
+ headers: { "Content-Type": "application/json" },
1978
+ body: JSON.stringify({ parentId, items, shippingMethodHandle })
1979
+ }
1980
+ );
1981
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1982
+ return { _tag: "Success", data: void 0 };
1983
+ } catch (e2) {
1984
+ return { _tag: "Failure", error: e2 };
1985
+ }
1986
+ },
1987
+ async addDiscount({ subscriptionId, code }) {
1988
+ try {
1989
+ const res = await fetcher(
1990
+ `/customer/v1/subscriptions/${subscriptionId}/discounts`,
1991
+ {
1992
+ method: "POST",
1993
+ headers: { "Content-Type": "application/json" },
1994
+ body: JSON.stringify({ code })
1995
+ }
1996
+ );
1997
+ if (!res.ok) {
1998
+ const body = await readErrorBody(res);
1999
+ if (res.status === 400 && body.code === "DISCOUNT_CODE_REJECTED") {
2000
+ const raw = body.rejectionReason;
2001
+ const reason = typeof raw === "string" && DISCOUNT_REJECTION_REASONS.includes(raw) ? raw : "INVALID_CODE";
2002
+ return { _tag: "Failure", error: { kind: "REJECTED", reason } };
2003
+ }
2004
+ if (res.status === 404) {
2005
+ return { _tag: "Failure", error: { kind: "SUBSCRIPTION_NOT_FOUND" } };
2006
+ }
2007
+ return { _tag: "Failure", error: { kind: "UNKNOWN", message: `HTTP ${res.status}` } };
2008
+ }
2009
+ return { _tag: "Success", data: void 0 };
2010
+ } catch (e2) {
2011
+ return {
2012
+ _tag: "Failure",
2013
+ error: { kind: "NETWORK", message: e2 instanceof Error ? e2.message : void 0 }
2014
+ };
2015
+ }
2016
+ },
2017
+ async removeDiscount({ subscriptionId, discountId }) {
2018
+ try {
2019
+ const res = await fetcher(
2020
+ `/customer/v1/subscriptions/${subscriptionId}/discounts/${discountId}`,
2021
+ { method: "DELETE" }
2022
+ );
2023
+ if (!res.ok) {
2024
+ if (res.status === 404) {
2025
+ const body = await readErrorBody(res);
2026
+ const message = typeof body.message === "string" ? body.message : "";
2027
+ if (message.toLowerCase().includes("discount")) {
2028
+ return { _tag: "Failure", error: { kind: "DISCOUNT_NOT_FOUND" } };
2029
+ }
2030
+ return { _tag: "Failure", error: { kind: "SUBSCRIPTION_NOT_FOUND" } };
2031
+ }
2032
+ return { _tag: "Failure", error: { kind: "UNKNOWN", message: `HTTP ${res.status}` } };
2033
+ }
2034
+ return { _tag: "Success", data: void 0 };
2035
+ } catch (e2) {
2036
+ return {
2037
+ _tag: "Failure",
2038
+ error: { kind: "NETWORK", message: e2 instanceof Error ? e2.message : void 0 }
2039
+ };
2040
+ }
2041
+ },
2042
+ // Uses PATCH /customer/v1/subscriptions/{id} — the dedicated /reschedule route does not exist.
2043
+ async reschedule({ subscriptionId, newDate }) {
2044
+ try {
2045
+ const res = await fetcher(
2046
+ `/customer/v1/subscriptions/${subscriptionId}`,
2047
+ {
2048
+ method: "PATCH",
2049
+ headers: { "Content-Type": "application/json" },
2050
+ body: JSON.stringify({ nextBillingDate: newDate })
2051
+ }
2052
+ );
2053
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2054
+ const data = await res.json();
2055
+ return { _tag: "Success", data: mapSubscription(data) };
2056
+ } catch (e2) {
2057
+ return { _tag: "Failure", error: e2 };
2058
+ }
2059
+ },
2060
+ // PATCH /customer/v1/subscriptions/{id} with the full customer-visible set.
2061
+ async updateCustomAttributes({ subscriptionId, customAttributes }) {
2062
+ try {
2063
+ const res = await fetcher(
2064
+ `/customer/v1/subscriptions/${subscriptionId}`,
2065
+ {
2066
+ method: "PATCH",
2067
+ headers: { "Content-Type": "application/json" },
2068
+ body: JSON.stringify({ customAttributes })
2069
+ }
2070
+ );
2071
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2072
+ const data = await res.json();
2073
+ return { _tag: "Success", data: mapSubscription(data) };
2074
+ } catch (e2) {
2075
+ return { _tag: "Failure", error: e2 };
2076
+ }
2077
+ },
2078
+ async resume({ subscriptionId }) {
2079
+ try {
2080
+ const res = await fetcher(
2081
+ `/customer/v1/subscriptions/${subscriptionId}/resume`,
2082
+ { method: "POST" }
2083
+ );
2084
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2085
+ const data = await res.json();
2086
+ return { _tag: "Success", data: mapSubscription(data) };
2087
+ } catch (e2) {
2088
+ return { _tag: "Failure", error: e2 };
2089
+ }
2090
+ },
2091
+ async pause({ subscriptionId }) {
2092
+ try {
2093
+ const res = await fetcher(
2094
+ `/customer/v1/subscriptions/${subscriptionId}/pause`,
2095
+ { method: "POST" }
2096
+ );
2097
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2098
+ const data = await res.json();
2099
+ return { _tag: "Success", data: mapSubscription(data) };
2100
+ } catch (e2) {
2101
+ return { _tag: "Failure", error: e2 };
2102
+ }
2103
+ },
2104
+ async reactivate({ subscriptionId }) {
2105
+ try {
2106
+ const res = await fetcher(
2107
+ `/customer/v1/subscriptions/${subscriptionId}/reactivate`,
2108
+ { method: "POST" }
2109
+ );
2110
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2111
+ const data = await res.json();
2112
+ return { _tag: "Success", data: mapSubscription(data) };
2113
+ } catch (e2) {
2114
+ return { _tag: "Failure", error: e2 };
2115
+ }
2116
+ },
2117
+ async cancel({ subscriptionId, reason }) {
2118
+ try {
2119
+ const res = await fetcher(
2120
+ `/customer/v1/subscriptions/${subscriptionId}/cancel`,
2121
+ {
2122
+ method: "POST",
2123
+ headers: { "Content-Type": "application/json" },
2124
+ body: JSON.stringify({ cancellationReason: reason })
2125
+ }
2126
+ );
2127
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2128
+ const data = await res.json();
2129
+ return { _tag: "Success", data: mapSubscription(data) };
2130
+ } catch (e2) {
2131
+ return { _tag: "Failure", error: e2 };
2132
+ }
2133
+ },
2134
+ // Legacy endpoint — no /customer/v1/subscriptions/{id}/plans exists yet.
2135
+ async getAvailablePlans({ subscriptionId }) {
2136
+ try {
2137
+ const res = await fetcher(
2138
+ `/customer/subscriptions/${subscriptionId}/plans`
2139
+ );
2140
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2141
+ const data = await res.json();
2142
+ return { _tag: "Success", data };
2143
+ } catch (e2) {
2144
+ return { _tag: "Failure", error: e2 };
2145
+ }
2146
+ },
2147
+ // Legacy endpoint — no /customer/v1 equivalent exists yet.
2148
+ async getDefaultSubscriptionId() {
2149
+ try {
2150
+ const res = await fetcher(
2151
+ `/customer/subscriptions/one-by-next-billing-date`
2152
+ );
2153
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2154
+ const data = await res.json();
2155
+ return { _tag: "Success", data: (data == null ? void 0 : data.id) ?? null };
2156
+ } catch (e2) {
2157
+ return { _tag: "Failure", error: e2 };
2158
+ }
2159
+ },
2160
+ // Legacy endpoint — no /customer/v1/subscriptions/{id}/upsells exists yet.
2161
+ async getUpsellProducts({ subscriptionId }) {
2162
+ try {
2163
+ const res = await fetcher(
2164
+ `/customer/subscriptions/${subscriptionId}/upsell-products`
2165
+ );
2166
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2167
+ const data = await res.json();
2168
+ const products = data.map((p2) => {
2169
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
2170
+ return {
2171
+ id: p2.id,
2172
+ variantId: ((_b = (_a = p2.variants) == null ? void 0 : _a[0]) == null ? void 0 : _b.id) ?? "",
2173
+ title: p2.title ?? "",
2174
+ imageUrl: ((_c = p2.featuredImage) == null ? void 0 : _c.url) ?? void 0,
2175
+ price: {
2176
+ amount: String(((_f = (_e = (_d = p2.variants) == null ? void 0 : _d[0]) == null ? void 0 : _e.currentPrice) == null ? void 0 : _f.amount) ?? 0),
2177
+ currencyCode: ((_i = (_h = (_g = p2.variants) == null ? void 0 : _g[0]) == null ? void 0 : _h.currentPrice) == null ? void 0 : _i.currencyCode) ?? ""
2178
+ },
2179
+ // recurring comes from the upsell rule on variants[0]; present in PreparedProductSchema
2180
+ recurring: (_k = (_j = p2.variants) == null ? void 0 : _j[0]) == null ? void 0 : _k.recurring
2181
+ };
2182
+ });
2183
+ return { _tag: "Success", data: products };
2184
+ } catch (e2) {
2185
+ return { _tag: "Failure", error: e2 };
2186
+ }
2187
+ },
2188
+ // Legacy endpoint — no /customer/v1/subscriptions/{id}/upsells POST exists yet.
2189
+ async addUpsellProduct({ subscriptionId, variantId, quantity, oneTime }) {
2190
+ try {
2191
+ const res = await fetcher(
2192
+ `/customer/subscriptions/${subscriptionId}/upsell`,
2193
+ {
2194
+ method: "POST",
2195
+ headers: { "Content-Type": "application/json" },
2196
+ body: JSON.stringify({
2197
+ productVariantId: variantId,
2198
+ quantity,
2199
+ ...oneTime ? { renewalLimit: 1 } : {}
2200
+ })
2201
+ }
2202
+ );
2203
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2204
+ return { _tag: "Success", data: void 0 };
2205
+ } catch (e2) {
2206
+ return { _tag: "Failure", error: e2 };
2207
+ }
2208
+ },
2209
+ // Legacy endpoint — no /customer/v1 equivalent exists yet.
2210
+ async getLastOrder({ subscriptionId }) {
2211
+ try {
2212
+ const res = await fetcher(
2213
+ `/customer/subscriptions/${subscriptionId}/last-order`
2214
+ );
2215
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2216
+ const data = await res.json();
2217
+ if (!data) return { _tag: "Success", data: null };
2218
+ return {
2219
+ _tag: "Success",
2220
+ data: { id: data.id, createdAt: data.createdAt }
2221
+ };
2222
+ } catch (e2) {
2223
+ return { _tag: "Failure", error: e2 };
2224
+ }
2225
+ },
2226
+ // Legacy endpoints, split by stage (see SubscriptionService.renewNow docs):
2227
+ // retry → POST /customer/subscriptions/{id}/retry
2228
+ // renewalId → POST /customer/renewals/{renewalId}/renew
2229
+ // default → POST /customer/subscriptions/{id}/order-now
2230
+ async renewNow({ subscriptionId, renewalId, retry }) {
2231
+ try {
2232
+ const url = retry ? `/customer/subscriptions/${subscriptionId}/retry` : renewalId ? `/customer/renewals/${renewalId}/renew` : `/customer/subscriptions/${subscriptionId}/order-now`;
2233
+ const res = await fetcher(url, { method: "POST" });
2234
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2235
+ const body = res.status === 204 ? null : await res.json();
2236
+ return {
2237
+ _tag: "Success",
2238
+ data: {
2239
+ nextActionUrl: (body == null ? void 0 : body.nextActionUrl) ?? void 0,
2240
+ result: (body == null ? void 0 : body.result) ?? "success"
2241
+ }
2242
+ };
2243
+ } catch (e2) {
2244
+ return { _tag: "Failure", error: e2 };
2245
+ }
2246
+ },
2247
+ // Legacy endpoint — POST /plan returns 204; re-fetch to return the updated subscription.
2248
+ async changeFrequency({ subscriptionId, plan }) {
2249
+ try {
2250
+ const res = await fetcher(
2251
+ `/customer/subscriptions/${subscriptionId}/plan`,
2252
+ {
2253
+ method: "POST",
2254
+ headers: { "Content-Type": "application/json" },
2255
+ body: JSON.stringify({ plan })
2256
+ }
2257
+ );
2258
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2259
+ const refreshed = await adapter.getById(subscriptionId);
2260
+ if (refreshed._tag === "Failure") return refreshed;
2261
+ if (refreshed.data) return { _tag: "Success", data: refreshed.data };
2262
+ return {
2263
+ _tag: "Failure",
2264
+ error: "Subscription not found after frequency change"
2265
+ };
2266
+ } catch (e2) {
2267
+ return { _tag: "Failure", error: e2 };
2268
+ }
2269
+ }
2270
+ };
2271
+ return adapter;
2272
+ }
2273
+ function expandableId(field) {
2274
+ if (typeof field === "string") return field;
2275
+ if (field != null && typeof field === "object" && "id" in field) {
2276
+ const id = field.id;
2277
+ return typeof id === "string" ? id : void 0;
2278
+ }
2279
+ return void 0;
2280
+ }
2281
+ function mapScheduleOrder(order) {
2282
+ return {
2283
+ id: order.id,
2284
+ customerId: order.customerId,
2285
+ subscriptionId: order.subscriptionId,
2286
+ date: order.date,
2287
+ skipped: order.skipped ?? false,
2288
+ currencyCode: order.currencyCode,
2289
+ subtotal: order.subtotal,
2290
+ deliveryPrice: order.deliveryPrice,
2291
+ cycleIndex: order.cycleIndex,
2292
+ deliveryDate: order.deliveryDate ?? order.date,
2293
+ status: order.status ?? (order.skipped ? "skipped" : "scheduled"),
2294
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2295
+ items: (order.items ?? []).map((item) => {
2296
+ var _a;
2297
+ return {
2298
+ id: item.id,
2299
+ title: item.title,
2300
+ subtitle: item.subtitle ?? null,
2301
+ imageUrl: item.imageUrl ?? ((_a = item.variantImage) == null ? void 0 : _a.url) ?? void 0,
2302
+ variantId: expandableId(item.variant) ?? "",
2303
+ quantity: item.quantity,
2304
+ price: item.price,
2305
+ totalPrice: item.totalPrice,
2306
+ skipped: item.skipped ?? false,
2307
+ variantImage: item.variantImage != null ? { url: item.variantImage.url ?? "", altText: item.variantImage.altText ?? void 0 } : null,
2308
+ variantAvailableForSale: item.variantAvailableForSale ?? void 0,
2309
+ subscriptionId: expandableId(item.subscription) ?? "",
2310
+ subscriptionLineId: expandableId(item.subscriptionItem) ?? "",
2311
+ productId: expandableId(item.product),
2312
+ parentProductId: item.parentProductId ?? null,
2313
+ parentLineId: item.parentLineId ?? null,
2314
+ isVirtual: item.isVirtual ?? void 0,
2315
+ isUpsell: item.isUpsell ?? void 0,
2316
+ recurringCycleLimit: item.recurringCycleLimit ?? null,
2317
+ customAttributes: item.customAttributes ?? [],
2318
+ sellingPlanId: item.sellingPlanId ?? null,
2319
+ sellingPlanName: item.sellingPlanName ?? null,
2320
+ nextBillingDate: item.nextBillingDate ?? null,
2321
+ billingPolicy: item.billingPolicy ?? null,
2322
+ subscriptionStatus: item.subscriptionStatus ?? null,
2323
+ subscriptionIsEditable: item.subscriptionIsEditable ?? null,
2324
+ requiredBillingCyclesLeft: item.requiredBillingCyclesLeft ?? null
2325
+ };
2326
+ }),
2327
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2328
+ discounts: (order.discounts ?? []).map((d3) => {
2329
+ var _a, _b, _c;
2330
+ return {
2331
+ id: d3.id,
2332
+ title: d3.title ?? void 0,
2333
+ type: ((_a = d3.value) == null ? void 0 : _a.type) === "fixed-amount" ? "fixed_amount" : "percentage",
2334
+ value: ((_b = d3.value) == null ? void 0 : _b.amount) ?? ((_c = d3.value) == null ? void 0 : _c.percentage) ?? 0
2335
+ };
2336
+ })
2337
+ };
2338
+ }
2339
+ function toIsoOrNull(value) {
2340
+ const date = new Date(value);
2341
+ return Number.isNaN(date.getTime()) ? null : date.toISOString();
2342
+ }
2343
+ function buildMatcher(params) {
2344
+ const { cycle, date } = params;
2345
+ let iso;
2346
+ if (date != null) {
2347
+ const parsed = toIsoOrNull(date);
2348
+ if (parsed == null) return { ok: false, error: "date must be a valid date" };
2349
+ iso = parsed;
2350
+ }
2351
+ if (cycle != null && iso != null) {
2352
+ return { ok: true, matcher: { type: "CYCLE_AND_DATE", input: { cycle, date: iso } } };
2353
+ }
2354
+ if (cycle != null) return { ok: true, matcher: { type: "CYCLE", input: { cycle } } };
2355
+ if (iso != null) return { ok: true, matcher: { type: "DATE", input: { date: iso } } };
2356
+ return { ok: false, error: "Either cycle or date is required" };
2357
+ }
2358
+ function buildSkipItemMatcher(params) {
2359
+ const { cycle, date } = params;
2360
+ if (cycle != null) {
2361
+ return { ok: true, matcher: { type: "CYCLE", input: { cycle } } };
2362
+ }
2363
+ if (date != null) {
2364
+ const iso = toIsoOrNull(date);
2365
+ if (iso == null) return { ok: false, error: "date must be a valid date" };
2366
+ return { ok: true, matcher: { type: "DATE", input: { date: iso } } };
2367
+ }
2368
+ return { ok: false, error: "Either cycle or date is required" };
2369
+ }
2370
+ const DELIVERY_PRICE = 5;
2371
+ const DELIVERY_OFFSET_DAYS = 2;
2372
+ function toPrice(amount) {
2373
+ const n = Number.parseFloat(amount ?? "0");
2374
+ return Number.isFinite(n) ? n : 0;
2375
+ }
2376
+ function addInterval(date, interval, units) {
2377
+ const d3 = new Date(date);
2378
+ switch (interval) {
2379
+ case "DAY":
2380
+ d3.setDate(d3.getDate() + units);
2381
+ break;
2382
+ case "WEEK":
2383
+ d3.setDate(d3.getDate() + units * 7);
2384
+ break;
2385
+ case "YEAR":
2386
+ d3.setFullYear(d3.getFullYear() + units);
2387
+ break;
2388
+ default:
2389
+ d3.setMonth(d3.getMonth() + units);
2390
+ break;
2391
+ }
2392
+ return d3;
2393
+ }
2394
+ function mapItem(sub, item, cycle) {
2395
+ var _a, _b;
2396
+ const price = toPrice(((_a = item.currentPrice) == null ? void 0 : _a.amount) ?? item.price.amount);
2397
+ return {
2398
+ id: `${sub.id}_cycle_${cycle}_${item.id}`,
2399
+ title: item.title ?? `Product ${item.variantId}`,
2400
+ subtitle: item.variantTitle ?? null,
2401
+ imageUrl: (_b = item.variantImage) == null ? void 0 : _b.url,
2402
+ variantId: item.variantId,
2403
+ quantity: item.quantity,
2404
+ price,
2405
+ totalPrice: price * item.quantity,
2406
+ skipped: false,
2407
+ variantImage: item.variantImage ?? null,
2408
+ variantAvailableForSale: item.variantAvailableForSale ?? true,
2409
+ subscriptionId: sub.id,
2410
+ subscriptionLineId: item.id,
2411
+ productId: item.productId,
2412
+ parentProductId: item.parentProductId ?? null,
2413
+ parentLineId: item.parentLineId ?? null,
2414
+ isVirtual: item.isVirtual ?? false,
2415
+ isUpsell: item.isUpsell ?? false,
2416
+ recurringCycleLimit: item.recurringCycleLimit ?? null,
2417
+ customAttributes: item.customAttributes ?? []
2418
+ };
2419
+ }
2420
+ function projectUpcomingOrders(sub, count) {
2421
+ var _a, _b;
2422
+ if (sub.status !== "active") return [];
2423
+ const billing = sub.billingPolicy ?? ((_a = sub.items[0]) == null ? void 0 : _a.billingPolicy) ?? { interval: "MONTH", intervalCount: 1 };
2424
+ const baseStr = sub.nextBillingDate ?? (sub.nextDeliveryDate ? sub.nextDeliveryDate.toISOString() : null);
2425
+ const base = baseStr ? new Date(baseStr) : /* @__PURE__ */ new Date();
2426
+ const currencyCode = ((_b = sub.items[0]) == null ? void 0 : _b.price.currencyCode) ?? "USD";
2427
+ const orders = [];
2428
+ for (let cycle = 0; cycle < count; cycle++) {
2429
+ const billingDate = addInterval(
2430
+ base,
2431
+ billing.interval,
2432
+ cycle * billing.intervalCount
2433
+ );
2434
+ const deliveryDate = addInterval(billingDate, "DAY", DELIVERY_OFFSET_DAYS);
2435
+ const items = sub.items.map((item) => mapItem(sub, item, cycle));
2436
+ const subtotal = items.reduce((sum, it) => sum + it.totalPrice, 0);
2437
+ orders.push({
2438
+ id: `${sub.id}_cycle_${cycle}`,
2439
+ customerId: sub.customerId,
2440
+ subscriptionId: sub.id,
2441
+ date: billingDate.toISOString(),
2442
+ skipped: false,
2443
+ currencyCode,
2444
+ subtotal,
2445
+ deliveryPrice: DELIVERY_PRICE,
2446
+ cycleIndex: cycle,
2447
+ deliveryDate: deliveryDate.toISOString(),
2448
+ status: "scheduled",
2449
+ items,
2450
+ discounts: sub.discounts.map((d3) => ({
2451
+ id: d3.id,
2452
+ title: d3.code,
2453
+ type: d3.type,
2454
+ value: d3.value
2455
+ }))
2456
+ });
2457
+ }
2458
+ return orders;
2459
+ }
2460
+ function applyAdjustments(orders, stored) {
2461
+ return orders.map((order) => {
2462
+ let next = order;
2463
+ for (const { adjustment, subscriptionId, cycle } of stored) {
2464
+ if (subscriptionId !== order.subscriptionId) continue;
2465
+ if (cycle !== order.cycleIndex) continue;
2466
+ const { action } = adjustment;
2467
+ if (action.type === "SKIP_ORDER") {
2468
+ next = { ...next, skipped: true, status: "skipped" };
2469
+ } else if (action.type === "CHANGE_DATE") {
2470
+ next = {
2471
+ ...next,
2472
+ date: action.input.newDate,
2473
+ deliveryDate: action.input.newDate
2474
+ };
2475
+ } else if (action.type === "SKIP_ITEM") {
2476
+ const lineIds = new Set(action.input.lineIds);
2477
+ const items = next.items.map(
2478
+ (item) => lineIds.has(item.subscriptionLineId) ? { ...item, skipped: true, totalPrice: 0 } : item
2479
+ );
2480
+ const subtotal = items.reduce((sum, it) => sum + it.totalPrice, 0);
2481
+ next = { ...next, items, subtotal };
2482
+ if (items.length > 0 && items.every((i2) => i2.skipped)) {
2483
+ next = { ...next, skipped: true, status: "skipped", deliveryPrice: 0 };
2484
+ }
2485
+ }
2486
+ }
2487
+ return next;
2488
+ });
2489
+ }
2490
+ function createMockSchedulesAdapter(subscriptions = createMockSubscriptionAdapter()) {
2491
+ const RESOLVE_DEPTH = 12;
2492
+ let stored = [];
2493
+ let idSeq = 0;
2494
+ async function subscriptionsToProject(subscriptionId) {
2495
+ if (subscriptionId) {
2496
+ const res = await subscriptions.getById(subscriptionId);
2497
+ return res._tag === "Success" && res.data ? [res.data] : [];
2498
+ }
2499
+ const all = [];
2500
+ let pagination;
2501
+ for (let guard = 0; guard < 50; guard++) {
2502
+ const res = await subscriptions.search(pagination ? { pagination } : {});
2503
+ if (res._tag !== "Success") break;
2504
+ all.push(...res.data.items);
2505
+ const { hasNextPage, endCursor } = res.data.pageInfo;
2506
+ if (!hasNextPage || !endCursor) break;
2507
+ pagination = { after: endCursor };
2508
+ }
2509
+ return all;
2510
+ }
2511
+ function resolveCycle(orders, opts) {
2512
+ if (opts.cycle != null) return opts.cycle;
2513
+ if (opts.date) {
2514
+ const iso = toIsoOrNull(opts.date);
2515
+ const target = iso == null ? void 0 : iso.split("T")[0];
2516
+ const match = target ? orders.find(
2517
+ (o2) => o2.deliveryDate.split("T")[0] === target || o2.date.split("T")[0] === target
2518
+ ) : void 0;
2519
+ return match ? match.cycleIndex : null;
2520
+ }
2521
+ const firstScheduled = orders.find((o2) => o2.status === "scheduled");
2522
+ return (firstScheduled == null ? void 0 : firstScheduled.cycleIndex) ?? null;
2523
+ }
2524
+ async function recordAdjustment(subscriptionId, opts, buildAction, matcherOverride) {
2525
+ const res = await subscriptions.getById(subscriptionId);
2526
+ const sub = res._tag === "Success" ? res.data : null;
2527
+ if (!sub) return { _tag: "Failure", error: "No scheduled order found" };
2528
+ const orders = applyAdjustments(
2529
+ projectUpcomingOrders(sub, RESOLVE_DEPTH),
2530
+ stored
2531
+ );
2532
+ const cycle = resolveCycle(orders, opts);
2533
+ if (cycle == null) {
2534
+ return { _tag: "Failure", error: "No scheduled order found" };
2535
+ }
2536
+ const built = buildMatcher({ cycle, date: opts.date });
2537
+ const matcher = matcherOverride ?? (built.ok ? built.matcher : { type: "CYCLE", input: { cycle } });
2538
+ stored = [
2539
+ ...stored,
2540
+ {
2541
+ adjustment: { id: `adj-${idSeq++}`, matcher, action: buildAction() },
2542
+ subscriptionId,
2543
+ cycle
2544
+ }
2545
+ ];
2546
+ return { _tag: "Success", data: void 0 };
2547
+ }
2548
+ return {
2549
+ async getUpcomingOrders({ count, subscriptionId, excludeSkipped = true }) {
2550
+ await new Promise((r2) => setTimeout(r2, 200));
2551
+ const subs = await subscriptionsToProject(subscriptionId);
2552
+ const skipCount = new Set(
2553
+ stored.filter(
2554
+ (s) => s.adjustment.action.type === "SKIP_ORDER" || s.adjustment.action.type === "SKIP_ITEM"
2555
+ ).map((s) => `${s.subscriptionId}:${s.cycle}`)
2556
+ ).size;
2557
+ const depth = excludeSkipped ? count + skipCount : count;
2558
+ const orders = subs.flatMap(
2559
+ (sub) => applyAdjustments(projectUpcomingOrders(sub, depth), stored)
2560
+ ).filter((o2) => excludeSkipped ? !o2.skipped : true).map(
2561
+ (o2) => (
2562
+ // Mirror backend removeSkippedItems: the billable view also strips
2563
+ // skipped lines from surviving (partially-skipped) orders. No
2564
+ // recompute needed — skipped lines already carry totalPrice 0 (R4).
2565
+ excludeSkipped && o2.items.some((i2) => i2.skipped) ? { ...o2, items: o2.items.filter((i2) => !i2.skipped) } : o2
2566
+ )
2567
+ ).sort((a8, b2) => a8.date.localeCompare(b2.date));
2568
+ return { _tag: "Success", data: orders.slice(0, count) };
2569
+ },
2570
+ async getAdjustments() {
2571
+ await new Promise((r2) => setTimeout(r2, 200));
2572
+ return { _tag: "Success", data: stored.map((s) => s.adjustment) };
2573
+ },
2574
+ async skipOrder({ subscriptionId, cycle, date }) {
2575
+ await new Promise((r2) => setTimeout(r2, 300));
2576
+ return recordAdjustment(subscriptionId, { cycle, date }, () => ({
2577
+ type: "SKIP_ORDER",
2578
+ input: { reason: "" }
2579
+ }));
2580
+ },
2581
+ async skipItem({ subscriptionId, lineId, cycle, date, reason }) {
2582
+ await new Promise((r2) => setTimeout(r2, 300));
2583
+ const built = buildSkipItemMatcher({ cycle, date });
2584
+ if (!built.ok) return { _tag: "Failure", error: built.error };
2585
+ return recordAdjustment(
2586
+ subscriptionId,
2587
+ { cycle, date },
2588
+ () => ({
2589
+ type: "SKIP_ITEM",
2590
+ input: { lineIds: [lineId], reason: reason ?? "" }
2591
+ }),
2592
+ built.matcher
2593
+ );
2594
+ },
2595
+ async changeDateAdjustment({ subscriptionId, newDate, cycle, currentDate }) {
2596
+ await new Promise((r2) => setTimeout(r2, 300));
2597
+ const newIso = toIsoOrNull(newDate);
2598
+ if (newIso == null) {
2599
+ return { _tag: "Failure", error: "newDate must be a valid date" };
2600
+ }
2601
+ return recordAdjustment(
2602
+ subscriptionId,
2603
+ { cycle, date: currentDate },
2604
+ () => ({
2605
+ type: "CHANGE_DATE",
2606
+ input: { newDate: newIso }
2607
+ })
2608
+ );
2609
+ },
2610
+ async updateShippingAdjustment({ subscriptionId, address, cycle, date }) {
2611
+ await new Promise((r2) => setTimeout(r2, 300));
2612
+ return recordAdjustment(subscriptionId, { cycle, date }, () => ({
2613
+ type: "UPDATE_SHIPPING",
2614
+ input: { address }
2615
+ }));
2616
+ },
2617
+ async updatePaymentMethodAdjustment({
2618
+ subscriptionId,
2619
+ paymentMethodId,
2620
+ cycle,
2621
+ date
2622
+ }) {
2623
+ await new Promise((r2) => setTimeout(r2, 300));
2624
+ return recordAdjustment(subscriptionId, { cycle, date }, () => ({
2625
+ type: "UPDATE_PAYMENT_METHOD",
2626
+ input: { paymentMethodId }
2627
+ }));
2628
+ },
2629
+ async updateProductsAdjustment({ subscriptionId, lines, cycle, date }) {
2630
+ await new Promise((r2) => setTimeout(r2, 300));
2631
+ return recordAdjustment(subscriptionId, { cycle, date }, () => ({
2632
+ type: "UPDATE_PRODUCTS",
2633
+ input: { lines }
2634
+ }));
2635
+ },
2636
+ async applyDiscountAdjustment({ subscriptionId, discountCode, cycle }) {
2637
+ await new Promise((r2) => setTimeout(r2, 300));
2638
+ return recordAdjustment(subscriptionId, { cycle }, () => ({
2639
+ type: "APPLY_DISCOUNT",
2640
+ input: { discountSource: "code", discountCode }
2641
+ }));
2642
+ },
2643
+ async removeAdjustment({ adjustmentId }) {
2644
+ await new Promise((r2) => setTimeout(r2, 200));
2645
+ stored = stored.filter((s) => s.adjustment.id !== adjustmentId);
2646
+ return { _tag: "Success", data: void 0 };
2647
+ }
2648
+ };
2649
+ }
2650
+ function createApiSchedulesAdapter(fetcher) {
2651
+ async function postAdjustment(matcher, action) {
2652
+ try {
2653
+ const res = await fetcher(`/customer/v1/schedules/adjustments`, {
2654
+ method: "POST",
2655
+ headers: { "Content-Type": "application/json" },
2656
+ body: JSON.stringify({ matcher, action })
2657
+ });
2658
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2659
+ return { _tag: "Success", data: void 0 };
2660
+ } catch (e2) {
2661
+ return { _tag: "Failure", error: e2 };
2662
+ }
2663
+ }
2664
+ return {
2665
+ async getUpcomingOrders({ count, subscriptionId, excludeSkipped = true }) {
2666
+ try {
2667
+ const params = new URLSearchParams({ count: String(count) });
2668
+ params.set("excludeSkipped", String(excludeSkipped));
2669
+ if (subscriptionId) params.set("query", `id:"${subscriptionId}"`);
2670
+ const res = await fetcher(`/customer/v1/schedules/?${params.toString()}`);
2671
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2672
+ const data = await res.json();
2673
+ return { _tag: "Success", data: (data.orders ?? []).map(mapScheduleOrder) };
2674
+ } catch (e2) {
2675
+ return { _tag: "Failure", error: e2 };
2676
+ }
2677
+ },
2678
+ async getAdjustments() {
2679
+ try {
2680
+ const all = [];
2681
+ let after;
2682
+ for (let guard = 0; guard < 50; guard++) {
2683
+ const params = new URLSearchParams({ limit: "100" });
2684
+ if (after) params.set("after", after);
2685
+ const res = await fetcher(
2686
+ `/customer/v1/schedules/adjustments?${params.toString()}`
2687
+ );
2688
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2689
+ const body = await res.json();
2690
+ all.push(
2691
+ ...body.data.map((a8) => ({
2692
+ id: a8.id,
2693
+ matcher: a8.matcher,
2694
+ action: a8.action
2695
+ }))
2696
+ );
2697
+ if (!body.hasNextPage) return { _tag: "Success", data: all };
2698
+ if (!body.endCursor || body.endCursor === after) {
2699
+ return {
2700
+ _tag: "Failure",
2701
+ error: "Adjustments pagination cursor did not advance"
2702
+ };
2703
+ }
2704
+ after = body.endCursor;
2705
+ }
2706
+ return {
2707
+ _tag: "Failure",
2708
+ error: "Adjustments pagination exceeded 50 pages"
2709
+ };
2710
+ } catch (e2) {
2711
+ return { _tag: "Failure", error: e2 };
2712
+ }
2713
+ },
2714
+ async skipOrder({ cycle, date }) {
2715
+ const built = buildMatcher({ cycle, date });
2716
+ if (!built.ok) return { _tag: "Failure", error: built.error };
2717
+ return postAdjustment(built.matcher, {
2718
+ type: "SKIP_ORDER",
2719
+ input: { reason: "" }
2720
+ });
2721
+ },
2722
+ async skipItem({ lineId, cycle, date, reason }) {
2723
+ const built = buildSkipItemMatcher({ cycle, date });
2724
+ if (!built.ok) return { _tag: "Failure", error: built.error };
2725
+ return postAdjustment(built.matcher, {
2726
+ type: "SKIP_ITEM",
2727
+ input: { lineIds: [lineId], reason: reason ?? "" }
2728
+ });
2729
+ },
2730
+ async changeDateAdjustment({ newDate, cycle }) {
2731
+ if (cycle == null) {
2732
+ return {
2733
+ _tag: "Failure",
2734
+ error: "cycle is required for changeDateAdjustment"
2735
+ };
2736
+ }
2737
+ const newIso = toIsoOrNull(newDate);
2738
+ if (newIso == null) {
2739
+ return { _tag: "Failure", error: "newDate must be a valid date" };
2740
+ }
2741
+ return postAdjustment(
2742
+ { type: "CYCLE", input: { cycle } },
2743
+ { type: "CHANGE_DATE", input: { newDate: newIso } }
2744
+ );
2745
+ },
2746
+ async updateShippingAdjustment({ address, cycle, date }) {
2747
+ const built = buildMatcher({ cycle, date });
2748
+ if (!built.ok) return { _tag: "Failure", error: built.error };
2749
+ return postAdjustment(built.matcher, {
2750
+ type: "UPDATE_SHIPPING",
2751
+ input: { address }
2752
+ });
2753
+ },
2754
+ async updatePaymentMethodAdjustment({ paymentMethodId, cycle, date }) {
2755
+ const built = buildMatcher({ cycle, date });
2756
+ if (!built.ok) return { _tag: "Failure", error: built.error };
2757
+ return postAdjustment(built.matcher, {
2758
+ type: "UPDATE_PAYMENT_METHOD",
2759
+ input: { paymentMethodId }
2760
+ });
2761
+ },
2762
+ async updateProductsAdjustment({ lines, cycle, date }) {
2763
+ const built = buildMatcher({ cycle, date });
2764
+ if (!built.ok) return { _tag: "Failure", error: built.error };
2765
+ return postAdjustment(built.matcher, {
2766
+ type: "UPDATE_PRODUCTS",
2767
+ input: { lines }
2768
+ });
2769
+ },
2770
+ async applyDiscountAdjustment({ discountCode, cycle }) {
2771
+ return postAdjustment(
2772
+ { type: "CYCLE", input: { cycle } },
2773
+ {
2774
+ type: "APPLY_DISCOUNT",
2775
+ input: { discountSource: "code", discountCode }
2776
+ }
2777
+ );
2778
+ },
2779
+ async removeAdjustment({ adjustmentId }) {
2780
+ try {
2781
+ const res = await fetcher(
2782
+ `/customer/v1/schedules/adjustments/${adjustmentId}`,
2783
+ { method: "DELETE" }
2784
+ );
2785
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2786
+ return { _tag: "Success", data: void 0 };
2787
+ } catch (e2) {
2788
+ return { _tag: "Failure", error: e2 };
2789
+ }
2790
+ }
2791
+ };
2792
+ }
2793
+ const SchedulesServiceContext = createContext("services/schedules");
2794
+ function isSameCalendarDay(a8, b2) {
2795
+ const da = new Date(a8);
2796
+ const db = new Date(b2);
2797
+ if (Number.isNaN(da.getTime()) || Number.isNaN(db.getTime())) return false;
2798
+ return da.toISOString().slice(0, 10) === db.toISOString().slice(0, 10);
2799
+ }
2800
+ function matcherArms(matcher, target) {
2801
+ const cycleArm = (matcher.type === "CYCLE" || matcher.type === "CYCLE_AND_DATE") && matcher.input.cycle === target.cycle;
2802
+ const dateArm = (matcher.type === "DATE" || matcher.type === "CYCLE_AND_DATE") && isSameCalendarDay(matcher.input.date, target.date);
2803
+ return { cycleArm, dateArm };
2804
+ }
2805
+ function matcherAffects(matcher, target) {
2806
+ const { cycleArm, dateArm } = matcherArms(matcher, target);
2807
+ return cycleArm || dateArm;
2808
+ }
2809
+ function matcherExactlyTargets(matcher, target) {
2810
+ const { cycleArm, dateArm } = matcherArms(matcher, target);
2811
+ if (matcher.type === "CYCLE") return cycleArm;
2812
+ if (matcher.type === "DATE") return dateArm;
2813
+ return cycleArm && dateArm;
2814
+ }
2815
+ function createSchedulesService(adapter, opts) {
2816
+ const current = d(null);
2817
+ const currentLoading = d(false);
2818
+ const hasPendingBilling = (opts == null ? void 0 : opts.hasPendingBilling) ?? d(false);
2819
+ const schedulesChangedAt = d(0);
2820
+ let reloadToken = 0;
2821
+ let currentSubscriptionId;
2822
+ async function getUpcomingOrder() {
2823
+ const result = await adapter.getUpcomingOrders({ count: 1 });
2824
+ if (result._tag === "Failure") return result;
2825
+ return { _tag: "Success", data: result.data[0] ?? null };
2826
+ }
2827
+ async function getUpcomingOrders(count, opts2) {
2828
+ return adapter.getUpcomingOrders({ count, ...opts2 });
2829
+ }
2830
+ async function reloadCurrent(subscriptionId) {
2831
+ var _a;
2832
+ const scopeChanged = currentSubscriptionId !== subscriptionId;
2833
+ currentSubscriptionId = subscriptionId;
2834
+ const token = ++reloadToken;
2835
+ currentLoading.value = true;
2836
+ const count = Math.max(((_a = current.value) == null ? void 0 : _a.length) ?? 0, 3);
2837
+ if (scopeChanged) current.value = null;
2838
+ const result = await adapter.getUpcomingOrders({
2839
+ count,
2840
+ subscriptionId
2841
+ });
2842
+ if (token !== reloadToken) return;
2843
+ currentLoading.value = false;
2844
+ if (result._tag === "Success") {
2845
+ current.value = result.data;
2846
+ }
2847
+ }
2848
+ async function notifyChanged(subscriptionId) {
2849
+ if (subscriptionId) await reloadCurrent(subscriptionId);
2850
+ schedulesChangedAt.value++;
2851
+ }
2852
+ return {
2853
+ current,
2854
+ currentLoading,
2855
+ hasPendingBilling,
2856
+ schedulesChangedAt,
2857
+ getUpcomingOrder,
2858
+ getUpcomingOrders,
2859
+ async getAdjustments() {
2860
+ return adapter.getAdjustments();
2861
+ },
2862
+ async skipOrder(params) {
2863
+ const result = await adapter.skipOrder(params);
2864
+ if (result._tag === "Success") await notifyChanged(params.subscriptionId);
2865
+ return result;
2866
+ },
2867
+ async skipItem(params) {
2868
+ const result = await adapter.skipItem(params);
2869
+ if (result._tag === "Success") await notifyChanged(params.subscriptionId);
2870
+ return result;
2871
+ },
2872
+ async unskipItem({ subscriptionId, lineId, cycle, date }) {
2873
+ const adjustments = await adapter.getAdjustments();
2874
+ if (adjustments._tag === "Failure") return adjustments;
2875
+ const covering = adjustments.data.filter(
2876
+ (a8) => a8.action.type === "SKIP_ITEM" && a8.action.input.lineIds.includes(lineId) && matcherAffects(a8.matcher, { cycle, date })
2877
+ );
2878
+ if (covering.length === 0) {
2879
+ return { _tag: "Failure", error: "No skip found for this item" };
2880
+ }
2881
+ if (covering.some((a8) => a8.action.input.lineIds.length > 1)) {
2882
+ return {
2883
+ _tag: "Failure",
2884
+ error: "This item was skipped together with other items and can't be unskipped individually"
2885
+ };
2886
+ }
2887
+ if (covering.some((a8) => !matcherExactlyTargets(a8.matcher, { cycle, date }))) {
2888
+ return {
2889
+ _tag: "Failure",
2890
+ error: "This skip also affects another order and can't be removed from here"
2891
+ };
2892
+ }
2893
+ let deleted = 0;
2894
+ for (const a8 of covering) {
2895
+ const result = await adapter.removeAdjustment({ adjustmentId: a8.id });
2896
+ if (result._tag === "Failure") {
2897
+ if (deleted > 0) await notifyChanged(subscriptionId);
2898
+ return result;
2899
+ }
2900
+ deleted++;
2901
+ }
2902
+ await notifyChanged(subscriptionId);
2903
+ return { _tag: "Success", data: void 0 };
2904
+ },
2905
+ async changeDateAdjustment(params) {
2906
+ const result = await adapter.changeDateAdjustment(params);
2907
+ if (result._tag === "Success") await notifyChanged(params.subscriptionId);
2908
+ return result;
2909
+ },
2910
+ async updateShippingAdjustment(params) {
2911
+ const result = await adapter.updateShippingAdjustment(params);
2912
+ if (result._tag === "Success") await notifyChanged(params.subscriptionId);
2913
+ return result;
2914
+ },
2915
+ async updatePaymentMethodAdjustment(params) {
2916
+ const result = await adapter.updatePaymentMethodAdjustment(params);
2917
+ if (result._tag === "Success") await notifyChanged(params.subscriptionId);
2918
+ return result;
2919
+ },
2920
+ async updateProductsAdjustment(params) {
2921
+ const result = await adapter.updateProductsAdjustment(params);
2922
+ if (result._tag === "Success") await notifyChanged(params.subscriptionId);
2923
+ return result;
2924
+ },
2925
+ async applyDiscountAdjustment(params) {
2926
+ const result = await adapter.applyDiscountAdjustment(params);
2927
+ if (result._tag === "Success") await notifyChanged(params.subscriptionId);
2928
+ return result;
2929
+ },
2930
+ async removeAdjustment(params) {
2931
+ var _a, _b, _c;
2932
+ const result = await adapter.removeAdjustment(params);
2933
+ if (result._tag === "Success") {
2934
+ const order = (_a = current.value) == null ? void 0 : _a[0];
2935
+ const subscriptionId = currentSubscriptionId ?? (order == null ? void 0 : order.subscriptionId) ?? ((_c = (_b = order == null ? void 0 : order.items) == null ? void 0 : _b[0]) == null ? void 0 : _c.subscriptionId);
2936
+ await notifyChanged(subscriptionId);
2937
+ }
2938
+ return result;
2939
+ },
2940
+ reloadCurrent
2941
+ };
2942
+ }
2943
+ const SettingsServiceContext = createContext("services/settings");
2944
+ function createSettingsService(initial = {}) {
2945
+ const current = d(initial);
2946
+ return {
2947
+ current,
2948
+ get(key) {
2949
+ return this.current.value[key];
2950
+ }
2951
+ };
2952
+ }
2953
+ function createShopService(options) {
2954
+ const contactEmail = d(options.getContactEmail());
2955
+ const service = { contactEmail };
2956
+ return service;
2957
+ }
2958
+ const ShopServiceContext = createContext("services/shop");
2959
+ const SubscriptionServiceContext = createContext("services/subscription");
2960
+ function getCurrentSubscriptionId() {
2961
+ if (typeof window === "undefined") {
2962
+ return null;
2963
+ }
2964
+ const pathname = window.location.pathname;
2965
+ const segments = pathname.split("/").filter(Boolean);
2966
+ const subscriptionIndex = segments.findIndex(
2967
+ (segment) => segment === "subscriptions" || segment === "subscription"
2968
+ );
2969
+ if (subscriptionIndex !== -1 && segments[subscriptionIndex + 1]) {
2970
+ return segments[subscriptionIndex + 1];
2971
+ }
2972
+ const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
2973
+ const lastSegment = segments[segments.length - 1];
2974
+ if (lastSegment && uuidPattern.test(lastSegment)) {
2975
+ return lastSegment;
2976
+ }
2977
+ return null;
2978
+ }
2979
+ function createSubscriptionService(adapter) {
2980
+ const current = d(null);
2981
+ const hasPendingBilling = w(
2982
+ () => {
2983
+ var _a, _b, _c, _d;
2984
+ return ((_b = (_a = current.value) == null ? void 0 : _a.lastBillingAttempt) == null ? void 0 : _b.ready) === false || ((_d = (_c = current.value) == null ? void 0 : _c.renewal) == null ? void 0 : _d.isInActiveProcessState) === true;
2985
+ }
2986
+ );
2987
+ const discountsChangedAt = d(0);
2988
+ const itemsChangedAt = d(0);
2989
+ const lastOrder = d(null);
2990
+ async function refreshAfterDiscountChange() {
2991
+ await reload();
2992
+ discountsChangedAt.value = discountsChangedAt.value + 1;
2993
+ }
2994
+ async function refreshAfterItemChange() {
2995
+ await reload();
2996
+ itemsChangedAt.value = itemsChangedAt.value + 1;
2997
+ }
2998
+ function subscriptionIdFromCurrent() {
2999
+ var _a;
3000
+ return ((_a = current.value) == null ? void 0 : _a.id) ?? null;
3001
+ }
3002
+ async function reload() {
3003
+ var _a, _b;
3004
+ const id = (_a = current.value) == null ? void 0 : _a.id;
3005
+ if (!id) return;
3006
+ const [subResult, lastOrderResult] = await Promise.all([
3007
+ adapter.getById(id),
3008
+ adapter.getLastOrder({ subscriptionId: id })
3009
+ ]);
3010
+ if (((_b = current.value) == null ? void 0 : _b.id) !== id) return;
3011
+ if (subResult._tag === "Success") current.value = subResult.data;
3012
+ if (lastOrderResult._tag === "Success") lastOrder.value = lastOrderResult.data;
3013
+ }
3014
+ const service = {
3015
+ current,
3016
+ hasPendingBilling,
3017
+ discountsChangedAt,
3018
+ itemsChangedAt,
3019
+ lastOrder,
3020
+ search: adapter.search,
3021
+ // Prefer the adapter's rich cross-subscription list (legacy endpoint, active-first).
3022
+ // Fall back to a single `search` page for adapters that don't implement it
3023
+ // (e.g. the editor mock) so the cross-subscription views still render.
3024
+ listAll: adapter.listAll ? adapter.listAll.bind(adapter) : async () => {
3025
+ const result = await adapter.search();
3026
+ if (result._tag !== "Success") return result;
3027
+ return { _tag: "Success", data: result.data.items };
3028
+ },
3029
+ getById: adapter.getById,
3030
+ getCurrentSubscriptionId,
3031
+ addItem: async (subscriptionId, item) => {
3032
+ const result = await adapter.addItem({ subscriptionId, item });
3033
+ if (result._tag === "Success") {
3034
+ await refreshAfterItemChange();
3035
+ }
3036
+ return result;
3037
+ },
3038
+ removeItem: async (subscriptionId, itemId) => {
3039
+ const result = await adapter.removeItem({ subscriptionId, itemId });
3040
+ if (result._tag === "Success") {
3041
+ await refreshAfterItemChange();
3042
+ }
3043
+ return result;
3044
+ },
3045
+ updateItem: async (subscriptionId, lineId, update) => {
3046
+ const result = await adapter.updateItem({ subscriptionId, lineId, update });
3047
+ if (result._tag === "Success") {
3048
+ await refreshAfterItemChange();
3049
+ }
3050
+ return result;
3051
+ },
3052
+ updateItemAttributes: async (subscriptionId, lineId, customAttributes) => {
3053
+ const result = await adapter.updateItemAttributes({ subscriptionId, lineId, customAttributes });
3054
+ if (result._tag === "Success") {
3055
+ await refreshAfterItemChange();
3056
+ }
3057
+ return result;
3058
+ },
3059
+ updateAttributes: async (subscriptionId, customAttributes) => {
3060
+ const result = await adapter.updateAttributes({ subscriptionId, customAttributes });
3061
+ if (result._tag === "Success") {
3062
+ await reload();
3063
+ }
3064
+ return result;
3065
+ },
3066
+ skipLineItem: async (subscriptionId, lineId, nextBillingDate) => {
3067
+ const result = await adapter.skipLineItem({ subscriptionId, lineId, nextBillingDate });
3068
+ if (result._tag === "Success") {
3069
+ await refreshAfterItemChange();
3070
+ }
3071
+ return result;
3072
+ },
3073
+ updateBoxItems: async (subscriptionId, params) => {
3074
+ const result = await adapter.updateBoxItems({ subscriptionId, ...params });
3075
+ if (result._tag === "Success") {
3076
+ await refreshAfterItemChange();
3077
+ }
3078
+ return result;
3079
+ },
3080
+ addDiscount: async (code, subscriptionId) => {
3081
+ const id = subscriptionId ?? subscriptionIdFromCurrent();
3082
+ if (id == null) {
3083
+ return {
3084
+ _tag: "Failure",
3085
+ error: { kind: "SUBSCRIPTION_NOT_FOUND" }
3086
+ };
3087
+ }
3088
+ const result = await adapter.addDiscount({ subscriptionId: id, code });
3089
+ if (result._tag === "Success") {
3090
+ await refreshAfterDiscountChange();
3091
+ }
3092
+ return result;
3093
+ },
3094
+ removeDiscount: async (discountId, subscriptionId) => {
3095
+ const id = subscriptionId ?? subscriptionIdFromCurrent();
3096
+ if (id == null) {
3097
+ return {
3098
+ _tag: "Failure",
3099
+ error: { kind: "SUBSCRIPTION_NOT_FOUND" }
3100
+ };
3101
+ }
3102
+ const result = await adapter.removeDiscount({
3103
+ subscriptionId: id,
3104
+ discountId
3105
+ });
3106
+ if (result._tag === "Success") {
3107
+ await refreshAfterDiscountChange();
1193
3108
  }
1194
- const result = await adapter.removeDiscount({
1195
- subscriptionId: id,
1196
- discountId
1197
- });
1198
- if (result._tag === "Success") {
1199
- await refreshAfterDiscountChange(id);
1200
- }
1201
3109
  return result;
1202
3110
  },
1203
3111
  reschedule: (subscriptionId, newDate) => adapter.reschedule({ subscriptionId, newDate }),
3112
+ updateCustomAttributes: (subscriptionId, customAttributes) => adapter.updateCustomAttributes({ subscriptionId, customAttributes }),
1204
3113
  resume: (subscriptionId) => adapter.resume({ subscriptionId }),
1205
3114
  pause: (subscriptionId) => adapter.pause({ subscriptionId }),
1206
3115
  reactivate: (subscriptionId) => adapter.reactivate({ subscriptionId }),
1207
3116
  cancel: (subscriptionId, reason) => adapter.cancel({ subscriptionId, reason }),
3117
+ renewNow: (params) => adapter.renewNow(params),
3118
+ changeFrequency: (subscriptionId, plan) => adapter.changeFrequency({ subscriptionId, plan }),
3119
+ reload,
1208
3120
  getAvailablePlans: (subscriptionId) => adapter.getAvailablePlans({ subscriptionId }),
1209
3121
  getDefaultSubscriptionId: () => adapter.getDefaultSubscriptionId(),
1210
- current,
1211
- discountsChangedAt: discountsChangedAtSignal,
1212
- itemsChangedAt: itemsChangedAtSignal
3122
+ getUpsellProducts: (params) => adapter.getUpsellProducts ? adapter.getUpsellProducts(params) : Promise.resolve({ _tag: "Success", data: [] }),
3123
+ addUpsellProduct: (params) => adapter.addUpsellProduct ? adapter.addUpsellProduct(params) : Promise.resolve({ _tag: "Success", data: void 0 })
1213
3124
  };
1214
3125
  return service;
1215
3126
  }
1216
- const DISCOUNT_REJECTION_REASONS = [
1217
- "INVALID_CODE",
1218
- "INACTIVE",
1219
- "NOT_APPLICABLE_TO_CUSTOMER",
1220
- "ALREADY_ADDED",
1221
- "ALREADY_USED",
1222
- "NOT_APPLICABLE_TO_PRODUCTS",
1223
- "NOT_APPLICABLE_TO_SUBSCRIPTION",
1224
- "INVALID_COMBINATION",
1225
- "QUANTITY_REQUIREMENT_NOT_MET"
1226
- ];
1227
3127
  function getDefaultBlockForAction(actionType, actionConfig, blockId) {
1228
3128
  const blockName = `action:${actionType}`;
1229
3129
  const definedBlocks = getDefinedBlocks();
@@ -2200,7 +4100,10 @@ export {
2200
4100
  P as PAGE_ROOT_SLOT,
2201
4101
  ProductServiceContext,
2202
4102
  RouterServiceContext,
4103
+ SchedulesServiceContext,
2203
4104
  S as SelectedBlockContext,
4105
+ SettingsServiceContext,
4106
+ ShopServiceContext,
2204
4107
  u as Signal,
2205
4108
  StateContext,
2206
4109
  SubscriptionServiceContext,
@@ -2219,6 +4122,12 @@ export {
2219
4122
  compactItems,
2220
4123
  I as compactLayoutDocument,
2221
4124
  w as computed,
4125
+ createApiCustomerAdapter,
4126
+ createApiLoginAdapter,
4127
+ createApiOrdersAdapter,
4128
+ createApiProductAdapter,
4129
+ createApiSchedulesAdapter,
4130
+ createApiSubscriptionAdapter,
2222
4131
  createBlockInstance,
2223
4132
  J as createBlockInstanceFromJSON,
2224
4133
  K as createBlockInstanceFromObject,
@@ -2226,15 +4135,22 @@ export {
2226
4135
  createContext,
2227
4136
  createCustomerService,
2228
4137
  O as createDefaultLayoutDocument,
4138
+ createLoginService,
4139
+ createMockCustomerAdapter,
4140
+ createMockLoginAdapter,
2229
4141
  createMockOrdersAdapter,
2230
4142
  createMockProductAdapter,
2231
4143
  createMockRouterService,
4144
+ createMockSchedulesAdapter,
2232
4145
  createMockSubscriptionAdapter,
2233
4146
  createMockWorkflowApiAdapter,
2234
4147
  createOrdersService,
2235
4148
  createProductService,
2236
4149
  createRouterService,
4150
+ createSchedulesService,
2237
4151
  Q as createSelectedBlock,
4152
+ createSettingsService,
4153
+ createShopService,
2238
4154
  createState,
2239
4155
  createSubscriptionService,
2240
4156
  R as createThemeState,