@aglyn/plugins-commerce 1.0.0-beta.218 → 1.0.0-beta.220

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 (148) hide show
  1. package/README.md +6 -0
  2. package/package.json +14 -14
  3. package/src/index.d.ts +1 -0
  4. package/src/index.js +1 -19
  5. package/src/index.js.map +1 -1
  6. package/src/lib/components/cart.js +2 -1
  7. package/src/lib/components/cart.js.map +1 -1
  8. package/src/lib/components/console/catalog-organization-card.component.js +665 -459
  9. package/src/lib/components/console/catalog-organization-card.component.js.map +1 -1
  10. package/src/lib/components/console/host-orders-card.component.js +2 -1
  11. package/src/lib/components/console/host-orders-card.component.js.map +1 -1
  12. package/src/lib/components/console/member-posts-card.component.js +1 -1
  13. package/src/lib/components/console/member-posts-card.component.js.map +1 -1
  14. package/src/lib/components/console/order-detail-dialog.component.js +8 -7
  15. package/src/lib/components/console/order-detail-dialog.component.js.map +1 -1
  16. package/src/lib/components/console/payments-settings-card.component.js +2 -2
  17. package/src/lib/components/console/payments-settings-card.component.js.map +1 -1
  18. package/src/lib/components/console/product-editor-dialog.component.js +1221 -889
  19. package/src/lib/components/console/product-editor-dialog.component.js.map +1 -1
  20. package/src/lib/components/console/product-zones.d.ts +184 -0
  21. package/src/lib/components/console/product-zones.js +54 -0
  22. package/src/lib/components/console/product-zones.js.map +1 -0
  23. package/src/lib/components/console/products-hub-card.component.js +3 -2
  24. package/src/lib/components/console/products-hub-card.component.js.map +1 -1
  25. package/src/lib/components/console/products-hub-zone.component.d.ts +1 -1
  26. package/src/lib/components/console/products-hub-zone.component.js +2 -1
  27. package/src/lib/components/console/products-hub-zone.component.js.map +1 -1
  28. package/src/lib/components/console/site-member-purchases.component.d.ts +24 -0
  29. package/src/lib/components/console/site-member-purchases.component.js +339 -0
  30. package/src/lib/components/console/site-member-purchases.component.js.map +1 -0
  31. package/src/lib/components/console/store-settings-card.component.js +2 -2
  32. package/src/lib/components/console/store-settings-card.component.js.map +1 -1
  33. package/src/lib/components/console/storefront-tax-summary-card.component.js +1 -1
  34. package/src/lib/components/console/storefront-tax-summary-card.component.js.map +1 -1
  35. package/src/lib/components/member-signup.js +2 -2
  36. package/src/lib/components/member-signup.js.map +1 -1
  37. package/src/lib/components/newsletter-signup.js +2 -2
  38. package/src/lib/components/newsletter-signup.js.map +1 -1
  39. package/src/lib/components/product-detail.js +2 -1
  40. package/src/lib/components/product-detail.js.map +1 -1
  41. package/src/lib/components/product-grid.js +1 -1
  42. package/src/lib/components/product-grid.js.map +1 -1
  43. package/src/lib/constants/operator-alerts.js +5 -3
  44. package/src/lib/constants/operator-alerts.js.map +1 -1
  45. package/src/lib/constants/storefront-payments.d.ts +33 -0
  46. package/src/lib/constants/storefront-payments.js +35 -0
  47. package/src/lib/constants/storefront-payments.js.map +1 -0
  48. package/src/lib/declarations.server.d.ts +3 -1
  49. package/src/lib/declarations.server.js +19 -1
  50. package/src/lib/declarations.server.js.map +1 -1
  51. package/src/lib/model/commerce-io.js +1 -1
  52. package/src/lib/model/commerce-io.js.map +1 -1
  53. package/src/lib/model/commerce-record-lists.d.ts +31 -0
  54. package/src/lib/model/commerce-record-lists.js +45 -0
  55. package/src/lib/model/commerce-record-lists.js.map +1 -0
  56. package/src/lib/model/commerce-record-routes.d.ts +45 -0
  57. package/src/lib/model/commerce-record-routes.js +72 -0
  58. package/src/lib/model/commerce-record-routes.js.map +1 -0
  59. package/src/lib/model/commerce-search-sources.d.ts +21 -0
  60. package/src/lib/model/commerce-search-sources.js +42 -0
  61. package/src/lib/model/commerce-search-sources.js.map +1 -0
  62. package/src/lib/model/commerce-topics.d.ts +20 -0
  63. package/src/lib/model/commerce-topics.js +33 -0
  64. package/src/lib/model/commerce-topics.js.map +1 -0
  65. package/src/lib/model/index.d.ts +1 -0
  66. package/src/lib/model/index.js +1 -0
  67. package/src/lib/model/index.js.map +1 -1
  68. package/src/lib/model/product-record.d.ts +32 -0
  69. package/src/lib/model/product-record.js +82 -0
  70. package/src/lib/model/product-record.js.map +1 -0
  71. package/src/lib/model/site-member-purchases.d.ts +48 -0
  72. package/src/lib/model/site-member-purchases.js +50 -0
  73. package/src/lib/model/site-member-purchases.js.map +1 -0
  74. package/src/lib/model/storefront-tax-summary.d.ts +121 -0
  75. package/src/lib/model/storefront-tax-summary.js +148 -0
  76. package/src/lib/model/storefront-tax-summary.js.map +1 -0
  77. package/src/lib/plan-entitlements.d.ts +35 -0
  78. package/src/lib/plan-entitlements.js +54 -0
  79. package/src/lib/plan-entitlements.js.map +1 -0
  80. package/src/lib/plugin.js +21 -0
  81. package/src/lib/plugin.js.map +1 -1
  82. package/src/lib/server/billing-webhook.js +387 -248
  83. package/src/lib/server/billing-webhook.js.map +1 -1
  84. package/src/lib/server/cart-checkout.js +3 -3
  85. package/src/lib/server/cart-checkout.js.map +1 -1
  86. package/src/lib/server/checkout.js +2 -2
  87. package/src/lib/server/checkout.js.map +1 -1
  88. package/src/lib/server/connect.js +5 -5
  89. package/src/lib/server/connect.js.map +1 -1
  90. package/src/lib/server/draft-order.js +2 -2
  91. package/src/lib/server/draft-order.js.map +1 -1
  92. package/src/lib/server/gift-cards.js +7 -3
  93. package/src/lib/server/gift-cards.js.map +1 -1
  94. package/src/lib/server/low-stock.js +3 -1
  95. package/src/lib/server/low-stock.js.map +1 -1
  96. package/src/lib/server/member-post.js +2 -2
  97. package/src/lib/server/member-post.js.map +1 -1
  98. package/src/lib/server/membership-admin-password.js +4 -1
  99. package/src/lib/server/membership-admin-password.js.map +1 -1
  100. package/src/lib/server/membership-content.js +1 -1
  101. package/src/lib/server/membership-content.js.map +1 -1
  102. package/src/lib/server/membership-register.js +13 -2
  103. package/src/lib/server/membership-register.js.map +1 -1
  104. package/src/lib/server/newsletter.js +32 -19
  105. package/src/lib/server/newsletter.js.map +1 -1
  106. package/src/lib/server/pos-order.js +3 -3
  107. package/src/lib/server/pos-order.js.map +1 -1
  108. package/src/lib/server/process-abandoned.js +4 -2
  109. package/src/lib/server/process-abandoned.js.map +1 -1
  110. package/src/lib/server/process-restock.js +7 -5
  111. package/src/lib/server/process-restock.js.map +1 -1
  112. package/src/lib/server/product-record-index.d.ts +19 -0
  113. package/src/lib/server/product-record-index.js +83 -0
  114. package/src/lib/server/product-record-index.js.map +1 -0
  115. package/src/lib/server/reconcile-stock.js +1 -1
  116. package/src/lib/server/reconcile-stock.js.map +1 -1
  117. package/src/lib/server/reserve-stock.js +6 -1
  118. package/src/lib/server/reserve-stock.js.map +1 -1
  119. package/src/lib/server/reserve.js +2 -2
  120. package/src/lib/server/reserve.js.map +1 -1
  121. package/src/lib/server/revenue-source.d.ts +130 -0
  122. package/src/lib/server/revenue-source.js +286 -0
  123. package/src/lib/server/revenue-source.js.map +1 -0
  124. package/src/lib/server/supplier-outbox.js +16 -1
  125. package/src/lib/server/supplier-outbox.js.map +1 -1
  126. package/src/lib/server/supplier-update.js +19 -9
  127. package/src/lib/server/supplier-update.js.map +1 -1
  128. package/src/lib/server/tax-profile.d.ts +8 -5
  129. package/src/lib/server/tax-profile.js +22 -5
  130. package/src/lib/server/tax-profile.js.map +1 -1
  131. package/src/lib/server/tax-return-source.d.ts +125 -0
  132. package/src/lib/server/tax-return-source.js +524 -0
  133. package/src/lib/server/tax-return-source.js.map +1 -0
  134. package/src/lib/server/tax-summary.d.ts +92 -0
  135. package/src/lib/server/tax-summary.js +258 -0
  136. package/src/lib/server/tax-summary.js.map +1 -0
  137. package/src/lib/server.d.ts +1 -0
  138. package/src/lib/server.js +23 -0
  139. package/src/lib/server.js.map +1 -1
  140. package/src/lib/starter-templates.d.ts +19 -0
  141. package/src/lib/starter-templates.js +157 -0
  142. package/src/lib/starter-templates.js.map +1 -0
  143. package/src/lib/tenant-emails.d.ts +33 -0
  144. package/src/lib/tenant-emails.js +551 -0
  145. package/src/lib/tenant-emails.js.map +1 -0
  146. package/src/lib/testing/stand-in-crm-record-routes.d.ts +25 -0
  147. package/src/lib/testing/stand-in-crm-record-routes.js +46 -0
  148. package/src/lib/testing/stand-in-crm-record-routes.js.map +1 -0
@@ -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
+ import { type PluginRecordListSource } from '@aglyn/aglyn/plugin-manager/plugin-record-lists';
18
+ /**
19
+ * A site's products, FOUND in the console for another plugin's picker
20
+ * (AGL-3080) — a deal adding a product as a line, priced from the catalog.
21
+ *
22
+ * A search, not a listing: the first word typed is matched as a prefix of a
23
+ * word in an ACTIVE product's name, ordered by name — a draft or archived
24
+ * product is not offered for sale. With nothing typed, at the organization,
25
+ * where no catalog lives, and for a listing, from which no product is
26
+ * installed, the source answers none. Each record is `productIndexedRecord`'s,
27
+ * the server index's shape, with its price and its priced variants.
28
+ */
29
+ export declare const productRecordListSource: PluginRecordListSource;
30
+ /** Called from the console registrar, owner named for a spec that calls it directly. */
31
+ export declare function registerCommerceRecordLists(): void;
@@ -0,0 +1,45 @@
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
+ */ import { nameSearchToken } from "@aglyn/aglyn/app-utils/name-search";
17
+ import { registerPluginRecordListSource } from "@aglyn/aglyn/plugin-manager/plugin-record-lists";
18
+ import { collection, limit, orderBy, query, where } from "firebase/firestore";
19
+ import { BUNDLE_ID } from "../constants/bundle-common.js";
20
+ import { productIndexedRecord } from "./product-record.js";
21
+ /**
22
+ * A site's products, FOUND in the console for another plugin's picker
23
+ * (AGL-3080) — a deal adding a product as a line, priced from the catalog.
24
+ *
25
+ * A search, not a listing: the first word typed is matched as a prefix of a
26
+ * word in an ACTIVE product's name, ordered by name — a draft or archived
27
+ * product is not offered for sale. With nothing typed, at the organization,
28
+ * where no catalog lives, and for a listing, from which no product is
29
+ * installed, the source answers none. Each record is `productIndexedRecord`'s,
30
+ * the server index's shape, with its price and its priced variants.
31
+ */ export const productRecordListSource = {
32
+ query (firestore, request) {
33
+ const token = nameSearchToken(request.search);
34
+ if (!request.hostId || !token || request.installedFrom) return null;
35
+ return query(collection(firestore, 'hosts', request.hostId, 'products'), where('status', '==', 'active'), where('nameTokens', 'array-contains', token), orderBy('nameLower'), limit(request.limit));
36
+ },
37
+ record: productIndexedRecord
38
+ };
39
+ /** Called from the console registrar, owner named for a spec that calls it directly. */ export function registerCommerceRecordLists() {
40
+ registerPluginRecordListSource('product', productRecordListSource, {
41
+ pluginId: BUNDLE_ID
42
+ });
43
+ }
44
+
45
+ //# sourceMappingURL=commerce-record-lists.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/commerce-record-lists.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\nimport { nameSearchToken } from '@aglyn/aglyn/app-utils/name-search'\nimport {\n registerPluginRecordListSource,\n type PluginRecordListSource,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-lists'\nimport { collection, limit, orderBy, query, where } from 'firebase/firestore'\nimport { BUNDLE_ID } from '../constants/bundle-common'\nimport { productIndexedRecord } from './product-record'\n\n/**\n * A site's products, FOUND in the console for another plugin's picker\n * (AGL-3080) — a deal adding a product as a line, priced from the catalog.\n *\n * A search, not a listing: the first word typed is matched as a prefix of a\n * word in an ACTIVE product's name, ordered by name — a draft or archived\n * product is not offered for sale. With nothing typed, at the organization,\n * where no catalog lives, and for a listing, from which no product is\n * installed, the source answers none. Each record is `productIndexedRecord`'s,\n * the server index's shape, with its price and its priced variants.\n */\nexport const productRecordListSource: PluginRecordListSource = {\n query(firestore, request) {\n const token = nameSearchToken(request.search)\n if (!request.hostId || !token || request.installedFrom) return null\n return query(\n collection(firestore, 'hosts', request.hostId, 'products'),\n where('status', '==', 'active'),\n where('nameTokens', 'array-contains', token),\n orderBy('nameLower'),\n limit(request.limit),\n )\n },\n record: productIndexedRecord,\n}\n\n/** Called from the console registrar, owner named for a spec that calls it directly. */\nexport function registerCommerceRecordLists(): void {\n registerPluginRecordListSource('product', productRecordListSource, { pluginId: BUNDLE_ID })\n}\n"],"names":["nameSearchToken","registerPluginRecordListSource","collection","limit","orderBy","query","where","BUNDLE_ID","productIndexedRecord","productRecordListSource","firestore","request","token","search","hostId","installedFrom","record","registerCommerceRecordLists","pluginId"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,eAAe,QAAQ,qCAAoC;AACpE,SACEC,8BAA8B,QAEzB,kDAAiD;AACxD,SAASC,UAAU,EAAEC,KAAK,EAAEC,OAAO,EAAEC,KAAK,EAAEC,KAAK,QAAQ,qBAAoB;AAC7E,SAASC,SAAS,QAAQ,gCAA4B;AACtD,SAASC,oBAAoB,QAAQ,sBAAkB;AAEvD;;;;;;;;;;CAUC,GACD,OAAO,MAAMC,0BAAkD;IAC7DJ,OAAMK,SAAS,EAAEC,OAAO;QACtB,MAAMC,QAAQZ,gBAAgBW,QAAQE,MAAM;QAC5C,IAAI,CAACF,QAAQG,MAAM,IAAI,CAACF,SAASD,QAAQI,aAAa,EAAE,OAAO;QAC/D,OAAOV,MACLH,WAAWQ,WAAW,SAASC,QAAQG,MAAM,EAAE,aAC/CR,MAAM,UAAU,MAAM,WACtBA,MAAM,cAAc,kBAAkBM,QACtCR,QAAQ,cACRD,MAAMQ,QAAQR,KAAK;IAEvB;IACAa,QAAQR;AACV,EAAC;AAED,sFAAsF,GACtF,OAAO,SAASS;IACdhB,+BAA+B,WAAWQ,yBAAyB;QAAES,UAAUX;IAAU;AAC3F"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ * Unless required by applicable law or agreed to in writing, software
9
+ * distributed under the License is distributed on an "AS IS" BASIS,
10
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ * See the License for the specific language governing permissions and
12
+ * limitations under the License.
13
+ */
14
+ /**
15
+ * Where a product and an order are read, published for every other surface
16
+ * (AGL-3080).
17
+ *
18
+ * A surface elsewhere — the AI plugin's job drawer opening a product it
19
+ * drafted, a contact's timeline opening the order it names, a person's page
20
+ * listing their orders — asks the record-route registry for `product` or
21
+ * `order` rather than spelling this plugin's console path, and gets `null`
22
+ * (text instead of a link) where this plugin is not loaded.
23
+ *
24
+ * Both are a site's, so only a site has an address. The catalog has no page
25
+ * per product — a product opens in the hub's editor — so the record answers
26
+ * the catalog a caller would find it in. An order opens as a dialog over the
27
+ * Orders list, which reads {@link ORDERS_ORDER_PARAM} on arrival; the list
28
+ * narrowed to one buyer reads {@link ORDERS_CUSTOMER_PARAM}.
29
+ */
30
+ /** The nav slug the shell resolves the catalog by. */
31
+ export declare const PRODUCTS_SLUG = "products";
32
+ /** The query key the Orders list opens one order's dialog by. */
33
+ export declare const ORDERS_ORDER_PARAM = "order";
34
+ /**
35
+ * The query key the Orders list narrows to one buyer by — the same word the
36
+ * CRM's contacts list opens a person by, so a reader who edits one URL into
37
+ * the other is not surprised.
38
+ */
39
+ export declare const ORDERS_CUSTOMER_PARAM = "email";
40
+ /**
41
+ * Called from the console registrar. The owner is named rather than read off
42
+ * the loader's marker, so the route registers the same way under a spec that
43
+ * calls the registrar directly.
44
+ */
45
+ export declare function registerCommerceRecordRoutes(): void;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ * Unless required by applicable law or agreed to in writing, software
9
+ * distributed under the License is distributed on an "AS IS" BASIS,
10
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ * See the License for the specific language governing permissions and
12
+ * limitations under the License.
13
+ */ import { buildRoute, Route } from "@aglyn/aglyn/app-utils/console-routes";
14
+ import { registerPluginRecordRoute } from "@aglyn/aglyn/plugin-manager/plugin-record-routes";
15
+ import { BUNDLE_ID } from "../constants/bundle-common.js";
16
+ /**
17
+ * Where a product and an order are read, published for every other surface
18
+ * (AGL-3080).
19
+ *
20
+ * A surface elsewhere — the AI plugin's job drawer opening a product it
21
+ * drafted, a contact's timeline opening the order it names, a person's page
22
+ * listing their orders — asks the record-route registry for `product` or
23
+ * `order` rather than spelling this plugin's console path, and gets `null`
24
+ * (text instead of a link) where this plugin is not loaded.
25
+ *
26
+ * Both are a site's, so only a site has an address. The catalog has no page
27
+ * per product — a product opens in the hub's editor — so the record answers
28
+ * the catalog a caller would find it in. An order opens as a dialog over the
29
+ * Orders list, which reads {@link ORDERS_ORDER_PARAM} on arrival; the list
30
+ * narrowed to one buyer reads {@link ORDERS_CUSTOMER_PARAM}.
31
+ */ /** The nav slug the shell resolves the catalog by. */ export const PRODUCTS_SLUG = 'products';
32
+ /** The query key the Orders list opens one order's dialog by. */ export const ORDERS_ORDER_PARAM = 'order';
33
+ /**
34
+ * The query key the Orders list narrows to one buyer by — the same word the
35
+ * CRM's contacts list opens a person by, so a reader who edits one URL into
36
+ * the other is not surprised.
37
+ */ export const ORDERS_CUSTOMER_PARAM = 'email';
38
+ function catalog(context) {
39
+ return context.host ? buildRoute(Route.HOST_PLUGIN, {
40
+ orgSlug: context.orgSlug,
41
+ host: context.host,
42
+ pluginSlug: PRODUCTS_SLUG
43
+ }) : null;
44
+ }
45
+ function orders(context, key, value) {
46
+ const list = catalog(context);
47
+ if (!list) return null;
48
+ return key && value ? `${list}/orders?${new URLSearchParams({
49
+ [key]: value
50
+ }).toString()}` : `${list}/orders`;
51
+ }
52
+ /**
53
+ * Called from the console registrar. The owner is named rather than read off
54
+ * the loader's marker, so the route registers the same way under a spec that
55
+ * calls the registrar directly.
56
+ */ export function registerCommerceRecordRoutes() {
57
+ registerPluginRecordRoute('product', {
58
+ list: catalog,
59
+ record: catalog
60
+ }, {
61
+ pluginId: BUNDLE_ID
62
+ });
63
+ registerPluginRecordRoute('order', {
64
+ list: (context)=>orders(context),
65
+ record: (context, id)=>orders(context, ORDERS_ORDER_PARAM, id),
66
+ byEmail: (context, email)=>orders(context, ORDERS_CUSTOMER_PARAM, email)
67
+ }, {
68
+ pluginId: BUNDLE_ID
69
+ });
70
+ }
71
+
72
+ //# sourceMappingURL=commerce-record-routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/commerce-record-routes.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\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 * http://www.apache.org/licenses/LICENSE-2.0\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\nimport { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport {\n registerPluginRecordRoute,\n type PluginRecordRouteContext,\n} from '@aglyn/aglyn/plugin-manager/plugin-record-routes'\nimport { BUNDLE_ID } from '../constants/bundle-common'\n\n/**\n * Where a product and an order are read, published for every other surface\n * (AGL-3080).\n *\n * A surface elsewhere — the AI plugin's job drawer opening a product it\n * drafted, a contact's timeline opening the order it names, a person's page\n * listing their orders — asks the record-route registry for `product` or\n * `order` rather than spelling this plugin's console path, and gets `null`\n * (text instead of a link) where this plugin is not loaded.\n *\n * Both are a site's, so only a site has an address. The catalog has no page\n * per product — a product opens in the hub's editor — so the record answers\n * the catalog a caller would find it in. An order opens as a dialog over the\n * Orders list, which reads {@link ORDERS_ORDER_PARAM} on arrival; the list\n * narrowed to one buyer reads {@link ORDERS_CUSTOMER_PARAM}.\n */\n\n/** The nav slug the shell resolves the catalog by. */\nexport const PRODUCTS_SLUG = 'products'\n\n/** The query key the Orders list opens one order's dialog by. */\nexport const ORDERS_ORDER_PARAM = 'order'\n\n/**\n * The query key the Orders list narrows to one buyer by — the same word the\n * CRM's contacts list opens a person by, so a reader who edits one URL into\n * the other is not surprised.\n */\nexport const ORDERS_CUSTOMER_PARAM = 'email'\n\nfunction catalog(context: PluginRecordRouteContext): string | null {\n return context.host\n ? buildRoute(Route.HOST_PLUGIN, {\n orgSlug: context.orgSlug,\n host: context.host,\n pluginSlug: PRODUCTS_SLUG,\n })\n : null\n}\n\nfunction orders(context: PluginRecordRouteContext, key?: string, value?: string): string | null {\n const list = catalog(context)\n if (!list) return null\n return key && value\n ? `${list}/orders?${new URLSearchParams({ [key]: value }).toString()}`\n : `${list}/orders`\n}\n\n/**\n * Called from the console registrar. The owner is named rather than read off\n * the loader's marker, so the route registers the same way under a spec that\n * calls the registrar directly.\n */\nexport function registerCommerceRecordRoutes(): void {\n registerPluginRecordRoute(\n 'product',\n { list: catalog, record: catalog },\n { pluginId: BUNDLE_ID },\n )\n registerPluginRecordRoute(\n 'order',\n {\n list: (context) => orders(context),\n record: (context, id) => orders(context, ORDERS_ORDER_PARAM, id),\n byEmail: (context, email) => orders(context, ORDERS_CUSTOMER_PARAM, email),\n },\n { pluginId: BUNDLE_ID },\n )\n}\n"],"names":["buildRoute","Route","registerPluginRecordRoute","BUNDLE_ID","PRODUCTS_SLUG","ORDERS_ORDER_PARAM","ORDERS_CUSTOMER_PARAM","catalog","context","host","HOST_PLUGIN","orgSlug","pluginSlug","orders","key","value","list","URLSearchParams","toString","registerCommerceRecordRoutes","record","pluginId","id","byEmail","email"],"mappings":"AAAA;;;;;;;;;;;;CAYC,GAGD,SAASA,UAAU,EAAEC,KAAK,QAAQ,wCAAuC;AACzE,SACEC,yBAAyB,QAEpB,mDAAkD;AACzD,SAASC,SAAS,QAAQ,gCAA4B;AAEtD;;;;;;;;;;;;;;;CAeC,GAED,oDAAoD,GACpD,OAAO,MAAMC,gBAAgB,WAAU;AAEvC,+DAA+D,GAC/D,OAAO,MAAMC,qBAAqB,QAAO;AAEzC;;;;CAIC,GACD,OAAO,MAAMC,wBAAwB,QAAO;AAE5C,SAASC,QAAQC,OAAiC;IAChD,OAAOA,QAAQC,IAAI,GACfT,WAAWC,MAAMS,WAAW,EAAE;QAC5BC,SAASH,QAAQG,OAAO;QACxBF,MAAMD,QAAQC,IAAI;QAClBG,YAAYR;IACd,KACA;AACN;AAEA,SAASS,OAAOL,OAAiC,EAAEM,GAAY,EAAEC,KAAc;IAC7E,MAAMC,OAAOT,QAAQC;IACrB,IAAI,CAACQ,MAAM,OAAO;IAClB,OAAOF,OAAOC,QACV,GAAGC,KAAK,QAAQ,EAAE,IAAIC,gBAAgB;QAAE,CAACH,IAAI,EAAEC;IAAM,GAAGG,QAAQ,IAAI,GACpE,GAAGF,KAAK,OAAO,CAAC;AACtB;AAEA;;;;CAIC,GACD,OAAO,SAASG;IACdjB,0BACE,WACA;QAAEc,MAAMT;QAASa,QAAQb;IAAQ,GACjC;QAAEc,UAAUlB;IAAU;IAExBD,0BACE,SACA;QACEc,MAAM,CAACR,UAAYK,OAAOL;QAC1BY,QAAQ,CAACZ,SAASc,KAAOT,OAAOL,SAASH,oBAAoBiB;QAC7DC,SAAS,CAACf,SAASgB,QAAUX,OAAOL,SAASF,uBAAuBkB;IACtE,GACA;QAAEH,UAAUlB;IAAU;AAE1B"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ * Unless required by applicable law or agreed to in writing, software
9
+ * distributed under the License is distributed on an "AS IS" BASIS,
10
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ * See the License for the specific language governing permissions and
12
+ * limitations under the License.
13
+ */
14
+ import type { ConsoleSearchSource } from '@aglyn/aglyn';
15
+ /**
16
+ * What the console's search finds in a store (AGL-3080): its products, by
17
+ * name or slug, opening on the site's Products page. Read only where the plan
18
+ * carries commerce and allows the site products, so a workspace that holds
19
+ * none never spends a read on them.
20
+ */
21
+ export declare const COMMERCE_SEARCH_SOURCES: readonly ConsoleSearchSource[];
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ * Unless required by applicable law or agreed to in writing, software
9
+ * distributed under the License is distributed on an "AS IS" BASIS,
10
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ * See the License for the specific language governing permissions and
12
+ * limitations under the License.
13
+ */ import { buildRoute, Route } from "@aglyn/aglyn/app-utils/console-routes";
14
+ import { PRODUCTS_SLUG } from "./commerce-record-routes.js";
15
+ /**
16
+ * What the console's search finds in a store (AGL-3080): its products, by
17
+ * name or slug, opening on the site's Products page. Read only where the plan
18
+ * carries commerce and allows the site products, so a workspace that holds
19
+ * none never spends a read on them.
20
+ */ export const COMMERCE_SEARCH_SOURCES = [
21
+ {
22
+ id: 'products',
23
+ group: 'Products',
24
+ noun: 'products',
25
+ scope: 'host',
26
+ collection: 'products',
27
+ nameField: 'name',
28
+ extraFields: [
29
+ 'slug'
30
+ ],
31
+ entitlementKey: 'productsPerHost',
32
+ featureFlag: 'commerce',
33
+ order: 160,
34
+ href: (_row, { orgSlug, host })=>host ? buildRoute(Route.HOST_PLUGIN, {
35
+ orgSlug,
36
+ host,
37
+ pluginSlug: PRODUCTS_SLUG
38
+ }) : null
39
+ }
40
+ ];
41
+
42
+ //# sourceMappingURL=commerce-search-sources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/commerce-search-sources.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\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 * http://www.apache.org/licenses/LICENSE-2.0\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\nimport type { ConsoleSearchSource } from '@aglyn/aglyn'\nimport { buildRoute, Route } from '@aglyn/aglyn/app-utils/console-routes'\nimport { PRODUCTS_SLUG } from './commerce-record-routes'\n\n/**\n * What the console's search finds in a store (AGL-3080): its products, by\n * name or slug, opening on the site's Products page. Read only where the plan\n * carries commerce and allows the site products, so a workspace that holds\n * none never spends a read on them.\n */\nexport const COMMERCE_SEARCH_SOURCES: readonly ConsoleSearchSource[] = [\n {\n id: 'products',\n group: 'Products',\n noun: 'products',\n scope: 'host',\n collection: 'products',\n nameField: 'name',\n extraFields: ['slug'],\n entitlementKey: 'productsPerHost',\n featureFlag: 'commerce',\n order: 160,\n href: (_row, { orgSlug, host }) =>\n host\n ? buildRoute(Route.HOST_PLUGIN, { orgSlug, host, pluginSlug: PRODUCTS_SLUG })\n : null,\n },\n]\n"],"names":["buildRoute","Route","PRODUCTS_SLUG","COMMERCE_SEARCH_SOURCES","id","group","noun","scope","collection","nameField","extraFields","entitlementKey","featureFlag","order","href","_row","orgSlug","host","HOST_PLUGIN","pluginSlug"],"mappings":"AAAA;;;;;;;;;;;;CAYC,GAGD,SAASA,UAAU,EAAEC,KAAK,QAAQ,wCAAuC;AACzE,SAASC,aAAa,QAAQ,8BAA0B;AAExD;;;;;CAKC,GACD,OAAO,MAAMC,0BAA0D;IACrE;QACEC,IAAI;QACJC,OAAO;QACPC,MAAM;QACNC,OAAO;QACPC,YAAY;QACZC,WAAW;QACXC,aAAa;YAAC;SAAO;QACrBC,gBAAgB;QAChBC,aAAa;QACbC,OAAO;QACPC,MAAM,CAACC,MAAM,EAAEC,OAAO,EAAEC,IAAI,EAAE,GAC5BA,OACIjB,WAAWC,MAAMiB,WAAW,EAAE;gBAAEF;gBAASC;gBAAME,YAAYjB;YAAc,KACzE;IACR;CACD,CAAA"}
@@ -0,0 +1,20 @@
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
+ /** Regular news and stories, including a site's posts to its members. */
18
+ export declare const NEWSLETTER_TOPIC_ID = "newsletter";
19
+ /** New products, restocks, and changes to what a site offers. */
20
+ export declare const PRODUCT_UPDATES_TOPIC_ID = "product-updates";
@@ -0,0 +1,33 @@
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 STREAMS THIS PLUGIN'S MAIL IS SENT UNDER.
18
+ *
19
+ * Declared in `plugins.config.json` (`subscriptionTopics`), which is where
20
+ * their names, descriptions and preference-page order live; named here
21
+ * wherever a send declares which stream it belongs to, because a mistyped id
22
+ * does not fail loudly. It names a stream nobody has opted out of, so the
23
+ * send path finds no opt-out and the send goes to everybody — the control
24
+ * silently doing nothing.
25
+ *
26
+ * Each value is a wire format: a Firestore path component and a component of
27
+ * the unsubscribe link's signed subject, so changing one orphans every
28
+ * opt-out already recorded against it. A cart reminder rides the platform's
29
+ * default stream (`DEFAULT_SUBSCRIPTION_TOPIC_ID`), as a campaign does.
30
+ *=========================================*/ /** Regular news and stories, including a site's posts to its members. */ export const NEWSLETTER_TOPIC_ID = 'newsletter';
31
+ /** New products, restocks, and changes to what a site offers. */ export const PRODUCT_UPDATES_TOPIC_ID = 'product-updates';
32
+
33
+ //# sourceMappingURL=commerce-topics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/commerce-topics.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 STREAMS THIS PLUGIN'S MAIL IS SENT UNDER.\n *\n * Declared in `plugins.config.json` (`subscriptionTopics`), which is where\n * their names, descriptions and preference-page order live; named here\n * wherever a send declares which stream it belongs to, because a mistyped id\n * does not fail loudly. It names a stream nobody has opted out of, so the\n * send path finds no opt-out and the send goes to everybody — the control\n * silently doing nothing.\n *\n * Each value is a wire format: a Firestore path component and a component of\n * the unsubscribe link's signed subject, so changing one orphans every\n * opt-out already recorded against it. A cart reminder rides the platform's\n * default stream (`DEFAULT_SUBSCRIPTION_TOPIC_ID`), as a campaign does.\n *=========================================*/\n\n/** Regular news and stories, including a site's posts to its members. */\nexport const NEWSLETTER_TOPIC_ID = 'newsletter'\n\n/** New products, restocks, and changes to what a site offers. */\nexport const PRODUCT_UPDATES_TOPIC_ID = 'product-updates'\n"],"names":["NEWSLETTER_TOPIC_ID","PRODUCT_UPDATES_TOPIC_ID"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;;;;;;;;;2CAc2C,GAE3C,uEAAuE,GACvE,OAAO,MAAMA,sBAAsB,aAAY;AAE/C,+DAA+D,GAC/D,OAAO,MAAMC,2BAA2B,kBAAiB"}
@@ -26,6 +26,7 @@ export * from './commerce-cart';
26
26
  export * from './commerce-connect-transfer';
27
27
  export * from './commerce-discounts';
28
28
  export * from './commerce-dispute';
29
+ export * from './commerce-topics';
29
30
  export * from './commerce-gift-cards';
30
31
  export * from './commerce-io';
31
32
  export * from './commerce-orders';
@@ -24,6 +24,7 @@ export * from "./commerce-cart.js";
24
24
  export * from "./commerce-connect-transfer.js";
25
25
  export * from "./commerce-discounts.js";
26
26
  export * from "./commerce-dispute.js";
27
+ export * from "./commerce-topics.js";
27
28
  export * from "./commerce-gift-cards.js";
28
29
  export * from "./commerce-io.js";
29
30
  export * from "./commerce-orders.js";
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/index.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 * Commerce domain model (AGL-411): pure types + logic relocated from the\n * core `@aglyn/aglyn` app-utils — the commerce plugin owns its domain.\n * Context-free and framework-free, so both the plugin's client components\n * and its `/server` handlers (and other plugins/apps via\n * `@aglyn/plugins-commerce/model`) can import it.\n */\nexport * from './commerce'\nexport * from './commerce-cart'\nexport * from './commerce-connect-transfer'\nexport * from './commerce-discounts'\nexport * from './commerce-dispute'\nexport * from './commerce-gift-cards'\nexport * from './commerce-io'\nexport * from './commerce-orders'\nexport * from './commerce-promotions'\nexport * from './commerce-reservations'\nexport * from './commerce-shipping'\nexport * from './commerce-stock-holds'\nexport * from './commerce-subscription-status'\nexport * from './commerce-tax'\nexport * from './commerce-tax-decision'\nexport * from './order-figures'\nexport * from './order-list-fields'\nexport * from './purchase-analytics'\n"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;CAMC,GACD,cAAc,gBAAY;AAC1B,cAAc,qBAAiB;AAC/B,cAAc,iCAA6B;AAC3C,cAAc,0BAAsB;AACpC,cAAc,wBAAoB;AAClC,cAAc,2BAAuB;AACrC,cAAc,mBAAe;AAC7B,cAAc,uBAAmB;AACjC,cAAc,2BAAuB;AACrC,cAAc,6BAAyB;AACvC,cAAc,yBAAqB;AACnC,cAAc,4BAAwB;AACtC,cAAc,oCAAgC;AAC9C,cAAc,oBAAgB;AAC9B,cAAc,6BAAyB;AACvC,cAAc,qBAAiB;AAC/B,cAAc,yBAAqB;AACnC,cAAc,0BAAsB"}
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/index.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 * Commerce domain model (AGL-411): pure types + logic relocated from the\n * core `@aglyn/aglyn` app-utils — the commerce plugin owns its domain.\n * Context-free and framework-free, so both the plugin's client components\n * and its `/server` handlers (and other plugins/apps via\n * `@aglyn/plugins-commerce/model`) can import it.\n */\nexport * from './commerce'\nexport * from './commerce-cart'\nexport * from './commerce-connect-transfer'\nexport * from './commerce-discounts'\nexport * from './commerce-dispute'\nexport * from './commerce-topics'\nexport * from './commerce-gift-cards'\nexport * from './commerce-io'\nexport * from './commerce-orders'\nexport * from './commerce-promotions'\nexport * from './commerce-reservations'\nexport * from './commerce-shipping'\nexport * from './commerce-stock-holds'\nexport * from './commerce-subscription-status'\nexport * from './commerce-tax'\nexport * from './commerce-tax-decision'\nexport * from './order-figures'\nexport * from './order-list-fields'\nexport * from './purchase-analytics'\n"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;CAMC,GACD,cAAc,gBAAY;AAC1B,cAAc,qBAAiB;AAC/B,cAAc,iCAA6B;AAC3C,cAAc,0BAAsB;AACpC,cAAc,wBAAoB;AAClC,cAAc,uBAAmB;AACjC,cAAc,2BAAuB;AACrC,cAAc,mBAAe;AAC7B,cAAc,uBAAmB;AACjC,cAAc,2BAAuB;AACrC,cAAc,6BAAyB;AACvC,cAAc,yBAAqB;AACnC,cAAc,4BAAwB;AACtC,cAAc,oCAAgC;AAC9C,cAAc,oBAAgB;AAC9B,cAAc,6BAAyB;AACvC,cAAc,qBAAiB;AAC/B,cAAc,yBAAqB;AACnC,cAAc,0BAAsB"}
@@ -0,0 +1,32 @@
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
+ import type { PluginIndexedRecord } from '@aglyn/aglyn/plugin-manager/plugin-record-index';
18
+ /**
19
+ * One stored product as this plugin shares it with another (AGL-3080) — the
20
+ * one shape its server index (`product-record-index.ts`) and its console list
21
+ * source (`commerce-record-lists.ts`) both answer.
22
+ *
23
+ * `null` for a deleted product and an unnamed one. Otherwise its `facts`:
24
+ * `type` (`physical` | `digital` | `service` | the stored value),
25
+ * `description`, `tags: string[]`, `categoryIds: string[]`,
26
+ * `options: Array<{ name, values: string[] }>`, `imageUrl` (the first
27
+ * photo's stored media value, or `null`), `seoTitle`, `seoDescription`,
28
+ * `priceUsd` (the product's own price in dollars, or `null`) and
29
+ * `variants: Array<{ id, options: Record<string, string>, priceUsd }>` —
30
+ * each priced variant as stored, empty for a product without variants.
31
+ */
32
+ export declare function productIndexedRecord(id: string, data: Readonly<Record<string, unknown>> | undefined): PluginIndexedRecord | null;
@@ -0,0 +1,82 @@
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
+ * One stored product as this plugin shares it with another (AGL-3080) — the
18
+ * one shape its server index (`product-record-index.ts`) and its console list
19
+ * source (`commerce-record-lists.ts`) both answer.
20
+ *
21
+ * `null` for a deleted product and an unnamed one. Otherwise its `facts`:
22
+ * `type` (`physical` | `digital` | `service` | the stored value),
23
+ * `description`, `tags: string[]`, `categoryIds: string[]`,
24
+ * `options: Array<{ name, values: string[] }>`, `imageUrl` (the first
25
+ * photo's stored media value, or `null`), `seoTitle`, `seoDescription`,
26
+ * `priceUsd` (the product's own price in dollars, or `null`) and
27
+ * `variants: Array<{ id, options: Record<string, string>, priceUsd }>` —
28
+ * each priced variant as stored, empty for a product without variants.
29
+ */ export function productIndexedRecord(id, data) {
30
+ var _data_seo;
31
+ if (!data || data['deletedAt'] != null) return null;
32
+ const name = str(data['name']);
33
+ if (!name) return null;
34
+ const seo = (_data_seo = data['seo']) != null ? _data_seo : {};
35
+ const mediaUrls = Array.isArray(data['mediaUrls']) ? data['mediaUrls'] : [];
36
+ return {
37
+ id,
38
+ name,
39
+ facts: {
40
+ type: data['type'],
41
+ description: str(data['description']),
42
+ tags: strings(data['tags']),
43
+ categoryIds: strings(data['categoryIds']),
44
+ options: (Array.isArray(data['options']) ? data['options'] : []).map((option)=>{
45
+ const record = option != null ? option : {};
46
+ return {
47
+ name: str(record['name']),
48
+ values: strings(record['values'])
49
+ };
50
+ }),
51
+ imageUrl: str(mediaUrls[0]) || str(data['imageUrl']) || null,
52
+ seoTitle: str(seo['title']),
53
+ seoDescription: str(seo['description']),
54
+ priceUsd: price(data['priceUsd']),
55
+ variants: (Array.isArray(data['variants']) ? data['variants'] : []).map((variant)=>{
56
+ var _record_options;
57
+ const record = variant != null ? variant : {};
58
+ const options = (_record_options = record['options']) != null ? _record_options : {};
59
+ return {
60
+ id: str(record['id']),
61
+ options: Object.fromEntries(Object.entries(options).map(([key, value])=>[
62
+ key,
63
+ str(value)
64
+ ])),
65
+ priceUsd: price(record['priceUsd'])
66
+ };
67
+ })
68
+ }
69
+ };
70
+ }
71
+ function str(value) {
72
+ return typeof value === 'string' ? value : '';
73
+ }
74
+ function strings(value) {
75
+ return (Array.isArray(value) ? value : []).map(str).filter(Boolean);
76
+ }
77
+ function price(value) {
78
+ const parsed = typeof value === 'number' ? value : Number(value);
79
+ return value === null || value === undefined || value === '' || !Number.isFinite(parsed) ? null : parsed;
80
+ }
81
+
82
+ //# sourceMappingURL=product-record.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/product-record.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\nimport type { PluginIndexedRecord } from '@aglyn/aglyn/plugin-manager/plugin-record-index'\n\n/**\n * One stored product as this plugin shares it with another (AGL-3080) — the\n * one shape its server index (`product-record-index.ts`) and its console list\n * source (`commerce-record-lists.ts`) both answer.\n *\n * `null` for a deleted product and an unnamed one. Otherwise its `facts`:\n * `type` (`physical` | `digital` | `service` | the stored value),\n * `description`, `tags: string[]`, `categoryIds: string[]`,\n * `options: Array<{ name, values: string[] }>`, `imageUrl` (the first\n * photo's stored media value, or `null`), `seoTitle`, `seoDescription`,\n * `priceUsd` (the product's own price in dollars, or `null`) and\n * `variants: Array<{ id, options: Record<string, string>, priceUsd }>` —\n * each priced variant as stored, empty for a product without variants.\n */\nexport function productIndexedRecord(\n id: string,\n data: Readonly<Record<string, unknown>> | undefined,\n): PluginIndexedRecord | null {\n if (!data || data['deletedAt'] != null) return null\n const name = str(data['name'])\n if (!name) return null\n const seo = (data['seo'] ?? {}) as Record<string, unknown>\n const mediaUrls = Array.isArray(data['mediaUrls']) ? data['mediaUrls'] : []\n return {\n id,\n name,\n facts: {\n type: data['type'],\n description: str(data['description']),\n tags: strings(data['tags']),\n categoryIds: strings(data['categoryIds']),\n options: (Array.isArray(data['options']) ? data['options'] : []).map((option) => {\n const record = (option ?? {}) as Record<string, unknown>\n return { name: str(record['name']), values: strings(record['values']) }\n }),\n imageUrl: str(mediaUrls[0]) || str(data['imageUrl']) || null,\n seoTitle: str(seo['title']),\n seoDescription: str(seo['description']),\n priceUsd: price(data['priceUsd']),\n variants: (Array.isArray(data['variants']) ? data['variants'] : []).map((variant) => {\n const record = (variant ?? {}) as Record<string, unknown>\n const options = (record['options'] ?? {}) as Record<string, unknown>\n return {\n id: str(record['id']),\n options: Object.fromEntries(Object.entries(options).map(([key, value]) => [key, str(value)])),\n priceUsd: price(record['priceUsd']),\n }\n }),\n },\n }\n}\n\nfunction str(value: unknown): string {\n return typeof value === 'string' ? value : ''\n}\n\nfunction strings(value: unknown): string[] {\n return (Array.isArray(value) ? value : []).map(str).filter(Boolean)\n}\n\nfunction price(value: unknown): number | null {\n const parsed = typeof value === 'number' ? value : Number(value)\n return value === null || value === undefined || value === '' || !Number.isFinite(parsed) ? null : parsed\n}\n"],"names":["productIndexedRecord","id","data","name","str","seo","mediaUrls","Array","isArray","facts","type","description","tags","strings","categoryIds","options","map","option","record","values","imageUrl","seoTitle","seoDescription","priceUsd","price","variants","variant","Object","fromEntries","entries","key","value","filter","Boolean","parsed","Number","undefined","isFinite"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAID;;;;;;;;;;;;;CAaC,GACD,OAAO,SAASA,qBACdC,EAAU,EACVC,IAAmD;QAKtCA;IAHb,IAAI,CAACA,QAAQA,IAAI,CAAC,YAAY,IAAI,MAAM,OAAO;IAC/C,MAAMC,OAAOC,IAAIF,IAAI,CAAC,OAAO;IAC7B,IAAI,CAACC,MAAM,OAAO;IAClB,MAAME,OAAOH,YAAAA,IAAI,CAAC,MAAM,YAAXA,YAAe,CAAC;IAC7B,MAAMI,YAAYC,MAAMC,OAAO,CAACN,IAAI,CAAC,YAAY,IAAIA,IAAI,CAAC,YAAY,GAAG,EAAE;IAC3E,OAAO;QACLD;QACAE;QACAM,OAAO;YACLC,MAAMR,IAAI,CAAC,OAAO;YAClBS,aAAaP,IAAIF,IAAI,CAAC,cAAc;YACpCU,MAAMC,QAAQX,IAAI,CAAC,OAAO;YAC1BY,aAAaD,QAAQX,IAAI,CAAC,cAAc;YACxCa,SAAS,AAACR,CAAAA,MAAMC,OAAO,CAACN,IAAI,CAAC,UAAU,IAAIA,IAAI,CAAC,UAAU,GAAG,EAAE,AAAD,EAAGc,GAAG,CAAC,CAACC;gBACpE,MAAMC,SAAUD,iBAAAA,SAAU,CAAC;gBAC3B,OAAO;oBAAEd,MAAMC,IAAIc,MAAM,CAAC,OAAO;oBAAGC,QAAQN,QAAQK,MAAM,CAAC,SAAS;gBAAE;YACxE;YACAE,UAAUhB,IAAIE,SAAS,CAAC,EAAE,KAAKF,IAAIF,IAAI,CAAC,WAAW,KAAK;YACxDmB,UAAUjB,IAAIC,GAAG,CAAC,QAAQ;YAC1BiB,gBAAgBlB,IAAIC,GAAG,CAAC,cAAc;YACtCkB,UAAUC,MAAMtB,IAAI,CAAC,WAAW;YAChCuB,UAAU,AAAClB,CAAAA,MAAMC,OAAO,CAACN,IAAI,CAAC,WAAW,IAAIA,IAAI,CAAC,WAAW,GAAG,EAAE,AAAD,EAAGc,GAAG,CAAC,CAACU;oBAEtDR;gBADjB,MAAMA,SAAUQ,kBAAAA,UAAW,CAAC;gBAC5B,MAAMX,WAAWG,kBAAAA,MAAM,CAAC,UAAU,YAAjBA,kBAAqB,CAAC;gBACvC,OAAO;oBACLjB,IAAIG,IAAIc,MAAM,CAAC,KAAK;oBACpBH,SAASY,OAAOC,WAAW,CAACD,OAAOE,OAAO,CAACd,SAASC,GAAG,CAAC,CAAC,CAACc,KAAKC,MAAM,GAAK;4BAACD;4BAAK1B,IAAI2B;yBAAO;oBAC3FR,UAAUC,MAAMN,MAAM,CAAC,WAAW;gBACpC;YACF;QACF;IACF;AACF;AAEA,SAASd,IAAI2B,KAAc;IACzB,OAAO,OAAOA,UAAU,WAAWA,QAAQ;AAC7C;AAEA,SAASlB,QAAQkB,KAAc;IAC7B,OAAO,AAACxB,CAAAA,MAAMC,OAAO,CAACuB,SAASA,QAAQ,EAAE,AAAD,EAAGf,GAAG,CAACZ,KAAK4B,MAAM,CAACC;AAC7D;AAEA,SAAST,MAAMO,KAAc;IAC3B,MAAMG,SAAS,OAAOH,UAAU,WAAWA,QAAQI,OAAOJ;IAC1D,OAAOA,UAAU,QAAQA,UAAUK,aAAaL,UAAU,MAAM,CAACI,OAAOE,QAAQ,CAACH,UAAU,OAAOA;AACpG"}
@@ -0,0 +1,48 @@
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
+ * Lifetime purchase math for a site's visitor accounts (AGL-546). Pure —
19
+ * operates on raw `hosts/{hostId}/orders` doc shapes (both the v1
20
+ * `totals.totalCents` form and the legacy flat `amountCents` form) so a
21
+ * member's purchases can be totalled from the docs already loaded. The stale
22
+ * `siteMembers.purchaseCents` field this replaces was never written.
23
+ */
24
+ /** The slice of an order doc the lifetime total needs. */
25
+ export interface PurchaseTotalOrder {
26
+ status?: string;
27
+ /** Legacy Commerce Starter flat total (cents). */
28
+ amountCents?: number;
29
+ /** Orders v1 totals block. */
30
+ totals?: {
31
+ totalCents?: number;
32
+ };
33
+ /** Cents refunded so far (partial or full), a lost chargeback included. */
34
+ refundedCents?: number;
35
+ }
36
+ /** Charged cents for one order: v1 total, else legacy amount. */
37
+ export declare function orderChargedCents(order: PurchaseTotalOrder): number;
38
+ /**
39
+ * Sum of what the member actually paid: charged totals minus refunds,
40
+ * skipping never-charged (pending/cancelled) orders. Each order clamps at
41
+ * zero so an over-recorded refund cannot drag the lifetime negative.
42
+ *
43
+ * `refundedCents` carries a lost chargeback as well as a refund (AGL-1787),
44
+ * and money reversed is money reversed whichever door it left by, so the
45
+ * whole figure is netted; only a LABEL splits the two
46
+ * (`splitOrderReversal`, AGL-1810).
47
+ */
48
+ export declare function computeLifetimePurchaseCents(orders: readonly PurchaseTotalOrder[]): number;
@@ -0,0 +1,50 @@
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
+ * Lifetime purchase math for a site's visitor accounts (AGL-546). Pure —
18
+ * operates on raw `hosts/{hostId}/orders` doc shapes (both the v1
19
+ * `totals.totalCents` form and the legacy flat `amountCents` form) so a
20
+ * member's purchases can be totalled from the docs already loaded. The stale
21
+ * `siteMembers.purchaseCents` field this replaces was never written.
22
+ */ /** The slice of an order doc the lifetime total needs. */ /** Orders that never charged the customer don't count toward lifetime. */ const UNCHARGED_STATUSES = new Set([
23
+ 'pending',
24
+ 'cancelled'
25
+ ]);
26
+ /** Charged cents for one order: v1 total, else legacy amount. */ export function orderChargedCents(order) {
27
+ var _ref, _ref1;
28
+ var _order_totals;
29
+ return Number((_ref = (_ref1 = (_order_totals = order.totals) == null ? void 0 : _order_totals.totalCents) != null ? _ref1 : order.amountCents) != null ? _ref : 0) || 0;
30
+ }
31
+ /**
32
+ * Sum of what the member actually paid: charged totals minus refunds,
33
+ * skipping never-charged (pending/cancelled) orders. Each order clamps at
34
+ * zero so an over-recorded refund cannot drag the lifetime negative.
35
+ *
36
+ * `refundedCents` carries a lost chargeback as well as a refund (AGL-1787),
37
+ * and money reversed is money reversed whichever door it left by, so the
38
+ * whole figure is netted; only a LABEL splits the two
39
+ * (`splitOrderReversal`, AGL-1810).
40
+ */ export function computeLifetimePurchaseCents(orders) {
41
+ return orders.reduce((sum, order)=>{
42
+ var _order_status, _order_refundedCents;
43
+ if (UNCHARGED_STATUSES.has(String((_order_status = order.status) != null ? _order_status : ''))) return sum;
44
+ const charged = orderChargedCents(order);
45
+ const refunded = Math.max(0, Number((_order_refundedCents = order.refundedCents) != null ? _order_refundedCents : 0) || 0);
46
+ return sum + Math.max(0, charged - refunded);
47
+ }, 0);
48
+ }
49
+
50
+ //# sourceMappingURL=site-member-purchases.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/site-member-purchases.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 * Lifetime purchase math for a site's visitor accounts (AGL-546). Pure —\n * operates on raw `hosts/{hostId}/orders` doc shapes (both the v1\n * `totals.totalCents` form and the legacy flat `amountCents` form) so a\n * member's purchases can be totalled from the docs already loaded. The stale\n * `siteMembers.purchaseCents` field this replaces was never written.\n */\n\n/** The slice of an order doc the lifetime total needs. */\nexport interface PurchaseTotalOrder {\n status?: string\n /** Legacy Commerce Starter flat total (cents). */\n amountCents?: number\n /** Orders v1 totals block. */\n totals?: { totalCents?: number }\n /** Cents refunded so far (partial or full), a lost chargeback included. */\n refundedCents?: number\n}\n\n/** Orders that never charged the customer don't count toward lifetime. */\nconst UNCHARGED_STATUSES = new Set(['pending', 'cancelled'])\n\n/** Charged cents for one order: v1 total, else legacy amount. */\nexport function orderChargedCents(order: PurchaseTotalOrder): number {\n return Number(order.totals?.totalCents ?? order.amountCents ?? 0) || 0\n}\n\n/**\n * Sum of what the member actually paid: charged totals minus refunds,\n * skipping never-charged (pending/cancelled) orders. Each order clamps at\n * zero so an over-recorded refund cannot drag the lifetime negative.\n *\n * `refundedCents` carries a lost chargeback as well as a refund (AGL-1787),\n * and money reversed is money reversed whichever door it left by, so the\n * whole figure is netted; only a LABEL splits the two\n * (`splitOrderReversal`, AGL-1810).\n */\nexport function computeLifetimePurchaseCents(\n orders: readonly PurchaseTotalOrder[],\n): number {\n return orders.reduce((sum, order) => {\n if (UNCHARGED_STATUSES.has(String(order.status ?? ''))) return sum\n const charged = orderChargedCents(order)\n const refunded = Math.max(0, Number(order.refundedCents ?? 0) || 0)\n return sum + Math.max(0, charged - refunded)\n }, 0)\n}\n"],"names":["UNCHARGED_STATUSES","Set","orderChargedCents","order","Number","totals","totalCents","amountCents","computeLifetimePurchaseCents","orders","reduce","sum","has","String","status","charged","refunded","Math","max","refundedCents"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;;;CAMC,GAED,wDAAwD,GAWxD,wEAAwE,GACxE,MAAMA,qBAAqB,IAAIC,IAAI;IAAC;IAAW;CAAY;AAE3D,+DAA+D,GAC/D,OAAO,SAASC,kBAAkBC,KAAyB;QAC3CA;QAAAA;IAAd,OAAOC,QAAOD,iBAAAA,gBAAAA,MAAME,MAAM,qBAAZF,cAAcG,UAAU,oBAAIH,MAAMI,WAAW,YAA7CJ,OAAiD,MAAM;AACvE;AAEA;;;;;;;;;CASC,GACD,OAAO,SAASK,6BACdC,MAAqC;IAErC,OAAOA,OAAOC,MAAM,CAAC,CAACC,KAAKR;YACSA,eAEEA;QAFpC,IAAIH,mBAAmBY,GAAG,CAACC,QAAOV,gBAAAA,MAAMW,MAAM,YAAZX,gBAAgB,MAAM,OAAOQ;QAC/D,MAAMI,UAAUb,kBAAkBC;QAClC,MAAMa,WAAWC,KAAKC,GAAG,CAAC,GAAGd,QAAOD,uBAAAA,MAAMgB,aAAa,YAAnBhB,uBAAuB,MAAM;QACjE,OAAOQ,MAAMM,KAAKC,GAAG,CAAC,GAAGH,UAAUC;IACrC,GAAG;AACL"}