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

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 +526 -608
  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 +10 -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 {
@@ -704,7 +827,9 @@ function createMockSubscriptionAdapter() {
704
827
  variantId: "variant_1",
705
828
  quantity: 2,
706
829
  price: { amount: "29.99", currencyCode: "USD" },
707
- nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15")
830
+ nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-15"),
831
+ billingPolicy: { intervalCount: 1, interval: "MONTH" },
832
+ deliveryPolicy: { intervalCount: 1, interval: "MONTH" }
708
833
  }
709
834
  ],
710
835
  discounts: [
@@ -732,7 +857,9 @@ function createMockSubscriptionAdapter() {
732
857
  variantId: "variant_2",
733
858
  quantity: 1,
734
859
  price: { amount: "49.99", currencyCode: "USD" },
735
- nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-20")
860
+ nextDeliveryDate: /* @__PURE__ */ new Date("2025-11-20"),
861
+ billingPolicy: { intervalCount: 2, interval: "WEEK" },
862
+ deliveryPolicy: { intervalCount: 2, interval: "WEEK" }
736
863
  }
737
864
  ],
738
865
  discounts: [],
@@ -759,14 +886,6 @@ function createMockSubscriptionAdapter() {
759
886
  imageUrl: "https://images.unsplash.com/photo-1556228578-8c89e6adf883?w=200",
760
887
  price: { amount: "24.00", currencyCode: "USD" },
761
888
  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
889
  }
771
890
  ];
772
891
  return {
@@ -777,7 +896,7 @@ function createMockSubscriptionAdapter() {
777
896
  },
778
897
  async search(options = {}) {
779
898
  await new Promise((resolve) => setTimeout(resolve, 300));
780
- const pageSize = 1;
899
+ const pageSize = 2;
781
900
  const allItems = mockSubscriptions;
782
901
  let startIndex = 0;
783
902
  let endIndex = pageSize;
@@ -793,20 +912,22 @@ function createMockSubscriptionAdapter() {
793
912
  }
794
913
  }
795
914
  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
915
+ return {
916
+ _tag: "Success",
917
+ data: {
918
+ items: pageItems.map((sub) => ({
919
+ ...sub,
920
+ items: sub.items.map((item) => ({ ...item })),
921
+ discounts: sub.discounts.map((discount) => ({ ...discount }))
922
+ })),
923
+ pageInfo: {
924
+ hasNextPage: endIndex < allItems.length,
925
+ hasPrevPage: startIndex > 0,
926
+ startCursor: pageItems.length > 0 ? startIndex.toString() : null,
927
+ endCursor: pageItems.length > 0 ? (endIndex - 1).toString() : null
928
+ }
807
929
  }
808
930
  };
809
- return { _tag: "Success", data: paginatedList };
810
931
  },
811
932
  async addItem() {
812
933
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -830,26 +951,11 @@ function createMockSubscriptionAdapter() {
830
951
  (sub) => sub.id === params.subscriptionId
831
952
  );
832
953
  if (!subscription) {
833
- return {
834
- _tag: "Failure",
835
- error: "Subscription not found"
836
- };
954
+ return { _tag: "Failure", error: "Subscription not found" };
837
955
  }
838
956
  subscription.nextDeliveryDate = new Date(params.newDate);
839
957
  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
- };
958
+ return { _tag: "Success", data: { ...subscription } };
853
959
  },
854
960
  async resume(params) {
855
961
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -857,23 +963,11 @@ function createMockSubscriptionAdapter() {
857
963
  (sub) => sub.id === params.subscriptionId
858
964
  );
859
965
  if (!subscription) {
860
- return {
861
- _tag: "Failure",
862
- error: "Subscription not found"
863
- };
966
+ return { _tag: "Failure", error: "Subscription not found" };
864
967
  }
865
968
  subscription.status = "active";
866
969
  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
- };
970
+ return { _tag: "Success", data: { ...subscription } };
877
971
  },
878
972
  async pause(params) {
879
973
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -881,23 +975,11 @@ function createMockSubscriptionAdapter() {
881
975
  (sub) => sub.id === params.subscriptionId
882
976
  );
883
977
  if (!subscription) {
884
- return {
885
- _tag: "Failure",
886
- error: "Subscription not found"
887
- };
978
+ return { _tag: "Failure", error: "Subscription not found" };
888
979
  }
889
980
  subscription.status = "paused";
890
981
  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
- };
982
+ return { _tag: "Success", data: { ...subscription } };
901
983
  },
902
984
  async reactivate(params) {
903
985
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -905,23 +987,11 @@ function createMockSubscriptionAdapter() {
905
987
  (sub) => sub.id === params.subscriptionId
906
988
  );
907
989
  if (!subscription) {
908
- return {
909
- _tag: "Failure",
910
- error: "Subscription not found"
911
- };
990
+ return { _tag: "Failure", error: "Subscription not found" };
912
991
  }
913
992
  subscription.status = "active";
914
993
  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
- };
994
+ return { _tag: "Success", data: { ...subscription } };
925
995
  },
926
996
  async cancel(params) {
927
997
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -929,23 +999,11 @@ function createMockSubscriptionAdapter() {
929
999
  (sub) => sub.id === params.subscriptionId
930
1000
  );
931
1001
  if (!subscription) {
932
- return {
933
- _tag: "Failure",
934
- error: "Subscription not found"
935
- };
1002
+ return { _tag: "Failure", error: "Subscription not found" };
936
1003
  }
937
1004
  subscription.status = "canceled";
938
1005
  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
- };
1006
+ return { _tag: "Success", data: { ...subscription } };
949
1007
  },
950
1008
  async getAvailablePlans() {
951
1009
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -978,26 +1036,6 @@ function createMockSubscriptionAdapter() {
978
1036
  await new Promise((resolve) => setTimeout(resolve, 300));
979
1037
  return { _tag: "Success", data: ((_a = mockSubscriptions[0]) == null ? void 0 : _a.id) ?? null };
980
1038
  },
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
1039
  async getUpsellProducts() {
1002
1040
  await new Promise((resolve) => setTimeout(resolve, 300));
1003
1041
  return { _tag: "Success", data: [...mockUpsellProducts] };
@@ -1005,74 +1043,98 @@ function createMockSubscriptionAdapter() {
1005
1043
  async addUpsellProduct() {
1006
1044
  await new Promise((resolve) => setTimeout(resolve, 400));
1007
1045
  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
1046
  }
1023
1047
  };
1024
1048
  }
1049
+ function mapSubscription(raw) {
1050
+ var _a, _b;
1051
+ const status = raw.status === "merged" ? "canceled" : raw.status;
1052
+ const deliveryPolicy = raw.deliveryPolicy ?? ((_b = (_a = raw.items) == null ? void 0 : _a[0]) == null ? void 0 : _b.deliveryPolicy);
1053
+ const frequency = deliveryPolicy ? `${deliveryPolicy.intervalCount} ${deliveryPolicy.interval}` : "";
1054
+ return {
1055
+ id: raw.id,
1056
+ customerId: "",
1057
+ items: (raw.items ?? []).map(
1058
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1059
+ (item) => ({
1060
+ id: item.id,
1061
+ productId: item.productId ?? item.product ?? "",
1062
+ variantId: item.variantId ?? item.variant ?? "",
1063
+ quantity: item.quantity,
1064
+ price: item.price ?? {
1065
+ amount: String(item.totalPrice ?? 0),
1066
+ currencyCode: raw.currencyCode ?? ""
1067
+ },
1068
+ nextDeliveryDate: item.nextDeliveryDate ? new Date(item.nextDeliveryDate) : null,
1069
+ billingPolicy: item.billingPolicy ?? raw.billingPolicy ?? null,
1070
+ deliveryPolicy: item.deliveryPolicy ?? raw.deliveryPolicy ?? null
1071
+ })
1072
+ ),
1073
+ discounts: (raw.discounts ?? []).map(
1074
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1075
+ (d2) => {
1076
+ var _a2, _b2, _c;
1077
+ return {
1078
+ id: d2.id,
1079
+ type: ((_a2 = d2.value) == null ? void 0 : _a2.type) === "fixed-amount" || d2.type === "fixed_amount" ? "fixed_amount" : "percentage",
1080
+ value: ((_b2 = d2.value) == null ? void 0 : _b2.amount) ?? ((_c = d2.value) == null ? void 0 : _c.percentage) ?? d2.value ?? 0,
1081
+ code: d2.title ?? d2.code ?? "",
1082
+ description: d2.description ?? ""
1083
+ };
1084
+ }
1085
+ ),
1086
+ status,
1087
+ nextDeliveryDate: raw.nextBillingDate ? new Date(raw.nextBillingDate) : raw.nextDeliveryDate ? new Date(raw.nextDeliveryDate) : null,
1088
+ frequency,
1089
+ createdAt: raw.createdAt,
1090
+ updatedAt: raw.updatedAt
1091
+ };
1092
+ }
1025
1093
  function createApiSubscriptionAdapter(fetcher) {
1026
1094
  return {
1027
1095
  async search(options) {
1096
+ var _a;
1028
1097
  try {
1029
1098
  const params = new URLSearchParams();
1030
- if (options == null ? void 0 : options.filters) {
1031
- params.set("filters", JSON.stringify(options.filters));
1099
+ if ((_a = options == null ? void 0 : options.filters) == null ? void 0 : _a.length) {
1100
+ 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 ");
1101
+ if (q2) params.set("query", q2);
1032
1102
  }
1033
1103
  if (options == null ? void 0 : options.pagination) {
1034
- if ("after" in options.pagination) {
1104
+ if ("after" in options.pagination)
1035
1105
  params.set("after", options.pagination.after);
1036
- } else if ("before" in options.pagination) {
1106
+ else if ("before" in options.pagination)
1037
1107
  params.set("before", options.pagination.before);
1038
- }
1039
1108
  }
1040
1109
  const query = params.toString() ? `?${params.toString()}` : "";
1041
1110
  const res = await fetcher(`/customer/v1/subscriptions${query}`);
1042
1111
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1043
1112
  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);
1113
+ return {
1114
+ _tag: "Success",
1115
+ data: {
1116
+ items: body.data.map(mapSubscription),
1117
+ pageInfo: {
1118
+ hasNextPage: body.hasNextPage,
1119
+ hasPrevPage: body.hasPrevPage,
1120
+ startCursor: body.startCursor,
1121
+ endCursor: body.endCursor
1051
1122
  }
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
1123
  }
1062
1124
  };
1063
- return { _tag: "Success", data };
1064
1125
  } catch (e2) {
1065
1126
  return { _tag: "Failure", error: e2 };
1066
1127
  }
1067
1128
  },
1068
1129
  async getById(id) {
1069
1130
  try {
1070
- const res = await fetcher(`/customer/v1/subscriptions/${id}`);
1131
+ const res = await fetcher(
1132
+ `/customer/v1/subscriptions?query=id:"${id}"&limit=1`
1133
+ );
1071
1134
  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 };
1135
+ const body = await res.json();
1136
+ const raw = body.data[0];
1137
+ return { _tag: "Success", data: raw ? mapSubscription(raw) : null };
1076
1138
  } catch (e2) {
1077
1139
  return { _tag: "Failure", error: e2 };
1078
1140
  }
@@ -1133,21 +1195,20 @@ function createApiSubscriptionAdapter(fetcher) {
1133
1195
  return { _tag: "Failure", error: e2 };
1134
1196
  }
1135
1197
  },
1198
+ // Uses PATCH /customer/v1/subscriptions/{id} — the dedicated /reschedule route does not exist.
1136
1199
  async reschedule({ subscriptionId, newDate }) {
1137
1200
  try {
1138
1201
  const res = await fetcher(
1139
- `/customer/v1/subscriptions/${subscriptionId}/reschedule`,
1202
+ `/customer/v1/subscriptions/${subscriptionId}`,
1140
1203
  {
1141
- method: "POST",
1204
+ method: "PATCH",
1142
1205
  headers: { "Content-Type": "application/json" },
1143
- body: JSON.stringify({ newDate })
1206
+ body: JSON.stringify({ nextBillingDate: newDate })
1144
1207
  }
1145
1208
  );
1146
1209
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1147
1210
  const data = await res.json();
1148
- if (data.nextDeliveryDate)
1149
- data.nextDeliveryDate = new Date(data.nextDeliveryDate);
1150
- return { _tag: "Success", data };
1211
+ return { _tag: "Success", data: mapSubscription(data) };
1151
1212
  } catch (e2) {
1152
1213
  return { _tag: "Failure", error: e2 };
1153
1214
  }
@@ -1160,7 +1221,7 @@ function createApiSubscriptionAdapter(fetcher) {
1160
1221
  );
1161
1222
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1162
1223
  const data = await res.json();
1163
- return { _tag: "Success", data };
1224
+ return { _tag: "Success", data: mapSubscription(data) };
1164
1225
  } catch (e2) {
1165
1226
  return { _tag: "Failure", error: e2 };
1166
1227
  }
@@ -1173,7 +1234,7 @@ function createApiSubscriptionAdapter(fetcher) {
1173
1234
  );
1174
1235
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1175
1236
  const data = await res.json();
1176
- return { _tag: "Success", data };
1237
+ return { _tag: "Success", data: mapSubscription(data) };
1177
1238
  } catch (e2) {
1178
1239
  return { _tag: "Failure", error: e2 };
1179
1240
  }
@@ -1186,7 +1247,7 @@ function createApiSubscriptionAdapter(fetcher) {
1186
1247
  );
1187
1248
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1188
1249
  const data = await res.json();
1189
- return { _tag: "Success", data };
1250
+ return { _tag: "Success", data: mapSubscription(data) };
1190
1251
  } catch (e2) {
1191
1252
  return { _tag: "Failure", error: e2 };
1192
1253
  }
@@ -1198,71 +1259,81 @@ function createApiSubscriptionAdapter(fetcher) {
1198
1259
  {
1199
1260
  method: "POST",
1200
1261
  headers: { "Content-Type": "application/json" },
1201
- body: JSON.stringify({ reason })
1262
+ body: JSON.stringify({ cancellationReason: reason })
1202
1263
  }
1203
1264
  );
1204
1265
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1205
1266
  const data = await res.json();
1206
- return { _tag: "Success", data };
1267
+ return { _tag: "Success", data: mapSubscription(data) };
1207
1268
  } catch (e2) {
1208
1269
  return { _tag: "Failure", error: e2 };
1209
1270
  }
1210
1271
  },
1272
+ // Legacy endpoint — no /customer/v1/subscriptions/{id}/plans exists yet.
1211
1273
  async getAvailablePlans({ subscriptionId }) {
1212
1274
  try {
1213
1275
  const res = await fetcher(
1214
- `/customer/v1/subscriptions/${subscriptionId}/plans`
1276
+ `/customer/subscriptions/${subscriptionId}/plans`
1215
1277
  );
1216
1278
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1217
1279
  const data = await res.json();
1218
- return { _tag: "Success", data: data.items };
1280
+ return { _tag: "Success", data };
1219
1281
  } catch (e2) {
1220
1282
  return { _tag: "Failure", error: e2 };
1221
1283
  }
1222
1284
  },
1285
+ // Legacy endpoint — no /customer/v1 equivalent exists yet.
1223
1286
  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
1287
  try {
1236
1288
  const res = await fetcher(
1237
- `/customer/v1/subscriptions/${subscriptionId}/frequencies`
1289
+ `/customer/subscriptions/one-by-next-billing-date`
1238
1290
  );
1239
1291
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1240
1292
  const data = await res.json();
1241
- return { _tag: "Success", data: data.items };
1293
+ return { _tag: "Success", data: (data == null ? void 0 : data.id) ?? null };
1242
1294
  } catch (e2) {
1243
1295
  return { _tag: "Failure", error: e2 };
1244
1296
  }
1245
1297
  },
1298
+ // Legacy endpoint — no /customer/v1/subscriptions/{id}/upsells exists yet.
1246
1299
  async getUpsellProducts({ subscriptionId }) {
1247
1300
  try {
1248
1301
  const res = await fetcher(
1249
- `/customer/v1/subscriptions/${subscriptionId}/upsells`
1302
+ `/customer/subscriptions/${subscriptionId}/upsell-products`
1250
1303
  );
1251
1304
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1252
1305
  const data = await res.json();
1253
- return { _tag: "Success", data: data.items };
1306
+ const products = data.map((p2) => {
1307
+ var _a, _b, _c, _d, _e;
1308
+ return {
1309
+ id: p2.id,
1310
+ variantId: ((_b = (_a = p2.variants) == null ? void 0 : _a[0]) == null ? void 0 : _b.id) ?? "",
1311
+ title: p2.title ?? "",
1312
+ imageUrl: ((_c = p2.featuredImage) == null ? void 0 : _c.url) ?? void 0,
1313
+ price: {
1314
+ amount: String(((_e = (_d = p2.variants) == null ? void 0 : _d[0]) == null ? void 0 : _e.price) ?? 0),
1315
+ currencyCode: p2.currencyCode ?? ""
1316
+ }
1317
+ };
1318
+ });
1319
+ return { _tag: "Success", data: products };
1254
1320
  } catch (e2) {
1255
1321
  return { _tag: "Failure", error: e2 };
1256
1322
  }
1257
1323
  },
1324
+ // Legacy endpoint — no /customer/v1/subscriptions/{id}/upsells POST exists yet.
1258
1325
  async addUpsellProduct({ subscriptionId, variantId, quantity, oneTime }) {
1259
1326
  try {
1260
1327
  const res = await fetcher(
1261
- `/customer/v1/subscriptions/${subscriptionId}/upsells`,
1328
+ `/customer/subscriptions/${subscriptionId}/upsell`,
1262
1329
  {
1263
1330
  method: "POST",
1264
1331
  headers: { "Content-Type": "application/json" },
1265
- body: JSON.stringify({ variantId, quantity, oneTime })
1332
+ body: JSON.stringify({
1333
+ productVariantId: variantId,
1334
+ quantity,
1335
+ ...oneTime ? { renewalLimit: 1 } : {}
1336
+ })
1266
1337
  }
1267
1338
  );
1268
1339
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
@@ -1270,23 +1341,6 @@ function createApiSubscriptionAdapter(fetcher) {
1270
1341
  } catch (e2) {
1271
1342
  return { _tag: "Failure", error: e2 };
1272
1343
  }
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
1344
  }
1291
1345
  };
1292
1346
  }
@@ -1342,10 +1396,8 @@ function createSubscriptionService(adapter) {
1342
1396
  cancel: (subscriptionId, reason) => adapter.cancel({ subscriptionId, reason }),
1343
1397
  getAvailablePlans: (subscriptionId) => adapter.getAvailablePlans({ subscriptionId }),
1344
1398
  getDefaultSubscriptionId: () => adapter.getDefaultSubscriptionId(),
1345
- getDeliveryFrequencies: (params) => adapter.getDeliveryFrequencies ? adapter.getDeliveryFrequencies(params) : Promise.resolve({ _tag: "Success", data: [] }),
1346
1399
  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" })
1400
+ addUpsellProduct: (params) => adapter.addUpsellProduct ? adapter.addUpsellProduct(params) : Promise.resolve({ _tag: "Success", data: void 0 })
1349
1401
  };
1350
1402
  }
1351
1403
  const CustomerServiceContext = createContext("services/customer");
@@ -1357,55 +1409,14 @@ function createCustomerService(adapter) {
1357
1409
  const result = await adapter.getCurrent();
1358
1410
  if (result._tag === "Success") current.value = result.data;
1359
1411
  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)
1412
+ }
1366
1413
  };
1367
1414
  }
1368
1415
  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
- ];
1416
+ const customer = { id: "customer-1", name: "Customer 1" };
1381
1417
  return {
1382
1418
  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
1419
  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
1420
  }
1410
1421
  };
1411
1422
  }
@@ -1413,84 +1424,40 @@ function createApiCustomerAdapter(fetcher) {
1413
1424
  return {
1414
1425
  async getCurrent() {
1415
1426
  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() };
1427
+ const res = await fetcher("/customer/info");
1428
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1438
1429
  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 };
1430
+ const name = [data.firstName, data.lastName].filter(Boolean).join(" ") || data.email || "";
1431
+ return {
1432
+ _tag: "Success",
1433
+ data: { id: data.id, name, email: data.email }
1434
+ };
1479
1435
  } catch (e2) {
1480
1436
  return { _tag: "Failure", error: e2 };
1481
1437
  }
1482
1438
  }
1483
1439
  };
1484
1440
  }
1441
+ function mapOrder(order) {
1442
+ const items = order.items.map((item) => ({
1443
+ id: item.id,
1444
+ productId: item.productId ?? "",
1445
+ variantId: item.variantId ?? "",
1446
+ quantity: item.quantity
1447
+ }));
1448
+ return {
1449
+ id: order.id,
1450
+ customerId: "",
1451
+ items,
1452
+ deliveryDate: order.placedAt ?? null,
1453
+ status: "delivered"
1454
+ };
1455
+ }
1485
1456
  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
- },
1457
+ const now = Date.now();
1458
+ const past1 = new Date(now - 30 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0] ?? null;
1459
+ const past2 = new Date(now - 60 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0] ?? null;
1460
+ const orders = [
1494
1461
  {
1495
1462
  id: "order-2",
1496
1463
  customerId: "customer-1",
@@ -1498,121 +1465,47 @@ function createMockOrderAdapter() {
1498
1465
  { id: "item-1", productId: "p1", variantId: "v1", quantity: 1 },
1499
1466
  { id: "item-2", productId: "p2", variantId: "v2", quantity: 2 }
1500
1467
  ],
1501
- deliveryDate: new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0] ?? null,
1468
+ deliveryDate: past1,
1502
1469
  status: "delivered"
1503
1470
  },
1504
1471
  {
1505
1472
  id: "order-3",
1506
1473
  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,
1474
+ items: [{ id: "item-3", productId: "p1", variantId: "v1", quantity: 1 }],
1475
+ deliveryDate: past2,
1511
1476
  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
1477
  }
1520
1478
  ];
1521
1479
  return {
1522
- async getUpcoming() {
1523
- return {
1524
- _tag: "Success",
1525
- data: orders.filter((o2) => o2.status === "pending")
1526
- };
1527
- },
1528
1480
  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
- };
1481
+ return { _tag: "Success", data: orders };
1533
1482
  },
1534
1483
  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 };
1484
+ return { _tag: "Success", data: orders.find((o2) => o2.id === id) ?? null };
1559
1485
  }
1560
1486
  };
1561
1487
  }
1562
1488
  function createApiOrderAdapter(fetcher) {
1563
1489
  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
1490
  async getHistory() {
1575
1491
  try {
1576
- const res = await fetcher("/customer/v1/orders?status=delivered,skipped");
1577
- if (!res.ok) return { _tag: "Failure", error: await res.text() };
1492
+ const res = await fetcher("/customer/v1/orders");
1493
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1578
1494
  const body = await res.json();
1579
- return { _tag: "Success", data: body.orders };
1495
+ return { _tag: "Success", data: body.orders.map(mapOrder) };
1580
1496
  } catch (e2) {
1581
1497
  return { _tag: "Failure", error: e2 };
1582
1498
  }
1583
1499
  },
1584
1500
  async getById(id) {
1585
1501
  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 };
1502
+ const res = await fetcher(
1503
+ `/customer/v1/orders?query=id:"${id}"&limit=1`
1504
+ );
1505
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1506
+ const body = await res.json();
1507
+ const raw = body.orders[0];
1508
+ return { _tag: "Success", data: raw ? mapOrder(raw) : null };
1616
1509
  } catch (e2) {
1617
1510
  return { _tag: "Failure", error: e2 };
1618
1511
  }
@@ -1622,11 +1515,8 @@ function createApiOrderAdapter(fetcher) {
1622
1515
  const OrderServiceContext = createContext("services/order");
1623
1516
  function createOrderService(adapter) {
1624
1517
  return {
1625
- getUpcoming: () => adapter.getUpcoming(),
1626
1518
  getHistory: () => adapter.getHistory(),
1627
- getById: (id) => adapter.getById(id),
1628
- updateItems: (orderId, items) => adapter.updateItems(orderId, items),
1629
- skip: (orderId) => adapter.skip(orderId)
1519
+ getById: (id) => adapter.getById(id)
1630
1520
  };
1631
1521
  }
1632
1522
  function futureDateStr(daysFromNow) {
@@ -1741,9 +1631,7 @@ function createApiSchedulesAdapter(fetcher) {
1741
1631
  return {
1742
1632
  async getUpcomingOrders(count) {
1743
1633
  try {
1744
- const res = await fetcher(
1745
- `/customer/v1/schedules?count=${count}`
1746
- );
1634
+ const res = await fetcher(`/customer/v1/schedules/?count=${count}`);
1747
1635
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1748
1636
  const data = await res.json();
1749
1637
  return { _tag: "Success", data: data.orders };
@@ -1756,17 +1644,30 @@ function createApiSchedulesAdapter(fetcher) {
1756
1644
  const res = await fetcher(`/customer/v1/schedules/adjustments`);
1757
1645
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1758
1646
  const data = await res.json();
1759
- return { _tag: "Success", data: data.items };
1647
+ const adjustments = data.data.map((a3) => ({
1648
+ id: a3.id,
1649
+ subscriptionId: a3.subscriptionId ?? "",
1650
+ type: a3.type === "CHANGE_DATE" ? "date_change" : "skip",
1651
+ newDate: a3.newDate
1652
+ }));
1653
+ return { _tag: "Success", data: adjustments };
1760
1654
  } catch (e2) {
1761
1655
  return { _tag: "Failure", error: e2 };
1762
1656
  }
1763
1657
  },
1764
- async skipOrder(params) {
1658
+ async skipOrder({ cycle, date }) {
1659
+ if (cycle == null && date == null) {
1660
+ return { _tag: "Failure", error: "Either cycle or date is required for skipOrder" };
1661
+ }
1765
1662
  try {
1663
+ const matcher = cycle != null ? { type: "CYCLE", input: { cycle } } : { type: "DATE", input: { date: new Date(date).toISOString() } };
1766
1664
  const res = await fetcher(`/customer/v1/schedules/adjustments`, {
1767
1665
  method: "POST",
1768
1666
  headers: { "Content-Type": "application/json" },
1769
- body: JSON.stringify({ action: "skip", ...params })
1667
+ body: JSON.stringify({
1668
+ matcher,
1669
+ action: { type: "SKIP_ORDER", input: { reason: "" } }
1670
+ })
1770
1671
  });
1771
1672
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1772
1673
  return { _tag: "Success", data: void 0 };
@@ -1774,12 +1675,25 @@ function createApiSchedulesAdapter(fetcher) {
1774
1675
  return { _tag: "Failure", error: e2 };
1775
1676
  }
1776
1677
  },
1777
- async changeDateAdjustment(params) {
1678
+ async changeDateAdjustment({ newDate, currentDate }) {
1679
+ if (currentDate == null) {
1680
+ return {
1681
+ _tag: "Failure",
1682
+ error: "currentDate is required for changeDateAdjustment"
1683
+ };
1684
+ }
1778
1685
  try {
1686
+ const matcher = { type: "DATE", input: { date: new Date(currentDate).toISOString() } };
1779
1687
  const res = await fetcher(`/customer/v1/schedules/adjustments`, {
1780
1688
  method: "POST",
1781
1689
  headers: { "Content-Type": "application/json" },
1782
- body: JSON.stringify({ action: "date_change", ...params })
1690
+ body: JSON.stringify({
1691
+ matcher,
1692
+ action: {
1693
+ type: "CHANGE_DATE",
1694
+ input: { newDate: new Date(newDate).toISOString() }
1695
+ }
1696
+ })
1783
1697
  });
1784
1698
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
1785
1699
  return { _tag: "Success", data: void 0 };
@@ -2122,7 +2036,9 @@ export {
2122
2036
  W as WorkflowServiceContext,
2123
2037
  w as computed,
2124
2038
  createApiCustomerAdapter,
2039
+ createApiLoginAdapter,
2125
2040
  createApiOrderAdapter,
2041
+ createApiProductAdapter,
2126
2042
  createApiSchedulesAdapter,
2127
2043
  createApiSubscriptionAdapter,
2128
2044
  a as createBlockInstance,
@@ -2131,7 +2047,9 @@ export {
2131
2047
  f as createCeRenderer,
2132
2048
  createContext,
2133
2049
  createCustomerService,
2050
+ createLoginService,
2134
2051
  createMockCustomerAdapter,
2052
+ createMockLoginAdapter,
2135
2053
  createMockOrderAdapter,
2136
2054
  createMockProductAdapter,
2137
2055
  createMockSchedulesAdapter,