@juo/blocks 1.1.0-alpha.5 → 1.1.0-alpha.6

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 (34) hide show
  1. package/dist/core.js +518 -606
  2. package/dist/services/customer/adapter.d.ts +1 -6
  3. package/dist/services/customer/adapter.d.ts.map +1 -1
  4. package/dist/services/customer/customerService.d.ts.map +1 -1
  5. package/dist/services/customer/index.d.ts +1 -1
  6. package/dist/services/customer/index.d.ts.map +1 -1
  7. package/dist/services/customer/types.d.ts +0 -6
  8. package/dist/services/customer/types.d.ts.map +1 -1
  9. package/dist/services/login/adapter.d.ts +32 -0
  10. package/dist/services/login/adapter.d.ts.map +1 -0
  11. package/dist/services/login/index.d.ts +2 -1
  12. package/dist/services/login/index.d.ts.map +1 -1
  13. package/dist/services/login/loginService.d.ts +4 -2
  14. package/dist/services/login/loginService.d.ts.map +1 -1
  15. package/dist/services/order/adapter.d.ts +1 -4
  16. package/dist/services/order/adapter.d.ts.map +1 -1
  17. package/dist/services/order/orderService.d.ts.map +1 -1
  18. package/dist/services/order/types.d.ts +0 -4
  19. package/dist/services/order/types.d.ts.map +1 -1
  20. package/dist/services/product/adapter.d.ts +4 -1
  21. package/dist/services/product/adapter.d.ts.map +1 -1
  22. package/dist/services/product/index.d.ts +1 -1
  23. package/dist/services/product/index.d.ts.map +1 -1
  24. package/dist/services/schedules/adapter.d.ts +2 -0
  25. package/dist/services/schedules/adapter.d.ts.map +1 -1
  26. package/dist/services/schedules/types.d.ts +2 -0
  27. package/dist/services/schedules/types.d.ts.map +1 -1
  28. package/dist/services/subscription/adapter.d.ts +0 -7
  29. package/dist/services/subscription/adapter.d.ts.map +1 -1
  30. package/dist/services/subscription/subscriptionService.d.ts +0 -4
  31. package/dist/services/subscription/subscriptionService.d.ts.map +1 -1
  32. package/dist/services/subscription/types.d.ts +8 -15
  33. package/dist/services/subscription/types.d.ts.map +1 -1
  34. package/package.json +1 -1
package/dist/core.js CHANGED
@@ -155,6 +155,122 @@ function uuid() {
155
155
  (c2) => (c2 ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c2 / 4).toString(16)
156
156
  );
157
157
  }
158
+ function createMockLoginAdapter() {
159
+ return {
160
+ async passwordlessStart() {
161
+ return { _tag: "Success", data: void 0 };
162
+ },
163
+ async authenticate() {
164
+ return {
165
+ _tag: "Success",
166
+ data: {
167
+ access_token: "mock-access-token",
168
+ refresh_token: "mock-refresh-token",
169
+ expires_in: 3600
170
+ }
171
+ };
172
+ },
173
+ getAuthorizeUrl({ connection, state, shop, appUrl }) {
174
+ const url = new URL(`${appUrl}/auth/customer/authorize`);
175
+ url.searchParams.set("connection", connection);
176
+ url.searchParams.set("state", state);
177
+ url.searchParams.set("shop", shop);
178
+ return url.toString();
179
+ }
180
+ };
181
+ }
182
+ function createApiLoginAdapter(fetcher, opts) {
183
+ const { shopDomain, appUrl } = opts;
184
+ async function callAuthEndpoint(path, body, lang) {
185
+ const url = lang ? `${path}?lang=${lang}` : path;
186
+ const res = await fetcher(url, {
187
+ method: "POST",
188
+ headers: {
189
+ Accept: "application/json",
190
+ "Content-Type": "application/json",
191
+ "X-Shopify-Shop-Domain": shopDomain
192
+ },
193
+ body: JSON.stringify(body)
194
+ });
195
+ if (res.status === 403) {
196
+ const data = await res.json();
197
+ if ("error_description" in data) {
198
+ if (data.error_description === "Wrong email or verification code.") {
199
+ throw new AuthenticationError(AuthenticationErrorReason.IncorrectCode);
200
+ } else if (data.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
201
+ throw new AuthenticationError(AuthenticationErrorReason.TooManyTries);
202
+ } else if (data.error_description === "The verification code has expired. Please try to login again.") {
203
+ throw new AuthenticationError(AuthenticationErrorReason.Expired);
204
+ }
205
+ } else if ("code" in data && data.code === "AUTH_ERROR") {
206
+ throw new AuthenticationError(AuthenticationErrorReason.NoEmail, {
207
+ connection: data.connection
208
+ });
209
+ }
210
+ }
211
+ if (!res.ok) {
212
+ throw new Error(`Unexpected response: ${res.statusText}`);
213
+ }
214
+ return { _tag: "Success", data: void 0 };
215
+ }
216
+ return {
217
+ async passwordlessStart({ email, lang }) {
218
+ try {
219
+ await callAuthEndpoint(
220
+ "/auth/customer/passwordless/start",
221
+ { email },
222
+ lang
223
+ );
224
+ return { _tag: "Success", data: void 0 };
225
+ } catch (e2) {
226
+ return { _tag: "Failure", error: e2 };
227
+ }
228
+ },
229
+ async authenticate(params) {
230
+ try {
231
+ const res = await fetcher("/auth/customer/token", {
232
+ method: "POST",
233
+ headers: {
234
+ Accept: "application/json",
235
+ "Content-Type": "application/json",
236
+ "X-Shopify-Shop-Domain": shopDomain
237
+ },
238
+ body: JSON.stringify(params)
239
+ });
240
+ if (res.status === 403) {
241
+ const data = await res.json();
242
+ if ("error_description" in data) {
243
+ if (data.error_description === "Wrong email or verification code.") {
244
+ throw new AuthenticationError(AuthenticationErrorReason.IncorrectCode);
245
+ } else if (data.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
246
+ throw new AuthenticationError(AuthenticationErrorReason.TooManyTries);
247
+ } else if (data.error_description === "The verification code has expired. Please try to login again.") {
248
+ throw new AuthenticationError(AuthenticationErrorReason.Expired);
249
+ }
250
+ } else if ("code" in data && data.code === "AUTH_ERROR") {
251
+ throw new AuthenticationError(AuthenticationErrorReason.NoEmail, {
252
+ connection: data.connection
253
+ });
254
+ }
255
+ }
256
+ if (!res.ok) {
257
+ throw new Error(`Unexpected response: ${res.statusText}`);
258
+ }
259
+ return { _tag: "Success", data: await res.json() };
260
+ } catch (e2) {
261
+ return { _tag: "Failure", error: e2 };
262
+ }
263
+ },
264
+ getAuthorizeUrl({ connection, state, shop }) {
265
+ const url = new URL(`${appUrl}/auth/customer/authorize`);
266
+ url.searchParams.set("connection", connection);
267
+ url.searchParams.set("state", state);
268
+ url.searchParams.set("shop", shop);
269
+ return url.toString();
270
+ }
271
+ };
272
+ }
273
+ const __vite_import_meta_env__ = {};
158
274
  var AuthenticationErrorReason = /* @__PURE__ */ ((AuthenticationErrorReason2) => {
159
275
  AuthenticationErrorReason2["Expired"] = "expired";
160
276
  AuthenticationErrorReason2["IncorrectCode"] = "wrong-code";
@@ -173,6 +289,9 @@ class AuthenticationError extends Error {
173
289
  this.data = data ?? {};
174
290
  }
175
291
  }
292
+ const STORAGE_DELEGATED_TOKEN = "delegated-token";
293
+ const STORAGE_REFRESH_TOKEN_KEY = "refresh-token";
294
+ const STORAGE_STATE_KEY = "state";
176
295
  function storage(key, value) {
177
296
  const storageKey = `auth:${key}`;
178
297
  try {
@@ -188,83 +307,33 @@ function storage(key, value) {
188
307
  }
189
308
  return null;
190
309
  }
191
- const STORAGE_DELEGATED_TOKEN = "delegated-token";
192
- const STORAGE_REFRESH_TOKEN_KEY = "refresh-token";
193
- const STORAGE_STATE_KEY = "state";
194
- function createAuthFunctions(basePath, shop) {
195
- async function start(email, lang) {
196
- const response = await fetch(
197
- `${basePath}/auth/customer/passwordless/start?lang=${lang}`,
198
- {
199
- method: "POST",
200
- headers: {
201
- Accept: "application/json",
202
- "Content-Type": "application/json",
203
- "X-Shopify-Shop-Domain": shop
204
- },
205
- body: JSON.stringify({ email })
206
- }
207
- );
208
- if (response.status !== 200) {
209
- throw new Error(`Unexpected response: ${response.statusText}`);
210
- }
211
- }
212
- async function authenticate(data) {
213
- const response = await fetch(`${basePath}/auth/customer/token`, {
214
- method: "POST",
215
- headers: {
216
- Accept: "application/json",
217
- "Content-Type": "application/json",
218
- "X-Shopify-Shop-Domain": shop
219
- },
220
- body: JSON.stringify(data)
221
- });
222
- if (response.status === 403) {
223
- const data2 = await response.json();
224
- if ("error_description" in data2) {
225
- if (data2.error_description === "Wrong email or verification code.") {
226
- throw new AuthenticationError(
227
- "wrong-code"
228
- /* IncorrectCode */
229
- );
230
- } else if (data2.error_description === "You've reached the maximum number of attempts. Please try to login again.") {
231
- throw new AuthenticationError(
232
- "too-many-tries"
233
- /* TooManyTries */
234
- );
235
- } else if (data2.error_description === "The verification code has expired. Please try to login again.") {
236
- throw new AuthenticationError(
237
- "expired"
238
- /* Expired */
239
- );
240
- }
241
- } else if ("code" in data2) {
242
- if (data2.code === "AUTH_ERROR") {
243
- throw new AuthenticationError("no-email", {
244
- connection: data2.connection
245
- });
246
- }
247
- }
248
- }
249
- if (response.status !== 200) {
250
- throw new Error(`Unexpected response: ${response.statusText}`);
251
- }
252
- return await response.json();
253
- }
254
- return { start, authenticate };
255
- }
256
310
  function nonce(len = 10) {
257
311
  return uuid().replace(/-/g, "").slice(0, len);
258
312
  }
259
- function createAuthService(apiUrl, shopDomain) {
260
- const { start: authStart, authenticate: authAuthenticate } = createAuthFunctions(apiUrl, shopDomain);
313
+ function loginService(deps) {
314
+ const apiBase = deps.shop.rootUrl.replace(/\/$/, "") + "/api";
315
+ 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";
316
+ const fetcher = (path, init) => fetch(`${apiBase}${path}`, init);
317
+ const adapter = createApiLoginAdapter(fetcher, {
318
+ shopDomain: deps.shop.domain,
319
+ appUrl
320
+ });
321
+ return createLoginService({
322
+ adapter,
323
+ router: deps.router,
324
+ formatError: deps.formatError,
325
+ shop: {
326
+ domain: deps.shop.domain,
327
+ locale: w(() => deps.getLocale())
328
+ },
329
+ onLoginSuccess: deps.onLoginSuccess
330
+ });
331
+ }
332
+ function createLoginService(init) {
333
+ const { adapter, formatError, router, shop, onLoginSuccess } = init;
261
334
  const accessToken = d(null);
262
- const delegatedToken = d(
263
- storage(STORAGE_DELEGATED_TOKEN)
264
- );
265
- const refreshToken = d(
266
- storage(STORAGE_REFRESH_TOKEN_KEY)
267
- );
335
+ const delegatedToken = d(storage(STORAGE_DELEGATED_TOKEN));
336
+ const refreshToken = d(storage(STORAGE_REFRESH_TOKEN_KEY));
268
337
  const expires = d(null);
269
338
  const state = d(storage(STORAGE_STATE_KEY));
270
339
  E(() => {
@@ -276,67 +345,28 @@ function createAuthService(apiUrl, shopDomain) {
276
345
  E(() => {
277
346
  storage(STORAGE_STATE_KEY, state.value);
278
347
  });
279
- function store(data) {
348
+ function storeTokens(data) {
280
349
  const { access_token, refresh_token, expires_in } = data;
281
350
  accessToken.value = access_token;
282
351
  refreshToken.value = refresh_token;
283
352
  expires.value = new Date(Date.now() + expires_in);
284
353
  }
285
- return {
286
- state,
287
- async start(email, lang) {
288
- await authStart(email, lang);
289
- },
290
- async authenticate(data) {
291
- store(await authAuthenticate(data));
292
- },
293
- getDelegatedToken() {
294
- return delegatedToken.value;
295
- },
296
- setDelegatedToken(token) {
297
- delegatedToken.value = token;
298
- },
299
- async getToken() {
300
- if (refreshToken.value == null) {
301
- return null;
302
- }
303
- if (accessToken.value == null || typeof accessToken.value === "string" && (expires.value == null || expires.value < /* @__PURE__ */ new Date())) {
304
- accessToken.value = new Promise(
305
- (resolve, reject) => authAuthenticate({ refreshToken: refreshToken.value }).then((data) => data).then((data) => {
306
- store(data);
307
- resolve(data.access_token);
354
+ async function getToken() {
355
+ if (refreshToken.value == null) return null;
356
+ if (accessToken.value == null || typeof accessToken.value === "string" && (expires.value == null || expires.value < /* @__PURE__ */ new Date())) {
357
+ accessToken.value = new Promise(
358
+ (resolve, reject) => adapter.authenticate({ refreshToken: refreshToken.value }).then((result) => {
359
+ if (result._tag === "Failure") {
360
+ reject(result.error);
308
361
  return;
309
- }).catch(reject)
310
- );
311
- }
312
- return Promise.resolve(accessToken.value);
313
- },
314
- logout() {
315
- accessToken.value = null;
316
- delegatedToken.value = null;
317
- refreshToken.value = null;
318
- expires.value = null;
319
- },
320
- isAuthenticated: w(
321
- () => delegatedToken.value != null || refreshToken.value != null
322
- )
323
- };
324
- }
325
- function loginService(deps) {
326
- return createLoginService({
327
- apiUrl: `${deps.shop.rootUrl.replace(/\/$/, "")}/api`,
328
- router: deps.router,
329
- formatError: deps.formatError,
330
- shop: {
331
- domain: deps.shop.domain,
332
- locale: w(() => deps.getLocale())
333
- },
334
- onLoginSuccess: deps.onLoginSuccess
335
- });
336
- }
337
- function createLoginService(init) {
338
- const { formatError, router, shop, onLoginSuccess } = init;
339
- const auth = createAuthService(init.apiUrl.replace(/\/$/, ""), shop.domain);
362
+ }
363
+ storeTokens(result.data);
364
+ resolve(result.data.access_token);
365
+ }).catch(reject)
366
+ );
367
+ }
368
+ return Promise.resolve(accessToken.value);
369
+ }
340
370
  const userEmail = d(null);
341
371
  async function onSuccess(query) {
342
372
  await (onLoginSuccess == null ? void 0 : onLoginSuccess());
@@ -349,13 +379,9 @@ function createLoginService(init) {
349
379
  queryParams[key] = value;
350
380
  }
351
381
  });
352
- router.push({
353
- path: decodeURIComponent(redirect),
354
- query: queryParams
355
- });
382
+ router.push({ path: decodeURIComponent(redirect), query: queryParams });
356
383
  } else {
357
- const redirect = query.get(PARAMS.REDIRECT_TO);
358
- router.replace(decodeURIComponent(redirect));
384
+ router.replace(decodeURIComponent(query.get(PARAMS.REDIRECT_TO)));
359
385
  }
360
386
  } else {
361
387
  router.replace("/");
@@ -367,16 +393,17 @@ function createLoginService(init) {
367
393
  async start(email) {
368
394
  try {
369
395
  userEmail.value = email;
370
- await auth.start(
371
- userEmail.value,
372
- shop.locale instanceof u ? shop.locale.value : shop.locale
373
- );
396
+ const lang = shop.locale instanceof u ? shop.locale.value : shop.locale;
397
+ const result = await adapter.passwordlessStart({
398
+ email: userEmail.value,
399
+ lang
400
+ });
401
+ if (result._tag === "Failure") {
402
+ return { _tag: "Failure", error: formatError(result.error) };
403
+ }
374
404
  return { _tag: "Success", data: void 0 };
375
405
  } catch (error) {
376
- return {
377
- _tag: "Failure",
378
- error: formatError(error)
379
- };
406
+ return { _tag: "Failure", error: formatError(error) };
380
407
  }
381
408
  },
382
409
  async verify(email, code) {
@@ -387,63 +414,61 @@ function createLoginService(init) {
387
414
  };
388
415
  }
389
416
  try {
390
- const result = await auth.authenticate({
417
+ const result = await adapter.authenticate({
391
418
  email: userEmail.value,
392
419
  code
393
420
  });
421
+ if (result._tag === "Failure") {
422
+ return { _tag: "Failure", error: formatError(result.error) };
423
+ }
424
+ storeTokens(result.data);
394
425
  await onSuccess();
395
- return { _tag: "Success", data: result };
426
+ return { _tag: "Success", data: void 0 };
396
427
  } catch (error) {
397
- return {
398
- _tag: "Failure",
399
- error: formatError(error)
400
- };
428
+ return { _tag: "Failure", error: formatError(error) };
401
429
  }
402
430
  }
403
431
  }
404
432
  },
405
433
  social: {
406
434
  async start(provider) {
407
- function socialUrl(key, shop2) {
408
- auth.state.value = nonce();
409
- const url = new URL(
410
- // TODO: double check admin url
411
- `${"https://admin.juo.io"}/auth/customer/authorize`
412
- );
413
- url.searchParams.append("connection", key);
414
- url.searchParams.append("state", auth.state.value ?? "");
415
- url.searchParams.append("shop", shop2);
416
- return url.toString();
417
- }
418
- window.location.href = socialUrl(
419
- provider === "Facebook" ? "facebook" : "google-oauth2",
420
- shop.domain
421
- );
422
- return {
423
- _tag: "Success",
424
- data: void 0
425
- };
435
+ state.value = nonce();
436
+ const connection = provider === "Facebook" ? "facebook" : "google-oauth2";
437
+ window.location.href = adapter.getAuthorizeUrl({
438
+ connection,
439
+ state: state.value ?? "",
440
+ shop: shop.domain,
441
+ appUrl: (
442
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
443
+ (typeof import.meta !== "undefined" ? __vite_import_meta_env__ == null ? void 0 : __vite_import_meta_env__.VITE_APP_URL : void 0) ?? "https://admin.juo.io"
444
+ )
445
+ });
446
+ return { _tag: "Success", data: void 0 };
426
447
  },
427
448
  async verify(code) {
428
449
  try {
429
- const result = await auth.authenticate({ code });
450
+ const result = await adapter.authenticate({ code });
451
+ if (result._tag === "Failure") {
452
+ return { _tag: "Failure", error: formatError(result.error) };
453
+ }
454
+ storeTokens(result.data);
430
455
  await onSuccess();
431
- return { _tag: "Success", data: result };
456
+ return { _tag: "Success", data: void 0 };
432
457
  } catch (error) {
433
- return {
434
- _tag: "Failure",
435
- error: formatError(error)
436
- };
458
+ return { _tag: "Failure", error: formatError(error) };
437
459
  }
438
460
  }
439
461
  },
440
462
  async logout() {
441
- await auth.logout();
463
+ accessToken.value = null;
464
+ delegatedToken.value = null;
465
+ refreshToken.value = null;
466
+ expires.value = null;
442
467
  return { _tag: "Success", data: void 0 };
443
468
  },
444
469
  async handleQueryParams(query) {
445
470
  if (query.has(PARAMS.DELETGATED_TOKEN)) {
446
- auth.setDelegatedToken(query.get(PARAMS.DELETGATED_TOKEN));
471
+ delegatedToken.value = query.get(PARAMS.DELETGATED_TOKEN);
447
472
  await onSuccess(query);
448
473
  return { _tag: "Success", data: void 0 };
449
474
  }
@@ -455,16 +480,13 @@ function createLoginService(init) {
455
480
  }
456
481
  const code = query.get(PARAMS.CODE);
457
482
  if (code != null) {
458
- if (query.get(PARAMS.STATE) !== auth.state.value) {
459
- return {
460
- _tag: "Failure",
461
- error: formatError("Invalid state")
462
- };
483
+ if (query.get(PARAMS.STATE) !== state.value) {
484
+ return { _tag: "Failure", error: formatError("Invalid state") };
463
485
  } else {
464
486
  return service.social.verify(code);
465
487
  }
466
488
  }
467
- if (query.get(PARAMS.REFRESH) === "1" && await auth.getToken() != null) {
489
+ if (query.get(PARAMS.REFRESH) === "1" && await getToken() != null) {
468
490
  await onSuccess();
469
491
  return { _tag: "Success", data: void 0 };
470
492
  }
@@ -481,14 +503,15 @@ function createLoginService(init) {
481
503
  }
482
504
  return { _tag: "Success", data: void 0 };
483
505
  },
484
- isAuthenticated: auth.isAuthenticated,
485
- getToken: () => auth.getToken(),
506
+ isAuthenticated: w(
507
+ () => delegatedToken.value != null || refreshToken.value != null
508
+ ),
509
+ getToken,
486
510
  async getAuthHeaders() {
487
- const delegated = auth.getDelegatedToken();
488
- if (delegated) {
489
- return { "X-Delegated-Token": delegated };
511
+ if (delegatedToken.value) {
512
+ return { "X-Delegated-Token": delegatedToken.value };
490
513
  }
491
- const token = await auth.getToken();
514
+ const token = await getToken();
492
515
  if (token) {
493
516
  return { Authorization: `Bearer ${token}` };
494
517
  }
@@ -678,6 +701,106 @@ function createMockProductAdapter() {
678
701
  }
679
702
  };
680
703
  }
704
+ function mapVariant(variant, currencyCode) {
705
+ var _a;
706
+ return {
707
+ id: variant.id,
708
+ title: variant.title,
709
+ image: variant.image == null ? null : { url: variant.image.url, altText: variant.image.altText ?? void 0 },
710
+ price: { amount: variant.price.toFixed(2), currencyCode },
711
+ ...((_a = variant.purchaseOptions) == null ? void 0 : _a.length) ? {
712
+ purchaseOptions: variant.purchaseOptions.map((opt) => ({
713
+ name: opt.name,
714
+ price: { amount: opt.price.toFixed(2), currencyCode },
715
+ interval: opt.interval
716
+ }))
717
+ } : {}
718
+ };
719
+ }
720
+ function mapProduct(product, currencyCode) {
721
+ return {
722
+ id: product.id,
723
+ title: product.title,
724
+ featuredImage: product.image == null ? null : { url: product.image.url, altText: product.image.altText ?? void 0 },
725
+ hasOnlyDefaultVariant: product.variants.length <= 1,
726
+ variants: {
727
+ items: product.variants.map((v2) => mapVariant(v2, currencyCode)),
728
+ pageInfo: {
729
+ hasNextPage: false,
730
+ hasPrevPage: false,
731
+ startCursor: null,
732
+ endCursor: null
733
+ }
734
+ },
735
+ collections: product.collections
736
+ };
737
+ }
738
+ function createApiProductAdapter(fetcher, opts = { currencyCode: "USD" }) {
739
+ const { currencyCode } = opts;
740
+ async function fetchProducts(params) {
741
+ const query = params.toString() ? `?${params.toString()}` : "";
742
+ const res = await fetcher(`/customer/v1/products${query}`);
743
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
744
+ return res.json();
745
+ }
746
+ return {
747
+ async getById(id) {
748
+ try {
749
+ const params = new URLSearchParams({ query: `id:"${id}"`, limit: "1" });
750
+ const body = await fetchProducts(params);
751
+ return body.data.length > 0 ? mapProduct(body.data[0], currencyCode) : null;
752
+ } catch {
753
+ return null;
754
+ }
755
+ },
756
+ async getByVariantId(variantId) {
757
+ try {
758
+ const params = new URLSearchParams({
759
+ query: `variants.id:"${variantId}"`,
760
+ limit: "1"
761
+ });
762
+ const body = await fetchProducts(params);
763
+ return body.data.length > 0 ? mapProduct(body.data[0], currencyCode) : null;
764
+ } catch {
765
+ return null;
766
+ }
767
+ },
768
+ async search(opts2) {
769
+ try {
770
+ const params = new URLSearchParams();
771
+ if (opts2 == null ? void 0 : opts2.query) {
772
+ params.set("query", `"${opts2.query.trim().replace(/"/g, '\\"')}"`);
773
+ }
774
+ if (opts2 == null ? void 0 : opts2.pagination) {
775
+ if ("after" in opts2.pagination)
776
+ params.set("after", opts2.pagination.after);
777
+ else if ("before" in opts2.pagination)
778
+ params.set("before", opts2.pagination.before);
779
+ }
780
+ const body = await fetchProducts(params);
781
+ return {
782
+ items: body.data.map((p2) => mapProduct(p2, currencyCode)),
783
+ pageInfo: {
784
+ hasNextPage: body.hasNextPage,
785
+ hasPrevPage: body.hasPrevPage,
786
+ startCursor: body.startCursor,
787
+ endCursor: body.endCursor
788
+ }
789
+ };
790
+ } catch {
791
+ return {
792
+ items: [],
793
+ pageInfo: {
794
+ hasNextPage: false,
795
+ hasPrevPage: false,
796
+ startCursor: null,
797
+ endCursor: null
798
+ }
799
+ };
800
+ }
801
+ }
802
+ };
803
+ }
681
804
  const ProductServiceContext = createContext("services/product");
682
805
  function createProductService(adapter) {
683
806
  return {
@@ -759,14 +882,6 @@ function createMockSubscriptionAdapter() {
759
882
  imageUrl: "https://images.unsplash.com/photo-1556228578-8c89e6adf883?w=200",
760
883
  price: { amount: "24.00", currencyCode: "USD" },
761
884
  description: "Lightweight daily sun protection"
762
- },
763
- {
764
- id: "upsell-3",
765
- variantId: "upsell-v-3",
766
- title: "Collagen Eye Patches",
767
- imageUrl: "https://images.unsplash.com/photo-1512290923902-8a9f81dc236c?w=200",
768
- price: { amount: "18.00", currencyCode: "USD" },
769
- description: "Depuffing under-eye treatment"
770
885
  }
771
886
  ];
772
887
  return {
@@ -777,7 +892,7 @@ function createMockSubscriptionAdapter() {
777
892
  },
778
893
  async search(options = {}) {
779
894
  await new Promise((resolve) => setTimeout(resolve, 300));
780
- const pageSize = 1;
895
+ const pageSize = 2;
781
896
  const allItems = mockSubscriptions;
782
897
  let startIndex = 0;
783
898
  let endIndex = pageSize;
@@ -793,20 +908,22 @@ function createMockSubscriptionAdapter() {
793
908
  }
794
909
  }
795
910
  const pageItems = allItems.slice(startIndex, endIndex);
796
- const paginatedList = {
797
- items: pageItems.map((sub) => ({
798
- ...sub,
799
- items: sub.items.map((item) => ({ ...item })),
800
- discounts: sub.discounts.map((discount) => ({ ...discount }))
801
- })),
802
- pageInfo: {
803
- hasNextPage: endIndex < allItems.length,
804
- hasPrevPage: startIndex > 0,
805
- startCursor: pageItems.length > 0 ? startIndex.toString() : null,
806
- endCursor: pageItems.length > 0 ? (endIndex - 1).toString() : null
911
+ return {
912
+ _tag: "Success",
913
+ data: {
914
+ items: pageItems.map((sub) => ({
915
+ ...sub,
916
+ items: sub.items.map((item) => ({ ...item })),
917
+ discounts: sub.discounts.map((discount) => ({ ...discount }))
918
+ })),
919
+ pageInfo: {
920
+ hasNextPage: endIndex < allItems.length,
921
+ hasPrevPage: startIndex > 0,
922
+ startCursor: pageItems.length > 0 ? startIndex.toString() : null,
923
+ endCursor: pageItems.length > 0 ? (endIndex - 1).toString() : null
924
+ }
807
925
  }
808
926
  };
809
- return { _tag: "Success", data: paginatedList };
810
927
  },
811
928
  async addItem() {
812
929
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -830,26 +947,11 @@ function createMockSubscriptionAdapter() {
830
947
  (sub) => sub.id === params.subscriptionId
831
948
  );
832
949
  if (!subscription) {
833
- return {
834
- _tag: "Failure",
835
- error: "Subscription not found"
836
- };
950
+ return { _tag: "Failure", error: "Subscription not found" };
837
951
  }
838
952
  subscription.nextDeliveryDate = new Date(params.newDate);
839
953
  subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
840
- subscription.items.forEach((item) => {
841
- item.nextDeliveryDate = new Date(params.newDate);
842
- });
843
- return {
844
- _tag: "Success",
845
- data: {
846
- ...subscription,
847
- items: subscription.items.map((item) => ({ ...item })),
848
- discounts: subscription.discounts.map((discount) => ({
849
- ...discount
850
- }))
851
- }
852
- };
954
+ return { _tag: "Success", data: { ...subscription } };
853
955
  },
854
956
  async resume(params) {
855
957
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -857,23 +959,11 @@ function createMockSubscriptionAdapter() {
857
959
  (sub) => sub.id === params.subscriptionId
858
960
  );
859
961
  if (!subscription) {
860
- return {
861
- _tag: "Failure",
862
- error: "Subscription not found"
863
- };
962
+ return { _tag: "Failure", error: "Subscription not found" };
864
963
  }
865
964
  subscription.status = "active";
866
965
  subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
867
- return {
868
- _tag: "Success",
869
- data: {
870
- ...subscription,
871
- items: subscription.items.map((item) => ({ ...item })),
872
- discounts: subscription.discounts.map((discount) => ({
873
- ...discount
874
- }))
875
- }
876
- };
966
+ return { _tag: "Success", data: { ...subscription } };
877
967
  },
878
968
  async pause(params) {
879
969
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -881,23 +971,11 @@ function createMockSubscriptionAdapter() {
881
971
  (sub) => sub.id === params.subscriptionId
882
972
  );
883
973
  if (!subscription) {
884
- return {
885
- _tag: "Failure",
886
- error: "Subscription not found"
887
- };
974
+ return { _tag: "Failure", error: "Subscription not found" };
888
975
  }
889
976
  subscription.status = "paused";
890
977
  subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
891
- return {
892
- _tag: "Success",
893
- data: {
894
- ...subscription,
895
- items: subscription.items.map((item) => ({ ...item })),
896
- discounts: subscription.discounts.map((discount) => ({
897
- ...discount
898
- }))
899
- }
900
- };
978
+ return { _tag: "Success", data: { ...subscription } };
901
979
  },
902
980
  async reactivate(params) {
903
981
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -905,23 +983,11 @@ function createMockSubscriptionAdapter() {
905
983
  (sub) => sub.id === params.subscriptionId
906
984
  );
907
985
  if (!subscription) {
908
- return {
909
- _tag: "Failure",
910
- error: "Subscription not found"
911
- };
986
+ return { _tag: "Failure", error: "Subscription not found" };
912
987
  }
913
988
  subscription.status = "active";
914
989
  subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
915
- return {
916
- _tag: "Success",
917
- data: {
918
- ...subscription,
919
- items: subscription.items.map((item) => ({ ...item })),
920
- discounts: subscription.discounts.map((discount) => ({
921
- ...discount
922
- }))
923
- }
924
- };
990
+ return { _tag: "Success", data: { ...subscription } };
925
991
  },
926
992
  async cancel(params) {
927
993
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -929,23 +995,11 @@ function createMockSubscriptionAdapter() {
929
995
  (sub) => sub.id === params.subscriptionId
930
996
  );
931
997
  if (!subscription) {
932
- return {
933
- _tag: "Failure",
934
- error: "Subscription not found"
935
- };
998
+ return { _tag: "Failure", error: "Subscription not found" };
936
999
  }
937
1000
  subscription.status = "canceled";
938
1001
  subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
939
- return {
940
- _tag: "Success",
941
- data: {
942
- ...subscription,
943
- items: subscription.items.map((item) => ({ ...item })),
944
- discounts: subscription.discounts.map((discount) => ({
945
- ...discount
946
- }))
947
- }
948
- };
1002
+ return { _tag: "Success", data: { ...subscription } };
949
1003
  },
950
1004
  async getAvailablePlans() {
951
1005
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -978,26 +1032,6 @@ function createMockSubscriptionAdapter() {
978
1032
  await new Promise((resolve) => setTimeout(resolve, 300));
979
1033
  return { _tag: "Success", data: ((_a = mockSubscriptions[0]) == null ? void 0 : _a.id) ?? null };
980
1034
  },
981
- async getDeliveryFrequencies() {
982
- await new Promise((resolve) => setTimeout(resolve, 200));
983
- return {
984
- _tag: "Success",
985
- data: [
986
- {
987
- id: "plan_1",
988
- name: "Monthly",
989
- billingPolicy: { intervalCount: 1, interval: "MONTH" },
990
- deliveryPolicy: { intervalCount: 1, interval: "MONTH" }
991
- },
992
- {
993
- id: "plan_2",
994
- name: "Every 2 months",
995
- billingPolicy: { intervalCount: 2, interval: "MONTH" },
996
- deliveryPolicy: { intervalCount: 2, interval: "MONTH" }
997
- }
998
- ]
999
- };
1000
- },
1001
1035
  async getUpsellProducts() {
1002
1036
  await new Promise((resolve) => setTimeout(resolve, 300));
1003
1037
  return { _tag: "Success", data: [...mockUpsellProducts] };
@@ -1005,74 +1039,96 @@ function createMockSubscriptionAdapter() {
1005
1039
  async addUpsellProduct() {
1006
1040
  await new Promise((resolve) => setTimeout(resolve, 400));
1007
1041
  return { _tag: "Success", data: void 0 };
1008
- },
1009
- async changeFrequency(params) {
1010
- await new Promise((resolve) => setTimeout(resolve, 300));
1011
- const subscription = mockSubscriptions.find(
1012
- (sub) => sub.id === params.subscriptionId
1013
- );
1014
- if (!subscription) {
1015
- return { _tag: "Failure", error: "Subscription not found" };
1016
- }
1017
- subscription.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1018
- return {
1019
- _tag: "Success",
1020
- data: { ...subscription }
1021
- };
1022
1042
  }
1023
1043
  };
1024
1044
  }
1045
+ function mapSubscription(raw) {
1046
+ var _a, _b;
1047
+ const status = raw.status === "merged" ? "canceled" : raw.status;
1048
+ const deliveryPolicy = raw.deliveryPolicy ?? ((_b = (_a = raw.items) == null ? void 0 : _a[0]) == null ? void 0 : _b.deliveryPolicy);
1049
+ const frequency = deliveryPolicy ? `${deliveryPolicy.intervalCount} ${deliveryPolicy.interval}` : "";
1050
+ return {
1051
+ id: raw.id,
1052
+ customerId: "",
1053
+ items: (raw.items ?? []).map(
1054
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1055
+ (item) => ({
1056
+ id: item.id,
1057
+ productId: item.productId ?? item.product ?? "",
1058
+ variantId: item.variantId ?? item.variant ?? "",
1059
+ quantity: item.quantity,
1060
+ price: item.price ?? {
1061
+ amount: String(item.totalPrice ?? 0),
1062
+ currencyCode: raw.currencyCode ?? ""
1063
+ },
1064
+ nextDeliveryDate: item.nextDeliveryDate ? new Date(item.nextDeliveryDate) : null
1065
+ })
1066
+ ),
1067
+ discounts: (raw.discounts ?? []).map(
1068
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1069
+ (d2) => {
1070
+ var _a2, _b2, _c;
1071
+ return {
1072
+ id: d2.id,
1073
+ type: ((_a2 = d2.value) == null ? void 0 : _a2.type) === "fixed-amount" || d2.type === "fixed_amount" ? "fixed_amount" : "percentage",
1074
+ value: ((_b2 = d2.value) == null ? void 0 : _b2.amount) ?? ((_c = d2.value) == null ? void 0 : _c.percentage) ?? d2.value ?? 0,
1075
+ code: d2.title ?? d2.code ?? "",
1076
+ description: d2.description ?? ""
1077
+ };
1078
+ }
1079
+ ),
1080
+ status,
1081
+ nextDeliveryDate: raw.nextBillingDate ? new Date(raw.nextBillingDate) : raw.nextDeliveryDate ? new Date(raw.nextDeliveryDate) : null,
1082
+ frequency,
1083
+ createdAt: raw.createdAt,
1084
+ updatedAt: raw.updatedAt
1085
+ };
1086
+ }
1025
1087
  function createApiSubscriptionAdapter(fetcher) {
1026
1088
  return {
1027
1089
  async search(options) {
1090
+ var _a;
1028
1091
  try {
1029
1092
  const params = new URLSearchParams();
1030
- if (options == null ? void 0 : options.filters) {
1031
- params.set("filters", JSON.stringify(options.filters));
1093
+ if ((_a = options == null ? void 0 : options.filters) == null ? void 0 : _a.length) {
1094
+ 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 ");
1095
+ if (q2) params.set("query", q2);
1032
1096
  }
1033
1097
  if (options == null ? void 0 : options.pagination) {
1034
- if ("after" in options.pagination) {
1098
+ if ("after" in options.pagination)
1035
1099
  params.set("after", options.pagination.after);
1036
- } else if ("before" in options.pagination) {
1100
+ else if ("before" in options.pagination)
1037
1101
  params.set("before", options.pagination.before);
1038
- }
1039
1102
  }
1040
1103
  const query = params.toString() ? `?${params.toString()}` : "";
1041
1104
  const res = await fetcher(`/customer/v1/subscriptions${query}`);
1042
1105
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1043
1106
  const body = await res.json();
1044
- body.data.forEach((sub) => {
1045
- if (sub.nextDeliveryDate) {
1046
- sub.nextDeliveryDate = new Date(sub.nextDeliveryDate);
1047
- }
1048
- sub.items.forEach((item) => {
1049
- if (item.nextDeliveryDate) {
1050
- item.nextDeliveryDate = new Date(item.nextDeliveryDate);
1107
+ return {
1108
+ _tag: "Success",
1109
+ data: {
1110
+ items: body.data.map(mapSubscription),
1111
+ pageInfo: {
1112
+ hasNextPage: body.hasNextPage,
1113
+ hasPrevPage: body.hasPrevPage,
1114
+ startCursor: body.startCursor,
1115
+ endCursor: body.endCursor
1051
1116
  }
1052
- });
1053
- });
1054
- const data = {
1055
- items: body.data,
1056
- pageInfo: {
1057
- hasNextPage: body.hasNextPage,
1058
- hasPrevPage: body.hasPrevPage,
1059
- startCursor: body.startCursor,
1060
- endCursor: body.endCursor
1061
1117
  }
1062
1118
  };
1063
- return { _tag: "Success", data };
1064
1119
  } catch (e2) {
1065
1120
  return { _tag: "Failure", error: e2 };
1066
1121
  }
1067
1122
  },
1068
1123
  async getById(id) {
1069
1124
  try {
1070
- const res = await fetcher(`/customer/v1/subscriptions/${id}`);
1125
+ const res = await fetcher(
1126
+ `/customer/v1/subscriptions?query=id:"${id}"&limit=1`
1127
+ );
1071
1128
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1072
- const data = await res.json();
1073
- if (data.nextDeliveryDate)
1074
- data.nextDeliveryDate = new Date(data.nextDeliveryDate);
1075
- return { _tag: "Success", data };
1129
+ const body = await res.json();
1130
+ const raw = body.data[0];
1131
+ return { _tag: "Success", data: raw ? mapSubscription(raw) : null };
1076
1132
  } catch (e2) {
1077
1133
  return { _tag: "Failure", error: e2 };
1078
1134
  }
@@ -1133,21 +1189,20 @@ function createApiSubscriptionAdapter(fetcher) {
1133
1189
  return { _tag: "Failure", error: e2 };
1134
1190
  }
1135
1191
  },
1192
+ // Uses PATCH /customer/v1/subscriptions/{id} — the dedicated /reschedule route does not exist.
1136
1193
  async reschedule({ subscriptionId, newDate }) {
1137
1194
  try {
1138
1195
  const res = await fetcher(
1139
- `/customer/v1/subscriptions/${subscriptionId}/reschedule`,
1196
+ `/customer/v1/subscriptions/${subscriptionId}`,
1140
1197
  {
1141
- method: "POST",
1198
+ method: "PATCH",
1142
1199
  headers: { "Content-Type": "application/json" },
1143
- body: JSON.stringify({ newDate })
1200
+ body: JSON.stringify({ nextBillingDate: newDate })
1144
1201
  }
1145
1202
  );
1146
1203
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1147
1204
  const data = await res.json();
1148
- if (data.nextDeliveryDate)
1149
- data.nextDeliveryDate = new Date(data.nextDeliveryDate);
1150
- return { _tag: "Success", data };
1205
+ return { _tag: "Success", data: mapSubscription(data) };
1151
1206
  } catch (e2) {
1152
1207
  return { _tag: "Failure", error: e2 };
1153
1208
  }
@@ -1160,7 +1215,7 @@ function createApiSubscriptionAdapter(fetcher) {
1160
1215
  );
1161
1216
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1162
1217
  const data = await res.json();
1163
- return { _tag: "Success", data };
1218
+ return { _tag: "Success", data: mapSubscription(data) };
1164
1219
  } catch (e2) {
1165
1220
  return { _tag: "Failure", error: e2 };
1166
1221
  }
@@ -1173,7 +1228,7 @@ function createApiSubscriptionAdapter(fetcher) {
1173
1228
  );
1174
1229
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1175
1230
  const data = await res.json();
1176
- return { _tag: "Success", data };
1231
+ return { _tag: "Success", data: mapSubscription(data) };
1177
1232
  } catch (e2) {
1178
1233
  return { _tag: "Failure", error: e2 };
1179
1234
  }
@@ -1186,7 +1241,7 @@ function createApiSubscriptionAdapter(fetcher) {
1186
1241
  );
1187
1242
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1188
1243
  const data = await res.json();
1189
- return { _tag: "Success", data };
1244
+ return { _tag: "Success", data: mapSubscription(data) };
1190
1245
  } catch (e2) {
1191
1246
  return { _tag: "Failure", error: e2 };
1192
1247
  }
@@ -1198,71 +1253,81 @@ function createApiSubscriptionAdapter(fetcher) {
1198
1253
  {
1199
1254
  method: "POST",
1200
1255
  headers: { "Content-Type": "application/json" },
1201
- body: JSON.stringify({ reason })
1256
+ body: JSON.stringify({ cancellationReason: reason })
1202
1257
  }
1203
1258
  );
1204
1259
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1205
1260
  const data = await res.json();
1206
- return { _tag: "Success", data };
1261
+ return { _tag: "Success", data: mapSubscription(data) };
1207
1262
  } catch (e2) {
1208
1263
  return { _tag: "Failure", error: e2 };
1209
1264
  }
1210
1265
  },
1266
+ // Legacy endpoint — no /customer/v1/subscriptions/{id}/plans exists yet.
1211
1267
  async getAvailablePlans({ subscriptionId }) {
1212
1268
  try {
1213
1269
  const res = await fetcher(
1214
- `/customer/v1/subscriptions/${subscriptionId}/plans`
1270
+ `/customer/subscriptions/${subscriptionId}/plans`
1215
1271
  );
1216
1272
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1217
1273
  const data = await res.json();
1218
- return { _tag: "Success", data: data.items };
1274
+ return { _tag: "Success", data };
1219
1275
  } catch (e2) {
1220
1276
  return { _tag: "Failure", error: e2 };
1221
1277
  }
1222
1278
  },
1279
+ // Legacy endpoint — no /customer/v1 equivalent exists yet.
1223
1280
  async getDefaultSubscriptionId() {
1224
- var _a;
1225
- try {
1226
- const res = await fetcher(`/customer/v1/subscriptions?limit=1`);
1227
- if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1228
- const body = await res.json();
1229
- return { _tag: "Success", data: ((_a = body.data[0]) == null ? void 0 : _a.id) ?? null };
1230
- } catch (e2) {
1231
- return { _tag: "Failure", error: e2 };
1232
- }
1233
- },
1234
- async getDeliveryFrequencies({ subscriptionId }) {
1235
1281
  try {
1236
1282
  const res = await fetcher(
1237
- `/customer/v1/subscriptions/${subscriptionId}/frequencies`
1283
+ `/customer/subscriptions/one-by-next-billing-date`
1238
1284
  );
1239
1285
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1240
1286
  const data = await res.json();
1241
- return { _tag: "Success", data: data.items };
1287
+ return { _tag: "Success", data: (data == null ? void 0 : data.id) ?? null };
1242
1288
  } catch (e2) {
1243
1289
  return { _tag: "Failure", error: e2 };
1244
1290
  }
1245
1291
  },
1292
+ // Legacy endpoint — no /customer/v1/subscriptions/{id}/upsells exists yet.
1246
1293
  async getUpsellProducts({ subscriptionId }) {
1247
1294
  try {
1248
1295
  const res = await fetcher(
1249
- `/customer/v1/subscriptions/${subscriptionId}/upsells`
1296
+ `/customer/subscriptions/${subscriptionId}/upsell-products`
1250
1297
  );
1251
1298
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1252
1299
  const data = await res.json();
1253
- return { _tag: "Success", data: data.items };
1300
+ const products = data.map((p2) => {
1301
+ var _a, _b, _c, _d, _e;
1302
+ return {
1303
+ id: p2.id,
1304
+ variantId: ((_b = (_a = p2.variants) == null ? void 0 : _a[0]) == null ? void 0 : _b.id) ?? "",
1305
+ title: p2.title ?? "",
1306
+ imageUrl: ((_c = p2.featuredImage) == null ? void 0 : _c.url) ?? void 0,
1307
+ price: {
1308
+ amount: String(((_e = (_d = p2.variants) == null ? void 0 : _d[0]) == null ? void 0 : _e.price) ?? 0),
1309
+ currencyCode: p2.currencyCode ?? ""
1310
+ }
1311
+ };
1312
+ });
1313
+ return { _tag: "Success", data: products };
1254
1314
  } catch (e2) {
1255
1315
  return { _tag: "Failure", error: e2 };
1256
1316
  }
1257
1317
  },
1318
+ // Legacy endpoint — no /customer/v1/subscriptions/{id}/upsells POST exists yet.
1258
1319
  async addUpsellProduct({ subscriptionId, variantId, quantity, oneTime }) {
1259
1320
  try {
1260
1321
  const res = await fetcher(
1261
- `/customer/v1/subscriptions/${subscriptionId}/upsells`,
1322
+ `/customer/subscriptions/${subscriptionId}/upsell`,
1262
1323
  {
1263
1324
  method: "POST",
1264
1325
  headers: { "Content-Type": "application/json" },
1265
- body: JSON.stringify({ variantId, quantity, oneTime })
1326
+ body: JSON.stringify({
1327
+ productVariantId: variantId,
1328
+ quantity,
1329
+ ...oneTime ? { renewalLimit: 1 } : {}
1330
+ })
1266
1331
  }
1267
1332
  );
1268
1333
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
@@ -1270,23 +1335,6 @@ function createApiSubscriptionAdapter(fetcher) {
1270
1335
  } catch (e2) {
1271
1336
  return { _tag: "Failure", error: e2 };
1272
1337
  }
1273
- },
1274
- async changeFrequency({ subscriptionId, sellingPlanId }) {
1275
- try {
1276
- const res = await fetcher(
1277
- `/customer/v1/subscriptions/${subscriptionId}/frequency`,
1278
- {
1279
- method: "POST",
1280
- headers: { "Content-Type": "application/json" },
1281
- body: JSON.stringify({ sellingPlanId })
1282
- }
1283
- );
1284
- if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1285
- const data = await res.json();
1286
- return { _tag: "Success", data };
1287
- } catch (e2) {
1288
- return { _tag: "Failure", error: e2 };
1289
- }
1290
1338
  }
1291
1339
  };
1292
1340
  }
@@ -1342,10 +1390,8 @@ function createSubscriptionService(adapter) {
1342
1390
  cancel: (subscriptionId, reason) => adapter.cancel({ subscriptionId, reason }),
1343
1391
  getAvailablePlans: (subscriptionId) => adapter.getAvailablePlans({ subscriptionId }),
1344
1392
  getDefaultSubscriptionId: () => adapter.getDefaultSubscriptionId(),
1345
- getDeliveryFrequencies: (params) => adapter.getDeliveryFrequencies ? adapter.getDeliveryFrequencies(params) : Promise.resolve({ _tag: "Success", data: [] }),
1346
1393
  getUpsellProducts: (params) => adapter.getUpsellProducts ? adapter.getUpsellProducts(params) : Promise.resolve({ _tag: "Success", data: [] }),
1347
- addUpsellProduct: (params) => adapter.addUpsellProduct ? adapter.addUpsellProduct(params) : Promise.resolve({ _tag: "Success", data: void 0 }),
1348
- changeFrequency: (params) => adapter.changeFrequency ? adapter.changeFrequency(params) : Promise.resolve({ _tag: "Failure", error: "Not implemented" })
1394
+ addUpsellProduct: (params) => adapter.addUpsellProduct ? adapter.addUpsellProduct(params) : Promise.resolve({ _tag: "Success", data: void 0 })
1349
1395
  };
1350
1396
  }
1351
1397
  const CustomerServiceContext = createContext("services/customer");
@@ -1357,55 +1403,14 @@ function createCustomerService(adapter) {
1357
1403
  const result = await adapter.getCurrent();
1358
1404
  if (result._tag === "Success") current.value = result.data;
1359
1405
  return result;
1360
- },
1361
- getAll: () => adapter.getAll(),
1362
- getById: (id) => adapter.getById(id),
1363
- create: (data) => adapter.create(data),
1364
- update: (id, data) => adapter.update(id, data),
1365
- remove: (id) => adapter.remove(id)
1406
+ }
1366
1407
  };
1367
1408
  }
1368
1409
  function createMockCustomerAdapter() {
1369
- let customers = [
1370
- {
1371
- id: "customer-1",
1372
- name: "Customer 1",
1373
- metadata: { mealCount: 3 }
1374
- },
1375
- {
1376
- id: "customer-2",
1377
- name: "Customer 2",
1378
- metadata: { mealCount: 4 }
1379
- }
1380
- ];
1410
+ const customer = { id: "customer-1", name: "Customer 1" };
1381
1411
  return {
1382
1412
  async getCurrent() {
1383
- return { _tag: "Success", data: customers[0] ?? null };
1384
- },
1385
- async getAll() {
1386
- return { _tag: "Success", data: [...customers] };
1387
- },
1388
- async getById(id) {
1389
- return {
1390
- _tag: "Success",
1391
- data: customers.find((c2) => c2.id === id) ?? null
1392
- };
1393
- },
1394
- async create(data) {
1395
- const customer = { id: `customer-${Date.now()}`, ...data };
1396
- customers = [...customers, customer];
1397
1413
  return { _tag: "Success", data: customer };
1398
- },
1399
- async update(id, data) {
1400
- const existing = customers.find((c2) => c2.id === id);
1401
- if (!existing) return { _tag: "Failure", error: "Customer not found" };
1402
- const updated = { ...existing, ...data };
1403
- customers = customers.map((c2) => c2.id === id ? updated : c2);
1404
- return { _tag: "Success", data: updated };
1405
- },
1406
- async remove(id) {
1407
- customers = customers.filter((c2) => c2.id !== id);
1408
- return { _tag: "Success", data: void 0 };
1409
1414
  }
1410
1415
  };
1411
1416
  }
@@ -1413,84 +1418,40 @@ function createApiCustomerAdapter(fetcher) {
1413
1418
  return {
1414
1419
  async getCurrent() {
1415
1420
  try {
1416
- const res = await fetcher("/customer/v1/customer");
1417
- if (!res.ok) return { _tag: "Failure", error: await res.text() };
1418
- const data = await res.json();
1419
- return { _tag: "Success", data };
1420
- } catch (e2) {
1421
- return { _tag: "Failure", error: e2 };
1422
- }
1423
- },
1424
- async getAll() {
1425
- try {
1426
- const res = await fetcher("/customer/v1/customers");
1427
- if (!res.ok) return { _tag: "Failure", error: await res.text() };
1428
- const data = await res.json();
1429
- return { _tag: "Success", data };
1430
- } catch (e2) {
1431
- return { _tag: "Failure", error: e2 };
1432
- }
1433
- },
1434
- async getById(id) {
1435
- try {
1436
- const res = await fetcher(`/customer/v1/customers/${id}`);
1437
- if (!res.ok) return { _tag: "Failure", error: await res.text() };
1421
+ const res = await fetcher("/customer/info");
1422
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1438
1423
  const data = await res.json();
1439
- return { _tag: "Success", data };
1440
- } catch (e2) {
1441
- return { _tag: "Failure", error: e2 };
1442
- }
1443
- },
1444
- async create(data) {
1445
- try {
1446
- const res = await fetcher("/customer/v1/customers", {
1447
- method: "POST",
1448
- headers: { "Content-Type": "application/json" },
1449
- body: JSON.stringify(data)
1450
- });
1451
- if (!res.ok) return { _tag: "Failure", error: await res.text() };
1452
- const created = await res.json();
1453
- return { _tag: "Success", data: created };
1454
- } catch (e2) {
1455
- return { _tag: "Failure", error: e2 };
1456
- }
1457
- },
1458
- async update(id, data) {
1459
- try {
1460
- const res = await fetcher(`/customer/v1/customers/${id}`, {
1461
- method: "PATCH",
1462
- headers: { "Content-Type": "application/json" },
1463
- body: JSON.stringify(data)
1464
- });
1465
- if (!res.ok) return { _tag: "Failure", error: await res.text() };
1466
- const updated = await res.json();
1467
- return { _tag: "Success", data: updated };
1468
- } catch (e2) {
1469
- return { _tag: "Failure", error: e2 };
1470
- }
1471
- },
1472
- async remove(id) {
1473
- try {
1474
- const res = await fetcher(`/customer/v1/customers/${id}`, {
1475
- method: "DELETE"
1476
- });
1477
- if (!res.ok) return { _tag: "Failure", error: await res.text() };
1478
- return { _tag: "Success", data: void 0 };
1424
+ const name = [data.firstName, data.lastName].filter(Boolean).join(" ") || data.email || "";
1425
+ return {
1426
+ _tag: "Success",
1427
+ data: { id: data.id, name, email: data.email }
1428
+ };
1479
1429
  } catch (e2) {
1480
1430
  return { _tag: "Failure", error: e2 };
1481
1431
  }
1482
1432
  }
1483
1433
  };
1484
1434
  }
1435
+ function mapOrder(order) {
1436
+ const items = order.items.map((item) => ({
1437
+ id: item.id,
1438
+ productId: item.productId ?? "",
1439
+ variantId: item.variantId ?? "",
1440
+ quantity: item.quantity
1441
+ }));
1442
+ return {
1443
+ id: order.id,
1444
+ customerId: "",
1445
+ items,
1446
+ deliveryDate: order.placedAt ?? null,
1447
+ status: "delivered"
1448
+ };
1449
+ }
1485
1450
  function createMockOrderAdapter() {
1486
- let orders = [
1487
- {
1488
- id: "order-1",
1489
- customerId: "customer-1",
1490
- items: [],
1491
- deliveryDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0] ?? null,
1492
- status: "pending"
1493
- },
1451
+ const now = Date.now();
1452
+ const past1 = new Date(now - 30 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0] ?? null;
1453
+ const past2 = new Date(now - 60 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0] ?? null;
1454
+ const orders = [
1494
1455
  {
1495
1456
  id: "order-2",
1496
1457
  customerId: "customer-1",
@@ -1498,121 +1459,47 @@ function createMockOrderAdapter() {
1498
1459
  { id: "item-1", productId: "p1", variantId: "v1", quantity: 1 },
1499
1460
  { id: "item-2", productId: "p2", variantId: "v2", quantity: 2 }
1500
1461
  ],
1501
- deliveryDate: new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0] ?? null,
1462
+ deliveryDate: past1,
1502
1463
  status: "delivered"
1503
1464
  },
1504
1465
  {
1505
1466
  id: "order-3",
1506
1467
  customerId: "customer-1",
1507
- items: [
1508
- { id: "item-3", productId: "p1", variantId: "v1", quantity: 1 }
1509
- ],
1510
- deliveryDate: new Date(Date.now() - 60 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0] ?? null,
1468
+ items: [{ id: "item-3", productId: "p1", variantId: "v1", quantity: 1 }],
1469
+ deliveryDate: past2,
1511
1470
  status: "delivered"
1512
- },
1513
- {
1514
- id: "order-4",
1515
- customerId: "customer-1",
1516
- items: [],
1517
- deliveryDate: new Date(Date.now() - 90 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0] ?? null,
1518
- status: "skipped"
1519
1471
  }
1520
1472
  ];
1521
1473
  return {
1522
- async getUpcoming() {
1523
- return {
1524
- _tag: "Success",
1525
- data: orders.filter((o2) => o2.status === "pending")
1526
- };
1527
- },
1528
1474
  async getHistory() {
1529
- return {
1530
- _tag: "Success",
1531
- data: orders.filter((o2) => o2.status === "delivered" || o2.status === "skipped").sort((a3, b3) => (b3.deliveryDate ?? "").localeCompare(a3.deliveryDate ?? ""))
1532
- };
1475
+ return { _tag: "Success", data: orders };
1533
1476
  },
1534
1477
  async getById(id) {
1535
- return {
1536
- _tag: "Success",
1537
- data: orders.find((o2) => o2.id === id) ?? null
1538
- };
1539
- },
1540
- async updateItems(orderId, items) {
1541
- const order = orders.find((o2) => o2.id === orderId);
1542
- if (!order) return { _tag: "Failure", error: "Order not found" };
1543
- const updated = {
1544
- ...order,
1545
- items: items.map((item, i2) => ({
1546
- id: `item-${Date.now()}-${i2}`,
1547
- ...item
1548
- }))
1549
- };
1550
- orders = orders.map((o2) => o2.id === orderId ? updated : o2);
1551
- return { _tag: "Success", data: updated };
1552
- },
1553
- async skip(orderId) {
1554
- const order = orders.find((o2) => o2.id === orderId);
1555
- if (!order) return { _tag: "Failure", error: "Order not found" };
1556
- const updated = { ...order, status: "skipped" };
1557
- orders = orders.map((o2) => o2.id === orderId ? updated : o2);
1558
- return { _tag: "Success", data: updated };
1478
+ return { _tag: "Success", data: orders.find((o2) => o2.id === id) ?? null };
1559
1479
  }
1560
1480
  };
1561
1481
  }
1562
1482
  function createApiOrderAdapter(fetcher) {
1563
1483
  return {
1564
- async getUpcoming() {
1565
- try {
1566
- const res = await fetcher("/customer/v1/orders?status=pending");
1567
- if (!res.ok) return { _tag: "Failure", error: await res.text() };
1568
- const body = await res.json();
1569
- return { _tag: "Success", data: body.orders };
1570
- } catch (e2) {
1571
- return { _tag: "Failure", error: e2 };
1572
- }
1573
- },
1574
1484
  async getHistory() {
1575
1485
  try {
1576
- const res = await fetcher("/customer/v1/orders?status=delivered,skipped");
1577
- if (!res.ok) return { _tag: "Failure", error: await res.text() };
1486
+ const res = await fetcher("/customer/v1/orders");
1487
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1578
1488
  const body = await res.json();
1579
- return { _tag: "Success", data: body.orders };
1489
+ return { _tag: "Success", data: body.orders.map(mapOrder) };
1580
1490
  } catch (e2) {
1581
1491
  return { _tag: "Failure", error: e2 };
1582
1492
  }
1583
1493
  },
1584
1494
  async getById(id) {
1585
1495
  try {
1586
- const res = await fetcher(`/customer/v1/orders/${id}`);
1587
- if (!res.ok) return { _tag: "Failure", error: await res.text() };
1588
- const data = await res.json();
1589
- return { _tag: "Success", data };
1590
- } catch (e2) {
1591
- return { _tag: "Failure", error: e2 };
1592
- }
1593
- },
1594
- async updateItems(orderId, items) {
1595
- try {
1596
- const res = await fetcher(`/customer/v1/orders/${orderId}/items`, {
1597
- method: "PUT",
1598
- headers: { "Content-Type": "application/json" },
1599
- body: JSON.stringify({ items })
1600
- });
1601
- if (!res.ok) return { _tag: "Failure", error: await res.text() };
1602
- const data = await res.json();
1603
- return { _tag: "Success", data };
1604
- } catch (e2) {
1605
- return { _tag: "Failure", error: e2 };
1606
- }
1607
- },
1608
- async skip(orderId) {
1609
- try {
1610
- const res = await fetcher(`/customer/v1/orders/${orderId}/skip`, {
1611
- method: "POST"
1612
- });
1613
- if (!res.ok) return { _tag: "Failure", error: await res.text() };
1614
- const data = await res.json();
1615
- return { _tag: "Success", data };
1496
+ const res = await fetcher(
1497
+ `/customer/v1/orders?query=id:"${id}"&limit=1`
1498
+ );
1499
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1500
+ const body = await res.json();
1501
+ const raw = body.orders[0];
1502
+ return { _tag: "Success", data: raw ? mapOrder(raw) : null };
1616
1503
  } catch (e2) {
1617
1504
  return { _tag: "Failure", error: e2 };
1618
1505
  }
@@ -1622,11 +1509,8 @@ function createApiOrderAdapter(fetcher) {
1622
1509
  const OrderServiceContext = createContext("services/order");
1623
1510
  function createOrderService(adapter) {
1624
1511
  return {
1625
- getUpcoming: () => adapter.getUpcoming(),
1626
1512
  getHistory: () => adapter.getHistory(),
1627
- getById: (id) => adapter.getById(id),
1628
- updateItems: (orderId, items) => adapter.updateItems(orderId, items),
1629
- skip: (orderId) => adapter.skip(orderId)
1513
+ getById: (id) => adapter.getById(id)
1630
1514
  };
1631
1515
  }
1632
1516
  function futureDateStr(daysFromNow) {
@@ -1741,9 +1625,7 @@ function createApiSchedulesAdapter(fetcher) {
1741
1625
  return {
1742
1626
  async getUpcomingOrders(count) {
1743
1627
  try {
1744
- const res = await fetcher(
1745
- `/customer/v1/schedules?count=${count}`
1746
- );
1628
+ const res = await fetcher(`/customer/v1/schedules/?count=${count}`);
1747
1629
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1748
1630
  const data = await res.json();
1749
1631
  return { _tag: "Success", data: data.orders };
@@ -1756,17 +1638,30 @@ function createApiSchedulesAdapter(fetcher) {
1756
1638
  const res = await fetcher(`/customer/v1/schedules/adjustments`);
1757
1639
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1758
1640
  const data = await res.json();
1759
- return { _tag: "Success", data: data.items };
1641
+ const adjustments = data.data.map((a3) => ({
1642
+ id: a3.id,
1643
+ subscriptionId: a3.subscriptionId ?? "",
1644
+ type: a3.type === "CHANGE_DATE" ? "date_change" : "skip",
1645
+ newDate: a3.newDate
1646
+ }));
1647
+ return { _tag: "Success", data: adjustments };
1760
1648
  } catch (e2) {
1761
1649
  return { _tag: "Failure", error: e2 };
1762
1650
  }
1763
1651
  },
1764
- async skipOrder(params) {
1652
+ async skipOrder({ cycle, date }) {
1653
+ if (cycle == null && date == null) {
1654
+ return { _tag: "Failure", error: "Either cycle or date is required for skipOrder" };
1655
+ }
1765
1656
  try {
1657
+ const matcher = cycle != null ? { type: "CYCLE", input: { cycle } } : { type: "DATE", input: { date: new Date(date).toISOString() } };
1766
1658
  const res = await fetcher(`/customer/v1/schedules/adjustments`, {
1767
1659
  method: "POST",
1768
1660
  headers: { "Content-Type": "application/json" },
1769
- body: JSON.stringify({ action: "skip", ...params })
1661
+ body: JSON.stringify({
1662
+ matcher,
1663
+ action: { type: "SKIP_ORDER", input: { reason: "" } }
1664
+ })
1770
1665
  });
1771
1666
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1772
1667
  return { _tag: "Success", data: void 0 };
@@ -1774,12 +1669,25 @@ function createApiSchedulesAdapter(fetcher) {
1774
1669
  return { _tag: "Failure", error: e2 };
1775
1670
  }
1776
1671
  },
1777
- async changeDateAdjustment(params) {
1672
+ async changeDateAdjustment({ newDate, currentDate }) {
1673
+ if (currentDate == null) {
1674
+ return {
1675
+ _tag: "Failure",
1676
+ error: "currentDate is required for changeDateAdjustment"
1677
+ };
1678
+ }
1778
1679
  try {
1680
+ const matcher = { type: "DATE", input: { date: new Date(currentDate).toISOString() } };
1779
1681
  const res = await fetcher(`/customer/v1/schedules/adjustments`, {
1780
1682
  method: "POST",
1781
1683
  headers: { "Content-Type": "application/json" },
1782
- body: JSON.stringify({ action: "date_change", ...params })
1684
+ body: JSON.stringify({
1685
+ matcher,
1686
+ action: {
1687
+ type: "CHANGE_DATE",
1688
+ input: { newDate: new Date(newDate).toISOString() }
1689
+ }
1690
+ })
1783
1691
  });
1784
1692
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1785
1693
  return { _tag: "Success", data: void 0 };
@@ -2122,7 +2030,9 @@ export {
2122
2030
  W as WorkflowServiceContext,
2123
2031
  w as computed,
2124
2032
  createApiCustomerAdapter,
2033
+ createApiLoginAdapter,
2125
2034
  createApiOrderAdapter,
2035
+ createApiProductAdapter,
2126
2036
  createApiSchedulesAdapter,
2127
2037
  createApiSubscriptionAdapter,
2128
2038
  a as createBlockInstance,
@@ -2131,7 +2041,9 @@ export {
2131
2041
  f as createCeRenderer,
2132
2042
  createContext,
2133
2043
  createCustomerService,
2044
+ createLoginService,
2134
2045
  createMockCustomerAdapter,
2046
+ createMockLoginAdapter,
2135
2047
  createMockOrderAdapter,
2136
2048
  createMockProductAdapter,
2137
2049
  createMockSchedulesAdapter,