@aglyn/plugins-commerce 1.0.0-beta.221 → 1.0.0-beta.223

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 (125) hide show
  1. package/README.md +4 -2
  2. package/package.json +14 -13
  3. package/src/lib/components/console/catalog-organization-card.component.js +9 -0
  4. package/src/lib/components/console/catalog-organization-card.component.js.map +1 -1
  5. package/src/lib/components/console/discounts-card.component.js +8 -0
  6. package/src/lib/components/console/discounts-card.component.js.map +1 -1
  7. package/src/lib/components/console/gift-cards-card.component.js +8 -0
  8. package/src/lib/components/console/gift-cards-card.component.js.map +1 -1
  9. package/src/lib/components/console/host-coupons-card.component.js +8 -0
  10. package/src/lib/components/console/host-coupons-card.component.js.map +1 -1
  11. package/src/lib/components/console/host-orders-card.component.js +34 -70
  12. package/src/lib/components/console/host-orders-card.component.js.map +1 -1
  13. package/src/lib/components/console/product-zones.d.ts +7 -6
  14. package/src/lib/components/console/product-zones.js +5 -4
  15. package/src/lib/components/console/product-zones.js.map +1 -1
  16. package/src/lib/components/console/products-hub-card.component.js +86 -284
  17. package/src/lib/components/console/products-hub-card.component.js.map +1 -1
  18. package/src/lib/declarations.console-server.d.ts +38 -0
  19. package/src/lib/declarations.console-server.js +67 -0
  20. package/src/lib/declarations.console-server.js.map +1 -0
  21. package/src/lib/model/commerce-gift-cards.d.ts +26 -0
  22. package/src/lib/model/commerce-gift-cards.js +42 -0
  23. package/src/lib/model/commerce-gift-cards.js.map +1 -1
  24. package/src/lib/model/commerce-orders.d.ts +0 -57
  25. package/src/lib/model/commerce-orders.js +0 -80
  26. package/src/lib/model/commerce-orders.js.map +1 -1
  27. package/src/lib/model/index.d.ts +0 -1
  28. package/src/lib/model/index.js +0 -1
  29. package/src/lib/model/index.js.map +1 -1
  30. package/src/lib/plugin.js +5 -0
  31. package/src/lib/plugin.js.map +1 -1
  32. package/src/lib/server/api-v1/openapi.d.ts +27 -0
  33. package/src/lib/server/api-v1/openapi.js +219 -0
  34. package/src/lib/server/api-v1/openapi.js.map +1 -0
  35. package/src/lib/server/api-v1/orders-and-products.d.ts +19 -0
  36. package/src/lib/server/api-v1/orders-and-products.js +419 -0
  37. package/src/lib/server/api-v1/orders-and-products.js.map +1 -0
  38. package/src/lib/server/billing-webhook.js +10 -13
  39. package/src/lib/server/billing-webhook.js.map +1 -1
  40. package/src/lib/server/download.js +4 -1
  41. package/src/lib/server/download.js.map +1 -1
  42. package/src/lib/server/fulfill-order.d.ts +52 -11
  43. package/src/lib/server/fulfill-order.js +6 -9
  44. package/src/lib/server/fulfill-order.js.map +1 -1
  45. package/src/lib/server/gift-card-issue.d.ts +57 -0
  46. package/src/lib/server/gift-card-issue.js +155 -0
  47. package/src/lib/server/gift-card-issue.js.map +1 -0
  48. package/src/lib/server/gift-cards.js +25 -84
  49. package/src/lib/server/gift-cards.js.map +1 -1
  50. package/src/lib/server/membership-register.js +9 -8
  51. package/src/lib/server/membership-register.js.map +1 -1
  52. package/src/lib/server/newsletter.js +7 -5
  53. package/src/lib/server/newsletter.js.map +1 -1
  54. package/src/lib/server/paid-media-delivery.d.ts +21 -0
  55. package/src/lib/server/paid-media-delivery.js +23 -5
  56. package/src/lib/server/paid-media-delivery.js.map +1 -1
  57. package/src/lib/server/paid-media-serve-count.d.ts +28 -0
  58. package/src/lib/server/paid-media-serve-count.js +46 -0
  59. package/src/lib/server/paid-media-serve-count.js.map +1 -0
  60. package/src/lib/server/person-eraser.d.ts +45 -0
  61. package/src/lib/server/person-eraser.js +57 -0
  62. package/src/lib/server/person-eraser.js.map +1 -0
  63. package/src/lib/server/refund.js +15 -15
  64. package/src/lib/server/refund.js.map +1 -1
  65. package/src/lib/server/restock-flag.d.ts +2 -2
  66. package/src/lib/server/restock-flag.js +1 -1
  67. package/src/lib/server/restock-flag.js.map +1 -1
  68. package/src/lib/server/stream.js +4 -1
  69. package/src/lib/server/stream.js.map +1 -1
  70. package/src/lib/server.js +5 -21
  71. package/src/lib/server.js.map +1 -1
  72. package/src/lib/testing/stand-in-person-records.d.ts +43 -0
  73. package/src/lib/testing/stand-in-person-records.js +63 -0
  74. package/src/lib/testing/stand-in-person-records.js.map +1 -0
  75. package/src/lib/transfer/gift-card-confirm-step.component.d.ts +22 -0
  76. package/src/lib/transfer/gift-card-confirm-step.component.js +259 -0
  77. package/src/lib/transfer/gift-card-confirm-step.component.js.map +1 -0
  78. package/src/lib/transfer/gift-cards-plan.d.ts +29 -0
  79. package/src/lib/transfer/gift-cards-plan.js +40 -0
  80. package/src/lib/transfer/gift-cards-plan.js.map +1 -0
  81. package/src/lib/transfer/gift-cards.server.d.ts +34 -0
  82. package/src/lib/transfer/gift-cards.server.js +296 -0
  83. package/src/lib/transfer/gift-cards.server.js.map +1 -0
  84. package/src/lib/transfer/list-filter.d.ts +29 -0
  85. package/src/lib/transfer/list-filter.js +112 -0
  86. package/src/lib/transfer/list-filter.js.map +1 -0
  87. package/src/lib/transfer/product-import-results.d.ts +29 -0
  88. package/src/lib/transfer/product-import-results.js +49 -0
  89. package/src/lib/transfer/product-import-results.js.map +1 -0
  90. package/src/lib/transfer/product-import-step.component.d.ts +30 -0
  91. package/src/lib/transfer/product-import-step.component.js +99 -0
  92. package/src/lib/transfer/product-import-step.component.js.map +1 -0
  93. package/src/lib/transfer/product-transfer.d.ts +211 -0
  94. package/src/lib/transfer/product-transfer.js +1594 -0
  95. package/src/lib/transfer/product-transfer.js.map +1 -0
  96. package/src/lib/transfer/products.server.d.ts +44 -0
  97. package/src/lib/transfer/products.server.js +550 -0
  98. package/src/lib/transfer/products.server.js.map +1 -0
  99. package/src/lib/transfer/records-transfer.d.ts +182 -0
  100. package/src/lib/transfer/records-transfer.js +1153 -0
  101. package/src/lib/transfer/records-transfer.js.map +1 -0
  102. package/src/lib/transfer/records.server.d.ts +68 -0
  103. package/src/lib/transfer/records.server.js +455 -0
  104. package/src/lib/transfer/records.server.js.map +1 -0
  105. package/src/lib/transfer/register-transfer-resources.d.ts +17 -0
  106. package/src/lib/transfer/register-transfer-resources.js +171 -0
  107. package/src/lib/transfer/register-transfer-resources.js.map +1 -0
  108. package/src/lib/transfer/register-transfer-ui.d.ts +23 -0
  109. package/src/lib/transfer/register-transfer-ui.js +78 -0
  110. package/src/lib/transfer/register-transfer-ui.js.map +1 -0
  111. package/src/lib/transfer/server-common.d.ts +76 -0
  112. package/src/lib/transfer/server-common.js +165 -0
  113. package/src/lib/transfer/server-common.js.map +1 -0
  114. package/src/lib/transfer/transfer-header-actions.component.d.ts +39 -0
  115. package/src/lib/transfer/transfer-header-actions.component.js +79 -0
  116. package/src/lib/transfer/transfer-header-actions.component.js.map +1 -0
  117. package/src/lib/transfer/transfer-keys.d.ts +31 -0
  118. package/src/lib/transfer/transfer-keys.js +56 -0
  119. package/src/lib/transfer/transfer-keys.js.map +1 -0
  120. package/src/lib/model/commerce-io.d.ts +0 -36
  121. package/src/lib/model/commerce-io.js +0 -245
  122. package/src/lib/model/commerce-io.js.map +0 -1
  123. package/src/lib/server/contact-refund.d.ts +0 -117
  124. package/src/lib/server/contact-refund.js +0 -196
  125. package/src/lib/server/contact-refund.js.map +0 -1
@@ -0,0 +1,79 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ 'use client';
17
+ import { _ as _extends } from "@swc/helpers/_/_extends";
18
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
19
+ import { TransferResumeImport } from "@aglyn/aglyn/app-utils/transfer-resume-import";
20
+ import { useTransferLauncher } from "@aglyn/aglyn/app-utils/transfer-launcher-context";
21
+ import { Button, Stack } from "@mui/material";
22
+ import { COMMERCE_IMPORTABLE_TRANSFERS } from "./transfer-keys.js";
23
+ /**
24
+ * A commerce card's Import and Export, for its card header (AGL-3531): the
25
+ * console's import wizard and export dialog on the card's resource, through
26
+ * the core launcher. Each shows only when the launcher's `can` says the
27
+ * person may (AGL-3554) — their access on the site, the resource's
28
+ * `importRoles` (gift cards: a site's admins) and the workspace's plan —
29
+ * and Import only for a resource a file may write. Off the console shell
30
+ * there is no launcher, and nothing renders.
31
+ */ export function TransferHeaderActions(props) {
32
+ const { resource, hostId, noun, onImported } = props;
33
+ const transfer = useTransferLauncher();
34
+ if (!transfer) return null;
35
+ const target = {
36
+ resource,
37
+ scope: 'host',
38
+ hostId
39
+ };
40
+ const canImport = COMMERCE_IMPORTABLE_TRANSFERS.has(resource) && transfer.can('import', target);
41
+ const canExport = transfer.can('export', target);
42
+ if (!canImport && !canExport) return null;
43
+ const suffix = noun ? ` ${noun}` : '';
44
+ return /*#__PURE__*/ _jsxs(Stack, {
45
+ direction: "row",
46
+ spacing: 1,
47
+ children: [
48
+ canImport ? /*#__PURE__*/ _jsx(TransferResumeImport, _extends({
49
+ target: target
50
+ }, onImported ? {
51
+ onFinished: onImported
52
+ } : {})) : null,
53
+ canImport ? /*#__PURE__*/ _jsx(Button, {
54
+ size: "small",
55
+ onClick: ()=>transfer.openImport(_extends({
56
+ resource,
57
+ scope: 'host',
58
+ hostId
59
+ }, onImported ? {
60
+ onFinished: onImported
61
+ } : {})),
62
+ children: `Import${suffix}`
63
+ }) : null,
64
+ canExport ? /*#__PURE__*/ _jsx(Button, {
65
+ size: "small",
66
+ onClick: ()=>transfer.openExport({
67
+ resource,
68
+ scope: 'host',
69
+ hostId
70
+ }),
71
+ children: `Export${suffix}`
72
+ }) : null
73
+ ]
74
+ });
75
+ }
76
+ TransferHeaderActions.displayName = 'TransferHeaderActions';
77
+ export default TransferHeaderActions;
78
+
79
+ //# sourceMappingURL=transfer-header-actions.component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/transfer/transfer-header-actions.component.tsx"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use client'\n\nimport { TransferResumeImport } from '@aglyn/aglyn/app-utils/transfer-resume-import'\nimport { useTransferLauncher } from '@aglyn/aglyn/app-utils/transfer-launcher-context'\nimport { Button, Stack } from '@mui/material'\nimport { COMMERCE_IMPORTABLE_TRANSFERS } from './transfer-keys'\n\nexport interface TransferHeaderActionsProps {\n /** The commerce resource key (`transfer-keys.ts`). */\n resource: string\n hostId: string\n /** What the buttons name, when the card holds more than this resource (\"categories\"). */\n noun?: string\n /** Called when the person leaves the import wizard from its results. */\n onImported?(): void\n}\n\n/**\n * A commerce card's Import and Export, for its card header (AGL-3531): the\n * console's import wizard and export dialog on the card's resource, through\n * the core launcher. Each shows only when the launcher's `can` says the\n * person may (AGL-3554) — their access on the site, the resource's\n * `importRoles` (gift cards: a site's admins) and the workspace's plan —\n * and Import only for a resource a file may write. Off the console shell\n * there is no launcher, and nothing renders.\n */\nexport function TransferHeaderActions(props: TransferHeaderActionsProps) {\n const { resource, hostId, noun, onImported } = props\n const transfer = useTransferLauncher()\n if (!transfer) return null\n const target = { resource, scope: 'host' as const, hostId }\n const canImport = COMMERCE_IMPORTABLE_TRANSFERS.has(resource) && transfer.can('import', target)\n const canExport = transfer.can('export', target)\n if (!canImport && !canExport) return null\n const suffix = noun ? ` ${noun}` : ''\n return (\n <Stack direction=\"row\" spacing={1}>\n {/* An import left unfinished, reopened where it stopped (AGL-3549). */}\n {canImport ? <TransferResumeImport target={target} {...(onImported ? { onFinished: onImported } : {})} /> : null}\n {canImport ? (\n <Button\n size=\"small\"\n onClick={() =>\n transfer.openImport({ resource, scope: 'host', hostId, ...(onImported ? { onFinished: onImported } : {}) })\n }\n >\n {`Import${suffix}`}\n </Button>\n ) : null}\n {canExport ? (\n <Button size=\"small\" onClick={() => transfer.openExport({ resource, scope: 'host', hostId })}>\n {`Export${suffix}`}\n </Button>\n ) : null}\n </Stack>\n )\n}\nTransferHeaderActions.displayName = 'TransferHeaderActions'\n\nexport default TransferHeaderActions\n"],"names":["TransferResumeImport","useTransferLauncher","Button","Stack","COMMERCE_IMPORTABLE_TRANSFERS","TransferHeaderActions","props","resource","hostId","noun","onImported","transfer","target","scope","canImport","has","can","canExport","suffix","direction","spacing","onFinished","size","onClick","openImport","openExport","displayName"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;AAEA,SAASA,oBAAoB,QAAQ,gDAA+C;AACpF,SAASC,mBAAmB,QAAQ,mDAAkD;AACtF,SAASC,MAAM,EAAEC,KAAK,QAAQ,gBAAe;AAC7C,SAASC,6BAA6B,QAAQ,qBAAiB;AAY/D;;;;;;;;CAQC,GACD,OAAO,SAASC,sBAAsBC,KAAiC;IACrE,MAAM,EAAEC,QAAQ,EAAEC,MAAM,EAAEC,IAAI,EAAEC,UAAU,EAAE,GAAGJ;IAC/C,MAAMK,WAAWV;IACjB,IAAI,CAACU,UAAU,OAAO;IACtB,MAAMC,SAAS;QAAEL;QAAUM,OAAO;QAAiBL;IAAO;IAC1D,MAAMM,YAAYV,8BAA8BW,GAAG,CAACR,aAAaI,SAASK,GAAG,CAAC,UAAUJ;IACxF,MAAMK,YAAYN,SAASK,GAAG,CAAC,UAAUJ;IACzC,IAAI,CAACE,aAAa,CAACG,WAAW,OAAO;IACrC,MAAMC,SAAST,OAAO,CAAC,CAAC,EAAEA,MAAM,GAAG;IACnC,qBACE,MAACN;QAAMgB,WAAU;QAAMC,SAAS;;YAE7BN,0BAAY,KAACd;gBAAqBY,QAAQA;eAAaF,aAAa;gBAAEW,YAAYX;YAAW,IAAI,CAAC,MAAS;YAC3GI,0BACC,KAACZ;gBACCoB,MAAK;gBACLC,SAAS,IACPZ,SAASa,UAAU,CAAC;wBAAEjB;wBAAUM,OAAO;wBAAQL;uBAAYE,aAAa;wBAAEW,YAAYX;oBAAW,IAAI,CAAC;0BAGvG,CAAC,MAAM,EAAEQ,QAAQ;iBAElB;YACHD,0BACC,KAACf;gBAAOoB,MAAK;gBAAQC,SAAS,IAAMZ,SAASc,UAAU,CAAC;wBAAElB;wBAAUM,OAAO;wBAAQL;oBAAO;0BACvF,CAAC,MAAM,EAAEU,QAAQ;iBAElB;;;AAGV;AACAb,sBAAsBqB,WAAW,GAAG;AAEpC,eAAerB,sBAAqB"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ export declare const COMMERCE_PRODUCTS_TRANSFER = "commerce.products";
18
+ export declare const COMMERCE_ORDERS_TRANSFER = "commerce.orders";
19
+ export declare const COMMERCE_DISCOUNTS_TRANSFER = "commerce.discounts";
20
+ export declare const COMMERCE_COUPONS_TRANSFER = "commerce.coupons";
21
+ export declare const COMMERCE_GIFT_CARDS_TRANSFER = "commerce.gift-cards";
22
+ export declare const COMMERCE_CATEGORIES_TRANSFER = "commerce.categories";
23
+ /** Every commerce resource key, in the order the hub lists them. */
24
+ export declare const COMMERCE_TRANSFER_RESOURCES: readonly ["commerce.products", "commerce.categories", "commerce.orders", "commerce.discounts", "commerce.coupons", "commerce.gift-cards"];
25
+ /** The resources a file may write; the others are exported only. */
26
+ export declare const COMMERCE_IMPORTABLE_TRANSFERS: ReadonlySet<string>;
27
+ /**
28
+ * The gift card import's Confirm step (AGL-3551), and the key its answer
29
+ * rides under in the wizard's `extras`: the total value typed back.
30
+ */
31
+ export declare const GIFT_CARD_CONFIRM_STEP_ID = "gift-card-total";
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */ /*
17
+ * THE COMMERCE TRANSFER RESOURCES (AGL-3531), by the key each is declared
18
+ * under in `plugins.config.json` (`transferResources`). Every one is a site's
19
+ * records (`scope: host`); the console's lists open the import wizard and the
20
+ * export dialog on these keys through the core launcher.
21
+ *
22
+ * | key | import | why |
23
+ * | -- | -- | -- |
24
+ * | products | yes | matched by handle, then SKU, then ID; a match is updated |
25
+ * | categories | yes | matched by slug, then name, then ID |
26
+ * | discounts | yes | matched by code, then name, then ID; new ones start switched off |
27
+ * | coupons | yes | matched by code; new ones start switched off |
28
+ * | orders | no | an order is the record of a sale, written by checkout alone |
29
+ * | gift cards | yes, by issuing | each row issues a card through the issue path, confirmed by its total; owners and admins only |
30
+ */ export const COMMERCE_PRODUCTS_TRANSFER = 'commerce.products';
31
+ export const COMMERCE_ORDERS_TRANSFER = 'commerce.orders';
32
+ export const COMMERCE_DISCOUNTS_TRANSFER = 'commerce.discounts';
33
+ export const COMMERCE_COUPONS_TRANSFER = 'commerce.coupons';
34
+ export const COMMERCE_GIFT_CARDS_TRANSFER = 'commerce.gift-cards';
35
+ export const COMMERCE_CATEGORIES_TRANSFER = 'commerce.categories';
36
+ /** Every commerce resource key, in the order the hub lists them. */ export const COMMERCE_TRANSFER_RESOURCES = [
37
+ COMMERCE_PRODUCTS_TRANSFER,
38
+ COMMERCE_CATEGORIES_TRANSFER,
39
+ COMMERCE_ORDERS_TRANSFER,
40
+ COMMERCE_DISCOUNTS_TRANSFER,
41
+ COMMERCE_COUPONS_TRANSFER,
42
+ COMMERCE_GIFT_CARDS_TRANSFER
43
+ ];
44
+ /** The resources a file may write; the others are exported only. */ export const COMMERCE_IMPORTABLE_TRANSFERS = new Set([
45
+ COMMERCE_PRODUCTS_TRANSFER,
46
+ COMMERCE_CATEGORIES_TRANSFER,
47
+ COMMERCE_DISCOUNTS_TRANSFER,
48
+ COMMERCE_COUPONS_TRANSFER,
49
+ COMMERCE_GIFT_CARDS_TRANSFER
50
+ ]);
51
+ /**
52
+ * The gift card import's Confirm step (AGL-3551), and the key its answer
53
+ * rides under in the wizard's `extras`: the total value typed back.
54
+ */ export const GIFT_CARD_CONFIRM_STEP_ID = 'gift-card-total';
55
+
56
+ //# sourceMappingURL=transfer-keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/transfer/transfer-keys.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * THE COMMERCE TRANSFER RESOURCES (AGL-3531), by the key each is declared\n * under in `plugins.config.json` (`transferResources`). Every one is a site's\n * records (`scope: host`); the console's lists open the import wizard and the\n * export dialog on these keys through the core launcher.\n *\n * | key | import | why |\n * | -- | -- | -- |\n * | products | yes | matched by handle, then SKU, then ID; a match is updated |\n * | categories | yes | matched by slug, then name, then ID |\n * | discounts | yes | matched by code, then name, then ID; new ones start switched off |\n * | coupons | yes | matched by code; new ones start switched off |\n * | orders | no | an order is the record of a sale, written by checkout alone |\n * | gift cards | yes, by issuing | each row issues a card through the issue path, confirmed by its total; owners and admins only |\n */\n\nexport const COMMERCE_PRODUCTS_TRANSFER = 'commerce.products'\nexport const COMMERCE_ORDERS_TRANSFER = 'commerce.orders'\nexport const COMMERCE_DISCOUNTS_TRANSFER = 'commerce.discounts'\nexport const COMMERCE_COUPONS_TRANSFER = 'commerce.coupons'\nexport const COMMERCE_GIFT_CARDS_TRANSFER = 'commerce.gift-cards'\nexport const COMMERCE_CATEGORIES_TRANSFER = 'commerce.categories'\n\n/** Every commerce resource key, in the order the hub lists them. */\nexport const COMMERCE_TRANSFER_RESOURCES = [\n COMMERCE_PRODUCTS_TRANSFER,\n COMMERCE_CATEGORIES_TRANSFER,\n COMMERCE_ORDERS_TRANSFER,\n COMMERCE_DISCOUNTS_TRANSFER,\n COMMERCE_COUPONS_TRANSFER,\n COMMERCE_GIFT_CARDS_TRANSFER,\n] as const\n\n/** The resources a file may write; the others are exported only. */\nexport const COMMERCE_IMPORTABLE_TRANSFERS: ReadonlySet<string> = new Set([\n COMMERCE_PRODUCTS_TRANSFER,\n COMMERCE_CATEGORIES_TRANSFER,\n COMMERCE_DISCOUNTS_TRANSFER,\n COMMERCE_COUPONS_TRANSFER,\n COMMERCE_GIFT_CARDS_TRANSFER,\n])\n\n/**\n * The gift card import's Confirm step (AGL-3551), and the key its answer\n * rides under in the wizard's `extras`: the total value typed back.\n */\nexport const GIFT_CARD_CONFIRM_STEP_ID = 'gift-card-total'\n"],"names":["COMMERCE_PRODUCTS_TRANSFER","COMMERCE_ORDERS_TRANSFER","COMMERCE_DISCOUNTS_TRANSFER","COMMERCE_COUPONS_TRANSFER","COMMERCE_GIFT_CARDS_TRANSFER","COMMERCE_CATEGORIES_TRANSFER","COMMERCE_TRANSFER_RESOURCES","COMMERCE_IMPORTABLE_TRANSFERS","Set","GIFT_CARD_CONFIRM_STEP_ID"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;CAcC,GAED,OAAO,MAAMA,6BAA6B,oBAAmB;AAC7D,OAAO,MAAMC,2BAA2B,kBAAiB;AACzD,OAAO,MAAMC,8BAA8B,qBAAoB;AAC/D,OAAO,MAAMC,4BAA4B,mBAAkB;AAC3D,OAAO,MAAMC,+BAA+B,sBAAqB;AACjE,OAAO,MAAMC,+BAA+B,sBAAqB;AAEjE,kEAAkE,GAClE,OAAO,MAAMC,8BAA8B;IACzCN;IACAK;IACAJ;IACAC;IACAC;IACAC;CACD,CAAS;AAEV,kEAAkE,GAClE,OAAO,MAAMG,gCAAqD,IAAIC,IAAI;IACxER;IACAK;IACAH;IACAC;IACAC;CACD,EAAC;AAEF;;;CAGC,GACD,OAAO,MAAMK,4BAA4B,kBAAiB"}
@@ -1,36 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2026 Aglyn LLC
4
- *
5
- * Licensed under the Apache License, Version 2.0 (the "License");
6
- * you may not use this file except in compliance with the License.
7
- * You may obtain a copy of the License at
8
- *
9
- * http://www.apache.org/licenses/LICENSE-2.0
10
- *
11
- * Unless required by applicable law or agreed to in writing, software
12
- * distributed under the License is distributed on an "AS IS" BASIS,
13
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- * See the License for the specific language governing permissions and
15
- * limitations under the License.
16
- */
17
- /**
18
- * Catalog CSV import/export (AGL-282) speaking Shopify's product CSV
19
- * dialect — the migration wedge for switchers: `Handle` groups variant
20
- * rows into one product, `Option1..3 Name/Value` build the option axes,
21
- * `Variant *` columns carry per-variant data, and repeated-handle rows
22
- * with only `Image Src` add media. Export mirrors the same columns so
23
- * round-trips are lossless for the fields both sides share.
24
- */
25
- import { type HostProduct } from './commerce';
26
- export declare const PRODUCT_CSV_HEADER: readonly ["Handle", "Title", "Body (HTML)", "Type", "Tags", "Published", "Option1 Name", "Option1 Value", "Option2 Name", "Option2 Value", "Option3 Name", "Option3 Value", "Variant SKU", "Variant Barcode", "Variant Price", "Variant Compare At Price", "Variant Inventory Qty", "Variant Grams", "Image Src"];
27
- /** One row per variant; extra image rows follow Shopify's convention. */
28
- export declare function productsToCsv(products: Array<HostProduct & {
29
- $id?: string;
30
- }>): string;
31
- export interface ProductCsvImport {
32
- products: HostProduct[];
33
- /** Row-anchored problems; importable products still parse. */
34
- errors: string[];
35
- }
36
- export declare function parseProductsCsv(text: string): ProductCsvImport;
@@ -1,245 +0,0 @@
1
- import { _ as _extends } from "@swc/helpers/_/_extends";
2
- /**
3
- * @license
4
- * Copyright 2026 Aglyn LLC
5
- *
6
- * Licensed under the Apache License, Version 2.0 (the "License");
7
- * you may not use this file except in compliance with the License.
8
- * You may obtain a copy of the License at
9
- *
10
- * http://www.apache.org/licenses/LICENSE-2.0
11
- *
12
- * Unless required by applicable law or agreed to in writing, software
13
- * distributed under the License is distributed on an "AS IS" BASIS,
14
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- * See the License for the specific language governing permissions and
16
- * limitations under the License.
17
- */ /**
18
- * Catalog CSV import/export (AGL-282) speaking Shopify's product CSV
19
- * dialect — the migration wedge for switchers: `Handle` groups variant
20
- * rows into one product, `Option1..3 Name/Value` build the option axes,
21
- * `Variant *` columns carry per-variant data, and repeated-handle rows
22
- * with only `Image Src` add media. Export mirrors the same columns so
23
- * round-trips are lossless for the fields both sides share.
24
- */ import { commerceSlug, validateProduct } from "./commerce.js";
25
- import { parseCsv } from "@aglyn/aglyn/app-utils/csv";
26
- export const PRODUCT_CSV_HEADER = [
27
- 'Handle',
28
- 'Title',
29
- 'Body (HTML)',
30
- 'Type',
31
- 'Tags',
32
- 'Published',
33
- 'Option1 Name',
34
- 'Option1 Value',
35
- 'Option2 Name',
36
- 'Option2 Value',
37
- 'Option3 Name',
38
- 'Option3 Value',
39
- 'Variant SKU',
40
- 'Variant Barcode',
41
- 'Variant Price',
42
- 'Variant Compare At Price',
43
- 'Variant Inventory Qty',
44
- 'Variant Grams',
45
- 'Image Src'
46
- ];
47
- function csvCell(value) {
48
- const text = value == null ? '' : String(value);
49
- return /[",\n]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
50
- }
51
- /** One row per variant; extra image rows follow Shopify's convention. */ export function productsToCsv(products) {
52
- const lines = [
53
- PRODUCT_CSV_HEADER.join(',')
54
- ];
55
- for (const product of products){
56
- var _product_options, _product_mediaUrls;
57
- const options = (_product_options = product.options) != null ? _product_options : [];
58
- product.variants.forEach((variant, index)=>{
59
- var _product_description, _product_tags, _variant_sku, _variant_barcode, _variant_compareAtPriceUsd, _variant_inventory, _variant_weightGrams, _ref;
60
- var _product_mediaUrls;
61
- const optionCells = [];
62
- for(let axis = 0; axis < 3; axis += 1){
63
- var _ref1, _ref2;
64
- var _variant_options;
65
- const option = options[axis];
66
- optionCells.push(index === 0 ? (_ref1 = option == null ? void 0 : option.name) != null ? _ref1 : '' : '', option ? (_ref2 = (_variant_options = variant.options) == null ? void 0 : _variant_options[option.name]) != null ? _ref2 : '' : '');
67
- }
68
- lines.push([
69
- csvCell(product.slug),
70
- csvCell(index === 0 ? product.name : ''),
71
- csvCell(index === 0 ? (_product_description = product.description) != null ? _product_description : '' : ''),
72
- csvCell(index === 0 ? product.type : ''),
73
- csvCell(index === 0 ? ((_product_tags = product.tags) != null ? _product_tags : []).join(', ') : ''),
74
- csvCell(index === 0 ? String(product.status === 'active') : ''),
75
- ...optionCells.map(csvCell),
76
- csvCell((_variant_sku = variant.sku) != null ? _variant_sku : ''),
77
- csvCell((_variant_barcode = variant.barcode) != null ? _variant_barcode : ''),
78
- csvCell(variant.priceUsd),
79
- csvCell((_variant_compareAtPriceUsd = variant.compareAtPriceUsd) != null ? _variant_compareAtPriceUsd : ''),
80
- csvCell((_variant_inventory = variant.inventory) != null ? _variant_inventory : ''),
81
- csvCell((_variant_weightGrams = variant.weightGrams) != null ? _variant_weightGrams : ''),
82
- csvCell(index === 0 ? (_ref = (_product_mediaUrls = product.mediaUrls) == null ? void 0 : _product_mediaUrls[0]) != null ? _ref : '' : '')
83
- ].join(','));
84
- });
85
- for (const url of ((_product_mediaUrls = product.mediaUrls) != null ? _product_mediaUrls : []).slice(1)){
86
- lines.push([
87
- csvCell(product.slug),
88
- ...Array(PRODUCT_CSV_HEADER.length - 2).fill(''),
89
- csvCell(url)
90
- ].join(','));
91
- }
92
- }
93
- return lines.join('\n');
94
- }
95
- /** Case-insensitive column lookup so hand-edited headers still map. */ function headerIndex(header) {
96
- const map = new Map();
97
- header.forEach((cell, index)=>map.set(cell.trim().toLowerCase(), index));
98
- return map;
99
- }
100
- export function parseProductsCsv(text) {
101
- const rows = parseCsv(text);
102
- if (rows.length < 2) {
103
- return {
104
- products: [],
105
- errors: [
106
- 'No data rows found'
107
- ]
108
- };
109
- }
110
- const columns = headerIndex(rows[0]);
111
- const cell = (row, name)=>{
112
- var _row_index;
113
- const index = columns.get(name.toLowerCase());
114
- return index == null ? '' : ((_row_index = row[index]) != null ? _row_index : '').trim();
115
- };
116
- if (!columns.has('handle') || !columns.has('title')) {
117
- return {
118
- products: [],
119
- errors: [
120
- 'Missing required columns: Handle, Title'
121
- ]
122
- };
123
- }
124
- const byHandle = new Map();
125
- const errors = [];
126
- rows.slice(1).forEach((row, rowIndex)=>{
127
- var _product_mediaUrls;
128
- const line = rowIndex + 2;
129
- const handle = commerceSlug(cell(row, 'handle'));
130
- if (!handle) {
131
- if (row.some((value)=>value.trim())) {
132
- errors.push(`Row ${line}: missing Handle`);
133
- }
134
- return;
135
- }
136
- let product = byHandle.get(handle);
137
- const title = cell(row, 'title');
138
- if (!product) {
139
- if (!title) {
140
- // Image-only continuation row for a product we never saw.
141
- errors.push(`Row ${line}: first row for "${handle}" needs a Title`);
142
- return;
143
- }
144
- const published = cell(row, 'published').toLowerCase();
145
- const type = cell(row, 'type').toLowerCase();
146
- product = {
147
- name: title,
148
- slug: handle,
149
- description: cell(row, 'body (html)') || undefined,
150
- type: [
151
- 'physical',
152
- 'digital',
153
- 'service'
154
- ].includes(type) ? type : 'physical',
155
- status: published === 'false' ? 'draft' : 'active',
156
- tags: cell(row, 'tags') ? cell(row, 'tags').split(',').map((tag)=>tag.trim()).filter(Boolean) : undefined,
157
- options: [],
158
- variants: [],
159
- mediaUrls: []
160
- };
161
- byHandle.set(handle, product);
162
- }
163
- const imageSrc = cell(row, 'image src');
164
- if (imageSrc && !((_product_mediaUrls = product.mediaUrls) != null ? _product_mediaUrls : []).includes(imageSrc)) {
165
- var _product_mediaUrls1;
166
- product.mediaUrls = [
167
- ...(_product_mediaUrls1 = product.mediaUrls) != null ? _product_mediaUrls1 : [],
168
- imageSrc
169
- ];
170
- }
171
- const priceRaw = cell(row, 'variant price');
172
- const hasVariant = priceRaw !== '' || cell(row, 'variant sku') !== '';
173
- if (!hasVariant) return;
174
- const options = {};
175
- for(let axis = 1; axis <= 3; axis += 1){
176
- var _product_options_;
177
- const name = cell(row, `option${axis} name`);
178
- const value = cell(row, `option${axis} value`);
179
- if (name && product.options[axis - 1] == null) {
180
- product.options[axis - 1] = {
181
- name,
182
- values: []
183
- };
184
- }
185
- const optionName = (_product_options_ = product.options[axis - 1]) == null ? void 0 : _product_options_.name;
186
- if (optionName && value) {
187
- options[optionName] = value;
188
- const axisOption = product.options[axis - 1];
189
- if (!axisOption.values.includes(value)) {
190
- axisOption.values.push(value);
191
- }
192
- }
193
- }
194
- const price = Number(priceRaw);
195
- if (!Number.isFinite(price) || price < 0) {
196
- errors.push(`Row ${line}: invalid Variant Price "${priceRaw}"`);
197
- return;
198
- }
199
- const qtyRaw = cell(row, 'variant inventory qty');
200
- const compareRaw = cell(row, 'variant compare at price');
201
- const gramsRaw = cell(row, 'variant grams');
202
- const variant = _extends({
203
- id: `v${product.variants.length + 1}`
204
- }, Object.keys(options).length ? {
205
- options
206
- } : {}, {
207
- priceUsd: price
208
- }, cell(row, 'variant sku') ? {
209
- sku: cell(row, 'variant sku')
210
- } : {}, cell(row, 'variant barcode') ? {
211
- barcode: cell(row, 'variant barcode')
212
- } : {}, compareRaw && Number.isFinite(Number(compareRaw)) ? {
213
- compareAtPriceUsd: Number(compareRaw)
214
- } : {}, {
215
- inventory: qtyRaw === '' ? null : Math.max(0, Math.round(Number(qtyRaw)))
216
- }, gramsRaw && Number.isFinite(Number(gramsRaw)) ? {
217
- weightGrams: Number(gramsRaw)
218
- } : {});
219
- product.variants.push(variant);
220
- });
221
- const products = [];
222
- for (const [handle, product] of byHandle){
223
- var _product_options, _ref;
224
- var _product_mediaUrls;
225
- product.options = ((_product_options = product.options) != null ? _product_options : []).filter(Boolean);
226
- if (product.options.length === 0) delete product.options;
227
- if (((_ref = (_product_mediaUrls = product.mediaUrls) == null ? void 0 : _product_mediaUrls.length) != null ? _ref : 0) === 0) delete product.mediaUrls;
228
- if (product.variants.length === 0) {
229
- errors.push(`"${handle}": no variant rows (needs Variant Price)`);
230
- continue;
231
- }
232
- const problem = validateProduct(product);
233
- if (problem) {
234
- errors.push(`"${handle}": ${problem}`);
235
- continue;
236
- }
237
- products.push(product);
238
- }
239
- return {
240
- products,
241
- errors
242
- };
243
- }
244
-
245
- //# sourceMappingURL=commerce-io.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/commerce-io.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Catalog CSV import/export (AGL-282) speaking Shopify's product CSV\n * dialect — the migration wedge for switchers: `Handle` groups variant\n * rows into one product, `Option1..3 Name/Value` build the option axes,\n * `Variant *` columns carry per-variant data, and repeated-handle rows\n * with only `Image Src` add media. Export mirrors the same columns so\n * round-trips are lossless for the fields both sides share.\n */\n\nimport {\n commerceSlug,\n validateProduct,\n type HostProduct,\n type ProductOption,\n type ProductStatus,\n type ProductVariant,\n} from './commerce'\nimport { parseCsv } from '@aglyn/aglyn/app-utils/csv'\n\nexport const PRODUCT_CSV_HEADER = [\n 'Handle',\n 'Title',\n 'Body (HTML)',\n 'Type',\n 'Tags',\n 'Published',\n 'Option1 Name',\n 'Option1 Value',\n 'Option2 Name',\n 'Option2 Value',\n 'Option3 Name',\n 'Option3 Value',\n 'Variant SKU',\n 'Variant Barcode',\n 'Variant Price',\n 'Variant Compare At Price',\n 'Variant Inventory Qty',\n 'Variant Grams',\n 'Image Src',\n] as const\n\nfunction csvCell(value: unknown): string {\n const text = value == null ? '' : String(value)\n return /[\",\\n]/.test(text) ? `\"${text.replace(/\"/g, '\"\"')}\"` : text\n}\n\n/** One row per variant; extra image rows follow Shopify's convention. */\nexport function productsToCsv(\n products: Array<HostProduct & { $id?: string }>,\n): string {\n const lines = [PRODUCT_CSV_HEADER.join(',')]\n for (const product of products) {\n const options = product.options ?? []\n product.variants.forEach((variant, index) => {\n const optionCells: string[] = []\n for (let axis = 0; axis < 3; axis += 1) {\n const option = options[axis]\n optionCells.push(\n index === 0 ? (option?.name ?? '') : '',\n option ? (variant.options?.[option.name] ?? '') : '',\n )\n }\n lines.push(\n [\n csvCell(product.slug),\n csvCell(index === 0 ? product.name : ''),\n csvCell(index === 0 ? (product.description ?? '') : ''),\n csvCell(index === 0 ? product.type : ''),\n csvCell(index === 0 ? (product.tags ?? []).join(', ') : ''),\n csvCell(index === 0 ? String(product.status === 'active') : ''),\n ...optionCells.map(csvCell),\n csvCell(variant.sku ?? ''),\n csvCell(variant.barcode ?? ''),\n csvCell(variant.priceUsd),\n csvCell(variant.compareAtPriceUsd ?? ''),\n csvCell(variant.inventory ?? ''),\n csvCell(variant.weightGrams ?? ''),\n csvCell(index === 0 ? (product.mediaUrls?.[0] ?? '') : ''),\n ].join(','),\n )\n })\n for (const url of (product.mediaUrls ?? []).slice(1)) {\n lines.push(\n [\n csvCell(product.slug),\n ...Array(PRODUCT_CSV_HEADER.length - 2).fill(''),\n csvCell(url),\n ].join(','),\n )\n }\n }\n return lines.join('\\n')\n}\n\nexport interface ProductCsvImport {\n products: HostProduct[]\n /** Row-anchored problems; importable products still parse. */\n errors: string[]\n}\n\n/** Case-insensitive column lookup so hand-edited headers still map. */\nfunction headerIndex(header: string[]): Map<string, number> {\n const map = new Map<string, number>()\n header.forEach((cell, index) =>\n map.set(cell.trim().toLowerCase(), index),\n )\n return map\n}\n\nexport function parseProductsCsv(text: string): ProductCsvImport {\n const rows = parseCsv(text)\n if (rows.length < 2) {\n return { products: [], errors: ['No data rows found'] }\n }\n const columns = headerIndex(rows[0])\n const cell = (row: string[], name: string): string => {\n const index = columns.get(name.toLowerCase())\n return index == null ? '' : (row[index] ?? '').trim()\n }\n if (!columns.has('handle') || !columns.has('title')) {\n return {\n products: [],\n errors: ['Missing required columns: Handle, Title'],\n }\n }\n\n const byHandle = new Map<string, HostProduct>()\n const errors: string[] = []\n rows.slice(1).forEach((row, rowIndex) => {\n const line = rowIndex + 2\n const handle = commerceSlug(cell(row, 'handle'))\n if (!handle) {\n if (row.some((value) => value.trim())) {\n errors.push(`Row ${line}: missing Handle`)\n }\n return\n }\n let product = byHandle.get(handle)\n const title = cell(row, 'title')\n if (!product) {\n if (!title) {\n // Image-only continuation row for a product we never saw.\n errors.push(`Row ${line}: first row for \"${handle}\" needs a Title`)\n return\n }\n const published = cell(row, 'published').toLowerCase()\n const type = cell(row, 'type').toLowerCase()\n product = {\n name: title,\n slug: handle,\n description: cell(row, 'body (html)') || undefined,\n type: ['physical', 'digital', 'service'].includes(type)\n ? (type as HostProduct['type'])\n : 'physical',\n status: (published === 'false'\n ? 'draft'\n : 'active') as ProductStatus,\n tags: cell(row, 'tags')\n ? cell(row, 'tags')\n .split(',')\n .map((tag) => tag.trim())\n .filter(Boolean)\n : undefined,\n options: [],\n variants: [],\n mediaUrls: [],\n }\n byHandle.set(handle, product)\n }\n\n const imageSrc = cell(row, 'image src')\n if (imageSrc && !(product.mediaUrls ?? []).includes(imageSrc)) {\n product.mediaUrls = [...(product.mediaUrls ?? []), imageSrc]\n }\n\n const priceRaw = cell(row, 'variant price')\n const hasVariant = priceRaw !== '' || cell(row, 'variant sku') !== ''\n if (!hasVariant) return\n\n const options: Record<string, string> = {}\n for (let axis = 1; axis <= 3; axis += 1) {\n const name = cell(row, `option${axis} name`)\n const value = cell(row, `option${axis} value`)\n if (name && product.options![axis - 1] == null) {\n product.options![axis - 1] = { name, values: [] }\n }\n const optionName = product.options![axis - 1]?.name\n if (optionName && value) {\n options[optionName] = value\n const axisOption = product.options![axis - 1] as ProductOption\n if (!axisOption.values.includes(value)) {\n axisOption.values.push(value)\n }\n }\n }\n\n const price = Number(priceRaw)\n if (!Number.isFinite(price) || price < 0) {\n errors.push(`Row ${line}: invalid Variant Price \"${priceRaw}\"`)\n return\n }\n const qtyRaw = cell(row, 'variant inventory qty')\n const compareRaw = cell(row, 'variant compare at price')\n const gramsRaw = cell(row, 'variant grams')\n const variant: ProductVariant = {\n id: `v${product.variants.length + 1}`,\n ...(Object.keys(options).length ? { options } : {}),\n priceUsd: price,\n ...(cell(row, 'variant sku') ? { sku: cell(row, 'variant sku') } : {}),\n ...(cell(row, 'variant barcode')\n ? { barcode: cell(row, 'variant barcode') }\n : {}),\n ...(compareRaw && Number.isFinite(Number(compareRaw))\n ? { compareAtPriceUsd: Number(compareRaw) }\n : {}),\n inventory: qtyRaw === '' ? null : Math.max(0, Math.round(Number(qtyRaw))),\n ...(gramsRaw && Number.isFinite(Number(gramsRaw))\n ? { weightGrams: Number(gramsRaw) }\n : {}),\n }\n product.variants.push(variant)\n })\n\n const products: HostProduct[] = []\n for (const [handle, product] of byHandle) {\n product.options = (product.options ?? []).filter(Boolean)\n if (product.options.length === 0) delete product.options\n if ((product.mediaUrls?.length ?? 0) === 0) delete product.mediaUrls\n if (product.variants.length === 0) {\n errors.push(`\"${handle}\": no variant rows (needs Variant Price)`)\n continue\n }\n const problem = validateProduct(product)\n if (problem) {\n errors.push(`\"${handle}\": ${problem}`)\n continue\n }\n products.push(product)\n }\n return { products, errors }\n}\n"],"names":["commerceSlug","validateProduct","parseCsv","PRODUCT_CSV_HEADER","csvCell","value","text","String","test","replace","productsToCsv","products","lines","join","product","options","variants","forEach","variant","index","optionCells","axis","option","push","name","slug","description","type","tags","status","map","sku","barcode","priceUsd","compareAtPriceUsd","inventory","weightGrams","mediaUrls","url","slice","Array","length","fill","headerIndex","header","Map","cell","set","trim","toLowerCase","parseProductsCsv","rows","errors","columns","row","get","has","byHandle","rowIndex","line","handle","some","title","published","undefined","includes","split","tag","filter","Boolean","imageSrc","priceRaw","hasVariant","values","optionName","axisOption","price","Number","isFinite","qtyRaw","compareRaw","gramsRaw","id","Object","keys","Math","max","round","problem"],"mappings":"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"}
@@ -1,117 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2026 Aglyn LLC
4
- *
5
- * Licensed under the Apache License, Version 2.0 (the "License");
6
- * you may not use this file except in compliance with the License.
7
- * You may obtain a copy of the License at
8
- *
9
- * http://www.apache.org/licenses/LICENSE-2.0
10
- *
11
- * Unless required by applicable law or agreed to in writing, software
12
- * distributed under the License is distributed on an "AS IS" BASIS,
13
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
- * See the License for the specific language governing permissions and
15
- * limitations under the License.
16
- */
17
- /**
18
- * Money going BACK to a customer, recorded on their contact (AGL-1754).
19
- *
20
- * `upsertHostContact` accumulates `ltvCents` and nothing anywhere decremented
21
- * it, so a customer who bought $500 and returned all of it read identically to
22
- * one who bought $500 and kept it — and the first surface built on the field
23
- * (RFM segmentation, a lifetime-value column, a best-customers list) would have
24
- * ranked a serial returner among the merchant's best customers.
25
- *
26
- * WHAT THIS WRITES, AND WHY IT IS NOT A DECREMENT
27
- *
28
- * `ltvCents` is left exactly as it is and the reversal is recorded BESIDE it:
29
- *
30
- * | field | meaning |
31
- * | -- | -- |
32
- * | `ltvCents` | gross money handed over, unchanged (AGL-1748) |
33
- * | `refundedCents` | gross money handed back |
34
- * | `ordersCount` | orders placed, unchanged |
35
- * | `refundedOrdersCount` | of those, how many ended fully reversed |
36
- * | `lastRefundAtMs` | when money last went back |
37
- *
38
- * This is the shape AGL-1747 chose two days earlier for the same question on
39
- * the orders CSV — `amountUsd` stayed gross under its existing name and
40
- * `refundedUsd`/`netUsd` were appended — and it is the shape AGL-1748 named
41
- * when it recorded the gross semantics in `upsert-contact.ts`. Netting in
42
- * place would make a stored number mean one thing for rows written before this
43
- * commit and another for rows written after, with nothing on the document to
44
- * tell the two apart. Two fields cost one number's worth of storage and keep
45
- * both facts, and a reader that wants the net has `ltvCents - refundedCents`.
46
- *
47
- * It also answers "can it go negative?" by moving the question off storage.
48
- * Both figures are monotonic counters of money that actually moved in one
49
- * direction, so neither can go negative. The DERIVED net can, for exactly one
50
- * reason: an order placed before AGL-1748 taught POS, drafts, reservations and
51
- * bookings to pass `purchaseCents` at all, whose sale was never added and whose
52
- * refund is subtracted here. That is a true statement about the data, and
53
- * clamping it away at write time would erase the only evidence that a purchase
54
- * is missing. AGL-1753 is the backfill that reconciles it, by SETTING both
55
- * fields from the orders — which already carry `refundedCents`, so whatever a
56
- * reader decides here costs the rebuild nothing. A merchant-facing reader
57
- * should show gross and refunded, and clamp only the net it ranks on.
58
- *
59
- * `ordersCount` is deliberately NOT decremented. A fully refunded order still
60
- * happened, and a decrement cannot be undone by a reader that would rather
61
- * count it — while `refundedOrdersCount` beside it lets RFM's frequency term
62
- * net the two, or not, at the point of display. Same argument as the amount,
63
- * one field along.
64
- *
65
- * WHEN THE CONTACT IS MISSING, THIS REFUSES — and records the refusal.
66
- *
67
- * Refusing is right on AGL-1732's test, "does refusing discard money or work
68
- * that already happened?", because it does not: the refund is durable on the
69
- * order (`refundedCents`, the timeline entry, Stripe), and the contact is a
70
- * derived aggregate that AGL-1753's rebuild reads back out of those same
71
- * orders. Creating one here would be worse three ways — creation is audience
72
- * BAND gated, so it hard-bands a Free org (raising the AGL-891 dropped-contacts
73
- * alert for a record the merchant never had) or meters onto a paid invoice; the
74
- * contact it created would hold a refund and no purchase, which is the negative
75
- * lifetime value above with nothing to reconcile it against; and it is the
76
- * phantom-document shape the `updateExisting` sweep spent yesterday closing.
77
- *
78
- * So the write is an `update()`, never `set(…, { merge: true })` — the contact
79
- * may have been deleted between the query and the write, and a merge-set would
80
- * resurrect it as a document holding nothing but a refund. Every refusal
81
- * increments `hosts/{hostId}/counters/contactRefundsUnmatched`, the counter
82
- * shape `contactsDropped` already uses, so a refund that reached no contact is
83
- * countable rather than silent.
84
- *
85
- * CHARGEBACKS (AGL-1554) are a separate decision and are NOT handled here.
86
- * This takes `kind` so the handler that eventually answers
87
- * `charge.dispute.closed` can record a lost dispute through the same writer
88
- * with the same fields: money reversed is money reversed, whichever door it
89
- * left by, and only the wording of the timeline entry differs.
90
- */
91
- export declare function recordContactRefund(options: {
92
- hostId: string;
93
- /** The order the money went back on — the contact timeline's `refId`. */
94
- orderId: string;
95
- /**
96
- * The buyer as the ORDER recorded them, raw. Normalized here, because that
97
- * is the key the contacts collection is stored under and orders are not
98
- * consistent about it (AGL-1753 item 3). No `name` rides along on purpose: a
99
- * refund is not a capture point and must not rewrite who the contact is.
100
- */
101
- email: unknown;
102
- /**
103
- * Cents reversed by THIS attempt, never the order total. `refund.ts` caps
104
- * each attempt against what is left, so several partials on one order sum to
105
- * at most the order total; this follows that number rather than recomputing
106
- * it.
107
- */
108
- amountCents: number;
109
- /**
110
- * True only for the write that moved the order INTO `refunded`. Two partials
111
- * settling concurrently can both observe a fully refunded total, so the
112
- * caller decides this from the status transition it made, not from the
113
- * total it read.
114
- */
115
- closedTheOrder: boolean;
116
- kind?: 'refund' | 'chargeback';
117
- }): Promise<void>;