@juo/blocks 1.9.0 → 1.11.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 (69) hide show
  1. package/dist/{ExtensionRoot-BmfKKpcU.js → ExtensionRoot-C7tcvV-n.js} +3 -3
  2. package/dist/{block-C8lPBCF8.js → block-WoHZvF51.js} +2 -0
  3. package/dist/block.d.ts +8 -0
  4. package/dist/block.d.ts.map +1 -1
  5. package/dist/{bridge-BOcO9toS.js → bridge-w5KQ-czl.js} +1 -0
  6. package/dist/core.js +985 -274
  7. package/dist/editor/components.d.ts.map +1 -1
  8. package/dist/editor/main.d.ts.map +1 -1
  9. package/dist/editor/messages.d.ts +1 -0
  10. package/dist/editor/messages.d.ts.map +1 -1
  11. package/dist/editor.js +6 -5
  12. package/dist/flows/WorkflowService.d.ts.map +1 -1
  13. package/dist/{juo-workflow-B3OHYsp-.js → juo-workflow-DRauDEnU.js} +18 -3
  14. package/dist/preact.js +1 -1
  15. package/dist/react.js +1 -1
  16. package/dist/services/customer/adapter.d.ts +8 -0
  17. package/dist/services/customer/adapter.d.ts.map +1 -0
  18. package/dist/services/customer/customerService.d.ts +3 -2
  19. package/dist/services/customer/customerService.d.ts.map +1 -1
  20. package/dist/services/customer/index.d.ts +1 -0
  21. package/dist/services/customer/index.d.ts.map +1 -1
  22. package/dist/services/customer/types.d.ts +4 -1
  23. package/dist/services/customer/types.d.ts.map +1 -1
  24. package/dist/services/index.d.ts +1 -0
  25. package/dist/services/index.d.ts.map +1 -1
  26. package/dist/services/login/adapter.d.ts +32 -0
  27. package/dist/services/login/adapter.d.ts.map +1 -0
  28. package/dist/services/login/index.d.ts +3 -1
  29. package/dist/services/login/index.d.ts.map +1 -1
  30. package/dist/services/login/loginService.d.ts +6 -2
  31. package/dist/services/login/loginService.d.ts.map +1 -1
  32. package/dist/services/order/adapter.d.ts +9 -0
  33. package/dist/services/order/adapter.d.ts.map +1 -0
  34. package/dist/services/order/index.d.ts +5 -0
  35. package/dist/services/order/index.d.ts.map +1 -0
  36. package/dist/services/order/orderService.d.ts +7 -0
  37. package/dist/services/order/orderService.d.ts.map +1 -0
  38. package/dist/services/order/types.d.ts +21 -0
  39. package/dist/services/order/types.d.ts.map +1 -0
  40. package/dist/services/product/adapter.d.ts +4 -1
  41. package/dist/services/product/adapter.d.ts.map +1 -1
  42. package/dist/services/product/index.d.ts +1 -1
  43. package/dist/services/product/index.d.ts.map +1 -1
  44. package/dist/services/product/types.d.ts +2 -0
  45. package/dist/services/product/types.d.ts.map +1 -1
  46. package/dist/services/schedules/adapter.d.ts +22 -0
  47. package/dist/services/schedules/adapter.d.ts.map +1 -0
  48. package/dist/services/schedules/index.d.ts +5 -0
  49. package/dist/services/schedules/index.d.ts.map +1 -0
  50. package/dist/services/schedules/schedulesService.d.ts +10 -0
  51. package/dist/services/schedules/schedulesService.d.ts.map +1 -0
  52. package/dist/services/schedules/types.d.ts +47 -0
  53. package/dist/services/schedules/types.d.ts.map +1 -0
  54. package/dist/services/subscription/adapter.d.ts +12 -2
  55. package/dist/services/subscription/adapter.d.ts.map +1 -1
  56. package/dist/services/subscription/index.d.ts +1 -1
  57. package/dist/services/subscription/index.d.ts.map +1 -1
  58. package/dist/services/subscription/subscriptionService.d.ts +1 -6
  59. package/dist/services/subscription/subscriptionService.d.ts.map +1 -1
  60. package/dist/services/subscription/types.d.ts +25 -9
  61. package/dist/services/subscription/types.d.ts.map +1 -1
  62. package/dist/{theme-state-LZS5T-Xb.js → theme-state-CzXQGOTk.js} +1 -1
  63. package/dist/utils/index.d.ts +6 -0
  64. package/dist/utils/index.d.ts.map +1 -1
  65. package/dist/vue.js +3 -3
  66. package/dist/web-components/editor/InlineText.d.ts.map +1 -1
  67. package/dist/web-components-editor.js +5 -4
  68. package/dist/web-components-runtime.js +3 -3
  69. package/package.json +1 -1
package/dist/core.js CHANGED
@@ -1,10 +1,10 @@
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 "./block-C8lPBCF8.js";
5
- import { B, C, a, S, b, e, f, g, h, i, j, k, l, m, n, o, p, q, s, t, v, x, y } from "./block-C8lPBCF8.js";
6
- import { D, T, c, i as i2 } from "./theme-state-LZS5T-Xb.js";
7
- import { T as T2, W, c as c2, a as a2, b as b2, g as g2, h as h2 } from "./juo-workflow-B3OHYsp-.js";
4
+ import { c as createContext, d, w, r as registerBlock, E, u } from "./block-WoHZvF51.js";
5
+ import { B, C, a, S, b, e, f, g, h, i, j, k, l, m, n, o, p, q, s, t, v, x, y } from "./block-WoHZvF51.js";
6
+ import { D, T, c, i as i2 } from "./theme-state-CzXQGOTk.js";
7
+ import { T as T2, W, c as c2, a as a2, b as b2, g as g2, h as h2 } from "./juo-workflow-DRauDEnU.js";
8
8
  import { reportBlockWarning, reportBlockError } from "./observability.js";
9
9
  function createState() {
10
10
  const discountCode = d(null);
@@ -158,13 +158,43 @@ async function loadBlockRegistry(options = {}) {
158
158
  function createCustomerService(options) {
159
159
  const current = d(null);
160
160
  const accountOrdersUrl = d(options.getAccountOrdersUrl());
161
- const service = {
161
+ return {
162
162
  current,
163
- accountOrdersUrl
163
+ accountOrdersUrl,
164
+ async getCurrent() {
165
+ const result = await options.adapter.getCurrent();
166
+ if (result._tag === "Success") current.value = result.data;
167
+ return result;
168
+ }
164
169
  };
165
- return service;
166
170
  }
167
171
  const CustomerServiceContext = createContext("services/customer");
172
+ function createMockCustomerAdapter() {
173
+ const customer = { id: "customer-1", name: "Customer 1" };
174
+ return {
175
+ async getCurrent() {
176
+ return { _tag: "Success", data: customer };
177
+ }
178
+ };
179
+ }
180
+ function createApiCustomerAdapter(fetcher) {
181
+ return {
182
+ async getCurrent() {
183
+ try {
184
+ const res = await fetcher("/customer/info");
185
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
186
+ const data = await res.json();
187
+ const name = [data.firstName, data.lastName].filter(Boolean).join(" ") || data.email || "";
188
+ return {
189
+ _tag: "Success",
190
+ data: { id: data.id, name, email: data.email }
191
+ };
192
+ } catch (e2) {
193
+ return { _tag: "Failure", error: e2 };
194
+ }
195
+ }
196
+ };
197
+ }
168
198
  const PARAMS = {
169
199
  DELETGATED_TOKEN: "delegated-token",
170
200
  REDIRECT_TO: "redirect-to",
@@ -181,6 +211,122 @@ function uuid() {
181
211
  (c3) => (c3 ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c3 / 4).toString(16)
182
212
  );
183
213
  }
214
+ function createMockLoginAdapter() {
215
+ return {
216
+ async passwordlessStart() {
217
+ return { _tag: "Success", data: void 0 };
218
+ },
219
+ async authenticate() {
220
+ return {
221
+ _tag: "Success",
222
+ data: {
223
+ access_token: "mock-access-token",
224
+ refresh_token: "mock-refresh-token",
225
+ expires_in: 3600
226
+ }
227
+ };
228
+ },
229
+ getAuthorizeUrl({ connection, state, shop, appUrl }) {
230
+ const url = new URL(`${appUrl}/auth/customer/authorize`);
231
+ url.searchParams.set("connection", connection);
232
+ url.searchParams.set("state", state);
233
+ url.searchParams.set("shop", shop);
234
+ return url.toString();
235
+ }
236
+ };
237
+ }
238
+ function createApiLoginAdapter(fetcher, opts) {
239
+ const { shopDomain, appUrl } = opts;
240
+ async function callAuthEndpoint(path, body, lang) {
241
+ const url = lang ? `${path}?lang=${lang}` : path;
242
+ const res = await fetcher(url, {
243
+ method: "POST",
244
+ headers: {
245
+ Accept: "application/json",
246
+ "Content-Type": "application/json",
247
+ "X-Shopify-Shop-Domain": shopDomain
248
+ },
249
+ body: JSON.stringify(body)
250
+ });
251
+ if (res.status === 403) {
252
+ const data = await res.json();
253
+ if ("error_description" in data) {
254
+ if (data.error_description === "Wrong email or verification code.") {
255
+ throw new AuthenticationError(AuthenticationErrorReason.IncorrectCode);
256
+ } else if (data.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
257
+ throw new AuthenticationError(AuthenticationErrorReason.TooManyTries);
258
+ } else if (data.error_description === "The verification code has expired. Please try to login again.") {
259
+ throw new AuthenticationError(AuthenticationErrorReason.Expired);
260
+ }
261
+ } else if ("code" in data && data.code === "AUTH_ERROR") {
262
+ throw new AuthenticationError(AuthenticationErrorReason.NoEmail, {
263
+ connection: data.connection
264
+ });
265
+ }
266
+ }
267
+ if (!res.ok) {
268
+ throw new Error(`Unexpected response: ${res.statusText}`);
269
+ }
270
+ return { _tag: "Success", data: void 0 };
271
+ }
272
+ return {
273
+ async passwordlessStart({ email, lang }) {
274
+ try {
275
+ await callAuthEndpoint(
276
+ "/auth/customer/passwordless/start",
277
+ { email },
278
+ lang
279
+ );
280
+ return { _tag: "Success", data: void 0 };
281
+ } catch (e2) {
282
+ return { _tag: "Failure", error: e2 };
283
+ }
284
+ },
285
+ async authenticate(params) {
286
+ try {
287
+ const res = await fetcher("/auth/customer/token", {
288
+ method: "POST",
289
+ headers: {
290
+ Accept: "application/json",
291
+ "Content-Type": "application/json",
292
+ "X-Shopify-Shop-Domain": shopDomain
293
+ },
294
+ body: JSON.stringify(params)
295
+ });
296
+ if (res.status === 403) {
297
+ const data = await res.json();
298
+ if ("error_description" in data) {
299
+ if (data.error_description === "Wrong email or verification code.") {
300
+ throw new AuthenticationError(AuthenticationErrorReason.IncorrectCode);
301
+ } else if (data.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
302
+ throw new AuthenticationError(AuthenticationErrorReason.TooManyTries);
303
+ } else if (data.error_description === "The verification code has expired. Please try to login again.") {
304
+ throw new AuthenticationError(AuthenticationErrorReason.Expired);
305
+ }
306
+ } else if ("code" in data && data.code === "AUTH_ERROR") {
307
+ throw new AuthenticationError(AuthenticationErrorReason.NoEmail, {
308
+ connection: data.connection
309
+ });
310
+ }
311
+ }
312
+ if (!res.ok) {
313
+ throw new Error(`Unexpected response: ${res.statusText}`);
314
+ }
315
+ return { _tag: "Success", data: await res.json() };
316
+ } catch (e2) {
317
+ return { _tag: "Failure", error: e2 };
318
+ }
319
+ },
320
+ getAuthorizeUrl({ connection, state, shop }) {
321
+ const url = new URL(`${appUrl}/auth/customer/authorize`);
322
+ url.searchParams.set("connection", connection);
323
+ url.searchParams.set("state", state);
324
+ url.searchParams.set("shop", shop);
325
+ return url.toString();
326
+ }
327
+ };
328
+ }
329
+ const __vite_import_meta_env__ = {};
184
330
  var AuthenticationErrorReason = /* @__PURE__ */ ((AuthenticationErrorReason2) => {
185
331
  AuthenticationErrorReason2["Expired"] = "expired";
186
332
  AuthenticationErrorReason2["IncorrectCode"] = "wrong-code";
@@ -199,6 +345,9 @@ class AuthenticationError extends Error {
199
345
  this.data = data ?? {};
200
346
  }
201
347
  }
348
+ const STORAGE_DELEGATED_TOKEN = "delegated-token";
349
+ const STORAGE_REFRESH_TOKEN_KEY = "refresh-token";
350
+ const STORAGE_STATE_KEY = "state";
202
351
  function storage(key, value) {
203
352
  const storageKey = `auth:${key}`;
204
353
  try {
@@ -214,83 +363,33 @@ function storage(key, value) {
214
363
  }
215
364
  return null;
216
365
  }
217
- const STORAGE_DELEGATED_TOKEN = "delegated-token";
218
- const STORAGE_REFRESH_TOKEN_KEY = "refresh-token";
219
- const STORAGE_STATE_KEY = "state";
220
- function createAuthFunctions(basePath, shop) {
221
- async function start(email, lang) {
222
- const response = await fetch(
223
- `${basePath}/auth/customer/passwordless/start?lang=${lang}`,
224
- {
225
- method: "POST",
226
- headers: {
227
- Accept: "application/json",
228
- "Content-Type": "application/json",
229
- "X-Shopify-Shop-Domain": shop
230
- },
231
- body: JSON.stringify({ email })
232
- }
233
- );
234
- if (response.status !== 200) {
235
- throw new Error(`Unexpected response: ${response.statusText}`);
236
- }
237
- }
238
- async function authenticate(data) {
239
- const response = await fetch(`${basePath}/auth/customer/token`, {
240
- method: "POST",
241
- headers: {
242
- Accept: "application/json",
243
- "Content-Type": "application/json",
244
- "X-Shopify-Shop-Domain": shop
245
- },
246
- body: JSON.stringify(data)
247
- });
248
- if (response.status === 403) {
249
- const data2 = await response.json();
250
- if ("error_description" in data2) {
251
- if (data2.error_description === "Wrong email or verification code.") {
252
- throw new AuthenticationError(
253
- "wrong-code"
254
- /* IncorrectCode */
255
- );
256
- } else if (data2.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
257
- throw new AuthenticationError(
258
- "too-many-tries"
259
- /* TooManyTries */
260
- );
261
- } else if (data2.error_description === "The verification code has expired. Please try to login again.") {
262
- throw new AuthenticationError(
263
- "expired"
264
- /* Expired */
265
- );
266
- }
267
- } else if ("code" in data2) {
268
- if (data2.code === "AUTH_ERROR") {
269
- throw new AuthenticationError("no-email", {
270
- connection: data2.connection
271
- });
272
- }
273
- }
274
- }
275
- if (response.status !== 200) {
276
- throw new Error(`Unexpected response: ${response.statusText}`);
277
- }
278
- return await response.json();
279
- }
280
- return { start, authenticate };
281
- }
282
366
  function nonce(len = 10) {
283
367
  return uuid().replace(/-/g, "").slice(0, len);
284
368
  }
285
- function createAuthService(apiUrl, shopDomain) {
286
- const { start: authStart, authenticate: authAuthenticate } = createAuthFunctions(apiUrl, shopDomain);
369
+ function loginService(deps) {
370
+ const apiBase = deps.shop.rootUrl.replace(/\/$/, "") + "/api";
371
+ 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";
372
+ const fetcher = (path, init) => fetch(`${apiBase}${path}`, init);
373
+ const adapter = createApiLoginAdapter(fetcher, {
374
+ shopDomain: deps.shop.domain,
375
+ appUrl
376
+ });
377
+ return createLoginService({
378
+ adapter,
379
+ router: deps.router,
380
+ formatError: deps.formatError,
381
+ shop: {
382
+ domain: deps.shop.domain,
383
+ locale: w(() => deps.getLocale())
384
+ },
385
+ onLoginSuccess: deps.onLoginSuccess
386
+ });
387
+ }
388
+ function createLoginService(init) {
389
+ const { adapter, formatError, router, shop, onLoginSuccess } = init;
287
390
  const accessToken = d(null);
288
- const delegatedToken = d(
289
- storage(STORAGE_DELEGATED_TOKEN)
290
- );
291
- const refreshToken = d(
292
- storage(STORAGE_REFRESH_TOKEN_KEY)
293
- );
391
+ const delegatedToken = d(storage(STORAGE_DELEGATED_TOKEN));
392
+ const refreshToken = d(storage(STORAGE_REFRESH_TOKEN_KEY));
294
393
  const expires = d(null);
295
394
  const state = d(storage(STORAGE_STATE_KEY));
296
395
  E(() => {
@@ -302,67 +401,28 @@ function createAuthService(apiUrl, shopDomain) {
302
401
  E(() => {
303
402
  storage(STORAGE_STATE_KEY, state.value);
304
403
  });
305
- function store(data) {
404
+ function storeTokens(data) {
306
405
  const { access_token, refresh_token, expires_in } = data;
307
406
  accessToken.value = access_token;
308
407
  refreshToken.value = refresh_token;
309
408
  expires.value = new Date(Date.now() + expires_in);
310
409
  }
311
- return {
312
- state,
313
- async start(email, lang) {
314
- await authStart(email, lang);
315
- },
316
- async authenticate(data) {
317
- store(await authAuthenticate(data));
318
- },
319
- getDelegatedToken() {
320
- return delegatedToken.value;
321
- },
322
- setDelegatedToken(token) {
323
- delegatedToken.value = token;
324
- },
325
- async getToken() {
326
- if (refreshToken.value == null) {
327
- return null;
328
- }
329
- if (accessToken.value == null || typeof accessToken.value === "string" && (expires.value == null || expires.value < /* @__PURE__ */ new Date())) {
330
- accessToken.value = new Promise(
331
- (resolve, reject) => authAuthenticate({ refreshToken: refreshToken.value }).then((data) => data).then((data) => {
332
- store(data);
333
- resolve(data.access_token);
410
+ async function getToken() {
411
+ if (refreshToken.value == null) return null;
412
+ if (accessToken.value == null || typeof accessToken.value === "string" && (expires.value == null || expires.value < /* @__PURE__ */ new Date())) {
413
+ accessToken.value = new Promise(
414
+ (resolve, reject) => adapter.authenticate({ refreshToken: refreshToken.value }).then((result) => {
415
+ if (result._tag === "Failure") {
416
+ reject(result.error);
334
417
  return;
335
- }).catch(reject)
336
- );
337
- }
338
- return Promise.resolve(accessToken.value);
339
- },
340
- logout() {
341
- accessToken.value = null;
342
- delegatedToken.value = null;
343
- refreshToken.value = null;
344
- expires.value = null;
345
- },
346
- isAuthenticated: w(
347
- () => delegatedToken.value != null || refreshToken.value != null
348
- )
349
- };
350
- }
351
- function loginService(deps) {
352
- return createLoginService({
353
- apiUrl: `${deps.shop.rootUrl.replace(/\/$/, "")}/api`,
354
- router: deps.router,
355
- formatError: deps.formatError,
356
- shop: {
357
- domain: deps.shop.domain,
358
- locale: w(() => deps.getLocale())
359
- },
360
- onLoginSuccess: deps.onLoginSuccess
361
- });
362
- }
363
- function createLoginService(init) {
364
- const { formatError, router, shop, onLoginSuccess } = init;
365
- const auth = createAuthService(init.apiUrl.replace(/\/$/, ""), shop.domain);
418
+ }
419
+ storeTokens(result.data);
420
+ resolve(result.data.access_token);
421
+ }).catch(reject)
422
+ );
423
+ }
424
+ return Promise.resolve(accessToken.value);
425
+ }
366
426
  const userEmail = d(null);
367
427
  async function onSuccess(query) {
368
428
  await (onLoginSuccess == null ? void 0 : onLoginSuccess());
@@ -375,13 +435,9 @@ function createLoginService(init) {
375
435
  queryParams[key] = value;
376
436
  }
377
437
  });
378
- router.push({
379
- path: decodeURIComponent(redirect),
380
- query: queryParams
381
- });
438
+ router.push({ path: decodeURIComponent(redirect), query: queryParams });
382
439
  } else {
383
- const redirect = query.get(PARAMS.REDIRECT_TO);
384
- router.replace(decodeURIComponent(redirect));
440
+ router.replace(decodeURIComponent(query.get(PARAMS.REDIRECT_TO)));
385
441
  }
386
442
  } else {
387
443
  router.replace("/");
@@ -393,16 +449,17 @@ function createLoginService(init) {
393
449
  async start(email) {
394
450
  try {
395
451
  userEmail.value = email;
396
- await auth.start(
397
- userEmail.value,
398
- shop.locale instanceof u ? shop.locale.value : shop.locale
399
- );
452
+ const lang = shop.locale instanceof u ? shop.locale.value : shop.locale;
453
+ const result = await adapter.passwordlessStart({
454
+ email: userEmail.value,
455
+ lang
456
+ });
457
+ if (result._tag === "Failure") {
458
+ return { _tag: "Failure", error: formatError(result.error) };
459
+ }
400
460
  return { _tag: "Success", data: void 0 };
401
461
  } catch (error) {
402
- return {
403
- _tag: "Failure",
404
- error: formatError(error)
405
- };
462
+ return { _tag: "Failure", error: formatError(error) };
406
463
  }
407
464
  },
408
465
  async verify(email, code) {
@@ -413,63 +470,61 @@ function createLoginService(init) {
413
470
  };
414
471
  }
415
472
  try {
416
- const result = await auth.authenticate({
473
+ const result = await adapter.authenticate({
417
474
  email: userEmail.value,
418
475
  code
419
476
  });
477
+ if (result._tag === "Failure") {
478
+ return { _tag: "Failure", error: formatError(result.error) };
479
+ }
480
+ storeTokens(result.data);
420
481
  await onSuccess();
421
- return { _tag: "Success", data: result };
482
+ return { _tag: "Success", data: void 0 };
422
483
  } catch (error) {
423
- return {
424
- _tag: "Failure",
425
- error: formatError(error)
426
- };
484
+ return { _tag: "Failure", error: formatError(error) };
427
485
  }
428
486
  }
429
487
  }
430
488
  },
431
489
  social: {
432
490
  async start(provider) {
433
- function socialUrl(key, shop2) {
434
- auth.state.value = nonce();
435
- const url = new URL(
436
- // TODO: double check admin url
437
- `${"https://admin.juo.io"}/auth/customer/authorize`
438
- );
439
- url.searchParams.append("connection", key);
440
- url.searchParams.append("state", auth.state.value ?? "");
441
- url.searchParams.append("shop", shop2);
442
- return url.toString();
443
- }
444
- window.location.href = socialUrl(
445
- provider === "Facebook" ? "facebook" : "google-oauth2",
446
- shop.domain
447
- );
448
- return {
449
- _tag: "Success",
450
- data: void 0
451
- };
491
+ state.value = nonce();
492
+ const connection = provider === "Facebook" ? "facebook" : "google-oauth2";
493
+ window.location.href = adapter.getAuthorizeUrl({
494
+ connection,
495
+ state: state.value ?? "",
496
+ shop: shop.domain,
497
+ appUrl: (
498
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
499
+ (typeof import.meta !== "undefined" ? __vite_import_meta_env__ == null ? void 0 : __vite_import_meta_env__.VITE_APP_URL : void 0) ?? "https://admin.juo.io"
500
+ )
501
+ });
502
+ return { _tag: "Success", data: void 0 };
452
503
  },
453
504
  async verify(code) {
454
505
  try {
455
- const result = await auth.authenticate({ code });
506
+ const result = await adapter.authenticate({ code });
507
+ if (result._tag === "Failure") {
508
+ return { _tag: "Failure", error: formatError(result.error) };
509
+ }
510
+ storeTokens(result.data);
456
511
  await onSuccess();
457
- return { _tag: "Success", data: result };
512
+ return { _tag: "Success", data: void 0 };
458
513
  } catch (error) {
459
- return {
460
- _tag: "Failure",
461
- error: formatError(error)
462
- };
514
+ return { _tag: "Failure", error: formatError(error) };
463
515
  }
464
516
  }
465
517
  },
466
518
  async logout() {
467
- await auth.logout();
519
+ accessToken.value = null;
520
+ delegatedToken.value = null;
521
+ refreshToken.value = null;
522
+ expires.value = null;
468
523
  return { _tag: "Success", data: void 0 };
469
524
  },
470
525
  async handleQueryParams(query) {
471
526
  if (query.has(PARAMS.DELETGATED_TOKEN)) {
472
- auth.setDelegatedToken(query.get(PARAMS.DELETGATED_TOKEN));
527
+ delegatedToken.value = query.get(PARAMS.DELETGATED_TOKEN);
473
528
  await onSuccess(query);
474
529
  return { _tag: "Success", data: void 0 };
475
530
  }
@@ -481,16 +536,13 @@ function createLoginService(init) {
481
536
  }
482
537
  const code = query.get(PARAMS.CODE);
483
538
  if (code != null) {
484
- if (query.get(PARAMS.STATE) !== auth.state.value) {
485
- return {
486
- _tag: "Failure",
487
- error: formatError("Invalid state")
488
- };
539
+ if (query.get(PARAMS.STATE) !== state.value) {
540
+ return { _tag: "Failure", error: formatError("Invalid state") };
489
541
  } else {
490
542
  return service.social.verify(code);
491
543
  }
492
544
  }
493
- if (query.get(PARAMS.REFRESH) === "1" && await auth.getToken() != null) {
545
+ if (query.get(PARAMS.REFRESH) === "1" && await getToken() != null) {
494
546
  await onSuccess();
495
547
  return { _tag: "Success", data: void 0 };
496
548
  }
@@ -507,8 +559,20 @@ function createLoginService(init) {
507
559
  }
508
560
  return { _tag: "Success", data: void 0 };
509
561
  },
510
- isAuthenticated: auth.isAuthenticated,
511
- getToken: () => auth.getToken()
562
+ isAuthenticated: w(
563
+ () => delegatedToken.value != null || refreshToken.value != null
564
+ ),
565
+ getToken,
566
+ async getAuthHeaders() {
567
+ if (delegatedToken.value) {
568
+ return { "X-Delegated-Token": delegatedToken.value };
569
+ }
570
+ const token = await getToken();
571
+ if (token) {
572
+ return { Authorization: `Bearer ${token}` };
573
+ }
574
+ return {};
575
+ }
512
576
  };
513
577
  return service;
514
578
  }
@@ -721,6 +785,106 @@ function createMockProductAdapter() {
721
785
  }
722
786
  };
723
787
  }
788
+ function mapVariant(variant, currencyCode) {
789
+ var _a;
790
+ return {
791
+ id: variant.id,
792
+ title: variant.title,
793
+ image: variant.image == null ? null : { url: variant.image.url, altText: variant.image.altText ?? void 0 },
794
+ price: { amount: variant.price.toFixed(2), currencyCode },
795
+ ...((_a = variant.purchaseOptions) == null ? void 0 : _a.length) ? {
796
+ purchaseOptions: variant.purchaseOptions.map((opt) => ({
797
+ name: opt.name,
798
+ price: { amount: opt.price.toFixed(2), currencyCode },
799
+ interval: opt.interval
800
+ }))
801
+ } : {}
802
+ };
803
+ }
804
+ function mapProduct(product, currencyCode) {
805
+ return {
806
+ id: product.id,
807
+ title: product.title,
808
+ featuredImage: product.image == null ? null : { url: product.image.url, altText: product.image.altText ?? void 0 },
809
+ hasOnlyDefaultVariant: product.variants.length <= 1,
810
+ variants: {
811
+ items: product.variants.map((v2) => mapVariant(v2, currencyCode)),
812
+ pageInfo: {
813
+ hasNextPage: false,
814
+ hasPrevPage: false,
815
+ startCursor: null,
816
+ endCursor: null
817
+ }
818
+ },
819
+ collections: product.collections
820
+ };
821
+ }
822
+ function createApiProductAdapter(fetcher, opts = { currencyCode: "USD" }) {
823
+ const { currencyCode } = opts;
824
+ async function fetchProducts(params) {
825
+ const query = params.toString() ? `?${params.toString()}` : "";
826
+ const res = await fetcher(`/customer/v1/products${query}`);
827
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
828
+ return res.json();
829
+ }
830
+ return {
831
+ async getById(id) {
832
+ try {
833
+ const params = new URLSearchParams({ query: `id:"${id}"`, limit: "1" });
834
+ const body = await fetchProducts(params);
835
+ return body.data.length > 0 ? mapProduct(body.data[0], currencyCode) : null;
836
+ } catch {
837
+ return null;
838
+ }
839
+ },
840
+ async getByVariantId(variantId) {
841
+ try {
842
+ const params = new URLSearchParams({
843
+ query: `variants.id:"${variantId}"`,
844
+ limit: "1"
845
+ });
846
+ const body = await fetchProducts(params);
847
+ return body.data.length > 0 ? mapProduct(body.data[0], currencyCode) : null;
848
+ } catch {
849
+ return null;
850
+ }
851
+ },
852
+ async search(opts2) {
853
+ try {
854
+ const params = new URLSearchParams();
855
+ if (opts2 == null ? void 0 : opts2.query) {
856
+ params.set("query", `"${opts2.query.trim().replace(/"/g, '\\"')}"`);
857
+ }
858
+ if (opts2 == null ? void 0 : opts2.pagination) {
859
+ if ("after" in opts2.pagination)
860
+ params.set("after", opts2.pagination.after);
861
+ else if ("before" in opts2.pagination)
862
+ params.set("before", opts2.pagination.before);
863
+ }
864
+ const body = await fetchProducts(params);
865
+ return {
866
+ items: body.data.map((p2) => mapProduct(p2, currencyCode)),
867
+ pageInfo: {
868
+ hasNextPage: body.hasNextPage,
869
+ hasPrevPage: body.hasPrevPage,
870
+ startCursor: body.startCursor,
871
+ endCursor: body.endCursor
872
+ }
873
+ };
874
+ } catch {
875
+ return {
876
+ items: [],
877
+ pageInfo: {
878
+ hasNextPage: false,
879
+ hasPrevPage: false,
880
+ startCursor: null,
881
+ endCursor: null
882
+ }
883
+ };
884
+ }
885
+ }
886
+ };
887
+ }
724
888
  const ProductServiceContext = createContext("services/product");
725
889
  function createProductService(adapter) {
726
890
  return {
@@ -786,6 +950,250 @@ function createMockRouterService() {
786
950
  }
787
951
  }).routerService;
788
952
  }
953
+ function futureDateStr(daysFromNow) {
954
+ const d2 = /* @__PURE__ */ new Date();
955
+ d2.setDate(d2.getDate() + daysFromNow);
956
+ return d2.toISOString().split("T")[0] ?? "";
957
+ }
958
+ function createMockSchedulesAdapter() {
959
+ let orders = [
960
+ {
961
+ id: "schedule-order-1",
962
+ subscriptionId: "sub_1",
963
+ deliveryDate: futureDateStr(7),
964
+ items: [
965
+ {
966
+ id: "si-1",
967
+ productId: "p-beauty-1",
968
+ variantId: "v-beauty-1",
969
+ quantity: 1,
970
+ title: "Rose Face Serum",
971
+ imageUrl: "https://images.unsplash.com/photo-1611930022073-b7a4ba5fcccd?w=200"
972
+ },
973
+ {
974
+ id: "si-2",
975
+ productId: "p-beauty-2",
976
+ variantId: "v-beauty-2",
977
+ quantity: 1,
978
+ title: "Vitamin C Moisturizer",
979
+ imageUrl: "https://images.unsplash.com/photo-1556228578-8c89e6adf883?w=200"
980
+ }
981
+ ],
982
+ status: "scheduled"
983
+ },
984
+ {
985
+ id: "schedule-order-2",
986
+ subscriptionId: "sub_1",
987
+ deliveryDate: futureDateStr(35),
988
+ items: [
989
+ {
990
+ id: "si-3",
991
+ productId: "p-beauty-1",
992
+ variantId: "v-beauty-1",
993
+ quantity: 1,
994
+ title: "Rose Face Serum",
995
+ imageUrl: "https://images.unsplash.com/photo-1611930022073-b7a4ba5fcccd?w=200"
996
+ },
997
+ {
998
+ id: "si-4",
999
+ productId: "p-beauty-3",
1000
+ variantId: "v-beauty-3",
1001
+ quantity: 1,
1002
+ title: "Hyaluronic Acid Serum",
1003
+ imageUrl: "https://images.unsplash.com/photo-1621232201696-ba6c9db10a97?w=200"
1004
+ }
1005
+ ],
1006
+ status: "scheduled"
1007
+ }
1008
+ ];
1009
+ let adjustments = [];
1010
+ return {
1011
+ async getUpcomingOrders(count) {
1012
+ await new Promise((r) => setTimeout(r, 200));
1013
+ return { _tag: "Success", data: orders.slice(0, count) };
1014
+ },
1015
+ async getAdjustments() {
1016
+ await new Promise((r) => setTimeout(r, 200));
1017
+ return { _tag: "Success", data: [...adjustments] };
1018
+ },
1019
+ async skipOrder({ subscriptionId }) {
1020
+ await new Promise((r) => setTimeout(r, 300));
1021
+ const order = orders.find(
1022
+ (o2) => o2.subscriptionId === subscriptionId && o2.status === "scheduled"
1023
+ );
1024
+ if (!order) return { _tag: "Failure", error: "No scheduled order found" };
1025
+ orders = orders.map(
1026
+ (o2) => o2.id === order.id ? { ...o2, status: "skipped" } : o2
1027
+ );
1028
+ const adj = {
1029
+ id: `adj-${Date.now()}`,
1030
+ subscriptionId,
1031
+ type: "skip"
1032
+ };
1033
+ adjustments = [...adjustments, adj];
1034
+ return { _tag: "Success", data: void 0 };
1035
+ },
1036
+ async changeDateAdjustment({ subscriptionId, newDate }) {
1037
+ await new Promise((r) => setTimeout(r, 300));
1038
+ const order = orders.find(
1039
+ (o2) => o2.subscriptionId === subscriptionId && o2.status === "scheduled"
1040
+ );
1041
+ if (!order) return { _tag: "Failure", error: "No scheduled order found" };
1042
+ orders = orders.map(
1043
+ (o2) => o2.id === order.id ? { ...o2, deliveryDate: newDate } : o2
1044
+ );
1045
+ const adj = {
1046
+ id: `adj-${Date.now()}`,
1047
+ subscriptionId,
1048
+ type: "date_change",
1049
+ newDate
1050
+ };
1051
+ adjustments = [...adjustments, adj];
1052
+ return { _tag: "Success", data: void 0 };
1053
+ },
1054
+ async removeAdjustment({ adjustmentId }) {
1055
+ await new Promise((r) => setTimeout(r, 200));
1056
+ adjustments = adjustments.filter((a3) => a3.id !== adjustmentId);
1057
+ return { _tag: "Success", data: void 0 };
1058
+ }
1059
+ };
1060
+ }
1061
+ function createApiSchedulesAdapter(fetcher) {
1062
+ return {
1063
+ async getUpcomingOrders(count) {
1064
+ try {
1065
+ const res = await fetcher(`/customer/v1/schedules/?count=${count}`);
1066
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1067
+ const data = await res.json();
1068
+ return { _tag: "Success", data: data.orders };
1069
+ } catch (e2) {
1070
+ return { _tag: "Failure", error: e2 };
1071
+ }
1072
+ },
1073
+ async getAdjustments() {
1074
+ try {
1075
+ const res = await fetcher(`/customer/v1/schedules/adjustments`);
1076
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1077
+ const data = await res.json();
1078
+ const adjustments = data.data.map((a3) => ({
1079
+ id: a3.id,
1080
+ subscriptionId: a3.subscriptionId ?? "",
1081
+ type: a3.type === "CHANGE_DATE" ? "date_change" : "skip",
1082
+ newDate: a3.newDate
1083
+ }));
1084
+ return { _tag: "Success", data: adjustments };
1085
+ } catch (e2) {
1086
+ return { _tag: "Failure", error: e2 };
1087
+ }
1088
+ },
1089
+ async skipOrder({ cycle, date }) {
1090
+ if (cycle == null && date == null) {
1091
+ return { _tag: "Failure", error: "Either cycle or date is required for skipOrder" };
1092
+ }
1093
+ try {
1094
+ const matcher = cycle != null ? { type: "CYCLE", input: { cycle } } : { type: "DATE", input: { date: new Date(date).toISOString() } };
1095
+ const res = await fetcher(`/customer/v1/schedules/adjustments`, {
1096
+ method: "POST",
1097
+ headers: { "Content-Type": "application/json" },
1098
+ body: JSON.stringify({
1099
+ matcher,
1100
+ action: { type: "SKIP_ORDER", input: { reason: "" } }
1101
+ })
1102
+ });
1103
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1104
+ return { _tag: "Success", data: void 0 };
1105
+ } catch (e2) {
1106
+ return { _tag: "Failure", error: e2 };
1107
+ }
1108
+ },
1109
+ async changeDateAdjustment({ newDate, currentDate }) {
1110
+ if (currentDate == null) {
1111
+ return {
1112
+ _tag: "Failure",
1113
+ error: "currentDate is required for changeDateAdjustment"
1114
+ };
1115
+ }
1116
+ try {
1117
+ const matcher = { type: "DATE", input: { date: new Date(currentDate).toISOString() } };
1118
+ const res = await fetcher(`/customer/v1/schedules/adjustments`, {
1119
+ method: "POST",
1120
+ headers: { "Content-Type": "application/json" },
1121
+ body: JSON.stringify({
1122
+ matcher,
1123
+ action: {
1124
+ type: "CHANGE_DATE",
1125
+ input: { newDate: new Date(newDate).toISOString() }
1126
+ }
1127
+ })
1128
+ });
1129
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1130
+ return { _tag: "Success", data: void 0 };
1131
+ } catch (e2) {
1132
+ return { _tag: "Failure", error: e2 };
1133
+ }
1134
+ },
1135
+ async removeAdjustment({ adjustmentId }) {
1136
+ try {
1137
+ const res = await fetcher(
1138
+ `/customer/v1/schedules/adjustments/${adjustmentId}`,
1139
+ { method: "DELETE" }
1140
+ );
1141
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1142
+ return { _tag: "Success", data: void 0 };
1143
+ } catch (e2) {
1144
+ return { _tag: "Failure", error: e2 };
1145
+ }
1146
+ }
1147
+ };
1148
+ }
1149
+ const SchedulesServiceContext = createContext("services/schedules");
1150
+ function createSchedulesService(adapter, opts) {
1151
+ const upcoming = d([]);
1152
+ async function refreshUpcoming() {
1153
+ const count = Math.max(upcoming.value.length, 3);
1154
+ const result = await adapter.getUpcomingOrders(count);
1155
+ if (result._tag === "Success") {
1156
+ upcoming.value = result.data;
1157
+ }
1158
+ }
1159
+ return {
1160
+ upcoming,
1161
+ hasPendingBilling: opts.hasPendingBilling,
1162
+ async getUpcomingOrder() {
1163
+ const result = await adapter.getUpcomingOrders(1);
1164
+ if (result._tag === "Failure") return result;
1165
+ upcoming.value = result.data;
1166
+ return { _tag: "Success", data: result.data[0] ?? null };
1167
+ },
1168
+ async getUpcomingOrders(count) {
1169
+ const result = await adapter.getUpcomingOrders(count);
1170
+ if (result._tag === "Success") {
1171
+ upcoming.value = result.data;
1172
+ }
1173
+ return result;
1174
+ },
1175
+ async getAdjustments() {
1176
+ return adapter.getAdjustments();
1177
+ },
1178
+ async skipOrder(params) {
1179
+ const result = await adapter.skipOrder(params);
1180
+ if (result._tag === "Success") {
1181
+ await refreshUpcoming();
1182
+ }
1183
+ return result;
1184
+ },
1185
+ async changeDateAdjustment(params) {
1186
+ const result = await adapter.changeDateAdjustment(params);
1187
+ if (result._tag === "Success") {
1188
+ await refreshUpcoming();
1189
+ }
1190
+ return result;
1191
+ },
1192
+ async removeAdjustment(params) {
1193
+ return adapter.removeAdjustment(params);
1194
+ }
1195
+ };
1196
+ }
789
1197
  function createMockSubscriptionAdapter(options = {}) {
790
1198
  const mockSubscriptions = [
791
1199
  {
@@ -798,7 +1206,9 @@ function createMockSubscriptionAdapter(options = {}) {
798
1206
  variantId: "variant_1",
799
1207
  quantity: 2,
800
1208
  price: { amount: "29.99", currencyCode: "USD" },
801
- nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15")
1209
+ nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15"),
1210
+ billingPolicy: { intervalCount: 1, interval: "MONTH" },
1211
+ deliveryPolicy: { intervalCount: 1, interval: "MONTH" }
802
1212
  }
803
1213
  ],
804
1214
  discounts: [
@@ -826,7 +1236,9 @@ function createMockSubscriptionAdapter(options = {}) {
826
1236
  variantId: "variant_2",
827
1237
  quantity: 1,
828
1238
  price: { amount: "49.99", currencyCode: "USD" },
829
- nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-20")
1239
+ nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-20"),
1240
+ billingPolicy: { intervalCount: 2, interval: "WEEK" },
1241
+ deliveryPolicy: { intervalCount: 2, interval: "WEEK" }
830
1242
  }
831
1243
  ],
832
1244
  discounts: [],
@@ -837,6 +1249,24 @@ function createMockSubscriptionAdapter(options = {}) {
837
1249
  updatedAt: "2024-10-10T00:00:00Z"
838
1250
  }
839
1251
  ];
1252
+ const mockUpsellProducts = [
1253
+ {
1254
+ id: "upsell-1",
1255
+ variantId: "upsell-v-1",
1256
+ title: "Retinol Night Cream",
1257
+ imageUrl: "https://images.unsplash.com/photo-1617897903246-719242758050?w=200",
1258
+ price: { amount: "38.00", currencyCode: "USD" },
1259
+ description: "Anti-aging night treatment"
1260
+ },
1261
+ {
1262
+ id: "upsell-2",
1263
+ variantId: "upsell-v-2",
1264
+ title: "SPF 50 Day Shield",
1265
+ imageUrl: "https://images.unsplash.com/photo-1556228578-8c89e6adf883?w=200",
1266
+ price: { amount: "24.00", currencyCode: "USD" },
1267
+ description: "Lightweight daily sun protection"
1268
+ }
1269
+ ];
840
1270
  return {
841
1271
  async getById(id) {
842
1272
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -845,7 +1275,7 @@ function createMockSubscriptionAdapter(options = {}) {
845
1275
  },
846
1276
  async search(options2 = {}) {
847
1277
  await new Promise((resolve) => setTimeout(resolve, 300));
848
- const pageSize = 1;
1278
+ const pageSize = 2;
849
1279
  const allItems = mockSubscriptions;
850
1280
  let startIndex = 0;
851
1281
  let endIndex = pageSize;
@@ -861,20 +1291,22 @@ function createMockSubscriptionAdapter(options = {}) {
861
1291
  }
862
1292
  }
863
1293
  const pageItems = allItems.slice(startIndex, endIndex);
864
- const paginatedList = {
865
- items: pageItems.map((sub) => ({
866
- ...sub,
867
- items: sub.items.map((item) => ({ ...item })),
868
- discounts: sub.discounts.map((discount) => ({ ...discount }))
869
- })),
870
- pageInfo: {
871
- hasNextPage: endIndex < allItems.length,
872
- hasPrevPage: startIndex > 0,
873
- startCursor: pageItems.length > 0 ? startIndex.toString() : null,
874
- endCursor: pageItems.length > 0 ? (endIndex - 1).toString() : null
1294
+ return {
1295
+ _tag: "Success",
1296
+ data: {
1297
+ items: pageItems.map((sub) => ({
1298
+ ...sub,
1299
+ items: sub.items.map((item) => ({ ...item })),
1300
+ discounts: sub.discounts.map((discount) => ({ ...discount }))
1301
+ })),
1302
+ pageInfo: {
1303
+ hasNextPage: endIndex < allItems.length,
1304
+ hasPrevPage: startIndex > 0,
1305
+ startCursor: pageItems.length > 0 ? startIndex.toString() : null,
1306
+ endCursor: pageItems.length > 0 ? (endIndex - 1).toString() : null
1307
+ }
875
1308
  }
876
1309
  };
877
- return { _tag: "Success", data: paginatedList };
878
1310
  },
879
1311
  async addItem() {
880
1312
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -912,24 +1344,11 @@ function createMockSubscriptionAdapter(options = {}) {
912
1344
  (sub) => sub.id === params.subscriptionId
913
1345
  );
914
1346
  if (!subscription) {
915
- return {
916
- _tag: "Failure",
917
- error: "Subscription not found"
918
- };
1347
+ return { _tag: "Failure", error: "Subscription not found" };
919
1348
  }
920
1349
  subscription.nextDeliveryDate = new Date(params.newDate);
921
1350
  subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
922
- subscription.items.forEach((item) => {
923
- item.nextDeliveryDate = new Date(params.newDate);
924
- });
925
- return {
926
- _tag: "Success",
927
- data: {
928
- ...subscription,
929
- items: subscription.items.map((item) => ({ ...item })),
930
- discounts: subscription.discounts.map((discount) => ({ ...discount }))
931
- }
932
- };
1351
+ return { _tag: "Success", data: { ...subscription } };
933
1352
  },
934
1353
  async resume(params) {
935
1354
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -937,21 +1356,11 @@ function createMockSubscriptionAdapter(options = {}) {
937
1356
  (sub) => sub.id === params.subscriptionId
938
1357
  );
939
1358
  if (!subscription) {
940
- return {
941
- _tag: "Failure",
942
- error: "Subscription not found"
943
- };
1359
+ return { _tag: "Failure", error: "Subscription not found" };
944
1360
  }
945
1361
  subscription.status = "active";
946
1362
  subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
947
- return {
948
- _tag: "Success",
949
- data: {
950
- ...subscription,
951
- items: subscription.items.map((item) => ({ ...item })),
952
- discounts: subscription.discounts.map((discount) => ({ ...discount }))
953
- }
954
- };
1363
+ return { _tag: "Success", data: { ...subscription } };
955
1364
  },
956
1365
  async pause(params) {
957
1366
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -959,21 +1368,11 @@ function createMockSubscriptionAdapter(options = {}) {
959
1368
  (sub) => sub.id === params.subscriptionId
960
1369
  );
961
1370
  if (!subscription) {
962
- return {
963
- _tag: "Failure",
964
- error: "Subscription not found"
965
- };
1371
+ return { _tag: "Failure", error: "Subscription not found" };
966
1372
  }
967
1373
  subscription.status = "paused";
968
1374
  subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
969
- return {
970
- _tag: "Success",
971
- data: {
972
- ...subscription,
973
- items: subscription.items.map((item) => ({ ...item })),
974
- discounts: subscription.discounts.map((discount) => ({ ...discount }))
975
- }
976
- };
1375
+ return { _tag: "Success", data: { ...subscription } };
977
1376
  },
978
1377
  async reactivate(params) {
979
1378
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -981,21 +1380,11 @@ function createMockSubscriptionAdapter(options = {}) {
981
1380
  (sub) => sub.id === params.subscriptionId
982
1381
  );
983
1382
  if (!subscription) {
984
- return {
985
- _tag: "Failure",
986
- error: "Subscription not found"
987
- };
1383
+ return { _tag: "Failure", error: "Subscription not found" };
988
1384
  }
989
1385
  subscription.status = "active";
990
1386
  subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
991
- return {
992
- _tag: "Success",
993
- data: {
994
- ...subscription,
995
- items: subscription.items.map((item) => ({ ...item })),
996
- discounts: subscription.discounts.map((discount) => ({ ...discount }))
997
- }
998
- };
1387
+ return { _tag: "Success", data: { ...subscription } };
999
1388
  },
1000
1389
  async cancel(params) {
1001
1390
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -1003,21 +1392,11 @@ function createMockSubscriptionAdapter(options = {}) {
1003
1392
  (sub) => sub.id === params.subscriptionId
1004
1393
  );
1005
1394
  if (!subscription) {
1006
- return {
1007
- _tag: "Failure",
1008
- error: "Subscription not found"
1009
- };
1395
+ return { _tag: "Failure", error: "Subscription not found" };
1010
1396
  }
1011
1397
  subscription.status = "canceled";
1012
1398
  subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1013
- return {
1014
- _tag: "Success",
1015
- data: {
1016
- ...subscription,
1017
- items: subscription.items.map((item) => ({ ...item })),
1018
- discounts: subscription.discounts.map((discount) => ({ ...discount }))
1019
- }
1020
- };
1399
+ return { _tag: "Success", data: { ...subscription } };
1021
1400
  },
1022
1401
  async getAvailablePlans() {
1023
1402
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -1049,6 +1428,312 @@ function createMockSubscriptionAdapter(options = {}) {
1049
1428
  var _a;
1050
1429
  await new Promise((resolve) => setTimeout(resolve, 300));
1051
1430
  return { _tag: "Success", data: ((_a = mockSubscriptions[0]) == null ? void 0 : _a.id) ?? null };
1431
+ },
1432
+ async getUpsellProducts() {
1433
+ await new Promise((resolve) => setTimeout(resolve, 300));
1434
+ return { _tag: "Success", data: [...mockUpsellProducts] };
1435
+ },
1436
+ async addUpsellProduct() {
1437
+ await new Promise((resolve) => setTimeout(resolve, 400));
1438
+ return { _tag: "Success", data: void 0 };
1439
+ }
1440
+ };
1441
+ }
1442
+ function mapSubscription(raw) {
1443
+ var _a, _b;
1444
+ const status = raw.status === "merged" ? "canceled" : raw.status;
1445
+ const deliveryPolicy = raw.deliveryPolicy ?? ((_b = (_a = raw.items) == null ? void 0 : _a[0]) == null ? void 0 : _b.deliveryPolicy);
1446
+ const frequency = deliveryPolicy ? `${deliveryPolicy.intervalCount} ${deliveryPolicy.interval}` : "";
1447
+ return {
1448
+ id: raw.id,
1449
+ customerId: "",
1450
+ items: (raw.items ?? []).map(
1451
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1452
+ (item) => ({
1453
+ id: item.id,
1454
+ productId: item.productId ?? item.product ?? "",
1455
+ variantId: item.variantId ?? item.variant ?? "",
1456
+ quantity: item.quantity,
1457
+ price: item.price ?? {
1458
+ amount: String(item.totalPrice ?? 0),
1459
+ currencyCode: raw.currencyCode ?? ""
1460
+ },
1461
+ nextDeliveryDate: item.nextDeliveryDate ? new Date(item.nextDeliveryDate) : null,
1462
+ billingPolicy: item.billingPolicy ?? raw.billingPolicy ?? null,
1463
+ deliveryPolicy: item.deliveryPolicy ?? raw.deliveryPolicy ?? null
1464
+ })
1465
+ ),
1466
+ discounts: (raw.discounts ?? []).map(
1467
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1468
+ (d2) => {
1469
+ var _a2, _b2, _c;
1470
+ return {
1471
+ id: d2.id,
1472
+ type: ((_a2 = d2.value) == null ? void 0 : _a2.type) === "fixed-amount" || d2.type === "fixed_amount" ? "fixed_amount" : "percentage",
1473
+ value: ((_b2 = d2.value) == null ? void 0 : _b2.amount) ?? ((_c = d2.value) == null ? void 0 : _c.percentage) ?? d2.value ?? 0,
1474
+ code: d2.title ?? d2.code ?? "",
1475
+ description: d2.description ?? ""
1476
+ };
1477
+ }
1478
+ ),
1479
+ status,
1480
+ nextDeliveryDate: raw.nextBillingDate ? new Date(raw.nextBillingDate) : raw.nextDeliveryDate ? new Date(raw.nextDeliveryDate) : null,
1481
+ frequency,
1482
+ createdAt: raw.createdAt,
1483
+ updatedAt: raw.updatedAt
1484
+ };
1485
+ }
1486
+ function createApiSubscriptionAdapter(fetcher) {
1487
+ return {
1488
+ async search(options) {
1489
+ var _a;
1490
+ try {
1491
+ const params = new URLSearchParams();
1492
+ if ((_a = options == null ? void 0 : options.filters) == null ? void 0 : _a.length) {
1493
+ const q2 = options.filters.filter((f2) => Object.keys(f2).length > 0).flatMap((f2) => Object.entries(f2).filter(([, v2]) => Boolean(v2))).map(([k2, v2]) => `${k2}:'${v2}'`).join(" AND ");
1494
+ if (q2) params.set("query", q2);
1495
+ }
1496
+ if (options == null ? void 0 : options.pagination) {
1497
+ if ("after" in options.pagination)
1498
+ params.set("after", options.pagination.after);
1499
+ else if ("before" in options.pagination)
1500
+ params.set("before", options.pagination.before);
1501
+ }
1502
+ const query = params.toString() ? `?${params.toString()}` : "";
1503
+ const res = await fetcher(`/customer/v1/subscriptions${query}`);
1504
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1505
+ const body = await res.json();
1506
+ return {
1507
+ _tag: "Success",
1508
+ data: {
1509
+ items: body.data.map(mapSubscription),
1510
+ pageInfo: {
1511
+ hasNextPage: body.hasNextPage,
1512
+ hasPrevPage: body.hasPrevPage,
1513
+ startCursor: body.startCursor,
1514
+ endCursor: body.endCursor
1515
+ }
1516
+ }
1517
+ };
1518
+ } catch (e2) {
1519
+ return { _tag: "Failure", error: e2 };
1520
+ }
1521
+ },
1522
+ async getById(id) {
1523
+ try {
1524
+ const res = await fetcher(
1525
+ `/customer/v1/subscriptions?query=id:"${id}"&limit=1`
1526
+ );
1527
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1528
+ const body = await res.json();
1529
+ const raw = body.data[0];
1530
+ return { _tag: "Success", data: raw ? mapSubscription(raw) : null };
1531
+ } catch (e2) {
1532
+ return { _tag: "Failure", error: e2 };
1533
+ }
1534
+ },
1535
+ async addItem({ subscriptionId, item }) {
1536
+ try {
1537
+ const res = await fetcher(
1538
+ `/customer/v1/subscriptions/${subscriptionId}/items`,
1539
+ {
1540
+ method: "POST",
1541
+ headers: { "Content-Type": "application/json" },
1542
+ body: JSON.stringify(item)
1543
+ }
1544
+ );
1545
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1546
+ return { _tag: "Success", data: void 0 };
1547
+ } catch (e2) {
1548
+ return { _tag: "Failure", error: e2 };
1549
+ }
1550
+ },
1551
+ async removeItem({ subscriptionId, itemId }) {
1552
+ try {
1553
+ const res = await fetcher(
1554
+ `/customer/v1/subscriptions/${subscriptionId}/items/${itemId}`,
1555
+ { method: "DELETE" }
1556
+ );
1557
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1558
+ return { _tag: "Success", data: void 0 };
1559
+ } catch (e2) {
1560
+ return { _tag: "Failure", error: e2 };
1561
+ }
1562
+ },
1563
+ async addDiscount({ subscriptionId, code }) {
1564
+ try {
1565
+ const res = await fetcher(
1566
+ `/customer/v1/subscriptions/${subscriptionId}/discounts`,
1567
+ {
1568
+ method: "POST",
1569
+ headers: { "Content-Type": "application/json" },
1570
+ body: JSON.stringify({ code })
1571
+ }
1572
+ );
1573
+ if (!res.ok) return { _tag: "Failure", error: { kind: "NETWORK", message: `HTTP ${res.status}` } };
1574
+ return { _tag: "Success", data: void 0 };
1575
+ } catch (e2) {
1576
+ return { _tag: "Failure", error: { kind: "UNKNOWN", message: e2 instanceof Error ? e2.message : void 0 } };
1577
+ }
1578
+ },
1579
+ async removeDiscount({ subscriptionId, discountId }) {
1580
+ try {
1581
+ const res = await fetcher(
1582
+ `/customer/v1/subscriptions/${subscriptionId}/discounts/${discountId}`,
1583
+ { method: "DELETE" }
1584
+ );
1585
+ if (!res.ok) return { _tag: "Failure", error: { kind: "NETWORK", message: `HTTP ${res.status}` } };
1586
+ return { _tag: "Success", data: void 0 };
1587
+ } catch (e2) {
1588
+ return { _tag: "Failure", error: { kind: "UNKNOWN", message: e2 instanceof Error ? e2.message : void 0 } };
1589
+ }
1590
+ },
1591
+ // Uses PATCH /customer/v1/subscriptions/{id} — the dedicated /reschedule route does not exist.
1592
+ async reschedule({ subscriptionId, newDate }) {
1593
+ try {
1594
+ const res = await fetcher(
1595
+ `/customer/v1/subscriptions/${subscriptionId}`,
1596
+ {
1597
+ method: "PATCH",
1598
+ headers: { "Content-Type": "application/json" },
1599
+ body: JSON.stringify({ nextBillingDate: newDate })
1600
+ }
1601
+ );
1602
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1603
+ const data = await res.json();
1604
+ return { _tag: "Success", data: mapSubscription(data) };
1605
+ } catch (e2) {
1606
+ return { _tag: "Failure", error: e2 };
1607
+ }
1608
+ },
1609
+ async resume({ subscriptionId }) {
1610
+ try {
1611
+ const res = await fetcher(
1612
+ `/customer/v1/subscriptions/${subscriptionId}/resume`,
1613
+ { method: "POST" }
1614
+ );
1615
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1616
+ const data = await res.json();
1617
+ return { _tag: "Success", data: mapSubscription(data) };
1618
+ } catch (e2) {
1619
+ return { _tag: "Failure", error: e2 };
1620
+ }
1621
+ },
1622
+ async pause({ subscriptionId }) {
1623
+ try {
1624
+ const res = await fetcher(
1625
+ `/customer/v1/subscriptions/${subscriptionId}/pause`,
1626
+ { method: "POST" }
1627
+ );
1628
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1629
+ const data = await res.json();
1630
+ return { _tag: "Success", data: mapSubscription(data) };
1631
+ } catch (e2) {
1632
+ return { _tag: "Failure", error: e2 };
1633
+ }
1634
+ },
1635
+ async reactivate({ subscriptionId }) {
1636
+ try {
1637
+ const res = await fetcher(
1638
+ `/customer/v1/subscriptions/${subscriptionId}/reactivate`,
1639
+ { method: "POST" }
1640
+ );
1641
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1642
+ const data = await res.json();
1643
+ return { _tag: "Success", data: mapSubscription(data) };
1644
+ } catch (e2) {
1645
+ return { _tag: "Failure", error: e2 };
1646
+ }
1647
+ },
1648
+ async cancel({ subscriptionId, reason }) {
1649
+ try {
1650
+ const res = await fetcher(
1651
+ `/customer/v1/subscriptions/${subscriptionId}/cancel`,
1652
+ {
1653
+ method: "POST",
1654
+ headers: { "Content-Type": "application/json" },
1655
+ body: JSON.stringify({ cancellationReason: reason })
1656
+ }
1657
+ );
1658
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1659
+ const data = await res.json();
1660
+ return { _tag: "Success", data: mapSubscription(data) };
1661
+ } catch (e2) {
1662
+ return { _tag: "Failure", error: e2 };
1663
+ }
1664
+ },
1665
+ // Legacy endpoint — no /customer/v1/subscriptions/{id}/plans exists yet.
1666
+ async getAvailablePlans({ subscriptionId }) {
1667
+ try {
1668
+ const res = await fetcher(
1669
+ `/customer/subscriptions/${subscriptionId}/plans`
1670
+ );
1671
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1672
+ const data = await res.json();
1673
+ return { _tag: "Success", data };
1674
+ } catch (e2) {
1675
+ return { _tag: "Failure", error: e2 };
1676
+ }
1677
+ },
1678
+ // Legacy endpoint — no /customer/v1 equivalent exists yet.
1679
+ async getDefaultSubscriptionId() {
1680
+ try {
1681
+ const res = await fetcher(
1682
+ `/customer/subscriptions/one-by-next-billing-date`
1683
+ );
1684
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1685
+ const data = await res.json();
1686
+ return { _tag: "Success", data: (data == null ? void 0 : data.id) ?? null };
1687
+ } catch (e2) {
1688
+ return { _tag: "Failure", error: e2 };
1689
+ }
1690
+ },
1691
+ // Legacy endpoint — no /customer/v1/subscriptions/{id}/upsells exists yet.
1692
+ async getUpsellProducts({ subscriptionId }) {
1693
+ try {
1694
+ const res = await fetcher(
1695
+ `/customer/subscriptions/${subscriptionId}/upsell-products`
1696
+ );
1697
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1698
+ const data = await res.json();
1699
+ const products = data.map((p2) => {
1700
+ var _a, _b, _c, _d, _e;
1701
+ return {
1702
+ id: p2.id,
1703
+ variantId: ((_b = (_a = p2.variants) == null ? void 0 : _a[0]) == null ? void 0 : _b.id) ?? "",
1704
+ title: p2.title ?? "",
1705
+ imageUrl: ((_c = p2.featuredImage) == null ? void 0 : _c.url) ?? void 0,
1706
+ price: {
1707
+ amount: String(((_e = (_d = p2.variants) == null ? void 0 : _d[0]) == null ? void 0 : _e.price) ?? 0),
1708
+ currencyCode: p2.currencyCode ?? ""
1709
+ }
1710
+ };
1711
+ });
1712
+ return { _tag: "Success", data: products };
1713
+ } catch (e2) {
1714
+ return { _tag: "Failure", error: e2 };
1715
+ }
1716
+ },
1717
+ // Legacy endpoint — no /customer/v1/subscriptions/{id}/upsells POST exists yet.
1718
+ async addUpsellProduct({ subscriptionId, variantId, quantity, oneTime }) {
1719
+ try {
1720
+ const res = await fetcher(
1721
+ `/customer/subscriptions/${subscriptionId}/upsell`,
1722
+ {
1723
+ method: "POST",
1724
+ headers: { "Content-Type": "application/json" },
1725
+ body: JSON.stringify({
1726
+ productVariantId: variantId,
1727
+ quantity,
1728
+ ...oneTime ? { renewalLimit: 1 } : {}
1729
+ })
1730
+ }
1731
+ );
1732
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1733
+ return { _tag: "Success", data: void 0 };
1734
+ } catch (e2) {
1735
+ return { _tag: "Failure", error: e2 };
1736
+ }
1052
1737
  }
1053
1738
  };
1054
1739
  }
@@ -1074,6 +1759,7 @@ function getCurrentSubscriptionId() {
1074
1759
  }
1075
1760
  function createSubscriptionService(adapter) {
1076
1761
  const current = d(null);
1762
+ const hasPendingBilling = d(false);
1077
1763
  const discountsChangedAtSignal = d(0);
1078
1764
  async function refreshAfterDiscountChange(subscriptionId) {
1079
1765
  const result = await adapter.getById(subscriptionId);
@@ -1086,9 +1772,24 @@ function createSubscriptionService(adapter) {
1086
1772
  var _a;
1087
1773
  return ((_a = current.value) == null ? void 0 : _a.id) ?? null;
1088
1774
  }
1089
- const service = {
1090
- search: adapter.search,
1091
- getById: adapter.getById,
1775
+ return {
1776
+ current,
1777
+ hasPendingBilling,
1778
+ discountsChangedAt: discountsChangedAtSignal,
1779
+ async search(options) {
1780
+ const result = await adapter.search(options);
1781
+ if (result._tag === "Success") {
1782
+ current.value = result.data.items[0] ?? null;
1783
+ }
1784
+ return result;
1785
+ },
1786
+ async getById(id) {
1787
+ const result = await adapter.getById(id);
1788
+ if (result._tag === "Success" && result.data) {
1789
+ current.value = result.data;
1790
+ }
1791
+ return result;
1792
+ },
1092
1793
  getCurrentSubscriptionId,
1093
1794
  addItem: (subscriptionId, item) => adapter.addItem({ subscriptionId, item }),
1094
1795
  removeItem: (subscriptionId, itemId) => adapter.removeItem({ subscriptionId, itemId }),
@@ -1130,10 +1831,9 @@ function createSubscriptionService(adapter) {
1130
1831
  cancel: (subscriptionId, reason) => adapter.cancel({ subscriptionId, reason }),
1131
1832
  getAvailablePlans: (subscriptionId) => adapter.getAvailablePlans({ subscriptionId }),
1132
1833
  getDefaultSubscriptionId: () => adapter.getDefaultSubscriptionId(),
1133
- current,
1134
- discountsChangedAt: discountsChangedAtSignal
1834
+ getUpsellProducts: (params) => adapter.getUpsellProducts ? adapter.getUpsellProducts(params) : Promise.resolve({ _tag: "Success", data: [] }),
1835
+ addUpsellProduct: (params) => adapter.addUpsellProduct ? adapter.addUpsellProduct(params) : Promise.resolve({ _tag: "Success", data: void 0 })
1135
1836
  };
1136
- return service;
1137
1837
  }
1138
1838
  const DISCOUNT_REJECTION_REASONS = [
1139
1839
  "INVALID_CODE",
@@ -1409,6 +2109,7 @@ export {
1409
2109
  OrdersServiceContext,
1410
2110
  ProductServiceContext,
1411
2111
  RouterServiceContext,
2112
+ SchedulesServiceContext,
1412
2113
  S as SelectedBlockContext,
1413
2114
  u as Signal,
1414
2115
  StateContext,
@@ -1419,20 +2120,30 @@ export {
1419
2120
  b as attachEmptyTracking,
1420
2121
  e as backfillInstanceDefaults,
1421
2122
  w as computed,
2123
+ createApiCustomerAdapter,
2124
+ createApiLoginAdapter,
2125
+ createApiProductAdapter,
2126
+ createApiSchedulesAdapter,
2127
+ createApiSubscriptionAdapter,
1422
2128
  f as createBlockInstance,
1423
2129
  g as createBlockInstanceFromJSON,
1424
2130
  h as createBlockInstanceFromObject,
1425
2131
  i as createCeRenderer,
1426
2132
  createContext,
1427
2133
  createCustomerService,
2134
+ createLoginService,
2135
+ createMockCustomerAdapter,
2136
+ createMockLoginAdapter,
1428
2137
  createMockOrdersAdapter,
1429
2138
  createMockProductAdapter,
1430
2139
  createMockRouterService,
2140
+ createMockSchedulesAdapter,
1431
2141
  createMockSubscriptionAdapter,
1432
2142
  createMockWorkflowApiAdapter,
1433
2143
  createOrdersService,
1434
2144
  createProductService,
1435
2145
  createRouterService,
2146
+ createSchedulesService,
1436
2147
  j as createSelectedBlock,
1437
2148
  createState,
1438
2149
  createSubscriptionService,