@juo/blocks 2.0.0-alpha.5 → 2.0.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 (62) hide show
  1. package/dist/{ExtensionRoot-DRpD5EcC.js → ExtensionRoot-CG9dH98z.js} +1 -1
  2. package/dist/core.js +473 -13
  3. package/dist/{css-runtime-B7tZrBIp.js → css-runtime-CjZl0K48.js} +23 -17
  4. package/dist/editor/handler.d.ts.map +1 -1
  5. package/dist/editor.js +4 -1
  6. package/dist/{gridstack-adapter-BGKGC-eO.js → gridstack-adapter-C1Q8gbsH.js} +2 -2
  7. package/dist/languages.d.ts +51 -0
  8. package/dist/languages.d.ts.map +1 -0
  9. package/dist/main.d.ts +1 -0
  10. package/dist/main.d.ts.map +1 -1
  11. package/dist/{project-gridstack-PxN_DIfP.js → project-gridstack-CRZ_7HoT.js} +1 -1
  12. package/dist/services/customer/adapter.d.ts +12 -0
  13. package/dist/services/customer/adapter.d.ts.map +1 -1
  14. package/dist/services/customer/adapter.test.d.ts +2 -0
  15. package/dist/services/customer/adapter.test.d.ts.map +1 -0
  16. package/dist/services/customer/customerService.d.ts.map +1 -1
  17. package/dist/services/customer/index.d.ts +1 -1
  18. package/dist/services/customer/index.d.ts.map +1 -1
  19. package/dist/services/customer/types.d.ts +13 -0
  20. package/dist/services/customer/types.d.ts.map +1 -1
  21. package/dist/services/login/adapter.d.ts.map +1 -1
  22. package/dist/services/login/loginService.d.ts +1 -0
  23. package/dist/services/login/loginService.d.ts.map +1 -1
  24. package/dist/services/login/loginService.test.d.ts +2 -0
  25. package/dist/services/login/loginService.test.d.ts.map +1 -0
  26. package/dist/services/order/adapter.d.ts +11 -1
  27. package/dist/services/order/adapter.d.ts.map +1 -1
  28. package/dist/services/order/adapter.test.d.ts +2 -0
  29. package/dist/services/order/adapter.test.d.ts.map +1 -0
  30. package/dist/services/order/index.d.ts +2 -2
  31. package/dist/services/order/index.d.ts.map +1 -1
  32. package/dist/services/order/types.d.ts +56 -6
  33. package/dist/services/order/types.d.ts.map +1 -1
  34. package/dist/services/subscription/adapter.d.ts +10 -1
  35. package/dist/services/subscription/adapter.d.ts.map +1 -1
  36. package/dist/services/subscription/index.d.ts +2 -0
  37. package/dist/services/subscription/index.d.ts.map +1 -1
  38. package/dist/services/subscription/instantOrderErrors.d.ts +66 -0
  39. package/dist/services/subscription/instantOrderErrors.d.ts.map +1 -0
  40. package/dist/services/subscription/lineRemoval.d.ts +56 -0
  41. package/dist/services/subscription/lineRemoval.d.ts.map +1 -0
  42. package/dist/services/subscription/lineRemoval.test.d.ts +2 -0
  43. package/dist/services/subscription/lineRemoval.test.d.ts.map +1 -0
  44. package/dist/services/subscription/paymentInstrument.d.ts +8 -1
  45. package/dist/services/subscription/paymentInstrument.d.ts.map +1 -1
  46. package/dist/services/subscription/subscriptionService.d.ts.map +1 -1
  47. package/dist/services/subscription/types.d.ts +87 -1
  48. package/dist/services/subscription/types.d.ts.map +1 -1
  49. package/dist/theme-state.d.ts +10 -1
  50. package/dist/theme-state.d.ts.map +1 -1
  51. package/dist/utils/index.d.ts +7 -0
  52. package/dist/utils/index.d.ts.map +1 -1
  53. package/dist/vue/context.d.ts +25 -1
  54. package/dist/vue/context.d.ts.map +1 -1
  55. package/dist/vue/useTranslatedProp.test.d.ts +2 -0
  56. package/dist/vue/useTranslatedProp.test.d.ts.map +1 -0
  57. package/dist/vue.js +36 -2
  58. package/dist/web-components/editor/InlineText.d.ts +1 -0
  59. package/dist/web-components/editor/InlineText.d.ts.map +1 -1
  60. package/dist/web-components-editor.js +13 -5
  61. package/dist/web-components-runtime.js +2 -2
  62. package/package.json +1 -1
@@ -4,7 +4,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
4
4
  import { signal, effect, createBlockInstance, layoutSystemEnabled, untracked } from "@juo/blocks";
5
5
  import { reportBlockWarning, reportBlockError } from "./observability.js";
6
6
  import { b as isStubDefinition, w as withWrapper, s as subscribeContext, c as backfillInstanceDefaults, d as __clearDevtoolsBlockState, o as onRegisterBlock } from "./block-BjJztStr.js";
7
- import { L as LAYOUT_SCOPE_ATTR, b as buildGridLayout, a as ThemeStateContext } from "./css-runtime-B7tZrBIp.js";
7
+ import { L as LAYOUT_SCOPE_ATTR, b as buildGridLayout, a as ThemeStateContext } from "./css-runtime-CjZl0K48.js";
8
8
  import { getHook } from "./devtools.js";
9
9
  class Block extends HTMLElement {
10
10
  constructor() {
package/dist/core.js CHANGED
@@ -3,12 +3,33 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
  import { e as createContext, f as d, m as w, n as defineBlock, h as createBlockInstance, j as getDefinedBlocks, p as registerBlock, E, u, s as subscribeContext } from "./block-BjJztStr.js";
5
5
  import { B, q, t, S, v, _, x, c, y, g, z, A, D, F, G, i, H, b, I, o, J, k, K, l, L, w as w2 } from "./block-BjJztStr.js";
6
- import { G as GRID_V1_ENGINE_ID, d as clampRect, e as applyFillExpansion, f as compactItems, g as findFreeRect, D as DEFAULT_GRID } from "./css-runtime-B7tZrBIp.js";
7
- import { h, i as i2, L as L2, P, a, T, b as b2, j, k as k2, l as l2, m, n, o as o2, p, q as q2, s, t as t2, u as u2, v as v2, c as c2, w as w3, r } from "./css-runtime-B7tZrBIp.js";
6
+ import { G as GRID_V1_ENGINE_ID, d as clampRect, e as applyFillExpansion, f as compactItems, g as findFreeRect, D as DEFAULT_GRID } from "./css-runtime-CjZl0K48.js";
7
+ import { h, i as i2, L as L2, P, a, T, b as b2, j, k as k2, l as l2, m, n, o as o2, p, q as q2, s, t as t2, u as u2, v as v2, c as c2, w as w3, r } from "./css-runtime-CjZl0K48.js";
8
8
  import { reportBlockWarning, reportBlockError } from "./observability.js";
9
9
  import { a as createEditorBridge, E as createSelectBlockMessage } from "./bridge-Mz0CqG6E.js";
10
- import { p as p2 } from "./project-gridstack-PxN_DIfP.js";
10
+ import { p as p2 } from "./project-gridstack-CRZ_7HoT.js";
11
11
  import { clearLayoutModeAdapter, getLayoutModeAdapter, registerLayoutModeAdapter } from "./layout-mode-adapter-BBd4JRQd.js";
12
+ const SUPPORTED_UI_LANGUAGES = [
13
+ { code: "cs", label: "Czech" },
14
+ { code: "da", label: "Danish" },
15
+ { code: "nl", label: "Dutch" },
16
+ { code: "en", label: "English" },
17
+ { code: "fr", label: "French" },
18
+ { code: "de", label: "German" },
19
+ { code: "el", label: "Greek" },
20
+ { code: "hu", label: "Hungarian" },
21
+ { code: "pl", label: "Polish" },
22
+ { code: "pt", label: "Portuguese" },
23
+ { code: "sk", label: "Slovak" },
24
+ { code: "sl", label: "Slovenian" },
25
+ { code: "es", label: "Spanish" }
26
+ ];
27
+ function toLanguageSelectOptions(languages) {
28
+ return languages.map(({ code, label }) => ({ label, value: code }));
29
+ }
30
+ const SUPPORTED_UI_LANGUAGE_OPTIONS = toLanguageSelectOptions(
31
+ SUPPORTED_UI_LANGUAGES
32
+ );
12
33
  function createState() {
13
34
  const discountCode = d(null);
14
35
  const products = d([]);
@@ -276,6 +297,13 @@ function createCustomerService(options) {
276
297
  },
277
298
  createPaymentMethod(params) {
278
299
  return options.adapter.createPaymentMethod(params);
300
+ },
301
+ async updateAttributes(attributes) {
302
+ var _a;
303
+ const merged = { ...(_a = current.value) == null ? void 0 : _a.attributes, ...attributes };
304
+ const result = await options.adapter.updateAttributes(merged);
305
+ if (result._tag === "Success") current.value = result.data;
306
+ return result;
279
307
  }
280
308
  };
281
309
  }
@@ -287,7 +315,8 @@ const PAYMENT_INSTRUMENT_TYPES = [
287
315
  "sepa_direct_debit",
288
316
  "blik",
289
317
  "mbway",
290
- "bacs"
318
+ "bacs",
319
+ "manual"
291
320
  ];
292
321
  const INSTRUMENT_TYPE_TO_NAME = {
293
322
  credit_card: null,
@@ -296,7 +325,8 @@ const INSTRUMENT_TYPE_TO_NAME = {
296
325
  sepa_direct_debit: "SEPA",
297
326
  mbway: "MB WAY",
298
327
  shop_pay: "Shop Pay",
299
- bacs: "Bacs"
328
+ bacs: "Bacs",
329
+ manual: "Shopify Checkout"
300
330
  };
301
331
  function getPaymentInstrumentName(instrument) {
302
332
  const name = INSTRUMENT_TYPE_TO_NAME[instrument.type] ?? null;
@@ -378,16 +408,23 @@ function serializeV1PaymentInstrument(raw) {
378
408
  return { type: "mbway", ...r2.phone != null ? { phone: r2.phone } : {} };
379
409
  case "bacs":
380
410
  return { type: "bacs", maskedNumber: r2.maskedNumber ?? "" };
411
+ case "manual":
412
+ return { type: "manual" };
381
413
  default:
382
414
  return null;
383
415
  }
384
416
  }
385
417
  function createMockCustomerAdapter() {
386
- const customer = { id: "customer-1", name: "Customer 1" };
418
+ let customer = { id: "customer-1", name: "Customer 1", attributes: {} };
387
419
  return {
388
420
  async getCurrent() {
389
421
  return { _tag: "Success", data: customer };
390
422
  },
423
+ async updateAttributes(attributes) {
424
+ await new Promise((resolve) => setTimeout(resolve, 300));
425
+ customer = { ...customer, attributes: { ...attributes } };
426
+ return { _tag: "Success", data: customer };
427
+ },
391
428
  async createPaymentMethod(params) {
392
429
  await new Promise((resolve) => setTimeout(resolve, 300));
393
430
  if (params.provider === "shopify") {
@@ -410,17 +447,41 @@ function createMockCustomerAdapter() {
410
447
  }
411
448
  };
412
449
  }
450
+ function fromV1Customer(body) {
451
+ return {
452
+ id: body.id,
453
+ name: body.displayName,
454
+ email: body.email ?? void 0,
455
+ attributes: Object.fromEntries(
456
+ body.customAttributes.map(({ key, value }) => [key, value])
457
+ )
458
+ };
459
+ }
413
460
  function createApiCustomerAdapter(fetcher) {
414
461
  return {
415
462
  async getCurrent() {
416
463
  try {
417
- const res = await fetcher("/customer/info");
464
+ const res = await fetcher("/customer/v1/customer");
465
+ if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
466
+ return {
467
+ _tag: "Success",
468
+ data: fromV1Customer(await res.json())
469
+ };
470
+ } catch (e) {
471
+ return { _tag: "Failure", error: e };
472
+ }
473
+ },
474
+ async updateAttributes(attributes) {
475
+ try {
476
+ const res = await fetcher("/customer/v1/customer", {
477
+ method: "PATCH",
478
+ headers: { "Content-Type": "application/json" },
479
+ body: JSON.stringify({ customAttributes: attributes })
480
+ });
418
481
  if (!res.ok) return { _tag: "Failure", error: `HTTP ${res.status}` };
419
- const data = await res.json();
420
- const name = [data.firstName, data.lastName].filter(Boolean).join(" ") || data.email || "";
421
482
  return {
422
483
  _tag: "Success",
423
- data: { id: data.id, name, email: data.email }
484
+ data: fromV1Customer(await res.json())
424
485
  };
425
486
  } catch (e) {
426
487
  return { _tag: "Failure", error: e };
@@ -578,6 +639,12 @@ function createApiLoginAdapter(fetcher, opts) {
578
639
  });
579
640
  }
580
641
  }
642
+ if (res.status === 401) {
643
+ const data = await res.json().catch(() => null);
644
+ if (data != null && typeof data === "object" && "message" in data && data.message === "Customer not found") {
645
+ throw new AuthenticationError(AuthenticationErrorReason.Missing);
646
+ }
647
+ }
581
648
  if (!res.ok) {
582
649
  throw new Error(`Unexpected response: ${res.statusText}`);
583
650
  }
@@ -851,7 +918,10 @@ function createMockOrderAdapter() {
851
918
  currencyCode: "USD",
852
919
  totalPrice: 89.97,
853
920
  financialStatus: "PAID",
921
+ paymentStatus: "paid",
922
+ paidAt: "2024-10-15T09:31:00Z",
854
923
  fulfillmentStatus: "FULFILLED",
924
+ subscriptionIds: ["subscription_1"],
855
925
  lines: [
856
926
  {
857
927
  id: "line_1",
@@ -876,7 +946,10 @@ function createMockOrderAdapter() {
876
946
  currencyCode: "USD",
877
947
  totalPrice: 45.99,
878
948
  financialStatus: "PAID",
949
+ paymentStatus: "paid",
950
+ paidAt: "2024-09-20T14:16:00Z",
879
951
  fulfillmentStatus: "PARTIALLY_FULFILLED",
952
+ subscriptionIds: [],
880
953
  lines: [
881
954
  {
882
955
  id: "line_2",
@@ -921,11 +994,28 @@ function mapOrder(order) {
921
994
  currencyCode: order.currencyCode,
922
995
  totalPrice: order.totalPrice,
923
996
  financialStatus: order.financialStatus,
997
+ // Defaulted rather than required so an older backend (which returns neither
998
+ // field) degrades to "settled, time unknown" instead of producing an Order
999
+ // with holes in it. Matches the endpoint's own fallback for an order it
1000
+ // never tracked.
1001
+ paymentStatus: order.paymentStatus ?? "paid",
1002
+ paidAt: order.paidAt ?? null,
924
1003
  fulfillmentStatus: order.fulfillmentStatus,
925
1004
  lines: order.items.map(mapOrderLine),
926
- customAttributes: order.customAttributes
1005
+ customAttributes: order.customAttributes,
1006
+ subscriptionIds: order.subscriptionIds ?? []
927
1007
  };
928
1008
  }
1009
+ function quoteSearchValue(value) {
1010
+ return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
1011
+ }
1012
+ function buildOrdersQuery(params) {
1013
+ const callerQuery = (params == null ? void 0 : params.query) != null && params.query.trim() !== "" ? params.query : null;
1014
+ const subscriptionTerm = (params == null ? void 0 : params.subscriptionId) != null ? `subscriptionId:${quoteSearchValue(params.subscriptionId)}` : null;
1015
+ if (callerQuery == null) return subscriptionTerm;
1016
+ if (subscriptionTerm == null) return callerQuery;
1017
+ return `(${callerQuery}) AND ${subscriptionTerm}`;
1018
+ }
929
1019
  function createApiOrderAdapter(fetcher) {
930
1020
  return {
931
1021
  async getOrders(params) {
@@ -933,9 +1023,10 @@ function createApiOrderAdapter(fetcher) {
933
1023
  if ((params == null ? void 0 : params.limit) != null) query.set("limit", String(params.limit));
934
1024
  if ((params == null ? void 0 : params.after) != null) query.set("after", params.after);
935
1025
  if ((params == null ? void 0 : params.before) != null) query.set("before", params.before);
936
- if ((params == null ? void 0 : params.query) != null) query.set("query", params.query);
937
1026
  if ((params == null ? void 0 : params.includeOneTime) != null) query.set("includeOneTime", String(params.includeOneTime));
938
1027
  if ((params == null ? void 0 : params.sort) != null) query.set("sort", params.sort);
1028
+ const searchQuery = buildOrdersQuery(params);
1029
+ if (searchQuery != null) query.set("query", searchQuery);
939
1030
  const qs = query.toString();
940
1031
  const res = await fetcher(`/customer/v1/orders${qs ? `?${qs}` : ""}`);
941
1032
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
@@ -1520,6 +1611,10 @@ function toNonEmptyAttributeArray(next) {
1520
1611
  ({ key, value }) => key !== "" && value !== ""
1521
1612
  );
1522
1613
  }
1614
+ const MOCK_INSTANT_ORDER_MIN_ITEMS = 1;
1615
+ const MOCK_INSTANT_ORDER_MAX_ITEMS = 20;
1616
+ const MOCK_INSTANT_ORDER_MIN_QUANTITY = 1;
1617
+ const MOCK_INSTANT_ORDER_MAX_QUANTITY = 10;
1523
1618
  function createMockSubscriptionAdapter(options = {}) {
1524
1619
  const richBase = (id, itemId, renewalId) => ({
1525
1620
  id,
@@ -1804,6 +1899,60 @@ function createMockSubscriptionAdapter(options = {}) {
1804
1899
  description: "Lightweight daily sun protection"
1805
1900
  }
1806
1901
  ];
1902
+ const mockInstantOrderImages = {
1903
+ "io-product-1": "https://images.unsplash.com/photo-1447933601403-0c6688de566e?w=200",
1904
+ "io-product-2": "https://images.unsplash.com/photo-1495474472287-4d71bcdd2085?w=200"
1905
+ };
1906
+ const mockInstantOrderProducts = [
1907
+ {
1908
+ id: "io-product-1",
1909
+ title: "Single-Origin Espresso",
1910
+ hasOnlyDefaultVariant: false,
1911
+ featuredImage: {
1912
+ url: mockInstantOrderImages["io-product-1"],
1913
+ altText: "Single-Origin Espresso"
1914
+ },
1915
+ variants: {
1916
+ items: [
1917
+ {
1918
+ id: "io-variant-1",
1919
+ title: "250 g",
1920
+ image: { url: mockInstantOrderImages["io-product-1"] },
1921
+ price: { amount: "12.00", currencyCode: "USD" },
1922
+ availableForSale: true
1923
+ },
1924
+ {
1925
+ id: "io-variant-2",
1926
+ title: "1 kg",
1927
+ image: { url: mockInstantOrderImages["io-product-1"] },
1928
+ price: { amount: "38.00", currencyCode: "USD" },
1929
+ availableForSale: true
1930
+ }
1931
+ ]
1932
+ }
1933
+ },
1934
+ {
1935
+ id: "io-product-2",
1936
+ title: "Ceramic Pour-Over Dripper",
1937
+ hasOnlyDefaultVariant: true,
1938
+ featuredImage: {
1939
+ url: mockInstantOrderImages["io-product-2"],
1940
+ altText: "Ceramic Pour-Over Dripper"
1941
+ },
1942
+ variants: {
1943
+ items: [
1944
+ {
1945
+ id: "io-variant-3",
1946
+ title: "Default Title",
1947
+ image: { url: mockInstantOrderImages["io-product-2"] },
1948
+ price: { amount: "24.50", currencyCode: "USD" },
1949
+ availableForSale: true
1950
+ }
1951
+ ]
1952
+ }
1953
+ }
1954
+ ];
1955
+ let placedInstantOrders = 0;
1807
1956
  return {
1808
1957
  async getById(id) {
1809
1958
  await new Promise((resolve) => setTimeout(resolve, 300));
@@ -2210,6 +2359,75 @@ function createMockSubscriptionAdapter(options = {}) {
2210
2359
  async addUpsellProduct() {
2211
2360
  await new Promise((resolve) => setTimeout(resolve, 400));
2212
2361
  return { _tag: "Success", data: void 0 };
2362
+ },
2363
+ async getInstantOrderProducts({
2364
+ query
2365
+ }) {
2366
+ await new Promise((resolve) => setTimeout(resolve, 300));
2367
+ const term = query == null ? void 0 : query.trim().toLowerCase();
2368
+ const products = term ? mockInstantOrderProducts.filter(
2369
+ (product) => product.title.toLowerCase().includes(term)
2370
+ ) : [...mockInstantOrderProducts];
2371
+ return {
2372
+ _tag: "Success",
2373
+ data: {
2374
+ // Deliberately independent of `query`, mirroring the endpoint: a search
2375
+ // that matches nothing must not read as "the feature is unavailable"
2376
+ // and make a consumer hide its entry point mid-interaction.
2377
+ available: mockInstantOrderProducts.length > 0,
2378
+ requiresCheckout: false,
2379
+ products,
2380
+ hasNextPage: false,
2381
+ endCursor: null
2382
+ }
2383
+ };
2384
+ },
2385
+ async placeInstantOrder({
2386
+ items
2387
+ }) {
2388
+ var _a;
2389
+ await new Promise((resolve) => setTimeout(resolve, 400));
2390
+ const malformed = items.length < MOCK_INSTANT_ORDER_MIN_ITEMS || items.length > MOCK_INSTANT_ORDER_MAX_ITEMS || items.some(
2391
+ (item) => !Number.isInteger(item.quantity) || item.quantity < MOCK_INSTANT_ORDER_MIN_QUANTITY || item.quantity > MOCK_INSTANT_ORDER_MAX_QUANTITY
2392
+ );
2393
+ if (malformed) {
2394
+ return {
2395
+ _tag: "Failure",
2396
+ error: {
2397
+ kind: "INVALID_REQUEST",
2398
+ message: "Malformed item list"
2399
+ }
2400
+ };
2401
+ }
2402
+ const variants = mockInstantOrderProducts.flatMap(
2403
+ (product) => product.variants.items
2404
+ );
2405
+ const unknown = items.find(
2406
+ (item) => !variants.some((variant) => variant.id === item.variantId)
2407
+ );
2408
+ if (unknown) {
2409
+ return {
2410
+ _tag: "Failure",
2411
+ error: {
2412
+ kind: "INVALID_REQUEST",
2413
+ message: `Unknown variant ${unknown.variantId}`
2414
+ }
2415
+ };
2416
+ }
2417
+ const total = items.reduce((sum, item) => {
2418
+ const variant = variants.find((v3) => v3.id === item.variantId);
2419
+ return sum + Number((variant == null ? void 0 : variant.price.amount) ?? 0) * item.quantity;
2420
+ }, 0);
2421
+ placedInstantOrders += 1;
2422
+ return {
2423
+ _tag: "Success",
2424
+ data: {
2425
+ orderId: `io-order-${placedInstantOrders}`,
2426
+ totalPrice: total.toFixed(2),
2427
+ currencyCode: ((_a = variants[0]) == null ? void 0 : _a.price.currencyCode) ?? "USD",
2428
+ status: "paid"
2429
+ }
2430
+ };
2213
2431
  }
2214
2432
  };
2215
2433
  }
@@ -2352,6 +2570,51 @@ async function readErrorBody(res) {
2352
2570
  return {};
2353
2571
  }
2354
2572
  }
2573
+ async function readErrorMessage(res) {
2574
+ const body = await readErrorBody(res);
2575
+ return typeof body.message === "string" ? body.message : void 0;
2576
+ }
2577
+ const PLACE_INSTANT_ORDER_ERROR_KINDS = {
2578
+ 400: "INVALID_REQUEST",
2579
+ 403: "REFUSED",
2580
+ 409: "DUPLICATE_REQUEST",
2581
+ 429: "RATE_LIMITED"
2582
+ };
2583
+ function mapInstantOrderProducts(body) {
2584
+ const currencyCode = body.currencyCode;
2585
+ const products = (body.products ?? []).map(
2586
+ (product) => {
2587
+ const image = product.featuredImage ? {
2588
+ url: product.featuredImage.url,
2589
+ ...product.featuredImage.altText != null ? { altText: product.featuredImage.altText } : {}
2590
+ } : null;
2591
+ return {
2592
+ id: product.productId,
2593
+ title: product.title,
2594
+ // Shopify's own flag, not a count of the variants this response happens
2595
+ // to carry — the two differ whenever some are filtered.
2596
+ hasOnlyDefaultVariant: product.hasOnlyDefaultVariant,
2597
+ featuredImage: image,
2598
+ variants: {
2599
+ items: (product.variants ?? []).map((variant) => ({
2600
+ id: variant.id,
2601
+ title: variant.title,
2602
+ image,
2603
+ price: { amount: variant.price, currencyCode },
2604
+ availableForSale: variant.availableForSale
2605
+ }))
2606
+ }
2607
+ };
2608
+ }
2609
+ );
2610
+ return {
2611
+ available: body.available,
2612
+ requiresCheckout: body.requiresCheckout,
2613
+ products,
2614
+ hasNextPage: body.hasNextPage,
2615
+ endCursor: body.endCursor ?? null
2616
+ };
2617
+ }
2355
2618
  function createApiSubscriptionAdapter(fetcher) {
2356
2619
  const adapter = {
2357
2620
  async search(options) {
@@ -2882,6 +3145,108 @@ function createApiSubscriptionAdapter(fetcher) {
2882
3145
  }
2883
3146
  },
2884
3147
  // Legacy endpoint — no /customer/v1 equivalent exists yet.
3148
+ async getInstantOrderProducts({ subscriptionId, query, after, limit }) {
3149
+ try {
3150
+ const params = new URLSearchParams();
3151
+ if (query) params.set("q", query);
3152
+ if (after != null) params.set("after", after);
3153
+ if (limit != null) params.set("limit", String(limit));
3154
+ const search = params.toString() ? `?${params.toString()}` : "";
3155
+ const res = await fetcher(
3156
+ `/customer/subscriptions/${subscriptionId}/instant-orders/products${search}`
3157
+ );
3158
+ if (!res.ok) {
3159
+ if (res.status === 404) {
3160
+ return { _tag: "Failure", error: { kind: "SUBSCRIPTION_NOT_FOUND" } };
3161
+ }
3162
+ const message = await readErrorMessage(res);
3163
+ return {
3164
+ _tag: "Failure",
3165
+ error: { kind: "UNKNOWN", message: message ?? `HTTP ${res.status}` }
3166
+ };
3167
+ }
3168
+ const body = await res.json().catch(() => null);
3169
+ if (body == null) {
3170
+ return {
3171
+ _tag: "Failure",
3172
+ error: { kind: "UNKNOWN", message: "Malformed response" }
3173
+ };
3174
+ }
3175
+ return { _tag: "Success", data: mapInstantOrderProducts(body) };
3176
+ } catch (e) {
3177
+ return {
3178
+ _tag: "Failure",
3179
+ error: {
3180
+ kind: "NETWORK",
3181
+ message: e instanceof Error ? e.message : void 0
3182
+ }
3183
+ };
3184
+ }
3185
+ },
3186
+ // Legacy endpoint — no /customer/v1 equivalent exists yet.
3187
+ //
3188
+ // No X-Last-Updated-At header: an instant order never mutates the
3189
+ // subscription, so the optimistic-lock preValidation (which only fires when
3190
+ // the header is present) has nothing to guard here.
3191
+ async placeInstantOrder({ subscriptionId, requestKey, items }) {
3192
+ try {
3193
+ const res = await fetcher(
3194
+ `/customer/subscriptions/${subscriptionId}/instant-orders`,
3195
+ {
3196
+ method: "POST",
3197
+ headers: { "Content-Type": "application/json" },
3198
+ body: JSON.stringify({
3199
+ requestKey,
3200
+ // Narrowed to what the endpoint accepts: the rest of
3201
+ // PostableSubscriptionItem describes a subscription line (its
3202
+ // billing policy, its cycle limit), and an instant order is not one.
3203
+ items: items.map(({ variantId, quantity }) => ({
3204
+ variantId,
3205
+ quantity
3206
+ }))
3207
+ })
3208
+ }
3209
+ );
3210
+ if (!res.ok) {
3211
+ if (res.status === 404) {
3212
+ return { _tag: "Failure", error: { kind: "SUBSCRIPTION_NOT_FOUND" } };
3213
+ }
3214
+ const message = await readErrorMessage(res);
3215
+ const kind = PLACE_INSTANT_ORDER_ERROR_KINDS[res.status] ?? "UNKNOWN";
3216
+ return {
3217
+ _tag: "Failure",
3218
+ error: { kind, message: message ?? `HTTP ${res.status}` }
3219
+ };
3220
+ }
3221
+ const body = await res.json().catch(() => null);
3222
+ if (body == null) {
3223
+ return {
3224
+ _tag: "Failure",
3225
+ error: { kind: "UNKNOWN", message: "Malformed response" }
3226
+ };
3227
+ }
3228
+ return {
3229
+ _tag: "Success",
3230
+ data: {
3231
+ orderId: body.orderId,
3232
+ totalPrice: body.totalPrice ?? null,
3233
+ currencyCode: body.currencyCode,
3234
+ status: body.status,
3235
+ ...body.paymentUrl != null ? { paymentUrl: body.paymentUrl } : {},
3236
+ ...body.reason != null ? { reason: body.reason } : {}
3237
+ }
3238
+ };
3239
+ } catch (e) {
3240
+ return {
3241
+ _tag: "Failure",
3242
+ error: {
3243
+ kind: "NETWORK",
3244
+ message: e instanceof Error ? e.message : void 0
3245
+ }
3246
+ };
3247
+ }
3248
+ },
3249
+ // Legacy endpoint — no /customer/v1 equivalent exists yet.
2885
3250
  async getLastOrder({ subscriptionId }) {
2886
3251
  try {
2887
3252
  const res = await fetcher(
@@ -3816,10 +4181,98 @@ function createSubscriptionService(adapter) {
3816
4181
  getAvailablePlans: (subscriptionId) => adapter.getAvailablePlans({ subscriptionId }),
3817
4182
  getDefaultSubscriptionId: () => adapter.getDefaultSubscriptionId(),
3818
4183
  getUpsellProducts: (params) => adapter.getUpsellProducts ? adapter.getUpsellProducts(params) : Promise.resolve({ _tag: "Success", data: [] }),
3819
- addUpsellProduct: (params) => adapter.addUpsellProduct ? adapter.addUpsellProduct(params) : Promise.resolve({ _tag: "Success", data: void 0 })
4184
+ addUpsellProduct: (params) => adapter.addUpsellProduct ? adapter.addUpsellProduct(params) : Promise.resolve({ _tag: "Success", data: void 0 }),
4185
+ getInstantOrderProducts: (subscriptionId, params) => adapter.getInstantOrderProducts ? adapter.getInstantOrderProducts({ subscriptionId, ...params }) : Promise.resolve({
4186
+ _tag: "Success",
4187
+ data: {
4188
+ available: false,
4189
+ requiresCheckout: false,
4190
+ products: [],
4191
+ hasNextPage: false,
4192
+ endCursor: null
4193
+ }
4194
+ }),
4195
+ placeInstantOrder: (subscriptionId, requestKey, items) => adapter.placeInstantOrder ? adapter.placeInstantOrder({ subscriptionId, requestKey, items }) : (
4196
+ // Distinguishable from every server-side refusal, so a consumer can
4197
+ // treat "this host cannot do instant orders" as a missing capability
4198
+ // rather than telling the customer their order was declined.
4199
+ Promise.resolve({
4200
+ _tag: "Failure",
4201
+ error: { kind: "UNSUPPORTED" }
4202
+ })
4203
+ )
3820
4204
  };
3821
4205
  return service;
3822
4206
  }
4207
+ function isOneTimeLine(line) {
4208
+ return line.isOneTime === true || line.recurringCycleLimit === 1;
4209
+ }
4210
+ function hasValue(value) {
4211
+ return typeof value === "string" && value.length > 0;
4212
+ }
4213
+ function hasIdentity(line) {
4214
+ return hasValue(line.subscriptionId) && hasValue(line.subscriptionLineId);
4215
+ }
4216
+ function isSameLine(a2, b3) {
4217
+ return hasValue(a2.subscriptionLineId) && a2.subscriptionLineId === b3.subscriptionLineId;
4218
+ }
4219
+ function belongsToSameSubscription(a2, b3) {
4220
+ return hasValue(a2.subscriptionId) && a2.subscriptionId === b3.subscriptionId;
4221
+ }
4222
+ function parentLineFor(child, lines) {
4223
+ if (!hasValue(child.parentLineId)) {
4224
+ return void 0;
4225
+ }
4226
+ return lines.find(
4227
+ (candidate) => belongsToSameSubscription(candidate, child) && (candidate.subscriptionLineId === child.parentLineId || candidate.id === child.parentLineId)
4228
+ );
4229
+ }
4230
+ function productKey(line, lines) {
4231
+ if (hasValue(line.parentProductId)) {
4232
+ return `product:${line.parentProductId}`;
4233
+ }
4234
+ if (hasValue(line.parentLineId)) {
4235
+ const parent = parentLineFor(line, lines);
4236
+ if (hasValue(parent == null ? void 0 : parent.productId)) {
4237
+ return `product:${parent.productId}`;
4238
+ }
4239
+ return `line:${line.parentLineId}`;
4240
+ }
4241
+ const lineIdentity = hasValue(line.subscriptionLineId) ? line.subscriptionLineId : line.id;
4242
+ if (!hasValue(lineIdentity)) {
4243
+ return null;
4244
+ }
4245
+ const isReferencedAsParent = lines.some(
4246
+ (candidate) => belongsToSameSubscription(candidate, line) && (hasValue(line.productId) && candidate.parentProductId === line.productId || hasValue(candidate.parentLineId) && (candidate.parentLineId === line.subscriptionLineId || candidate.parentLineId === line.id))
4247
+ );
4248
+ if (isReferencedAsParent && hasValue(line.productId)) {
4249
+ return `product:${line.productId}`;
4250
+ }
4251
+ return `line:${lineIdentity}`;
4252
+ }
4253
+ function remainingRecurringProductKeys(line, lines) {
4254
+ const removedProductKey = productKey(line, lines);
4255
+ return new Set(
4256
+ lines.filter(
4257
+ (item) => belongsToSameSubscription(item, line) && !isOneTimeLine(item)
4258
+ ).map((item) => productKey(item, lines)).filter((key) => key != null && key !== removedProductKey)
4259
+ );
4260
+ }
4261
+ function canRemoveLine(line, lines) {
4262
+ if (!hasIdentity(line)) {
4263
+ return false;
4264
+ }
4265
+ const targetExists = lines.some(
4266
+ (item) => belongsToSameSubscription(item, line) && isSameLine(item, line)
4267
+ );
4268
+ if (!targetExists) {
4269
+ return false;
4270
+ }
4271
+ if (isOneTimeLine(line)) {
4272
+ return true;
4273
+ }
4274
+ return remainingRecurringProductKeys(line, lines).size > 0;
4275
+ }
3823
4276
  function getDefaultBlockForAction(actionType, actionConfig, blockId) {
3824
4277
  const blockName = `action:${actionType}`;
3825
4278
  const definedBlocks = getDefinedBlocks();
@@ -4798,6 +5251,8 @@ export {
4798
5251
  PAYMENT_INSTRUMENT_TYPES,
4799
5252
  ProductServiceContext,
4800
5253
  RouterServiceContext,
5254
+ SUPPORTED_UI_LANGUAGES,
5255
+ SUPPORTED_UI_LANGUAGE_OPTIONS,
4801
5256
  SchedulesServiceContext,
4802
5257
  S as SelectedBlockContext,
4803
5258
  SettingsServiceContext,
@@ -4814,6 +5269,8 @@ export {
4814
5269
  c as backfillInstanceDefaults,
4815
5270
  b2 as buildGridLayout,
4816
5271
  j as buildLayoutCss,
5272
+ buildOrdersQuery,
5273
+ canRemoveLine,
4817
5274
  canvasMode,
4818
5275
  clampRect,
4819
5276
  k2 as classifyRowDividers,
@@ -4867,6 +5324,7 @@ export {
4867
5324
  emitLayoutSetRects,
4868
5325
  q2 as expandFillLayoutDocument,
4869
5326
  findFreeRect,
5327
+ fromV1Customer,
4870
5328
  getActiveSurfaceContext,
4871
5329
  getDefaultBlockForAction,
4872
5330
  getDefinedBlocks,
@@ -4884,6 +5342,7 @@ export {
4884
5342
  insertRectRelative,
4885
5343
  H as isEditorPreview,
4886
5344
  isFilterQuery,
5345
+ isOneTimeLine,
4887
5346
  isStaleSubscriptionMessage,
4888
5347
  b as isStubDefinition,
4889
5348
  t2 as layoutScopeSelector,
@@ -4910,6 +5369,7 @@ export {
4910
5369
  l as setLayoutSystemEnabled,
4911
5370
  d as signal,
4912
5371
  subscribeContext,
5372
+ toLanguageSelectOptions,
4913
5373
  L as untracked,
4914
5374
  waitForDevBlocksUrl,
4915
5375
  w2 as withWrapper