@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,123 @@
1
+ import { economyTables as e } from "./tables.entity.js";
2
+ import { CodePort as t, defaultCodeEligibility as n, defineCode as r, resolveCodeEligibility as i } from "./codes.js";
3
+ import { codeTables as a } from "./codeTables.entity.js";
4
+ import { EconomyPort as o } from "./port.js";
5
+ import { economyPlugin as s } from "./plugin.js";
6
+ import { Effect as c, Layer as l } from "effect";
7
+ import { systemSubject as u } from "@voltro/protocol";
8
+ import { randomBytes as d } from "node:crypto";
9
+ import { ensureTableRegistered as f } from "@voltro/database";
10
+ import { InMemoryDataStore as p } from "@voltro/runtime";
11
+ // src/codePort.fixture.ts
12
+ var m = () => {
13
+ let e = [{
14
+ id: "test.gold",
15
+ kind: "soft",
16
+ cap: "1000"
17
+ }];
18
+ return Object.freeze({
19
+ currencies: e,
20
+ context: Object.freeze({
21
+ definition: r({
22
+ id: "fixture.gift",
23
+ campaign: "fixture.campaign",
24
+ currencies: [{
25
+ currency: "test.gold",
26
+ amount: "5"
27
+ }],
28
+ limits: {
29
+ global: 2,
30
+ perPlayer: 1
31
+ },
32
+ from: 0,
33
+ until: 864e5
34
+ }, e),
35
+ at: 1,
36
+ claims: 0,
37
+ playerClaims: 0
38
+ })
39
+ });
40
+ }, h = m(), g = Object.freeze({
41
+ kind: "slot",
42
+ defaultImplementation: n,
43
+ defaultProbe: {
44
+ run: () => c.sync(() => i(h.context, h.currencies)),
45
+ expected: !0
46
+ },
47
+ overrideProbe: {
48
+ run: () => c.sync(() => i(h.context, h.currencies, (e) => n(e) && e.claims > 0)),
49
+ expected: !1
50
+ }
51
+ }), _ = {
52
+ app: {
53
+ name: "codes-fixture",
54
+ type: "api",
55
+ voltroVersion: "0.83.1"
56
+ },
57
+ config: {},
58
+ env: {},
59
+ logger: {
60
+ info() {},
61
+ warn() {},
62
+ error() {},
63
+ debug() {}
64
+ }
65
+ }, v = c.gen(function* () {
66
+ yield* c.sync(() => [...e, ...a].forEach(f));
67
+ let t = new p(), n = s({
68
+ currencies: h.currencies,
69
+ now: () => 1,
70
+ codes: {}
71
+ });
72
+ yield* c.promise(async () => {
73
+ await n.bindDataStore?.(t);
74
+ });
75
+ let r = d(32).toString("hex"), i = Object.freeze({
76
+ code: r,
77
+ requestId: "fixture.redeem"
78
+ });
79
+ return yield* n.economy.codes().create(u("codes.fixture"), {
80
+ tenantId: "fixture",
81
+ code: r,
82
+ definition: h.context.definition
83
+ }), Object.freeze({
84
+ plugin: n,
85
+ store: t,
86
+ input: i,
87
+ close: c.suspend(() => n.onDeactivate?.(_))
88
+ });
89
+ }), y = l.effectContext(c.acquireRelease(v, (e) => e.close).pipe(c.flatMap((e) => l.build(e.plugin.services)))), b = Object.freeze({
90
+ kind: "port",
91
+ tag: t,
92
+ fakeLayer: y,
93
+ probe: {
94
+ run: () => c.scoped(c.gen(function* () {
95
+ let e = yield* t, n = yield* o, r = d(32).toString("hex"), i = {
96
+ code: r,
97
+ requestId: "fixture.probe"
98
+ }, a = {
99
+ type: "user",
100
+ id: "a",
101
+ tenantId: "fixture",
102
+ kind: "guest"
103
+ };
104
+ yield* e.create(u("codes.fixture"), {
105
+ tenantId: "fixture",
106
+ code: r,
107
+ definition: h.context.definition
108
+ });
109
+ let s = yield* e.redeem(a, i), c = yield* e.redeem(a, i), l = yield* e.receipt(a, i), f = yield* n.mine(a);
110
+ return {
111
+ same: s.operationId === c.operationId,
112
+ state: l.state,
113
+ balance: f[0]?.balance
114
+ };
115
+ }).pipe(c.provide(y))),
116
+ expected: {
117
+ same: !0,
118
+ state: "fulfilled",
119
+ balance: "5"
120
+ }
121
+ }
122
+ });
123
+ export { m as codeEligibilityFixture, g as codeEligibilitySystemFixture, v as codePortFixture, b as codePortSystemFixture, y as codesFakeLayer };
@@ -0,0 +1,439 @@
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 { positiveAmount as o } from "./amount.js";
4
+ import { captureCurrencies as s } from "./currency.js";
5
+ import { shopInteger as c } from "./shop.js";
6
+ import { captureCodeIntent as l, captureCodeValue as u, defaultCodeEligibility as d, defineCode as f, resolveCodeEligibility as p } from "./codes.js";
7
+ import { codeClaimsTable as m, codeSubjectsTable as h, codeTenantsTable as g, codesTable as _ } from "./codeTables.entity.js";
8
+ import { Effect as v, Result as y } from "effect";
9
+ import { systemSubject as b } from "@voltro/protocol";
10
+ import { createHash as x } from "node:crypto";
11
+ import { and as S, eq as C, queryFor as w } from "@voltro/database";
12
+ import { executeSqlMutations as ee, prepareSqlMutation as te, prepareSqlMutationGroup as ne } from "@voltro/database/sql";
13
+ import { InventoryError as T } from "@voltro/plugin-inventory/errors";
14
+ // src/codeStore.server.ts
15
+ var E = (e) => x("sha256").update(JSON.stringify(e)).digest("hex"), D = (e, ...t) => e + E(t).slice(0, 56), O = (n) => n instanceof e ? n : n instanceof T && [
16
+ "invalid_input",
17
+ "invalid_record",
18
+ "not_found",
19
+ "erased",
20
+ "forbidden",
21
+ "conflict",
22
+ "limit_exceeded"
23
+ ].includes(n.code) ? t(n.code) : t("unavailable"), k = (e) => v.tryPromise({
24
+ try: e,
25
+ catch: O
26
+ }), A = (e) => {
27
+ try {
28
+ return e();
29
+ } catch (e) {
30
+ return v.fail(O(e));
31
+ }
32
+ }, j = async (e) => {
33
+ let t = await v.runPromise(v.result(e));
34
+ if (y.isFailure(t)) throw t.failure;
35
+ return t.success;
36
+ }, M = (e) => {
37
+ let t = e instanceof Date ? e.getTime() : typeof e == "string" ? Date.parse(e) : NaN;
38
+ return c(t, 0, 864e13);
39
+ }, N = class extends Error {
40
+ constructor() {
41
+ super("Accepted code request replay"), this.name = "CodeReplay";
42
+ }
43
+ }, P = (e) => {
44
+ let o = i(e, [
45
+ "currencies",
46
+ "economy",
47
+ "inventory",
48
+ "systemAccount",
49
+ "allowedServices",
50
+ "maxHistoryRows",
51
+ "now",
52
+ "eligibility",
53
+ "version"
54
+ ]), l = s(o.currencies), u = n(o.economy, ["prepareTransfer"]), f = o.inventory === void 0 ? void 0 : n(o.inventory, ["prepareGrant"]), p = a(o.systemAccount ?? "codes"), m = r(o.allowedServices ?? [], 256).map((e) => a(e));
55
+ if (new Set(m).size !== m.length) throw t("invalid_input");
56
+ let h = c(o.maxHistoryRows ?? 1e4, 1, 1e5), g = o.now ?? Date.now, _ = o.eligibility ?? d;
57
+ if (typeof _ != "function") throw t("invalid_input");
58
+ let v = a(o.version ?? (_ === d ? "default.v1" : void 0));
59
+ if (_ !== d && v === "default.v1" || typeof g != "function") throw t("invalid_input");
60
+ return Object.freeze({
61
+ currencies: l,
62
+ economy: u,
63
+ inventory: f,
64
+ systemAccount: p,
65
+ allowedServices: Object.freeze(m),
66
+ maxHistoryRows: h,
67
+ now: g,
68
+ eligibility: _,
69
+ version: v
70
+ });
71
+ }, F = (e, n) => {
72
+ let v = P(n), { currencies: y, economy: x, inventory: T, systemAccount: O, eligibility: F, version: I } = v, L = v.maxHistoryRows, re = v.now, ie = new Set(v.allowedServices), R = () => c(re(), 0, 864e13), z = b("economy.codes"), B = (e) => {
73
+ if (e.type !== "user") throw t("forbidden");
74
+ return {
75
+ tenant: a(e.tenantId),
76
+ owner: a(e.id)
77
+ };
78
+ }, V = (e, t) => D("ecs_", e, t), H = (e, t) => D("ecd_", e, t), U = (e, t, n) => D("ecr_", e, t, n), W = (e, t) => E([e, t]), G = (t) => e.insertIgnore(g.tableName, {
79
+ id: D("ect_", t),
80
+ tenantId: t,
81
+ admission: 0
82
+ }, { conflictColumns: ["id"] }), K = (t, n) => e.insertIgnore(h.tableName, {
83
+ id: V(t, n),
84
+ tenantId: t,
85
+ ownerId: n,
86
+ erased: !1,
87
+ admission: 0
88
+ }, { conflictColumns: ["id"] }), q = async (e, n) => {
89
+ let r = D("ect_", n);
90
+ if (await e.updateMany(g.tableName, { admission: 0 }, { where: C("id", r) }) !== 1) throw t("unavailable");
91
+ let i = (await e.query(w(g).where(C("id", r)).limit(1).descriptor))[0];
92
+ if (!i || i.tenantId !== n || i.admission !== 0) throw t("invalid_record");
93
+ }, J = async (e, n, r, i = !1) => {
94
+ let a = V(n, r);
95
+ if (await e.updateMany(h.tableName, { admission: 0 }, { where: C("id", a) }) !== 1) throw t("unavailable");
96
+ let o = (await e.query(w(h).where(C("id", a)).limit(1).descriptor))[0];
97
+ if (!o || o.tenantId !== n || o.admission !== 0 || o.erased !== !0 && o.erased !== !1 || o.erased === !1 && o.ownerId !== r || o.erased === !0 && o.ownerId !== null) throw t("invalid_record");
98
+ if (o.erased === !0 && !i) throw t("erased");
99
+ }, Y = async (e, n, r, i) => {
100
+ let a = (await e.query(w(m).where(C("id", i)).limit(1).descriptor))[0];
101
+ if (a && (a.tenantId !== n || a.ownerKey !== V(n, r))) throw t("invalid_record");
102
+ return a;
103
+ }, X = (e) => {
104
+ if (e.state !== "fulfilled" || e.accepted !== !0 || e.ownerId === null) throw t("invalid_record");
105
+ let n = i(e.intent, [
106
+ "definition",
107
+ "currencies",
108
+ "requestId",
109
+ "codeDigest",
110
+ "rulesVersion",
111
+ "at"
112
+ ]), l = f(n.definition, s(n.currencies));
113
+ if (a(n.rulesVersion), typeof n.codeDigest != "string" || !/^[a-f0-9]{64}$/.test(n.codeDigest) || e.ownerKey !== V(a(e.tenantId), a(e.ownerId)) || e.digest !== E([
114
+ e.tenantId,
115
+ e.ownerId,
116
+ n.requestId,
117
+ n.codeDigest
118
+ ]) || e.id !== U(String(e.tenantId), String(e.ownerId), a(n.requestId)) || e.codeKey !== "ecd_" + String(n.codeDigest).slice(0, 56)) throw t("invalid_record");
119
+ let u = i(e.receipt, [
120
+ "operationId",
121
+ "codeId",
122
+ "version",
123
+ "campaign",
124
+ "at",
125
+ "transfers",
126
+ "grants"
127
+ ]), d = c(u.at, M(e.createdAt), 864e13);
128
+ if (u.operationId !== e.id || u.codeId !== l.id || u.version !== l.version || u.campaign !== l.campaign || d !== n.at || M(e.fulfilledAt) !== d || l.from !== null && d < l.from || l.until !== null && d >= l.until) throw t("invalid_record");
129
+ let p = Object.freeze(r(u.transfers, 16).map((n, r) => {
130
+ let s = i(n, [
131
+ "txId",
132
+ "currency",
133
+ "amount",
134
+ "at"
135
+ ]), u = l.currencies[r];
136
+ if (!u || s.currency !== u.currency || s.amount !== u.amount || typeof s.amount != "string") throw t("invalid_record");
137
+ return Object.freeze({
138
+ txId: a(s.txId),
139
+ currency: u.currency,
140
+ amount: o(s.amount).toString(),
141
+ at: c(s.at, M(e.createdAt), d)
142
+ });
143
+ })), m = Object.freeze(r(u.grants, 16).map((n, o) => {
144
+ let s = i(n, [
145
+ "operationId",
146
+ "kind",
147
+ "changes",
148
+ "at"
149
+ ]), u = l.items[o];
150
+ if (!u || s.kind !== "grant") throw t("invalid_record");
151
+ let f = Object.freeze(r(s.changes, 1e4).map((e) => {
152
+ let t = i(e, ["instanceId", "delta"]);
153
+ return Object.freeze({
154
+ instanceId: a(t.instanceId),
155
+ delta: c(t.delta, 1, 1e6)
156
+ });
157
+ }));
158
+ if (f.reduce((e, t) => e + t.delta, 0) !== u.qty || new Set(f.map((e) => e.instanceId)).size !== f.length) throw t("invalid_record");
159
+ return Object.freeze({
160
+ operationId: a(s.operationId),
161
+ kind: "grant",
162
+ changes: f,
163
+ at: c(s.at, M(e.createdAt), d)
164
+ });
165
+ }));
166
+ if (p.length !== l.currencies.length || m.length !== l.items.length || new Set(p.map((e) => e.txId)).size !== p.length || new Set(m.map((e) => e.operationId)).size !== m.length) throw t("invalid_record");
167
+ return Object.freeze({
168
+ operationId: a(e.id),
169
+ codeId: l.id,
170
+ version: l.version,
171
+ campaign: l.campaign,
172
+ at: d,
173
+ transfers: p,
174
+ grants: m
175
+ });
176
+ }, Z = async (e, n, r) => {
177
+ let i = await e.query(w(m).where(S(C("tenantId", n), r)).limit(L + 1).descriptor);
178
+ if (i.length > L) throw t("limit_exceeded");
179
+ for (let e of i) {
180
+ if (!/^ecr_[a-f0-9]{56}$/.test(String(e.id)) || !/^ecs_[a-f0-9]{56}$/.test(String(e.ownerKey)) || !/^ecd_[a-f0-9]{56}$/.test(String(e.codeKey)) || e.tenantId !== n || typeof e.accepted != "boolean" || ![
181
+ "pending",
182
+ "fulfilled",
183
+ "erased"
184
+ ].includes(String(e.state))) throw t("invalid_record");
185
+ if (e.state === "fulfilled") X(e);
186
+ else if (e.state === "pending" && (e.accepted !== !1 || e.receipt !== null || e.fulfilledAt !== null)) throw t("invalid_record");
187
+ else if (e.state === "erased" && (e.ownerId !== null || e.digest !== null || e.intent !== null || e.receipt !== null || e.accepted && e.fulfilledAt === null || !e.accepted && e.fulfilledAt !== null)) throw t("invalid_record");
188
+ }
189
+ return i;
190
+ }, Q = (n, r, i, a) => e.transactional(async (e) => {
191
+ await q(e, n), await J(e, n, r);
192
+ let o = await Y(e, n, r, i);
193
+ if (o) {
194
+ if (o.digest !== a) throw t("conflict");
195
+ if (o.state === "fulfilled") return X(o);
196
+ if (o.state !== "pending" || o.accepted !== !1) throw t("invalid_record");
197
+ }
198
+ }), ae = (n, r) => A(() => {
199
+ if (n.type !== "system" && (n.type !== "service" || !ie.has(n.service))) throw t("forbidden");
200
+ let o = i(r, [
201
+ "tenantId",
202
+ "code",
203
+ "definition"
204
+ ]), s = a(o.tenantId), c = u(o.code), l = f(o.definition, y);
205
+ if (l.items.length && !T) throw t("unavailable");
206
+ let d = H(s, c), p = W(s, c);
207
+ return k(async () => {
208
+ let n = R();
209
+ await G(s), await e.transactional(async (e) => {
210
+ await q(e, s);
211
+ let r = (await e.query(w(_).where(C("id", d)).limit(1).descriptor))[0];
212
+ if (r) {
213
+ if (r.tenantId !== s || r.codeDigest !== p || r.rulesVersion !== I || JSON.stringify(f(r.definition, y)) !== JSON.stringify(l)) throw t("conflict");
214
+ } else await e.insert(_.tableName, {
215
+ id: d,
216
+ tenantId: s,
217
+ codeDigest: p,
218
+ definition: l,
219
+ rulesVersion: I,
220
+ createdAt: new Date(n)
221
+ });
222
+ });
223
+ });
224
+ }), oe = (n, r) => A(() => {
225
+ let { tenant: i, owner: a } = B(n), o = l(r), s = U(i, a, o.requestId), c = H(i, o.code), u = W(i, o.code), h = E([
226
+ i,
227
+ a,
228
+ o.requestId,
229
+ u
230
+ ]), g = o.requestId;
231
+ return k(async () => {
232
+ await G(i), await K(i, a);
233
+ let n = await Q(i, a, s, h);
234
+ if (n) return n;
235
+ let r = R(), o = await e.transactional(async (e) => {
236
+ await q(e, i), await J(e, i, a);
237
+ let n = (await e.query(w(_).where(C("id", c)).limit(1).descriptor))[0];
238
+ if (!n || n.tenantId !== i || n.codeDigest !== u) throw t("not_found");
239
+ if (n.rulesVersion !== I) throw t("conflict");
240
+ if (M(n.createdAt) > r) throw t("invalid_record");
241
+ let o = f(n.definition, y);
242
+ if (o.from !== null && r < o.from || o.until !== null && r >= o.until) throw t("forbidden");
243
+ let l = await Z(e, i, C("ownerKey", V(i, a))), d = await Y(e, i, a, s);
244
+ if (!d) {
245
+ if (l.length >= L) throw t("limit_exceeded");
246
+ await e.insert(m.tableName, {
247
+ id: s,
248
+ tenantId: i,
249
+ ownerId: a,
250
+ ownerKey: V(i, a),
251
+ codeKey: c,
252
+ state: "pending",
253
+ accepted: !1,
254
+ digest: h,
255
+ intent: null,
256
+ receipt: null,
257
+ createdAt: new Date(r),
258
+ fulfilledAt: null
259
+ });
260
+ } else if (d.digest !== h || d.codeKey !== c) throw t("conflict");
261
+ let p = (await Z(e, i, C("codeKey", c))).filter((e) => e.accepted);
262
+ return Object.freeze({
263
+ definition: o,
264
+ claims: p.length,
265
+ playerClaims: p.filter((e) => e.ownerKey === V(i, a)).length,
266
+ at: r
267
+ });
268
+ }), l = o.definition;
269
+ if (o.claims >= l.limits.global || o.playerClaims >= l.limits.perPlayer) throw t("limit_exceeded");
270
+ if (!p(o, y, F)) throw t("forbidden");
271
+ let v = [];
272
+ v.push(te(e, async (e) => {
273
+ await q(e, i), await J(e, i, a);
274
+ let n = await Y(e, i, a, s);
275
+ if (!n || n.digest !== h || n.codeKey !== c || n.ownerId !== a) throw t("invalid_record");
276
+ if (n.state === "fulfilled") throw X(n), new N();
277
+ if (n.state !== "pending" || n.accepted !== !1) throw t("invalid_record");
278
+ let r = (await Z(e, i, C("codeKey", c))).filter((e) => e.accepted);
279
+ if (F !== d && (r.length !== o.claims || r.filter((e) => e.ownerKey === V(i, a)).length !== o.playerClaims)) throw t("conflict");
280
+ if (r.length >= l.limits.global || r.filter((e) => e.ownerKey === V(i, a)).length >= l.limits.perPlayer) throw t("limit_exceeded");
281
+ return null;
282
+ }));
283
+ for (let [e, t] of l.currencies.entries()) v.push(await j(x.prepareTransfer(z, {
284
+ tenantId: i,
285
+ requestId: D("eck_", s, "transfer", String(e)),
286
+ currency: t.currency,
287
+ amount: t.amount,
288
+ from: {
289
+ kind: "system",
290
+ id: O
291
+ },
292
+ to: {
293
+ kind: "wallet",
294
+ ownerKind: "player",
295
+ ownerId: a
296
+ },
297
+ reason: "code.redeem",
298
+ ref: {
299
+ kind: "code-claim",
300
+ id: s
301
+ }
302
+ })));
303
+ if (l.items.length && !T) throw t("unavailable");
304
+ for (let [e, t] of l.items.entries()) v.push(await j(T.prepareGrant(z, {
305
+ tenantId: i,
306
+ owner: {
307
+ ownerKind: "player",
308
+ ownerId: a
309
+ },
310
+ itemId: t.itemId,
311
+ qty: t.qty,
312
+ requestId: D("eck_", s, "grant", String(e)),
313
+ reason: "code.redeem",
314
+ ref: {
315
+ kind: "code-claim",
316
+ id: s
317
+ }
318
+ })));
319
+ let b = R();
320
+ if (b < r || l.from !== null && b < l.from || l.until !== null && b >= l.until) throw t("forbidden");
321
+ let S = ne(e, v, async (e, n) => {
322
+ let r = {
323
+ operationId: s,
324
+ codeId: l.id,
325
+ version: l.version,
326
+ campaign: l.campaign,
327
+ at: b,
328
+ transfers: n.slice(1, 1 + l.currencies.length),
329
+ grants: n.slice(1 + l.currencies.length)
330
+ };
331
+ await e.update(m.tableName, s, {
332
+ state: "fulfilled",
333
+ accepted: !0,
334
+ intent: {
335
+ definition: l,
336
+ currencies: y,
337
+ requestId: g,
338
+ codeDigest: u,
339
+ rulesVersion: I,
340
+ at: b
341
+ },
342
+ receipt: r,
343
+ fulfilledAt: new Date(b)
344
+ });
345
+ let o = await Y(e, i, a, s);
346
+ if (!o) throw t("invalid_record");
347
+ return X(o);
348
+ });
349
+ try {
350
+ return (await ee(e, [S]))[0];
351
+ } catch (e) {
352
+ if (e instanceof N) {
353
+ let e = await Q(i, a, s, h);
354
+ if (e) return e;
355
+ }
356
+ throw e;
357
+ }
358
+ });
359
+ }), se = (n, r) => A(() => {
360
+ let { tenant: i, owner: a } = B(n), o = l(r), s = U(i, a, o.requestId), c = E([
361
+ i,
362
+ a,
363
+ o.requestId,
364
+ W(i, o.code)
365
+ ]), u = o.requestId;
366
+ return k(async () => e.transactional(async (e) => {
367
+ let n = (await e.query(w(h).where(C("id", V(i, a))).limit(1).descriptor))[0];
368
+ if (n && n.tenantId !== i) throw t("invalid_record");
369
+ if (n?.erased === !0) throw n.ownerId === null ? t("erased") : t("invalid_record");
370
+ if (n && (n.tenantId !== i || n.ownerId !== a || n.erased !== !1)) throw t("invalid_record");
371
+ let r = await Y(e, i, a, s);
372
+ if (!r) return Object.freeze({
373
+ requestId: u,
374
+ state: "not-observed"
375
+ });
376
+ if (r.digest !== c) throw t("conflict");
377
+ if (r.state === "fulfilled") return Object.freeze({
378
+ requestId: u,
379
+ state: "fulfilled",
380
+ receipt: X(r)
381
+ });
382
+ if (r.state !== "pending" || r.accepted !== !1) throw t("invalid_record");
383
+ return Object.freeze({
384
+ requestId: u,
385
+ state: "pending"
386
+ });
387
+ }));
388
+ }), $ = (e) => {
389
+ let n = i(e, [
390
+ "tenantId",
391
+ "subjectId",
392
+ "kind"
393
+ ]);
394
+ if (n.kind !== "guest" && n.kind !== "user") throw t("invalid_input");
395
+ return {
396
+ tenant: a(n.tenantId),
397
+ owner: a(n.subjectId)
398
+ };
399
+ };
400
+ return Object.freeze({
401
+ create: ae,
402
+ redeem: oe,
403
+ receipt: se,
404
+ exportSubject: (n) => A(() => {
405
+ let { tenant: r, owner: i } = $(n);
406
+ return k(async () => e.transactional(async (e) => {
407
+ let n = (await e.query(w(h).where(C("id", V(r, i))).limit(1).descriptor))[0];
408
+ if (n && n.tenantId !== r) throw t("invalid_record");
409
+ if (n?.erased === !0) {
410
+ if (n.ownerId !== null) throw t("invalid_record");
411
+ return { claims: [] };
412
+ }
413
+ if (n && (n.tenantId !== r || n.ownerId !== i || n.erased !== !1)) throw t("invalid_record");
414
+ return { claims: await Z(e, r, C("ownerKey", V(r, i))) };
415
+ }));
416
+ }),
417
+ eraseSubject: (t) => A(() => {
418
+ let { tenant: n, owner: r } = $(t);
419
+ return k(async () => {
420
+ await G(n), await K(n, r), await e.transactional(async (e) => {
421
+ await q(e, n), await J(e, n, r, !0);
422
+ let t = await Z(e, n, C("ownerKey", V(n, r)));
423
+ for (let n of t) await e.update(m.tableName, String(n.id), {
424
+ ownerId: null,
425
+ state: "erased",
426
+ digest: null,
427
+ intent: null,
428
+ receipt: null
429
+ });
430
+ await e.update(h.tableName, V(n, r), {
431
+ ownerId: null,
432
+ erased: !0
433
+ });
434
+ });
435
+ });
436
+ })
437
+ });
438
+ };
439
+ export { F as dataStoreCodes, P as resolveCodeStoreOptions };
@@ -0,0 +1,43 @@
1
+ import { boolean as e, id as t, integer as n, json as r, table as i, text as a, timestamp as o } from "@voltro/database";
2
+ // src/codeTables.entity.ts
3
+ var s = () => a().maxLength(120).externalId().sensitive("pii"), c = i("_voltro_eco_code_tenants", {
4
+ id: t({ prefix: "ect" }),
5
+ tenantId: s(),
6
+ admission: n().serverOnly()
7
+ }).index(["tenantId"]), l = i("_voltro_eco_codes", {
8
+ id: t({ prefix: "ecd" }),
9
+ tenantId: s(),
10
+ codeDigest: a().maxLength(64).serverOnly(),
11
+ definition: r().serverOnly(),
12
+ rulesVersion: a().maxLength(120).serverOnly(),
13
+ createdAt: o().serverOnly()
14
+ }).index(["tenantId"]), u = i("_voltro_eco_code_subjects", {
15
+ id: t({ prefix: "ecs" }),
16
+ tenantId: s(),
17
+ ownerId: a().maxLength(120).nullable().externalId().sensitive("pii"),
18
+ erased: e().serverOnly(),
19
+ admission: n().serverOnly()
20
+ }).index(["tenantId", "ownerId"]), d = i("_voltro_eco_code_claims", {
21
+ id: t({ prefix: "ecr" }),
22
+ tenantId: s(),
23
+ ownerId: a().maxLength(120).nullable().externalId().sensitive("pii"),
24
+ ownerKey: a().maxLength(64).serverOnly(),
25
+ codeKey: a().maxLength(64).serverOnly(),
26
+ state: a().maxLength(16).oneOf([
27
+ "pending",
28
+ "fulfilled",
29
+ "erased"
30
+ ]).serverOnly(),
31
+ accepted: e().serverOnly(),
32
+ digest: a().maxLength(64).nullable().serverOnly(),
33
+ intent: r().nullable().serverOnly(),
34
+ receipt: r().nullable().serverOnly(),
35
+ createdAt: o().serverOnly(),
36
+ fulfilledAt: o().nullable().serverOnly()
37
+ }).index(["tenantId", "ownerKey"]).index(["tenantId", "codeKey"]), f = [
38
+ c,
39
+ l,
40
+ u,
41
+ d
42
+ ];
43
+ export { d as codeClaimsTable, u as codeSubjectsTable, f as codeTables, c as codeTenantsTable, l as codesTable };