@voltro/plugin-progression 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 (97) hide show
  1. package/CHANGELOG.md +24492 -0
  2. package/LICENSE +61 -0
  3. package/README.md +478 -2
  4. package/SECURITY.md +56 -0
  5. package/THIRD-PARTY-NOTICES.md +9841 -0
  6. package/dist/achievementComponents.js +91 -0
  7. package/dist/achievementStore.js +602 -0
  8. package/dist/achievementTables.entity.js +97 -0
  9. package/dist/achievements.d.ts +188 -0
  10. package/dist/achievements.js +204 -0
  11. package/dist/capture.js +63 -0
  12. package/dist/cmsQuests.d.ts +91 -0
  13. package/dist/cmsQuests.js +82 -0
  14. package/dist/components.d.ts +172 -0
  15. package/dist/components.js +4 -0
  16. package/dist/curve.d.ts +27 -0
  17. package/dist/curve.js +37 -0
  18. package/dist/daily.d.ts +152 -0
  19. package/dist/daily.js +125 -0
  20. package/dist/dailyStore.js +420 -0
  21. package/dist/dailySystem.fixture.js +137 -0
  22. package/dist/dailyTables.entity.js +89 -0
  23. package/dist/declarationFixtures.js +142 -0
  24. package/dist/errors.d.ts +13 -0
  25. package/dist/errors.js +19 -0
  26. package/dist/index.d.ts +1833 -0
  27. package/dist/index.js +19 -0
  28. package/dist/pass.d.ts +186 -0
  29. package/dist/pass.js +121 -0
  30. package/dist/passRules.js +95 -0
  31. package/dist/passStore.js +484 -0
  32. package/dist/passTables.entity.js +95 -0
  33. package/dist/plugin.js +1085 -0
  34. package/dist/port.js +4 -0
  35. package/dist/progressionStore.js +705 -0
  36. package/dist/questComponents.js +162 -0
  37. package/dist/questPort.d.ts +126 -0
  38. package/dist/questPort.js +4 -0
  39. package/dist/questRefresh.js +55 -0
  40. package/dist/questRefresh.server.js +125 -0
  41. package/dist/questRerollJournal.js +76 -0
  42. package/dist/questStore.js +591 -0
  43. package/dist/questTables.entity.js +116 -0
  44. package/dist/quests.d.ts +142 -0
  45. package/dist/quests.js +152 -0
  46. package/dist/rank-app/schedules/progression.dispatchRankClosures.cron.tsx +19 -0
  47. package/dist/rank-app/workflows/progression.closeRankSeason.workflow.server.tsx +10 -0
  48. package/dist/rank-app/workflows/progression.closeRankSeason.workflow.tsx +1 -0
  49. package/dist/rankClosure.d.ts +69 -0
  50. package/dist/rankClosure.js +34 -0
  51. package/dist/rankClosure.server.js +138 -0
  52. package/dist/rankClosureWorkflow.d.ts +34 -0
  53. package/dist/rankClosureWorkflow.js +15 -0
  54. package/dist/rankReader.server.js +56 -0
  55. package/dist/rankRewardParent.server.js +64 -0
  56. package/dist/rankSeasons.d.ts +144 -0
  57. package/dist/rankSeasons.js +90 -0
  58. package/dist/rankStore.js +520 -0
  59. package/dist/rankSystem.fixture.js +169 -0
  60. package/dist/rankTables.entity.js +100 -0
  61. package/dist/ranks.d.ts +167 -0
  62. package/dist/ranks.js +218 -0
  63. package/dist/resourceStore.js +463 -0
  64. package/dist/resourceTables.entity.js +88 -0
  65. package/dist/resources.d.ts +105 -0
  66. package/dist/resources.js +59 -0
  67. package/dist/reward-app/schedules/progression.dispatchRewards.cron.tsx +19 -0
  68. package/dist/reward-app/workflows/progression.deliverReward.workflow.server.tsx +10 -0
  69. package/dist/reward-app/workflows/progression.deliverReward.workflow.tsx +1 -0
  70. package/dist/reward.d.ts +27 -0
  71. package/dist/reward.js +38 -0
  72. package/dist/rewardDelivery.d.ts +163 -0
  73. package/dist/rewardDelivery.js +37 -0
  74. package/dist/rewardDelivery.server.js +429 -0
  75. package/dist/rewardDeliveryRecord.js +47 -0
  76. package/dist/rewardDeliveryWorkflow.d.ts +36 -0
  77. package/dist/rewardDeliveryWorkflow.js +15 -0
  78. package/dist/rpc.d.ts +880 -0
  79. package/dist/rpc.js +497 -0
  80. package/dist/server.d.ts +2812 -0
  81. package/dist/server.js +21 -0
  82. package/dist/stats.js +44 -0
  83. package/dist/strategies.d.ts +131 -0
  84. package/dist/strategies.js +4 -0
  85. package/dist/systemFixtures.js +473 -0
  86. package/dist/tables.entity.js +154 -0
  87. package/dist/testing.d.ts +1566 -0
  88. package/dist/testing.js +11 -0
  89. package/dist/track.js +71 -0
  90. package/dist/tutorial.d.ts +157 -0
  91. package/dist/tutorial.js +109 -0
  92. package/dist/tutorialStore.js +495 -0
  93. package/dist/tutorialSystem.fixture.js +99 -0
  94. package/dist/tutorialTables.entity.js +90 -0
  95. package/dist/web.d.ts +363 -0
  96. package/dist/web.js +6 -0
  97. package/package.json +167 -4
@@ -0,0 +1,520 @@
1
+ import { ProgressionError as e, progressionError as t } from "./errors.js";
2
+ import { captureArguments as n, identifier as r, integer as i, list as a, object as o } from "./capture.js";
3
+ import { captureRankClosureReceipt as s } from "./rankClosure.js";
4
+ import { capturePlayerRank as c, captureRankRating as l, defineRankLadder as u, resolveRank as d, resolveRankSeasonReset as f, resolveRankStrategies as p } from "./ranks.js";
5
+ import { acceptRankObservation as m, captureRankSeasonState as h, closeRankSeasonState as ee, defineRankSeason as g, initialRankSeasonState as _ } from "./rankSeasons.js";
6
+ import { admitRewardDeliveryRecord as v } from "./rewardDeliveryRecord.js";
7
+ import { questHash as y } from "./questStore.js";
8
+ import { rankClosureUnitsTable as b, rankDefinitionsTable as x, rankLedgerTable as S, rankOperationsTable as C, rankRewardsTable as w, rankStatesTable as T, rankSubjectsTable as E } from "./rankTables.entity.js";
9
+ import { Effect as D } from "effect";
10
+ import { and as O, eq as k, queryFor as A } from "@voltro/database";
11
+ // src/rankStore.ts
12
+ var j = (e) => {
13
+ let n = o(e, [
14
+ "ladders",
15
+ "seasons",
16
+ "source",
17
+ "strategies",
18
+ "allowedServices",
19
+ "maxHistoryRows",
20
+ "maxPendingRewards",
21
+ "now"
22
+ ]), s = Object.freeze(a(n.ladders, 64).map((e) => u(e))), c = o(n.source), l = n.now ?? Date.now, d = p(n.strategies), f = new Map(s.map((e) => [e.id, e])), m = Object.freeze(a(n.seasons, 256).map((e) => {
23
+ let n = o(e), i = f.get(r(n.ladderId));
24
+ if (!i) throw t("invalid_input");
25
+ return g(e, i);
26
+ })), h = Object.freeze(a(n.allowedServices ?? [], 256).map(r));
27
+ if (!s.length || !m.length || f.size !== s.length || new Set(m.map((e) => e.id)).size !== m.length || new Set(h).size !== h.length || typeof c.read != "function" || typeof l != "function") throw t("invalid_input");
28
+ return Object.freeze({
29
+ ladders: s,
30
+ seasons: m,
31
+ source: Object.freeze({ read: c.read.bind(n.source) }),
32
+ strategies: d,
33
+ allowedServices: h,
34
+ now: l,
35
+ maxHistoryRows: i(n.maxHistoryRows ?? 1e4, 1, 1e5),
36
+ maxPendingRewards: i(n.maxPendingRewards ?? 1e4, 1, 1e5)
37
+ });
38
+ }, M = (e, ...t) => e + y(t).slice(0, 56), N = (e) => i(e instanceof Date ? e.getTime() : typeof e == "string" ? Date.parse(e) : NaN, 0, 864e13), P = (n) => D.tryPromise({
39
+ try: n,
40
+ catch: (n) => n instanceof e ? n : t("unavailable")
41
+ }), F = (a, u) => {
42
+ let p = j(u), g = new Map(p.ladders.map((e) => [e.id, e])), F = new Map(p.seasons.map((e) => [e.id, e])), I = new Set(p.allowedServices), L = (e) => {
43
+ let n = F.get(e);
44
+ if (!n) throw t("not_found");
45
+ return {
46
+ season: n,
47
+ ladder: g.get(n.ladderId)
48
+ };
49
+ }, R = (e) => {
50
+ if (e.type !== "system" && !(e.type === "service" && I.has(e.service))) throw t("forbidden");
51
+ }, z = (e) => {
52
+ if (e.type !== "user") throw t("forbidden");
53
+ return {
54
+ tenant: r(e.tenantId),
55
+ owner: r(e.id)
56
+ };
57
+ }, B = (e, t) => M("prs_", e, t), V = (e, t, n) => M("prv_", e, t, n.id, String(n.version)), H = (e, t) => O(k("tenantId", e), k("ownerKey", B(e, t))), U = (e) => {
58
+ let n = o(e, [
59
+ "seasonId",
60
+ "kind",
61
+ "requestId"
62
+ ]);
63
+ if (n.kind !== "observe" && n.kind !== "close") throw t("invalid_record");
64
+ return L(r(n.seasonId)), Object.freeze({
65
+ seasonId: r(n.seasonId),
66
+ kind: n.kind,
67
+ requestId: r(n.requestId)
68
+ });
69
+ }, W = (e, t, n) => y({
70
+ tenant: e,
71
+ owner: t,
72
+ request: n
73
+ }), G = (e, t, n) => M("pro_", e, t, n.requestId), te = (e, t, n, r) => M("pri_", e, t, n.seasonId, n.kind, n.kind === "observe" ? y(r) : ""), ne = async (e, n) => {
74
+ if ((await a.insertIgnoreWithOutcome(e.tableName, n, { conflictColumns: ["id"] })).outcome !== "inserted" && !(await a.query(A(e).where(k("id", n.id)).limit(1).descriptor))[0]) throw t("unavailable");
75
+ }, K = (e, t) => ne(E, {
76
+ id: B(e, t),
77
+ tenantId: e,
78
+ ownerId: t,
79
+ erased: !1,
80
+ sequence: 0,
81
+ admission: 0
82
+ }), q = (e, n, r, a = !1) => {
83
+ if (e) {
84
+ if (e.id !== B(n, r) || e.tenantId !== n || typeof e.erased != "boolean") throw t("invalid_record");
85
+ if (e.erased) {
86
+ if (e.ownerId !== null) throw t("invalid_record");
87
+ if (!a) throw t("erased");
88
+ } else if (e.ownerId !== r) throw t("invalid_record");
89
+ i(e.sequence, 0, 2147483647);
90
+ }
91
+ }, re = async (e, n, r, i = !1) => {
92
+ if (await e.updateMany(E.tableName, { admission: 0 }, { where: k("id", B(n, r)) }) !== 1) throw t("invalid_record");
93
+ let a = (await e.query(A(E).where(k("id", B(n, r))).limit(1).descriptor))[0];
94
+ if (q(a, n, r, i), !a) throw t("invalid_record");
95
+ return a;
96
+ }, J = async (e, n, r, i) => {
97
+ let a = n === w ? p.maxPendingRewards : p.maxHistoryRows, o = await e.query(A(n).where(H(r, i)).limit(a + 1).descriptor);
98
+ if (o.length > a) throw t("limit_exceeded");
99
+ if (o.some((e) => e.tenantId !== r || e.ownerKey !== B(r, i))) throw t("invalid_record");
100
+ return o;
101
+ }, Y = (e, t) => M("prd_", e, t.id, String(t.version)), X = async (e, n, r, i = !1) => {
102
+ let a = Y(n, r);
103
+ if (i && await e.updateMany(x.tableName, { admission: 0 }, { where: k("id", a) }) !== 1) throw t("invalid_record");
104
+ let o = (await e.query(A(x).where(k("id", a)).limit(1).descriptor))[0];
105
+ if (!o || o.tenantId !== n || o.seasonId !== r.id || o.rulesVersion !== p.strategies.version || y(o.config) !== y({
106
+ season: r,
107
+ ladder: g.get(r.ladderId)
108
+ })) throw t("conflict");
109
+ }, Z = (e, n, i) => {
110
+ let { season: a, ladder: o } = L(r(e.seasonId)), s = h(e.state, a, o, p.strategies.version);
111
+ if (e.id !== V(n, i, a) || e.ownerId !== i || e.ownerKey !== B(n, i) || e.tenantId !== n || N(e.updatedAt) !== (s.closedAt ?? s.last?.observedAt)) throw t("invalid_record");
112
+ return s;
113
+ }, Q = (e) => {
114
+ let n = U(e.intent), { season: i, ladder: a } = L(n.seasonId), s = o(e.receipt, [
115
+ "operationId",
116
+ "kind",
117
+ "after",
118
+ "rewardsQueued",
119
+ "at"
120
+ ]), c = h(s.after, i, a, p.strategies.version), l = N(e.at), u = n.kind === "close" && c.highest ? i.rewards[c.highest.tierIndex].length : 0;
121
+ if (r(e.claimKey), e.state !== "committed" || e.linkedOperationId !== null || s.operationId !== e.id || s.kind !== n.kind || s.at !== l || (n.kind === "close" ? c.closedAt !== l : c.last?.observedAt !== l || c.closedAt !== null) || s.rewardsQueued !== u) throw t("invalid_record");
122
+ return Object.freeze({
123
+ operationId: r(e.id),
124
+ kind: n.kind,
125
+ after: c,
126
+ rewardsQueued: u,
127
+ at: l
128
+ });
129
+ }, ie = async (e, n, r, i) => {
130
+ let a = (await e.query(A(C).where(k("id", G(n, r, i))).limit(1).descriptor))[0];
131
+ if (!a) return;
132
+ if (a.state === "erased") throw t("erased");
133
+ if (a.tenantId !== n || a.ownerKey !== B(n, r) || a.digest !== W(n, r, i) || y(U(a.intent)) !== y(i)) throw t("conflict");
134
+ if (await X(e, n, L(i.seasonId).season), a.state === "committed") return Q(a);
135
+ if (a.state !== "replayed" || a.receipt !== null || typeof a.linkedOperationId != "string") throw t("invalid_record");
136
+ let o = (await e.query(A(C).where(k("id", a.linkedOperationId)).limit(1).descriptor))[0], s = o ? U(o.intent) : void 0;
137
+ if (!o || o.tenantId !== n || o.ownerKey !== B(n, r) || s?.seasonId !== i.seasonId || s.kind !== i.kind || o.claimKey !== a.claimKey) throw t("invalid_record");
138
+ return Q(o);
139
+ }, ae = async (e, n, r, i) => {
140
+ let a = (await J(e, T, n, r)).filter((e) => e.seasonId === i.id), o = (await J(e, S, n, r)).filter((e) => e.seasonId === i.id).sort((e, t) => Number(e.sequence) - Number(t.sequence)).at(-1);
141
+ if (a.length > 1 || !!o != !!a.length) throw t("invalid_record");
142
+ let s = a[0] ? Z(a[0], n, r) : _(i);
143
+ if (o && y(o.after) !== y(s)) throw t("invalid_record");
144
+ return s;
145
+ }, $ = (c, u, h) => {
146
+ try {
147
+ let e = n(c), g = o(u, h === "observe" ? ["seasonId", "requestId"] : [
148
+ "tenantId",
149
+ "ownerId",
150
+ "seasonId",
151
+ "requestId"
152
+ ]), { tenant: _, owner: v } = h === "observe" ? z(e) : (R(e), {
153
+ tenant: r(g.tenantId),
154
+ owner: r(g.ownerId)
155
+ }), O = U({
156
+ seasonId: g.seasonId,
157
+ requestId: g.requestId,
158
+ kind: h
159
+ }), { season: j, ladder: F } = L(O.seasonId);
160
+ return P(async () => {
161
+ let n = (await a.query(A(E).where(k("id", B(_, v))).limit(1).descriptor))[0];
162
+ q(n, _, v);
163
+ let r = await ie(a, _, v, O);
164
+ if (r) return r;
165
+ let o = null, c = null;
166
+ h === "observe" && (o = l(await D.runPromise(D.try({
167
+ try: () => p.source.read(e, F.rating),
168
+ catch: () => t("unavailable")
169
+ }).pipe(D.flatten)), F.rating));
170
+ let u = i(p.now(), 0, 864e13);
171
+ if (h === "observe") {
172
+ if (u < j.from || u >= j.until) throw t("conflict");
173
+ c = d({
174
+ ladder: F,
175
+ rating: o,
176
+ at: u
177
+ }, p.strategies);
178
+ } else if (u < j.until) throw t("conflict");
179
+ await K(_, v), await ne(x, {
180
+ id: Y(_, j),
181
+ tenantId: _,
182
+ seasonId: j.id,
183
+ config: {
184
+ season: j,
185
+ ladder: F
186
+ },
187
+ rulesVersion: p.strategies.version,
188
+ admission: 0
189
+ });
190
+ let g = null, P = null;
191
+ if (h === "close") {
192
+ let e = await ae(a, _, v, j);
193
+ P = y(e.last), e.last && (g = f(F, e.last.rawScore, p.strategies));
194
+ }
195
+ return a.transactional(async (e) => {
196
+ let n = await re(e, _, v);
197
+ await X(e, _, j, !0);
198
+ let r = await ie(e, _, v, O);
199
+ if (r) return r;
200
+ let a = await J(e, S, _, v), l = await J(e, C, _, v), d = await J(e, w, _, v), f = te(_, v, O, o), x = a.find((e) => e.claimKey === f);
201
+ if (l.length >= p.maxHistoryRows) throw t("limit_exceeded");
202
+ if (x) {
203
+ let n = l.find((e) => e.id === x.operationId);
204
+ if (!n) throw t("invalid_record");
205
+ let r = Q(n);
206
+ return await e.insert(C.tableName, {
207
+ id: G(_, v, O),
208
+ tenantId: _,
209
+ ownerKey: B(_, v),
210
+ state: "replayed",
211
+ claimKey: f,
212
+ linkedOperationId: r.operationId,
213
+ intent: O,
214
+ digest: W(_, v, O),
215
+ receipt: null,
216
+ at: new Date(u)
217
+ }), r;
218
+ }
219
+ if (a.length >= p.maxHistoryRows) throw t("limit_exceeded");
220
+ if (a.length !== n.sequence) throw t("invalid_record");
221
+ let D = await ae(e, _, v, j);
222
+ if (h === "close" && y(D.last) !== P) throw t("conflict");
223
+ let I = h === "observe" ? m(j, F, D, c, p.strategies.version) : ee(j, F, D, u, g, p.strategies.version), L = h === "close" && I.highest ? j.rewards[I.highest.tierIndex] : [], R = L.length;
224
+ if (d.length + R > p.maxPendingRewards) throw t("limit_exceeded");
225
+ let z = G(_, v, O), H = i(Number(n.sequence) + 1, 1, 2147483647), U = Object.freeze({
226
+ operationId: z,
227
+ kind: h,
228
+ after: I,
229
+ rewardsQueued: R,
230
+ at: u
231
+ }), ne = {
232
+ request: O,
233
+ rating: o,
234
+ rank: c,
235
+ resetScore: g
236
+ }, K = (await e.query(A(T).where(k("id", V(_, v, j))).limit(1).descriptor))[0], q = {
237
+ tenantId: _,
238
+ ownerId: v,
239
+ ownerKey: B(_, v),
240
+ seasonId: j.id,
241
+ state: I,
242
+ updatedAt: new Date(u)
243
+ };
244
+ if (K) {
245
+ if (!await e.update(T.tableName, String(K.id), q)) throw t("invalid_record");
246
+ } else await e.insert(T.tableName, {
247
+ id: V(_, v, j),
248
+ ...q
249
+ });
250
+ let Y = V(_, v, j), Z = M("prc_", Y), $ = (await e.query(A(b).where(k("id", Z)).limit(1).descriptor))[0], oe = h === "close" ? s({
251
+ unitId: Z,
252
+ status: "closed",
253
+ operationId: z,
254
+ rewardsQueued: R,
255
+ at: u
256
+ }) : null;
257
+ if ($) {
258
+ if ($.tenantId !== _ || $.ownerId !== v || $.ownerKey !== B(_, v) || $.stateId !== Y || $.seasonId !== j.id || $.version !== j.version || N($.until) !== j.until || $.state !== "open" || $.receipt !== null || h === "close" && !await e.update(b.tableName, Z, {
259
+ state: "closed",
260
+ receipt: oe
261
+ })) throw t("invalid_record");
262
+ } else {
263
+ if (D.observations > 0) throw t("invalid_record");
264
+ await e.insert(b.tableName, {
265
+ id: Z,
266
+ tenantId: _,
267
+ ownerId: v,
268
+ ownerKey: B(_, v),
269
+ seasonId: j.id,
270
+ version: j.version,
271
+ stateId: Y,
272
+ until: new Date(j.until),
273
+ state: h === "close" ? "closed" : "open",
274
+ receipt: oe,
275
+ createdAt: new Date(u)
276
+ });
277
+ }
278
+ await e.insert(S.tableName, {
279
+ id: M("prl_", z),
280
+ tenantId: _,
281
+ ownerId: v,
282
+ ownerKey: B(_, v),
283
+ seasonId: j.id,
284
+ sequence: H,
285
+ operationId: z,
286
+ claimKey: f,
287
+ intent: ne,
288
+ before: D,
289
+ after: I,
290
+ at: new Date(u)
291
+ }), await e.insert(C.tableName, {
292
+ id: z,
293
+ tenantId: _,
294
+ ownerKey: B(_, v),
295
+ state: "committed",
296
+ claimKey: f,
297
+ linkedOperationId: null,
298
+ intent: O,
299
+ digest: W(_, v, O),
300
+ receipt: U,
301
+ at: new Date(u)
302
+ });
303
+ for (let [t, n] of L.entries()) await e.insert(w.tableName, {
304
+ id: M("prr_", z, String(t)),
305
+ tenantId: _,
306
+ ownerId: v,
307
+ ownerKey: B(_, v),
308
+ seasonId: j.id,
309
+ operationId: z,
310
+ reward: n,
311
+ state: "pending",
312
+ createdAt: new Date(u),
313
+ deliveredAt: null,
314
+ deliveryIntent: null,
315
+ deliveryDigest: null,
316
+ deliveryReceipt: null
317
+ });
318
+ if (!await e.update(E.tableName, B(_, v), { sequence: H })) throw t("invalid_record");
319
+ return U;
320
+ });
321
+ });
322
+ } catch (n) {
323
+ return D.fail(n instanceof e ? n : t("invalid_input"));
324
+ }
325
+ }, oe = (n, i) => {
326
+ try {
327
+ let e = o(n, [
328
+ "tenantId",
329
+ "subjectId",
330
+ "kind"
331
+ ]), s = r(e.tenantId), c = r(e.subjectId);
332
+ if (e.kind !== "user" && e.kind !== "guest") throw t("invalid_input");
333
+ return P(async () => (await K(s, c), a.transactional(async (e) => {
334
+ await re(e, s, c, !0);
335
+ let n = await J(e, T, s, c), r = await J(e, S, s, c), a = await J(e, C, s, c), o = await J(e, w, s, c), l = await J(e, b, s, c);
336
+ if (!i) return {
337
+ ranks: n.map((e) => e.state),
338
+ history: r.map((e) => ({
339
+ intent: e.intent,
340
+ before: e.before,
341
+ after: e.after
342
+ })),
343
+ requests: a.map((e) => ({
344
+ intent: e.intent,
345
+ receipt: e.receipt
346
+ })),
347
+ rewards: o.map((e) => ({
348
+ reward: e.reward,
349
+ state: e.state,
350
+ deliveryIntent: e.deliveryIntent,
351
+ deliveryReceipt: e.deliveryReceipt
352
+ })),
353
+ closure: l.map((e) => ({
354
+ seasonId: e.seasonId,
355
+ state: e.state,
356
+ receipt: e.receipt
357
+ }))
358
+ };
359
+ if (await e.deleteMany(T.tableName, { where: H(s, c) }), await e.updateMany(S.tableName, {
360
+ ownerId: null,
361
+ ownerKey: null,
362
+ intent: null,
363
+ before: null,
364
+ after: null
365
+ }, { where: H(s, c) }), await e.updateMany(C.tableName, {
366
+ ownerKey: null,
367
+ state: "erased",
368
+ linkedOperationId: null,
369
+ intent: null,
370
+ digest: null,
371
+ receipt: null
372
+ }, { where: H(s, c) }), await e.updateMany(w.tableName, {
373
+ ownerId: null,
374
+ ownerKey: null,
375
+ reward: null,
376
+ state: "erased",
377
+ deliveredAt: null,
378
+ deliveryIntent: null,
379
+ deliveryDigest: null,
380
+ deliveryReceipt: null
381
+ }, { where: H(s, c) }), await e.updateMany(b.tableName, {
382
+ ownerId: null,
383
+ ownerKey: null,
384
+ state: "erased",
385
+ receipt: null
386
+ }, { where: H(s, c) }), !await e.update(E.tableName, B(s, c), {
387
+ erased: !0,
388
+ ownerId: null
389
+ })) throw t("invalid_record");
390
+ })));
391
+ } catch (n) {
392
+ return D.fail(n instanceof e ? n : t("invalid_input"));
393
+ }
394
+ }, se = (i, u, d, f) => {
395
+ try {
396
+ R(n(i));
397
+ let e = r(u), h = r(d);
398
+ return P(async () => (await K(e, h), a.transactional(async (n) => {
399
+ let i = await re(n, e, h), a = [...await J(n, S, e, h)].sort((e, t) => Number(e.sequence) - Number(t.sequence)), u = await J(n, C, e, h), d = await J(n, w, e, h), g = /* @__PURE__ */ new Map(), x = /* @__PURE__ */ new Set(), E = 0;
400
+ if (a.length !== i.sequence) throw t("invalid_record");
401
+ for (let [i, s] of a.entries()) {
402
+ let a = o(s.intent, [
403
+ "request",
404
+ "rating",
405
+ "rank",
406
+ "resetScore"
407
+ ]), f = U(a.request), { season: b, ladder: S } = L(f.seasonId), C = g.get(b.id) ?? _(b), w = u.find((e) => e.id === s.operationId), T = N(s.at);
408
+ await X(n, e, b);
409
+ let D = f.kind === "observe" ? l(a.rating, S.rating) : null, O = f.kind === "observe" ? c(a.rank, S, p.strategies.version) : null;
410
+ if (f.kind === "close" && (a.rating !== null || a.rank !== null) || f.kind === "observe" && (a.resetScore !== null || O.games !== D.games || O.ratingUpdatedAt !== D.updatedAt || O.observedAt !== T)) throw t("invalid_record");
411
+ let k = te(e, h, f, D);
412
+ if (s.sequence !== i + 1 || s.id !== M("prl_", r(s.operationId)) || s.operationId !== G(e, h, f) || s.seasonId !== b.id || s.ownerId !== h || s.claimKey !== k || w?.claimKey !== k || x.has(k) || y(s.before) !== y(C) || !w || w.digest !== W(e, h, f) || y(U(w.intent)) !== y(f)) throw t("invalid_record");
413
+ let A = f.kind === "observe" ? m(b, S, C, O, p.strategies.version) : ee(b, S, C, T, a.resetScore, p.strategies.version), j = Q(w);
414
+ if (y(A) !== y(s.after) || y(A) !== y(j.after) || j.at !== T) throw t("invalid_record");
415
+ let P = f.kind === "close" && A.highest ? b.rewards[A.highest.tierIndex] : [];
416
+ for (let [e, n] of P.entries()) {
417
+ let r = d.find((t) => t.id === M("prr_", j.operationId, String(e)));
418
+ if (!r || r.ownerId !== h || r.seasonId !== b.id || r.operationId !== j.operationId || r.state !== "pending" && r.state !== "delivered" || N(r.createdAt) !== T || y(r.reward) !== y(n)) throw t("invalid_record");
419
+ }
420
+ for (let e of d.filter((e) => e.operationId === j.operationId)) v(e, "rank", y);
421
+ E += P.length, g.set(b.id, A), x.add(k);
422
+ }
423
+ if (d.length !== E || u.filter((e) => e.state === "committed").length !== a.length) throw t("invalid_record");
424
+ for (let n of u) {
425
+ let r = U(n.intent);
426
+ if (n.id !== G(e, h, r) || n.digest !== W(e, h, r)) throw t("invalid_record");
427
+ if (n.state === "committed") {
428
+ Q(n);
429
+ continue;
430
+ }
431
+ if (n.state !== "replayed" || n.receipt !== null || typeof n.linkedOperationId != "string") throw t("invalid_record");
432
+ let i = u.find((e) => e.id === n.linkedOperationId), a = i ? U(i.intent) : void 0;
433
+ if (!i || a?.seasonId !== r.seasonId || a.kind !== r.kind || i.claimKey !== n.claimKey) throw t("invalid_record");
434
+ Q(i);
435
+ }
436
+ let D = await J(n, T, e, h), O = await J(n, b, e, h), k = D.length === g.size && O.length === g.size;
437
+ for (let t of D) {
438
+ let n = Z(t, e, h), r = g.get(n.seasonId);
439
+ r && y(r) === y(n) || (k = !1);
440
+ }
441
+ let A = [...g.values()].map((t) => {
442
+ let { season: n } = L(t.seasonId), r = V(e, h, n), i = M("prc_", r), c = a.find((e) => e.seasonId === n.id), l = t.closedAt === null ? void 0 : a.find((e) => e.seasonId === n.id && o(o(e.intent).request).kind === "close"), d = l ? Q(u.find((e) => e.id === l.operationId)) : null;
443
+ return {
444
+ id: i,
445
+ tenantId: e,
446
+ ownerId: h,
447
+ ownerKey: B(e, h),
448
+ seasonId: n.id,
449
+ version: n.version,
450
+ stateId: r,
451
+ until: new Date(n.until),
452
+ state: t.closedAt === null ? "open" : "closed",
453
+ receipt: d ? s({
454
+ unitId: i,
455
+ status: "closed",
456
+ operationId: d.operationId,
457
+ rewardsQueued: d.rewardsQueued,
458
+ at: d.at
459
+ }) : null,
460
+ createdAt: new Date(N(c.at))
461
+ };
462
+ });
463
+ for (let e of O) {
464
+ let t = A.find((t) => t.id === e.id);
465
+ t && e.ownerId === h && e.stateId === t.stateId && e.seasonId === t.seasonId && e.version === t.version && e.state === t.state && N(e.until) === t.until.getTime() && N(e.createdAt) === t.createdAt.getTime() && y(e.receipt) === y(t.receipt) || (k = !1);
466
+ }
467
+ if (f) {
468
+ await n.deleteMany(T.tableName, { where: H(e, h) });
469
+ for (let t of g.values()) {
470
+ let { season: r } = L(t.seasonId);
471
+ await n.insert(T.tableName, {
472
+ id: V(e, h, r),
473
+ tenantId: e,
474
+ ownerId: h,
475
+ ownerKey: B(e, h),
476
+ seasonId: r.id,
477
+ state: t,
478
+ updatedAt: new Date(t.closedAt ?? t.last.observedAt)
479
+ });
480
+ }
481
+ await n.deleteMany(b.tableName, { where: H(e, h) });
482
+ for (let e of A) await n.insert(b.tableName, e);
483
+ k = !0;
484
+ }
485
+ return {
486
+ operations: a.length,
487
+ seasons: g.size,
488
+ rewardRows: d.length,
489
+ matches: k
490
+ };
491
+ })));
492
+ } catch (n) {
493
+ return D.fail(n instanceof e ? n : t("invalid_input"));
494
+ }
495
+ };
496
+ return Object.freeze({
497
+ observe: (e, t) => $(e, t, "observe"),
498
+ close: (e, t) => $(e, t, "close"),
499
+ mine: (i) => {
500
+ try {
501
+ let { tenant: e, owner: o } = z(n(i));
502
+ return P(async () => {
503
+ let n = (await a.query(A(E).where(k("id", B(e, o))).limit(1).descriptor))[0];
504
+ q(n, e, o);
505
+ let i = await J(a, T, e, o);
506
+ if (!n && i.length) throw t("invalid_record");
507
+ for (let t of i) await X(a, e, L(r(t.seasonId)).season);
508
+ return Object.freeze(i.map((t) => Z(t, e, o)));
509
+ });
510
+ } catch (n) {
511
+ return D.fail(n instanceof e ? n : t("invalid_input"));
512
+ }
513
+ },
514
+ exportSubject: (e) => oe(e, !1),
515
+ eraseSubject: (e) => D.asVoid(oe(e, !0)),
516
+ auditRanks: (e, t, n) => se(e, t, n, !1),
517
+ rebuildRanks: (e, t, n) => se(e, t, n, !0)
518
+ });
519
+ };
520
+ export { F as dataStoreRankSeasons, j as resolveRankStoreOptions };
@@ -0,0 +1,169 @@
1
+ import { RankRatingSource as e, defaultRankDecay as t, defaultRankGrade as n, defaultRankScore as r, defaultRankSeasonReset as i, resolveRank as a, resolveRankSeasonReset as o } from "./ranks.js";
2
+ import { RankPort as s } from "./rankSeasons.js";
3
+ import { trackDeclaration as c } from "./track.js";
4
+ import { progressionPlugin as l } from "./plugin.js";
5
+ import { progressionFixture as u, rankSeasonPolicyFixture as d } from "./declarationFixtures.js";
6
+ import { Effect as f, Layer as p } from "effect";
7
+ import { ensureTableRegistered as m } from "@voltro/database";
8
+ import { InMemoryDataStore as h } from "@voltro/runtime";
9
+ // src/rankSystem.fixture.ts
10
+ var g = u(), { ladder: _, rating: v, at: y, season: b } = d(), x = Object.freeze({ read: () => f.succeed(v) }), S = {
11
+ app: {
12
+ name: "rank-port-fixture",
13
+ type: "api",
14
+ voltroVersion: "0.83.1"
15
+ },
16
+ config: {},
17
+ env: {},
18
+ logger: {
19
+ info() {},
20
+ warn() {},
21
+ error() {},
22
+ debug() {}
23
+ }
24
+ }, C = p.effectContext(f.acquireRelease(f.sync(() => l({
25
+ tracks: [c(g.track)],
26
+ stats: [{ id: "wins" }],
27
+ statSources: ["match.finished"],
28
+ now: () => y,
29
+ ranks: {
30
+ ladders: [_],
31
+ seasons: [b],
32
+ source: x,
33
+ now: () => y
34
+ }
35
+ })), (e) => f.suspend(() => e.onDeactivate(S))).pipe(f.tap((e) => f.gen(function* () {
36
+ e.extendSchema.tables.forEach((e) => m(e)), yield* f.promise(() => Promise.resolve(e.bindDataStore(new h())));
37
+ }).pipe(f.orDie)), f.flatMap((e) => p.build(e.services)))), w = p.succeed(e, x), T = {
38
+ type: "user",
39
+ id: "a",
40
+ tenantId: "fixture",
41
+ kind: "guest"
42
+ }, E = Object.freeze({
43
+ kind: "port",
44
+ tag: s,
45
+ fakeLayer: C,
46
+ probe: {
47
+ run: () => f.scoped(f.gen(function* () {
48
+ let e = yield* s, t = {
49
+ seasonId: b.id,
50
+ requestId: "fixture.observe"
51
+ }, n = yield* e.observe(T, t), r = yield* e.observe(T, t);
52
+ return {
53
+ original: n.operationId === r.operationId,
54
+ highest: n.after.highest?.tier ?? null,
55
+ seasons: (yield* e.mine(T)).length
56
+ };
57
+ }).pipe(f.provide(C))),
58
+ expected: {
59
+ original: !0,
60
+ highest: "Gold",
61
+ seasons: 1
62
+ }
63
+ }
64
+ }), D = Object.freeze({
65
+ kind: "port",
66
+ tag: e,
67
+ fakeLayer: w,
68
+ probe: {
69
+ run: () => f.scoped(f.gen(function* () {
70
+ let t = yield* (yield* e).read(T, _.rating);
71
+ return {
72
+ games: t.games,
73
+ updatedAt: t.updatedAt
74
+ };
75
+ }).pipe(f.provide(w))),
76
+ expected: {
77
+ games: 10,
78
+ updatedAt: 1e3
79
+ }
80
+ }
81
+ }), O = Object.freeze({
82
+ ladder: _,
83
+ rating: v,
84
+ at: y
85
+ }), k = Object.freeze({
86
+ ladder: _,
87
+ rating: v,
88
+ at: y + 2592e6
89
+ }), A = Object.freeze({
90
+ kind: "slot",
91
+ defaultImplementation: r,
92
+ defaultProbe: {
93
+ run: () => f.sync(() => a(O).rawScore),
94
+ expected: 250
95
+ },
96
+ overrideProbe: {
97
+ run: () => f.sync(() => a(O, {
98
+ version: "fixture.v2",
99
+ score: () => 120
100
+ }).rawScore),
101
+ expected: 120
102
+ }
103
+ }), j = Object.freeze({
104
+ kind: "slot",
105
+ defaultImplementation: t,
106
+ defaultProbe: {
107
+ run: () => f.sync(() => a(k).score),
108
+ expected: 10
109
+ },
110
+ overrideProbe: {
111
+ run: () => f.sync(() => a(k, {
112
+ version: "fixture.v2",
113
+ decay: (e) => e.score
114
+ }).score),
115
+ expected: 250
116
+ }
117
+ }), M = Object.freeze({
118
+ kind: "slot",
119
+ defaultImplementation: n,
120
+ defaultProbe: {
121
+ run: () => f.sync(() => {
122
+ let e = a(O).grade;
123
+ return e && {
124
+ tier: e.tier,
125
+ division: e.division
126
+ };
127
+ }),
128
+ expected: {
129
+ tier: "Gold",
130
+ division: 2
131
+ }
132
+ },
133
+ overrideProbe: {
134
+ run: () => f.sync(() => {
135
+ let e = a(O, {
136
+ version: "fixture.v2",
137
+ grade: () => ({
138
+ index: 0,
139
+ tier: "Bronze",
140
+ tierIndex: 0,
141
+ division: 2
142
+ })
143
+ }).grade;
144
+ return e && {
145
+ tier: e.tier,
146
+ division: e.division
147
+ };
148
+ }),
149
+ expected: {
150
+ tier: "Bronze",
151
+ division: 2
152
+ }
153
+ }
154
+ }), N = Object.freeze({
155
+ kind: "slot",
156
+ defaultImplementation: i,
157
+ defaultProbe: {
158
+ run: () => f.sync(() => o(_, 250)),
159
+ expected: 175
160
+ },
161
+ overrideProbe: {
162
+ run: () => f.sync(() => o(_, 250, {
163
+ version: "fixture.v2",
164
+ seasonReset: () => 0
165
+ })),
166
+ expected: 0
167
+ }
168
+ });
169
+ export { j as rankDecaySystemFixture, C as rankFakeLayer, M as rankGradeSystemFixture, E as rankPortSystemFixture, w as rankRatingSourceFakeLayer, D as rankRatingSourceSystemFixture, A as rankScoreSystemFixture, N as rankSeasonResetSystemFixture };