@01.software/sdk 0.31.0 → 0.33.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +128 -47
  2. package/dist/analytics/react.cjs.map +1 -1
  3. package/dist/analytics/react.js.map +1 -1
  4. package/dist/analytics.cjs.map +1 -1
  5. package/dist/analytics.js.map +1 -1
  6. package/dist/client.cjs +41 -48
  7. package/dist/client.cjs.map +1 -1
  8. package/dist/client.d.cts +6 -6
  9. package/dist/client.d.ts +6 -6
  10. package/dist/client.js +41 -48
  11. package/dist/client.js.map +1 -1
  12. package/dist/{collection-client-ByzY3hWK.d.ts → collection-client-B6SlhzIP.d.ts} +3 -3
  13. package/dist/{collection-client-DFXXz0vk.d.cts → collection-client-De6eKW1J.d.cts} +3 -3
  14. package/dist/{const-AytzliEu.d.cts → const-DwmSDeWq.d.ts} +2 -2
  15. package/dist/{const-BGCP-OJL.d.ts → const-sPR2IkCe.d.cts} +2 -2
  16. package/dist/index.cjs +310 -133
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.cts +7 -7
  19. package/dist/index.d.ts +7 -7
  20. package/dist/index.js +310 -133
  21. package/dist/index.js.map +1 -1
  22. package/dist/{payload-types-Wa4-eC6x.d.cts → payload-types-dkeQyrDC.d.cts} +1572 -1440
  23. package/dist/{payload-types-Wa4-eC6x.d.ts → payload-types-dkeQyrDC.d.ts} +1572 -1440
  24. package/dist/query.cjs.map +1 -1
  25. package/dist/query.d.cts +20 -20
  26. package/dist/query.d.ts +20 -20
  27. package/dist/query.js.map +1 -1
  28. package/dist/realtime.cjs.map +1 -1
  29. package/dist/realtime.d.cts +2 -2
  30. package/dist/realtime.d.ts +2 -2
  31. package/dist/realtime.js.map +1 -1
  32. package/dist/server.cjs +70 -6
  33. package/dist/server.cjs.map +1 -1
  34. package/dist/server.d.cts +100 -8
  35. package/dist/server.d.ts +100 -8
  36. package/dist/server.js +70 -6
  37. package/dist/server.js.map +1 -1
  38. package/dist/{types-CmLG-7RL.d.cts → types-B3YT092I.d.cts} +1 -1
  39. package/dist/{types-BX2mqDf6.d.ts → types-BHh0YLmq.d.ts} +63 -13
  40. package/dist/{types-DChFjQGz.d.cts → types-BZKxss8Y.d.cts} +63 -13
  41. package/dist/{types-CVA10VC-.d.ts → types-Cel_4L9t.d.ts} +1 -1
  42. package/dist/ui/form.d.cts +1 -1
  43. package/dist/ui/form.d.ts +1 -1
  44. package/dist/ui/video.d.cts +1 -1
  45. package/dist/ui/video.d.ts +1 -1
  46. package/dist/webhook.cjs +142 -1
  47. package/dist/webhook.cjs.map +1 -1
  48. package/dist/webhook.d.cts +149 -5
  49. package/dist/webhook.d.ts +149 -5
  50. package/dist/webhook.js +142 -1
  51. package/dist/webhook.js.map +1 -1
  52. package/package.json +3 -3
@@ -1,7 +1,7 @@
1
- import { D as DebugConfig, R as RetryConfig, A as ApiQueryOptions, P as PayloadFindResponse, g as PayloadMutationResponse } from './types-BX2mqDf6.js';
1
+ import { D as DebugConfig, R as RetryConfig, A as ApiQueryOptions, P as PayloadFindResponse, g as PayloadMutationResponse } from './types-BHh0YLmq.js';
2
2
  import { GenerateMetadataOptions, Metadata } from './metadata.js';
3
- import { c as ServerCollection, P as PublicCollection } from './const-BGCP-OJL.js';
4
- import { C as CollectionType } from './types-CVA10VC-.js';
3
+ import { c as ServerCollection, P as PublicCollection } from './const-DwmSDeWq.js';
4
+ import { C as CollectionType } from './types-Cel_4L9t.js';
5
5
 
6
6
  interface FetchOptions extends RequestInit {
7
7
  apiUrl?: string;
@@ -1,7 +1,7 @@
1
- import { D as DebugConfig, R as RetryConfig, A as ApiQueryOptions, P as PayloadFindResponse, g as PayloadMutationResponse } from './types-DChFjQGz.cjs';
1
+ import { D as DebugConfig, R as RetryConfig, A as ApiQueryOptions, P as PayloadFindResponse, g as PayloadMutationResponse } from './types-BZKxss8Y.cjs';
2
2
  import { GenerateMetadataOptions, Metadata } from './metadata.cjs';
3
- import { c as ServerCollection, P as PublicCollection } from './const-AytzliEu.cjs';
4
- import { C as CollectionType } from './types-CmLG-7RL.cjs';
3
+ import { c as ServerCollection, P as PublicCollection } from './const-sPR2IkCe.cjs';
4
+ import { C as CollectionType } from './types-B3YT092I.cjs';
5
5
 
6
6
  interface FetchOptions extends RequestInit {
7
7
  apiUrl?: string;
@@ -1,4 +1,4 @@
1
- import { e as Config } from './payload-types-Wa4-eC6x.cjs';
1
+ import { e as Config } from './payload-types-dkeQyrDC.js';
2
2
 
3
3
  /**
4
4
  * Collection type derived from Payload Config.
@@ -9,7 +9,7 @@ type Collection = keyof Config['collections'];
9
9
  * Internal collections that should not be exposed via SDK.
10
10
  * Includes Payload system collections and admin-only collections.
11
11
  */
12
- declare const INTERNAL_COLLECTIONS: readonly ["users", "payload-kv", "payload-locked-documents", "payload-preferences", "payload-migrations", "payload-folders", "field-configs", "system-media", "track-assets", "audiences", "email-logs", "api-usage", "tenant-analytics-daily", "tenant-web-analytics-config", "analytics-event-schemas", "subscriptions", "billing-history", "inventory-reservations", "product-collection-items", "order-status-logs", "api-keys", "personal-access-tokens", "tenant-entitlements", "tenant-purge-jobs", "direct-upload-sessions", "webhook-events", "webhook-deliveries", "audit-logs", "plans", "webhooks", "event-registrations"];
12
+ declare const INTERNAL_COLLECTIONS: readonly ["users", "payload-kv", "payload-locked-documents", "payload-preferences", "payload-migrations", "payload-folders", "field-configs", "system-media", "track-assets", "audiences", "email-logs", "api-usage", "tenant-analytics-daily", "tenant-web-analytics-config", "analytics-event-schemas", "subscriptions", "billing-history", "inventory-reservations", "commerce-notification-intents", "product-collection-items", "order-status-logs", "api-keys", "personal-access-tokens", "tenant-entitlements", "tenant-purge-jobs", "direct-upload-sessions", "webhook-events", "webhook-deliveries", "audit-logs", "plans", "webhooks", "event-registrations"];
13
13
  /**
14
14
  * Array of all public collection names for runtime use (e.g., Zod enum validation).
15
15
  * This is the single source of truth for which collections are publicly accessible via SDK.
@@ -1,4 +1,4 @@
1
- import { e as Config } from './payload-types-Wa4-eC6x.js';
1
+ import { e as Config } from './payload-types-dkeQyrDC.cjs';
2
2
 
3
3
  /**
4
4
  * Collection type derived from Payload Config.
@@ -9,7 +9,7 @@ type Collection = keyof Config['collections'];
9
9
  * Internal collections that should not be exposed via SDK.
10
10
  * Includes Payload system collections and admin-only collections.
11
11
  */
12
- declare const INTERNAL_COLLECTIONS: readonly ["users", "payload-kv", "payload-locked-documents", "payload-preferences", "payload-migrations", "payload-folders", "field-configs", "system-media", "track-assets", "audiences", "email-logs", "api-usage", "tenant-analytics-daily", "tenant-web-analytics-config", "analytics-event-schemas", "subscriptions", "billing-history", "inventory-reservations", "product-collection-items", "order-status-logs", "api-keys", "personal-access-tokens", "tenant-entitlements", "tenant-purge-jobs", "direct-upload-sessions", "webhook-events", "webhook-deliveries", "audit-logs", "plans", "webhooks", "event-registrations"];
12
+ declare const INTERNAL_COLLECTIONS: readonly ["users", "payload-kv", "payload-locked-documents", "payload-preferences", "payload-migrations", "payload-folders", "field-configs", "system-media", "track-assets", "audiences", "email-logs", "api-usage", "tenant-analytics-daily", "tenant-web-analytics-config", "analytics-event-schemas", "subscriptions", "billing-history", "inventory-reservations", "commerce-notification-intents", "product-collection-items", "order-status-logs", "api-keys", "personal-access-tokens", "tenant-entitlements", "tenant-purge-jobs", "direct-upload-sessions", "webhook-events", "webhook-deliveries", "audit-logs", "plans", "webhooks", "event-registrations"];
13
13
  /**
14
14
  * Array of all public collection names for runtime use (e.g., Zod enum validation).
15
15
  * This is the single source of truth for which collections are publicly accessible via SDK.
package/dist/index.cjs CHANGED
@@ -24,6 +24,9 @@ __export(src_exports, {
24
24
  AuthError: () => AuthError,
25
25
  BaseApi: () => BaseApi,
26
26
  COLLECTIONS: () => COLLECTIONS,
27
+ COMMERCE_NOTIFICATION_EVENTS: () => COMMERCE_NOTIFICATION_EVENTS,
28
+ COMMERCE_NOTIFICATION_EVENT_TYPE: () => COMMERCE_NOTIFICATION_EVENT_TYPE,
29
+ COMMERCE_NOTIFICATION_OPERATION: () => COMMERCE_NOTIFICATION_OPERATION,
27
30
  CUSTOMER_PASSWORD_RESET_OPERATION: () => CUSTOMER_PASSWORD_RESET_OPERATION,
28
31
  CartApi: () => CartApi,
29
32
  CommerceClient: () => CommerceClient,
@@ -38,6 +41,7 @@ __export(src_exports, {
38
41
  INTERNAL_COLLECTIONS: () => INTERNAL_COLLECTIONS,
39
42
  NetworkError: () => NetworkError,
40
43
  NotFoundError: () => NotFoundError,
44
+ ORDER_CHANGED_EVENT_TYPE: () => ORDER_CHANGED_EVENT_TYPE,
41
45
  OrderApi: () => OrderApi,
42
46
  PermissionError: () => PermissionError,
43
47
  ProductApi: () => ProductApi,
@@ -61,6 +65,7 @@ __export(src_exports, {
61
65
  createAnalytics: () => createAnalytics,
62
66
  createAuthError: () => createAuthError,
63
67
  createClient: () => createClient2,
68
+ createCommerceEmailWebhookHandler: () => createCommerceEmailWebhookHandler,
64
69
  createConflictError: () => createConflictError,
65
70
  createCustomerAuthWebhookHandler: () => createCustomerAuthWebhookHandler,
66
71
  createNotFoundError: () => createNotFoundError,
@@ -68,9 +73,11 @@ __export(src_exports, {
68
73
  createProductSelectionCodec: () => createProductSelectionCodec,
69
74
  createRateLimitError: () => createRateLimitError,
70
75
  createTypedWebhookHandler: () => createTypedWebhookHandler,
76
+ defineCommerceEmailConfig: () => defineCommerceEmailConfig,
71
77
  formatOrderName: () => formatOrderName,
72
78
  generateOrderNumber: () => generateOrderNumber,
73
79
  getAvailableOptionValues: () => getAvailableOptionValues,
80
+ getCommerceNotificationIdempotencyKey: () => getCommerceNotificationIdempotencyKey,
74
81
  getImageLqip: () => getImageLqip,
75
82
  getImagePalette: () => getImagePalette,
76
83
  getImagePlaceholderStyle: () => getImagePlaceholderStyle,
@@ -86,12 +93,14 @@ __export(src_exports, {
86
93
  handleWebhook: () => handleWebhook,
87
94
  isApiError: () => isApiError,
88
95
  isAuthError: () => isAuthError,
96
+ isCommerceNotificationWebhookEvent: () => isCommerceNotificationWebhookEvent,
89
97
  isConfigError: () => isConfigError,
90
98
  isConflictError: () => isConflictError,
91
99
  isCustomerPasswordResetWebhookEvent: () => isCustomerPasswordResetWebhookEvent,
92
100
  isGoneError: () => isGoneError,
93
101
  isNetworkError: () => isNetworkError,
94
102
  isNotFoundError: () => isNotFoundError,
103
+ isOrderChangedWebhookEvent: () => isOrderChangedWebhookEvent,
95
104
  isPermissionError: () => isPermissionError,
96
105
  isRateLimitError: () => isRateLimitError,
97
106
  isSDKError: () => isSDKError,
@@ -100,6 +109,8 @@ __export(src_exports, {
100
109
  isUsageLimitError: () => isUsageLimitError,
101
110
  isValidWebhookEvent: () => isValidWebhookEvent,
102
111
  isValidationError: () => isValidationError,
112
+ isWebhookCollection: () => isWebhookCollection,
113
+ isWebhookOperation: () => isWebhookOperation,
103
114
  normalizeProductSelection: () => normalizeProductSelection,
104
115
  normalizeProductSelectionFromMatrix: () => normalizeProductSelectionFromMatrix,
105
116
  normalizeSelectedValueIds: () => normalizeSelectedValueIds,
@@ -1340,7 +1351,7 @@ var CustomerAuth = class {
1340
1351
  );
1341
1352
  return data.customer ?? null;
1342
1353
  } catch (error) {
1343
- if (error instanceof ApiError && error.status === 401) {
1354
+ if (error instanceof SDKError && error.status === 401) {
1344
1355
  this.setToken(null);
1345
1356
  return null;
1346
1357
  }
@@ -1435,51 +1446,15 @@ var CustomerAuth = class {
1435
1446
  if (!headers.has("Content-Type") && init.body) {
1436
1447
  headers.set("Content-Type", "application/json");
1437
1448
  }
1438
- const controller = new AbortController();
1439
- const timeoutId = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT2);
1440
- let res;
1441
- try {
1442
- res = await fetch(`${this.baseUrl}${path}`, {
1443
- ...init,
1444
- headers,
1445
- signal: controller.signal
1446
- });
1447
- } catch (error) {
1448
- clearTimeout(timeoutId);
1449
- if (error instanceof Error && error.name === "AbortError") {
1450
- throw new TimeoutError(`Request timed out after ${DEFAULT_TIMEOUT2}ms`, {
1451
- url: path,
1452
- timeout: DEFAULT_TIMEOUT2
1453
- });
1454
- }
1455
- throw new NetworkError(
1456
- error instanceof Error ? error.message : "Network request failed",
1457
- void 0,
1458
- { url: path },
1459
- "Network connection failed.",
1460
- "Please check your internet connection and try again."
1461
- );
1462
- }
1463
- clearTimeout(timeoutId);
1464
- if (!res.ok) {
1465
- const body = await res.json().catch(() => ({}));
1466
- throw new ApiError(
1467
- body.error || `HTTP ${res.status}`,
1468
- res.status,
1469
- body.details,
1470
- body.error
1471
- );
1472
- }
1473
- try {
1474
- return await res.json();
1475
- } catch {
1476
- throw new ApiError(
1477
- "Invalid JSON response from server",
1478
- res.status,
1479
- void 0,
1480
- "INVALID_RESPONSE"
1481
- );
1482
- }
1449
+ const response = await httpFetch(path, {
1450
+ ...init,
1451
+ apiUrl: this.baseUrl,
1452
+ publishableKey: this.publishableKey,
1453
+ headers,
1454
+ timeout: DEFAULT_TIMEOUT2,
1455
+ retry: { maxRetries: 0 }
1456
+ });
1457
+ return parseApiResponse(response, path);
1483
1458
  }
1484
1459
  };
1485
1460
 
@@ -1560,6 +1535,117 @@ var CartApi = class {
1560
1535
  }
1561
1536
  };
1562
1537
 
1538
+ // src/core/api/base-api.ts
1539
+ var BaseApi = class {
1540
+ constructor(apiName, options) {
1541
+ if (!options.secretKey) {
1542
+ throw createConfigError(`secretKey is required for ${apiName}.`);
1543
+ }
1544
+ this.publishableKey = requirePublishableKeyForSecret(
1545
+ apiName,
1546
+ options.publishableKey,
1547
+ options.secretKey
1548
+ );
1549
+ this.secretKey = options.secretKey;
1550
+ this.apiUrl = options.apiUrl;
1551
+ this.onRequestId = options.onRequestId;
1552
+ }
1553
+ async request(endpoint, body, options) {
1554
+ const method = options?.method ?? "POST";
1555
+ try {
1556
+ const response = await httpFetch(endpoint, {
1557
+ method,
1558
+ apiUrl: this.apiUrl,
1559
+ publishableKey: this.publishableKey,
1560
+ secretKey: this.secretKey,
1561
+ ...body !== void 0 && { body: JSON.stringify(body) },
1562
+ ...options?.headers && { headers: options.headers }
1563
+ });
1564
+ this.onRequestId?.(response.headers.get("x-request-id") ?? null);
1565
+ return parseApiResponse(response, endpoint);
1566
+ } catch (err) {
1567
+ const id = err instanceof SDKError ? err.requestId ?? null : null;
1568
+ this.onRequestId?.(id);
1569
+ throw err;
1570
+ }
1571
+ }
1572
+ };
1573
+
1574
+ // src/core/api/product-api.ts
1575
+ var PRODUCT_DETAIL_UNAVAILABLE_REASONS = /* @__PURE__ */ new Set([
1576
+ "not_found",
1577
+ "not_published",
1578
+ "feature_disabled"
1579
+ ]);
1580
+ function isRecord(value) {
1581
+ return typeof value === "object" && value !== null;
1582
+ }
1583
+ function readProductDetailUnavailableReason(value) {
1584
+ if (!isRecord(value)) return void 0;
1585
+ const directReason = value.reason ?? value.code;
1586
+ if (typeof directReason === "string" && PRODUCT_DETAIL_UNAVAILABLE_REASONS.has(directReason)) {
1587
+ return directReason;
1588
+ }
1589
+ return readProductDetailUnavailableReason(value.body);
1590
+ }
1591
+ function productDetailResultFromError(error) {
1592
+ if (!(error instanceof SDKError) || error.status !== 404) return void 0;
1593
+ const reason = readProductDetailUnavailableReason(error.details);
1594
+ if (!reason) return void 0;
1595
+ return { found: false, reason };
1596
+ }
1597
+ var ProductApi = class extends BaseApi {
1598
+ constructor(options) {
1599
+ super("ProductApi", options);
1600
+ }
1601
+ /**
1602
+ * Check point-in-time stock availability for one or more product variants.
1603
+ * Results reflect available stock at the moment of the call and are not guaranteed
1604
+ * to remain available by the time an order is placed.
1605
+ */
1606
+ stockCheck(params) {
1607
+ return this.request("/api/products/stock-check", params);
1608
+ }
1609
+ listingGroups(params) {
1610
+ return this.request(
1611
+ "/api/products/listing-groups",
1612
+ params
1613
+ );
1614
+ }
1615
+ /**
1616
+ * Fetch full product detail by slug or id.
1617
+ * Returns a discriminated result so storefronts can distinguish missing,
1618
+ * unpublished, and feature-disabled products.
1619
+ *
1620
+ * Only product-detail 404 responses carrying one of those allowlisted reasons
1621
+ * are mapped to `{ found: false, reason }`. Unknown or uncoded 404s, plus
1622
+ * permission/auth errors such as tenant mismatch, continue to throw typed SDK
1623
+ * errors instead of being collapsed into a storefront absence result.
1624
+ */
1625
+ async detail(params) {
1626
+ try {
1627
+ const product = await this.request(
1628
+ "/api/products/detail",
1629
+ params
1630
+ );
1631
+ return { found: true, product };
1632
+ } catch (err) {
1633
+ const notFoundResult = productDetailResultFromError(err);
1634
+ if (notFoundResult) return notFoundResult;
1635
+ throw err;
1636
+ }
1637
+ }
1638
+ /**
1639
+ * Atomically create or update a product together with its options,
1640
+ * option-values, and variants in a single transaction. Mirrors Shopify's
1641
+ * `productSet` shape and is the canonical write path for the MCP
1642
+ * `product-upsert` tool.
1643
+ */
1644
+ upsert(params) {
1645
+ return this.request("/api/products/upsert", params);
1646
+ }
1647
+ };
1648
+
1563
1649
  // src/core/commerce/commerce-client.ts
1564
1650
  var CommerceClient = class {
1565
1651
  constructor(options) {
@@ -1594,9 +1680,14 @@ var CommerceClient = class {
1594
1680
  listingGroups: (params) => execute("/api/products/listing-groups", params),
1595
1681
  detail: async (params) => {
1596
1682
  try {
1597
- return await execute("/api/products/detail", params);
1683
+ const product = await execute(
1684
+ "/api/products/detail",
1685
+ params
1686
+ );
1687
+ return { found: true, product };
1598
1688
  } catch (err) {
1599
- if (err instanceof NotFoundError) return null;
1689
+ const notFoundResult = productDetailResultFromError(err);
1690
+ if (notFoundResult) return notFoundResult;
1600
1691
  throw err;
1601
1692
  }
1602
1693
  }
@@ -1688,42 +1779,6 @@ function createClient(options) {
1688
1779
  return new Client(options);
1689
1780
  }
1690
1781
 
1691
- // src/core/api/base-api.ts
1692
- var BaseApi = class {
1693
- constructor(apiName, options) {
1694
- if (!options.secretKey) {
1695
- throw createConfigError(`secretKey is required for ${apiName}.`);
1696
- }
1697
- this.publishableKey = requirePublishableKeyForSecret(
1698
- apiName,
1699
- options.publishableKey,
1700
- options.secretKey
1701
- );
1702
- this.secretKey = options.secretKey;
1703
- this.apiUrl = options.apiUrl;
1704
- this.onRequestId = options.onRequestId;
1705
- }
1706
- async request(endpoint, body, options) {
1707
- const method = options?.method ?? "POST";
1708
- try {
1709
- const response = await httpFetch(endpoint, {
1710
- method,
1711
- apiUrl: this.apiUrl,
1712
- publishableKey: this.publishableKey,
1713
- secretKey: this.secretKey,
1714
- ...body !== void 0 && { body: JSON.stringify(body) },
1715
- ...options?.headers && { headers: options.headers }
1716
- });
1717
- this.onRequestId?.(response.headers.get("x-request-id") ?? null);
1718
- return parseApiResponse(response, endpoint);
1719
- } catch (err) {
1720
- const id = err instanceof SDKError ? err.requestId ?? null : null;
1721
- this.onRequestId?.(id);
1722
- throw err;
1723
- }
1724
- }
1725
- };
1726
-
1727
1782
  // src/core/api/order-api.ts
1728
1783
  var OrderApi = class extends BaseApi {
1729
1784
  constructor(options) {
@@ -1756,7 +1811,7 @@ var OrderApi = class extends BaseApi {
1756
1811
  }
1757
1812
  bulkImportFulfillments(params) {
1758
1813
  return this.request(
1759
- "/api/orders/bulk-import-fulfillments",
1814
+ "/api/fulfillments/bulk-import",
1760
1815
  params
1761
1816
  );
1762
1817
  }
@@ -1794,58 +1849,144 @@ var ShippingApi = class extends BaseApi {
1794
1849
  }
1795
1850
  };
1796
1851
 
1797
- // src/core/api/product-api.ts
1798
- var ProductApi = class extends BaseApi {
1799
- constructor(options) {
1800
- super("ProductApi", options);
1801
- }
1802
- /**
1803
- * Check point-in-time stock availability for one or more product variants.
1804
- * Results reflect available stock at the moment of the call and are not guaranteed
1805
- * to remain available by the time an order is placed.
1806
- */
1807
- stockCheck(params) {
1808
- return this.request("/api/products/stock-check", params);
1809
- }
1810
- listingGroups(params) {
1811
- return this.request(
1812
- "/api/products/listing-groups",
1813
- params
1814
- );
1815
- }
1816
- /**
1817
- * Fetch full product detail by slug or id.
1818
- * Returns `null` on 404 regardless of reason (`not_found` / `not_published` /
1819
- * `tenant_mismatch` / `feature_disabled`). For the reason behind a null,
1820
- * inspect `client.lastRequestId` against backend logs.
1821
- */
1822
- async detail(params) {
1823
- try {
1824
- return await this.request("/api/products/detail", params);
1825
- } catch (err) {
1826
- if (err instanceof NotFoundError) return null;
1827
- throw err;
1828
- }
1829
- }
1830
- /**
1831
- * Atomically create or update a product together with its options,
1832
- * option-values, and variants in a single transaction. Mirrors Shopify's
1833
- * `productSet` shape and is the canonical write path for the MCP
1834
- * `product-upsert` tool.
1835
- */
1836
- upsert(params) {
1837
- return this.request("/api/products/upsert", params);
1838
- }
1839
- };
1840
-
1841
1852
  // src/core/webhook/index.ts
1853
+ var ORDER_CHANGED_EVENT_TYPE = "collection.orderChanged";
1854
+ var COMMERCE_NOTIFICATION_EVENT_TYPE = "commerce.notification";
1842
1855
  function isValidWebhookEvent(data) {
1843
1856
  if (typeof data !== "object" || data === null) return false;
1844
1857
  const obj = data;
1845
1858
  return typeof obj.collection === "string" && typeof obj.operation === "string" && obj.operation.length > 0 && typeof obj.data === "object" && obj.data !== null;
1846
1859
  }
1860
+ var COMMERCE_NOTIFICATION_OPERATION = "notification";
1861
+ var COMMERCE_NOTIFICATION_EVENTS = [
1862
+ "orderPaid",
1863
+ "fulfillmentShipped",
1864
+ "orderDelivered",
1865
+ "returnRequested",
1866
+ "returnCompleted"
1867
+ ];
1868
+ function isStringOrNumber(value) {
1869
+ return typeof value === "string" || typeof value === "number";
1870
+ }
1871
+ function isWebhookOrderScope(value) {
1872
+ if (!isRecord2(value)) return false;
1873
+ if (value.kind === "collection") {
1874
+ return typeof value.collection === "string";
1875
+ }
1876
+ if (value.kind === "join") {
1877
+ return typeof value.collection === "string" && typeof value.field === "string" && isStringOrNumber(value.id);
1878
+ }
1879
+ return false;
1880
+ }
1881
+ function isWebhookOrderMoved(value) {
1882
+ if (!isRecord2(value)) return false;
1883
+ return typeof value.collection === "string" && isStringOrNumber(value.id) && (value.relatedCollection === void 0 || typeof value.relatedCollection === "string") && (value.relatedId === void 0 || isStringOrNumber(value.relatedId));
1884
+ }
1885
+ function hasOptionalOrderValue(value, key) {
1886
+ return value[key] === void 0 || value[key] === null || typeof value[key] === "string";
1887
+ }
1888
+ function isWebhookOrderChange(value) {
1889
+ if (!isRecord2(value)) return false;
1890
+ return value.type === "order" && value.source === "payload-orderable" && (value.orderableFieldName === void 0 || typeof value.orderableFieldName === "string") && hasOptionalOrderValue(value, "previousOrder") && hasOptionalOrderValue(value, "nextOrder") && isWebhookOrderScope(value.scope) && isWebhookOrderMoved(value.moved);
1891
+ }
1892
+ function isCommerceNotificationEventName(value) {
1893
+ return typeof value === "string" && COMMERCE_NOTIFICATION_EVENTS.includes(value);
1894
+ }
1895
+ function isCommerceNotificationSourceCollection(value) {
1896
+ return value === "orders" || value === "fulfillments" || value === "returns";
1897
+ }
1898
+ function isCommerceNotificationData(value) {
1899
+ if (!isRecord2(value)) return false;
1900
+ if (value.source !== void 0 && (!isRecord2(value.source) || !isCommerceNotificationSourceCollection(value.source.collection) || !isStringOrNumber(value.source.id))) {
1901
+ return false;
1902
+ }
1903
+ return (value.orderId === void 0 || isStringOrNumber(value.orderId)) && (value.orderNumber === void 0 || typeof value.orderNumber === "string") && (value.status === void 0 || typeof value.status === "string") && (value.totalAmount === void 0 || typeof value.totalAmount === "number") && (value.currency === void 0 || typeof value.currency === "string") && (value.fulfillmentId === void 0 || isStringOrNumber(value.fulfillmentId)) && (value.fulfillmentStatus === void 0 || typeof value.fulfillmentStatus === "string") && (value.shippedAt === void 0 || typeof value.shippedAt === "string") && (value.returnId === void 0 || isStringOrNumber(value.returnId)) && (value.returnStatus === void 0 || typeof value.returnStatus === "string") && (value.refundAmount === void 0 || typeof value.refundAmount === "number") && (value.completedAt === void 0 || typeof value.completedAt === "string");
1904
+ }
1905
+ function isCommerceNotification(value) {
1906
+ if (!isRecord2(value)) return false;
1907
+ return isCommerceNotificationEventName(value.event) && typeof value.intentId === "string" && value.intentId.length > 0 && typeof value.dedupeKey === "string" && value.dedupeKey.length > 0 && (value.orderId === void 0 || typeof value.orderId === "string") && (value.fulfillmentId === void 0 || typeof value.fulfillmentId === "string") && (value.returnId === void 0 || typeof value.returnId === "string");
1908
+ }
1909
+ function isWebhookCommerceNotificationChange(value) {
1910
+ if (!isRecord2(value)) return false;
1911
+ return value.type === "notification" && value.source === "commerce-notifications" && isCommerceNotificationEventName(value.event) && isCommerceNotificationSourceCollection(value.sourceCollection) && isStringOrNumber(value.sourceId);
1912
+ }
1913
+ function matchesOptionalId(actual, expected) {
1914
+ return actual === void 0 || expected === void 0 || actual === expected;
1915
+ }
1916
+ function isOrderChangedWebhookEvent(event) {
1917
+ if (!isValidWebhookEvent(event) || event.operation !== "update" || event.eventType !== ORDER_CHANGED_EVENT_TYPE || !isWebhookOrderChange(event.change)) {
1918
+ return false;
1919
+ }
1920
+ if (event.collection !== event.change.scope.collection) {
1921
+ return false;
1922
+ }
1923
+ if (event.change.scope.kind === "collection" && event.change.moved.collection !== event.change.scope.collection) {
1924
+ return false;
1925
+ }
1926
+ return true;
1927
+ }
1928
+ function isCommerceNotificationWebhookEvent(event) {
1929
+ if (!isValidWebhookEvent(event) || event.operation !== COMMERCE_NOTIFICATION_OPERATION || event.eventType !== COMMERCE_NOTIFICATION_EVENT_TYPE || !isCommerceNotificationData(event.data) || !isCommerceNotification(event.notification)) {
1930
+ return false;
1931
+ }
1932
+ const notification = event.notification;
1933
+ const data = event.data;
1934
+ const change = event.change;
1935
+ const sourceCollection = data.source?.collection ?? event.collection;
1936
+ const sourceId = data.source?.id;
1937
+ if (!isCommerceNotificationSourceCollection(sourceCollection)) return false;
1938
+ if (data.source !== void 0 && event.collection !== data.source.collection) {
1939
+ return false;
1940
+ }
1941
+ if (change !== void 0) {
1942
+ if (!isWebhookCommerceNotificationChange(change)) return false;
1943
+ if (change.sourceCollection !== sourceCollection) return false;
1944
+ if (change.event !== notification.event) return false;
1945
+ if (!matchesOptionalId(change.sourceId, sourceId)) return false;
1946
+ }
1947
+ const changeSourceId = isWebhookCommerceNotificationChange(change) ? change.sourceId : void 0;
1948
+ if (notification.event === "orderPaid" || notification.event === "orderDelivered") {
1949
+ return sourceCollection === "orders" && typeof notification.orderId === "string" && notification.orderId.length > 0 && matchesOptionalId(data.orderId, sourceId) && matchesOptionalId(notification.orderId, sourceId) && matchesOptionalId(data.orderId, notification.orderId) && matchesOptionalId(changeSourceId, data.orderId ?? notification.orderId);
1950
+ }
1951
+ if (notification.event === "fulfillmentShipped") {
1952
+ return sourceCollection === "fulfillments" && matchesOptionalId(data.fulfillmentId, sourceId) && typeof notification.fulfillmentId === "string" && notification.fulfillmentId.length > 0 && matchesOptionalId(notification.fulfillmentId, sourceId) && matchesOptionalId(data.fulfillmentId, notification.fulfillmentId) && matchesOptionalId(changeSourceId, notification.fulfillmentId);
1953
+ }
1954
+ if (notification.event === "returnRequested" || notification.event === "returnCompleted") {
1955
+ return sourceCollection === "returns" && matchesOptionalId(data.returnId, sourceId) && typeof notification.returnId === "string" && notification.returnId.length > 0 && matchesOptionalId(notification.returnId, sourceId) && matchesOptionalId(data.returnId, notification.returnId) && matchesOptionalId(changeSourceId, notification.returnId);
1956
+ }
1957
+ return false;
1958
+ }
1959
+ function getCommerceNotificationIdempotencyKey(event) {
1960
+ return `${event.notification.intentId}:${event.notification.dedupeKey}`;
1961
+ }
1962
+ function defineCommerceEmailConfig(config) {
1963
+ return config;
1964
+ }
1965
+ function createCommerceEmailWebhookHandler(handlers) {
1966
+ return async (event) => {
1967
+ if (!isCommerceNotificationWebhookEvent(event)) {
1968
+ await handlers.unhandled?.(event);
1969
+ return;
1970
+ }
1971
+ const handler = handlers[event.notification.event];
1972
+ if (!handler) {
1973
+ await handlers.unhandled?.(event);
1974
+ return;
1975
+ }
1976
+ await handler({
1977
+ event,
1978
+ idempotencyKey: getCommerceNotificationIdempotencyKey(event)
1979
+ });
1980
+ };
1981
+ }
1982
+ function isWebhookCollection(event, collection) {
1983
+ return isValidWebhookEvent(event) && event.collection === collection;
1984
+ }
1985
+ function isWebhookOperation(event, operation) {
1986
+ return isValidWebhookEvent(event) && event.operation === operation;
1987
+ }
1847
1988
  var CUSTOMER_PASSWORD_RESET_OPERATION = "password-reset";
1848
- function isRecord(value) {
1989
+ function isRecord2(value) {
1849
1990
  return typeof value === "object" && value !== null;
1850
1991
  }
1851
1992
  function hasString(value, key) {
@@ -1855,7 +1996,7 @@ function hasStringOrNumber(value, key) {
1855
1996
  return typeof value[key] === "string" || typeof value[key] === "number";
1856
1997
  }
1857
1998
  function isCustomerPasswordResetWebhookEvent(event) {
1858
- if (event.collection !== "customers" || event.operation !== CUSTOMER_PASSWORD_RESET_OPERATION || !isRecord(event.data)) {
1999
+ if (event.collection !== "customers" || event.operation !== CUSTOMER_PASSWORD_RESET_OPERATION || !isRecord2(event.data)) {
1859
2000
  return false;
1860
2001
  }
1861
2002
  return hasStringOrNumber(event.data, "customerId") && hasString(event.data, "email") && hasString(event.data, "name") && hasString(event.data, "resetPasswordToken") && hasString(event.data, "resetPasswordExpiresAt");
@@ -1975,6 +2116,7 @@ var INTERNAL_COLLECTIONS = [
1975
2116
  "subscriptions",
1976
2117
  "billing-history",
1977
2118
  "inventory-reservations",
2119
+ "commerce-notification-intents",
1978
2120
  "product-collection-items",
1979
2121
  "order-status-logs",
1980
2122
  "api-keys",
@@ -3207,7 +3349,14 @@ function buildProductListingProjection(product, variants) {
3207
3349
  function buildProductListingCard(item, options = {}) {
3208
3350
  const product = item.product;
3209
3351
  const groups = item.groups;
3210
- const primaryImage = firstMedia(product.thumbnail) ?? firstMedia(product.images ?? null) ?? null;
3352
+ const variants = getProductListingCardVariants(item);
3353
+ const projectedListing = buildProductListingProjection(product, variants);
3354
+ const selectionHintVariant = getRelationID(product.listing?.selectionHintVariant) ?? projectedListing.selectionHintVariant;
3355
+ const representativeVariant = findListingCardRepresentativeVariant(
3356
+ variants,
3357
+ selectionHintVariant
3358
+ );
3359
+ const primaryImage = firstMedia(product.thumbnail) ?? firstMedia(product.images ?? null) ?? firstMedia(representativeVariant?.thumbnail) ?? firstMedia(representativeVariant?.images) ?? firstMedia(product.listing?.primaryImage) ?? firstMedia(projectedListing.primaryImage) ?? null;
3211
3360
  const priceRange = aggregateListingPriceRange(groups);
3212
3361
  const availableForSale = groups.some(
3213
3362
  (group) => group.listing.availableForSale
@@ -3215,14 +3364,42 @@ function buildProductListingCard(item, options = {}) {
3215
3364
  const swatches = groups.length > 1 ? groups.map((group) => buildListingSwatch(product, group, options)) : [];
3216
3365
  return {
3217
3366
  id: String(product.id),
3218
- href: buildProductHref({ slug: product.slug }, void 0, options),
3367
+ href: buildProductHref(
3368
+ { slug: product.slug },
3369
+ { listing: { selectionHintVariant } },
3370
+ options
3371
+ ),
3219
3372
  title: product.title,
3373
+ representativeVariant,
3220
3374
  primaryImage,
3221
3375
  priceRange,
3222
3376
  availableForSale,
3223
3377
  swatches
3224
3378
  };
3225
3379
  }
3380
+ function getProductListingCardVariants(item) {
3381
+ const productVariants = item.product.variants?.docs;
3382
+ if (Array.isArray(productVariants) && productVariants.length > 0) {
3383
+ return productVariants;
3384
+ }
3385
+ const variants = [];
3386
+ const seen = /* @__PURE__ */ new Set();
3387
+ for (const group of item.groups) {
3388
+ for (const variant of group.variants) {
3389
+ const id = getRelationID(variant.id);
3390
+ if (id && seen.has(id)) continue;
3391
+ if (id) seen.add(id);
3392
+ variants.push(variant);
3393
+ }
3394
+ }
3395
+ return variants;
3396
+ }
3397
+ function findListingCardRepresentativeVariant(variants, representativeVariantId) {
3398
+ if (representativeVariantId == null) return null;
3399
+ return variants.find(
3400
+ (variant) => getRelationID(variant.id) === representativeVariantId
3401
+ ) ?? null;
3402
+ }
3226
3403
  function aggregateListingPriceRange(groups) {
3227
3404
  const minPrice = minOfNullable(groups.map((g) => g.listing.minPrice));
3228
3405
  const maxPrice = maxOfNullable(groups.map((g) => g.listing.maxPrice));