@voltro/plugin-economy 0.0.0-stage → 0.87.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 +24329 -0
  2. package/LICENSE +61 -0
  3. package/README.md +2719 -2
  4. package/SECURITY.md +56 -0
  5. package/THIRD-PARTY-NOTICES.md +9189 -0
  6. package/dist/amount.js +21 -0
  7. package/dist/appleIap.server.js +263 -0
  8. package/dist/appleIapNotifications.server.js +163 -0
  9. package/dist/appleIapTrust.server.js +31 -0
  10. package/dist/bundleValue.server.js +113 -0
  11. package/dist/canonicalDataJson.js +11 -0
  12. package/dist/capture.js +54 -0
  13. package/dist/codePort.fixture.js +124 -0
  14. package/dist/codeStore.server.js +440 -0
  15. package/dist/codeTables.entity.js +44 -0
  16. package/dist/codes.d.ts +150 -0
  17. package/dist/codes.js +83 -0
  18. package/dist/components.d.ts +397 -0
  19. package/dist/components.js +284 -0
  20. package/dist/consistency.server.js +116 -0
  21. package/dist/containerStore.server.js +688 -0
  22. package/dist/containerStore.testFixtures.js +76 -0
  23. package/dist/containerTables.entity.js +46 -0
  24. package/dist/containers.d.ts +311 -0
  25. package/dist/containers.fixture.js +129 -0
  26. package/dist/containers.js +215 -0
  27. package/dist/containers.server.js +40 -0
  28. package/dist/containersPort.fixture.js +75 -0
  29. package/dist/currency.js +33 -0
  30. package/dist/declarationFixtures.js +62 -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 +125 -0
  34. package/dist/errors.d.ts +14 -0
  35. package/dist/errors.js +22 -0
  36. package/dist/events.d.ts +46 -0
  37. package/dist/events.js +1 -0
  38. package/dist/googleIap.server.js +392 -0
  39. package/dist/googleIapNotifications.server.js +131 -0
  40. package/dist/googleIapSubscription.server.js +184 -0
  41. package/dist/iap.d.ts +309 -0
  42. package/dist/iap.js +304 -0
  43. package/dist/iap.testFixtures.js +145 -0
  44. package/dist/iapAccountTables.entity.js +40 -0
  45. package/dist/iapAccounts.server.js +201 -0
  46. package/dist/iapBinding.server.js +524 -0
  47. package/dist/iapCheckout.server.js +98 -0
  48. package/dist/iapCompletion.server.js +56 -0
  49. package/dist/iapCurrentState.server.js +108 -0
  50. package/dist/iapCurrentState.testFixtures.js +23 -0
  51. package/dist/iapCurrentStatePort.fixture.js +64 -0
  52. package/dist/iapEntitlements.d.ts +79 -0
  53. package/dist/iapEntitlements.js +58 -0
  54. package/dist/iapEntitlements.server.js +49 -0
  55. package/dist/iapEntitlementsBinding.server.js +106 -0
  56. package/dist/iapEntitlementsPort.fixture.js +81 -0
  57. package/dist/iapInstalledEntitlements.testFixtures.js +76 -0
  58. package/dist/iapInstalledNotifications.testFixtures.js +94 -0
  59. package/dist/iapInventoryRefunds.server.js +262 -0
  60. package/dist/iapNotificationCustodyPort.fixture.js +68 -0
  61. package/dist/iapNotificationPort.fixture.js +74 -0
  62. package/dist/iapNotificationProcessor.server.js +135 -0
  63. package/dist/iapNotificationProcessor.testFixtures.js +64 -0
  64. package/dist/iapNotificationProcessorPort.fixture.js +56 -0
  65. package/dist/iapNotificationSchedule.server.js +70 -0
  66. package/dist/iapNotificationStore.server.js +353 -0
  67. package/dist/iapNotificationStore.testFixtures.js +32 -0
  68. package/dist/iapNotificationTables.entity.js +49 -0
  69. package/dist/iapNotificationTarget.server.js +37 -0
  70. package/dist/iapNotificationTarget.testFixtures.js +30 -0
  71. package/dist/iapNotifications.d.ts +644 -0
  72. package/dist/iapNotifications.js +6 -0
  73. package/dist/iapNotifications.server.js +96 -0
  74. package/dist/iapNotificationsBinding.server.js +233 -0
  75. package/dist/iapNotificationsSystemPort.fixture.js +51 -0
  76. package/dist/iapPlugin.testFixtures.js +60 -0
  77. package/dist/iapPort.fixture.js +75 -0
  78. package/dist/iapProviderCapture.server.js +18 -0
  79. package/dist/iapRecovery.d.ts +146 -0
  80. package/dist/iapRecovery.js +90 -0
  81. package/dist/iapRefundEventDelivery.server.js +83 -0
  82. package/dist/iapRefundEvents.js +45 -0
  83. package/dist/iapRefundEventsPort.fixture.js +60 -0
  84. package/dist/iapRefundPort.fixture.js +55 -0
  85. package/dist/iapRefundTables.entity.js +56 -0
  86. package/dist/iapRefunds.d.ts +755 -0
  87. package/dist/iapRefunds.js +6 -0
  88. package/dist/iapRefunds.server.js +554 -0
  89. package/dist/iapRefunds.testFixtures.js +65 -0
  90. package/dist/iapSpendAdmission.server.js +99 -0
  91. package/dist/iapStore.server.js +741 -0
  92. package/dist/iapStore.testFixtures.js +128 -0
  93. package/dist/iapTables.entity.js +81 -0
  94. package/dist/iapVerification.server.js +121 -0
  95. package/dist/index.d.ts +2984 -0
  96. package/dist/index.js +15 -0
  97. package/dist/intentionRecovery.js +134 -0
  98. package/dist/ledger.js +78 -0
  99. package/dist/loyalty.d.ts +274 -0
  100. package/dist/loyalty.fixture.js +93 -0
  101. package/dist/loyalty.js +312 -0
  102. package/dist/loyaltyBinding.server.js +204 -0
  103. package/dist/loyaltyPort.fixture.js +87 -0
  104. package/dist/loyaltyShopSource.server.js +91 -0
  105. package/dist/loyaltyStore.server.js +553 -0
  106. package/dist/loyaltyStore.testFixtures.js +83 -0
  107. package/dist/loyaltyTables.entity.js +50 -0
  108. package/dist/offerMerchandising.fixture.js +115 -0
  109. package/dist/offerMerchandising.js +283 -0
  110. package/dist/offerPolicies.d.ts +441 -0
  111. package/dist/offerPolicies.fixture.js +54 -0
  112. package/dist/offerPolicies.js +268 -0
  113. package/dist/piggyBanks.d.ts +272 -0
  114. package/dist/piggyBanks.fixture.js +71 -0
  115. package/dist/piggyBanks.js +201 -0
  116. package/dist/piggyBinding.server.js +205 -0
  117. package/dist/piggyPort.fixture.js +73 -0
  118. package/dist/piggyStore.server.js +611 -0
  119. package/dist/piggyStore.testFixtures.js +98 -0
  120. package/dist/piggyTables.entity.js +41 -0
  121. package/dist/plugin.js +1048 -0
  122. package/dist/port.js +13 -0
  123. package/dist/price.d.ts +99 -0
  124. package/dist/price.js +144 -0
  125. package/dist/purchaseRecovery.js +56 -0
  126. package/dist/reports.server.js +115 -0
  127. package/dist/rpc.d.ts +3201 -0
  128. package/dist/rpc.js +1201 -0
  129. package/dist/sanctionsTradeAuthority.server.js +80 -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 +196 -0
  134. package/dist/shop.testFixtures.js +114 -0
  135. package/dist/shopPort.fixture.js +72 -0
  136. package/dist/shopPresentation.js +68 -0
  137. package/dist/shopStore.server.js +624 -0
  138. package/dist/shopTables.entity.js +63 -0
  139. package/dist/shopUiCopy.js +57 -0
  140. package/dist/spendLimits.d.ts +140 -0
  141. package/dist/spendLimits.fixture.js +53 -0
  142. package/dist/spendLimits.js +155 -0
  143. package/dist/spendLimitsPort.fixture.js +120 -0
  144. package/dist/subscriptionBinding.server.js +211 -0
  145. package/dist/subscriptionStore.server.js +643 -0
  146. package/dist/subscriptionStore.testFixtures.js +72 -0
  147. package/dist/subscriptionTables.entity.js +37 -0
  148. package/dist/subscriptions.d.ts +394 -0
  149. package/dist/subscriptions.fixture.js +97 -0
  150. package/dist/subscriptions.js +213 -0
  151. package/dist/subscriptionsPort.fixture.js +76 -0
  152. package/dist/systemFixtures.js +89 -0
  153. package/dist/tables.entity.js +83 -0
  154. package/dist/testing.d.ts +4615 -0
  155. package/dist/testing.js +29 -0
  156. package/dist/tradeAuthority.server.js +172 -0
  157. package/dist/tradeBinding.server.js +201 -0
  158. package/dist/tradeComponents.js +342 -0
  159. package/dist/tradeEscrow.server.js +232 -0
  160. package/dist/tradeEscrow.testFixtures.js +133 -0
  161. package/dist/tradeExpiry.server.js +43 -0
  162. package/dist/tradeRecovery.js +48 -0
  163. package/dist/tradeStore.server.js +750 -0
  164. package/dist/tradeStore.testFixtures.js +73 -0
  165. package/dist/tradeTables.entity.js +66 -0
  166. package/dist/tradeUiCopy.js +79 -0
  167. package/dist/trades.d.ts +246 -0
  168. package/dist/trades.fixture.js +103 -0
  169. package/dist/trades.js +289 -0
  170. package/dist/tradesPort.fixture.js +94 -0
  171. package/dist/walletEscrow.js +106 -0
  172. package/dist/walletEscrow.server.js +376 -0
  173. package/dist/walletKeys.server.js +5 -0
  174. package/dist/walletStore.js +571 -0
  175. package/dist/web.d.ts +2257 -0
  176. package/dist/web.js +9 -0
  177. package/package.json +161 -4
@@ -0,0 +1,643 @@
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 { captureCurrencies as o } from "./currency.js";
4
+ import { defaultPriceResolver as s, priceResolverContext as c, resolvePrice as l } from "./price.js";
5
+ import { captureShopOrderReceipt as u, shopInteger as d, shopPayment as f } from "./shop.js";
6
+ import { captureSubscriptionReceiptIntent as p, defaultSubscriptionDailyGrant as m, defaultSubscriptionPeriod as h, defineSubscription as g, resolveSubscriptionDailyGrant as _, resolveSubscriptionPeriod as v, subscriptionEntitlementIntent as y, subscriptionRenewIntent as b, subscriptionStartIntent as x } from "./subscriptions.js";
7
+ import { subscriptionDefinitionsTable as S, subscriptionEventsTable as C, subscriptionSubjectsTable as w, subscriptionTenantsTable as T } from "./subscriptionTables.entity.js";
8
+ import { Effect as E, Result as ee } from "effect";
9
+ import { systemSubject as D } from "@voltro/protocol";
10
+ import { createHash as O } from "node:crypto";
11
+ import { and as k, eq as A, queryFor as j } from "@voltro/database";
12
+ import { executeSqlMutations as M, prepareSqlMutation as N, prepareSqlMutationGroup as P } from "@voltro/database/sql";
13
+ import { InventoryError as F } from "@voltro/plugin-inventory/errors";
14
+ //#region src/subscriptionStore.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) => O("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 : n instanceof F && [
16
+ "conflict",
17
+ "erased",
18
+ "limit_exceeded",
19
+ "forbidden",
20
+ "not_found",
21
+ "invalid_record"
22
+ ].includes(n.code) ? t(n.code) : t("unavailable"), V = (e) => E.tryPromise({
23
+ try: e,
24
+ catch: B
25
+ }), H = (e) => {
26
+ try {
27
+ return e();
28
+ } catch (e) {
29
+ return E.fail(B(e));
30
+ }
31
+ }, te = async (e) => {
32
+ let t = await E.runPromise(E.result(e));
33
+ if (ee.isFailure(t)) throw t.failure;
34
+ return t.success;
35
+ }, U = (e) => d(e, 0, 864e13), W = (e) => U(e instanceof Date ? e.getTime() : typeof e == "string" ? Date.parse(e) : NaN), G = (e) => {
36
+ let n = structuredClone(e);
37
+ if (n.type !== "user") throw t("forbidden");
38
+ return Object.freeze({
39
+ tenant: a(n.tenantId),
40
+ owner: a(n.id)
41
+ });
42
+ }, K = (e, t) => Object.freeze({
43
+ ...e.window,
44
+ state: t < e.window.expiresAt ? "active" : e.cancelledAt === null && t < e.window.graceUntil ? "grace" : "expired"
45
+ }), ne = class extends Error {
46
+ constructor() {
47
+ super("Original subscription event replay"), this.name = "SubscriptionReplay";
48
+ }
49
+ }, q = (e) => {
50
+ let c = i(e, [
51
+ "subscriptions",
52
+ "currencies",
53
+ "realMoney",
54
+ "economy",
55
+ "inventory",
56
+ "accessSource",
57
+ "version",
58
+ "period",
59
+ "daily",
60
+ "priceResolver",
61
+ "systemAccount",
62
+ "maxHistoryRows",
63
+ "now"
64
+ ]), l = o(c.currencies), u = c.realMoney ?? !1;
65
+ if (typeof u != "boolean") throw t("invalid_input");
66
+ let f = Object.freeze(r(c.subscriptions, 256).map((e) => g(e, l, u)));
67
+ if (!f.length || new Set(f.map((e) => e.id)).size !== f.length) throw t("invalid_input");
68
+ let p = n(c.economy, ["prepareTransfer"]), _ = c.inventory === void 0 ? void 0 : n(c.inventory, ["prepareGrant"]);
69
+ if (!_ && f.some((e) => e.grants.onPurchase.items.length || e.grants.daily.items.length)) throw t("invalid_input");
70
+ let v = n(c.accessSource, ["read"]), y = a(c.version);
71
+ if (y === "default.v1") throw t("invalid_input");
72
+ let b = c.period ?? h, x = c.daily ?? m, S = c.priceResolver ?? s, C = c.now ?? Date.now;
73
+ if ([
74
+ b,
75
+ x,
76
+ S,
77
+ C
78
+ ].some((e) => typeof e != "function")) throw t("invalid_input");
79
+ return Object.freeze({
80
+ subscriptions: f,
81
+ currencies: l,
82
+ realMoney: u,
83
+ economy: p,
84
+ inventory: _,
85
+ accessSource: v,
86
+ version: y,
87
+ period: b,
88
+ daily: x,
89
+ priceResolver: S,
90
+ now: C,
91
+ systemAccount: a(c.systemAccount ?? "subscriptions"),
92
+ maxHistoryRows: d(c.maxHistoryRows ?? 1e4, 1, 1e5)
93
+ });
94
+ }, J = (e, n) => {
95
+ let r = q(n), s = D("economy.subscriptions"), m = () => U(r.now()), E = (e, t) => R("evs_", e, t), ee = (e, t, n) => R("eve_", e, t, n), O = async (t, n) => {
96
+ await e.insertIgnore(T.tableName, {
97
+ id: R("evt_", t),
98
+ tenantId: t,
99
+ admission: 0
100
+ }, { conflictColumns: ["id"] }), await e.insertIgnore(w.tableName, {
101
+ id: E(t, n),
102
+ tenantId: t,
103
+ ownerId: n,
104
+ erased: !1,
105
+ admission: 0
106
+ }, { conflictColumns: ["id"] });
107
+ }, F = async (e, n, r, i = !1) => {
108
+ let a = (await e.query(j(w).where(A("id", E(n, r))).limit(1).descriptor))[0];
109
+ if (a) {
110
+ if (a.tenantId !== n || a.admission !== 0 || typeof a.erased != "boolean" || a.ownerId !== (a.erased ? null : r)) throw t("invalid_record");
111
+ if (a.erased && !i) throw t("erased");
112
+ return a;
113
+ }
114
+ }, I = async (e, n, r, i = !1) => {
115
+ let a = R("evt_", n);
116
+ if (await e.updateMany(T.tableName, { admission: 0 }, { where: A("id", a) }) !== 1) throw t("unavailable");
117
+ let o = (await e.query(j(T).where(A("id", a)).limit(1).descriptor))[0];
118
+ if (!o || o.tenantId !== n || o.admission !== 0) throw t("invalid_record");
119
+ if (await e.updateMany(w.tableName, { admission: 0 }, { where: A("id", E(n, r)) }) !== 1 || !await F(e, n, r, i)) throw t("unavailable");
120
+ }, B = async (e, n, r, s) => {
121
+ if (s.tenantId !== n || s.ownerId !== r || s.ownerKey !== E(n, r) || s.state !== "fulfilled") throw t("invalid_record");
122
+ let c = i(s.body, [
123
+ "kind",
124
+ "request",
125
+ "currencies",
126
+ "realMoney",
127
+ "rulesVersion",
128
+ "view",
129
+ "receipt",
130
+ "at"
131
+ ], "invalid_record");
132
+ if (c.kind !== "start" && c.kind !== "renew" && c.kind !== "daily" && c.kind !== "cancel" || typeof c.realMoney != "boolean") throw t("invalid_record");
133
+ let l = c.kind, f = o(c.currencies), p = a(c.rulesVersion), m = U(c.at), _ = i(c.request, [
134
+ "tenantId",
135
+ "ownerId",
136
+ "subscriptionId",
137
+ "subscriptionVersion",
138
+ "subscriptionDigest",
139
+ "requestId",
140
+ "currency",
141
+ "entitlementId"
142
+ ], "invalid_record");
143
+ if (_.tenantId !== n || _.ownerId !== r || s.id !== ee(n, r, a(_.requestId)) || s.digest !== L([l, _]) || W(s.createdAt) !== m) throw t("invalid_record");
144
+ let v = i(c.view, [
145
+ "entitlementId",
146
+ "subscription",
147
+ "subscriptionDigest",
148
+ "originAt",
149
+ "periodIndex",
150
+ "window",
151
+ "currency",
152
+ "renews",
153
+ "cancelledAt"
154
+ ], "invalid_record"), y = g(v.subscription, f, c.realMoney), b = U(v.originAt), x = d(v.periodIndex, 0, 1e5), C = i(v.window, [
155
+ "startsAt",
156
+ "expiresAt",
157
+ "graceUntil",
158
+ "state"
159
+ ], "invalid_record"), w = U(C.startsAt), T = U(C.expiresAt), D = U(C.graceUntil), O = h({
160
+ subscription: y,
161
+ currencies: f,
162
+ realMoney: c.realMoney,
163
+ originAt: b,
164
+ index: x
165
+ }), k = a(v.entitlementId), M = v.currency === null ? null : a(v.currency), N = v.cancelledAt === null ? null : U(v.cancelledAt);
166
+ if (w >= T || w < O.startsAt || T > O.expiresAt || D !== U(T + y.graceDays * 864e5) || ![
167
+ "active",
168
+ "grace",
169
+ "expired"
170
+ ].includes(String(C.state)) || typeof v.renews != "boolean" || N !== null && (v.renews || N > m) || v.subscriptionDigest !== z(y, p)) throw t("invalid_record");
171
+ let P = Object.freeze({
172
+ entitlementId: k,
173
+ subscription: y,
174
+ subscriptionDigest: String(v.subscriptionDigest),
175
+ originAt: b,
176
+ periodIndex: x,
177
+ window: Object.freeze({
178
+ startsAt: w,
179
+ expiresAt: T,
180
+ graceUntil: D,
181
+ state: C.state
182
+ }),
183
+ currency: M,
184
+ renews: v.renews,
185
+ cancelledAt: N
186
+ });
187
+ if (P.window.state !== K(P, m).state || m < b) throw t("invalid_record");
188
+ let F = R("evd_", n, y.id, y.version, p), I = (await e.query(j(S).where(A("id", F)).limit(1).descriptor))[0], B = {
189
+ id: F,
190
+ tenantId: n,
191
+ definition: y,
192
+ currencies: f,
193
+ realMoney: c.realMoney,
194
+ rulesVersion: p
195
+ };
196
+ if (!I || L(I) !== L(B)) throw t("invalid_record");
197
+ let V;
198
+ if (l === "cancel") {
199
+ let e = i(c.receipt, [
200
+ "operationId",
201
+ "entitlementId",
202
+ "at",
203
+ "renews"
204
+ ], "invalid_record");
205
+ if (e.operationId !== s.id || e.entitlementId !== k || e.at !== m || e.renews !== !1 || P.renews || P.cancelledAt === null || _.entitlementId !== k) throw t("invalid_record");
206
+ V = Object.freeze({
207
+ operationId: String(s.id),
208
+ entitlementId: k,
209
+ at: m,
210
+ renews: !1
211
+ });
212
+ } else {
213
+ let e = i(c.receipt, [
214
+ "operationId",
215
+ "entitlementId",
216
+ "kind",
217
+ "at",
218
+ "periodIndex",
219
+ "day",
220
+ "window",
221
+ "value"
222
+ ], "invalid_record"), o = u(e.value), d = l === "daily" ? y.grants.daily : y.grants.onPurchase;
223
+ if (e.operationId !== s.id || e.entitlementId !== k || e.kind !== l || e.at !== m || e.periodIndex !== x || L(e.window) !== L(P.window) || e.day !== (l === "daily" ? Math.floor(m / 864e5) : null) || o.orderId !== s.id || o.offerId !== y.id || o.offerVersion !== y.version || o.offerDigest !== P.subscriptionDigest || o.at !== m || o.quantity !== 1 || l === "start" && (_.subscriptionId !== y.id || _.subscriptionVersion !== y.version || _.subscriptionDigest !== P.subscriptionDigest || _.currency !== M || k !== R("eva_", n, r, a(_.requestId)) || x !== 0 || b > m) || l !== "start" && _.entitlementId !== k || l === "daily" && o.pricePaid.kind !== "free") throw t("invalid_record");
224
+ let f = o.pricePaid;
225
+ if ((l === "start" || l === "renew") && (P.window.state !== "active" || m < P.window.startsAt || m >= P.window.expiresAt) || l !== "daily" && (f.kind === "free" ? y.price.kind !== "free" || M !== null : M !== f.currency)) throw t("invalid_record");
226
+ if (f.kind === "virtual") {
227
+ let e = (y.price.kind === "virtual" ? y.price.options : y.price.kind === "mixed" ? y.price.virtual : []).find((e) => e.currency === f.currency);
228
+ if (!e || BigInt(f.amount) > BigInt(e.amount)) throw t("invalid_record");
229
+ }
230
+ let p = [...f.kind === "virtual" && BigInt(f.amount) > 0n ? [{
231
+ currency: f.currency,
232
+ amount: f.amount
233
+ }] : [], ...d.currencies];
234
+ if (o.transfers.length !== p.length || o.grants.length !== d.items.length) throw t("invalid_record");
235
+ for (let [e, r] of o.transfers.entries()) if (r.currency !== p[e].currency || r.amount !== p[e].amount || r.txId !== R("ect_", n, R("evr_", String(s.id), "transfer", String(e)))) throw t("invalid_record");
236
+ for (let [e, i] of o.grants.entries()) if (i.operationId !== R("ivo_", n, "player", r, R("evr_", String(s.id), "grant", String(e))) || i.changes.reduce((e, t) => e + t.delta, 0) !== d.items[e].qty) throw t("invalid_record");
237
+ V = Object.freeze({
238
+ operationId: String(s.id),
239
+ entitlementId: k,
240
+ kind: l,
241
+ at: m,
242
+ periodIndex: x,
243
+ day: l === "daily" ? Math.floor(m / 864e5) : null,
244
+ window: P.window,
245
+ value: o
246
+ });
247
+ }
248
+ return Object.freeze({
249
+ kind: l,
250
+ request: _,
251
+ currencies: f,
252
+ realMoney: c.realMoney,
253
+ rulesVersion: p,
254
+ view: P,
255
+ receipt: V,
256
+ at: m
257
+ });
258
+ }, J = async (e, n, i) => {
259
+ let a = await F(e, n, i), o = await e.query(j(C).where(k(A("tenantId", n), A("ownerKey", E(n, i)))).limit(r.maxHistoryRows + 1).descriptor);
260
+ if (o.length > r.maxHistoryRows) throw t("limit_exceeded");
261
+ if (o.length && !a) throw t("invalid_record");
262
+ o.sort((e, t) => Number(e.sequence) - Number(t.sequence));
263
+ let s = [], c = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Set();
264
+ for (let [r, a] of o.entries()) {
265
+ if (a.sequence !== r + 1) throw t("invalid_record");
266
+ let o = await B(e, n, i, a), u = o.view, d = c.get(u.entitlementId);
267
+ if (o.at < (s.at(-1)?.at ?? 0)) throw t("invalid_record");
268
+ if (o.kind === "start") {
269
+ if (d || [...c.values()].some((e) => e.subscription.id === u.subscription.id && e.window.graceUntil > o.at)) throw t("invalid_record");
270
+ c.set(u.entitlementId, u);
271
+ } else {
272
+ if (!d || L(d.subscription) !== L(u.subscription) || d.subscriptionDigest !== u.subscriptionDigest || d.originAt !== u.originAt || d.currency !== u.currency) throw t("invalid_record");
273
+ if (o.kind === "renew") {
274
+ if (!d.renews || d.cancelledAt !== null || u.cancelledAt !== null || u.periodIndex !== d.periodIndex + 1 || o.at < d.window.expiresAt || o.at >= u.window.expiresAt) throw t("invalid_record");
275
+ c.set(u.entitlementId, u);
276
+ } else if (o.kind === "cancel") {
277
+ if (u.periodIndex !== d.periodIndex || u.cancelledAt !== (d.cancelledAt ?? o.at) || L(u.window) !== L(K({
278
+ ...d,
279
+ cancelledAt: u.cancelledAt
280
+ }, o.at))) throw t("invalid_record");
281
+ c.set(u.entitlementId, u);
282
+ } else {
283
+ let e = `${u.entitlementId}:${Math.floor(o.at / 864e5)}`;
284
+ if (l.has(e) || u.periodIndex !== d.periodIndex || u.renews !== d.renews || u.cancelledAt !== d.cancelledAt || L(u.window) !== L(K(d, o.at)) || !_({
285
+ entitlement: u.window,
286
+ at: o.at,
287
+ claimedDays: []
288
+ }).eligible) throw t("invalid_record");
289
+ l.add(e);
290
+ }
291
+ }
292
+ s.push(o);
293
+ }
294
+ return await F(e, n, i), Object.freeze({
295
+ accepted: s,
296
+ states: c,
297
+ basis: o.length
298
+ });
299
+ }, Y = async (e, n, r, i, a, o) => {
300
+ await F(e, n, r);
301
+ let s = (await e.query(j(C).where(A("id", i)).limit(1).descriptor))[0];
302
+ if (!s) return;
303
+ if (!await F(e, n, r)) throw t("invalid_record");
304
+ if (s.digest !== L([a, o])) throw t("conflict");
305
+ let c = await B(e, n, r, s);
306
+ return await F(e, n, r), c.receipt;
307
+ }, X = (n, o, d) => V(async () => {
308
+ let { tenant: p, owner: h } = n, g = ee(p, h, a(d.requestId)), y = L([o, d]), b = await e.transactional((e) => Y(e, p, h, g, o, d));
309
+ if (b) return b;
310
+ await O(p, h);
311
+ let x = await e.transactional((e) => J(e, p, h));
312
+ if (x.basis >= r.maxHistoryRows) throw t("limit_exceeded");
313
+ let w = m();
314
+ if (w < (x.accepted.at(-1)?.at ?? 0)) throw t("invalid_record");
315
+ let T = o === "start" ? void 0 : x.states.get(a(d.entitlementId));
316
+ if (o !== "start" && !T) throw t("not_found");
317
+ let D = o === "start" ? r.subscriptions.find((e) => e.id === d.subscriptionId) : T.subscription;
318
+ if (!D) throw t("not_found");
319
+ let k = o === "start" ? r.currencies : x.accepted.find((e) => e.view.entitlementId === T.entitlementId).currencies, F = o === "start" ? r.realMoney : x.accepted.find((e) => e.view.entitlementId === T.entitlementId).realMoney, V = o === "start" ? r.version : x.accepted.find((e) => e.view.entitlementId === T.entitlementId).rulesVersion, H, W = Object.freeze({ kind: "free" }), G = Object.freeze({
320
+ items: [],
321
+ currencies: []
322
+ });
323
+ if (o === "start" || o === "renew") {
324
+ if (V !== r.version || o === "start" && (D.version !== d.subscriptionVersion || z(D, V) !== d.subscriptionDigest) || o === "renew" && (!T.renews || T.cancelledAt !== null || w < T.window.expiresAt)) throw t("conflict");
325
+ if (o === "start" && [...x.states.values()].some((e) => e.subscription.id === D.id && e.window.graceUntil > w)) throw t("limit_exceeded");
326
+ let e = i(await te(r.accessSource.read(Object.freeze({
327
+ tenantId: p,
328
+ ownerId: h,
329
+ subscription: D,
330
+ at: w,
331
+ kind: o
332
+ }))), [
333
+ "eligible",
334
+ "renewalConsent",
335
+ "adjustments"
336
+ ]);
337
+ if (typeof e.eligible != "boolean" || typeof e.renewalConsent != "boolean") throw t("invalid_input");
338
+ if (!e.eligible || o === "renew" && !e.renewalConsent) throw t("forbidden");
339
+ let n = l(c(D.price, k, F, e.adjustments), r.priceResolver), s = o === "start" ? d.currency : T.currency;
340
+ W = f(n, s, 1), G = D.grants.onPurchase;
341
+ let u = o === "start" ? w : T.originAt, m = o === "start" ? 0 : T.periodIndex + 1, g = v({
342
+ subscription: D,
343
+ currencies: k,
344
+ realMoney: F,
345
+ originAt: u,
346
+ index: m,
347
+ rulesVersion: V
348
+ }, r.period);
349
+ if (w < g.startsAt || w >= g.expiresAt) throw t("forbidden");
350
+ let _ = Object.freeze({
351
+ ...g,
352
+ graceUntil: U(g.expiresAt + D.graceDays * 864e5),
353
+ state: "active"
354
+ });
355
+ H = Object.freeze({
356
+ entitlementId: o === "start" ? R("eva_", p, h, a(d.requestId)) : T.entitlementId,
357
+ subscription: D,
358
+ subscriptionDigest: z(D, V),
359
+ originAt: u,
360
+ periodIndex: m,
361
+ window: _,
362
+ currency: s,
363
+ renews: e.renewalConsent,
364
+ cancelledAt: null
365
+ });
366
+ } else {
367
+ let e = K(T, w);
368
+ if (H = Object.freeze({
369
+ ...T,
370
+ window: e,
371
+ ...o === "cancel" ? {
372
+ renews: !1,
373
+ cancelledAt: T.cancelledAt ?? w
374
+ } : {}
375
+ }), o === "cancel" && (H = Object.freeze({
376
+ ...H,
377
+ window: K(H, w)
378
+ })), o === "daily") {
379
+ if (V !== r.version) throw t("conflict");
380
+ let n = x.accepted.filter((e) => e.kind === "daily" && e.view.entitlementId === H.entitlementId).map((e) => Math.floor(e.at / 864e5));
381
+ if (!_({
382
+ entitlement: e,
383
+ at: w,
384
+ claimedDays: n,
385
+ rulesVersion: V
386
+ }, r.daily).eligible) throw t("limit_exceeded");
387
+ G = D.grants.daily;
388
+ }
389
+ }
390
+ let q = R("evd_", p, D.id, D.version, V), X = {
391
+ id: q,
392
+ tenantId: p,
393
+ definition: D,
394
+ currencies: k,
395
+ realMoney: F,
396
+ rulesVersion: V
397
+ };
398
+ await e.insertIgnore(S.tableName, X, { conflictColumns: ["id"] });
399
+ let Z = [N(e, async (e) => {
400
+ if (await I(e, p, h), await Y(e, p, h, g, o, d)) throw new ne();
401
+ let n = await J(e, p, h);
402
+ if (n.basis !== x.basis || n.basis >= r.maxHistoryRows) throw t("conflict");
403
+ let i = (await e.query(j(S).where(A("id", q)).limit(1).descriptor))[0];
404
+ if (!i || L(i) !== L(X)) throw t("conflict");
405
+ return null;
406
+ })], Q = [...W.kind === "virtual" && BigInt(W.amount) > 0n ? [{
407
+ currency: W.currency,
408
+ amount: W.amount,
409
+ from: {
410
+ kind: "wallet",
411
+ ownerKind: "player",
412
+ ownerId: h
413
+ },
414
+ to: {
415
+ kind: "system",
416
+ id: r.systemAccount
417
+ }
418
+ }] : [], ...G.currencies.map((e) => ({
419
+ ...e,
420
+ from: {
421
+ kind: "system",
422
+ id: r.systemAccount
423
+ },
424
+ to: {
425
+ kind: "wallet",
426
+ ownerKind: "player",
427
+ ownerId: h
428
+ }
429
+ }))];
430
+ for (let [e, t] of Q.entries()) Z.push(await te(r.economy.prepareTransfer(s, {
431
+ tenantId: p,
432
+ requestId: R("evr_", g, "transfer", String(e)),
433
+ ...t,
434
+ reason: `subscription.${o}`,
435
+ ref: {
436
+ kind: "subscription",
437
+ id: g
438
+ }
439
+ })));
440
+ for (let [e, n] of G.items.entries()) {
441
+ if (!r.inventory) throw t("unavailable");
442
+ Z.push(await te(r.inventory.prepareGrant(s, {
443
+ tenantId: p,
444
+ owner: {
445
+ ownerKind: "player",
446
+ ownerId: h
447
+ },
448
+ requestId: R("evr_", g, "grant", String(e)),
449
+ ...n,
450
+ reason: `subscription.${o}`,
451
+ ref: {
452
+ kind: "subscription",
453
+ id: g
454
+ }
455
+ })));
456
+ }
457
+ let $ = m();
458
+ if ($ < w || o === "daily" && Math.floor($ / 864e5) !== Math.floor(w / 864e5) || (o === "start" || o === "renew") && $ >= H.window.expiresAt || o === "daily" && K(H, $).state === "expired") throw t("conflict");
459
+ o === "cancel" && T.cancelledAt === null && (H = Object.freeze({
460
+ ...H,
461
+ cancelledAt: $
462
+ })), H = Object.freeze({
463
+ ...H,
464
+ window: K(H, $)
465
+ });
466
+ let re = P(e, Z, async (e, n) => {
467
+ let r = Object.freeze(o === "cancel" ? {
468
+ operationId: g,
469
+ entitlementId: H.entitlementId,
470
+ at: $,
471
+ renews: !1
472
+ } : {
473
+ operationId: g,
474
+ entitlementId: H.entitlementId,
475
+ kind: o,
476
+ at: $,
477
+ periodIndex: H.periodIndex,
478
+ day: o === "daily" ? Math.floor(w / 864e5) : null,
479
+ window: H.window,
480
+ value: u({
481
+ orderId: g,
482
+ offerId: D.id,
483
+ offerVersion: D.version,
484
+ offerDigest: H.subscriptionDigest,
485
+ quantity: 1,
486
+ at: $,
487
+ pricePaid: W,
488
+ transfers: n.slice(1, Q.length + 1),
489
+ grants: n.slice(Q.length + 1)
490
+ })
491
+ }), i = {
492
+ kind: o,
493
+ request: d,
494
+ currencies: k,
495
+ realMoney: F,
496
+ rulesVersion: V,
497
+ view: H,
498
+ receipt: r,
499
+ at: $
500
+ };
501
+ await e.insert(C.tableName, {
502
+ id: g,
503
+ tenantId: p,
504
+ ownerId: h,
505
+ ownerKey: E(p, h),
506
+ sequence: x.basis + 1,
507
+ state: "fulfilled",
508
+ digest: y,
509
+ body: i,
510
+ createdAt: new Date($)
511
+ });
512
+ let a = (await e.query(j(C).where(A("id", g)).limit(1).descriptor))[0];
513
+ if (!a) throw t("invalid_record");
514
+ return (await B(e, p, h, a)).receipt;
515
+ });
516
+ try {
517
+ return (await M(e, [re]))[0];
518
+ } catch (t) {
519
+ if (t instanceof ne) {
520
+ let t = await e.transactional((e) => Y(e, p, h, g, o, d));
521
+ if (t) return t;
522
+ }
523
+ throw t;
524
+ }
525
+ }), Z = (t) => H(() => {
526
+ let n = G(t);
527
+ return V(async () => (await F(e, n.tenant, n.owner), Object.freeze(r.subscriptions.map((e) => Object.freeze({
528
+ subscription: e,
529
+ subscriptionDigest: z(e, r.version)
530
+ })))));
531
+ }), Q = (n) => H(() => {
532
+ let r = G(n);
533
+ return V(async () => {
534
+ let n = m();
535
+ return e.transactional(async (e) => {
536
+ let i = await J(e, r.tenant, r.owner);
537
+ if (n < (i.accepted.at(-1)?.at ?? 0)) throw t("invalid_record");
538
+ return Object.freeze([...i.states.values()].map((e) => Object.freeze({
539
+ ...e,
540
+ window: K(e, n)
541
+ })));
542
+ });
543
+ });
544
+ }), $ = (e, t) => H(() => {
545
+ let n = G(e), r = Object.freeze({
546
+ tenantId: n.tenant,
547
+ ownerId: n.owner,
548
+ ...x(t)
549
+ });
550
+ return X(n, "start", r);
551
+ }), re = (e, n) => H(() => {
552
+ if (structuredClone(e).type !== "system") throw t("forbidden");
553
+ let r = b(n);
554
+ return X({
555
+ tenant: r.tenantId,
556
+ owner: r.ownerId
557
+ }, "renew", r);
558
+ }), ie = (e, t) => H(() => {
559
+ let n = G(e), r = Object.freeze({
560
+ tenantId: n.tenant,
561
+ ownerId: n.owner,
562
+ ...y(t)
563
+ });
564
+ return X(n, "daily", r);
565
+ }), ae = (e, t) => H(() => {
566
+ let n = G(e), r = Object.freeze({
567
+ tenantId: n.tenant,
568
+ ownerId: n.owner,
569
+ ...y(t)
570
+ });
571
+ return X(n, "cancel", r);
572
+ }), oe = (t, n) => H(() => {
573
+ let r = G(t), i = p(n), a = Object.freeze({
574
+ tenantId: r.tenant,
575
+ ownerId: r.owner,
576
+ ...i.kind === "start" ? x(i.intent) : i.intent
577
+ }), o = ee(r.tenant, r.owner, a.requestId);
578
+ return V(() => e.transactional(async (e) => {
579
+ let t = await Y(e, r.tenant, r.owner, o, i.kind, a);
580
+ return await F(e, r.tenant, r.owner), Object.freeze(t ? {
581
+ requestId: a.requestId,
582
+ state: "fulfilled",
583
+ receipt: t
584
+ } : {
585
+ requestId: a.requestId,
586
+ state: "not-observed"
587
+ });
588
+ }));
589
+ }), se = (e) => {
590
+ let n = structuredClone(e);
591
+ if (n.kind !== "guest" && n.kind !== "user") throw t("forbidden");
592
+ return Object.freeze({
593
+ tenant: a(n.tenantId),
594
+ owner: a(n.subjectId)
595
+ });
596
+ };
597
+ return Object.freeze({
598
+ list: Z,
599
+ active: Q,
600
+ start: $,
601
+ renew: re,
602
+ claimDaily: ie,
603
+ cancel: ae,
604
+ receipt: oe,
605
+ exportSubject: (t) => H(() => {
606
+ let n = se(t);
607
+ return V(() => e.transactional(async (e) => {
608
+ if ((await F(e, n.tenant, n.owner, !0))?.erased) return Object.freeze({ subscriptions: [] });
609
+ let t = await J(e, n.tenant, n.owner);
610
+ return Object.freeze({ subscriptions: Object.freeze(t.accepted.map((e) => Object.freeze({
611
+ kind: e.kind,
612
+ view: e.view,
613
+ receipt: e.receipt
614
+ }))) });
615
+ }));
616
+ }),
617
+ eraseSubject: (n) => H(() => {
618
+ let i = se(n);
619
+ return V(async () => {
620
+ await O(i.tenant, i.owner), await e.transactional(async (e) => {
621
+ await I(e, i.tenant, i.owner, !0);
622
+ let n = await e.query(j(C).where(k(A("tenantId", i.tenant), A("ownerKey", E(i.tenant, i.owner)))).limit(r.maxHistoryRows + 1).descriptor);
623
+ if (n.length > r.maxHistoryRows) throw t("limit_exceeded");
624
+ for (let r of n) {
625
+ if (!/^eve_[a-f0-9]{56}$/.test(String(r.id)) || r.ownerId !== i.owner && r.ownerId !== null || r.state !== "fulfilled" && r.state !== "erased") throw t("invalid_record");
626
+ await e.update(C.tableName, String(r.id), {
627
+ ownerId: null,
628
+ state: "erased",
629
+ digest: null,
630
+ body: null
631
+ });
632
+ }
633
+ await e.update(w.tableName, E(i.tenant, i.owner), {
634
+ ownerId: null,
635
+ erased: !0
636
+ });
637
+ });
638
+ });
639
+ })
640
+ });
641
+ };
642
+ //#endregion
643
+ export { J as dataStoreSubscriptions, q as resolveSubscriptionsStoreOptions, z as subscriptionDefinitionDigest };