@juo/blocks 1.12.0 → 1.13.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/dist/{ExtensionRoot-9XfhG8YM.js → ExtensionRoot-peABLAZZ.js} +15 -3
  2. package/dist/block-B6s9jL7l.js +966 -0
  3. package/dist/block.d.ts +50 -0
  4. package/dist/block.d.ts.map +1 -1
  5. package/dist/block.devtools.test.d.ts +2 -0
  6. package/dist/block.devtools.test.d.ts.map +1 -0
  7. package/dist/{bridge-w5KQ-czl.js → bridge-r7Yk8fjY.js} +34 -25
  8. package/dist/context.d.ts.map +1 -1
  9. package/dist/context.devtools.test.d.ts +2 -0
  10. package/dist/context.devtools.test.d.ts.map +1 -0
  11. package/dist/core.js +1877 -377
  12. package/dist/devtools/env.d.ts +3 -0
  13. package/dist/devtools/env.d.ts.map +1 -0
  14. package/dist/devtools/hook.d.ts +35 -0
  15. package/dist/devtools/hook.d.ts.map +1 -0
  16. package/dist/devtools/index.d.ts +4 -0
  17. package/dist/devtools/index.d.ts.map +1 -0
  18. package/dist/devtools/protocol.d.ts +91 -0
  19. package/dist/devtools/protocol.d.ts.map +1 -0
  20. package/dist/devtools.js +44 -0
  21. package/dist/editor/bridge.d.ts +7 -0
  22. package/dist/editor/bridge.d.ts.map +1 -1
  23. package/dist/editor/components.d.ts.map +1 -1
  24. package/dist/editor/main.d.ts +2 -1
  25. package/dist/editor/main.d.ts.map +1 -1
  26. package/dist/editor/messages.d.ts +13 -2
  27. package/dist/editor/messages.d.ts.map +1 -1
  28. package/dist/editor/messages.test.d.ts +2 -0
  29. package/dist/editor/messages.test.d.ts.map +1 -0
  30. package/dist/editor/overlays.d.ts +14 -0
  31. package/dist/editor/overlays.d.ts.map +1 -0
  32. package/dist/editor/overlays.test.d.ts +2 -0
  33. package/dist/editor/overlays.test.d.ts.map +1 -0
  34. package/dist/editor.js +64 -45
  35. package/dist/{juo-workflow-CkyZRq60.js → juo-workflow-DY4rd256.js} +74 -873
  36. package/dist/main.d.ts +3 -1
  37. package/dist/main.d.ts.map +1 -1
  38. package/dist/overlay-wrapper.d.ts +69 -0
  39. package/dist/overlay-wrapper.d.ts.map +1 -0
  40. package/dist/overlay-wrapper.test.d.ts +2 -0
  41. package/dist/overlay-wrapper.test.d.ts.map +1 -0
  42. package/dist/overlays/OverlayService.d.ts +34 -0
  43. package/dist/overlays/OverlayService.d.ts.map +1 -0
  44. package/dist/overlays/OverlayService.test.d.ts +2 -0
  45. package/dist/overlays/OverlayService.test.d.ts.map +1 -0
  46. package/dist/preact.js +1 -1
  47. package/dist/react.js +1 -1
  48. package/dist/registry.devtools.test.d.ts +2 -0
  49. package/dist/registry.devtools.test.d.ts.map +1 -0
  50. package/dist/services/customer/adapter.d.ts +8 -0
  51. package/dist/services/customer/adapter.d.ts.map +1 -0
  52. package/dist/services/customer/customerService.d.ts +3 -2
  53. package/dist/services/customer/customerService.d.ts.map +1 -1
  54. package/dist/services/customer/index.d.ts +1 -0
  55. package/dist/services/customer/index.d.ts.map +1 -1
  56. package/dist/services/customer/types.d.ts +4 -1
  57. package/dist/services/customer/types.d.ts.map +1 -1
  58. package/dist/services/index.d.ts +3 -0
  59. package/dist/services/index.d.ts.map +1 -1
  60. package/dist/services/login/adapter.d.ts +32 -0
  61. package/dist/services/login/adapter.d.ts.map +1 -0
  62. package/dist/services/login/index.d.ts +3 -1
  63. package/dist/services/login/index.d.ts.map +1 -1
  64. package/dist/services/login/loginService.d.ts +6 -2
  65. package/dist/services/login/loginService.d.ts.map +1 -1
  66. package/dist/services/order/adapter.d.ts +9 -0
  67. package/dist/services/order/adapter.d.ts.map +1 -0
  68. package/dist/services/order/index.d.ts +5 -0
  69. package/dist/services/order/index.d.ts.map +1 -0
  70. package/dist/services/order/orderService.d.ts +7 -0
  71. package/dist/services/order/orderService.d.ts.map +1 -0
  72. package/dist/services/order/types.d.ts +21 -0
  73. package/dist/services/order/types.d.ts.map +1 -0
  74. package/dist/services/product/adapter.d.ts +27 -2
  75. package/dist/services/product/adapter.d.ts.map +1 -1
  76. package/dist/services/product/adapter.test.d.ts +2 -0
  77. package/dist/services/product/adapter.test.d.ts.map +1 -0
  78. package/dist/services/product/index.d.ts +1 -1
  79. package/dist/services/product/index.d.ts.map +1 -1
  80. package/dist/services/product/productService.d.ts.map +1 -1
  81. package/dist/services/product/types.d.ts +68 -0
  82. package/dist/services/product/types.d.ts.map +1 -1
  83. package/dist/services/schedules/adapter.d.ts +47 -0
  84. package/dist/services/schedules/adapter.d.ts.map +1 -0
  85. package/dist/services/schedules/adapter.test.d.ts +2 -0
  86. package/dist/services/schedules/adapter.test.d.ts.map +1 -0
  87. package/dist/services/schedules/index.d.ts +5 -0
  88. package/dist/services/schedules/index.d.ts.map +1 -0
  89. package/dist/services/schedules/schedulesService.d.ts +11 -0
  90. package/dist/services/schedules/schedulesService.d.ts.map +1 -0
  91. package/dist/services/schedules/schedulesService.test.d.ts +2 -0
  92. package/dist/services/schedules/schedulesService.test.d.ts.map +1 -0
  93. package/dist/services/schedules/types.d.ts +101 -0
  94. package/dist/services/schedules/types.d.ts.map +1 -0
  95. package/dist/services/settings/index.d.ts +3 -0
  96. package/dist/services/settings/index.d.ts.map +1 -0
  97. package/dist/services/settings/settingsService.d.ts +7 -0
  98. package/dist/services/settings/settingsService.d.ts.map +1 -0
  99. package/dist/services/settings/settingsService.test.d.ts +2 -0
  100. package/dist/services/settings/settingsService.test.d.ts.map +1 -0
  101. package/dist/services/settings/types.d.ts +35 -0
  102. package/dist/services/settings/types.d.ts.map +1 -0
  103. package/dist/services/shop/index.d.ts +3 -0
  104. package/dist/services/shop/index.d.ts.map +1 -0
  105. package/dist/services/shop/shopService.d.ts +12 -0
  106. package/dist/services/shop/shopService.d.ts.map +1 -0
  107. package/dist/services/shop/shopService.test.d.ts +2 -0
  108. package/dist/services/shop/shopService.test.d.ts.map +1 -0
  109. package/dist/services/shop/types.d.ts +7 -0
  110. package/dist/services/shop/types.d.ts.map +1 -0
  111. package/dist/services/subscription/adapter.d.ts +58 -2
  112. package/dist/services/subscription/adapter.d.ts.map +1 -1
  113. package/dist/services/subscription/adapter.test.d.ts +2 -0
  114. package/dist/services/subscription/adapter.test.d.ts.map +1 -0
  115. package/dist/services/subscription/index.d.ts +1 -1
  116. package/dist/services/subscription/index.d.ts.map +1 -1
  117. package/dist/services/subscription/subscriptionService.d.ts +1 -6
  118. package/dist/services/subscription/subscriptionService.d.ts.map +1 -1
  119. package/dist/services/subscription/types.d.ts +216 -9
  120. package/dist/services/subscription/types.d.ts.map +1 -1
  121. package/dist/services/subscription/types.test.d.ts +2 -0
  122. package/dist/services/subscription/types.test.d.ts.map +1 -0
  123. package/dist/theme-state.d.ts +2 -1
  124. package/dist/theme-state.d.ts.map +1 -1
  125. package/dist/theme-state.params.test.d.ts +2 -0
  126. package/dist/theme-state.params.test.d.ts.map +1 -0
  127. package/dist/theme-state.translations.test.d.ts +2 -0
  128. package/dist/theme-state.translations.test.d.ts.map +1 -0
  129. package/dist/utils/index.d.ts +6 -0
  130. package/dist/utils/index.d.ts.map +1 -1
  131. package/dist/vue/block.d.ts.map +1 -1
  132. package/dist/vue.js +25 -4
  133. package/dist/web-components/ExtensionRoot.d.ts +1 -0
  134. package/dist/web-components/ExtensionRoot.d.ts.map +1 -1
  135. package/dist/web-components-editor.js +4 -3
  136. package/dist/web-components-runtime.js +3 -2
  137. package/package.json +1 -1
package/dist/core.js CHANGED
@@ -1,8 +1,9 @@
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 { c as createContext, d, w, r as registerBlock, E, u } from "./juo-workflow-CkyZRq60.js";
5
- import { B, C, a, D, S, T, b, W, e, f, g, h, i, j, k, l, m, n, o, p, q, s, t, v, x, y, z, A, F, G, H, I, J, K, L } from "./juo-workflow-CkyZRq60.js";
4
+ import { e as createContext, d, k as w, l as defineBlock, g as createBlockInstance, h as getDefinedBlocks, r as registerBlock, E, u } from "./block-B6s9jL7l.js";
5
+ import { B, m, p, S, q, _, t, b, v, f, x, y, z, A, D, i, F, a, o, G, j, H, s, n, w as w2 } from "./block-B6s9jL7l.js";
6
+ import { D as D2, a as a2, T, W, c, b as b2, d as d2, e, g, h, i as i2 } from "./juo-workflow-DY4rd256.js";
6
7
  import { reportBlockWarning, reportBlockError } from "./observability.js";
7
8
  function createState() {
8
9
  const discountCode = d(null);
@@ -72,6 +73,111 @@ function createState() {
72
73
  };
73
74
  }
74
75
  const StateContext = createContext("state");
76
+ const OVERLAY_WRAPPER_SCHEMA = {
77
+ type: "object",
78
+ properties: {
79
+ props: {
80
+ type: "object",
81
+ properties: {
82
+ layout: {
83
+ type: "string",
84
+ enum: ["modal", "sidebar"]
85
+ },
86
+ canAbort: {
87
+ type: "boolean"
88
+ }
89
+ },
90
+ required: ["layout"],
91
+ additionalProperties: false
92
+ },
93
+ slots: {
94
+ type: "object",
95
+ properties: {
96
+ content: {}
97
+ },
98
+ additionalProperties: false
99
+ }
100
+ },
101
+ required: ["props", "slots"],
102
+ additionalProperties: false
103
+ };
104
+ function defineOverlayWrapper(opts) {
105
+ const layout = opts.layout ?? "sidebar";
106
+ const canAbort = opts.canAbort ?? true;
107
+ return defineBlock(opts.name, {
108
+ displayName: opts.title,
109
+ group: "internal",
110
+ schema: OVERLAY_WRAPPER_SCHEMA,
111
+ initialValue: () => ({
112
+ props: { layout, canAbort },
113
+ slots: {
114
+ content: [createBlockInstance(opts.content)]
115
+ }
116
+ }),
117
+ renderer: () => null
118
+ });
119
+ }
120
+ const OVERLAY_PREFIX$1 = "overlay:";
121
+ function callInitialValue(block) {
122
+ if (typeof block.initialValue !== "function") return block.initialValue;
123
+ try {
124
+ return block.initialValue();
125
+ } catch (error) {
126
+ console.warn(
127
+ `[overlays] initialValue() threw for "${block.name}"; its content block will not be hidden from the palette.`,
128
+ error
129
+ );
130
+ return void 0;
131
+ }
132
+ }
133
+ function buildOverlayRegistry(blocks) {
134
+ var _a, _b;
135
+ const registry = {};
136
+ for (const block of blocks) {
137
+ if (!block.name.startsWith(OVERLAY_PREFIX$1)) continue;
138
+ const id = block.name.slice(OVERLAY_PREFIX$1.length);
139
+ const initial = callInitialValue(block);
140
+ const first = (_b = (_a = initial == null ? void 0 : initial.slots) == null ? void 0 : _a.content) == null ? void 0 : _b[0];
141
+ const contentBlock = typeof (first == null ? void 0 : first.type) === "string" ? first.type : "";
142
+ registry[id] = {
143
+ block: block.name,
144
+ title: block.displayName ?? id,
145
+ contentBlock
146
+ };
147
+ }
148
+ return registry;
149
+ }
150
+ const OVERLAY_PREFIX = "overlay:";
151
+ const OverlayServiceContext = createContext("services/overlay");
152
+ function createOverlayService(themeState, options = {}) {
153
+ const getBlocks = options.getBlocks ?? getDefinedBlocks;
154
+ const current = w(() => {
155
+ const root = themeState.overlay.blocks.value[0];
156
+ const type = root == null ? void 0 : root.type;
157
+ if (typeof type !== "string" || !type.startsWith(OVERLAY_PREFIX)) {
158
+ return null;
159
+ }
160
+ const props = root.props ?? {};
161
+ return {
162
+ name: type,
163
+ // Defaults mirror defineOverlayWrapper's seeding in overlay-wrapper.ts — keep in sync.
164
+ layout: props.layout ?? "sidebar",
165
+ canAbort: Boolean(props.canAbort ?? true)
166
+ };
167
+ });
168
+ return {
169
+ getEditableOverlays() {
170
+ return buildOverlayRegistry(getBlocks());
171
+ },
172
+ open(name, params) {
173
+ void themeState.resolve("overlay", name, params);
174
+ },
175
+ close() {
176
+ themeState.overlay.blocks.value = [];
177
+ },
178
+ current
179
+ };
180
+ }
75
181
  function isLocalhostOrigin(origin) {
76
182
  try {
77
183
  const url = new URL(origin);
@@ -132,7 +238,7 @@ async function loadBlockRegistry(options = {}) {
132
238
  if ("registerBlocks" in module && typeof module.registerBlocks === "function") {
133
239
  module.registerBlocks();
134
240
  } else {
135
- Object.entries(module).forEach(([_, item]) => {
241
+ Object.entries(module).forEach(([_2, item]) => {
136
242
  if (typeof item === "object" && item != null) {
137
243
  registerBlock(item);
138
244
  }
@@ -156,13 +262,43 @@ async function loadBlockRegistry(options = {}) {
156
262
  function createCustomerService(options) {
157
263
  const current = d(null);
158
264
  const accountOrdersUrl = d(options.getAccountOrdersUrl());
159
- const service = {
265
+ return {
160
266
  current,
161
- accountOrdersUrl
267
+ accountOrdersUrl,
268
+ async getCurrent() {
269
+ const result = await options.adapter.getCurrent();
270
+ if (result._tag === "Success") current.value = result.data;
271
+ return result;
272
+ }
162
273
  };
163
- return service;
164
274
  }
165
275
  const CustomerServiceContext = createContext("services/customer");
276
+ function createMockCustomerAdapter() {
277
+ const customer = { id: "customer-1", name: "Customer 1" };
278
+ return {
279
+ async getCurrent() {
280
+ return { _tag: "Success", data: customer };
281
+ }
282
+ };
283
+ }
284
+ function createApiCustomerAdapter(fetcher) {
285
+ return {
286
+ async getCurrent() {
287
+ try {
288
+ const res = await fetcher("/customer/info");
289
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
290
+ const data = await res.json();
291
+ const name = [data.firstName, data.lastName].filter(Boolean).join(" ") || data.email || "";
292
+ return {
293
+ _tag: "Success",
294
+ data: { id: data.id, name, email: data.email }
295
+ };
296
+ } catch (e2) {
297
+ return { _tag: "Failure", error: e2 };
298
+ }
299
+ }
300
+ };
301
+ }
166
302
  const PARAMS = {
167
303
  DELETGATED_TOKEN: "delegated-token",
168
304
  REDIRECT_TO: "redirect-to",
@@ -176,9 +312,125 @@ const PARAMS = {
176
312
  function uuid() {
177
313
  return ("10000000-1000-4000-8000" + -1e11).replace(
178
314
  /[018]/g,
179
- (c) => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
315
+ (c2) => (c2 ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c2 / 4).toString(16)
180
316
  );
181
317
  }
318
+ function createMockLoginAdapter() {
319
+ return {
320
+ async passwordlessStart() {
321
+ return { _tag: "Success", data: void 0 };
322
+ },
323
+ async authenticate() {
324
+ return {
325
+ _tag: "Success",
326
+ data: {
327
+ access_token: "mock-access-token",
328
+ refresh_token: "mock-refresh-token",
329
+ expires_in: 3600
330
+ }
331
+ };
332
+ },
333
+ getAuthorizeUrl({ connection, state, shop, appUrl }) {
334
+ const url = new URL(`${appUrl}/auth/customer/authorize`);
335
+ url.searchParams.set("connection", connection);
336
+ url.searchParams.set("state", state);
337
+ url.searchParams.set("shop", shop);
338
+ return url.toString();
339
+ }
340
+ };
341
+ }
342
+ function createApiLoginAdapter(fetcher, opts) {
343
+ const { shopDomain, appUrl } = opts;
344
+ async function callAuthEndpoint(path, body, lang) {
345
+ const url = lang ? `${path}?lang=${lang}` : path;
346
+ const res = await fetcher(url, {
347
+ method: "POST",
348
+ headers: {
349
+ Accept: "application/json",
350
+ "Content-Type": "application/json",
351
+ "X-Shopify-Shop-Domain": shopDomain
352
+ },
353
+ body: JSON.stringify(body)
354
+ });
355
+ if (res.status === 403) {
356
+ const data = await res.json();
357
+ if ("error_description" in data) {
358
+ if (data.error_description === "Wrong email or verification code.") {
359
+ throw new AuthenticationError(AuthenticationErrorReason.IncorrectCode);
360
+ } else if (data.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
361
+ throw new AuthenticationError(AuthenticationErrorReason.TooManyTries);
362
+ } else if (data.error_description === "The verification code has expired. Please try to login again.") {
363
+ throw new AuthenticationError(AuthenticationErrorReason.Expired);
364
+ }
365
+ } else if ("code" in data && data.code === "AUTH_ERROR") {
366
+ throw new AuthenticationError(AuthenticationErrorReason.NoEmail, {
367
+ connection: data.connection
368
+ });
369
+ }
370
+ }
371
+ if (!res.ok) {
372
+ throw new Error(`Unexpected response: ${res.statusText}`);
373
+ }
374
+ return { _tag: "Success", data: void 0 };
375
+ }
376
+ return {
377
+ async passwordlessStart({ email, lang }) {
378
+ try {
379
+ await callAuthEndpoint(
380
+ "/auth/customer/passwordless/start",
381
+ { email },
382
+ lang
383
+ );
384
+ return { _tag: "Success", data: void 0 };
385
+ } catch (e2) {
386
+ return { _tag: "Failure", error: e2 };
387
+ }
388
+ },
389
+ async authenticate(params) {
390
+ try {
391
+ const res = await fetcher("/auth/customer/token", {
392
+ method: "POST",
393
+ headers: {
394
+ Accept: "application/json",
395
+ "Content-Type": "application/json",
396
+ "X-Shopify-Shop-Domain": shopDomain
397
+ },
398
+ body: JSON.stringify(params)
399
+ });
400
+ if (res.status === 403) {
401
+ const data = await res.json();
402
+ if ("error_description" in data) {
403
+ if (data.error_description === "Wrong email or verification code.") {
404
+ throw new AuthenticationError(AuthenticationErrorReason.IncorrectCode);
405
+ } else if (data.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
406
+ throw new AuthenticationError(AuthenticationErrorReason.TooManyTries);
407
+ } else if (data.error_description === "The verification code has expired. Please try to login again.") {
408
+ throw new AuthenticationError(AuthenticationErrorReason.Expired);
409
+ }
410
+ } else if ("code" in data && data.code === "AUTH_ERROR") {
411
+ throw new AuthenticationError(AuthenticationErrorReason.NoEmail, {
412
+ connection: data.connection
413
+ });
414
+ }
415
+ }
416
+ if (!res.ok) {
417
+ throw new Error(`Unexpected response: ${res.statusText}`);
418
+ }
419
+ return { _tag: "Success", data: await res.json() };
420
+ } catch (e2) {
421
+ return { _tag: "Failure", error: e2 };
422
+ }
423
+ },
424
+ getAuthorizeUrl({ connection, state, shop }) {
425
+ const url = new URL(`${appUrl}/auth/customer/authorize`);
426
+ url.searchParams.set("connection", connection);
427
+ url.searchParams.set("state", state);
428
+ url.searchParams.set("shop", shop);
429
+ return url.toString();
430
+ }
431
+ };
432
+ }
433
+ const __vite_import_meta_env__ = {};
182
434
  var AuthenticationErrorReason = /* @__PURE__ */ ((AuthenticationErrorReason2) => {
183
435
  AuthenticationErrorReason2["Expired"] = "expired";
184
436
  AuthenticationErrorReason2["IncorrectCode"] = "wrong-code";
@@ -197,6 +449,9 @@ class AuthenticationError extends Error {
197
449
  this.data = data ?? {};
198
450
  }
199
451
  }
452
+ const STORAGE_DELEGATED_TOKEN = "delegated-token";
453
+ const STORAGE_REFRESH_TOKEN_KEY = "refresh-token";
454
+ const STORAGE_STATE_KEY = "state";
200
455
  function storage(key, value) {
201
456
  const storageKey = `auth:${key}`;
202
457
  try {
@@ -212,83 +467,33 @@ function storage(key, value) {
212
467
  }
213
468
  return null;
214
469
  }
215
- const STORAGE_DELEGATED_TOKEN = "delegated-token";
216
- const STORAGE_REFRESH_TOKEN_KEY = "refresh-token";
217
- const STORAGE_STATE_KEY = "state";
218
- function createAuthFunctions(basePath, shop) {
219
- async function start(email, lang) {
220
- const response = await fetch(
221
- `${basePath}/auth/customer/passwordless/start?lang=${lang}`,
222
- {
223
- method: "POST",
224
- headers: {
225
- Accept: "application/json",
226
- "Content-Type": "application/json",
227
- "X-Shopify-Shop-Domain": shop
228
- },
229
- body: JSON.stringify({ email })
230
- }
231
- );
232
- if (response.status !== 200) {
233
- throw new Error(`Unexpected response: ${response.statusText}`);
234
- }
235
- }
236
- async function authenticate(data) {
237
- const response = await fetch(`${basePath}/auth/customer/token`, {
238
- method: "POST",
239
- headers: {
240
- Accept: "application/json",
241
- "Content-Type": "application/json",
242
- "X-Shopify-Shop-Domain": shop
243
- },
244
- body: JSON.stringify(data)
245
- });
246
- if (response.status === 403) {
247
- const data2 = await response.json();
248
- if ("error_description" in data2) {
249
- if (data2.error_description === "Wrong email or verification code.") {
250
- throw new AuthenticationError(
251
- "wrong-code"
252
- /* IncorrectCode */
253
- );
254
- } else if (data2.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
255
- throw new AuthenticationError(
256
- "too-many-tries"
257
- /* TooManyTries */
258
- );
259
- } else if (data2.error_description === "The verification code has expired. Please try to login again.") {
260
- throw new AuthenticationError(
261
- "expired"
262
- /* Expired */
263
- );
264
- }
265
- } else if ("code" in data2) {
266
- if (data2.code === "AUTH_ERROR") {
267
- throw new AuthenticationError("no-email", {
268
- connection: data2.connection
269
- });
270
- }
271
- }
272
- }
273
- if (response.status !== 200) {
274
- throw new Error(`Unexpected response: ${response.statusText}`);
275
- }
276
- return await response.json();
277
- }
278
- return { start, authenticate };
279
- }
280
470
  function nonce(len = 10) {
281
471
  return uuid().replace(/-/g, "").slice(0, len);
282
472
  }
283
- function createAuthService(apiUrl, shopDomain) {
284
- const { start: authStart, authenticate: authAuthenticate } = createAuthFunctions(apiUrl, shopDomain);
473
+ function loginService(deps) {
474
+ const apiBase = deps.shop.rootUrl.replace(/\/$/, "") + "/api";
475
+ 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";
476
+ const fetcher = (path, init) => fetch(`${apiBase}${path}`, init);
477
+ const adapter = createApiLoginAdapter(fetcher, {
478
+ shopDomain: deps.shop.domain,
479
+ appUrl
480
+ });
481
+ return createLoginService({
482
+ adapter,
483
+ router: deps.router,
484
+ formatError: deps.formatError,
485
+ shop: {
486
+ domain: deps.shop.domain,
487
+ locale: w(() => deps.getLocale())
488
+ },
489
+ onLoginSuccess: deps.onLoginSuccess
490
+ });
491
+ }
492
+ function createLoginService(init) {
493
+ const { adapter, formatError, router, shop, onLoginSuccess } = init;
285
494
  const accessToken = d(null);
286
- const delegatedToken = d(
287
- storage(STORAGE_DELEGATED_TOKEN)
288
- );
289
- const refreshToken = d(
290
- storage(STORAGE_REFRESH_TOKEN_KEY)
291
- );
495
+ const delegatedToken = d(storage(STORAGE_DELEGATED_TOKEN));
496
+ const refreshToken = d(storage(STORAGE_REFRESH_TOKEN_KEY));
292
497
  const expires = d(null);
293
498
  const state = d(storage(STORAGE_STATE_KEY));
294
499
  E(() => {
@@ -300,67 +505,28 @@ function createAuthService(apiUrl, shopDomain) {
300
505
  E(() => {
301
506
  storage(STORAGE_STATE_KEY, state.value);
302
507
  });
303
- function store(data) {
508
+ function storeTokens(data) {
304
509
  const { access_token, refresh_token, expires_in } = data;
305
510
  accessToken.value = access_token;
306
511
  refreshToken.value = refresh_token;
307
512
  expires.value = new Date(Date.now() + expires_in);
308
513
  }
309
- return {
310
- state,
311
- async start(email, lang) {
312
- await authStart(email, lang);
313
- },
314
- async authenticate(data) {
315
- store(await authAuthenticate(data));
316
- },
317
- getDelegatedToken() {
318
- return delegatedToken.value;
319
- },
320
- setDelegatedToken(token) {
321
- delegatedToken.value = token;
322
- },
323
- async getToken() {
324
- if (refreshToken.value == null) {
325
- return null;
326
- }
327
- if (accessToken.value == null || typeof accessToken.value === "string" && (expires.value == null || expires.value < /* @__PURE__ */ new Date())) {
328
- accessToken.value = new Promise(
329
- (resolve, reject) => authAuthenticate({ refreshToken: refreshToken.value }).then((data) => data).then((data) => {
330
- store(data);
331
- resolve(data.access_token);
514
+ async function getToken() {
515
+ if (refreshToken.value == null) return null;
516
+ if (accessToken.value == null || typeof accessToken.value === "string" && (expires.value == null || expires.value < /* @__PURE__ */ new Date())) {
517
+ accessToken.value = new Promise(
518
+ (resolve, reject) => adapter.authenticate({ refreshToken: refreshToken.value }).then((result) => {
519
+ if (result._tag === "Failure") {
520
+ reject(result.error);
332
521
  return;
333
- }).catch(reject)
334
- );
335
- }
336
- return Promise.resolve(accessToken.value);
337
- },
338
- logout() {
339
- accessToken.value = null;
340
- delegatedToken.value = null;
341
- refreshToken.value = null;
342
- expires.value = null;
343
- },
344
- isAuthenticated: w(
345
- () => delegatedToken.value != null || refreshToken.value != null
346
- )
347
- };
348
- }
349
- function loginService(deps) {
350
- return createLoginService({
351
- apiUrl: `${deps.shop.rootUrl.replace(/\/$/, "")}/api`,
352
- router: deps.router,
353
- formatError: deps.formatError,
354
- shop: {
355
- domain: deps.shop.domain,
356
- locale: w(() => deps.getLocale())
357
- },
358
- onLoginSuccess: deps.onLoginSuccess
359
- });
360
- }
361
- function createLoginService(init) {
362
- const { formatError, router, shop, onLoginSuccess } = init;
363
- const auth = createAuthService(init.apiUrl.replace(/\/$/, ""), shop.domain);
522
+ }
523
+ storeTokens(result.data);
524
+ resolve(result.data.access_token);
525
+ }).catch(reject)
526
+ );
527
+ }
528
+ return Promise.resolve(accessToken.value);
529
+ }
364
530
  const userEmail = d(null);
365
531
  async function onSuccess(query) {
366
532
  await (onLoginSuccess == null ? void 0 : onLoginSuccess());
@@ -373,13 +539,9 @@ function createLoginService(init) {
373
539
  queryParams[key] = value;
374
540
  }
375
541
  });
376
- router.push({
377
- path: decodeURIComponent(redirect),
378
- query: queryParams
379
- });
542
+ router.push({ path: decodeURIComponent(redirect), query: queryParams });
380
543
  } else {
381
- const redirect = query.get(PARAMS.REDIRECT_TO);
382
- router.replace(decodeURIComponent(redirect));
544
+ router.replace(decodeURIComponent(query.get(PARAMS.REDIRECT_TO)));
383
545
  }
384
546
  } else {
385
547
  router.replace("/");
@@ -391,16 +553,17 @@ function createLoginService(init) {
391
553
  async start(email) {
392
554
  try {
393
555
  userEmail.value = email;
394
- await auth.start(
395
- userEmail.value,
396
- shop.locale instanceof u ? shop.locale.value : shop.locale
397
- );
556
+ const lang = shop.locale instanceof u ? shop.locale.value : shop.locale;
557
+ const result = await adapter.passwordlessStart({
558
+ email: userEmail.value,
559
+ lang
560
+ });
561
+ if (result._tag === "Failure") {
562
+ return { _tag: "Failure", error: formatError(result.error) };
563
+ }
398
564
  return { _tag: "Success", data: void 0 };
399
565
  } catch (error) {
400
- return {
401
- _tag: "Failure",
402
- error: formatError(error)
403
- };
566
+ return { _tag: "Failure", error: formatError(error) };
404
567
  }
405
568
  },
406
569
  async verify(email, code) {
@@ -411,63 +574,61 @@ function createLoginService(init) {
411
574
  };
412
575
  }
413
576
  try {
414
- const result = await auth.authenticate({
577
+ const result = await adapter.authenticate({
415
578
  email: userEmail.value,
416
579
  code
417
580
  });
581
+ if (result._tag === "Failure") {
582
+ return { _tag: "Failure", error: formatError(result.error) };
583
+ }
584
+ storeTokens(result.data);
418
585
  await onSuccess();
419
- return { _tag: "Success", data: result };
586
+ return { _tag: "Success", data: void 0 };
420
587
  } catch (error) {
421
- return {
422
- _tag: "Failure",
423
- error: formatError(error)
424
- };
588
+ return { _tag: "Failure", error: formatError(error) };
425
589
  }
426
590
  }
427
591
  }
428
592
  },
429
593
  social: {
430
594
  async start(provider) {
431
- function socialUrl(key, shop2) {
432
- auth.state.value = nonce();
433
- const url = new URL(
434
- // TODO: double check admin url
435
- `${"https://admin.juo.io"}/auth/customer/authorize`
436
- );
437
- url.searchParams.append("connection", key);
438
- url.searchParams.append("state", auth.state.value ?? "");
439
- url.searchParams.append("shop", shop2);
440
- return url.toString();
441
- }
442
- window.location.href = socialUrl(
443
- provider === "Facebook" ? "facebook" : "google-oauth2",
444
- shop.domain
445
- );
446
- return {
447
- _tag: "Success",
448
- data: void 0
449
- };
595
+ state.value = nonce();
596
+ const connection = provider === "Facebook" ? "facebook" : "google-oauth2";
597
+ window.location.href = adapter.getAuthorizeUrl({
598
+ connection,
599
+ state: state.value ?? "",
600
+ shop: shop.domain,
601
+ appUrl: (
602
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
603
+ (typeof import.meta !== "undefined" ? __vite_import_meta_env__ == null ? void 0 : __vite_import_meta_env__.VITE_APP_URL : void 0) ?? "https://admin.juo.io"
604
+ )
605
+ });
606
+ return { _tag: "Success", data: void 0 };
450
607
  },
451
608
  async verify(code) {
452
609
  try {
453
- const result = await auth.authenticate({ code });
610
+ const result = await adapter.authenticate({ code });
611
+ if (result._tag === "Failure") {
612
+ return { _tag: "Failure", error: formatError(result.error) };
613
+ }
614
+ storeTokens(result.data);
454
615
  await onSuccess();
455
- return { _tag: "Success", data: result };
616
+ return { _tag: "Success", data: void 0 };
456
617
  } catch (error) {
457
- return {
458
- _tag: "Failure",
459
- error: formatError(error)
460
- };
618
+ return { _tag: "Failure", error: formatError(error) };
461
619
  }
462
620
  }
463
621
  },
464
622
  async logout() {
465
- await auth.logout();
623
+ accessToken.value = null;
624
+ delegatedToken.value = null;
625
+ refreshToken.value = null;
626
+ expires.value = null;
466
627
  return { _tag: "Success", data: void 0 };
467
628
  },
468
629
  async handleQueryParams(query) {
469
630
  if (query.has(PARAMS.DELETGATED_TOKEN)) {
470
- auth.setDelegatedToken(query.get(PARAMS.DELETGATED_TOKEN));
631
+ delegatedToken.value = query.get(PARAMS.DELETGATED_TOKEN);
471
632
  await onSuccess(query);
472
633
  return { _tag: "Success", data: void 0 };
473
634
  }
@@ -479,16 +640,13 @@ function createLoginService(init) {
479
640
  }
480
641
  const code = query.get(PARAMS.CODE);
481
642
  if (code != null) {
482
- if (query.get(PARAMS.STATE) !== auth.state.value) {
483
- return {
484
- _tag: "Failure",
485
- error: formatError("Invalid state")
486
- };
643
+ if (query.get(PARAMS.STATE) !== state.value) {
644
+ return { _tag: "Failure", error: formatError("Invalid state") };
487
645
  } else {
488
646
  return service.social.verify(code);
489
647
  }
490
648
  }
491
- if (query.get(PARAMS.REFRESH) === "1" && await auth.getToken() != null) {
649
+ if (query.get(PARAMS.REFRESH) === "1" && await getToken() != null) {
492
650
  await onSuccess();
493
651
  return { _tag: "Success", data: void 0 };
494
652
  }
@@ -505,8 +663,20 @@ function createLoginService(init) {
505
663
  }
506
664
  return { _tag: "Success", data: void 0 };
507
665
  },
508
- isAuthenticated: auth.isAuthenticated,
509
- getToken: () => auth.getToken()
666
+ isAuthenticated: w(
667
+ () => delegatedToken.value != null || refreshToken.value != null
668
+ ),
669
+ getToken,
670
+ async getAuthHeaders() {
671
+ if (delegatedToken.value) {
672
+ return { "X-Delegated-Token": delegatedToken.value };
673
+ }
674
+ const token = await getToken();
675
+ if (token) {
676
+ return { Authorization: `Bearer ${token}` };
677
+ }
678
+ return {};
679
+ }
510
680
  };
511
681
  return service;
512
682
  }
@@ -591,7 +761,7 @@ function createMockProductAdapter() {
591
761
  id: "sample-1",
592
762
  title: "Hydrating Gel Cleanser for Face and Eye Area",
593
763
  featuredImage: {
594
- url: "https://via.placeholder.com/400x400?text=Product+1"
764
+ url: "https://placehold.co/400x400?text=Product+1"
595
765
  },
596
766
  hasOnlyDefaultVariant: true,
597
767
  variants: [
@@ -599,7 +769,7 @@ function createMockProductAdapter() {
599
769
  id: "sample-1-1",
600
770
  title: "150 ml bottle",
601
771
  image: {
602
- url: "https://via.placeholder.com/400x400?text=Product+1"
772
+ url: "https://placehold.co/400x400?text=Product+1"
603
773
  },
604
774
  price: {
605
775
  amount: "12.49",
@@ -625,7 +795,7 @@ function createMockProductAdapter() {
625
795
  id: "sample-2",
626
796
  title: "More COLLAGEN",
627
797
  featuredImage: {
628
- url: "https://via.placeholder.com/400x400?text=Product+2"
798
+ url: "https://placehold.co/400x400?text=Product+2"
629
799
  },
630
800
  hasOnlyDefaultVariant: true,
631
801
  variants: [
@@ -633,7 +803,7 @@ function createMockProductAdapter() {
633
803
  id: "sample-2-1",
634
804
  title: "blackcurrant",
635
805
  image: {
636
- url: "https://via.placeholder.com/400x400?text=Product+2"
806
+ url: "https://placehold.co/400x400?text=Product+2"
637
807
  },
638
808
  price: {
639
809
  amount: "12.49",
@@ -654,7 +824,7 @@ function createMockProductAdapter() {
654
824
  id: "sample-3",
655
825
  title: "Gentle deodorant for underarms and bust",
656
826
  featuredImage: {
657
- url: "https://via.placeholder.com/400x400?text=Product+3"
827
+ url: "https://placehold.co/400x400?text=Product+3"
658
828
  },
659
829
  hasOnlyDefaultVariant: false,
660
830
  variants: [
@@ -662,7 +832,7 @@ function createMockProductAdapter() {
662
832
  id: "sample-3-1",
663
833
  title: "Green tea and jasmine",
664
834
  image: {
665
- url: "https://via.placeholder.com/400x400?text=Product+3a"
835
+ url: "https://placehold.co/400x400?text=Product+3a"
666
836
  },
667
837
  price: {
668
838
  amount: "6.7",
@@ -680,7 +850,7 @@ function createMockProductAdapter() {
680
850
  id: "sample-3-2",
681
851
  title: "Lavender and mint",
682
852
  image: {
683
- url: "https://via.placeholder.com/400x400?text=Product+3b"
853
+ url: "https://placehold.co/400x400?text=Product+3b"
684
854
  },
685
855
  price: {
686
856
  amount: "7.5",
@@ -712,6 +882,48 @@ function createMockProductAdapter() {
712
882
  }
713
883
  };
714
884
  }
885
+ function toAvailableProduct(product) {
886
+ return {
887
+ id: product.id,
888
+ title: product.title,
889
+ description: `Sample description for ${product.title}.`,
890
+ featuredImage: product.featuredImage ?? void 0,
891
+ hasOnlyDefaultVariant: product.hasOnlyDefaultVariant,
892
+ variants: product.variants.map((variant) => {
893
+ var _a, _b, _c;
894
+ const base = Number.parseFloat(variant.price.amount);
895
+ const sub = (_b = (_a = variant.purchaseOptions) == null ? void 0 : _a[0]) == null ? void 0 : _b.price.amount;
896
+ const current = sub == null ? base : Number.parseFloat(sub);
897
+ return {
898
+ id: variant.id,
899
+ availableForSale: true,
900
+ title: variant.title,
901
+ currentPrice: { amount: current, currencyCode: variant.price.currencyCode },
902
+ basePrice: { amount: base, currencyCode: variant.price.currencyCode },
903
+ image: variant.image ?? void 0,
904
+ recurring: (((_c = variant.purchaseOptions) == null ? void 0 : _c.length) ?? 0) > 0
905
+ };
906
+ })
907
+ };
908
+ }
909
+ const sampleBox = {
910
+ id: "sample-box-1",
911
+ title: "Build Your Own Box",
912
+ minProductsCount: 3,
913
+ parentProduct: "sample-1",
914
+ tiers: [
915
+ { value: 5, threshold: 3 },
916
+ { value: 10, threshold: 5 }
917
+ ],
918
+ variants: products.map((product) => ({
919
+ id: product.id,
920
+ variants: product.variants.map((variant) => ({ id: variant.id }))
921
+ })),
922
+ freeVariants: [],
923
+ thresholdType: "LINES_COUNT",
924
+ type: "PERCENTAGE",
925
+ restrictQuantityToTiers: false
926
+ };
715
927
  return {
716
928
  async getById(id) {
717
929
  await new Promise((resolve) => setTimeout(resolve, 100));
@@ -725,6 +937,27 @@ function createMockProductAdapter() {
725
937
  );
726
938
  return product == null ? null : processProduct(product);
727
939
  },
940
+ async getProductsByIds({ ids }) {
941
+ await new Promise((resolve) => setTimeout(resolve, 100));
942
+ return products.filter((product) => ids.includes(product.id)).map(({ variants: _variants, ...product }) => product);
943
+ },
944
+ async getBoxVariants({ ids }) {
945
+ await new Promise((resolve) => setTimeout(resolve, 100));
946
+ return products.filter((product) => ids.includes(product.id)).map(toAvailableProduct);
947
+ },
948
+ async getAvailableProducts({ q: q2 }) {
949
+ await new Promise((resolve) => setTimeout(resolve, 100));
950
+ const queryLower = q2 == null ? void 0 : q2.toLowerCase();
951
+ return products.filter(
952
+ (product) => !queryLower || product.title.toLowerCase().includes(queryLower) || product.variants.some(
953
+ (variant) => variant.title.toLowerCase().includes(queryLower)
954
+ )
955
+ ).map(toAvailableProduct);
956
+ },
957
+ async getBox({ id }) {
958
+ await new Promise((resolve) => setTimeout(resolve, 100));
959
+ return id === sampleBox.id ? sampleBox : null;
960
+ },
728
961
  async search(opts) {
729
962
  await new Promise((resolve) => setTimeout(resolve, 100));
730
963
  let filteredProducts = [...products];
@@ -765,49 +998,232 @@ function createMockProductAdapter() {
765
998
  }
766
999
  };
767
1000
  }
768
- const ProductServiceContext = createContext("services/product");
769
- function createProductService(adapter) {
1001
+ function mapVariant(variant, currencyCode) {
1002
+ var _a;
770
1003
  return {
771
- async getById(id) {
772
- return adapter.getById(id);
773
- },
774
- async getByVariantId(variantId) {
775
- return adapter.getByVariantId(variantId);
776
- },
777
- async search(opts) {
778
- return adapter.search(opts);
779
- }
1004
+ id: variant.id,
1005
+ title: variant.title,
1006
+ image: variant.image == null ? null : { url: variant.image.url, altText: variant.image.altText ?? void 0 },
1007
+ price: { amount: variant.price.toFixed(2), currencyCode },
1008
+ ...((_a = variant.purchaseOptions) == null ? void 0 : _a.length) ? {
1009
+ purchaseOptions: variant.purchaseOptions.map((opt) => ({
1010
+ name: opt.name,
1011
+ price: { amount: opt.price.toFixed(2), currencyCode },
1012
+ interval: opt.interval
1013
+ }))
1014
+ } : {}
780
1015
  };
781
1016
  }
782
- function routeNameTableFromPages(pages) {
783
- const table = {};
784
- for (const page of pages) {
785
- const names = page.routeNames;
786
- if (names.length === 0) continue;
787
- for (const name of names) {
788
- table[name] = page.path;
789
- }
790
- }
791
- return table;
1017
+ function mapProduct(product, currencyCode) {
1018
+ return {
1019
+ id: product.id,
1020
+ title: product.title,
1021
+ featuredImage: product.image == null ? null : { url: product.image.url, altText: product.image.altText ?? void 0 },
1022
+ hasOnlyDefaultVariant: product.variants.length <= 1,
1023
+ variants: {
1024
+ items: product.variants.map((v2) => mapVariant(v2, currencyCode)),
1025
+ pageInfo: {
1026
+ hasNextPage: false,
1027
+ hasPrevPage: false,
1028
+ startCursor: null,
1029
+ endCursor: null
1030
+ }
1031
+ },
1032
+ collections: product.collections
1033
+ };
792
1034
  }
793
- function createRouterService(options) {
794
- const { pages, pushImplementation } = options;
795
- const routeNameToPagePath = routeNameTableFromPages(pages);
796
- const pathSet = new Set(pages.map((p2) => p2.path));
797
- const navigationSubscribers = [];
798
- function notifySubscribers(logicalPath) {
799
- for (const cb of navigationSubscribers) cb({ path: logicalPath });
800
- }
801
- function notifyRouteName(routeName) {
802
- const pagePath = routeNameToPagePath[String(routeName)];
803
- if (pagePath != null) notifySubscribers(pagePath);
804
- }
805
- function notifyLogicalPath(logicalPath) {
806
- if (pathSet.has(logicalPath)) notifySubscribers(logicalPath);
1035
+ function createApiProductAdapter(fetcher, opts = { currencyCode: "USD" }) {
1036
+ const { currencyCode } = opts;
1037
+ async function fetchProducts(params) {
1038
+ const query = params.toString() ? `?${params.toString()}` : "";
1039
+ const res = await fetcher(`/customer/v1/products${query}`);
1040
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
1041
+ return res.json();
807
1042
  }
808
- const routerService = {
809
- push(to) {
810
- pushImplementation(to);
1043
+ return {
1044
+ async getById(id) {
1045
+ try {
1046
+ const params = new URLSearchParams({ query: `id:"${id}"`, limit: "1" });
1047
+ const body = await fetchProducts(params);
1048
+ return body.data.length > 0 ? mapProduct(body.data[0], currencyCode) : null;
1049
+ } catch {
1050
+ return null;
1051
+ }
1052
+ },
1053
+ async getByVariantId(variantId) {
1054
+ try {
1055
+ const params = new URLSearchParams({
1056
+ query: `variants.id:"${variantId}"`,
1057
+ limit: "1"
1058
+ });
1059
+ const body = await fetchProducts(params);
1060
+ return body.data.length > 0 ? mapProduct(body.data[0], currencyCode) : null;
1061
+ } catch {
1062
+ return null;
1063
+ }
1064
+ },
1065
+ async search(opts2) {
1066
+ try {
1067
+ const params = new URLSearchParams();
1068
+ if (opts2 == null ? void 0 : opts2.query) {
1069
+ params.set("query", `"${opts2.query.trim().replace(/"/g, '\\"')}"`);
1070
+ }
1071
+ if (opts2 == null ? void 0 : opts2.pagination) {
1072
+ if ("after" in opts2.pagination)
1073
+ params.set("after", opts2.pagination.after);
1074
+ else if ("before" in opts2.pagination)
1075
+ params.set("before", opts2.pagination.before);
1076
+ }
1077
+ const body = await fetchProducts(params);
1078
+ return {
1079
+ items: body.data.map((p2) => mapProduct(p2, currencyCode)),
1080
+ pageInfo: {
1081
+ hasNextPage: body.hasNextPage,
1082
+ hasPrevPage: body.hasPrevPage,
1083
+ startCursor: body.startCursor,
1084
+ endCursor: body.endCursor
1085
+ }
1086
+ };
1087
+ } catch {
1088
+ return {
1089
+ items: [],
1090
+ pageInfo: {
1091
+ hasNextPage: false,
1092
+ hasPrevPage: false,
1093
+ startCursor: null,
1094
+ endCursor: null
1095
+ }
1096
+ };
1097
+ }
1098
+ },
1099
+ async getProductsByIds({ ids }) {
1100
+ const params = new URLSearchParams();
1101
+ for (const id of ids) params.append("ids", id);
1102
+ const res = await fetcher(`/customer/products/by-ids?${params.toString()}`);
1103
+ if (!res.ok) return [];
1104
+ const data = await res.json();
1105
+ return (data ?? []).map((p2) => ({
1106
+ id: p2.id,
1107
+ title: p2.title ?? "",
1108
+ featuredImage: p2.featuredImage ? { url: p2.featuredImage.url, altText: p2.featuredImage.altText } : null,
1109
+ hasOnlyDefaultVariant: true,
1110
+ collections: []
1111
+ }));
1112
+ },
1113
+ async getBoxVariants({ ids, subscriptionId }) {
1114
+ const params = new URLSearchParams();
1115
+ for (const id of ids) params.append("ids", id);
1116
+ params.set("subscriptionId", subscriptionId);
1117
+ const res = await fetcher(`/customer/products/variants?${params.toString()}`);
1118
+ if (!res.ok) return [];
1119
+ const data = await res.json();
1120
+ return (data ?? []).map((p2) => {
1121
+ var _a;
1122
+ return {
1123
+ id: p2.id,
1124
+ title: p2.title,
1125
+ description: p2.description ?? "",
1126
+ featuredImage: p2.featuredImage,
1127
+ hasOnlyDefaultVariant: p2.hasOnlyDefaultVariant ?? (((_a = p2.variants) == null ? void 0 : _a.length) ?? 0) <= 1,
1128
+ variants: (p2.variants ?? []).map((v2) => ({
1129
+ id: v2.id,
1130
+ availableForSale: v2.availableForSale,
1131
+ title: v2.title,
1132
+ currentPrice: v2.currentPrice,
1133
+ basePrice: v2.basePrice,
1134
+ image: v2.image,
1135
+ recurring: v2.recurring
1136
+ }))
1137
+ };
1138
+ });
1139
+ },
1140
+ async getAvailableProducts({ subscriptionId, lineId, q: q2, mode, action, pagination }) {
1141
+ const params = new URLSearchParams();
1142
+ params.set("action", action);
1143
+ if (q2) params.set("q", q2);
1144
+ if (mode) params.set("mode", mode);
1145
+ if (pagination && "after" in pagination) params.set("after", pagination.after);
1146
+ if (pagination && "before" in pagination) params.set("before", pagination.before);
1147
+ const res = await fetcher(
1148
+ `/customer/subscriptions/${subscriptionId}/lines/${lineId}/available-products?${params.toString()}`
1149
+ );
1150
+ if (!res.ok) return [];
1151
+ const data = await res.json();
1152
+ return (data ?? []).map((p2) => {
1153
+ var _a;
1154
+ return {
1155
+ id: p2.id,
1156
+ title: p2.title,
1157
+ description: p2.description ?? "",
1158
+ featuredImage: p2.featuredImage,
1159
+ hasOnlyDefaultVariant: p2.hasOnlyDefaultVariant ?? (((_a = p2.variants) == null ? void 0 : _a.length) ?? 0) <= 1,
1160
+ variants: (p2.variants ?? []).map((v2) => ({
1161
+ id: v2.id,
1162
+ availableForSale: v2.availableForSale,
1163
+ title: v2.title,
1164
+ currentPrice: v2.currentPrice,
1165
+ basePrice: v2.basePrice,
1166
+ image: v2.image,
1167
+ recurring: v2.recurring
1168
+ }))
1169
+ };
1170
+ });
1171
+ },
1172
+ async getBox({ id }) {
1173
+ const res = await fetcher(`/customer/boxes/${id}`);
1174
+ if (!res.ok) return null;
1175
+ const data = await res.json();
1176
+ return { id, ...data };
1177
+ }
1178
+ };
1179
+ }
1180
+ const ProductServiceContext = createContext("services/product");
1181
+ function createProductService(adapter) {
1182
+ return {
1183
+ async getById(id) {
1184
+ return adapter.getById(id);
1185
+ },
1186
+ async getByVariantId(variantId) {
1187
+ return adapter.getByVariantId(variantId);
1188
+ },
1189
+ async search(opts) {
1190
+ return adapter.search(opts);
1191
+ },
1192
+ getProductsByIds: (ids) => adapter.getProductsByIds({ ids }),
1193
+ getBoxVariants: (ids, subscriptionId) => adapter.getBoxVariants({ ids, subscriptionId }),
1194
+ getAvailableProducts: (params) => adapter.getAvailableProducts(params),
1195
+ getBox: (id) => adapter.getBox({ id })
1196
+ };
1197
+ }
1198
+ function routeNameTableFromPages(pages) {
1199
+ const table = {};
1200
+ for (const page of pages) {
1201
+ const names = page.routeNames;
1202
+ if (names.length === 0) continue;
1203
+ for (const name of names) {
1204
+ table[name] = page.path;
1205
+ }
1206
+ }
1207
+ return table;
1208
+ }
1209
+ function createRouterService(options) {
1210
+ const { pages, pushImplementation } = options;
1211
+ const routeNameToPagePath = routeNameTableFromPages(pages);
1212
+ const pathSet = new Set(pages.map((p2) => p2.path));
1213
+ const navigationSubscribers = [];
1214
+ function notifySubscribers(logicalPath) {
1215
+ for (const cb of navigationSubscribers) cb({ path: logicalPath });
1216
+ }
1217
+ function notifyRouteName(routeName) {
1218
+ const pagePath = routeNameToPagePath[String(routeName)];
1219
+ if (pagePath != null) notifySubscribers(pagePath);
1220
+ }
1221
+ function notifyLogicalPath(logicalPath) {
1222
+ if (pathSet.has(logicalPath)) notifySubscribers(logicalPath);
1223
+ }
1224
+ const routerService = {
1225
+ push(to) {
1226
+ pushImplementation(to);
811
1227
  },
812
1228
  subscribe(callback) {
813
1229
  navigationSubscribers.push(callback);
@@ -831,6 +1247,70 @@ function createMockRouterService() {
831
1247
  }).routerService;
832
1248
  }
833
1249
  function createMockSubscriptionAdapter(options = {}) {
1250
+ const richBase = (id, itemId, renewalId) => ({
1251
+ id,
1252
+ customerId: "customer_1",
1253
+ items: [
1254
+ {
1255
+ id: itemId,
1256
+ // Aligned with the mock product catalog (createMockProductAdapter):
1257
+ // sample-3 "Gentle deodorant", variant sample-3-1 "Green tea and jasmine",
1258
+ // recurring price 5.36 EUR (10% off → 4.82 with SAVE10).
1259
+ productId: "sample-3",
1260
+ variantId: "sample-3-1",
1261
+ quantity: 2,
1262
+ price: { amount: "5.36", currencyCode: "EUR" },
1263
+ nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15"),
1264
+ isUpsell: false,
1265
+ isVirtual: false,
1266
+ nextBillingDate: "2025-11-15T00:00:00Z",
1267
+ currentPrice: { amount: "5.36", currencyCode: "EUR" },
1268
+ lineDiscountedPrice: { amount: "4.82", currencyCode: "EUR" },
1269
+ variantAvailableForSale: true,
1270
+ title: "Gentle deodorant for underarms and bust",
1271
+ variantTitle: "Green tea and jasmine",
1272
+ variantImage: { url: "https://placehold.co/400x400?text=Product+3a" },
1273
+ billingPolicy: { intervalCount: 1, interval: "MONTH" },
1274
+ deliveryPolicy: { intervalCount: 1, interval: "MONTH" }
1275
+ }
1276
+ ],
1277
+ discounts: [
1278
+ {
1279
+ id: "discount_1",
1280
+ type: "percentage",
1281
+ value: 10,
1282
+ code: "SAVE10",
1283
+ description: "10% off subscription"
1284
+ }
1285
+ ],
1286
+ status: "active",
1287
+ nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15"),
1288
+ frequency: "1 MONTH",
1289
+ createdAt: "2024-10-01T00:00:00Z",
1290
+ updatedAt: "2024-10-14T00:00:00Z",
1291
+ billingPolicy: { intervalCount: 1, interval: "MONTH", minCycles: 6 },
1292
+ deliveryPolicy: {
1293
+ intervalCount: 1,
1294
+ interval: "MONTH",
1295
+ anchors: [{ day: 15, type: "MONTHDAY" }]
1296
+ },
1297
+ nextBillingDate: "2025-11-15T00:00:00Z",
1298
+ nextBillingAttempt: { date: "2025-11-15T00:00:00Z", attempt: 1, ready: true },
1299
+ lastBillingAttempt: { date: "2025-10-15T00:00:00Z", ready: true },
1300
+ nextFulfillment: "2025-11-17T00:00:00Z",
1301
+ estimatedOrderDate: "2025-11-13T00:00:00Z",
1302
+ customerPaymentMethod: {
1303
+ id: "pm_1",
1304
+ instrument: { type: "credit_card", brand: "visa", maskedNumber: "************4242" },
1305
+ active: true,
1306
+ isRecurring: true
1307
+ },
1308
+ requiredBillingCyclesLeft: 3,
1309
+ renewal: { id: renewalId, isInActiveProcessState: false },
1310
+ lastPaymentStatus: "succeeded",
1311
+ pausePeriod: null,
1312
+ isEditable: true
1313
+ });
834
1314
  const mockSubscriptions = [
835
1315
  {
836
1316
  id: "sub_1",
@@ -838,14 +1318,27 @@ function createMockSubscriptionAdapter(options = {}) {
838
1318
  items: [
839
1319
  {
840
1320
  id: "item_1",
841
- productId: "product_1",
842
- variantId: "variant_1",
1321
+ // Aligned with the mock product catalog (createMockProductAdapter):
1322
+ // sample-1 "Hydrating Gel Cleanser" (default variant), recurring price
1323
+ // 9.99 EUR (10% off → 8.99 with SAVE10).
1324
+ productId: "sample-1",
1325
+ variantId: "sample-1-1",
843
1326
  quantity: 2,
844
- price: { amount: "29.99", currencyCode: "USD" },
1327
+ price: { amount: "9.99", currencyCode: "EUR" },
845
1328
  nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15"),
1329
+ isUpsell: false,
1330
+ isVirtual: false,
1331
+ nextBillingDate: "2025-11-15T00:00:00Z",
1332
+ currentPrice: { amount: "9.99", currencyCode: "EUR" },
1333
+ lineDiscountedPrice: { amount: "8.99", currencyCode: "EUR" },
1334
+ variantAvailableForSale: true,
1335
+ title: "Hydrating Gel Cleanser for Face and Eye Area",
1336
+ variantImage: { url: "https://placehold.co/400x400?text=Product+1" },
1337
+ billingPolicy: { intervalCount: 1, interval: "MONTH" },
1338
+ deliveryPolicy: { intervalCount: 1, interval: "MONTH" },
846
1339
  customAttributes: [
847
- { key: "roast_level", value: "medium" },
848
- { key: "grind_type", value: "whole_bean" }
1340
+ { key: "skin_type", value: "combination" },
1341
+ { key: "fragrance", value: "fragrance_free" }
849
1342
  ]
850
1343
  }
851
1344
  ],
@@ -863,9 +1356,31 @@ function createMockSubscriptionAdapter(options = {}) {
863
1356
  frequency: "1 MONTH",
864
1357
  createdAt: "2024-10-01T00:00:00Z",
865
1358
  updatedAt: "2024-10-14T00:00:00Z",
1359
+ billingPolicy: { intervalCount: 1, interval: "MONTH", minCycles: 6 },
1360
+ deliveryPolicy: {
1361
+ intervalCount: 1,
1362
+ interval: "MONTH",
1363
+ anchors: [{ day: 15, type: "MONTHDAY" }]
1364
+ },
1365
+ nextBillingDate: "2025-11-15T00:00:00Z",
1366
+ nextBillingAttempt: { date: "2025-11-15T00:00:00Z", attempt: 1, ready: true },
1367
+ lastBillingAttempt: { date: "2025-10-15T00:00:00Z", ready: true },
1368
+ nextFulfillment: "2025-11-17T00:00:00Z",
1369
+ estimatedOrderDate: "2025-11-13T00:00:00Z",
1370
+ customerPaymentMethod: {
1371
+ id: "pm_1",
1372
+ instrument: { type: "credit_card", brand: "visa", maskedNumber: "************4242" },
1373
+ active: true,
1374
+ isRecurring: true
1375
+ },
1376
+ requiredBillingCyclesLeft: 3,
1377
+ renewal: { id: "ren_1", isInActiveProcessState: false },
1378
+ lastPaymentStatus: "succeeded",
1379
+ pausePeriod: null,
1380
+ isEditable: true,
866
1381
  customAttributes: [
867
1382
  { key: "subscription_tier", value: "premium" },
868
- { key: "gift_message", value: "Enjoy your coffee!" }
1383
+ { key: "gift_message", value: "Enjoy your skincare routine!" }
869
1384
  ]
870
1385
  },
871
1386
  {
@@ -874,11 +1389,17 @@ function createMockSubscriptionAdapter(options = {}) {
874
1389
  items: [
875
1390
  {
876
1391
  id: "item_2",
877
- productId: "product_2",
878
- variantId: "variant_2",
1392
+ // Aligned with the mock product catalog (createMockProductAdapter):
1393
+ // sample-2 "More COLLAGEN" (default variant), recurring price 10.99 EUR.
1394
+ productId: "sample-2",
1395
+ variantId: "sample-2-1",
879
1396
  quantity: 1,
880
- price: { amount: "49.99", currencyCode: "USD" },
1397
+ price: { amount: "10.99", currencyCode: "EUR" },
881
1398
  nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-20"),
1399
+ title: "More COLLAGEN",
1400
+ variantImage: { url: "https://placehold.co/400x400?text=Product+2" },
1401
+ billingPolicy: { intervalCount: 2, interval: "WEEK" },
1402
+ deliveryPolicy: { intervalCount: 2, interval: "WEEK" },
882
1403
  customAttributes: []
883
1404
  }
884
1405
  ],
@@ -889,17 +1410,66 @@ function createMockSubscriptionAdapter(options = {}) {
889
1410
  createdAt: "2024-09-15T00:00:00Z",
890
1411
  updatedAt: "2024-10-10T00:00:00Z",
891
1412
  customAttributes: []
1413
+ },
1414
+ // Per-status fixtures: groundwork for the deferred editor preview picker
1415
+ // (docs/superpowers/specs/2026-06-17-renewalsummary-parity-and-mock-groundwork-design.md §9).
1416
+ // Not consumed yet — the editor still seeds the active sub_1 via getDefaultSubscriptionId().
1417
+ {
1418
+ ...richBase("sub_paused", "item_paused", "ren_paused"),
1419
+ status: "paused",
1420
+ pausePeriod: { start: "2025-11-01T00:00:00Z", end: "2026-01-01T00:00:00Z" }
1421
+ },
1422
+ {
1423
+ ...richBase("sub_canceled", "item_canceled", "ren_canceled"),
1424
+ status: "canceled",
1425
+ pausePeriod: null
1426
+ },
1427
+ {
1428
+ ...richBase("sub_failed", "item_failed", "ren_failed"),
1429
+ status: "failed",
1430
+ lastPaymentStatus: "failed",
1431
+ pausePeriod: null
1432
+ },
1433
+ {
1434
+ ...richBase("sub_expired", "item_expired", "ren_expired"),
1435
+ status: "expired",
1436
+ pausePeriod: null
1437
+ }
1438
+ ];
1439
+ const mockUpsellProducts = [
1440
+ {
1441
+ id: "upsell-1",
1442
+ variantId: "upsell-v-1",
1443
+ title: "Retinol Night Cream",
1444
+ imageUrl: "https://images.unsplash.com/photo-1617897903246-719242758050?w=200",
1445
+ price: { amount: "38.00", currencyCode: "USD" },
1446
+ description: "Anti-aging night treatment"
1447
+ },
1448
+ {
1449
+ id: "upsell-2",
1450
+ variantId: "upsell-v-2",
1451
+ title: "SPF 50 Day Shield",
1452
+ imageUrl: "https://images.unsplash.com/photo-1556228578-8c89e6adf883?w=200",
1453
+ price: { amount: "24.00", currencyCode: "USD" },
1454
+ description: "Lightweight daily sun protection"
892
1455
  }
893
1456
  ];
894
1457
  return {
895
1458
  async getById(id) {
896
1459
  await new Promise((resolve) => setTimeout(resolve, 300));
897
- const subscription = mockSubscriptions.find((sub) => sub.id === id) || null;
898
- return { _tag: "Success", data: subscription };
1460
+ const subscription = mockSubscriptions.find((sub) => sub.id === id);
1461
+ return {
1462
+ _tag: "Success",
1463
+ data: subscription ? {
1464
+ ...subscription,
1465
+ items: subscription.items.map((item) => ({ ...item })),
1466
+ discounts: subscription.discounts.map((d3) => ({ ...d3 }))
1467
+ } : null
1468
+ };
899
1469
  },
900
1470
  async search(options2 = {}) {
901
1471
  await new Promise((resolve) => setTimeout(resolve, 300));
902
- const pageSize = 1;
1472
+ const pageSize = 2;
903
1473
  const allItems = mockSubscriptions;
904
1474
  let startIndex = 0;
905
1475
  let endIndex = pageSize;
@@ -915,27 +1485,82 @@ function createMockSubscriptionAdapter(options = {}) {
915
1485
  }
916
1486
  }
917
1487
  const pageItems = allItems.slice(startIndex, endIndex);
918
- const paginatedList = {
919
- items: pageItems.map((sub) => ({
920
- ...sub,
921
- items: sub.items.map((item) => ({ ...item })),
922
- discounts: sub.discounts.map((discount) => ({ ...discount }))
923
- })),
924
- pageInfo: {
925
- hasNextPage: endIndex < allItems.length,
926
- hasPrevPage: startIndex > 0,
927
- startCursor: pageItems.length > 0 ? startIndex.toString() : null,
928
- endCursor: pageItems.length > 0 ? (endIndex - 1).toString() : null
1488
+ return {
1489
+ _tag: "Success",
1490
+ data: {
1491
+ items: pageItems.map((sub) => ({
1492
+ ...sub,
1493
+ items: sub.items.map((item) => ({ ...item })),
1494
+ discounts: sub.discounts.map((discount) => ({ ...discount }))
1495
+ })),
1496
+ pageInfo: {
1497
+ hasNextPage: endIndex < allItems.length,
1498
+ hasPrevPage: startIndex > 0,
1499
+ startCursor: pageItems.length > 0 ? startIndex.toString() : null,
1500
+ endCursor: pageItems.length > 0 ? (endIndex - 1).toString() : null
1501
+ }
929
1502
  }
930
1503
  };
931
- return { _tag: "Success", data: paginatedList };
932
1504
  },
933
- async addItem() {
1505
+ async addItem({ subscriptionId, item }) {
1506
+ var _a, _b, _c, _d, _e, _f, _g;
934
1507
  await new Promise((resolve) => setTimeout(resolve, 300));
1508
+ const subscription = mockSubscriptions.find((sub) => sub.id === subscriptionId);
1509
+ if (!subscription) {
1510
+ return { _tag: "Failure", error: "Subscription not found" };
1511
+ }
1512
+ const currencyCode = ((_a = subscription.items[0]) == null ? void 0 : _a.price.currencyCode) ?? "USD";
1513
+ 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;
1514
+ const variant = (product == null ? void 0 : product.variants.items.find((v2) => v2.id === item.variantId)) ?? (product == null ? void 0 : product.variants.items[0]);
1515
+ 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);
1516
+ subscription.items.push({
1517
+ id: `item_${Date.now()}_${subscription.items.length}`,
1518
+ productId: (product == null ? void 0 : product.id) ?? "",
1519
+ variantId: item.variantId,
1520
+ quantity: item.quantity,
1521
+ price: linePrice ? { ...linePrice } : { amount: "0.00", currencyCode },
1522
+ ...linePrice ? { currentPrice: { ...linePrice } } : {},
1523
+ title: (product == null ? void 0 : product.title) ?? `Product ${item.variantId}`,
1524
+ ...variant && product && !product.hasOnlyDefaultVariant ? { variantTitle: variant.title } : {},
1525
+ ...(variant == null ? void 0 : variant.image) ? { variantImage: variant.image } : {},
1526
+ variantAvailableForSale: true,
1527
+ billingPolicy: ((_f = subscription.items[0]) == null ? void 0 : _f.billingPolicy) ?? subscription.billingPolicy ?? null,
1528
+ deliveryPolicy: ((_g = subscription.items[0]) == null ? void 0 : _g.deliveryPolicy) ?? subscription.deliveryPolicy ?? null
1529
+ });
1530
+ subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
935
1531
  return { _tag: "Success", data: void 0 };
936
1532
  },
937
- async removeItem() {
1533
+ async removeItem({ subscriptionId, itemId }) {
938
1534
  await new Promise((resolve) => setTimeout(resolve, 300));
1535
+ const subscription = mockSubscriptions.find((sub) => sub.id === subscriptionId);
1536
+ if (!subscription) {
1537
+ return { _tag: "Failure", error: "Subscription not found" };
1538
+ }
1539
+ subscription.items = subscription.items.filter((i3) => i3.id !== itemId);
1540
+ subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1541
+ return { _tag: "Success", data: void 0 };
1542
+ },
1543
+ async updateItem({ subscriptionId, lineId, update }) {
1544
+ await new Promise((resolve) => setTimeout(resolve, 300));
1545
+ const subscription = mockSubscriptions.find((sub) => sub.id === subscriptionId);
1546
+ if (!subscription) {
1547
+ return { _tag: "Failure", error: "Subscription not found" };
1548
+ }
1549
+ const item = subscription.items.find((i3) => i3.id === lineId);
1550
+ if (!item) {
1551
+ return { _tag: "Failure", error: "Line item not found" };
1552
+ }
1553
+ if (update.variantId !== void 0) item.variantId = update.variantId;
1554
+ if (update.quantity !== void 0) item.quantity = update.quantity;
1555
+ if (update.billingPolicy !== void 0) item.billingPolicy = update.billingPolicy;
1556
+ subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1557
+ return { _tag: "Success", data: void 0 };
1558
+ },
1559
+ async skipLineItem() {
1560
+ await new Promise((resolve) => setTimeout(resolve, 300));
1561
+ return { _tag: "Success", data: void 0 };
1562
+ },
1563
+ async updateBoxItems() {
939
1564
  return { _tag: "Success", data: void 0 };
940
1565
  },
941
1566
  async addDiscount() {
@@ -966,24 +1591,11 @@ function createMockSubscriptionAdapter(options = {}) {
966
1591
  (sub) => sub.id === params.subscriptionId
967
1592
  );
968
1593
  if (!subscription) {
969
- return {
970
- _tag: "Failure",
971
- error: "Subscription not found"
972
- };
1594
+ return { _tag: "Failure", error: "Subscription not found" };
973
1595
  }
974
1596
  subscription.nextDeliveryDate = new Date(params.newDate);
975
1597
  subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
976
- subscription.items.forEach((item) => {
977
- item.nextDeliveryDate = new Date(params.newDate);
978
- });
979
- return {
980
- _tag: "Success",
981
- data: {
982
- ...subscription,
983
- items: subscription.items.map((item) => ({ ...item })),
984
- discounts: subscription.discounts.map((discount) => ({ ...discount }))
985
- }
986
- };
1598
+ return { _tag: "Success", data: { ...subscription } };
987
1599
  },
988
1600
  async resume(params) {
989
1601
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -991,21 +1603,11 @@ function createMockSubscriptionAdapter(options = {}) {
991
1603
  (sub) => sub.id === params.subscriptionId
992
1604
  );
993
1605
  if (!subscription) {
994
- return {
995
- _tag: "Failure",
996
- error: "Subscription not found"
997
- };
1606
+ return { _tag: "Failure", error: "Subscription not found" };
998
1607
  }
999
1608
  subscription.status = "active";
1000
1609
  subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1001
- return {
1002
- _tag: "Success",
1003
- data: {
1004
- ...subscription,
1005
- items: subscription.items.map((item) => ({ ...item })),
1006
- discounts: subscription.discounts.map((discount) => ({ ...discount }))
1007
- }
1008
- };
1610
+ return { _tag: "Success", data: { ...subscription } };
1009
1611
  },
1010
1612
  async pause(params) {
1011
1613
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -1013,21 +1615,11 @@ function createMockSubscriptionAdapter(options = {}) {
1013
1615
  (sub) => sub.id === params.subscriptionId
1014
1616
  );
1015
1617
  if (!subscription) {
1016
- return {
1017
- _tag: "Failure",
1018
- error: "Subscription not found"
1019
- };
1618
+ return { _tag: "Failure", error: "Subscription not found" };
1020
1619
  }
1021
1620
  subscription.status = "paused";
1022
1621
  subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1023
- return {
1024
- _tag: "Success",
1025
- data: {
1026
- ...subscription,
1027
- items: subscription.items.map((item) => ({ ...item })),
1028
- discounts: subscription.discounts.map((discount) => ({ ...discount }))
1029
- }
1030
- };
1622
+ return { _tag: "Success", data: { ...subscription } };
1031
1623
  },
1032
1624
  async reactivate(params) {
1033
1625
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -1035,21 +1627,11 @@ function createMockSubscriptionAdapter(options = {}) {
1035
1627
  (sub) => sub.id === params.subscriptionId
1036
1628
  );
1037
1629
  if (!subscription) {
1038
- return {
1039
- _tag: "Failure",
1040
- error: "Subscription not found"
1041
- };
1630
+ return { _tag: "Failure", error: "Subscription not found" };
1042
1631
  }
1043
1632
  subscription.status = "active";
1044
1633
  subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1045
- return {
1046
- _tag: "Success",
1047
- data: {
1048
- ...subscription,
1049
- items: subscription.items.map((item) => ({ ...item })),
1050
- discounts: subscription.discounts.map((discount) => ({ ...discount }))
1051
- }
1052
- };
1634
+ return { _tag: "Success", data: { ...subscription } };
1053
1635
  },
1054
1636
  async cancel(params) {
1055
1637
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -1057,21 +1639,11 @@ function createMockSubscriptionAdapter(options = {}) {
1057
1639
  (sub) => sub.id === params.subscriptionId
1058
1640
  );
1059
1641
  if (!subscription) {
1060
- return {
1061
- _tag: "Failure",
1062
- error: "Subscription not found"
1063
- };
1642
+ return { _tag: "Failure", error: "Subscription not found" };
1064
1643
  }
1065
1644
  subscription.status = "canceled";
1066
1645
  subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1067
- return {
1068
- _tag: "Success",
1069
- data: {
1070
- ...subscription,
1071
- items: subscription.items.map((item) => ({ ...item })),
1072
- discounts: subscription.discounts.map((discount) => ({ ...discount }))
1073
- }
1074
- };
1646
+ return { _tag: "Success", data: { ...subscription } };
1075
1647
  },
1076
1648
  async getAvailablePlans() {
1077
1649
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -1099,13 +1671,875 @@ function createMockSubscriptionAdapter(options = {}) {
1099
1671
  ]
1100
1672
  };
1101
1673
  },
1674
+ async getLastOrder(params) {
1675
+ await new Promise((resolve) => setTimeout(resolve, 200));
1676
+ const subscription = mockSubscriptions.find((sub) => sub.id === params.subscriptionId);
1677
+ if (!subscription) return { _tag: "Success", data: null };
1678
+ return {
1679
+ _tag: "Success",
1680
+ data: {
1681
+ id: `mock_last_order_${params.subscriptionId}`,
1682
+ createdAt: "2026-04-22T12:00:00Z"
1683
+ }
1684
+ };
1685
+ },
1686
+ async renewNow(_params) {
1687
+ await new Promise((resolve) => setTimeout(resolve, 300));
1688
+ return { _tag: "Success", data: { result: "success" } };
1689
+ },
1690
+ async changeFrequency(params) {
1691
+ await new Promise((resolve) => setTimeout(resolve, 300));
1692
+ const subscription = mockSubscriptions.find((sub) => sub.id === params.subscriptionId);
1693
+ if (!subscription) {
1694
+ return { _tag: "Failure", error: "Subscription not found" };
1695
+ }
1696
+ if (typeof params.plan === "string") {
1697
+ subscription.frequency = `1 ${params.plan}`;
1698
+ } else {
1699
+ subscription.frequency = `${params.plan.intervalCount} ${params.plan.interval}`;
1700
+ if (subscription.billingPolicy) {
1701
+ subscription.billingPolicy = { ...subscription.billingPolicy, ...params.plan };
1702
+ }
1703
+ if (subscription.deliveryPolicy) {
1704
+ subscription.deliveryPolicy = { ...subscription.deliveryPolicy, ...params.plan };
1705
+ }
1706
+ }
1707
+ subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1708
+ return {
1709
+ _tag: "Success",
1710
+ data: {
1711
+ ...subscription,
1712
+ items: subscription.items.map((item) => ({ ...item })),
1713
+ discounts: subscription.discounts.map((d3) => ({ ...d3 }))
1714
+ }
1715
+ };
1716
+ },
1102
1717
  async getDefaultSubscriptionId() {
1103
1718
  var _a;
1104
1719
  await new Promise((resolve) => setTimeout(resolve, 300));
1105
1720
  return { _tag: "Success", data: ((_a = mockSubscriptions[0]) == null ? void 0 : _a.id) ?? null };
1721
+ },
1722
+ async getUpsellProducts() {
1723
+ await new Promise((resolve) => setTimeout(resolve, 300));
1724
+ return { _tag: "Success", data: [...mockUpsellProducts] };
1725
+ },
1726
+ async addUpsellProduct() {
1727
+ await new Promise((resolve) => setTimeout(resolve, 400));
1728
+ return { _tag: "Success", data: void 0 };
1106
1729
  }
1107
1730
  };
1108
1731
  }
1732
+ function normalizeInstrumentType(type) {
1733
+ return type.replace(/-/g, "_");
1734
+ }
1735
+ function mapPaymentMethod(rawPm) {
1736
+ if (!rawPm) return null;
1737
+ if (typeof rawPm === "string") {
1738
+ return { id: rawPm };
1739
+ }
1740
+ return {
1741
+ id: rawPm.id ?? "",
1742
+ ...rawPm.instrument != null ? {
1743
+ instrument: {
1744
+ ...rawPm.instrument,
1745
+ type: normalizeInstrumentType(rawPm.instrument.type)
1746
+ }
1747
+ } : {},
1748
+ ...rawPm.active != null ? { active: rawPm.active } : {},
1749
+ ...rawPm.isRecurring != null ? { isRecurring: rawPm.isRecurring } : {}
1750
+ };
1751
+ }
1752
+ function mapSubscription(raw) {
1753
+ var _a, _b;
1754
+ const status = raw.status === "merged" ? "canceled" : raw.status;
1755
+ const deliveryPolicy = raw.deliveryPolicy ?? ((_b = (_a = raw.items) == null ? void 0 : _a[0]) == null ? void 0 : _b.deliveryPolicy);
1756
+ const frequency = deliveryPolicy ? `${deliveryPolicy.intervalCount} ${deliveryPolicy.interval}` : "";
1757
+ return {
1758
+ id: raw.id,
1759
+ customerId: "",
1760
+ serial: raw.serial,
1761
+ items: (raw.items ?? []).map(
1762
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1763
+ (item) => ({
1764
+ id: item.id,
1765
+ productId: item.productId ?? item.product ?? "",
1766
+ variantId: item.variantId ?? item.variant ?? "",
1767
+ quantity: item.quantity,
1768
+ price: item.price ?? {
1769
+ amount: String(item.totalPrice ?? 0),
1770
+ currencyCode: raw.currencyCode ?? ""
1771
+ },
1772
+ nextDeliveryDate: item.nextDeliveryDate ? new Date(item.nextDeliveryDate) : null,
1773
+ billingPolicy: item.billingPolicy ?? raw.billingPolicy ?? null,
1774
+ deliveryPolicy: item.deliveryPolicy ?? raw.deliveryPolicy ?? null,
1775
+ // Map human-readable titles from the v1 SubscriptionItemResource
1776
+ title: item.title,
1777
+ variantTitle: item.subtitle ?? void 0,
1778
+ // recurringCycleLimit === 1 means the item charges exactly once (one-time upsell)
1779
+ isOneTime: item.recurringCycleLimit === 1
1780
+ })
1781
+ ),
1782
+ discounts: (raw.discounts ?? []).map(
1783
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1784
+ (d3) => {
1785
+ var _a2, _b2, _c;
1786
+ return {
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 ?? ""
1792
+ };
1793
+ }
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 — note: arrives as bare id (not expanded) since the
1800
+ // subscriptions endpoint does not support ?expand=paymentMethod; instrument digits
1801
+ // remain a data gap pending backend API extension.
1802
+ customerPaymentMethod: mapPaymentMethod(raw.paymentMethod),
1803
+ frequency,
1804
+ createdAt: raw.createdAt,
1805
+ updatedAt: raw.updatedAt,
1806
+ billingPolicy: raw.billingPolicy ?? null,
1807
+ deliveryPolicy: raw.deliveryPolicy ?? null
1808
+ };
1809
+ }
1810
+ function createApiSubscriptionAdapter(fetcher) {
1811
+ const adapter = {
1812
+ async search(options) {
1813
+ var _a;
1814
+ try {
1815
+ const params = new URLSearchParams();
1816
+ if ((_a = options == null ? void 0 : options.filters) == null ? void 0 : _a.length) {
1817
+ const q2 = options.filters.filter((f2) => Object.keys(f2).length > 0).flatMap((f2) => Object.entries(f2).filter(([, v2]) => Boolean(v2))).map(([k, v2]) => `${k}:'${v2}'`).join(" AND ");
1818
+ if (q2) params.set("query", q2);
1819
+ }
1820
+ if (options == null ? void 0 : options.pagination) {
1821
+ if ("after" in options.pagination)
1822
+ params.set("after", options.pagination.after);
1823
+ else if ("before" in options.pagination)
1824
+ params.set("before", options.pagination.before);
1825
+ }
1826
+ const query = params.toString() ? `?${params.toString()}` : "";
1827
+ const res = await fetcher(`/customer/v1/subscriptions${query}`);
1828
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1829
+ const body = await res.json();
1830
+ return {
1831
+ _tag: "Success",
1832
+ data: {
1833
+ items: body.data.map(mapSubscription),
1834
+ pageInfo: {
1835
+ hasNextPage: body.hasNextPage,
1836
+ hasPrevPage: body.hasPrevPage,
1837
+ startCursor: body.startCursor,
1838
+ endCursor: body.endCursor
1839
+ }
1840
+ }
1841
+ };
1842
+ } catch (e2) {
1843
+ return { _tag: "Failure", error: e2 };
1844
+ }
1845
+ },
1846
+ async getById(id) {
1847
+ try {
1848
+ const res = await fetcher(
1849
+ `/customer/v1/subscriptions?query=id:"${id}"&limit=1`
1850
+ );
1851
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1852
+ const body = await res.json();
1853
+ const raw = body.data[0];
1854
+ return { _tag: "Success", data: raw ? mapSubscription(raw) : null };
1855
+ } catch (e2) {
1856
+ return { _tag: "Failure", error: e2 };
1857
+ }
1858
+ },
1859
+ async addItem({ subscriptionId, item }) {
1860
+ try {
1861
+ const res = await fetcher(
1862
+ `/customer/v1/subscriptions/${subscriptionId}/items`,
1863
+ {
1864
+ method: "POST",
1865
+ headers: { "Content-Type": "application/json" },
1866
+ body: JSON.stringify(item)
1867
+ }
1868
+ );
1869
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1870
+ return { _tag: "Success", data: void 0 };
1871
+ } catch (e2) {
1872
+ return { _tag: "Failure", error: e2 };
1873
+ }
1874
+ },
1875
+ async removeItem({ subscriptionId, itemId }) {
1876
+ try {
1877
+ const res = await fetcher(
1878
+ `/customer/v1/subscriptions/${subscriptionId}/items/${itemId}`,
1879
+ { method: "DELETE" }
1880
+ );
1881
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1882
+ return { _tag: "Success", data: void 0 };
1883
+ } catch (e2) {
1884
+ return { _tag: "Failure", error: e2 };
1885
+ }
1886
+ },
1887
+ async updateItem({ subscriptionId, lineId, update }) {
1888
+ try {
1889
+ const body = {
1890
+ ...update.variantId !== void 0 ? { productVariantId: update.variantId } : {},
1891
+ ...update.quantity !== void 0 ? { quantity: update.quantity } : {},
1892
+ ...update.billingPolicy !== void 0 ? { billingPolicy: update.billingPolicy } : {},
1893
+ ...update.shippingMethodHandle !== void 0 ? { shippingMethodHandle: update.shippingMethodHandle } : {}
1894
+ };
1895
+ const res = await fetcher(
1896
+ `/customer/subscriptions/${subscriptionId}/lines/${lineId}`,
1897
+ {
1898
+ method: "PUT",
1899
+ headers: { "Content-Type": "application/json" },
1900
+ body: JSON.stringify(body)
1901
+ }
1902
+ );
1903
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1904
+ return { _tag: "Success", data: void 0 };
1905
+ } catch (e2) {
1906
+ return { _tag: "Failure", error: e2 };
1907
+ }
1908
+ },
1909
+ async skipLineItem({ subscriptionId, lineId, nextBillingDate }) {
1910
+ try {
1911
+ const res = await fetcher(
1912
+ `/customer/subscriptions/${subscriptionId}/lines/${lineId}`,
1913
+ {
1914
+ method: "PUT",
1915
+ headers: { "Content-Type": "application/json" },
1916
+ body: JSON.stringify({ nextBillingDate })
1917
+ }
1918
+ );
1919
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1920
+ return { _tag: "Success", data: void 0 };
1921
+ } catch (e2) {
1922
+ return { _tag: "Failure", error: e2 };
1923
+ }
1924
+ },
1925
+ async updateBoxItems({ subscriptionId, parentId, items, shippingMethodHandle }) {
1926
+ try {
1927
+ const res = await fetcher(
1928
+ `/customer/subscriptions/${subscriptionId}/update-box-products`,
1929
+ {
1930
+ method: "POST",
1931
+ headers: { "Content-Type": "application/json" },
1932
+ body: JSON.stringify({ parentId, items, shippingMethodHandle })
1933
+ }
1934
+ );
1935
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1936
+ return { _tag: "Success", data: void 0 };
1937
+ } catch (e2) {
1938
+ return { _tag: "Failure", error: e2 };
1939
+ }
1940
+ },
1941
+ async addDiscount({ subscriptionId, code }) {
1942
+ try {
1943
+ const res = await fetcher(
1944
+ `/customer/v1/subscriptions/${subscriptionId}/discounts`,
1945
+ {
1946
+ method: "POST",
1947
+ headers: { "Content-Type": "application/json" },
1948
+ body: JSON.stringify({ code })
1949
+ }
1950
+ );
1951
+ if (!res.ok) return { _tag: "Failure", error: { kind: "NETWORK", message: `HTTP ${res.status}` } };
1952
+ return { _tag: "Success", data: void 0 };
1953
+ } catch (e2) {
1954
+ return { _tag: "Failure", error: { kind: "UNKNOWN", message: e2 instanceof Error ? e2.message : void 0 } };
1955
+ }
1956
+ },
1957
+ async removeDiscount({ subscriptionId, discountId }) {
1958
+ try {
1959
+ const res = await fetcher(
1960
+ `/customer/v1/subscriptions/${subscriptionId}/discounts/${discountId}`,
1961
+ { method: "DELETE" }
1962
+ );
1963
+ if (!res.ok) return { _tag: "Failure", error: { kind: "NETWORK", message: `HTTP ${res.status}` } };
1964
+ return { _tag: "Success", data: void 0 };
1965
+ } catch (e2) {
1966
+ return { _tag: "Failure", error: { kind: "UNKNOWN", message: e2 instanceof Error ? e2.message : void 0 } };
1967
+ }
1968
+ },
1969
+ // Uses PATCH /customer/v1/subscriptions/{id} — the dedicated /reschedule route does not exist.
1970
+ async reschedule({ subscriptionId, newDate }) {
1971
+ try {
1972
+ const res = await fetcher(
1973
+ `/customer/v1/subscriptions/${subscriptionId}`,
1974
+ {
1975
+ method: "PATCH",
1976
+ headers: { "Content-Type": "application/json" },
1977
+ body: JSON.stringify({ nextBillingDate: newDate })
1978
+ }
1979
+ );
1980
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1981
+ const data = await res.json();
1982
+ return { _tag: "Success", data: mapSubscription(data) };
1983
+ } catch (e2) {
1984
+ return { _tag: "Failure", error: e2 };
1985
+ }
1986
+ },
1987
+ async resume({ subscriptionId }) {
1988
+ try {
1989
+ const res = await fetcher(
1990
+ `/customer/v1/subscriptions/${subscriptionId}/resume`,
1991
+ { method: "POST" }
1992
+ );
1993
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1994
+ const data = await res.json();
1995
+ return { _tag: "Success", data: mapSubscription(data) };
1996
+ } catch (e2) {
1997
+ return { _tag: "Failure", error: e2 };
1998
+ }
1999
+ },
2000
+ async pause({ subscriptionId }) {
2001
+ try {
2002
+ const res = await fetcher(
2003
+ `/customer/v1/subscriptions/${subscriptionId}/pause`,
2004
+ { method: "POST" }
2005
+ );
2006
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2007
+ const data = await res.json();
2008
+ return { _tag: "Success", data: mapSubscription(data) };
2009
+ } catch (e2) {
2010
+ return { _tag: "Failure", error: e2 };
2011
+ }
2012
+ },
2013
+ async reactivate({ subscriptionId }) {
2014
+ try {
2015
+ const res = await fetcher(
2016
+ `/customer/v1/subscriptions/${subscriptionId}/reactivate`,
2017
+ { method: "POST" }
2018
+ );
2019
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2020
+ const data = await res.json();
2021
+ return { _tag: "Success", data: mapSubscription(data) };
2022
+ } catch (e2) {
2023
+ return { _tag: "Failure", error: e2 };
2024
+ }
2025
+ },
2026
+ async cancel({ subscriptionId, reason }) {
2027
+ try {
2028
+ const res = await fetcher(
2029
+ `/customer/v1/subscriptions/${subscriptionId}/cancel`,
2030
+ {
2031
+ method: "POST",
2032
+ headers: { "Content-Type": "application/json" },
2033
+ body: JSON.stringify({ cancellationReason: reason })
2034
+ }
2035
+ );
2036
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2037
+ const data = await res.json();
2038
+ return { _tag: "Success", data: mapSubscription(data) };
2039
+ } catch (e2) {
2040
+ return { _tag: "Failure", error: e2 };
2041
+ }
2042
+ },
2043
+ // Legacy endpoint — no /customer/v1/subscriptions/{id}/plans exists yet.
2044
+ async getAvailablePlans({ subscriptionId }) {
2045
+ try {
2046
+ const res = await fetcher(
2047
+ `/customer/subscriptions/${subscriptionId}/plans`
2048
+ );
2049
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2050
+ const data = await res.json();
2051
+ return { _tag: "Success", data };
2052
+ } catch (e2) {
2053
+ return { _tag: "Failure", error: e2 };
2054
+ }
2055
+ },
2056
+ // Legacy endpoint — no /customer/v1 equivalent exists yet.
2057
+ async getDefaultSubscriptionId() {
2058
+ try {
2059
+ const res = await fetcher(
2060
+ `/customer/subscriptions/one-by-next-billing-date`
2061
+ );
2062
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2063
+ const data = await res.json();
2064
+ return { _tag: "Success", data: (data == null ? void 0 : data.id) ?? null };
2065
+ } catch (e2) {
2066
+ return { _tag: "Failure", error: e2 };
2067
+ }
2068
+ },
2069
+ // Legacy endpoint — no /customer/v1/subscriptions/{id}/upsells exists yet.
2070
+ async getUpsellProducts({ subscriptionId }) {
2071
+ try {
2072
+ const res = await fetcher(
2073
+ `/customer/subscriptions/${subscriptionId}/upsell-products`
2074
+ );
2075
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2076
+ const data = await res.json();
2077
+ const products = data.map((p2) => {
2078
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
2079
+ return {
2080
+ id: p2.id,
2081
+ variantId: ((_b = (_a = p2.variants) == null ? void 0 : _a[0]) == null ? void 0 : _b.id) ?? "",
2082
+ title: p2.title ?? "",
2083
+ imageUrl: ((_c = p2.featuredImage) == null ? void 0 : _c.url) ?? void 0,
2084
+ price: {
2085
+ amount: String(((_f = (_e = (_d = p2.variants) == null ? void 0 : _d[0]) == null ? void 0 : _e.currentPrice) == null ? void 0 : _f.amount) ?? 0),
2086
+ currencyCode: ((_i = (_h = (_g = p2.variants) == null ? void 0 : _g[0]) == null ? void 0 : _h.currentPrice) == null ? void 0 : _i.currencyCode) ?? ""
2087
+ },
2088
+ // recurring comes from the upsell rule on variants[0]; present in PreparedProductSchema
2089
+ recurring: (_k = (_j = p2.variants) == null ? void 0 : _j[0]) == null ? void 0 : _k.recurring
2090
+ };
2091
+ });
2092
+ return { _tag: "Success", data: products };
2093
+ } catch (e2) {
2094
+ return { _tag: "Failure", error: e2 };
2095
+ }
2096
+ },
2097
+ // Legacy endpoint — no /customer/v1/subscriptions/{id}/upsells POST exists yet.
2098
+ async addUpsellProduct({ subscriptionId, variantId, quantity, oneTime }) {
2099
+ try {
2100
+ const res = await fetcher(
2101
+ `/customer/subscriptions/${subscriptionId}/upsell`,
2102
+ {
2103
+ method: "POST",
2104
+ headers: { "Content-Type": "application/json" },
2105
+ body: JSON.stringify({
2106
+ productVariantId: variantId,
2107
+ quantity,
2108
+ ...oneTime ? { renewalLimit: 1 } : {}
2109
+ })
2110
+ }
2111
+ );
2112
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2113
+ return { _tag: "Success", data: void 0 };
2114
+ } catch (e2) {
2115
+ return { _tag: "Failure", error: e2 };
2116
+ }
2117
+ },
2118
+ // Legacy endpoint — no /customer/v1 equivalent exists yet.
2119
+ async getLastOrder({ subscriptionId }) {
2120
+ try {
2121
+ const res = await fetcher(
2122
+ `/customer/subscriptions/${subscriptionId}/last-order`
2123
+ );
2124
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2125
+ const data = await res.json();
2126
+ if (!data) return { _tag: "Success", data: null };
2127
+ return {
2128
+ _tag: "Success",
2129
+ data: { id: data.id, createdAt: data.createdAt }
2130
+ };
2131
+ } catch (e2) {
2132
+ return { _tag: "Failure", error: e2 };
2133
+ }
2134
+ },
2135
+ // Legacy endpoints, split by stage (see SubscriptionService.renewNow docs):
2136
+ // retry → POST /customer/subscriptions/{id}/retry
2137
+ // renewalId → POST /customer/renewals/{renewalId}/renew
2138
+ // default → POST /customer/subscriptions/{id}/order-now
2139
+ async renewNow({ subscriptionId, renewalId, retry }) {
2140
+ try {
2141
+ const url = retry ? `/customer/subscriptions/${subscriptionId}/retry` : renewalId ? `/customer/renewals/${renewalId}/renew` : `/customer/subscriptions/${subscriptionId}/order-now`;
2142
+ const res = await fetcher(url, { method: "POST" });
2143
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2144
+ const body = res.status === 204 ? null : await res.json();
2145
+ return {
2146
+ _tag: "Success",
2147
+ data: {
2148
+ nextActionUrl: (body == null ? void 0 : body.nextActionUrl) ?? void 0,
2149
+ result: (body == null ? void 0 : body.result) ?? "success"
2150
+ }
2151
+ };
2152
+ } catch (e2) {
2153
+ return { _tag: "Failure", error: e2 };
2154
+ }
2155
+ },
2156
+ // Legacy endpoint — POST /plan returns 204; re-fetch to return the updated subscription.
2157
+ async changeFrequency({ subscriptionId, plan }) {
2158
+ try {
2159
+ const res = await fetcher(
2160
+ `/customer/subscriptions/${subscriptionId}/plan`,
2161
+ {
2162
+ method: "POST",
2163
+ headers: { "Content-Type": "application/json" },
2164
+ body: JSON.stringify({ plan })
2165
+ }
2166
+ );
2167
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2168
+ const refreshed = await adapter.getById(subscriptionId);
2169
+ if (refreshed._tag === "Failure") return refreshed;
2170
+ if (refreshed.data) return { _tag: "Success", data: refreshed.data };
2171
+ return {
2172
+ _tag: "Failure",
2173
+ error: "Subscription not found after frequency change"
2174
+ };
2175
+ } catch (e2) {
2176
+ return { _tag: "Failure", error: e2 };
2177
+ }
2178
+ }
2179
+ };
2180
+ return adapter;
2181
+ }
2182
+ const DELIVERY_PRICE_CENTS = 500;
2183
+ const DELIVERY_OFFSET_DAYS = 2;
2184
+ function toCents(amount) {
2185
+ const n2 = Number.parseFloat(amount ?? "0");
2186
+ return Number.isFinite(n2) ? Math.round(n2 * 100) : 0;
2187
+ }
2188
+ function addInterval(date, interval, units) {
2189
+ const d3 = new Date(date);
2190
+ switch (interval) {
2191
+ case "DAY":
2192
+ d3.setDate(d3.getDate() + units);
2193
+ break;
2194
+ case "WEEK":
2195
+ d3.setDate(d3.getDate() + units * 7);
2196
+ break;
2197
+ case "YEAR":
2198
+ d3.setFullYear(d3.getFullYear() + units);
2199
+ break;
2200
+ default:
2201
+ d3.setMonth(d3.getMonth() + units);
2202
+ break;
2203
+ }
2204
+ return d3;
2205
+ }
2206
+ function mapItem(sub, item, cycle) {
2207
+ var _a, _b;
2208
+ const price = toCents(((_a = item.currentPrice) == null ? void 0 : _a.amount) ?? item.price.amount);
2209
+ return {
2210
+ id: `${sub.id}_cycle_${cycle}_${item.id}`,
2211
+ title: item.title ?? `Product ${item.variantId}`,
2212
+ subtitle: item.variantTitle ?? null,
2213
+ imageUrl: (_b = item.variantImage) == null ? void 0 : _b.url,
2214
+ variantId: item.variantId,
2215
+ quantity: item.quantity,
2216
+ price,
2217
+ totalPrice: price * item.quantity,
2218
+ variantImage: item.variantImage ?? null,
2219
+ variantAvailableForSale: item.variantAvailableForSale ?? true,
2220
+ subscriptionId: sub.id,
2221
+ subscriptionLineId: item.id,
2222
+ productId: item.productId,
2223
+ parentProductId: item.parentProductId ?? null,
2224
+ parentLineId: item.parentLineId ?? null,
2225
+ isVirtual: item.isVirtual ?? false,
2226
+ isUpsell: item.isUpsell ?? false,
2227
+ isOneTime: item.isOneTime ?? false,
2228
+ customAttributes: (item.customAttributes ?? []).map((a3) => ({
2229
+ name: a3.key,
2230
+ value: a3.value
2231
+ }))
2232
+ };
2233
+ }
2234
+ function projectUpcomingOrders(sub, count) {
2235
+ var _a, _b;
2236
+ if (sub.status !== "active") return [];
2237
+ const billing = sub.billingPolicy ?? ((_a = sub.items[0]) == null ? void 0 : _a.billingPolicy) ?? { interval: "MONTH", intervalCount: 1 };
2238
+ const baseStr = sub.nextBillingDate ?? (sub.nextDeliveryDate ? sub.nextDeliveryDate.toISOString() : null);
2239
+ const base = baseStr ? new Date(baseStr) : /* @__PURE__ */ new Date();
2240
+ const currencyCode = ((_b = sub.items[0]) == null ? void 0 : _b.price.currencyCode) ?? "USD";
2241
+ const orders = [];
2242
+ for (let cycle = 0; cycle < count; cycle++) {
2243
+ const billingDate = addInterval(
2244
+ base,
2245
+ billing.interval,
2246
+ cycle * billing.intervalCount
2247
+ );
2248
+ const deliveryDate = addInterval(billingDate, "DAY", DELIVERY_OFFSET_DAYS);
2249
+ const items = sub.items.map((item) => mapItem(sub, item, cycle));
2250
+ const subtotal = items.reduce((sum, it) => sum + it.totalPrice, 0);
2251
+ orders.push({
2252
+ id: `${sub.id}_cycle_${cycle}`,
2253
+ customerId: sub.customerId,
2254
+ subscriptionId: sub.id,
2255
+ date: billingDate.toISOString(),
2256
+ skipped: false,
2257
+ currencyCode,
2258
+ subtotal,
2259
+ deliveryPrice: DELIVERY_PRICE_CENTS,
2260
+ cycleIndex: cycle,
2261
+ deliveryDate: deliveryDate.toISOString(),
2262
+ status: "scheduled",
2263
+ items,
2264
+ discounts: sub.discounts.map((d3) => ({
2265
+ id: d3.id,
2266
+ title: d3.code,
2267
+ type: d3.type,
2268
+ value: d3.value
2269
+ }))
2270
+ });
2271
+ }
2272
+ return orders;
2273
+ }
2274
+ function applyAdjustments(orders, adjustments) {
2275
+ return orders.map((order) => {
2276
+ let next = order;
2277
+ for (const adj of adjustments) {
2278
+ if (adj.subscriptionId !== order.subscriptionId) continue;
2279
+ if (adj.cycle !== order.cycleIndex) continue;
2280
+ if (adj.type === "skip") {
2281
+ next = { ...next, skipped: true, status: "skipped" };
2282
+ } else if (adj.type === "date_change" && adj.newDate) {
2283
+ next = { ...next, date: adj.newDate, deliveryDate: adj.newDate };
2284
+ }
2285
+ }
2286
+ return next;
2287
+ });
2288
+ }
2289
+ function createMockSchedulesAdapter(subscriptions = createMockSubscriptionAdapter()) {
2290
+ const RESOLVE_DEPTH = 12;
2291
+ let adjustments = [];
2292
+ async function subscriptionsToProject(subscriptionId) {
2293
+ if (subscriptionId) {
2294
+ const res = await subscriptions.getById(subscriptionId);
2295
+ return res._tag === "Success" && res.data ? [res.data] : [];
2296
+ }
2297
+ const all = [];
2298
+ let pagination;
2299
+ for (let guard = 0; guard < 50; guard++) {
2300
+ const res = await subscriptions.search(pagination ? { pagination } : {});
2301
+ if (res._tag !== "Success") break;
2302
+ all.push(...res.data.items);
2303
+ const { hasNextPage, endCursor } = res.data.pageInfo;
2304
+ if (!hasNextPage || !endCursor) break;
2305
+ pagination = { after: endCursor };
2306
+ }
2307
+ return all;
2308
+ }
2309
+ function resolveCycle(orders, opts) {
2310
+ if (opts.cycle != null) return opts.cycle;
2311
+ if (opts.date) {
2312
+ const target = new Date(opts.date).toISOString().split("T")[0];
2313
+ const match = orders.find(
2314
+ (o2) => o2.deliveryDate.split("T")[0] === target || o2.date.split("T")[0] === target
2315
+ );
2316
+ if (match) return match.cycleIndex;
2317
+ }
2318
+ const firstScheduled = orders.find((o2) => o2.status === "scheduled");
2319
+ return (firstScheduled == null ? void 0 : firstScheduled.cycleIndex) ?? null;
2320
+ }
2321
+ async function recordAdjustment(subscriptionId, opts, build) {
2322
+ const res = await subscriptions.getById(subscriptionId);
2323
+ const sub = res._tag === "Success" ? res.data : null;
2324
+ if (!sub) return { _tag: "Failure", error: "No scheduled order found" };
2325
+ const orders = applyAdjustments(
2326
+ projectUpcomingOrders(sub, RESOLVE_DEPTH),
2327
+ adjustments
2328
+ );
2329
+ const cycle = resolveCycle(orders, opts);
2330
+ if (cycle == null) {
2331
+ return { _tag: "Failure", error: "No scheduled order found" };
2332
+ }
2333
+ adjustments = [...adjustments, build(cycle)];
2334
+ return { _tag: "Success", data: void 0 };
2335
+ }
2336
+ return {
2337
+ async getUpcomingOrders({ count, subscriptionId }) {
2338
+ await new Promise((r) => setTimeout(r, 200));
2339
+ const subs = await subscriptionsToProject(subscriptionId);
2340
+ const orders = subs.flatMap(
2341
+ (sub) => applyAdjustments(projectUpcomingOrders(sub, count), adjustments)
2342
+ ).sort((a3, b3) => a3.date.localeCompare(b3.date));
2343
+ return { _tag: "Success", data: orders.slice(0, count) };
2344
+ },
2345
+ async getAdjustments() {
2346
+ await new Promise((r) => setTimeout(r, 200));
2347
+ return { _tag: "Success", data: [...adjustments] };
2348
+ },
2349
+ async skipOrder({ subscriptionId, cycle, date }) {
2350
+ await new Promise((r) => setTimeout(r, 300));
2351
+ return recordAdjustment(subscriptionId, { cycle, date }, (resolved) => ({
2352
+ id: `adj-${Date.now()}`,
2353
+ subscriptionId,
2354
+ type: "skip",
2355
+ cycle: resolved
2356
+ }));
2357
+ },
2358
+ async changeDateAdjustment({ subscriptionId, newDate, currentDate }) {
2359
+ await new Promise((r) => setTimeout(r, 300));
2360
+ return recordAdjustment(
2361
+ subscriptionId,
2362
+ { date: currentDate },
2363
+ (resolved) => ({
2364
+ id: `adj-${Date.now()}`,
2365
+ subscriptionId,
2366
+ type: "date_change",
2367
+ cycle: resolved,
2368
+ newDate
2369
+ })
2370
+ );
2371
+ },
2372
+ async removeAdjustment({ adjustmentId }) {
2373
+ await new Promise((r) => setTimeout(r, 200));
2374
+ adjustments = adjustments.filter((a3) => a3.id !== adjustmentId);
2375
+ return { _tag: "Success", data: void 0 };
2376
+ }
2377
+ };
2378
+ }
2379
+ function createApiSchedulesAdapter(fetcher) {
2380
+ return {
2381
+ async getUpcomingOrders({ count, subscriptionId }) {
2382
+ try {
2383
+ const params = new URLSearchParams({ count: String(count) });
2384
+ if (subscriptionId) params.set("query", `id:"${subscriptionId}"`);
2385
+ const res = await fetcher(`/customer/v1/schedules/?${params.toString()}`);
2386
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2387
+ const data = await res.json();
2388
+ return { _tag: "Success", data: data.orders };
2389
+ } catch (e2) {
2390
+ return { _tag: "Failure", error: e2 };
2391
+ }
2392
+ },
2393
+ async getAdjustments() {
2394
+ try {
2395
+ const res = await fetcher(`/customer/v1/schedules/adjustments`);
2396
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2397
+ const data = await res.json();
2398
+ const adjustments = data.data.map((a3) => ({
2399
+ id: a3.id,
2400
+ subscriptionId: a3.subscriptionId ?? "",
2401
+ type: a3.type === "CHANGE_DATE" ? "date_change" : "skip",
2402
+ newDate: a3.newDate
2403
+ }));
2404
+ return { _tag: "Success", data: adjustments };
2405
+ } catch (e2) {
2406
+ return { _tag: "Failure", error: e2 };
2407
+ }
2408
+ },
2409
+ async skipOrder({ cycle, date }) {
2410
+ if (cycle == null && date == null) {
2411
+ return { _tag: "Failure", error: "Either cycle or date is required for skipOrder" };
2412
+ }
2413
+ try {
2414
+ const matcher = cycle != null ? { type: "CYCLE", input: { cycle } } : { type: "DATE", input: { date: new Date(date).toISOString() } };
2415
+ const res = await fetcher(`/customer/v1/schedules/adjustments`, {
2416
+ method: "POST",
2417
+ headers: { "Content-Type": "application/json" },
2418
+ body: JSON.stringify({
2419
+ matcher,
2420
+ action: { type: "SKIP_ORDER", input: { reason: "" } }
2421
+ })
2422
+ });
2423
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2424
+ return { _tag: "Success", data: void 0 };
2425
+ } catch (e2) {
2426
+ return { _tag: "Failure", error: e2 };
2427
+ }
2428
+ },
2429
+ async changeDateAdjustment({ newDate, currentDate }) {
2430
+ if (currentDate == null) {
2431
+ return {
2432
+ _tag: "Failure",
2433
+ error: "currentDate is required for changeDateAdjustment"
2434
+ };
2435
+ }
2436
+ try {
2437
+ const matcher = { type: "DATE", input: { date: new Date(currentDate).toISOString() } };
2438
+ const res = await fetcher(`/customer/v1/schedules/adjustments`, {
2439
+ method: "POST",
2440
+ headers: { "Content-Type": "application/json" },
2441
+ body: JSON.stringify({
2442
+ matcher,
2443
+ action: {
2444
+ type: "CHANGE_DATE",
2445
+ input: { newDate: new Date(newDate).toISOString() }
2446
+ }
2447
+ })
2448
+ });
2449
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2450
+ return { _tag: "Success", data: void 0 };
2451
+ } catch (e2) {
2452
+ return { _tag: "Failure", error: e2 };
2453
+ }
2454
+ },
2455
+ async removeAdjustment({ adjustmentId }) {
2456
+ try {
2457
+ const res = await fetcher(
2458
+ `/customer/v1/schedules/adjustments/${adjustmentId}`,
2459
+ { method: "DELETE" }
2460
+ );
2461
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
2462
+ return { _tag: "Success", data: void 0 };
2463
+ } catch (e2) {
2464
+ return { _tag: "Failure", error: e2 };
2465
+ }
2466
+ }
2467
+ };
2468
+ }
2469
+ const SchedulesServiceContext = createContext("services/schedules");
2470
+ function createSchedulesService(adapter, opts) {
2471
+ const current = d(null);
2472
+ const currentLoading = d(false);
2473
+ const hasPendingBilling = (opts == null ? void 0 : opts.hasPendingBilling) ?? d(false);
2474
+ let reloadToken = 0;
2475
+ async function getUpcomingOrder() {
2476
+ const result = await adapter.getUpcomingOrders({ count: 1 });
2477
+ if (result._tag === "Failure") return result;
2478
+ return { _tag: "Success", data: result.data[0] ?? null };
2479
+ }
2480
+ async function getUpcomingOrders(count) {
2481
+ return adapter.getUpcomingOrders({ count });
2482
+ }
2483
+ async function reloadCurrent(subscriptionId) {
2484
+ var _a;
2485
+ const token = ++reloadToken;
2486
+ currentLoading.value = true;
2487
+ const count = Math.max(((_a = current.value) == null ? void 0 : _a.length) ?? 0, 3);
2488
+ const result = await adapter.getUpcomingOrders({
2489
+ count,
2490
+ subscriptionId
2491
+ });
2492
+ if (token !== reloadToken) return;
2493
+ currentLoading.value = false;
2494
+ if (result._tag === "Success") {
2495
+ current.value = result.data;
2496
+ }
2497
+ }
2498
+ return {
2499
+ current,
2500
+ currentLoading,
2501
+ hasPendingBilling,
2502
+ getUpcomingOrder,
2503
+ getUpcomingOrders,
2504
+ async getAdjustments() {
2505
+ return adapter.getAdjustments();
2506
+ },
2507
+ async skipOrder(params) {
2508
+ const result = await adapter.skipOrder(params);
2509
+ if (result._tag === "Success") {
2510
+ await reloadCurrent(params.subscriptionId);
2511
+ }
2512
+ return result;
2513
+ },
2514
+ async changeDateAdjustment(params) {
2515
+ const result = await adapter.changeDateAdjustment(params);
2516
+ if (result._tag === "Success") {
2517
+ await reloadCurrent(params.subscriptionId);
2518
+ }
2519
+ return result;
2520
+ },
2521
+ async removeAdjustment(params) {
2522
+ return adapter.removeAdjustment(params);
2523
+ },
2524
+ reloadCurrent
2525
+ };
2526
+ }
2527
+ const SettingsServiceContext = createContext("services/settings");
2528
+ function createSettingsService(initial = {}) {
2529
+ const current = d(initial);
2530
+ return {
2531
+ current,
2532
+ get(key) {
2533
+ return current.value[key];
2534
+ }
2535
+ };
2536
+ }
2537
+ function createShopService(options) {
2538
+ const contactEmail = d(options.getContactEmail());
2539
+ const service = { contactEmail };
2540
+ return service;
2541
+ }
2542
+ const ShopServiceContext = createContext("services/shop");
1109
2543
  const SubscriptionServiceContext = createContext("services/subscription");
1110
2544
  function getCurrentSubscriptionId() {
1111
2545
  if (typeof window === "undefined") {
@@ -1128,41 +2562,83 @@ function getCurrentSubscriptionId() {
1128
2562
  }
1129
2563
  function createSubscriptionService(adapter) {
1130
2564
  const current = d(null);
1131
- const discountsChangedAtSignal = d(0);
1132
- const itemsChangedAtSignal = d(0);
1133
- async function refreshCurrent(subscriptionId) {
1134
- const result = await adapter.getById(subscriptionId);
1135
- if (result._tag === "Success" && result.data != null) {
1136
- current.value = result.data;
1137
- }
2565
+ const hasPendingBilling = d(false);
2566
+ const discountsChangedAt = d(0);
2567
+ const itemsChangedAt = d(0);
2568
+ const lastOrder = d(null);
2569
+ async function refreshAfterDiscountChange() {
2570
+ await reload();
2571
+ discountsChangedAt.value = discountsChangedAt.value + 1;
1138
2572
  }
1139
- async function refreshAfterDiscountChange(subscriptionId) {
1140
- await refreshCurrent(subscriptionId);
1141
- discountsChangedAtSignal.value = discountsChangedAtSignal.value + 1;
1142
- }
1143
- async function refreshAfterItemChange(subscriptionId) {
1144
- await refreshCurrent(subscriptionId);
1145
- itemsChangedAtSignal.value = itemsChangedAtSignal.value + 1;
2573
+ async function refreshAfterItemChange() {
2574
+ await reload();
2575
+ itemsChangedAt.value = itemsChangedAt.value + 1;
1146
2576
  }
1147
2577
  function subscriptionIdFromCurrent() {
1148
2578
  var _a;
1149
2579
  return ((_a = current.value) == null ? void 0 : _a.id) ?? null;
1150
2580
  }
2581
+ async function reload() {
2582
+ var _a, _b;
2583
+ const id = (_a = current.value) == null ? void 0 : _a.id;
2584
+ if (!id) return;
2585
+ const [subResult, lastOrderResult] = await Promise.all([
2586
+ adapter.getById(id),
2587
+ adapter.getLastOrder({ subscriptionId: id })
2588
+ ]);
2589
+ if (((_b = current.value) == null ? void 0 : _b.id) !== id) return;
2590
+ if (subResult._tag === "Success") current.value = subResult.data;
2591
+ if (lastOrderResult._tag === "Success") lastOrder.value = lastOrderResult.data;
2592
+ }
1151
2593
  const service = {
2594
+ current,
2595
+ hasPendingBilling,
2596
+ discountsChangedAt,
2597
+ itemsChangedAt,
2598
+ lastOrder,
1152
2599
  search: adapter.search,
2600
+ // Prefer the adapter's rich cross-subscription list (legacy endpoint, active-first).
2601
+ // Fall back to a single `search` page for adapters that don't implement it
2602
+ // (e.g. the editor mock) so the cross-subscription views still render.
2603
+ listAll: adapter.listAll ? adapter.listAll.bind(adapter) : async () => {
2604
+ const result = await adapter.search();
2605
+ if (result._tag !== "Success") return result;
2606
+ return { _tag: "Success", data: result.data.items };
2607
+ },
1153
2608
  getById: adapter.getById,
1154
2609
  getCurrentSubscriptionId,
1155
2610
  addItem: async (subscriptionId, item) => {
1156
2611
  const result = await adapter.addItem({ subscriptionId, item });
1157
2612
  if (result._tag === "Success") {
1158
- await refreshAfterItemChange(subscriptionId);
2613
+ await refreshAfterItemChange();
1159
2614
  }
1160
2615
  return result;
1161
2616
  },
1162
2617
  removeItem: async (subscriptionId, itemId) => {
1163
2618
  const result = await adapter.removeItem({ subscriptionId, itemId });
1164
2619
  if (result._tag === "Success") {
1165
- await refreshAfterItemChange(subscriptionId);
2620
+ await refreshAfterItemChange();
2621
+ }
2622
+ return result;
2623
+ },
2624
+ updateItem: async (subscriptionId, lineId, update) => {
2625
+ const result = await adapter.updateItem({ subscriptionId, lineId, update });
2626
+ if (result._tag === "Success") {
2627
+ await refreshAfterItemChange();
2628
+ }
2629
+ return result;
2630
+ },
2631
+ skipLineItem: async (subscriptionId, lineId, nextBillingDate) => {
2632
+ const result = await adapter.skipLineItem({ subscriptionId, lineId, nextBillingDate });
2633
+ if (result._tag === "Success") {
2634
+ await refreshAfterItemChange();
2635
+ }
2636
+ return result;
2637
+ },
2638
+ updateBoxItems: async (subscriptionId, params) => {
2639
+ const result = await adapter.updateBoxItems({ subscriptionId, ...params });
2640
+ if (result._tag === "Success") {
2641
+ await refreshAfterItemChange();
1166
2642
  }
1167
2643
  return result;
1168
2644
  },
@@ -1176,7 +2652,7 @@ function createSubscriptionService(adapter) {
1176
2652
  }
1177
2653
  const result = await adapter.addDiscount({ subscriptionId: id, code });
1178
2654
  if (result._tag === "Success") {
1179
- await refreshAfterDiscountChange(id);
2655
+ await refreshAfterDiscountChange();
1180
2656
  }
1181
2657
  return result;
1182
2658
  },
@@ -1193,7 +2669,7 @@ function createSubscriptionService(adapter) {
1193
2669
  discountId
1194
2670
  });
1195
2671
  if (result._tag === "Success") {
1196
- await refreshAfterDiscountChange(id);
2672
+ await refreshAfterDiscountChange();
1197
2673
  }
1198
2674
  return result;
1199
2675
  },
@@ -1202,11 +2678,13 @@ function createSubscriptionService(adapter) {
1202
2678
  pause: (subscriptionId) => adapter.pause({ subscriptionId }),
1203
2679
  reactivate: (subscriptionId) => adapter.reactivate({ subscriptionId }),
1204
2680
  cancel: (subscriptionId, reason) => adapter.cancel({ subscriptionId, reason }),
2681
+ renewNow: (params) => adapter.renewNow(params),
2682
+ changeFrequency: (subscriptionId, plan) => adapter.changeFrequency({ subscriptionId, plan }),
2683
+ reload,
1205
2684
  getAvailablePlans: (subscriptionId) => adapter.getAvailablePlans({ subscriptionId }),
1206
2685
  getDefaultSubscriptionId: () => adapter.getDefaultSubscriptionId(),
1207
- current,
1208
- discountsChangedAt: discountsChangedAtSignal,
1209
- itemsChangedAt: itemsChangedAtSignal
2686
+ getUpsellProducts: (params) => adapter.getUpsellProducts ? adapter.getUpsellProducts(params) : Promise.resolve({ _tag: "Success", data: [] }),
2687
+ addUpsellProduct: (params) => adapter.addUpsellProduct ? adapter.addUpsellProduct(params) : Promise.resolve({ _tag: "Success", data: void 0 })
1210
2688
  };
1211
2689
  return service;
1212
2690
  }
@@ -1475,66 +2953,88 @@ export {
1475
2953
  AuthenticationError,
1476
2954
  AuthenticationErrorReason,
1477
2955
  B as BlockWrapperEvent,
1478
- C as ContextProvideEvent,
1479
- a as ContextRequestEvent,
2956
+ m as ContextProvideEvent,
2957
+ p as ContextRequestEvent,
1480
2958
  CustomerServiceContext,
1481
- D as DEFAULT_GLOBAL_STYLES,
2959
+ D2 as DEFAULT_GLOBAL_STYLES,
1482
2960
  DISCOUNT_REJECTION_REASONS,
1483
2961
  LoginServiceContext,
1484
2962
  OrdersServiceContext,
2963
+ OverlayServiceContext,
1485
2964
  ProductServiceContext,
1486
2965
  RouterServiceContext,
2966
+ SchedulesServiceContext,
1487
2967
  S as SelectedBlockContext,
2968
+ SettingsServiceContext,
2969
+ ShopServiceContext,
1488
2970
  u as Signal,
1489
2971
  StateContext,
1490
2972
  SubscriptionServiceContext,
1491
- T as ThemeStateContext,
1492
- b as TranslationContext,
2973
+ a2 as ThemeStateContext,
2974
+ T as TranslationContext,
1493
2975
  W as WorkflowServiceContext,
1494
- e as attachEmptyTracking,
1495
- f as backfillInstanceDefaults,
2976
+ q as __recordDevtoolsInstance,
2977
+ _ as __recordDevtoolsPropSetter,
2978
+ t as attachEmptyTracking,
2979
+ b as backfillInstanceDefaults,
1496
2980
  w as computed,
1497
- g as createBlockInstance,
1498
- h as createBlockInstanceFromJSON,
1499
- i as createBlockInstanceFromObject,
1500
- j as createCeRenderer,
2981
+ createApiCustomerAdapter,
2982
+ createApiLoginAdapter,
2983
+ createApiProductAdapter,
2984
+ createApiSchedulesAdapter,
2985
+ createApiSubscriptionAdapter,
2986
+ createBlockInstance,
2987
+ v as createBlockInstanceFromJSON,
2988
+ f as createBlockInstanceFromObject,
2989
+ x as createCeRenderer,
1501
2990
  createContext,
1502
2991
  createCustomerService,
2992
+ createLoginService,
2993
+ createMockCustomerAdapter,
2994
+ createMockLoginAdapter,
1503
2995
  createMockOrdersAdapter,
1504
2996
  createMockProductAdapter,
1505
2997
  createMockRouterService,
2998
+ createMockSchedulesAdapter,
1506
2999
  createMockSubscriptionAdapter,
1507
3000
  createMockWorkflowApiAdapter,
1508
3001
  createOrdersService,
3002
+ createOverlayService,
1509
3003
  createProductService,
1510
3004
  createRouterService,
1511
- k as createSelectedBlock,
3005
+ createSchedulesService,
3006
+ y as createSelectedBlock,
3007
+ createSettingsService,
3008
+ createShopService,
1512
3009
  createState,
1513
3010
  createSubscriptionService,
1514
- l as createThemeState,
1515
- m as createTranslationService,
1516
- n as createWorkflowApiAdapter,
1517
- o as createWorkflowService,
1518
- p as defineBlock,
1519
- q as defineBlockRuntime,
3011
+ c as createThemeState,
3012
+ b2 as createTranslationService,
3013
+ d2 as createWorkflowApiAdapter,
3014
+ e as createWorkflowService,
3015
+ defineBlock,
3016
+ z as defineBlockRuntime,
3017
+ defineOverlayWrapper,
1520
3018
  E as effect,
1521
- s as getDefaultBlockForAction,
1522
- t as getDefinedBlocks,
1523
- v as hasDefaultBlockForAction,
1524
- x as hasRenderedContent,
1525
- y as indexOverrides,
1526
- z as injectContext,
1527
- A as isEditorPreview,
1528
- F as isStubDefinition,
3019
+ g as getDefaultBlockForAction,
3020
+ getDefinedBlocks,
3021
+ A as getDevtoolsBlockState,
3022
+ h as hasDefaultBlockForAction,
3023
+ D as hasRenderedContent,
3024
+ i2 as indexOverrides,
3025
+ i as injectContext,
3026
+ F as isEditorPreview,
3027
+ a as isStubDefinition,
1529
3028
  loadBlockRegistry,
1530
3029
  loginService,
1531
- G as onRegisterBlock,
1532
- H as provideContext,
3030
+ o as onRegisterBlock,
3031
+ G as provideContext,
1533
3032
  registerBlock,
1534
- I as serializeBlocks,
3033
+ j as serializeBlocks,
3034
+ H as setDevtoolsBlockProp,
1535
3035
  d as signal,
1536
- J as subscribeContext,
1537
- K as untracked,
3036
+ s as subscribeContext,
3037
+ n as untracked,
1538
3038
  waitForDevBlocksUrl,
1539
- L as withWrapper
3039
+ w2 as withWrapper
1540
3040
  };