@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,591 @@
1
+ import { ProgressionError as e, progressionError as t } from "./errors.js";
2
+ import { identifier as n, integer as r, list as i, object as a } from "./capture.js";
3
+ import { captureQuestSignal as o, defaultQuestProgressResolver as s, defaultQuestSelector as c, defineQuest as l, defineQuestPool as u, questTimezone as d, resolveQuestProgress as f, selectQuests as p } from "./quests.js";
4
+ import { questDefinitionsTable as m, questOperationsTable as h, questOriginsTable as g, questRewardsTable as _, questSubjectsTable as v, questWindowsTable as y, questsTable as b } from "./questTables.entity.js";
5
+ import { Effect as x } from "effect";
6
+ import { createHash as S } from "node:crypto";
7
+ import { and as C, eq as w, queryFor as T } from "@voltro/database";
8
+ // src/questStore.ts
9
+ var E = (e) => {
10
+ let o = a(e, [
11
+ "source",
12
+ "channel",
13
+ "locale",
14
+ "allowedServices",
15
+ "eventSources",
16
+ "statSources",
17
+ "maxWorkRows",
18
+ "now",
19
+ "strategies"
20
+ ]), l = a(o.source), u = a(o.strategies ?? {}, [
21
+ "questSelector",
22
+ "questProgressResolver",
23
+ "version"
24
+ ]), d = u.questSelector ?? c, f = u.questProgressResolver ?? s, p = o.now ?? Date.now;
25
+ if (typeof l.read != "function" || typeof d != "function" || typeof f != "function" || typeof p != "function" || (d !== c || f !== s) && u.version === void 0) throw t("invalid_input");
26
+ let m = (e, r) => {
27
+ let a = Object.freeze(i(e ?? [], r).map(n));
28
+ if (new Set(a).size !== a.length) throw t("invalid_input");
29
+ return a;
30
+ };
31
+ return Object.freeze({
32
+ source: Object.freeze({ read: l.read }),
33
+ channel: n(o.channel),
34
+ locale: n(o.locale),
35
+ allowedServices: m(o.allowedServices, 256),
36
+ eventSources: m(o.eventSources, 64),
37
+ statSources: m(o.statSources, 64),
38
+ maxWorkRows: r(o.maxWorkRows ?? 1e4, 1, 1e5),
39
+ now: p,
40
+ strategies: Object.freeze({
41
+ questSelector: d,
42
+ questProgressResolver: f,
43
+ version: n(u.version ?? "default.v1")
44
+ })
45
+ });
46
+ }, D = (e) => {
47
+ if (e === null || typeof e == "string" || typeof e == "boolean" || typeof e == "number" && Number.isFinite(e)) return e;
48
+ if (Array.isArray(e)) return i(e, 1e5).map(D);
49
+ let t = a(e);
50
+ return Object.fromEntries(Object.keys(t).sort().map((e) => [e, D(t[e])]));
51
+ }, O = (e) => S("sha256").update(JSON.stringify(D(e))).digest("hex"), k = (e, ...t) => e + O(t).slice(0, 56), A = (n) => x.tryPromise({
52
+ try: n,
53
+ catch: (n) => n instanceof e ? n : t("unavailable")
54
+ }), j = (e) => {
55
+ let n = e instanceof Date ? e.getTime() : typeof e == "string" ? Date.parse(e) : NaN;
56
+ if (!Number.isSafeInteger(n) || n < 0 || n > 864e13) throw t("invalid_record");
57
+ return n;
58
+ }, M = (s, c) => {
59
+ let S = E(c), D = new Set(S.allowedServices), M = (e) => e.type === "system" || e.type === "service" && D.has(e.service), N = (e) => {
60
+ if (e.type !== "user") throw t("forbidden");
61
+ return {
62
+ tenantId: n(e.tenantId),
63
+ ownerId: n(e.id)
64
+ };
65
+ }, P = (n) => {
66
+ try {
67
+ return n();
68
+ } catch (n) {
69
+ return x.fail(n instanceof e ? n : t("invalid_input"));
70
+ }
71
+ }, F = () => r(S.now(), 0, 864e13), I = (e, t) => k("qps_", e, t), L = (e, t) => C(w("tenantId", e), w("ownerId", t)), R = async (e, t) => {
72
+ try {
73
+ await s.insertIgnore(e, t, { conflictColumns: ["id"] });
74
+ } catch (n) {
75
+ if (!(await s.query({
76
+ table: e,
77
+ predicate: w("id", t.id),
78
+ order: [],
79
+ take: 1,
80
+ skip: void 0,
81
+ projection: void 0
82
+ }))[0]) throw n;
83
+ }
84
+ }, z = (e, t) => R(v.tableName, {
85
+ id: I(e, t),
86
+ tenantId: e,
87
+ ownerId: t,
88
+ erased: !1,
89
+ admission: 0
90
+ }), B = (e, n, r, i = !1) => {
91
+ if (e) {
92
+ if (e.tenantId !== n) throw t("invalid_record");
93
+ if (e.erased === !0) {
94
+ if (i && e.ownerId === null) return;
95
+ throw t("erased");
96
+ }
97
+ if (e.erased !== !1 || e.ownerId !== r) throw t("invalid_record");
98
+ }
99
+ }, V = async (e, n, r, i = !1) => {
100
+ let a = I(n, r);
101
+ if (await e.updateMany(v.tableName, { admission: 0 }, { where: w("id", a) }) !== 1) throw t("unavailable");
102
+ let o = (await e.query(T(v).where(w("id", a)).limit(1).descriptor))[0];
103
+ if (!o) throw t("invalid_record");
104
+ B(o, n, r, i);
105
+ }, H = async (e, n, r, i) => {
106
+ let a = await e.query(T(n).where(C(w("tenantId", r), w("ownerKey", i))).limit(S.maxWorkRows + 1).descriptor);
107
+ if (a.length > S.maxWorkRows) throw t("limit_exceeded");
108
+ if (a.some((e) => e.tenantId !== r || e.ownerKey !== i)) throw t("invalid_record");
109
+ return a;
110
+ }, U = async (e, n, r, i) => {
111
+ let a = await e.query(T(n).where(L(r, i)).limit(S.maxWorkRows + 1).descriptor);
112
+ if (a.length > S.maxWorkRows) throw t("limit_exceeded");
113
+ if (a.some((e) => e.tenantId !== r || e.ownerId !== i || e.ownerKey !== I(r, i))) throw t("invalid_record");
114
+ return a;
115
+ }, W = (e) => {
116
+ let o = a(e.receipt, [
117
+ "operationId",
118
+ "kind",
119
+ "assignmentIds",
120
+ "completedIds",
121
+ "rewardsQueued",
122
+ "at"
123
+ ]);
124
+ if (o.operationId !== e.id || o.kind !== "assign" && o.kind !== "record" && o.kind !== "reroll") throw t("invalid_record");
125
+ let s = Object.freeze(i(o.assignmentIds, 8).map(n)), c = Object.freeze(i(o.completedIds, 8).map(n)), l = r(o.at, 0, 864e13);
126
+ if (new Set(s).size !== s.length || new Set(c).size !== c.length || c.some((e) => !s.includes(e)) || j(e.createdAt) > l) throw t("invalid_record");
127
+ return Object.freeze({
128
+ operationId: n(e.id),
129
+ kind: o.kind,
130
+ assignmentIds: s,
131
+ completedIds: c,
132
+ rewardsQueued: r(o.rewardsQueued, 0, 512),
133
+ at: l
134
+ });
135
+ }, G = async (e, r, i, a, o) => {
136
+ if (await e.updateMany(h.tableName, { admission: 0 }, { where: w("id", r) }) !== 1) throw t("unavailable");
137
+ let s = (await e.query(T(h).where(w("id", r)).limit(1).descriptor))[0];
138
+ if (!s || s.tenantId !== i || s.ownerKey !== a) throw t("invalid_record");
139
+ if (s.state === "committed" || s.state === "replayed") {
140
+ if (s.digest !== o) throw t("conflict");
141
+ if (s.state === "committed") {
142
+ if (s.linkedOperationId !== null) throw t("invalid_record");
143
+ return W(s);
144
+ }
145
+ let r = (await e.query(T(h).where(w("id", n(s.linkedOperationId))).limit(1).descriptor))[0];
146
+ if (!r || r.tenantId !== i || r.ownerKey !== a || r.state !== "committed" || r.digest !== o) throw t("invalid_record");
147
+ return W(r);
148
+ }
149
+ if (s.state !== "pending" || s.digest !== null || s.receipt !== null || s.linkedOperationId !== null) throw t("invalid_record");
150
+ await H(e, h, i, a);
151
+ }, K = (e, n, r) => A(async () => {
152
+ let { tenantId: i, ownerId: a, requestId: o, kind: c, payload: l } = e, u = I(i, a), d = k("qpo_", i, a, o), f = O({
153
+ tenantId: i,
154
+ ownerId: a,
155
+ kind: c,
156
+ payload: l
157
+ }), p = F();
158
+ await z(i, a);
159
+ let m = await s.transactional(async (e) => {
160
+ if (await V(e, i, a), !(await e.query(T(h).where(w("id", d)).limit(1).descriptor))[0]) {
161
+ if ((await H(e, h, i, u)).length >= S.maxWorkRows) throw t("limit_exceeded");
162
+ await e.insert(h.tableName, {
163
+ id: d,
164
+ tenantId: i,
165
+ ownerKey: u,
166
+ state: "pending",
167
+ linkedOperationId: null,
168
+ digest: null,
169
+ receipt: null,
170
+ createdAt: new Date(p),
171
+ admission: 0
172
+ });
173
+ }
174
+ return G(e, d, i, u, f);
175
+ });
176
+ if (m) return m;
177
+ let g = await n();
178
+ return s.transactional(async (e) => {
179
+ await V(e, i, a);
180
+ let t = await G(e, d, i, u, f);
181
+ if (t) return t;
182
+ let n = await r(e, g, p, d);
183
+ return n.operationId === d ? await e.update(h.tableName, d, {
184
+ state: "committed",
185
+ digest: f,
186
+ receipt: n
187
+ }) : await e.update(h.tableName, d, {
188
+ state: "replayed",
189
+ digest: f,
190
+ linkedOperationId: n.operationId
191
+ }), n;
192
+ });
193
+ }), q = async (e, i, a, o, s) => {
194
+ let c = k("qpw_", i, a, o, s), l = (await e.query(T(y).where(w("id", c)).limit(1).descriptor))[0];
195
+ if (!l) throw t("not_found");
196
+ if (l.tenantId !== i || l.ownerId !== a || l.ownerKey !== I(i, a) || l.poolId !== o || l.windowId !== s || l.rulesVersion !== S.strategies.version) throw t("invalid_record");
197
+ let f = u(l.pool), p = j(l.startsAt), m = j(l.expiresAt);
198
+ if (f.id !== o || m <= p || m - p > 316224e5) throw t("invalid_record");
199
+ let h = d(l.timezone), g = r(l.seed, 0, 2147483647), _ = r(l.rerollsUsed, 0, f.rerolls);
200
+ if (f.refresh.timezone !== "player" && f.refresh.timezone !== h) throw t("invalid_record");
201
+ return n(l.sourceRevision), {
202
+ row: l,
203
+ pool: f,
204
+ from: p,
205
+ until: m,
206
+ timezone: h,
207
+ seed: g,
208
+ rerollsUsed: _
209
+ };
210
+ }, J = async (e, n, a, o) => {
211
+ let s = await e.query(T(b).where(C(L(n, a), w("windowKey", o.row.id))).limit(17).descriptor);
212
+ if (s.length > o.pool.slots + o.pool.rerolls) throw t("invalid_record");
213
+ let c = s.map((e) => {
214
+ if (e.tenantId !== n || e.ownerId !== a || e.ownerKey !== I(n, a) || e.windowKey !== o.row.id || e.poolId !== o.pool.id || e.windowId !== o.row.windowId || e.state !== "active" && e.state !== "completed" && e.state !== "rerolled") throw t("invalid_record");
215
+ let s = r(e.slot, 1, o.pool.slots), c = r(e.generation, 0, o.pool.rerolls), u = l(e.definition);
216
+ if (e.id !== k("qpa_", String(o.row.id), String(s), String(c)) || e.questId !== u.id || !o.pool.quests.some((e) => e.id === u.id && O(e) === O(u)) || j(e.expiresAt) !== o.until || j(e.assignedAt) < o.from || j(e.assignedAt) >= o.until) throw t("invalid_record");
217
+ let d = Object.freeze(i(e.progress, 16).map((e, t) => r(e, 0, u.objectives[t]?.target ?? -1))), f = d.every((e, t) => e === u.objectives[t]?.target);
218
+ if (d.length !== u.objectives.length || e.state === "completed" && (!f || j(e.completedAt) < j(e.assignedAt) || j(e.completedAt) >= o.until) || e.state === "active" && (f || e.completedAt !== null) || e.state === "rerolled" && e.completedAt !== null) throw t("invalid_record");
219
+ return {
220
+ row: e,
221
+ quest: u,
222
+ progress: d,
223
+ slot: s,
224
+ generation: c
225
+ };
226
+ }), u = c.filter((e) => e.row.state !== "rerolled");
227
+ if (u.length !== o.pool.slots || new Set(u.map((e) => e.slot)).size !== u.length || new Set(c.map((e) => e.quest.id)).size !== c.length) throw t("invalid_record");
228
+ return c;
229
+ }, Y = async (e, n, r, i, a, o, s, c) => {
230
+ let l = n.pool.quests.find((e) => e.id === a);
231
+ if (!l) throw t("invalid_record");
232
+ let u = k("qpa_", String(n.row.id), String(o), String(s));
233
+ return await e.insert(b.tableName, {
234
+ id: u,
235
+ tenantId: r,
236
+ ownerId: i,
237
+ ownerKey: I(r, i),
238
+ windowKey: n.row.id,
239
+ poolId: n.pool.id,
240
+ windowId: n.row.windowId,
241
+ slot: o,
242
+ generation: s,
243
+ questId: a,
244
+ definition: l,
245
+ progress: l.objectives.map(() => 0),
246
+ state: "active",
247
+ assignedAt: new Date(c),
248
+ expiresAt: new Date(n.until),
249
+ completedAt: null
250
+ }), u;
251
+ }, X = (e, o) => P(() => {
252
+ let s = structuredClone(e), c = a(o, [
253
+ "tenantId",
254
+ "ownerId",
255
+ "poolId",
256
+ "requestId",
257
+ "windowId",
258
+ "from",
259
+ "until",
260
+ "timezone",
261
+ "seed",
262
+ "expectedPoolDigest"
263
+ ]);
264
+ if (!M(s)) throw t("forbidden");
265
+ let l = n(c.tenantId), f = n(c.ownerId), h = n(c.poolId), g = n(c.windowId), _ = r(c.from, 0, 864e13), v = r(c.until, _ + 1, 864e13), C = d(c.timezone), E = r(c.seed, 0, 2147483647);
266
+ if (v - _ > 316224e5) throw t("invalid_input");
267
+ let D = c.expectedPoolDigest;
268
+ if (D !== void 0 && (typeof D != "string" || !/^[a-f0-9]{64}$/.test(D))) throw t("invalid_input");
269
+ let A = Object.freeze({
270
+ poolId: h,
271
+ windowId: g,
272
+ from: _,
273
+ until: v,
274
+ timezone: C,
275
+ seed: E,
276
+ ...D === void 0 ? {} : { expectedPoolDigest: D }
277
+ });
278
+ return K({
279
+ tenantId: l,
280
+ ownerId: f,
281
+ requestId: n(c.requestId),
282
+ kind: "assign",
283
+ payload: A
284
+ }, async () => {
285
+ let e = a(await x.runPromise(S.source.read({
286
+ tenantId: l,
287
+ channel: S.channel,
288
+ locale: S.locale
289
+ })), ["revision", "pools"]), r = n(e.revision), o = i(e.pools, 64).map((e) => u(e));
290
+ if (new Set(o.map((e) => e.id)).size !== o.length) throw t("invalid_record");
291
+ let s = o.find((e) => e.id === h);
292
+ if (!s) throw t("not_found");
293
+ if (D !== void 0 && O(s) !== D) throw t("conflict");
294
+ if (s.refresh.timezone !== "player" && s.refresh.timezone !== C) throw t("invalid_input");
295
+ let c = k("qpd_", l, h, String(s.version));
296
+ return await R(m.tableName, {
297
+ id: c,
298
+ tenantId: l,
299
+ poolId: h,
300
+ version: s.version,
301
+ config: s,
302
+ rulesVersion: S.strategies.version,
303
+ admission: 0
304
+ }), {
305
+ pool: s,
306
+ revision: r,
307
+ definitionId: c
308
+ };
309
+ }, async (e, r, i, a) => {
310
+ if (i < _ || i >= v) throw t("conflict");
311
+ if (await e.updateMany(m.tableName, { admission: 0 }, { where: w("id", r.definitionId) }) !== 1) throw t("unavailable");
312
+ let o = (await e.query(T(m).where(w("id", r.definitionId)).limit(1).descriptor))[0];
313
+ if (!o || o.tenantId !== l || o.poolId !== h || o.version !== r.pool.version || o.rulesVersion !== S.strategies.version || O(o.config) !== O(r.pool)) throw t("conflict");
314
+ let s = k("qpw_", l, f, h, g), c = (await e.query(T(y).where(w("id", s)).limit(1).descriptor))[0], u;
315
+ if (c) {
316
+ let i = await q(e, l, f, h, g);
317
+ if (i.from !== _ || i.until !== v || i.timezone !== C || i.seed !== E || O(i.pool) !== O(r.pool)) throw t("conflict");
318
+ u = (await J(e, l, f, i)).filter((e) => e.row.state !== "rerolled").sort((e, t) => e.slot - t.slot).map((e) => n(e.row.id));
319
+ } else {
320
+ let n = await U(e, y, l, f), a = await U(e, b, l, f);
321
+ if (n.length >= S.maxWorkRows || a.length + r.pool.slots > S.maxWorkRows || n.some((e) => e.poolId === h && j(e.startsAt) < v && j(e.expiresAt) > _)) throw t("conflict");
322
+ await e.insert(y.tableName, {
323
+ id: s,
324
+ tenantId: l,
325
+ ownerId: f,
326
+ ownerKey: I(l, f),
327
+ poolId: h,
328
+ windowId: g,
329
+ startsAt: new Date(_),
330
+ expiresAt: new Date(v),
331
+ timezone: C,
332
+ seed: E,
333
+ rerollsUsed: 0,
334
+ pool: r.pool,
335
+ sourceRevision: r.revision,
336
+ rulesVersion: S.strategies.version
337
+ });
338
+ let o = await q(e, l, f, h, g), c = p({
339
+ pool: r.pool,
340
+ count: r.pool.slots,
341
+ seed: E,
342
+ excluded: []
343
+ }, S.strategies.questSelector), d = [];
344
+ for (let [t, n] of c.entries()) d.push(await Y(e, o, l, f, n, t + 1, 0, i));
345
+ u = d;
346
+ }
347
+ return Object.freeze({
348
+ operationId: a,
349
+ kind: "assign",
350
+ assignmentIds: Object.freeze([...u]),
351
+ completedIds: Object.freeze([]),
352
+ rewardsQueued: 0,
353
+ at: i
354
+ });
355
+ });
356
+ }), Z = (e, r) => P(() => {
357
+ let i = structuredClone(e), s = a(r, [
358
+ "tenantId",
359
+ "ownerId",
360
+ "poolId",
361
+ "windowId",
362
+ "requestId",
363
+ "source",
364
+ "origin",
365
+ "signal"
366
+ ]);
367
+ if (!M(i)) throw t("forbidden");
368
+ let c = n(s.tenantId), l = n(s.ownerId), u = n(s.poolId), d = n(s.windowId), p = n(s.source), m = n(s.origin), v = o(s.signal);
369
+ if (!(v.kind === "event" ? S.eventSources : S.statSources).includes(p)) throw t("forbidden");
370
+ let y = Object.freeze({
371
+ poolId: u,
372
+ windowId: d,
373
+ source: p,
374
+ origin: m,
375
+ signal: v
376
+ }), x = O({
377
+ tenantId: c,
378
+ ownerId: l,
379
+ kind: "record",
380
+ payload: y
381
+ }), C = I(c, l);
382
+ return K({
383
+ tenantId: c,
384
+ ownerId: l,
385
+ requestId: n(s.requestId),
386
+ kind: "record",
387
+ payload: y
388
+ }, async () => void 0, async (e, r, i, a) => {
389
+ let o = k("qpn_", c, l, u, d, p, m), s = (await e.query(T(g).where(w("id", o)).limit(1).descriptor))[0];
390
+ if (s) {
391
+ if (s.tenantId !== c || s.ownerKey !== C || s.source !== p || s.origin !== m || s.digest !== x) throw t("conflict");
392
+ let r = (await e.query(T(h).where(w("id", n(s.operationId))).limit(1).descriptor))[0];
393
+ if (!r || r.tenantId !== c || r.ownerKey !== C || r.state !== "committed" || r.digest !== x) throw t("invalid_record");
394
+ return W(r);
395
+ }
396
+ let y = await q(e, c, l, u, d);
397
+ if (i < y.from || i >= y.until) throw t("conflict");
398
+ let E = (await J(e, c, l, y)).filter((e) => e.row.state !== "rerolled").sort((e, t) => e.slot - t.slot), D = [], O = await H(e, _, c, C);
399
+ if ((await H(e, g, c, C)).length >= S.maxWorkRows) throw t("limit_exceeded");
400
+ let A = 0;
401
+ for (let r of E) if (r.row.state === "active") {
402
+ let o = f({
403
+ quest: r.quest,
404
+ progress: r.progress,
405
+ signal: v
406
+ }, S.strategies.questProgressResolver), s = o.every((e, t) => e === r.quest.objectives[t]?.target);
407
+ if (await e.update(b.tableName, String(r.row.id), {
408
+ progress: o,
409
+ state: s ? "completed" : "active",
410
+ completedAt: s ? new Date(i) : null
411
+ }), s) {
412
+ D.push(n(r.row.id));
413
+ for (let [n, o] of r.quest.reward.entries()) {
414
+ if (O.length + A >= S.maxWorkRows) throw t("limit_exceeded");
415
+ await e.insert(_.tableName, {
416
+ id: k("qpr_", String(r.row.id), String(n)),
417
+ tenantId: c,
418
+ ownerId: l,
419
+ ownerKey: C,
420
+ operationId: a,
421
+ assignmentId: r.row.id,
422
+ reward: o,
423
+ state: "pending",
424
+ createdAt: new Date(i),
425
+ deliveredAt: null,
426
+ deliveryIntent: null,
427
+ deliveryDigest: null,
428
+ deliveryReceipt: null
429
+ }), A++;
430
+ }
431
+ }
432
+ }
433
+ return await e.insert(g.tableName, {
434
+ id: o,
435
+ tenantId: c,
436
+ ownerKey: C,
437
+ operationId: a,
438
+ source: p,
439
+ origin: m,
440
+ digest: x
441
+ }), Object.freeze({
442
+ operationId: a,
443
+ kind: "record",
444
+ assignmentIds: Object.freeze(E.map((e) => n(e.row.id))),
445
+ completedIds: Object.freeze(D),
446
+ rewardsQueued: A,
447
+ at: i
448
+ });
449
+ });
450
+ }), Q = (e, r) => P(() => {
451
+ let i = structuredClone(e), { tenantId: o, ownerId: s } = N(i), c = a(r, [
452
+ "poolId",
453
+ "windowId",
454
+ "assignmentId",
455
+ "requestId"
456
+ ]), l = n(c.poolId), u = n(c.windowId), d = n(c.assignmentId), f = Object.freeze({
457
+ poolId: l,
458
+ windowId: u,
459
+ assignmentId: d
460
+ });
461
+ return K({
462
+ tenantId: o,
463
+ ownerId: s,
464
+ requestId: n(c.requestId),
465
+ kind: "reroll",
466
+ payload: f
467
+ }, async () => void 0, async (e, n, r, i) => {
468
+ let a = await q(e, o, s, l, u);
469
+ if (r < a.from || r >= a.until || a.rerollsUsed >= a.pool.rerolls) throw t("conflict");
470
+ let c = await J(e, o, s, a), f = c.find((e) => e.row.id === d);
471
+ if (!f || f.row.state !== "active") throw t("forbidden");
472
+ if ((await U(e, b, o, s)).length >= S.maxWorkRows) throw t("limit_exceeded");
473
+ let m = a.rerollsUsed + 1, h = p({
474
+ pool: a.pool,
475
+ count: 1,
476
+ seed: (a.seed ^ m) >>> 0,
477
+ excluded: c.map((e) => e.quest.id)
478
+ }, S.strategies.questSelector)[0];
479
+ await e.update(b.tableName, d, { state: "rerolled" }), await e.update(y.tableName, String(a.row.id), { rerollsUsed: m });
480
+ let g = await Y(e, a, o, s, h, f.slot, m, r);
481
+ return Object.freeze({
482
+ operationId: i,
483
+ kind: "reroll",
484
+ assignmentIds: Object.freeze([g]),
485
+ completedIds: Object.freeze([]),
486
+ rewardsQueued: 0,
487
+ at: r
488
+ });
489
+ });
490
+ }), ee = (e) => P(() => {
491
+ let r = structuredClone(e), { tenantId: i, ownerId: a } = N(r);
492
+ return A(async () => s.transactional(async (e) => {
493
+ let r = (await e.query(T(v).where(w("id", I(i, a))).forUpdate().limit(1).descriptor))[0];
494
+ B(r, i, a);
495
+ let o = F(), s = await U(e, y, i, a), c = [];
496
+ if (!r && s.length > 0) throw t("invalid_record");
497
+ for (let t of s) {
498
+ let r = await q(e, i, a, n(t.poolId), n(t.windowId));
499
+ if (!(o < r.from || o >= r.until)) for (let o of await J(e, i, a, r)) o.row.state !== "rerolled" && c.push(Object.freeze({
500
+ id: n(o.row.id),
501
+ poolId: r.pool.id,
502
+ windowId: n(t.windowId),
503
+ quest: o.quest,
504
+ progress: o.progress,
505
+ state: o.row.state,
506
+ assignedAt: j(o.row.assignedAt),
507
+ expiresAt: r.until,
508
+ rerollsUsed: r.rerollsUsed,
509
+ rerollsAllowed: r.pool.rerolls
510
+ }));
511
+ }
512
+ return Object.freeze(c.sort((e, t) => e.poolId < t.poolId ? -1 : e.poolId > t.poolId ? 1 : e.id < t.id ? -1 : +(e.id > t.id)));
513
+ }));
514
+ }), $ = (e, r) => P(() => {
515
+ let i = a(e, [
516
+ "subjectId",
517
+ "tenantId",
518
+ "kind"
519
+ ]), o = n(i.tenantId), c = n(i.subjectId), l = I(o, c);
520
+ if (i.kind !== "user" && i.kind !== "guest") throw t("forbidden");
521
+ return A(async () => (await z(o, c), s.transactional(async (e) => {
522
+ await V(e, o, c, !0);
523
+ let t = await U(e, y, o, c), n = await U(e, b, o, c);
524
+ for (let t of [
525
+ h,
526
+ g,
527
+ _
528
+ ]) await H(e, t, o, l);
529
+ let i = await H(e, _, o, l);
530
+ if (!r) return {
531
+ ...i.length ? { rewards: i.map((e) => ({
532
+ reward: e.reward,
533
+ state: e.state,
534
+ deliveryIntent: e.deliveryIntent ?? null,
535
+ deliveryReceipt: e.deliveryReceipt ?? null
536
+ })) } : {},
537
+ windows: t.map((e) => ({
538
+ poolId: e.poolId,
539
+ windowId: e.windowId,
540
+ from: j(e.startsAt),
541
+ until: j(e.expiresAt),
542
+ timezone: e.timezone,
543
+ rerollsUsed: e.rerollsUsed
544
+ })),
545
+ quests: n.map((e) => ({
546
+ id: e.id,
547
+ poolId: e.poolId,
548
+ windowId: e.windowId,
549
+ questId: e.questId,
550
+ progress: e.progress,
551
+ state: e.state,
552
+ assignedAt: j(e.assignedAt),
553
+ expiresAt: j(e.expiresAt)
554
+ }))
555
+ };
556
+ await e.deleteMany(b.tableName, { where: L(o, c) }), await e.deleteMany(y.tableName, { where: L(o, c) }), await e.updateMany(h.tableName, {
557
+ ownerKey: null,
558
+ state: "erased",
559
+ digest: null,
560
+ receipt: null,
561
+ linkedOperationId: null
562
+ }, { where: C(w("tenantId", o), w("ownerKey", l)) }), await e.updateMany(g.tableName, {
563
+ ownerKey: null,
564
+ source: null,
565
+ origin: null,
566
+ digest: null
567
+ }, { where: C(w("tenantId", o), w("ownerKey", l)) }), await e.updateMany(_.tableName, {
568
+ ownerId: null,
569
+ ownerKey: null,
570
+ state: "erased",
571
+ reward: null,
572
+ deliveryIntent: null,
573
+ deliveryDigest: null,
574
+ deliveryReceipt: null,
575
+ deliveredAt: null
576
+ }, { where: C(w("tenantId", o), w("ownerKey", l)) }), await e.update(v.tableName, l, {
577
+ ownerId: null,
578
+ erased: !0
579
+ });
580
+ })));
581
+ });
582
+ return Object.freeze({
583
+ assign: X,
584
+ record: Z,
585
+ reroll: Q,
586
+ mine: ee,
587
+ exportSubject: (e) => $(e, !1),
588
+ eraseSubject: (e) => x.asVoid($(e, !0))
589
+ });
590
+ };
591
+ export { M as dataStoreQuests, O as questHash, E as resolveQuestOptions };
@@ -0,0 +1,116 @@
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/questTables.entity.ts
3
+ var s = () => a().maxLength(120).externalId().sensitive("pii"), c = () => a().maxLength(120).externalId().sensitive("pii"), l = i("_voltro_prog_quest_definitions", {
4
+ id: t({ prefix: "qpd" }),
5
+ tenantId: s(),
6
+ poolId: a().maxLength(120).safe(),
7
+ version: n().safe(),
8
+ config: r().safe(),
9
+ rulesVersion: a().maxLength(120).safe(),
10
+ admission: n().serverOnly()
11
+ }).index([
12
+ "tenantId",
13
+ "poolId",
14
+ "version"
15
+ ]), u = i("_voltro_prog_quest_subjects", {
16
+ id: t({ prefix: "qps" }),
17
+ tenantId: s(),
18
+ ownerId: c().nullable(),
19
+ erased: e().serverOnly(),
20
+ admission: n().serverOnly()
21
+ }).index(["tenantId", "ownerId"]), d = i("_voltro_prog_quest_windows", {
22
+ id: t({ prefix: "qpw" }),
23
+ tenantId: s(),
24
+ ownerId: c(),
25
+ ownerKey: a().maxLength(64).serverOnly(),
26
+ poolId: a().maxLength(120).safe(),
27
+ windowId: a().maxLength(120).safe(),
28
+ startsAt: o().safe(),
29
+ expiresAt: o().safe(),
30
+ timezone: a().maxLength(64).safe(),
31
+ seed: n().serverOnly(),
32
+ rerollsUsed: n().safe(),
33
+ pool: r().safe(),
34
+ sourceRevision: a().maxLength(120).safe(),
35
+ rulesVersion: a().maxLength(120).safe()
36
+ }).index(["tenantId", "ownerId"]), f = i("_voltro_prog_quests", {
37
+ id: t({ prefix: "qpa" }),
38
+ tenantId: s(),
39
+ ownerId: c(),
40
+ ownerKey: a().maxLength(64).serverOnly(),
41
+ windowKey: a().maxLength(64).serverOnly(),
42
+ poolId: a().maxLength(120).safe(),
43
+ windowId: a().maxLength(120).safe(),
44
+ slot: n().safe(),
45
+ generation: n().safe(),
46
+ questId: a().maxLength(120).safe(),
47
+ definition: r().safe(),
48
+ progress: r().safe(),
49
+ state: a().maxLength(16).oneOf([
50
+ "active",
51
+ "completed",
52
+ "rerolled"
53
+ ]).safe(),
54
+ assignedAt: o().safe(),
55
+ expiresAt: o().safe(),
56
+ completedAt: o().nullable().safe()
57
+ }).index([
58
+ "tenantId",
59
+ "ownerId",
60
+ "poolId",
61
+ "windowId"
62
+ ]), p = i("_voltro_prog_quest_operations", {
63
+ id: t({ prefix: "qpo" }),
64
+ tenantId: s(),
65
+ ownerKey: a().maxLength(64).nullable().serverOnly(),
66
+ state: a().maxLength(16).oneOf([
67
+ "pending",
68
+ "committed",
69
+ "replayed",
70
+ "erased"
71
+ ]).serverOnly(),
72
+ linkedOperationId: a().maxLength(64).nullable().serverOnly(),
73
+ digest: a().maxLength(64).nullable().serverOnly(),
74
+ receipt: r().nullable().serverOnly(),
75
+ createdAt: o().safe(),
76
+ admission: n().serverOnly()
77
+ }).index(["tenantId", "ownerKey"]), m = i("_voltro_prog_quest_origins", {
78
+ id: t({ prefix: "qpn" }),
79
+ tenantId: s(),
80
+ ownerKey: a().maxLength(64).nullable().serverOnly(),
81
+ operationId: a().maxLength(64).safe(),
82
+ source: a().maxLength(120).nullable().safe(),
83
+ origin: a().maxLength(120).nullable().sensitive("pii"),
84
+ digest: a().maxLength(64).nullable().serverOnly()
85
+ }).index(["tenantId", "ownerKey"]), h = i("_voltro_prog_quest_rewards", {
86
+ id: t({ prefix: "qpr" }),
87
+ tenantId: s(),
88
+ ownerId: c().nullable(),
89
+ ownerKey: a().maxLength(64).nullable().serverOnly(),
90
+ operationId: a().maxLength(64).safe(),
91
+ assignmentId: a().maxLength(64).safe(),
92
+ reward: r().nullable().serverOnly(),
93
+ state: a().maxLength(16).oneOf([
94
+ "pending",
95
+ "delivered",
96
+ "erased"
97
+ ]).serverOnly(),
98
+ createdAt: o().safe(),
99
+ deliveredAt: o().nullable().safe(),
100
+ deliveryIntent: r().nullable().serverOnly(),
101
+ deliveryDigest: a().maxLength(64).nullable().serverOnly(),
102
+ deliveryReceipt: r().nullable().serverOnly()
103
+ }).index([
104
+ "tenantId",
105
+ "ownerKey",
106
+ "state"
107
+ ]), g = [
108
+ l,
109
+ u,
110
+ d,
111
+ f,
112
+ p,
113
+ m,
114
+ h
115
+ ];
116
+ export { l as questDefinitionsTable, p as questOperationsTable, m as questOriginsTable, h as questRewardsTable, u as questSubjectsTable, g as questTables, d as questWindowsTable, f as questsTable };