@aglyn/plugins-commerce 1.0.0-beta.217 → 1.0.0-beta.219

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 (100) hide show
  1. package/README.md +1 -0
  2. package/package.json +15 -15
  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 +44 -18
  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 +1 -1
  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 +114 -63
  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/store-settings-card.component.js +2 -2
  29. package/src/lib/components/console/store-settings-card.component.js.map +1 -1
  30. package/src/lib/components/console/storefront-tax-summary-card.component.js +1 -1
  31. package/src/lib/components/console/storefront-tax-summary-card.component.js.map +1 -1
  32. package/src/lib/components/product-detail.js +2 -1
  33. package/src/lib/components/product-detail.js.map +1 -1
  34. package/src/lib/components/product-grid.js +1 -1
  35. package/src/lib/components/product-grid.js.map +1 -1
  36. package/src/lib/components/storefront-payment-element.js +36 -5
  37. package/src/lib/components/storefront-payment-element.js.map +1 -1
  38. package/src/lib/constants/storefront-payments.d.ts +33 -0
  39. package/src/lib/constants/storefront-payments.js +35 -0
  40. package/src/lib/constants/storefront-payments.js.map +1 -0
  41. package/src/lib/declarations.server.d.ts +3 -1
  42. package/src/lib/declarations.server.js +19 -1
  43. package/src/lib/declarations.server.js.map +1 -1
  44. package/src/lib/model/commerce-record-routes.d.ts +27 -0
  45. package/src/lib/model/commerce-record-routes.js +72 -0
  46. package/src/lib/model/commerce-record-routes.js.map +1 -0
  47. package/src/lib/model/commerce-search-sources.d.ts +21 -0
  48. package/src/lib/model/commerce-search-sources.js +40 -0
  49. package/src/lib/model/commerce-search-sources.js.map +1 -0
  50. package/src/lib/model/storefront-tax-summary.d.ts +121 -0
  51. package/src/lib/model/storefront-tax-summary.js +148 -0
  52. package/src/lib/model/storefront-tax-summary.js.map +1 -0
  53. package/src/lib/plan-entitlements.d.ts +35 -0
  54. package/src/lib/plan-entitlements.js +54 -0
  55. package/src/lib/plan-entitlements.js.map +1 -0
  56. package/src/lib/plugin.js +9 -0
  57. package/src/lib/plugin.js.map +1 -1
  58. package/src/lib/server/billing-webhook.js +135 -168
  59. package/src/lib/server/billing-webhook.js.map +1 -1
  60. package/src/lib/server/cart-checkout.js +2 -2
  61. package/src/lib/server/cart-checkout.js.map +1 -1
  62. package/src/lib/server/checkout.js +2 -2
  63. package/src/lib/server/checkout.js.map +1 -1
  64. package/src/lib/server/connect.js +5 -5
  65. package/src/lib/server/connect.js.map +1 -1
  66. package/src/lib/server/draft-order.js +2 -2
  67. package/src/lib/server/draft-order.js.map +1 -1
  68. package/src/lib/server/membership-content.js +1 -1
  69. package/src/lib/server/membership-content.js.map +1 -1
  70. package/src/lib/server/native-checkout.d.ts +20 -5
  71. package/src/lib/server/native-checkout.js +27 -6
  72. package/src/lib/server/native-checkout.js.map +1 -1
  73. package/src/lib/server/pos-order.js +2 -2
  74. package/src/lib/server/pos-order.js.map +1 -1
  75. package/src/lib/server/product-record-index.d.ts +19 -0
  76. package/src/lib/server/product-record-index.js +115 -0
  77. package/src/lib/server/product-record-index.js.map +1 -0
  78. package/src/lib/server/reserve.js +2 -2
  79. package/src/lib/server/reserve.js.map +1 -1
  80. package/src/lib/server/revenue-source.d.ts +130 -0
  81. package/src/lib/server/revenue-source.js +286 -0
  82. package/src/lib/server/revenue-source.js.map +1 -0
  83. package/src/lib/server/tax-profile.d.ts +8 -5
  84. package/src/lib/server/tax-profile.js +22 -5
  85. package/src/lib/server/tax-profile.js.map +1 -1
  86. package/src/lib/server/tax-return-source.d.ts +125 -0
  87. package/src/lib/server/tax-return-source.js +524 -0
  88. package/src/lib/server/tax-return-source.js.map +1 -0
  89. package/src/lib/server/tax-summary.d.ts +92 -0
  90. package/src/lib/server/tax-summary.js +258 -0
  91. package/src/lib/server/tax-summary.js.map +1 -0
  92. package/src/lib/server.d.ts +1 -0
  93. package/src/lib/server.js +23 -0
  94. package/src/lib/server.js.map +1 -1
  95. package/src/lib/tenant-emails.d.ts +33 -0
  96. package/src/lib/tenant-emails.js +511 -0
  97. package/src/lib/tenant-emails.js.map +1 -0
  98. package/src/lib/testing/stand-in-crm-record-routes.d.ts +25 -0
  99. package/src/lib/testing/stand-in-crm-record-routes.js +46 -0
  100. package/src/lib/testing/stand-in-crm-record-routes.js.map +1 -0
@@ -0,0 +1,27 @@
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
+ /** The query key the Orders list opens one order's dialog by. */
15
+ export declare const ORDERS_ORDER_PARAM = "order";
16
+ /**
17
+ * The query key the Orders list narrows to one buyer by — the same word the
18
+ * CRM's contacts list opens a person by, so a reader who edits one URL into
19
+ * the other is not surprised.
20
+ */
21
+ export declare const ORDERS_CUSTOMER_PARAM = "email";
22
+ /**
23
+ * Called from the console registrar. The owner is named rather than read off
24
+ * the loader's marker, so the route registers the same way under a spec that
25
+ * calls the registrar directly.
26
+ */
27
+ 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. */ 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. */\nconst 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,MAAMC,gBAAgB;AAEtB,+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,40 @@
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
+ /**
15
+ * What the console's search finds in a store (AGL-3080): its products, by
16
+ * name or slug, opening on the site's Products page. Read only where the plan
17
+ * carries commerce and allows the site products, so a workspace that holds
18
+ * none never spends a read on them.
19
+ */ export const COMMERCE_SEARCH_SOURCES = [
20
+ {
21
+ id: 'products',
22
+ group: 'Products',
23
+ noun: 'products',
24
+ scope: 'host',
25
+ collection: 'products',
26
+ nameField: 'name',
27
+ extraFields: [
28
+ 'slug'
29
+ ],
30
+ entitlementKey: 'productsPerHost',
31
+ featureFlag: 'commerce',
32
+ order: 160,
33
+ href: (_row, { orgSlug, host })=>host ? buildRoute(Route.HOST_PRODUCTS, {
34
+ orgSlug,
35
+ host
36
+ }) : null
37
+ }
38
+ ];
39
+
40
+ //# 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'\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 ? buildRoute(Route.HOST_PRODUCTS, { orgSlug, host }) : null,\n },\n]\n"],"names":["buildRoute","Route","COMMERCE_SEARCH_SOURCES","id","group","noun","scope","collection","nameField","extraFields","entitlementKey","featureFlag","order","href","_row","orgSlug","host","HOST_PRODUCTS"],"mappings":"AAAA;;;;;;;;;;;;CAYC,GAGD,SAASA,UAAU,EAAEC,KAAK,QAAQ,wCAAuC;AAEzE;;;;;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,OAAOhB,WAAWC,MAAMgB,aAAa,EAAE;gBAAEF;gBAASC;YAAK,KAAK;IAChE;CACD,CAAA"}
@@ -0,0 +1,121 @@
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
+ * The tax storefront sales collected, by who is liable for it (AGL-1904).
19
+ *
20
+ * A storefront sale is a merchant's sale — but on a `mode: 'stripe'` store
21
+ * the tax charged to the shopper is computed against the PLATFORM's
22
+ * registrations on a Checkout Session created on the platform's own account
23
+ * (measured, not inferred: with the platform unregistered and the destination
24
+ * connected account registered in Texas, Stripe answered `amount_tax: 0` /
25
+ * `not_collecting`; with the platform registered and nothing else changed,
26
+ * 8.25% / `standard_rated`; both sessions reported
27
+ * `automatic_tax.liability: { type: "self" }`). That tax lands in the
28
+ * platform's balance, and the operator's return reads it through this fold.
29
+ *
30
+ * ## THREE BUCKETS, AND THEY MUST NEVER BE SUMMED
31
+ *
32
+ * There is deliberately no grand total on this summary, and adding one would
33
+ * be the bug — the three are answers to three different questions:
34
+ *
35
+ * - **`aglynLiable`** — Stripe Tax computed it against the platform's own
36
+ * registrations (`taxMode: 'stripe-automatic'`,
37
+ * `taxLiability: 'platform'`). This is money the platform is holding.
38
+ * - **`merchantManual`** — the merchant's own configured rate, added as an
39
+ * ordinary line item Stripe is never told is tax. The platform's
40
+ * registrations played no part; it is not the platform's to remit, and
41
+ * counting it would have the operator filing and paying another
42
+ * company's tax.
43
+ * - **`connectedAccountLiable`** — Stripe Tax that named a CONNECTED
44
+ * account as the liable party. None exists today (no storefront path sets
45
+ * `on_behalf_of`), and the bucket is here so that if one ever does, the
46
+ * money moves out of `aglynLiable` visibly rather than silently.
47
+ *
48
+ * `aglynLiable` is a wire name the merchant's own tax report and the staff
49
+ * return have both read since AGL-1904; it means "the platform operator".
50
+ *
51
+ * The classification comes from the stored `taxMode` / `taxLiability`, which
52
+ * `storefront-tax.ts` derives from `automatic_tax.enabled` and never from the
53
+ * presence of tax lines — a manual-mode subscription renewal carries genuine
54
+ * Stripe Tax Rates (AGL-1751) and is otherwise indistinguishable.
55
+ *
56
+ * **This says nothing about marketplace-facilitator status.** It reports which
57
+ * registration computed which tax and where the money is. Which line of a
58
+ * filed return each bucket belongs on is a question for the preparer and for
59
+ * counsel, and this module deliberately declines to answer it — which is
60
+ * exactly why there is no merged total to mistake for one.
61
+ *
62
+ * Rows that cannot be classified or read are counted in `attention`, never
63
+ * dropped and never zeroed.
64
+ *
65
+ * Pure and total: no Firestore, no clock. Read by the staff return's source
66
+ * (`server/tax-return-source.ts`) and by the merchant's own tax report, so
67
+ * both read the same rows through the same classifier.
68
+ */
69
+ import { type TaxLineInput, type TaxReturnJurisdiction } from '@aglyn/aglyn/app-utils/tax-jurisdiction-figures';
70
+ /** The subset of a `storefrontTaxCollected/{stripeId}` row this reads. */
71
+ export interface StorefrontTaxReturnRowInput {
72
+ id: string;
73
+ hostId?: unknown;
74
+ orgId?: string | null;
75
+ taxMode?: unknown;
76
+ taxLiability?: unknown;
77
+ grossCents?: unknown;
78
+ taxCents?: unknown;
79
+ currency?: unknown;
80
+ customerAddress?: {
81
+ country?: unknown;
82
+ state?: unknown;
83
+ } | null;
84
+ taxLines?: TaxLineInput[] | null;
85
+ paidAt?: unknown;
86
+ }
87
+ export interface StorefrontTaxBucket {
88
+ transactionCount: number;
89
+ /** What shoppers paid, tax included. NOT the platform's revenue. */
90
+ grossCents: number;
91
+ /** Stripe's `taxable_amount` summed; 0 for buckets that state no base. */
92
+ taxableSalesCents: number;
93
+ taxCollectedCents: number;
94
+ /** Keyed `COUNTRY-STATE`, or `unknown`. */
95
+ byJurisdiction: Record<string, TaxReturnJurisdiction>;
96
+ }
97
+ export interface StorefrontTaxSummary {
98
+ periodStart: string;
99
+ periodEnd: string;
100
+ transactionCount: number;
101
+ /** Tax computed against the PLATFORM's registrations. See the module note. */
102
+ aglynLiable: StorefrontTaxBucket;
103
+ /** The merchant's own configured tax. Never the platform's to remit. */
104
+ merchantManual: StorefrontTaxBucket;
105
+ /** Stripe Tax that named a connected account liable. Empty today. */
106
+ connectedAccountLiable: StorefrontTaxBucket;
107
+ attention: {
108
+ /** Tax collected but no line states its base — see the module note. */
109
+ rowsMissingTaxableBase: number;
110
+ rowsMissingAddress: number;
111
+ nonUsdRows: number;
112
+ rowsMissingPaidAt: number;
113
+ /** A `taxMode` this code does not recognize — never silently bucketed. */
114
+ rowsUnclassified: number;
115
+ };
116
+ }
117
+ /** The storefront figures from one period's rows. See the module note. */
118
+ export declare function storefrontTaxSummary(rows: readonly StorefrontTaxReturnRowInput[], period: {
119
+ start: Date;
120
+ end: Date;
121
+ }): StorefrontTaxSummary;
@@ -0,0 +1,148 @@
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 tax storefront sales collected, by who is liable for it (AGL-1904).
18
+ *
19
+ * A storefront sale is a merchant's sale — but on a `mode: 'stripe'` store
20
+ * the tax charged to the shopper is computed against the PLATFORM's
21
+ * registrations on a Checkout Session created on the platform's own account
22
+ * (measured, not inferred: with the platform unregistered and the destination
23
+ * connected account registered in Texas, Stripe answered `amount_tax: 0` /
24
+ * `not_collecting`; with the platform registered and nothing else changed,
25
+ * 8.25% / `standard_rated`; both sessions reported
26
+ * `automatic_tax.liability: { type: "self" }`). That tax lands in the
27
+ * platform's balance, and the operator's return reads it through this fold.
28
+ *
29
+ * ## THREE BUCKETS, AND THEY MUST NEVER BE SUMMED
30
+ *
31
+ * There is deliberately no grand total on this summary, and adding one would
32
+ * be the bug — the three are answers to three different questions:
33
+ *
34
+ * - **`aglynLiable`** — Stripe Tax computed it against the platform's own
35
+ * registrations (`taxMode: 'stripe-automatic'`,
36
+ * `taxLiability: 'platform'`). This is money the platform is holding.
37
+ * - **`merchantManual`** — the merchant's own configured rate, added as an
38
+ * ordinary line item Stripe is never told is tax. The platform's
39
+ * registrations played no part; it is not the platform's to remit, and
40
+ * counting it would have the operator filing and paying another
41
+ * company's tax.
42
+ * - **`connectedAccountLiable`** — Stripe Tax that named a CONNECTED
43
+ * account as the liable party. None exists today (no storefront path sets
44
+ * `on_behalf_of`), and the bucket is here so that if one ever does, the
45
+ * money moves out of `aglynLiable` visibly rather than silently.
46
+ *
47
+ * `aglynLiable` is a wire name the merchant's own tax report and the staff
48
+ * return have both read since AGL-1904; it means "the platform operator".
49
+ *
50
+ * The classification comes from the stored `taxMode` / `taxLiability`, which
51
+ * `storefront-tax.ts` derives from `automatic_tax.enabled` and never from the
52
+ * presence of tax lines — a manual-mode subscription renewal carries genuine
53
+ * Stripe Tax Rates (AGL-1751) and is otherwise indistinguishable.
54
+ *
55
+ * **This says nothing about marketplace-facilitator status.** It reports which
56
+ * registration computed which tax and where the money is. Which line of a
57
+ * filed return each bucket belongs on is a question for the preparer and for
58
+ * counsel, and this module deliberately declines to answer it — which is
59
+ * exactly why there is no merged total to mistake for one.
60
+ *
61
+ * Rows that cannot be classified or read are counted in `attention`, never
62
+ * dropped and never zeroed.
63
+ *
64
+ * Pure and total: no Firestore, no clock. Read by the staff return's source
65
+ * (`server/tax-return-source.ts`) and by the merchant's own tax report, so
66
+ * both read the same rows through the same classifier.
67
+ */ import { accumulateTaxWorkingPapers, asRowDate, emptyTaxJurisdiction, taxJurisdictionKey } from "@aglyn/aglyn/app-utils/tax-jurisdiction-figures";
68
+ function cents(value) {
69
+ const parsed = Number(value != null ? value : 0);
70
+ return Number.isFinite(parsed) ? Math.round(parsed) : 0;
71
+ }
72
+ function emptyBucket() {
73
+ return {
74
+ transactionCount: 0,
75
+ grossCents: 0,
76
+ taxableSalesCents: 0,
77
+ taxCollectedCents: 0,
78
+ byJurisdiction: {}
79
+ };
80
+ }
81
+ /** The storefront figures from one period's rows. See the module note. */ export function storefrontTaxSummary(rows, period) {
82
+ const summary = {
83
+ periodStart: period.start.toISOString(),
84
+ periodEnd: period.end.toISOString(),
85
+ transactionCount: 0,
86
+ aglynLiable: emptyBucket(),
87
+ merchantManual: emptyBucket(),
88
+ connectedAccountLiable: emptyBucket(),
89
+ attention: {
90
+ rowsMissingTaxableBase: 0,
91
+ rowsMissingAddress: 0,
92
+ nonUsdRows: 0,
93
+ rowsMissingPaidAt: 0,
94
+ rowsUnclassified: 0
95
+ }
96
+ };
97
+ for (const row of rows != null ? rows : []){
98
+ var _row_currency, _row_taxMode, _row_taxLiability, _bucket_byJurisdiction, _jurisdiction, _;
99
+ const gross = cents(row.grossCents);
100
+ const tax = cents(row.taxCents);
101
+ const lines = Array.isArray(row.taxLines) ? row.taxLines : [];
102
+ const statedBases = lines.map((line)=>line == null ? void 0 : line.taxableAmountCents).filter((base)=>base !== null && base !== undefined && Number.isFinite(Number(base)));
103
+ const taxableBase = statedBases.reduce((sum, base)=>sum + Math.round(Number(base)), 0);
104
+ const jurisdiction = taxJurisdictionKey(row.customerAddress);
105
+ if (jurisdiction === 'unknown') summary.attention.rowsMissingAddress += 1;
106
+ if (String((_row_currency = row.currency) != null ? _row_currency : 'usd').toLowerCase() !== 'usd') {
107
+ summary.attention.nonUsdRows += 1;
108
+ }
109
+ if (!asRowDate(row.paidAt)) summary.attention.rowsMissingPaidAt += 1;
110
+ // The bucket is chosen from the STORED classification, and an unfamiliar
111
+ // one falls through to `rowsUnclassified` rather than defaulting into a
112
+ // bucket — a default here would put a merchant's tax on the platform's
113
+ // return, or the platform's tax nowhere, and neither is allowed to happen
114
+ // quietly.
115
+ const mode = String((_row_taxMode = row.taxMode) != null ? _row_taxMode : '');
116
+ const liability = String((_row_taxLiability = row.taxLiability) != null ? _row_taxLiability : '');
117
+ const bucket = mode === 'stripe-automatic' && liability === 'connected-account' ? summary.connectedAccountLiable : mode === 'stripe-automatic' ? summary.aglynLiable : mode === 'manual' ? summary.merchantManual : null;
118
+ if (!bucket) {
119
+ summary.attention.rowsUnclassified += 1;
120
+ continue;
121
+ }
122
+ // A taxed row that cannot state the base the rate was applied to —
123
+ // counted ONLY for Stripe-computed tax. A manual-mode row has no Stripe
124
+ // base by construction (Stripe was never told the amount was tax), so
125
+ // flagging it here would raise a permanent alarm about a figure that can
126
+ // never exist, and an alarm that is always on is an alarm nobody reads.
127
+ if (mode === 'stripe-automatic' && tax > 0 && statedBases.length === 0) {
128
+ summary.attention.rowsMissingTaxableBase += 1;
129
+ }
130
+ summary.transactionCount += 1;
131
+ bucket.transactionCount += 1;
132
+ bucket.grossCents += gross;
133
+ bucket.taxableSalesCents += taxableBase;
134
+ bucket.taxCollectedCents += tax;
135
+ const jurisdictionBucket = (_ = (_bucket_byJurisdiction = bucket.byJurisdiction)[_jurisdiction = jurisdiction]) != null ? _ : _bucket_byJurisdiction[_jurisdiction] = emptyTaxJurisdiction();
136
+ jurisdictionBucket.transactionCount += 1;
137
+ jurisdictionBucket.totalSalesCents += gross - tax;
138
+ jurisdictionBucket.taxableSalesCents += taxableBase;
139
+ jurisdictionBucket.taxCollectedCents += tax;
140
+ // The storefront working papers (AGL-2329). Its lines carry the richer
141
+ // detail — `percentage`, `rateState`, `jurisdiction` — three fields the
142
+ // writer annotates "for the working papers".
143
+ accumulateTaxWorkingPapers(jurisdictionBucket, lines);
144
+ }
145
+ return summary;
146
+ }
147
+
148
+ //# sourceMappingURL=storefront-tax-summary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/model/storefront-tax-summary.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 tax storefront sales collected, by who is liable for it (AGL-1904).\n *\n * A storefront sale is a merchant's sale — but on a `mode: 'stripe'` store\n * the tax charged to the shopper is computed against the PLATFORM's\n * registrations on a Checkout Session created on the platform's own account\n * (measured, not inferred: with the platform unregistered and the destination\n * connected account registered in Texas, Stripe answered `amount_tax: 0` /\n * `not_collecting`; with the platform registered and nothing else changed,\n * 8.25% / `standard_rated`; both sessions reported\n * `automatic_tax.liability: { type: \"self\" }`). That tax lands in the\n * platform's balance, and the operator's return reads it through this fold.\n *\n * ## THREE BUCKETS, AND THEY MUST NEVER BE SUMMED\n *\n * There is deliberately no grand total on this summary, and adding one would\n * be the bug — the three are answers to three different questions:\n *\n * - **`aglynLiable`** — Stripe Tax computed it against the platform's own\n * registrations (`taxMode: 'stripe-automatic'`,\n * `taxLiability: 'platform'`). This is money the platform is holding.\n * - **`merchantManual`** — the merchant's own configured rate, added as an\n * ordinary line item Stripe is never told is tax. The platform's\n * registrations played no part; it is not the platform's to remit, and\n * counting it would have the operator filing and paying another\n * company's tax.\n * - **`connectedAccountLiable`** — Stripe Tax that named a CONNECTED\n * account as the liable party. None exists today (no storefront path sets\n * `on_behalf_of`), and the bucket is here so that if one ever does, the\n * money moves out of `aglynLiable` visibly rather than silently.\n *\n * `aglynLiable` is a wire name the merchant's own tax report and the staff\n * return have both read since AGL-1904; it means \"the platform operator\".\n *\n * The classification comes from the stored `taxMode` / `taxLiability`, which\n * `storefront-tax.ts` derives from `automatic_tax.enabled` and never from the\n * presence of tax lines — a manual-mode subscription renewal carries genuine\n * Stripe Tax Rates (AGL-1751) and is otherwise indistinguishable.\n *\n * **This says nothing about marketplace-facilitator status.** It reports which\n * registration computed which tax and where the money is. Which line of a\n * filed return each bucket belongs on is a question for the preparer and for\n * counsel, and this module deliberately declines to answer it — which is\n * exactly why there is no merged total to mistake for one.\n *\n * Rows that cannot be classified or read are counted in `attention`, never\n * dropped and never zeroed.\n *\n * Pure and total: no Firestore, no clock. Read by the staff return's source\n * (`server/tax-return-source.ts`) and by the merchant's own tax report, so\n * both read the same rows through the same classifier.\n */\n\nimport {\n accumulateTaxWorkingPapers,\n asRowDate,\n emptyTaxJurisdiction,\n taxJurisdictionKey,\n type TaxLineInput,\n type TaxReturnJurisdiction,\n} from '@aglyn/aglyn/app-utils/tax-jurisdiction-figures'\n\n/** The subset of a `storefrontTaxCollected/{stripeId}` row this reads. */\nexport interface StorefrontTaxReturnRowInput {\n id: string\n hostId?: unknown\n orgId?: string | null\n taxMode?: unknown\n taxLiability?: unknown\n grossCents?: unknown\n taxCents?: unknown\n currency?: unknown\n customerAddress?: {\n country?: unknown\n state?: unknown\n } | null\n taxLines?: TaxLineInput[] | null\n paidAt?: unknown\n}\n\nexport interface StorefrontTaxBucket {\n transactionCount: number\n /** What shoppers paid, tax included. NOT the platform's revenue. */\n grossCents: number\n /** Stripe's `taxable_amount` summed; 0 for buckets that state no base. */\n taxableSalesCents: number\n taxCollectedCents: number\n /** Keyed `COUNTRY-STATE`, or `unknown`. */\n byJurisdiction: Record<string, TaxReturnJurisdiction>\n}\n\nexport interface StorefrontTaxSummary {\n periodStart: string\n periodEnd: string\n transactionCount: number\n /** Tax computed against the PLATFORM's registrations. See the module note. */\n aglynLiable: StorefrontTaxBucket\n /** The merchant's own configured tax. Never the platform's to remit. */\n merchantManual: StorefrontTaxBucket\n /** Stripe Tax that named a connected account liable. Empty today. */\n connectedAccountLiable: StorefrontTaxBucket\n attention: {\n /** Tax collected but no line states its base — see the module note. */\n rowsMissingTaxableBase: number\n rowsMissingAddress: number\n nonUsdRows: number\n rowsMissingPaidAt: number\n /** A `taxMode` this code does not recognize — never silently bucketed. */\n rowsUnclassified: number\n }\n}\n\nfunction cents(value: unknown): number {\n const parsed = Number(value ?? 0)\n return Number.isFinite(parsed) ? Math.round(parsed) : 0\n}\n\nfunction emptyBucket(): StorefrontTaxBucket {\n return {\n transactionCount: 0,\n grossCents: 0,\n taxableSalesCents: 0,\n taxCollectedCents: 0,\n byJurisdiction: {},\n }\n}\n\n/** The storefront figures from one period's rows. See the module note. */\nexport function storefrontTaxSummary(\n rows: readonly StorefrontTaxReturnRowInput[],\n period: { start: Date; end: Date },\n): StorefrontTaxSummary {\n const summary: StorefrontTaxSummary = {\n periodStart: period.start.toISOString(),\n periodEnd: period.end.toISOString(),\n transactionCount: 0,\n aglynLiable: emptyBucket(),\n merchantManual: emptyBucket(),\n connectedAccountLiable: emptyBucket(),\n attention: {\n rowsMissingTaxableBase: 0,\n rowsMissingAddress: 0,\n nonUsdRows: 0,\n rowsMissingPaidAt: 0,\n rowsUnclassified: 0,\n },\n }\n\n for (const row of rows ?? []) {\n const gross = cents(row.grossCents)\n const tax = cents(row.taxCents)\n const lines = Array.isArray(row.taxLines) ? row.taxLines : []\n const statedBases = lines\n .map((line) => line?.taxableAmountCents)\n .filter(\n (base): base is number =>\n base !== null && base !== undefined && Number.isFinite(Number(base)),\n )\n const taxableBase = statedBases.reduce(\n (sum, base) => sum + Math.round(Number(base)),\n 0,\n )\n\n const jurisdiction = taxJurisdictionKey(row.customerAddress)\n if (jurisdiction === 'unknown') summary.attention.rowsMissingAddress += 1\n if (String(row.currency ?? 'usd').toLowerCase() !== 'usd') {\n summary.attention.nonUsdRows += 1\n }\n if (!asRowDate(row.paidAt)) summary.attention.rowsMissingPaidAt += 1\n\n // The bucket is chosen from the STORED classification, and an unfamiliar\n // one falls through to `rowsUnclassified` rather than defaulting into a\n // bucket — a default here would put a merchant's tax on the platform's\n // return, or the platform's tax nowhere, and neither is allowed to happen\n // quietly.\n const mode = String(row.taxMode ?? '')\n const liability = String(row.taxLiability ?? '')\n const bucket =\n mode === 'stripe-automatic' && liability === 'connected-account'\n ? summary.connectedAccountLiable\n : mode === 'stripe-automatic'\n ? summary.aglynLiable\n : mode === 'manual'\n ? summary.merchantManual\n : null\n if (!bucket) {\n summary.attention.rowsUnclassified += 1\n continue\n }\n\n // A taxed row that cannot state the base the rate was applied to —\n // counted ONLY for Stripe-computed tax. A manual-mode row has no Stripe\n // base by construction (Stripe was never told the amount was tax), so\n // flagging it here would raise a permanent alarm about a figure that can\n // never exist, and an alarm that is always on is an alarm nobody reads.\n if (mode === 'stripe-automatic' && tax > 0 && statedBases.length === 0) {\n summary.attention.rowsMissingTaxableBase += 1\n }\n\n summary.transactionCount += 1\n bucket.transactionCount += 1\n bucket.grossCents += gross\n bucket.taxableSalesCents += taxableBase\n bucket.taxCollectedCents += tax\n const jurisdictionBucket = (bucket.byJurisdiction[jurisdiction] ??=\n emptyTaxJurisdiction())\n jurisdictionBucket.transactionCount += 1\n jurisdictionBucket.totalSalesCents += gross - tax\n jurisdictionBucket.taxableSalesCents += taxableBase\n jurisdictionBucket.taxCollectedCents += tax\n // The storefront working papers (AGL-2329). Its lines carry the richer\n // detail — `percentage`, `rateState`, `jurisdiction` — three fields the\n // writer annotates \"for the working papers\".\n accumulateTaxWorkingPapers(jurisdictionBucket, lines)\n }\n\n return summary\n}\n"],"names":["accumulateTaxWorkingPapers","asRowDate","emptyTaxJurisdiction","taxJurisdictionKey","cents","value","parsed","Number","isFinite","Math","round","emptyBucket","transactionCount","grossCents","taxableSalesCents","taxCollectedCents","byJurisdiction","storefrontTaxSummary","rows","period","summary","periodStart","start","toISOString","periodEnd","end","aglynLiable","merchantManual","connectedAccountLiable","attention","rowsMissingTaxableBase","rowsMissingAddress","nonUsdRows","rowsMissingPaidAt","rowsUnclassified","row","bucket","jurisdiction","gross","tax","taxCents","lines","Array","isArray","taxLines","statedBases","map","line","taxableAmountCents","filter","base","undefined","taxableBase","reduce","sum","customerAddress","String","currency","toLowerCase","paidAt","mode","taxMode","liability","taxLiability","length","jurisdictionBucket","totalSalesCents"],"mappings":"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"}
@@ -0,0 +1,35 @@
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 { PluginPlanEntitlementsDeclaration } from '@aglyn/aglyn/plugin-manager/plugin-plan-entitlements';
18
+ /**
19
+ * COMMERCE'S PLAN GATES (AGL-3080): the storefront features that start above
20
+ * the entry tiers. Compiled into core's `PLAN_ENTITLEMENTS` by the manifest
21
+ * generator (`register.planEntitlements`), so the plan comparison and every
22
+ * gate read the answers they always have.
23
+ */
24
+ declare module '@aglyn/aglyn/plugin-manager/plugin-entitlement-keys' {
25
+ interface PluginEntitlementFeatures {
26
+ /** Recurring storefront subscription products (AGL-303). */
27
+ storefrontSubscriptions?: boolean;
28
+ /** Gift cards & store credit (AGL-322). */
29
+ giftCards?: boolean;
30
+ /** Commerce analytics dashboard (AGL-327). */
31
+ commerceAnalytics?: boolean;
32
+ }
33
+ }
34
+ /** Commerce's plan gates, for the manifest generator. */
35
+ export declare function commercePlanEntitlements(): PluginPlanEntitlementsDeclaration;
@@ -0,0 +1,54 @@
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
+ */ // Types only: the manifest generator loads this module on its own to compile
17
+ // the plan figures, so nothing here may reach a core module at runtime.
18
+ import { _ as _extends } from "@swc/helpers/_/_extends";
19
+ /** Business and up. */ const FROM_BUSINESS = {
20
+ free: false,
21
+ starter: false,
22
+ pro: false,
23
+ business: true,
24
+ scale: true,
25
+ advanced: true,
26
+ agency: true,
27
+ enterprise: true
28
+ };
29
+ /** Pro and up. */ const FROM_PRO = _extends({}, FROM_BUSINESS, {
30
+ pro: true
31
+ });
32
+ /** Commerce's plan gates, for the manifest generator. */ export function commercePlanEntitlements() {
33
+ return {
34
+ features: [
35
+ {
36
+ key: 'storefrontSubscriptions',
37
+ label: 'Storefront subscriptions',
38
+ byPlan: FROM_BUSINESS
39
+ },
40
+ {
41
+ key: 'giftCards',
42
+ label: 'Gift cards',
43
+ byPlan: FROM_BUSINESS
44
+ },
45
+ {
46
+ key: 'commerceAnalytics',
47
+ label: 'Commerce analytics',
48
+ byPlan: FROM_PRO
49
+ }
50
+ ]
51
+ };
52
+ }
53
+
54
+ //# sourceMappingURL=plan-entitlements.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/plugins/commerce/src/lib/plan-entitlements.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// Types only: the manifest generator loads this module on its own to compile\n// the plan figures, so nothing here may reach a core module at runtime.\nimport type { OrgPlan } from '@aglyn/aglyn/foundation/definitions/org-billing.types'\nimport type { PluginPlanEntitlementsDeclaration } from '@aglyn/aglyn/plugin-manager/plugin-plan-entitlements'\n\n/**\n * COMMERCE'S PLAN GATES (AGL-3080): the storefront features that start above\n * the entry tiers. Compiled into core's `PLAN_ENTITLEMENTS` by the manifest\n * generator (`register.planEntitlements`), so the plan comparison and every\n * gate read the answers they always have.\n */\ndeclare module '@aglyn/aglyn/plugin-manager/plugin-entitlement-keys' {\n interface PluginEntitlementFeatures {\n /** Recurring storefront subscription products (AGL-303). */\n storefrontSubscriptions?: boolean\n /** Gift cards & store credit (AGL-322). */\n giftCards?: boolean\n /** Commerce analytics dashboard (AGL-327). */\n commerceAnalytics?: boolean\n }\n}\n\n/** Business and up. */\nconst FROM_BUSINESS: Readonly<Record<OrgPlan, boolean>> = {\n free: false,\n starter: false,\n pro: false,\n business: true,\n scale: true,\n advanced: true,\n agency: true,\n enterprise: true,\n}\n\n/** Pro and up. */\nconst FROM_PRO: Readonly<Record<OrgPlan, boolean>> = {\n ...FROM_BUSINESS,\n pro: true,\n}\n\n/** Commerce's plan gates, for the manifest generator. */\nexport function commercePlanEntitlements(): PluginPlanEntitlementsDeclaration {\n return {\n features: [\n {\n key: 'storefrontSubscriptions',\n label: 'Storefront subscriptions',\n byPlan: FROM_BUSINESS,\n },\n { key: 'giftCards', label: 'Gift cards', byPlan: FROM_BUSINESS },\n {\n key: 'commerceAnalytics',\n label: 'Commerce analytics',\n byPlan: FROM_PRO,\n },\n ],\n }\n}\n"],"names":["FROM_BUSINESS","free","starter","pro","business","scale","advanced","agency","enterprise","FROM_PRO","commercePlanEntitlements","features","key","label","byPlan"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,6EAA6E;AAC7E,wEAAwE;;AAqBxE,qBAAqB,GACrB,MAAMA,gBAAoD;IACxDC,MAAM;IACNC,SAAS;IACTC,KAAK;IACLC,UAAU;IACVC,OAAO;IACPC,UAAU;IACVC,QAAQ;IACRC,YAAY;AACd;AAEA,gBAAgB,GAChB,MAAMC,WAA+C,aAChDT;IACHG,KAAK;;AAGP,uDAAuD,GACvD,OAAO,SAASO;IACd,OAAO;QACLC,UAAU;YACR;gBACEC,KAAK;gBACLC,OAAO;gBACPC,QAAQd;YACV;YACA;gBAAEY,KAAK;gBAAaC,OAAO;gBAAcC,QAAQd;YAAc;YAC/D;gBACEY,KAAK;gBACLC,OAAO;gBACPC,QAAQL;YACV;SACD;IACH;AACF"}
package/src/lib/plugin.js CHANGED
@@ -22,6 +22,9 @@ const NewestSiteUsersCard = lazy(()=>import("./components/console/newest-site-us
22
22
  import { COMMERCE_CONSOLE_SECTIONS } from "./components/commerce-console-sections.js";
23
23
  import { BUNDLE_ID } from "./constants/bundle-common.js";
24
24
  import { COMMERCE_PERMISSIONS } from "./model/plugin-permissions.js";
25
+ import { registerCommerceRecordRoutes } from "./model/commerce-record-routes.js";
26
+ import { registerCommerceZones } from "./components/console/product-zones.js";
27
+ import { COMMERCE_SEARCH_SOURCES } from "./model/commerce-search-sources.js";
25
28
  import { COMMERCE_CONFIG_SCHEMA } from "./plugin-config.js";
26
29
  /** Code-split: the Products console page only loads when opened. */ const CommerceConsolePage = lazy(()=>import("./components/commerce-console-page.js"));
27
30
  /**
@@ -34,6 +37,11 @@ import { COMMERCE_CONFIG_SCHEMA } from "./plugin-config.js";
34
37
  * tracked as follow-up. The nav item omits a `Component`, so the shell's
35
38
  * nav renders the link and the existing named route serves the page.
36
39
  */ export function registerCommerceConsole() {
40
+ // Where a product is read, for surfaces outside this plugin (AGL-3080).
41
+ registerCommerceRecordRoutes();
42
+ // The zones its product editor, products hub and CSV import dialog host,
43
+ // with the props each hands a widget (AGL-2916, AGL-3080).
44
+ registerCommerceZones();
37
45
  // Plugin-declared permissions (AGL-435): tier defaults ride every
38
46
  // resolved role set; custom roles override key-by-key.
39
47
  Aglyn.registerPluginPermissions(COMMERCE_PERMISSIONS);
@@ -43,6 +51,7 @@ import { COMMERCE_CONFIG_SCHEMA } from "./plugin-config.js";
43
51
  Aglyn.registerConsoleExtension({
44
52
  pluginId: BUNDLE_ID,
45
53
  displayName: 'Commerce',
54
+ searchSources: COMMERCE_SEARCH_SOURCES,
46
55
  // Dashboard/analytics glance card (AGL-419): rendered through the
47
56
  // shell's 'commerceGlance' widget slot.
48
57
  widgets: [