@voltro/plugin-economy 0.0.0-stage → 0.88.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (177) hide show
  1. package/CHANGELOG.md +24492 -0
  2. package/LICENSE +61 -0
  3. package/README.md +2719 -2
  4. package/SECURITY.md +56 -0
  5. package/THIRD-PARTY-NOTICES.md +9841 -0
  6. package/dist/amount.js +20 -0
  7. package/dist/appleIap.server.js +262 -0
  8. package/dist/appleIapNotifications.server.js +162 -0
  9. package/dist/appleIapTrust.server.js +30 -0
  10. package/dist/bundleValue.server.js +112 -0
  11. package/dist/canonicalDataJson.js +10 -0
  12. package/dist/capture.js +53 -0
  13. package/dist/codePort.fixture.js +123 -0
  14. package/dist/codeStore.server.js +439 -0
  15. package/dist/codeTables.entity.js +43 -0
  16. package/dist/codes.d.ts +150 -0
  17. package/dist/codes.js +82 -0
  18. package/dist/components.d.ts +397 -0
  19. package/dist/components.js +283 -0
  20. package/dist/consistency.server.js +115 -0
  21. package/dist/containerStore.server.js +687 -0
  22. package/dist/containerStore.testFixtures.js +75 -0
  23. package/dist/containerTables.entity.js +45 -0
  24. package/dist/containers.d.ts +311 -0
  25. package/dist/containers.fixture.js +128 -0
  26. package/dist/containers.js +214 -0
  27. package/dist/containers.server.js +39 -0
  28. package/dist/containersPort.fixture.js +74 -0
  29. package/dist/currency.js +32 -0
  30. package/dist/declarationFixtures.js +61 -0
  31. package/dist/economy-app/events/economy.refundReversed.event.ts +1 -0
  32. package/dist/economy-app/events/economy.refunded.event.ts +1 -0
  33. package/dist/economyPort.fixture.js +124 -0
  34. package/dist/errors.d.ts +14 -0
  35. package/dist/errors.js +21 -0
  36. package/dist/events.d.ts +46 -0
  37. package/dist/events.js +1 -0
  38. package/dist/googleIap.server.js +391 -0
  39. package/dist/googleIapNotifications.server.js +130 -0
  40. package/dist/googleIapSubscription.server.js +183 -0
  41. package/dist/iap.d.ts +309 -0
  42. package/dist/iap.js +303 -0
  43. package/dist/iap.testFixtures.js +144 -0
  44. package/dist/iapAccountTables.entity.js +39 -0
  45. package/dist/iapAccounts.server.js +200 -0
  46. package/dist/iapBinding.server.js +523 -0
  47. package/dist/iapCheckout.server.js +97 -0
  48. package/dist/iapCompletion.server.js +55 -0
  49. package/dist/iapCurrentState.server.js +107 -0
  50. package/dist/iapCurrentState.testFixtures.js +22 -0
  51. package/dist/iapCurrentStatePort.fixture.js +63 -0
  52. package/dist/iapEntitlements.d.ts +79 -0
  53. package/dist/iapEntitlements.js +57 -0
  54. package/dist/iapEntitlements.server.js +48 -0
  55. package/dist/iapEntitlementsBinding.server.js +105 -0
  56. package/dist/iapEntitlementsPort.fixture.js +80 -0
  57. package/dist/iapInstalledEntitlements.testFixtures.js +75 -0
  58. package/dist/iapInstalledNotifications.testFixtures.js +93 -0
  59. package/dist/iapInventoryRefunds.server.js +261 -0
  60. package/dist/iapNotificationCustodyPort.fixture.js +67 -0
  61. package/dist/iapNotificationPort.fixture.js +73 -0
  62. package/dist/iapNotificationProcessor.server.js +134 -0
  63. package/dist/iapNotificationProcessor.testFixtures.js +63 -0
  64. package/dist/iapNotificationProcessorPort.fixture.js +55 -0
  65. package/dist/iapNotificationSchedule.server.js +69 -0
  66. package/dist/iapNotificationStore.server.js +352 -0
  67. package/dist/iapNotificationStore.testFixtures.js +31 -0
  68. package/dist/iapNotificationTables.entity.js +48 -0
  69. package/dist/iapNotificationTarget.server.js +36 -0
  70. package/dist/iapNotificationTarget.testFixtures.js +29 -0
  71. package/dist/iapNotifications.d.ts +644 -0
  72. package/dist/iapNotifications.js +6 -0
  73. package/dist/iapNotifications.server.js +95 -0
  74. package/dist/iapNotificationsBinding.server.js +232 -0
  75. package/dist/iapNotificationsSystemPort.fixture.js +50 -0
  76. package/dist/iapPlugin.testFixtures.js +59 -0
  77. package/dist/iapPort.fixture.js +74 -0
  78. package/dist/iapProviderCapture.server.js +17 -0
  79. package/dist/iapRecovery.d.ts +146 -0
  80. package/dist/iapRecovery.js +89 -0
  81. package/dist/iapRefundEventDelivery.server.js +82 -0
  82. package/dist/iapRefundEvents.js +44 -0
  83. package/dist/iapRefundEventsPort.fixture.js +59 -0
  84. package/dist/iapRefundPort.fixture.js +54 -0
  85. package/dist/iapRefundTables.entity.js +55 -0
  86. package/dist/iapRefunds.d.ts +755 -0
  87. package/dist/iapRefunds.js +6 -0
  88. package/dist/iapRefunds.server.js +553 -0
  89. package/dist/iapRefunds.testFixtures.js +64 -0
  90. package/dist/iapSpendAdmission.server.js +98 -0
  91. package/dist/iapStore.server.js +740 -0
  92. package/dist/iapStore.testFixtures.js +127 -0
  93. package/dist/iapTables.entity.js +80 -0
  94. package/dist/iapVerification.server.js +120 -0
  95. package/dist/index.d.ts +2984 -0
  96. package/dist/index.js +15 -0
  97. package/dist/intentionRecovery.js +133 -0
  98. package/dist/ledger.js +77 -0
  99. package/dist/loyalty.d.ts +274 -0
  100. package/dist/loyalty.fixture.js +92 -0
  101. package/dist/loyalty.js +311 -0
  102. package/dist/loyaltyBinding.server.js +203 -0
  103. package/dist/loyaltyPort.fixture.js +86 -0
  104. package/dist/loyaltyShopSource.server.js +90 -0
  105. package/dist/loyaltyStore.server.js +552 -0
  106. package/dist/loyaltyStore.testFixtures.js +82 -0
  107. package/dist/loyaltyTables.entity.js +49 -0
  108. package/dist/offerMerchandising.fixture.js +114 -0
  109. package/dist/offerMerchandising.js +282 -0
  110. package/dist/offerPolicies.d.ts +441 -0
  111. package/dist/offerPolicies.fixture.js +53 -0
  112. package/dist/offerPolicies.js +267 -0
  113. package/dist/piggyBanks.d.ts +272 -0
  114. package/dist/piggyBanks.fixture.js +70 -0
  115. package/dist/piggyBanks.js +200 -0
  116. package/dist/piggyBinding.server.js +204 -0
  117. package/dist/piggyPort.fixture.js +72 -0
  118. package/dist/piggyStore.server.js +610 -0
  119. package/dist/piggyStore.testFixtures.js +97 -0
  120. package/dist/piggyTables.entity.js +40 -0
  121. package/dist/plugin.js +1047 -0
  122. package/dist/port.js +12 -0
  123. package/dist/price.d.ts +99 -0
  124. package/dist/price.js +143 -0
  125. package/dist/purchaseRecovery.js +55 -0
  126. package/dist/reports.server.js +114 -0
  127. package/dist/rpc.d.ts +3201 -0
  128. package/dist/rpc.js +1200 -0
  129. package/dist/sanctionsTradeAuthority.server.js +79 -0
  130. package/dist/server.d.ts +3856 -0
  131. package/dist/server.js +43 -0
  132. package/dist/shop.d.ts +279 -0
  133. package/dist/shop.js +195 -0
  134. package/dist/shop.testFixtures.js +113 -0
  135. package/dist/shopPort.fixture.js +71 -0
  136. package/dist/shopPresentation.js +67 -0
  137. package/dist/shopStore.server.js +623 -0
  138. package/dist/shopTables.entity.js +62 -0
  139. package/dist/shopUiCopy.js +56 -0
  140. package/dist/spendLimits.d.ts +140 -0
  141. package/dist/spendLimits.fixture.js +52 -0
  142. package/dist/spendLimits.js +154 -0
  143. package/dist/spendLimitsPort.fixture.js +119 -0
  144. package/dist/subscriptionBinding.server.js +210 -0
  145. package/dist/subscriptionStore.server.js +642 -0
  146. package/dist/subscriptionStore.testFixtures.js +71 -0
  147. package/dist/subscriptionTables.entity.js +36 -0
  148. package/dist/subscriptions.d.ts +394 -0
  149. package/dist/subscriptions.fixture.js +96 -0
  150. package/dist/subscriptions.js +212 -0
  151. package/dist/subscriptionsPort.fixture.js +75 -0
  152. package/dist/systemFixtures.js +88 -0
  153. package/dist/tables.entity.js +82 -0
  154. package/dist/testing.d.ts +4615 -0
  155. package/dist/testing.js +29 -0
  156. package/dist/tradeAuthority.server.js +171 -0
  157. package/dist/tradeBinding.server.js +200 -0
  158. package/dist/tradeComponents.js +341 -0
  159. package/dist/tradeEscrow.server.js +231 -0
  160. package/dist/tradeEscrow.testFixtures.js +132 -0
  161. package/dist/tradeExpiry.server.js +42 -0
  162. package/dist/tradeRecovery.js +47 -0
  163. package/dist/tradeStore.server.js +749 -0
  164. package/dist/tradeStore.testFixtures.js +72 -0
  165. package/dist/tradeTables.entity.js +65 -0
  166. package/dist/tradeUiCopy.js +78 -0
  167. package/dist/trades.d.ts +246 -0
  168. package/dist/trades.fixture.js +102 -0
  169. package/dist/trades.js +288 -0
  170. package/dist/tradesPort.fixture.js +93 -0
  171. package/dist/walletEscrow.js +105 -0
  172. package/dist/walletEscrow.server.js +375 -0
  173. package/dist/walletKeys.server.js +4 -0
  174. package/dist/walletStore.js +570 -0
  175. package/dist/web.d.ts +2257 -0
  176. package/dist/web.js +8 -0
  177. package/package.json +161 -4
@@ -0,0 +1,610 @@
1
+ import { EconomyError as e, economyError as t } from "./errors.js";
2
+ import { captureBoundMethods as n, dataList as r, dataObject as i, identifier as a } from "./capture.js";
3
+ import { nonNegativeAmount as o } from "./amount.js";
4
+ import { captureCurrencies as s } from "./currency.js";
5
+ import { defaultPriceResolver as c, priceResolverContext as l, resolvePrice as u } from "./price.js";
6
+ import { captureShopOrderReceipt as d, shopInteger as f, shopPayment as p } from "./shop.js";
7
+ import { defaultPiggyFill as m, defaultPiggyReset as h, definePiggyBank as g, piggyFillIntent as _, piggyUnlockIntent as v, projectPiggyBank as y, resolvePiggyFill as b, resolvePiggyReset as x } from "./piggyBanks.js";
8
+ import { piggyDefinitionsTable as S, piggyEventsTable as C, piggySubjectsTable as w, piggyTenantsTable as T } from "./piggyTables.entity.js";
9
+ import { Effect as E, Result as D } from "effect";
10
+ import { systemSubject as O } from "@voltro/protocol";
11
+ import { createHash as k } from "node:crypto";
12
+ import { and as A, eq as j, queryFor as M } from "@voltro/database";
13
+ import { executeSqlMutations as N, prepareSqlMutation as P, prepareSqlMutationGroup as F } from "@voltro/database/sql";
14
+ // src/piggyStore.server.ts
15
+ var I = (e) => Array.isArray(e) ? "[" + e.map(I).join(",") + "]" : typeof e == "object" && e ? "{" + Object.keys(e).sort().map((t) => JSON.stringify(t) + ":" + I(e[t])).join(",") + "}" : JSON.stringify(e), L = (e) => k("sha256").update(I(e)).digest("hex"), R = (e, ...t) => e + L(t).slice(0, 56), z = (e, t) => L([e, a(t)]), B = (n) => n instanceof e ? n : t("unavailable"), V = (e) => E.tryPromise({
16
+ try: e,
17
+ catch: B
18
+ }), H = (n) => {
19
+ try {
20
+ return n();
21
+ } catch (n) {
22
+ return E.fail(n instanceof e ? n : t("invalid_input"));
23
+ }
24
+ }, U = async (e) => {
25
+ let t = await E.runPromise(E.result(e));
26
+ if (D.isFailure(t)) throw t.failure;
27
+ return t.success;
28
+ }, W = (e) => f(e, 0, 864e13), G = (e) => W(e instanceof Date ? e.getTime() : typeof e == "string" ? Date.parse(e) : NaN), K = (e) => {
29
+ if (typeof e != "string" || o(e).toString() !== e) throw t("invalid_record");
30
+ return e;
31
+ }, ee = (e) => {
32
+ let t = i(e, [
33
+ "tenantId",
34
+ "ownerId",
35
+ "authorityId",
36
+ "eventId",
37
+ "sourceId",
38
+ "amount",
39
+ "acceptedAt"
40
+ ], "invalid_record");
41
+ return Object.freeze({
42
+ tenantId: a(t.tenantId),
43
+ ownerId: a(t.ownerId),
44
+ authorityId: a(t.authorityId),
45
+ eventId: a(t.eventId),
46
+ sourceId: a(t.sourceId),
47
+ amount: K(t.amount),
48
+ acceptedAt: W(t.acceptedAt)
49
+ });
50
+ }, q = (e) => {
51
+ let o = i(e, [
52
+ "banks",
53
+ "currencies",
54
+ "realMoney",
55
+ "economy",
56
+ "fillSource",
57
+ "unlockSource",
58
+ "version",
59
+ "fill",
60
+ "reset",
61
+ "priceResolver",
62
+ "systemAccount",
63
+ "allowedServices",
64
+ "maxHistoryRows",
65
+ "now"
66
+ ]), l = s(o.currencies), u = o.realMoney ?? !1;
67
+ if (typeof u != "boolean") throw t("invalid_input");
68
+ let d = Object.freeze(r(o.banks, 32).map((e) => g(e, l, u)));
69
+ if (!d.length || new Set(d.map((e) => e.id)).size !== d.length) throw t("invalid_input");
70
+ let p = n(o.economy, ["prepareTransfer"]), _ = n(o.fillSource, ["read"]), v = n(o.unlockSource, ["read"]), y = a(o.version), b = o.fill ?? m, x = o.reset ?? h, S = o.priceResolver ?? c, C = o.now ?? Date.now;
71
+ if (y === "default.v1" || [
72
+ b,
73
+ x,
74
+ S,
75
+ C
76
+ ].some((e) => typeof e != "function")) throw t("invalid_input");
77
+ let w = Object.freeze(r(o.allowedServices ?? [], 256).map((e) => a(e)));
78
+ if (new Set(w).size !== w.length) throw t("invalid_input");
79
+ return Object.freeze({
80
+ banks: d,
81
+ currencies: l,
82
+ realMoney: u,
83
+ economy: p,
84
+ fillSource: _,
85
+ unlockSource: v,
86
+ version: y,
87
+ fill: b,
88
+ reset: x,
89
+ priceResolver: S,
90
+ now: C,
91
+ allowedServices: w,
92
+ systemAccount: a(o.systemAccount ?? "piggy"),
93
+ maxHistoryRows: f(o.maxHistoryRows ?? 1e4, 1, 1e5)
94
+ });
95
+ }, te = class extends Error {
96
+ constructor() {
97
+ super("Original piggy-bank operation replay"), this.name = "PiggyReplay";
98
+ }
99
+ }, ne = (e, n) => {
100
+ let r = q(n), o = O("economy.piggy"), c = new Set(r.allowedServices), m = () => W(r.now()), h = (e, t) => R("eps_", e, t), E = (e, t, n) => R("epe_", e, t, n), D = (e) => {
101
+ let n = structuredClone(e);
102
+ if (n.type !== "user") throw t("forbidden");
103
+ return Object.freeze({
104
+ tenantId: a(n.tenantId),
105
+ ownerId: a(n.id),
106
+ subject: n
107
+ });
108
+ }, k = (e) => {
109
+ let n = structuredClone(e);
110
+ if (n.type !== "system" && (n.type !== "service" || !c.has(n.service))) throw t("forbidden");
111
+ }, I = async (t, n) => {
112
+ await e.insertIgnore(T.tableName, {
113
+ id: R("ept_", t),
114
+ tenantId: t,
115
+ admission: 0
116
+ }, { conflictColumns: ["id"] }), await e.insertIgnore(w.tableName, {
117
+ id: h(t, n),
118
+ tenantId: t,
119
+ ownerId: n,
120
+ erased: !1,
121
+ admission: 0,
122
+ entriesCount: 0
123
+ }, { conflictColumns: ["id"] });
124
+ }, B = async (e, n, r, i = !1) => {
125
+ let a = (await e.query(M(w).where(j("id", h(n, r))).limit(1).descriptor))[0];
126
+ if (a) {
127
+ if (a.tenantId !== n || a.admission !== 0 || typeof a.erased != "boolean" || a.ownerId !== (a.erased ? null : r)) throw t("invalid_record");
128
+ if (f(a.entriesCount, 0, 1e5), a.erased && !i) throw t("erased");
129
+ return a;
130
+ }
131
+ }, ne = async (e, n, r, i = !1) => {
132
+ let a = R("ept_", n);
133
+ if (await e.updateMany(T.tableName, { admission: 0 }, { where: j("id", a) }) !== 1) throw t("unavailable");
134
+ let o = (await e.query(M(T).where(j("id", a)).limit(1).descriptor))[0];
135
+ if (!o || o.tenantId !== n || o.admission !== 0) throw t("invalid_record");
136
+ if (await e.updateMany(w.tableName, { admission: 0 }, { where: j("id", h(n, r)) }) !== 1 || !await B(e, n, r, i)) throw t("unavailable");
137
+ }, J = (e, t) => ({
138
+ id: R("epd_", e, t.bank.id),
139
+ tenantId: e,
140
+ bankId: t.bank.id,
141
+ definition: t.bank,
142
+ currencies: t.currencies,
143
+ realMoney: t.realMoney,
144
+ rulesVersion: t.rulesVersion
145
+ }), re = async (e, n, r, i) => {
146
+ let a = J(n, r), o = (await e.query(M(S).where(j("id", a.id)).limit(1).descriptor))[0];
147
+ if (o) {
148
+ if (L(o) !== L(a)) throw t("conflict");
149
+ } else i && await e.insert(S.tableName, a);
150
+ }, Y = async (e, n, r, o) => {
151
+ if (o.tenantId !== n || o.ownerId !== r || o.ownerKey !== h(n, r) || o.state !== "fulfilled") throw t("invalid_record");
152
+ let c = i(o.body, [
153
+ "kind",
154
+ "request",
155
+ "bank",
156
+ "currencies",
157
+ "realMoney",
158
+ "rulesVersion",
159
+ "evidence",
160
+ "event",
161
+ "receipt",
162
+ "price",
163
+ "adjustments"
164
+ ], "invalid_record");
165
+ if (c.kind !== "fill" && c.kind !== "unlock" || typeof c.realMoney != "boolean") throw t("invalid_record");
166
+ let m = c.kind, v = i(c.request, m === "fill" ? [
167
+ "tenantId",
168
+ "ownerId",
169
+ "bankId",
170
+ "sourceId",
171
+ "eventId",
172
+ "requestId"
173
+ ] : [
174
+ "tenantId",
175
+ "ownerId",
176
+ "bankId",
177
+ "bankVersion",
178
+ "bankDigest",
179
+ "requestId",
180
+ "currency"
181
+ ], "invalid_record"), y = s(c.currencies), b = g(c.bank, y, c.realMoney), x = a(c.rulesVersion), C = i(c.event, m === "fill" ? [
182
+ "id",
183
+ "sequence",
184
+ "kind",
185
+ "at",
186
+ "authorityId",
187
+ "eventId",
188
+ "sourceId",
189
+ "acceptedAmount",
190
+ "delta"
191
+ ] : [
192
+ "id",
193
+ "sequence",
194
+ "kind",
195
+ "at",
196
+ "amount",
197
+ "remaining"
198
+ ], "invalid_record"), w = Object.freeze({
199
+ ...C,
200
+ at: W(C.at),
201
+ sequence: f(C.sequence, 1, 1e5)
202
+ }), T = i(c.receipt, m === "fill" ? [
203
+ "operationId",
204
+ "requestId",
205
+ "bankId",
206
+ "event",
207
+ "available"
208
+ ] : [
209
+ "operationId",
210
+ "requestId",
211
+ "bankId",
212
+ "event",
213
+ "available",
214
+ "value"
215
+ ], "invalid_record"), D = K(T.available);
216
+ if (v.tenantId !== n || v.ownerId !== r || v.bankId !== b.id || o.bankId !== b.id || o.id !== E(n, r, a(v.requestId)) || o.digest !== L([m, v]) || w.id !== o.id || w.kind !== m || G(o.createdAt) !== w.at || T.operationId !== w.id || T.requestId !== v.requestId || T.bankId !== b.id || L(T.event) !== L(w) || x === "default.v1") throw t("invalid_record");
217
+ let O = {
218
+ bank: b,
219
+ currencies: y,
220
+ realMoney: c.realMoney,
221
+ rulesVersion: x
222
+ }, k = (await e.query(M(S).where(j("id", J(n, O).id)).limit(1).descriptor))[0];
223
+ if (!k || L(k) !== L(J(n, O))) throw t("invalid_record");
224
+ let A = null, N = null, P = [], F;
225
+ if (m === "fill") {
226
+ let e = _({
227
+ tenantId: a(v.tenantId),
228
+ ownerId: a(v.ownerId),
229
+ bankId: a(v.bankId),
230
+ sourceId: a(v.sourceId),
231
+ eventId: a(v.eventId),
232
+ requestId: a(v.requestId)
233
+ }), i = w, s = ee(c.evidence);
234
+ if (!b.fillFrom.includes(e.sourceId) || s.tenantId !== n || s.ownerId !== r || s.eventId !== e.eventId || s.sourceId !== e.sourceId || s.acceptedAt > w.at || i.authorityId !== s.authorityId || i.eventId !== s.eventId || i.sourceId !== s.sourceId || i.acceptedAmount !== s.amount || o.eventKey !== L([
235
+ n,
236
+ b.id,
237
+ s.authorityId,
238
+ s.eventId
239
+ ]) || c.price !== null || !Array.isArray(c.adjustments) || c.adjustments.length) throw t("invalid_record");
240
+ A = s, F = Object.freeze({
241
+ operationId: w.id,
242
+ requestId: e.requestId,
243
+ bankId: b.id,
244
+ event: i,
245
+ available: D
246
+ });
247
+ } else {
248
+ let e = d(T.value), r = w, i = l(b.unlock, y, c.realMoney, c.adjustments);
249
+ N = u(i, () => c.price), P = i.adjustments;
250
+ let s = p(N, v.currency === null ? null : a(v.currency), 1);
251
+ if (c.evidence !== null || o.eventKey !== null || v.bankVersion !== b.version || v.bankDigest !== z(b, x) || e.orderId !== w.id || e.offerId !== b.id || e.offerVersion !== b.version || e.offerDigest !== v.bankDigest || e.quantity !== 1 || e.at !== w.at || L(e.pricePaid) !== L(s) || e.grants.length || D !== r.remaining) throw t("invalid_record");
252
+ let f = [...s.kind === "virtual" && BigInt(s.amount) > 0n ? [{
253
+ currency: s.currency,
254
+ amount: s.amount
255
+ }] : [], {
256
+ currency: b.currency,
257
+ amount: K(r.amount)
258
+ }];
259
+ if (e.transfers.length !== f.length) throw t("invalid_record");
260
+ for (let [r, i] of e.transfers.entries()) if (i.currency !== f[r].currency || i.amount !== f[r].amount || i.txId !== R("ect_", n, R("epr_", w.id, "transfer", String(r))) || i.at > w.at) throw t("invalid_record");
261
+ F = Object.freeze({
262
+ operationId: w.id,
263
+ requestId: a(v.requestId),
264
+ bankId: b.id,
265
+ event: r,
266
+ available: D,
267
+ value: e
268
+ });
269
+ }
270
+ return Object.freeze({
271
+ kind: m,
272
+ request: v,
273
+ ...O,
274
+ evidence: A,
275
+ event: w,
276
+ receipt: F,
277
+ price: N,
278
+ adjustments: P
279
+ });
280
+ }, X = async (e, n, i) => {
281
+ let a = await B(e, n, i), o = await e.query(M(C).where(A(j("tenantId", n), j("ownerKey", h(n, i)))).limit(r.maxHistoryRows + 1).descriptor);
282
+ if (o.length > r.maxHistoryRows) throw t("limit_exceeded");
283
+ if (o.length !== (a?.entriesCount ?? 0)) throw t("invalid_record");
284
+ o.sort((e, t) => Number(e.sequence) - Number(t.sequence));
285
+ let s = [], c = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map();
286
+ for (let [r, a] of o.entries()) {
287
+ if (a.sequence !== r + 1) throw t("invalid_record");
288
+ let o = await Y(e, n, i, a), l = c.get(o.bank.id) ?? [];
289
+ if (o.event.at < (s.at(-1)?.event.at ?? 0) || l.length && L(l[0].bank) !== L(o.bank)) throw t("invalid_record");
290
+ l.push(o), c.set(o.bank.id, l), s.push(o);
291
+ }
292
+ for (let [e, n] of c) {
293
+ let r = y(n[0].bank, n.map((e) => e.event));
294
+ l.set(e, r);
295
+ let i = 0n;
296
+ for (let e of n) if (i = e.event.kind === "fill" ? i + BigInt(e.event.delta) : BigInt(e.event.remaining), e.receipt.available !== i.toString()) throw t("invalid_record");
297
+ }
298
+ if ((await B(e, n, i))?.entriesCount !== a?.entriesCount) throw t("conflict");
299
+ return Object.freeze({
300
+ accepted: s,
301
+ groups: c,
302
+ projections: l,
303
+ count: o.length
304
+ });
305
+ }, Z = async (e, n, r, i, a, o) => {
306
+ let s = (await X(e, n, r)).accepted.find((e) => e.event.id === i);
307
+ if (s && a && (s.kind !== a || L(s.request) !== L(o))) throw t("conflict");
308
+ return s?.receipt;
309
+ }, Q = (n, s, c) => V(async () => {
310
+ let { tenantId: f, ownerId: g } = n, v = E(f, g, a(c.requestId)), S = await e.transactional((e) => Z(e, f, g, v, s, c));
311
+ if (S) return S;
312
+ await I(f, g);
313
+ let T = await e.transactional((e) => X(e, f, g));
314
+ if (T.count >= r.maxHistoryRows) throw t("limit_exceeded");
315
+ let D = r.banks.find((e) => e.id === c.bankId);
316
+ if (!D) throw t("not_found");
317
+ let O = T.groups.get(D.id) ?? [], k = T.projections.get(D.id)?.available ?? "0", B = m();
318
+ if (B < (T.accepted.at(-1)?.event.at ?? 0)) throw t("invalid_record");
319
+ let V = {
320
+ bank: D,
321
+ currencies: r.currencies,
322
+ realMoney: r.realMoney,
323
+ rulesVersion: r.version
324
+ };
325
+ await e.transactional((e) => re(e, f, V, !1));
326
+ let H = null, W = null, G = [], K = Object.freeze({ kind: "free" }), q, J = {
327
+ bank: D,
328
+ currencies: r.currencies,
329
+ realMoney: r.realMoney,
330
+ available: k,
331
+ rulesVersion: r.version
332
+ };
333
+ if (s === "fill") {
334
+ let e = _({
335
+ tenantId: f,
336
+ ownerId: g,
337
+ bankId: D.id,
338
+ sourceId: a(c.sourceId),
339
+ eventId: a(c.eventId),
340
+ requestId: a(c.requestId)
341
+ });
342
+ if (!D.fillFrom.includes(e.sourceId)) throw t("forbidden");
343
+ if (H = ee(await U(r.fillSource.read(Object.freeze({
344
+ tenantId: f,
345
+ ownerId: g,
346
+ bank: D,
347
+ sourceId: e.sourceId,
348
+ eventId: e.eventId
349
+ })))), H.tenantId !== f || H.ownerId !== g || H.sourceId !== e.sourceId || H.eventId !== e.eventId || H.acceptedAt > B) throw t("invalid_record");
350
+ let n = H;
351
+ if (O.some((e) => e.event.kind === "fill" && e.event.authorityId === n.authorityId && e.event.eventId === n.eventId)) throw t("conflict");
352
+ q = Object.freeze({
353
+ id: v,
354
+ sequence: O.length + 1,
355
+ kind: "fill",
356
+ at: B,
357
+ authorityId: H.authorityId,
358
+ eventId: H.eventId,
359
+ sourceId: H.sourceId,
360
+ acceptedAmount: H.amount,
361
+ delta: b({
362
+ ...J,
363
+ acceptedAmount: H.amount
364
+ }, r.fill)
365
+ });
366
+ } else {
367
+ if (c.bankVersion !== D.version || c.bankDigest !== z(D, r.version)) throw t("conflict");
368
+ if (k === "0") throw t("limit_exceeded");
369
+ let e = i(await U(r.unlockSource.read(Object.freeze({
370
+ subject: n.subject,
371
+ bank: D,
372
+ at: B
373
+ }))), ["eligible", "adjustments"]);
374
+ if (typeof e.eligible != "boolean") throw t("invalid_input");
375
+ if (!e.eligible) throw t("forbidden");
376
+ let o = l(D.unlock, r.currencies, r.realMoney, e.adjustments);
377
+ W = u(o, r.priceResolver), G = o.adjustments, K = p(W, c.currency === null ? null : a(c.currency), 1);
378
+ let s = x(J, r.reset);
379
+ q = Object.freeze({
380
+ id: v,
381
+ sequence: O.length + 1,
382
+ kind: "unlock",
383
+ at: B,
384
+ ...s
385
+ });
386
+ }
387
+ let Y = H ? L([
388
+ f,
389
+ D.id,
390
+ H.authorityId,
391
+ H.eventId
392
+ ]) : null, Q = [P(e, async (e) => {
393
+ if (await ne(e, f, g), await Z(e, f, g, v, s, c)) throw new te();
394
+ if ((await X(e, f, g)).count !== T.count || Y && (await e.query(M(C).where(A(j("tenantId", f), j("eventKey", Y))).limit(1).descriptor)).length) throw t("conflict");
395
+ return await re(e, f, V, !0), null;
396
+ })], ie = q.kind === "unlock" ? [...K.kind === "virtual" && BigInt(K.amount) > 0n ? [{
397
+ currency: K.currency,
398
+ amount: K.amount,
399
+ from: {
400
+ kind: "wallet",
401
+ ownerKind: "player",
402
+ ownerId: g
403
+ },
404
+ to: {
405
+ kind: "system",
406
+ id: r.systemAccount
407
+ }
408
+ }] : [], {
409
+ currency: D.currency,
410
+ amount: q.amount,
411
+ from: {
412
+ kind: "system",
413
+ id: r.systemAccount
414
+ },
415
+ to: {
416
+ kind: "wallet",
417
+ ownerKind: "player",
418
+ ownerId: g
419
+ }
420
+ }] : [];
421
+ for (let [e, t] of ie.entries()) Q.push(await U(r.economy.prepareTransfer(o, {
422
+ tenantId: f,
423
+ requestId: R("epr_", v, "transfer", String(e)),
424
+ ...t,
425
+ reason: "piggy.unlock",
426
+ ref: {
427
+ kind: "piggy",
428
+ id: v
429
+ }
430
+ })));
431
+ let $ = s === "fill" ? B : m();
432
+ if ($ < B) throw t("conflict");
433
+ q = Object.freeze({
434
+ ...q,
435
+ at: $
436
+ });
437
+ let ae = y(D, [...O.map((e) => e.event), q]), oe = F(e, Q, async (e, n) => {
438
+ let i = {
439
+ operationId: v,
440
+ requestId: a(c.requestId),
441
+ bankId: D.id,
442
+ available: ae.available
443
+ }, o = q.kind === "fill" ? Object.freeze({
444
+ ...i,
445
+ event: q
446
+ }) : Object.freeze({
447
+ ...i,
448
+ event: q,
449
+ value: d({
450
+ orderId: v,
451
+ offerId: D.id,
452
+ offerVersion: D.version,
453
+ offerDigest: z(D, r.version),
454
+ quantity: 1,
455
+ at: $,
456
+ pricePaid: K,
457
+ transfers: n.slice(1),
458
+ grants: []
459
+ })
460
+ }), l = Object.freeze({
461
+ kind: s,
462
+ request: c,
463
+ ...V,
464
+ evidence: H,
465
+ event: q,
466
+ receipt: o,
467
+ price: W,
468
+ adjustments: G
469
+ });
470
+ await e.insert(C.tableName, {
471
+ id: v,
472
+ tenantId: f,
473
+ ownerId: g,
474
+ ownerKey: h(f, g),
475
+ bankId: D.id,
476
+ sequence: T.count + 1,
477
+ state: "fulfilled",
478
+ eventKey: Y,
479
+ digest: L([s, c]),
480
+ body: l,
481
+ createdAt: new Date($)
482
+ }), await e.update(w.tableName, h(f, g), { entriesCount: T.count + 1 });
483
+ let u = (await X(e, f, g)).accepted.find((e) => e.event.id === v);
484
+ if (!u) throw t("invalid_record");
485
+ return u.receipt;
486
+ });
487
+ try {
488
+ return (await N(e, [oe]))[0];
489
+ } catch (t) {
490
+ if (t instanceof te) {
491
+ let t = await e.transactional((e) => Z(e, f, g, v, s, c));
492
+ if (t) return t;
493
+ }
494
+ throw t;
495
+ }
496
+ }), ie = (t) => H(() => {
497
+ let n = D(t);
498
+ return V(() => e.transactional(async (e) => {
499
+ let t = await X(e, n.tenantId, n.ownerId), i = [];
500
+ for (let a of r.banks) {
501
+ await re(e, n.tenantId, {
502
+ bank: a,
503
+ currencies: r.currencies,
504
+ realMoney: r.realMoney,
505
+ rulesVersion: r.version
506
+ }, !1);
507
+ let o = t.projections.get(a.id);
508
+ i.push(Object.freeze({
509
+ bank: a,
510
+ bankDigest: z(a, r.version),
511
+ available: o?.available ?? "0",
512
+ cycles: o?.cycles ?? 0
513
+ }));
514
+ }
515
+ return Object.freeze(i);
516
+ }));
517
+ }), $ = (e, t) => H(() => {
518
+ k(e);
519
+ let n = _(t);
520
+ return Q(n, "fill", { ...n });
521
+ }), ae = (e, t) => H(() => {
522
+ let n = D(e), r = v(t);
523
+ return Q(n, "unlock", Object.freeze({
524
+ tenantId: n.tenantId,
525
+ ownerId: n.ownerId,
526
+ ...r
527
+ }));
528
+ }), oe = (t, n) => H(() => {
529
+ let r = D(t), o = i(n, ["requestId"]), s = a(o.requestId);
530
+ return V(() => e.transactional(async (e) => {
531
+ let t = await Z(e, r.tenantId, r.ownerId, E(r.tenantId, r.ownerId, s));
532
+ return Object.freeze(t ? {
533
+ requestId: s,
534
+ state: "fulfilled",
535
+ receipt: t
536
+ } : {
537
+ requestId: s,
538
+ state: "not-observed"
539
+ });
540
+ }));
541
+ }), se = (t, n) => H(() => {
542
+ k(t);
543
+ let r = i(n, [
544
+ "tenantId",
545
+ "ownerId",
546
+ "bankId"
547
+ ]), o = a(r.tenantId), s = a(r.ownerId), c = a(r.bankId);
548
+ return V(() => e.transactional(async (e) => (await X(e, o, s)).projections.get(c) ?? Object.freeze({
549
+ events: [],
550
+ available: "0",
551
+ cycles: 0
552
+ })));
553
+ }), ce = (e) => {
554
+ let n = i(e, [
555
+ "tenantId",
556
+ "subjectId",
557
+ "kind"
558
+ ]);
559
+ if (n.kind !== "guest" && n.kind !== "user") throw t("forbidden");
560
+ return {
561
+ tenant: a(n.tenantId),
562
+ owner: a(n.subjectId)
563
+ };
564
+ };
565
+ return Object.freeze({
566
+ list: ie,
567
+ fill: $,
568
+ unlock: ae,
569
+ receipt: oe,
570
+ rebuild: se,
571
+ exportSubject: (t) => H(() => {
572
+ let n = ce(t);
573
+ return V(() => e.transactional(async (e) => {
574
+ if ((await B(e, n.tenant, n.owner, !0))?.erased) return Object.freeze({ piggyBanks: [] });
575
+ let t = await X(e, n.tenant, n.owner);
576
+ return Object.freeze({ piggyBanks: Object.freeze(t.accepted.map((e) => Object.freeze({
577
+ bank: e.bank,
578
+ evidence: e.evidence,
579
+ receipt: e.receipt
580
+ }))) });
581
+ }));
582
+ }),
583
+ eraseSubject: (n) => H(() => {
584
+ let i = ce(n);
585
+ return V(async () => {
586
+ await I(i.tenant, i.owner), await e.transactional(async (e) => {
587
+ await ne(e, i.tenant, i.owner, !0);
588
+ let n = await B(e, i.tenant, i.owner, !0), a = await e.query(M(C).where(A(j("tenantId", i.tenant), j("ownerKey", h(i.tenant, i.owner)))).limit(r.maxHistoryRows + 1).descriptor);
589
+ if (a.length > r.maxHistoryRows) throw t("limit_exceeded");
590
+ if (a.length !== n?.entriesCount) throw t("invalid_record");
591
+ a.sort((e, t) => Number(e.sequence) - Number(t.sequence));
592
+ for (let [n, r] of a.entries()) {
593
+ if (!/^epe_[a-f0-9]{56}$/.test(String(r.id)) || r.sequence !== n + 1 || r.ownerId !== i.owner && r.ownerId !== null || r.state !== "fulfilled" && r.state !== "erased" || r.eventKey !== null && !/^[a-f0-9]{64}$/.test(String(r.eventKey))) throw t("invalid_record");
594
+ await e.update(C.tableName, String(r.id), {
595
+ ownerId: null,
596
+ state: "erased",
597
+ digest: null,
598
+ body: null
599
+ });
600
+ }
601
+ await e.update(w.tableName, h(i.tenant, i.owner), {
602
+ ownerId: null,
603
+ erased: !0
604
+ });
605
+ });
606
+ });
607
+ })
608
+ });
609
+ };
610
+ export { ne as dataStorePiggyBanks, z as piggyBankDigest, q as resolvePiggyStoreOptions };
@@ -0,0 +1,97 @@
1
+ import { economyTables as e } from "./tables.entity.js";
2
+ import { dataStoreEconomy as t } from "./walletStore.js";
3
+ import { piggyTables as n } from "./piggyTables.entity.js";
4
+ import { dataStorePiggyBanks as r } from "./piggyStore.server.js";
5
+ import { shopAt as i, shopMachine as a, shopUser as o } from "./shop.testFixtures.js";
6
+ import { Effect as s } from "effect";
7
+ import { ensureTableRegistered as c } from "@voltro/database";
8
+ // src/piggyStore.testFixtures.ts
9
+ var l = async (l, u = "tenant", d = {}, f = "1000") => {
10
+ [...e, ...n].forEach(c);
11
+ let p = [{
12
+ id: "test.gold",
13
+ kind: "soft",
14
+ cap: f
15
+ }], m = t(l, {
16
+ currencies: p,
17
+ now: () => i
18
+ }), h = o("a", u);
19
+ await s.runPromise(m.transfer(a, {
20
+ tenantId: u,
21
+ requestId: "fund",
22
+ currency: "test.gold",
23
+ amount: "100",
24
+ from: {
25
+ kind: "system",
26
+ id: "fixture"
27
+ },
28
+ to: {
29
+ kind: "wallet",
30
+ ownerKind: "player",
31
+ ownerId: "a"
32
+ },
33
+ reason: "fixture",
34
+ ref: {
35
+ kind: "fixture",
36
+ id: "fund"
37
+ }
38
+ }));
39
+ let g = i, _ = {
40
+ currencies: p,
41
+ economy: m,
42
+ banks: [{
43
+ id: "piggy",
44
+ titleKey: "test.piggy",
45
+ currency: "test.gold",
46
+ cap: "20",
47
+ unlock: {
48
+ kind: "virtual",
49
+ options: [{
50
+ currency: "test.gold",
51
+ amount: "5"
52
+ }]
53
+ },
54
+ fillFrom: ["match.reward"]
55
+ }],
56
+ fillSource: { read: (e) => s.succeed({
57
+ tenantId: e.tenantId,
58
+ ownerId: e.ownerId,
59
+ authorityId: "fixture.match.rewards",
60
+ eventId: e.eventId,
61
+ sourceId: e.sourceId,
62
+ amount: "13",
63
+ acceptedAt: g
64
+ }) },
65
+ unlockSource: { read: () => s.succeed({
66
+ eligible: !0,
67
+ adjustments: []
68
+ }) },
69
+ version: "fixture.piggy.v1",
70
+ now: () => g,
71
+ ...d
72
+ };
73
+ return {
74
+ economy: m,
75
+ user: h,
76
+ options: _,
77
+ service: r(l, _),
78
+ fill: (e, t = e) => ({
79
+ tenantId: u,
80
+ ownerId: "a",
81
+ bankId: "piggy",
82
+ sourceId: "match.reward",
83
+ eventId: e,
84
+ requestId: t
85
+ }),
86
+ setAt: (e) => {
87
+ g = e;
88
+ }
89
+ };
90
+ }, u = (e, t = "unlock") => ({
91
+ bankId: e.bank.id,
92
+ bankVersion: e.bank.version,
93
+ bankDigest: e.bankDigest,
94
+ requestId: t,
95
+ currency: e.bank.unlock.kind === "virtual" ? e.bank.unlock.options[0].currency : void 0
96
+ });
97
+ export { u as piggyFixtureUnlock, l as piggyStoreFixture };