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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/README.md +4 -2
  2. package/package.json +14 -13
  3. package/src/lib/components/console/catalog-organization-card.component.js +9 -0
  4. package/src/lib/components/console/catalog-organization-card.component.js.map +1 -1
  5. package/src/lib/components/console/discounts-card.component.js +8 -0
  6. package/src/lib/components/console/discounts-card.component.js.map +1 -1
  7. package/src/lib/components/console/gift-cards-card.component.js +8 -0
  8. package/src/lib/components/console/gift-cards-card.component.js.map +1 -1
  9. package/src/lib/components/console/host-coupons-card.component.js +8 -0
  10. package/src/lib/components/console/host-coupons-card.component.js.map +1 -1
  11. package/src/lib/components/console/host-orders-card.component.js +34 -70
  12. package/src/lib/components/console/host-orders-card.component.js.map +1 -1
  13. package/src/lib/components/console/product-zones.d.ts +7 -6
  14. package/src/lib/components/console/product-zones.js +5 -4
  15. package/src/lib/components/console/product-zones.js.map +1 -1
  16. package/src/lib/components/console/products-hub-card.component.js +86 -284
  17. package/src/lib/components/console/products-hub-card.component.js.map +1 -1
  18. package/src/lib/declarations.console-server.d.ts +38 -0
  19. package/src/lib/declarations.console-server.js +67 -0
  20. package/src/lib/declarations.console-server.js.map +1 -0
  21. package/src/lib/model/commerce-gift-cards.d.ts +26 -0
  22. package/src/lib/model/commerce-gift-cards.js +42 -0
  23. package/src/lib/model/commerce-gift-cards.js.map +1 -1
  24. package/src/lib/model/commerce-orders.d.ts +0 -57
  25. package/src/lib/model/commerce-orders.js +0 -80
  26. package/src/lib/model/commerce-orders.js.map +1 -1
  27. package/src/lib/model/index.d.ts +0 -1
  28. package/src/lib/model/index.js +0 -1
  29. package/src/lib/model/index.js.map +1 -1
  30. package/src/lib/plugin.js +5 -0
  31. package/src/lib/plugin.js.map +1 -1
  32. package/src/lib/server/api-v1/openapi.d.ts +27 -0
  33. package/src/lib/server/api-v1/openapi.js +219 -0
  34. package/src/lib/server/api-v1/openapi.js.map +1 -0
  35. package/src/lib/server/api-v1/orders-and-products.d.ts +19 -0
  36. package/src/lib/server/api-v1/orders-and-products.js +419 -0
  37. package/src/lib/server/api-v1/orders-and-products.js.map +1 -0
  38. package/src/lib/server/billing-webhook.js +10 -13
  39. package/src/lib/server/billing-webhook.js.map +1 -1
  40. package/src/lib/server/download.js +4 -1
  41. package/src/lib/server/download.js.map +1 -1
  42. package/src/lib/server/fulfill-order.d.ts +52 -11
  43. package/src/lib/server/fulfill-order.js +6 -9
  44. package/src/lib/server/fulfill-order.js.map +1 -1
  45. package/src/lib/server/gift-card-issue.d.ts +57 -0
  46. package/src/lib/server/gift-card-issue.js +155 -0
  47. package/src/lib/server/gift-card-issue.js.map +1 -0
  48. package/src/lib/server/gift-cards.js +25 -84
  49. package/src/lib/server/gift-cards.js.map +1 -1
  50. package/src/lib/server/membership-register.js +9 -8
  51. package/src/lib/server/membership-register.js.map +1 -1
  52. package/src/lib/server/newsletter.js +7 -5
  53. package/src/lib/server/newsletter.js.map +1 -1
  54. package/src/lib/server/paid-media-delivery.d.ts +21 -0
  55. package/src/lib/server/paid-media-delivery.js +23 -5
  56. package/src/lib/server/paid-media-delivery.js.map +1 -1
  57. package/src/lib/server/paid-media-serve-count.d.ts +28 -0
  58. package/src/lib/server/paid-media-serve-count.js +46 -0
  59. package/src/lib/server/paid-media-serve-count.js.map +1 -0
  60. package/src/lib/server/person-eraser.d.ts +45 -0
  61. package/src/lib/server/person-eraser.js +57 -0
  62. package/src/lib/server/person-eraser.js.map +1 -0
  63. package/src/lib/server/refund.js +15 -15
  64. package/src/lib/server/refund.js.map +1 -1
  65. package/src/lib/server/restock-flag.d.ts +2 -2
  66. package/src/lib/server/restock-flag.js +1 -1
  67. package/src/lib/server/restock-flag.js.map +1 -1
  68. package/src/lib/server/stream.js +4 -1
  69. package/src/lib/server/stream.js.map +1 -1
  70. package/src/lib/server.js +5 -21
  71. package/src/lib/server.js.map +1 -1
  72. package/src/lib/testing/stand-in-person-records.d.ts +43 -0
  73. package/src/lib/testing/stand-in-person-records.js +63 -0
  74. package/src/lib/testing/stand-in-person-records.js.map +1 -0
  75. package/src/lib/transfer/gift-card-confirm-step.component.d.ts +22 -0
  76. package/src/lib/transfer/gift-card-confirm-step.component.js +259 -0
  77. package/src/lib/transfer/gift-card-confirm-step.component.js.map +1 -0
  78. package/src/lib/transfer/gift-cards-plan.d.ts +29 -0
  79. package/src/lib/transfer/gift-cards-plan.js +40 -0
  80. package/src/lib/transfer/gift-cards-plan.js.map +1 -0
  81. package/src/lib/transfer/gift-cards.server.d.ts +34 -0
  82. package/src/lib/transfer/gift-cards.server.js +296 -0
  83. package/src/lib/transfer/gift-cards.server.js.map +1 -0
  84. package/src/lib/transfer/list-filter.d.ts +29 -0
  85. package/src/lib/transfer/list-filter.js +112 -0
  86. package/src/lib/transfer/list-filter.js.map +1 -0
  87. package/src/lib/transfer/product-import-results.d.ts +29 -0
  88. package/src/lib/transfer/product-import-results.js +49 -0
  89. package/src/lib/transfer/product-import-results.js.map +1 -0
  90. package/src/lib/transfer/product-import-step.component.d.ts +30 -0
  91. package/src/lib/transfer/product-import-step.component.js +99 -0
  92. package/src/lib/transfer/product-import-step.component.js.map +1 -0
  93. package/src/lib/transfer/product-transfer.d.ts +211 -0
  94. package/src/lib/transfer/product-transfer.js +1594 -0
  95. package/src/lib/transfer/product-transfer.js.map +1 -0
  96. package/src/lib/transfer/products.server.d.ts +44 -0
  97. package/src/lib/transfer/products.server.js +550 -0
  98. package/src/lib/transfer/products.server.js.map +1 -0
  99. package/src/lib/transfer/records-transfer.d.ts +182 -0
  100. package/src/lib/transfer/records-transfer.js +1153 -0
  101. package/src/lib/transfer/records-transfer.js.map +1 -0
  102. package/src/lib/transfer/records.server.d.ts +68 -0
  103. package/src/lib/transfer/records.server.js +455 -0
  104. package/src/lib/transfer/records.server.js.map +1 -0
  105. package/src/lib/transfer/register-transfer-resources.d.ts +17 -0
  106. package/src/lib/transfer/register-transfer-resources.js +171 -0
  107. package/src/lib/transfer/register-transfer-resources.js.map +1 -0
  108. package/src/lib/transfer/register-transfer-ui.d.ts +23 -0
  109. package/src/lib/transfer/register-transfer-ui.js +78 -0
  110. package/src/lib/transfer/register-transfer-ui.js.map +1 -0
  111. package/src/lib/transfer/server-common.d.ts +76 -0
  112. package/src/lib/transfer/server-common.js +165 -0
  113. package/src/lib/transfer/server-common.js.map +1 -0
  114. package/src/lib/transfer/transfer-header-actions.component.d.ts +39 -0
  115. package/src/lib/transfer/transfer-header-actions.component.js +79 -0
  116. package/src/lib/transfer/transfer-header-actions.component.js.map +1 -0
  117. package/src/lib/transfer/transfer-keys.d.ts +31 -0
  118. package/src/lib/transfer/transfer-keys.js +56 -0
  119. package/src/lib/transfer/transfer-keys.js.map +1 -0
  120. package/src/lib/model/commerce-io.d.ts +0 -36
  121. package/src/lib/model/commerce-io.js +0 -245
  122. package/src/lib/model/commerce-io.js.map +0 -1
  123. package/src/lib/server/contact-refund.d.ts +0 -117
  124. package/src/lib/server/contact-refund.js +0 -196
  125. package/src/lib/server/contact-refund.js.map +0 -1
@@ -0,0 +1,29 @@
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 { TransferPlanRefusal } from '@aglyn/aglyn/plugin-manager/plugin-transfer-resources';
18
+ /**
19
+ * The refusal for importing gift cards on a plan without them (AGL-3548),
20
+ * or `null` when the plan has them: the sentence the Gift cards card's issue
21
+ * route answers (`server/gift-cards.ts`), naming the plan that includes
22
+ * them, in the 403 `plan_required` body every transfer route answers a plan
23
+ * with. The `commerce.gift-cards` declaration gates imports only — the cards
24
+ * a workspace holds are listed, and exported, on every plan. Asked by the
25
+ * transfer gate (the resource's `planGate`) and again by each hook that
26
+ * issues or voids a card, so the sweep that resumes an import is refused
27
+ * alike.
28
+ */
29
+ export declare function giftCardsPlanRefusal(org: unknown): TransferPlanRefusal | null;
@@ -0,0 +1,40 @@
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 { checkEntitlement, planLabelGrantingFeature } from "@aglyn/aglyn/app-utils/plan-entitlements";
17
+ /**
18
+ * The refusal for importing gift cards on a plan without them (AGL-3548),
19
+ * or `null` when the plan has them: the sentence the Gift cards card's issue
20
+ * route answers (`server/gift-cards.ts`), naming the plan that includes
21
+ * them, in the 403 `plan_required` body every transfer route answers a plan
22
+ * with. The `commerce.gift-cards` declaration gates imports only — the cards
23
+ * a workspace holds are listed, and exported, on every plan. Asked by the
24
+ * transfer gate (the resource's `planGate`) and again by each hook that
25
+ * issues or voids a card, so the sweep that resumes an import is refused
26
+ * alike.
27
+ */ export function giftCardsPlanRefusal(org) {
28
+ if (checkEntitlement(org, 'giftCards')) return null;
29
+ const plan = planLabelGrantingFeature('giftCards');
30
+ return {
31
+ status: 403,
32
+ body: {
33
+ error: 'Gift cards are not included on this plan.' + (plan ? ` Included from ${plan}.` : ''),
34
+ reason: 'plan_required',
35
+ code: 'giftCards'
36
+ }
37
+ };
38
+ }
39
+
40
+ //# sourceMappingURL=gift-cards-plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/transfer/gift-cards-plan.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 { checkEntitlement, planLabelGrantingFeature } from '@aglyn/aglyn/app-utils/plan-entitlements'\nimport type { TransferPlanRefusal } from '@aglyn/aglyn/plugin-manager/plugin-transfer-resources'\n\n/**\n * The refusal for importing gift cards on a plan without them (AGL-3548),\n * or `null` when the plan has them: the sentence the Gift cards card's issue\n * route answers (`server/gift-cards.ts`), naming the plan that includes\n * them, in the 403 `plan_required` body every transfer route answers a plan\n * with. The `commerce.gift-cards` declaration gates imports only — the cards\n * a workspace holds are listed, and exported, on every plan. Asked by the\n * transfer gate (the resource's `planGate`) and again by each hook that\n * issues or voids a card, so the sweep that resumes an import is refused\n * alike.\n */\nexport function giftCardsPlanRefusal(org: unknown): TransferPlanRefusal | null {\n if (checkEntitlement(org as never, 'giftCards')) return null\n const plan = planLabelGrantingFeature('giftCards')\n return {\n status: 403,\n body: {\n error: 'Gift cards are not included on this plan.' + (plan ? ` Included from ${plan}.` : ''),\n reason: 'plan_required',\n code: 'giftCards',\n },\n }\n}\n"],"names":["checkEntitlement","planLabelGrantingFeature","giftCardsPlanRefusal","org","plan","status","body","error","reason","code"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,gBAAgB,EAAEC,wBAAwB,QAAQ,2CAA0C;AAGrG;;;;;;;;;;CAUC,GACD,OAAO,SAASC,qBAAqBC,GAAY;IAC/C,IAAIH,iBAAiBG,KAAc,cAAc,OAAO;IACxD,MAAMC,OAAOH,yBAAyB;IACtC,OAAO;QACLI,QAAQ;QACRC,MAAM;YACJC,OAAO,8CAA+CH,CAAAA,OAAO,CAAC,eAAe,EAAEA,KAAK,CAAC,CAAC,GAAG,EAAC;YAC1FI,QAAQ;YACRC,MAAM;QACR;IACF;AACF"}
@@ -0,0 +1,34 @@
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 BuildTransferPlanInput, type TransferChunk, type PlannedTransferRow, type TransferPlan, type TransferUndoSnapshot } from '@aglyn/aglyn/data-transfer';
18
+ import type { PluginTransferResource, TransferApplyResult, TransferApplyWriter, TransferResourceContext, TransferRevertResult } from '@aglyn/aglyn/plugin-manager/plugin-transfer-resources';
19
+ /**
20
+ * The dry run: the core's plan with a Balance required of every card, made
21
+ * into the cards it issues. The codes the file names are read once, so a
22
+ * code another card holds — however the file spaced or cased it — is never
23
+ * planned as a new card.
24
+ */
25
+ export declare function planGiftCards(ctx: TransferResourceContext, input: BuildTransferPlanInput): Promise<TransferPlan>;
26
+ /** Issues one chunk's cards, each through the Gift cards card's own issue path. */
27
+ export declare function applyGiftCards(ctx: TransferResourceContext, chunk: TransferChunk<PlannedTransferRow>, writer: TransferApplyWriter): Promise<TransferApplyResult>;
28
+ /**
29
+ * Undoes one chunk: each card voided — zeroed, never deleted — when nobody
30
+ * has spent from it; a spent or held card is a conflict that stays as it is,
31
+ * whatever the person decided, because a shopper holds that money.
32
+ */
33
+ export declare function revertGiftCards(ctx: TransferResourceContext, snapshot: TransferUndoSnapshot): Promise<TransferRevertResult>;
34
+ export declare const giftCardsTransfer: PluginTransferResource;
@@ -0,0 +1,296 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ /**
3
+ * @license
4
+ * Copyright 2026 Aglyn LLC
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */ import { planTransferUndo } from "@aglyn/aglyn/data-transfer";
18
+ import { firebaseAdmin, getOrgForHost, logHostActivity, resolveOrgMembership } from "@aglyn/tenant-data-admin";
19
+ import { TransferEngineError, TransferPlanRefusedError } from "@aglyn/tenant-data-admin/server/transfer-jobs";
20
+ import { hostRoleFor } from "@aglyn/aglyn/app-utils/organizations";
21
+ import { transferImportRoleAllowed, transferImportRoleRefusal } from "@aglyn/aglyn/data-transfer";
22
+ import { declaredTransferResource } from "@aglyn/aglyn/plugin-manager/plugin-transfer-resources";
23
+ import { COMMERCE_GIFT_CARDS_TRANSFER } from "./transfer-keys.js";
24
+ import { giftCardsPlanRefusal } from "./gift-cards-plan.js";
25
+ import { giftCardCodeOf, giftCardRedeemed } from "../model/commerce-gift-cards.js";
26
+ import { GIFT_CARD_ACTIVITY_TYPE, GiftCardIssueRefusal, giftCardActivityName, issueGiftCard, mintGiftCardCode } from "../server/gift-card-issue.js";
27
+ import { GIFT_CARD_MATCH_KEYS, GIFT_CARD_UNDO_FIELDS, giftCardRecord, planGiftCardRows, planWithCreateRequired, readGiftCardConfirmation } from "./records-transfer.js";
28
+ import { readableRecords } from "./records.server.js";
29
+ import { ROW_BUDGET_MS, firestoreOf, hostRefOf } from "./server-common.js";
30
+ /** A code that could be a card's: the document id `in` query refuses anything with a slash. */ const CARD_ID = /^[A-Z0-9][A-Z0-9_-]*$/;
31
+ const GIFT_CARDS = {
32
+ collection: (firestore, ctx)=>hostRefOf(firestore, ctx).collection('giftCards'),
33
+ record: (id, data)=>giftCardRecord(id, data),
34
+ matchValues: (id)=>({
35
+ id,
36
+ code: id
37
+ }),
38
+ // A card's code is its document id: a code is looked up by id. A value
39
+ // that cannot be a code still asks one id no card holds, rather than an
40
+ // empty `in`, which the store refuses.
41
+ find: {
42
+ code: (collection, values)=>{
43
+ const codes = values.map((value)=>giftCardCodeOf(value)).filter((code)=>CARD_ID.test(code));
44
+ return collection.where(firebaseAdmin.firestore.FieldPath.documentId(), 'in', codes.length ? codes : [
45
+ '-'
46
+ ]);
47
+ }
48
+ }
49
+ };
50
+ /** The member importing, checked for the role and the plan, or the refusal. */ async function issuerOf(ctx) {
51
+ var _ref;
52
+ var _membership_member;
53
+ const hostId = ctx.hostId;
54
+ if (!hostId) throw new TransferEngineError('invalid', 400, 'Gift cards belong to a site; name the site.');
55
+ const uid = ctx.actorUid;
56
+ const membership = uid ? await resolveOrgMembership(uid, ctx.orgId) : null;
57
+ // The declaration's `importRoles` (AGL-3554), which the transfer gate
58
+ // asked already; asked again here for the sweep that resumes an import.
59
+ const declared = declaredTransferResource(COMMERCE_GIFT_CARDS_TRANSFER);
60
+ const role = (membership == null ? void 0 : membership.member) ? hostRoleFor(membership.member, hostId) : null;
61
+ if (!uid || !declared || !transferImportRoleAllowed('import', declared, role)) {
62
+ throw new TransferEngineError('forbidden', 403, declared ? transferImportRoleRefusal(declared) : 'Gift cards can’t be imported here.');
63
+ }
64
+ const owner = await getOrgForHost(hostId).catch(()=>null);
65
+ // The transfer gate asked already (the resource's `planGate`); asked again
66
+ // here, in the same words, for the sweep that resumes an import.
67
+ const refusal = giftCardsPlanRefusal(owner == null ? void 0 : owner.org);
68
+ if (refusal) throw new TransferPlanRefusedError(refusal);
69
+ const email = (_ref = membership == null ? void 0 : (_membership_member = membership.member) == null ? void 0 : _membership_member.email) != null ? _ref : null;
70
+ return {
71
+ hostId,
72
+ uid,
73
+ email,
74
+ ownerOrg: owner == null ? void 0 : owner.org
75
+ };
76
+ }
77
+ /** The codes among `codes` that a card on the site already holds. */ async function takenCodes(firestore, ctx, codes) {
78
+ const collection = GIFT_CARDS.collection(firestore, ctx);
79
+ const taken = new Set();
80
+ const ids = [
81
+ ...new Set(codes.filter((code)=>CARD_ID.test(code)))
82
+ ];
83
+ for(let at = 0; at < ids.length; at += 100){
84
+ for (const doc of (await firestore.getAll(...ids.slice(at, at + 100).map((id)=>collection.doc(id))))){
85
+ if (doc.exists) taken.add(doc.id);
86
+ }
87
+ }
88
+ return taken;
89
+ }
90
+ /**
91
+ * The dry run: the core's plan with a Balance required of every card, made
92
+ * into the cards it issues. The codes the file names are read once, so a
93
+ * code another card holds — however the file spaced or cased it — is never
94
+ * planned as a new card.
95
+ */ export async function planGiftCards(ctx, input) {
96
+ await issuerOf(ctx);
97
+ const firestore = firestoreOf();
98
+ const rows = input.rows.map((row)=>'code' in row.values ? _extends({}, row, {
99
+ values: _extends({}, row.values, {
100
+ code: giftCardCodeOf(row.values['code']) || null
101
+ })
102
+ }) : row);
103
+ const taken = await takenCodes(firestore, ctx, rows.map((row)=>{
104
+ var _row_values_code;
105
+ return String((_row_values_code = row.values['code']) != null ? _row_values_code : '');
106
+ }));
107
+ return planGiftCardRows(planWithCreateRequired(_extends({}, input, {
108
+ rows
109
+ }), [
110
+ 'balance'
111
+ ]), {
112
+ confirmation: readGiftCardConfirmation(ctx.extras),
113
+ mintCode: mintGiftCardCode,
114
+ taken
115
+ });
116
+ }
117
+ /** Whether the card under `code` is the one this job's `row` issued: a retried chunk finds its own card. */ async function issuedByRow(firestore, ctx, code, row) {
118
+ const doc = await GIFT_CARDS.collection(firestore, ctx).doc(code).get();
119
+ return doc.exists && doc.get('importJobId') === ctx.jobId && doc.get('importRow') === row;
120
+ }
121
+ /** Issues one chunk's cards, each through the Gift cards card's own issue path. */ export async function applyGiftCards(ctx, chunk, writer) {
122
+ const issuer = await issuerOf(ctx);
123
+ const confirmation = readGiftCardConfirmation(ctx.extras);
124
+ const firestore = firestoreOf();
125
+ const results = [];
126
+ const undo = [];
127
+ const refuse = async (row, message)=>{
128
+ const result = {
129
+ row,
130
+ outcome: 'failed',
131
+ reason: 'refusedValue',
132
+ message
133
+ };
134
+ results.push(result);
135
+ await writer.markApplied(result);
136
+ };
137
+ for (const row of chunk.rows){
138
+ var _issued_data;
139
+ const earlier = await writer.alreadyApplied(row.index);
140
+ if (earlier) {
141
+ results.push(earlier);
142
+ continue;
143
+ }
144
+ if (writer.timeLeftMs() < ROW_BUDGET_MS) break;
145
+ const card = row.giftCard;
146
+ if (row.verdict !== 'create' || !card) {
147
+ await refuse(row.index, 'A gift card import only issues new cards; it never changes one.');
148
+ continue;
149
+ }
150
+ if (card.problem) {
151
+ await refuse(row.index, card.problem);
152
+ continue;
153
+ }
154
+ // The typed total, read again here: the plan's verdict is not enough.
155
+ if (!card.confirmed || !confirmation || confirmation.totalCents !== card.totalCents) {
156
+ await refuse(row.index, 'The total value was not confirmed, so no card was issued. Confirm the total and import again.');
157
+ continue;
158
+ }
159
+ try {
160
+ var _ctx_jobId;
161
+ await issueGiftCard({
162
+ firestore,
163
+ hostId: issuer.hostId,
164
+ amountCents: card.amountCents,
165
+ recipientEmail: card.recipientEmail,
166
+ note: card.note,
167
+ code: card.code,
168
+ issuer: {
169
+ uid: issuer.uid,
170
+ email: issuer.email
171
+ },
172
+ email: confirmation.email,
173
+ ownerOrg: issuer.ownerOrg,
174
+ source: {
175
+ importJobId: (_ctx_jobId = ctx.jobId) != null ? _ctx_jobId : '',
176
+ importRow: row.index
177
+ }
178
+ });
179
+ } catch (error) {
180
+ if (!(error instanceof GiftCardIssueRefusal)) throw error;
181
+ // A retry after the card landed and before its row was recorded.
182
+ if (!(error.reason === 'taken' && await issuedByRow(firestore, ctx, card.code, row.index))) {
183
+ await refuse(row.index, error.message);
184
+ continue;
185
+ }
186
+ }
187
+ const issued = await GIFT_CARDS.collection(firestore, ctx).doc(card.code).get();
188
+ const record = giftCardRecord(card.code, (_issued_data = issued.data()) != null ? _issued_data : {});
189
+ const result = {
190
+ row: row.index,
191
+ outcome: 'created',
192
+ recordId: card.code
193
+ };
194
+ const entry = {
195
+ row: row.index,
196
+ recordId: card.code,
197
+ action: 'created',
198
+ written: Object.fromEntries(GIFT_CARD_UNDO_FIELDS.map((fieldId)=>{
199
+ var _record_fieldId;
200
+ return [
201
+ fieldId,
202
+ (_record_fieldId = record[fieldId]) != null ? _record_fieldId : null
203
+ ];
204
+ }))
205
+ };
206
+ results.push(result);
207
+ undo.push(entry);
208
+ await writer.markApplied(result, entry);
209
+ }
210
+ return {
211
+ results,
212
+ undo
213
+ };
214
+ }
215
+ /**
216
+ * Undoes one chunk: each card voided — zeroed, never deleted — when nobody
217
+ * has spent from it; a spent or held card is a conflict that stays as it is,
218
+ * whatever the person decided, because a shopper holds that money.
219
+ */ export async function revertGiftCards(ctx, snapshot) {
220
+ const issuer = await issuerOf(ctx);
221
+ const firestore = firestoreOf();
222
+ const collection = GIFT_CARDS.collection(firestore, ctx);
223
+ const done = [];
224
+ const conflicts = [];
225
+ for (const entry of [
226
+ ...snapshot.entries
227
+ ].reverse()){
228
+ const ref = collection.doc(entry.recordId);
229
+ const step = await firestore.runTransaction(async (tx)=>{
230
+ var _doc_data, _ctx_jobId;
231
+ const doc = await tx.get(ref);
232
+ if (!doc.exists) return {
233
+ action: 'nothing',
234
+ recordId: entry.recordId,
235
+ why: 'gone'
236
+ };
237
+ const card = (_doc_data = doc.data()) != null ? _doc_data : {};
238
+ if (Number(card.voidedAtMs) > 0) return {
239
+ action: 'nothing',
240
+ recordId: entry.recordId,
241
+ why: 'alreadyReverted'
242
+ };
243
+ if (giftCardRedeemed(card)) {
244
+ const planned = planTransferUndo(entry, giftCardRecord(entry.recordId, card));
245
+ return {
246
+ action: 'conflict',
247
+ recordId: entry.recordId,
248
+ fields: planned.action === 'conflict' ? planned.fields : [
249
+ 'balance'
250
+ ],
251
+ values: {}
252
+ };
253
+ }
254
+ tx.set(ref, {
255
+ balanceCents: 0,
256
+ voidedAtMs: Date.now(),
257
+ voidedBy: issuer.uid,
258
+ voidedByImportUndo: (_ctx_jobId = ctx.jobId) != null ? _ctx_jobId : null
259
+ }, {
260
+ merge: true
261
+ });
262
+ return {
263
+ action: 'delete',
264
+ recordId: entry.recordId
265
+ };
266
+ });
267
+ if (step.action === 'conflict') {
268
+ conflicts.push(step);
269
+ continue;
270
+ }
271
+ if (step.action === 'delete') {
272
+ await logHostActivity(issuer.hostId, {
273
+ uid: issuer.uid,
274
+ email: issuer.email
275
+ }, 'Voided gift card: import undone', {
276
+ type: GIFT_CARD_ACTIVITY_TYPE,
277
+ id: entry.recordId,
278
+ name: giftCardActivityName(entry.recordId)
279
+ });
280
+ }
281
+ done.push(step);
282
+ }
283
+ return {
284
+ done,
285
+ conflicts
286
+ };
287
+ }
288
+ export const giftCardsTransfer = _extends({
289
+ matchKeys: GIFT_CARD_MATCH_KEYS
290
+ }, readableRecords(GIFT_CARDS), {
291
+ plan: (ctx, input)=>planGiftCards(ctx, input),
292
+ apply: (ctx, chunk, writer)=>applyGiftCards(ctx, chunk, writer),
293
+ revert: (ctx, snapshot)=>revertGiftCards(ctx, snapshot)
294
+ });
295
+
296
+ //# sourceMappingURL=gift-cards.server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/transfer/gift-cards.server.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 {\n planTransferUndo,\n type BuildTransferPlanInput,\n type TransferChunk,\n type PlannedTransferRow,\n type TransferPlan,\n type TransferRowResult,\n type TransferUndoEntry,\n type TransferUndoSnapshot,\n type TransferUndoStep,\n} from '@aglyn/aglyn/data-transfer'\nimport type {\n PluginTransferResource,\n TransferApplyResult,\n TransferApplyWriter,\n TransferResourceContext,\n TransferRevertResult,\n} from '@aglyn/aglyn/plugin-manager/plugin-transfer-resources'\nimport { firebaseAdmin, getOrgForHost, logHostActivity, resolveOrgMembership } from '@aglyn/tenant-data-admin'\nimport { TransferEngineError, TransferPlanRefusedError } from '@aglyn/tenant-data-admin/server/transfer-jobs'\nimport { hostRoleFor } from '@aglyn/aglyn/app-utils/organizations'\nimport { transferImportRoleAllowed, transferImportRoleRefusal } from '@aglyn/aglyn/data-transfer'\nimport { declaredTransferResource } from '@aglyn/aglyn/plugin-manager/plugin-transfer-resources'\nimport { COMMERCE_GIFT_CARDS_TRANSFER } from './transfer-keys'\nimport { giftCardsPlanRefusal } from './gift-cards-plan'\nimport { giftCardCodeOf, giftCardRedeemed } from '../model/commerce-gift-cards'\nimport {\n GIFT_CARD_ACTIVITY_TYPE,\n GiftCardIssueRefusal,\n giftCardActivityName,\n issueGiftCard,\n mintGiftCardCode,\n} from '../server/gift-card-issue'\nimport {\n GIFT_CARD_MATCH_KEYS,\n GIFT_CARD_UNDO_FIELDS,\n giftCardRecord,\n planGiftCardRows,\n planWithCreateRequired,\n readGiftCardConfirmation,\n type GiftCardPlannedRow,\n type StoredGiftCard,\n} from './records-transfer'\nimport { readableRecords, type RecordsSpec } from './records.server'\nimport { ROW_BUDGET_MS, firestoreOf, hostRefOf, type Firestore } from './server-common'\n\n/*\n * THE GIFT CARD IMPORT'S SERVER HALF (AGL-3551). A file issues cards; it\n * never writes a balance. The plan (`planGiftCardRows`) decides each card and\n * the total; this module makes the three promises the dry run states:\n *\n * - WHO: the workspace's owners and admins, and a site's admins (the\n * declaration's `importRoles: [\"admin\"]`, AGL-3554), on a plan that\n * includes gift cards. The plan,\n * the apply and the undo each ask again, server-side.\n * - HOW: each card through `issueGiftCard`, the Gift cards card's own issue\n * path — so its amount is bounded, its code is created and never\n * overwritten, its activity line is written — and only when the total the\n * person typed on the Confirm step is the total the plan counted.\n * - UNDO: a card nobody has spent from is voided (zeroed, never deleted,\n * AGL-1767). A card with a redemption, or a checkout holding part of it,\n * is reported as a conflict and left as it is, whatever the decision says:\n * that money has been spent, or is being spent, by a shopper.\n */\n\ntype Doc = Record<string, unknown>\n\n/** A code that could be a card's: the document id `in` query refuses anything with a slash. */\nconst CARD_ID = /^[A-Z0-9][A-Z0-9_-]*$/\n\nconst GIFT_CARDS: RecordsSpec = {\n collection: (firestore, ctx) => hostRefOf(firestore, ctx).collection('giftCards'),\n record: (id, data) => giftCardRecord(id, data as StoredGiftCard),\n matchValues: (id) => ({ id, code: id }),\n // A card's code is its document id: a code is looked up by id. A value\n // that cannot be a code still asks one id no card holds, rather than an\n // empty `in`, which the store refuses.\n find: {\n code: (collection, values) => {\n const codes = values.map((value) => giftCardCodeOf(value)).filter((code) => CARD_ID.test(code))\n return collection.where(firebaseAdmin.firestore.FieldPath.documentId(), 'in', codes.length ? codes : ['-'])\n },\n },\n}\n\n\ninterface GiftCardIssuer {\n hostId: string\n uid: string\n email: string | null\n ownerOrg: unknown\n}\n\n/** The member importing, checked for the role and the plan, or the refusal. */\nasync function issuerOf(ctx: TransferResourceContext): Promise<GiftCardIssuer> {\n const hostId = ctx.hostId\n if (!hostId) throw new TransferEngineError('invalid', 400, 'Gift cards belong to a site; name the site.')\n const uid = ctx.actorUid\n const membership = uid ? await resolveOrgMembership(uid, ctx.orgId) : null\n // The declaration's `importRoles` (AGL-3554), which the transfer gate\n // asked already; asked again here for the sweep that resumes an import.\n const declared = declaredTransferResource(COMMERCE_GIFT_CARDS_TRANSFER)\n const role = membership?.member ? hostRoleFor(membership.member, hostId as never) : null\n if (!uid || !declared || !transferImportRoleAllowed('import', declared, role)) {\n throw new TransferEngineError(\n 'forbidden',\n 403,\n declared ? transferImportRoleRefusal(declared) : 'Gift cards can’t be imported here.',\n )\n }\n const owner = await getOrgForHost(hostId).catch(() => null)\n // The transfer gate asked already (the resource's `planGate`); asked again\n // here, in the same words, for the sweep that resumes an import.\n const refusal = giftCardsPlanRefusal(owner?.org)\n if (refusal) throw new TransferPlanRefusedError(refusal)\n const email = (membership?.member as { email?: string } | undefined)?.email ?? null\n return { hostId, uid, email, ownerOrg: owner?.org }\n}\n\n/** The codes among `codes` that a card on the site already holds. */\nasync function takenCodes(firestore: Firestore, ctx: TransferResourceContext, codes: readonly string[]): Promise<Set<string>> {\n const collection = GIFT_CARDS.collection(firestore, ctx)\n const taken = new Set<string>()\n const ids = [...new Set(codes.filter((code) => CARD_ID.test(code)))]\n for (let at = 0; at < ids.length; at += 100) {\n for (const doc of await firestore.getAll(...ids.slice(at, at + 100).map((id) => collection.doc(id)))) {\n if (doc.exists) taken.add(doc.id)\n }\n }\n return taken\n}\n\n/**\n * The dry run: the core's plan with a Balance required of every card, made\n * into the cards it issues. The codes the file names are read once, so a\n * code another card holds — however the file spaced or cased it — is never\n * planned as a new card.\n */\nexport async function planGiftCards(ctx: TransferResourceContext, input: BuildTransferPlanInput): Promise<TransferPlan> {\n await issuerOf(ctx)\n const firestore = firestoreOf()\n const rows = input.rows.map((row) =>\n 'code' in row.values ? { ...row, values: { ...row.values, code: giftCardCodeOf(row.values['code']) || null } } : row,\n )\n const taken = await takenCodes(\n firestore,\n ctx,\n rows.map((row) => String(row.values['code'] ?? '')),\n )\n return planGiftCardRows(planWithCreateRequired({ ...input, rows }, ['balance']), {\n confirmation: readGiftCardConfirmation(ctx.extras),\n mintCode: mintGiftCardCode,\n taken,\n })\n}\n\n/** Whether the card under `code` is the one this job's `row` issued: a retried chunk finds its own card. */\nasync function issuedByRow(firestore: Firestore, ctx: TransferResourceContext, code: string, row: number): Promise<boolean> {\n const doc = await GIFT_CARDS.collection(firestore, ctx).doc(code).get()\n return doc.exists && doc.get('importJobId') === ctx.jobId && doc.get('importRow') === row\n}\n\n/** Issues one chunk's cards, each through the Gift cards card's own issue path. */\nexport async function applyGiftCards(\n ctx: TransferResourceContext,\n chunk: TransferChunk<PlannedTransferRow>,\n writer: TransferApplyWriter,\n): Promise<TransferApplyResult> {\n const issuer = await issuerOf(ctx)\n const confirmation = readGiftCardConfirmation(ctx.extras)\n const firestore = firestoreOf()\n const results: TransferRowResult[] = []\n const undo: TransferUndoEntry[] = []\n const refuse = async (row: number, message: string) => {\n const result: TransferRowResult = { row, outcome: 'failed', reason: 'refusedValue', message }\n results.push(result)\n await writer.markApplied(result)\n }\n for (const row of chunk.rows as GiftCardPlannedRow[]) {\n const earlier = await writer.alreadyApplied(row.index)\n if (earlier) {\n results.push(earlier)\n continue\n }\n if (writer.timeLeftMs() < ROW_BUDGET_MS) break\n const card = row.giftCard\n if (row.verdict !== 'create' || !card) {\n await refuse(row.index, 'A gift card import only issues new cards; it never changes one.')\n continue\n }\n if (card.problem) {\n await refuse(row.index, card.problem)\n continue\n }\n // The typed total, read again here: the plan's verdict is not enough.\n if (!card.confirmed || !confirmation || confirmation.totalCents !== card.totalCents) {\n await refuse(row.index, 'The total value was not confirmed, so no card was issued. Confirm the total and import again.')\n continue\n }\n try {\n await issueGiftCard({\n firestore,\n hostId: issuer.hostId,\n amountCents: card.amountCents,\n recipientEmail: card.recipientEmail,\n note: card.note,\n code: card.code,\n issuer: { uid: issuer.uid, email: issuer.email },\n email: confirmation.email,\n ownerOrg: issuer.ownerOrg,\n source: { importJobId: ctx.jobId ?? '', importRow: row.index },\n })\n } catch (error) {\n if (!(error instanceof GiftCardIssueRefusal)) throw error\n // A retry after the card landed and before its row was recorded.\n if (!(error.reason === 'taken' && (await issuedByRow(firestore, ctx, card.code, row.index)))) {\n await refuse(row.index, error.message)\n continue\n }\n }\n const issued = await GIFT_CARDS.collection(firestore, ctx).doc(card.code).get()\n const record = giftCardRecord(card.code, (issued.data() ?? {}) as StoredGiftCard)\n const result: TransferRowResult = { row: row.index, outcome: 'created', recordId: card.code }\n const entry: TransferUndoEntry = {\n row: row.index,\n recordId: card.code,\n action: 'created',\n written: Object.fromEntries(GIFT_CARD_UNDO_FIELDS.map((fieldId) => [fieldId, record[fieldId] ?? null])),\n }\n results.push(result)\n undo.push(entry)\n await writer.markApplied(result, entry)\n }\n return { results, undo }\n}\n\n/**\n * Undoes one chunk: each card voided — zeroed, never deleted — when nobody\n * has spent from it; a spent or held card is a conflict that stays as it is,\n * whatever the person decided, because a shopper holds that money.\n */\nexport async function revertGiftCards(ctx: TransferResourceContext, snapshot: TransferUndoSnapshot): Promise<TransferRevertResult> {\n const issuer = await issuerOf(ctx)\n const firestore = firestoreOf()\n const collection = GIFT_CARDS.collection(firestore, ctx)\n const done: TransferUndoStep[] = []\n const conflicts: TransferUndoStep[] = []\n for (const entry of [...snapshot.entries].reverse()) {\n const ref = collection.doc(entry.recordId)\n const step = await firestore.runTransaction(async (tx): Promise<TransferUndoStep> => {\n const doc = await tx.get(ref)\n if (!doc.exists) return { action: 'nothing', recordId: entry.recordId, why: 'gone' }\n const card = (doc.data() ?? {}) as StoredGiftCard & Doc\n if (Number(card.voidedAtMs) > 0) return { action: 'nothing', recordId: entry.recordId, why: 'alreadyReverted' }\n if (giftCardRedeemed(card)) {\n const planned = planTransferUndo(entry, giftCardRecord(entry.recordId, card))\n return {\n action: 'conflict',\n recordId: entry.recordId,\n fields: planned.action === 'conflict' ? planned.fields : ['balance'],\n values: {},\n }\n }\n tx.set(\n ref,\n { balanceCents: 0, voidedAtMs: Date.now(), voidedBy: issuer.uid, voidedByImportUndo: ctx.jobId ?? null },\n { merge: true },\n )\n return { action: 'delete', recordId: entry.recordId }\n })\n if (step.action === 'conflict') {\n conflicts.push(step)\n continue\n }\n if (step.action === 'delete') {\n await logHostActivity(\n issuer.hostId,\n { uid: issuer.uid, email: issuer.email },\n 'Voided gift card: import undone',\n { type: GIFT_CARD_ACTIVITY_TYPE, id: entry.recordId, name: giftCardActivityName(entry.recordId) },\n )\n }\n done.push(step)\n }\n return { done, conflicts }\n}\n\nexport const giftCardsTransfer: PluginTransferResource = {\n matchKeys: GIFT_CARD_MATCH_KEYS,\n ...readableRecords(GIFT_CARDS),\n plan: (ctx, input) => planGiftCards(ctx, input),\n apply: (ctx, chunk, writer) => applyGiftCards(ctx, chunk, writer),\n revert: (ctx, snapshot) => revertGiftCards(ctx, snapshot),\n}\n"],"names":["planTransferUndo","firebaseAdmin","getOrgForHost","logHostActivity","resolveOrgMembership","TransferEngineError","TransferPlanRefusedError","hostRoleFor","transferImportRoleAllowed","transferImportRoleRefusal","declaredTransferResource","COMMERCE_GIFT_CARDS_TRANSFER","giftCardsPlanRefusal","giftCardCodeOf","giftCardRedeemed","GIFT_CARD_ACTIVITY_TYPE","GiftCardIssueRefusal","giftCardActivityName","issueGiftCard","mintGiftCardCode","GIFT_CARD_MATCH_KEYS","GIFT_CARD_UNDO_FIELDS","giftCardRecord","planGiftCardRows","planWithCreateRequired","readGiftCardConfirmation","readableRecords","ROW_BUDGET_MS","firestoreOf","hostRefOf","CARD_ID","GIFT_CARDS","collection","firestore","ctx","record","id","data","matchValues","code","find","values","codes","map","value","filter","test","where","FieldPath","documentId","length","issuerOf","membership","hostId","uid","actorUid","orgId","declared","role","member","owner","catch","refusal","org","email","ownerOrg","takenCodes","taken","Set","ids","at","doc","getAll","slice","exists","add","planGiftCards","input","rows","row","String","confirmation","extras","mintCode","issuedByRow","get","jobId","applyGiftCards","chunk","writer","issuer","results","undo","refuse","message","result","outcome","reason","push","markApplied","issued","earlier","alreadyApplied","index","timeLeftMs","card","giftCard","verdict","problem","confirmed","totalCents","amountCents","recipientEmail","note","source","importJobId","importRow","error","recordId","entry","action","written","Object","fromEntries","fieldId","revertGiftCards","snapshot","done","conflicts","entries","reverse","ref","step","runTransaction","tx","why","Number","voidedAtMs","planned","fields","set","balanceCents","Date","now","voidedBy","voidedByImportUndo","merge","type","name","giftCardsTransfer","matchKeys","plan","apply","revert"],"mappings":"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"}
@@ -0,0 +1,29 @@
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 { ListQueryDeclaration, ListQueryFilter, ListQueryPlan, ListQuerySort } from '@aglyn/shared-ui-jsx/const/list-query-plan';
18
+ /** What a list sends as its export filter. */
19
+ export interface CommerceListFilter {
20
+ filters: ListQueryFilter[];
21
+ orderBy: ListQuerySort;
22
+ }
23
+ /** The filter a list hands the export dialog: its plan's predicates and order, as plain data. */
24
+ export declare function commerceListFilter(plan: Pick<ListQueryPlan, 'filters' | 'orderBy'>): CommerceListFilter;
25
+ /**
26
+ * The filter a request carried, checked against the list's declaration.
27
+ * Throws a sentence naming what was refused.
28
+ */
29
+ export declare function readCommerceListFilter(value: unknown, declaration: ListQueryDeclaration, base?: readonly string[]): CommerceListFilter;
@@ -0,0 +1,112 @@
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
+ */ const OPS = [
17
+ '==',
18
+ '!=',
19
+ '<',
20
+ '<=',
21
+ '>',
22
+ '>=',
23
+ 'in',
24
+ 'array-contains',
25
+ 'array-contains-any'
26
+ ];
27
+ /** The id path a plan names the document id by. */ const ID_PATH = '__name__';
28
+ /** The filter a list hands the export dialog: its plan's predicates and order, as plain data. */ export function commerceListFilter(plan) {
29
+ return {
30
+ filters: plan.filters.map((filter)=>({
31
+ path: filter.path,
32
+ op: filter.op,
33
+ value: filter.value instanceof Date ? filter.value.getTime() : Array.isArray(filter.value) ? [
34
+ ...filter.value
35
+ ] : filter.value
36
+ })),
37
+ orderBy: {
38
+ path: plan.orderBy.path,
39
+ direction: plan.orderBy.direction
40
+ }
41
+ };
42
+ }
43
+ /** Every path the declaration lets a query name. */ function allowedPaths(declaration, base) {
44
+ const paths = new Set([
45
+ ID_PATH,
46
+ ...base
47
+ ]);
48
+ for (const field of declaration.fields){
49
+ for (const path of [
50
+ field.path,
51
+ field.lowerPath,
52
+ field.tokensPath,
53
+ field.containsOrderBy
54
+ ]){
55
+ if (path) paths.add(path);
56
+ }
57
+ }
58
+ if (declaration.search) paths.add(declaration.search.tokensPath);
59
+ for (const sort of declaration.sorts)paths.add(sort.path);
60
+ return paths;
61
+ }
62
+ const plain = (value)=>value === null || [
63
+ 'string',
64
+ 'number',
65
+ 'boolean'
66
+ ].includes(typeof value);
67
+ /**
68
+ * The filter a request carried, checked against the list's declaration.
69
+ * Throws a sentence naming what was refused.
70
+ */ export function readCommerceListFilter(value, declaration, base = []) {
71
+ var _raw_orderBy, _ref;
72
+ var _declaration_sorts_;
73
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
74
+ throw new Error('The filter could not be read.');
75
+ }
76
+ const raw = value;
77
+ const paths = allowedPaths(declaration, base);
78
+ const filters = [];
79
+ for (const entry of Array.isArray(raw.filters) ? raw.filters : []){
80
+ const { path, op, value: operand } = entry != null ? entry : {};
81
+ if (typeof path !== 'string' || !paths.has(path)) {
82
+ throw new Error(`The filter names "${String(path)}", which this list does not filter by.`);
83
+ }
84
+ if (!OPS.includes(op)) throw new Error(`The filter uses an unknown operator.`);
85
+ const list = op === 'in' || op === 'array-contains-any';
86
+ if (list) {
87
+ if (!Array.isArray(operand) || !operand.length || operand.length > 30 || !operand.every(plain)) {
88
+ throw new Error('The filter holds a list it cannot ask for.');
89
+ }
90
+ } else if (!plain(operand)) {
91
+ throw new Error('The filter holds a value it cannot ask for.');
92
+ }
93
+ filters.push({
94
+ path,
95
+ op: op,
96
+ value: operand
97
+ });
98
+ }
99
+ const order = (_raw_orderBy = raw.orderBy) != null ? _raw_orderBy : {};
100
+ const direction = order.direction === 'desc' ? 'desc' : 'asc';
101
+ const orderPath = typeof order.path === 'string' ? order.path : (_ref = (_declaration_sorts_ = declaration.sorts[0]) == null ? void 0 : _declaration_sorts_.path) != null ? _ref : ID_PATH;
102
+ if (!paths.has(orderPath)) throw new Error(`The filter orders by "${orderPath}", which this list does not sort by.`);
103
+ return {
104
+ filters,
105
+ orderBy: {
106
+ path: orderPath,
107
+ direction
108
+ }
109
+ };
110
+ }
111
+
112
+ //# sourceMappingURL=list-filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../libs/plugins/commerce/src/lib/transfer/list-filter.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 {\n ListQueryDeclaration,\n ListQueryFilter,\n ListQueryOp,\n ListQueryPlan,\n ListQuerySort,\n} from '@aglyn/shared-ui-jsx/const/list-query-plan'\n\n/*\n * \"THE CURRENT FILTER\" OF A COMMERCE LIST, CARRIED TO AN EXPORT (AGL-3531).\n *\n * A list's filter is its Firestore query (AGL-3321): the predicates and the\n * one order `planListQuery` made of the chips and the search. The export\n * reads the same query on the server, so the list hands over exactly that —\n * the planned predicates and order — rather than the chips, whose dates the\n * browser resolved in the person's own timezone.\n *\n * The server reads through the Admin SDK, so it never takes a predicate on\n * faith: every path must be one the list's declaration names (a field's\n * path, lower-cased key or tokens, the search tokens, the list's base), the\n * operator one Firestore has, and the value plain data. Anything else is\n * refused, never widened to \"everything\".\n */\n\n/** What a list sends as its export filter. */\nexport interface CommerceListFilter {\n filters: ListQueryFilter[]\n orderBy: ListQuerySort\n}\n\nconst OPS: readonly ListQueryOp[] = [\n '==',\n '!=',\n '<',\n '<=',\n '>',\n '>=',\n 'in',\n 'array-contains',\n 'array-contains-any',\n]\n\n/** The id path a plan names the document id by. */\nconst ID_PATH = '__name__'\n\n/** The filter a list hands the export dialog: its plan's predicates and order, as plain data. */\nexport function commerceListFilter(plan: Pick<ListQueryPlan, 'filters' | 'orderBy'>): CommerceListFilter {\n return {\n filters: plan.filters.map((filter) => ({\n path: filter.path,\n op: filter.op,\n value:\n filter.value instanceof Date\n ? filter.value.getTime()\n : Array.isArray(filter.value)\n ? [...filter.value]\n : filter.value,\n })),\n orderBy: { path: plan.orderBy.path, direction: plan.orderBy.direction },\n }\n}\n\n/** Every path the declaration lets a query name. */\nfunction allowedPaths(declaration: ListQueryDeclaration, base: readonly string[]): Set<string> {\n const paths = new Set<string>([ID_PATH, ...base])\n for (const field of declaration.fields) {\n for (const path of [field.path, field.lowerPath, field.tokensPath, field.containsOrderBy]) {\n if (path) paths.add(path)\n }\n }\n if (declaration.search) paths.add(declaration.search.tokensPath)\n for (const sort of declaration.sorts) paths.add(sort.path)\n return paths\n}\n\nconst plain = (value: unknown): boolean =>\n value === null || ['string', 'number', 'boolean'].includes(typeof value)\n\n/**\n * The filter a request carried, checked against the list's declaration.\n * Throws a sentence naming what was refused.\n */\nexport function readCommerceListFilter(\n value: unknown,\n declaration: ListQueryDeclaration,\n base: readonly string[] = [],\n): CommerceListFilter {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error('The filter could not be read.')\n }\n const raw = value as { filters?: unknown; orderBy?: unknown }\n const paths = allowedPaths(declaration, base)\n const filters: ListQueryFilter[] = []\n for (const entry of Array.isArray(raw.filters) ? raw.filters : []) {\n const { path, op, value: operand } = (entry ?? {}) as Record<string, unknown>\n if (typeof path !== 'string' || !paths.has(path)) {\n throw new Error(`The filter names \"${String(path)}\", which this list does not filter by.`)\n }\n if (!OPS.includes(op as ListQueryOp)) throw new Error(`The filter uses an unknown operator.`)\n const list = op === 'in' || op === 'array-contains-any'\n if (list) {\n if (!Array.isArray(operand) || !operand.length || operand.length > 30 || !operand.every(plain)) {\n throw new Error('The filter holds a list it cannot ask for.')\n }\n } else if (!plain(operand)) {\n throw new Error('The filter holds a value it cannot ask for.')\n }\n filters.push({ path, op: op as ListQueryOp, value: operand as ListQueryFilter['value'] })\n }\n const order = (raw.orderBy ?? {}) as Record<string, unknown>\n const direction = order.direction === 'desc' ? 'desc' : 'asc'\n const orderPath = typeof order.path === 'string' ? order.path : (declaration.sorts[0]?.path ?? ID_PATH)\n if (!paths.has(orderPath)) throw new Error(`The filter orders by \"${orderPath}\", which this list does not sort by.`)\n return { filters, orderBy: { path: orderPath, direction } }\n}\n"],"names":["OPS","ID_PATH","commerceListFilter","plan","filters","map","filter","path","op","value","Date","getTime","Array","isArray","orderBy","direction","allowedPaths","declaration","base","paths","Set","field","fields","lowerPath","tokensPath","containsOrderBy","add","search","sort","sorts","plain","includes","readCommerceListFilter","raw","Error","entry","operand","has","String","list","length","every","push","order","orderPath"],"mappings":"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"}