@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,495 @@
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 { captureTutorialEvidence as s, captureTutorialState as c, defaultTutorialStepPolicy as l, defineTutorial as u, initialTutorialState as d, resolveTutorialSkip as f, resolveTutorialStep as p } from "./tutorial.js";
4
+ import { admitRewardDeliveryRecord as m } from "./rewardDeliveryRecord.js";
5
+ import { questHash as h } from "./questStore.js";
6
+ import { tutorialDefinitionsTable as g, tutorialLedgerTable as _, tutorialOperationsTable as v, tutorialRewardsTable as y, tutorialStatesTable as b, tutorialSubjectsTable as x } from "./tutorialTables.entity.js";
7
+ import { Effect as S, Result as C } from "effect";
8
+ import { and as w, eq as T, queryFor as E } from "@voltro/database";
9
+ // src/tutorialStore.ts
10
+ var D = (e) => {
11
+ let n = o(e, [
12
+ "definitions",
13
+ "eventSources",
14
+ "verifier",
15
+ "allowedServices",
16
+ "maxHistoryRows",
17
+ "maxPendingRewards",
18
+ "now",
19
+ "strategies"
20
+ ]), s = o(n.strategies ?? {}, ["stepPolicy", "version"]), c = Object.freeze(a(n.definitions, 64).map((e) => u(e))), d = Object.freeze(a(n.eventSources, 64).map(r)), f = Object.freeze(a(n.allowedServices ?? [], 256).map(r)), p = n.now ?? Date.now, m = s.stepPolicy ?? l, h = n.verifier === void 0 ? void 0 : o(n.verifier);
21
+ if (!c.length || new Set(c.map((e) => e.id)).size !== c.length || new Set(d).size !== d.length || new Set(f).size !== f.length || h && typeof h.verify != "function" || typeof p != "function" || typeof m != "function" || m !== l && s.version === void 0) throw t("invalid_input");
22
+ return Object.freeze({
23
+ definitions: c,
24
+ eventSources: d,
25
+ allowedServices: f,
26
+ now: p,
27
+ verifier: h ? Object.freeze({ verify: h.verify }) : void 0,
28
+ maxHistoryRows: i(n.maxHistoryRows ?? 1e4, 1, 1e5),
29
+ maxPendingRewards: i(n.maxPendingRewards ?? 1e4, 1, 1e5),
30
+ strategies: Object.freeze({
31
+ stepPolicy: m,
32
+ version: r(s.version ?? "default.v1")
33
+ })
34
+ });
35
+ }, O = (e, ...t) => e + h(t).slice(0, 56), k = (e) => i(e instanceof Date ? e.getTime() : typeof e == "string" ? Date.parse(e) : NaN, 0, 864e13), A = (n) => S.tryPromise({
36
+ try: n,
37
+ catch: (n) => n instanceof e ? n : t("unavailable")
38
+ }), j = (a, l) => {
39
+ let u = D(l), j = new Map(u.definitions.map((e) => [e.id, e])), M = new Set(u.allowedServices), N = new Set(u.eventSources), P = (e) => {
40
+ let n = j.get(e);
41
+ if (!n) throw t("invalid_input");
42
+ return n;
43
+ }, F = (e) => {
44
+ if (e.type !== "user" || !e.tenantId) throw t("forbidden");
45
+ return {
46
+ tenantId: r(e.tenantId),
47
+ ownerId: r(e.id)
48
+ };
49
+ }, I = (e) => {
50
+ if (e.type !== "system" && !(e.type === "service" && M.has(e.service))) throw t("forbidden");
51
+ }, L = (e, t) => O("pts_", e, t), R = (e, t) => w(T("tenantId", e), T("ownerKey", L(e, t))), z = (e, t, n) => O("ptv_", e, t, n.id, String(n.version)), B = (e) => {
52
+ let n = o(e, [
53
+ "tutorialId",
54
+ "version",
55
+ "kind",
56
+ "step",
57
+ "mode",
58
+ "source",
59
+ "originId"
60
+ ]), i = P(r(n.tutorialId));
61
+ if (n.version !== i.version || n.kind !== "step" && n.kind !== "skip" || n.mode !== "player" && n.mode !== "event" || n.kind === "skip" && (n.step !== null || n.mode !== "player") || n.kind === "step" && !i.steps.includes(r(n.step)) || n.mode === "player" && (n.source !== null || n.originId !== null)) throw t("invalid_record");
62
+ let a = n.mode === "event" ? r(n.source) : null, s = n.mode === "event" ? r(n.originId) : null;
63
+ if (a !== null && !N.has(a)) throw t("forbidden");
64
+ return Object.freeze({
65
+ tutorialId: i.id,
66
+ version: i.version,
67
+ kind: n.kind,
68
+ step: n.kind === "step" ? r(n.step) : null,
69
+ mode: n.mode,
70
+ source: a,
71
+ originId: s
72
+ });
73
+ }, V = (e, t, n) => O("ptk_", e, t, n.tutorialId, String(n.version), n.kind, n.step ?? ""), H = (e, t, n) => h({
74
+ tenantId: e,
75
+ ownerId: t,
76
+ intent: n
77
+ }), U = async (e, n) => {
78
+ if ((await a.insertIgnoreWithOutcome(e.tableName, n, { conflictColumns: ["id"] })).outcome !== "inserted" && !(await a.query(E(e).where(T("id", n.id)).limit(1).descriptor))[0]) throw t("unavailable");
79
+ }, W = (e, t) => U(x, {
80
+ id: L(e, t),
81
+ tenantId: e,
82
+ ownerId: t,
83
+ erased: !1,
84
+ sequence: 0,
85
+ admission: 0
86
+ }), G = (e, n, r) => {
87
+ if (e) {
88
+ if (e.id !== L(n, r) || e.tenantId !== n || typeof e.erased != "boolean") throw t("invalid_record");
89
+ if (e.erased) throw e.ownerId === null ? t("erased") : t("invalid_record");
90
+ if (e.ownerId !== r) throw t("invalid_record");
91
+ i(e.sequence, 0, 2147483647);
92
+ }
93
+ }, K = async (e, n, r) => {
94
+ if (await e.updateMany(x.tableName, { admission: 0 }, { where: T("id", L(n, r)) }) !== 1) throw t("invalid_record");
95
+ let i = (await e.query(E(x).where(T("id", L(n, r))).limit(1).descriptor))[0];
96
+ if (G(i, n, r), !i) throw t("invalid_record");
97
+ return i;
98
+ }, q = async (e, n, r, i) => {
99
+ let a = n === y ? u.maxPendingRewards : u.maxHistoryRows, o = await e.query(E(n).where(R(r, i)).limit(a + 1).descriptor);
100
+ if (o.length > a) throw t("limit_exceeded");
101
+ if (o.some((e) => e.tenantId !== r || e.ownerKey !== L(r, i))) throw t("invalid_record");
102
+ return o;
103
+ }, J = async (e, n, r, i) => {
104
+ let a = O("ptd_", n, r.id, String(r.version));
105
+ if (i && await e.updateMany(g.tableName, { admission: 0 }, { where: T("id", a) }) !== 1) throw t("invalid_record");
106
+ let o = (await e.query(E(g).where(T("id", a)).limit(1).descriptor))[0];
107
+ if (!o || o.tenantId !== n || o.tutorialId !== r.id || o.version !== r.version || o.rulesVersion !== u.strategies.version || h(o.config) !== h(r)) throw t("conflict");
108
+ }, Y = (e, n, r, i) => {
109
+ if (e.id !== z(n, r, i) || e.tenantId !== n || e.ownerId !== r || e.ownerKey !== L(n, r) || e.tutorialId !== i.id || e.version !== i.version) throw t("invalid_record");
110
+ let a = c(e.state, i);
111
+ if (a.updatedAt !== k(e.updatedAt)) throw t("invalid_record");
112
+ return a;
113
+ }, X = (e) => {
114
+ let n = o(e.receipt, [
115
+ "operationId",
116
+ "kind",
117
+ "step",
118
+ "after",
119
+ "rewardsQueued",
120
+ "at"
121
+ ]), i = B(e.intent), a = P(i.tutorialId), s = c(n.after, a);
122
+ if (e.state !== "committed" || e.linkedOperationId !== null || n.operationId !== e.id || n.kind !== i.kind || n.step !== i.step || n.at !== k(e.at) || n.at !== s.updatedAt || n.kind === "step" && s.completedSteps.at(-1) !== n.step || n.kind === "skip" && !s.skipped) throw t("invalid_record");
123
+ let l = s.completed && (!s.skipped || a.rewardOnSkip) ? a.reward.length : 0;
124
+ if (n.rewardsQueued !== l) throw t("invalid_record");
125
+ return Object.freeze({
126
+ operationId: r(e.id),
127
+ kind: i.kind,
128
+ step: i.step,
129
+ after: s,
130
+ rewardsQueued: l,
131
+ at: s.updatedAt
132
+ });
133
+ }, ee = async (e, n, r, i, a) => {
134
+ let o = (await e.query(E(v).where(T("id", n)).limit(1).descriptor))[0];
135
+ if (!o) return;
136
+ if (o.state === "erased") throw t("erased");
137
+ if (o.tenantId !== i || o.ownerKey !== L(i, a) || o.digest !== H(i, a, r) || h(B(o.intent)) !== h(r)) throw t("conflict");
138
+ if (await J(e, i, P(r.tutorialId), !1), o.state === "committed") return X(o);
139
+ if (o.state !== "replayed" || o.receipt !== null || typeof o.linkedOperationId != "string") throw t("invalid_record");
140
+ let s = (await e.query(E(v).where(T("id", o.linkedOperationId)).limit(1).descriptor))[0];
141
+ if (!s || s.tenantId !== i || s.ownerKey !== L(i, a) || V(i, a, B(s.intent)) !== V(i, a, r)) throw t("invalid_record");
142
+ return X(s);
143
+ }, Z = (c, l, m, w) => {
144
+ try {
145
+ let e = n(c), D = o(l, w === "skip" ? ["tutorialId", "requestId"] : m === "player" ? [
146
+ "tutorialId",
147
+ "step",
148
+ "requestId"
149
+ ] : [
150
+ "tenantId",
151
+ "ownerId",
152
+ "tutorialId",
153
+ "step",
154
+ "requestId",
155
+ "source",
156
+ "originId"
157
+ ]), { tenantId: k, ownerId: j } = m === "player" ? F(e) : (I(e), {
158
+ tenantId: r(D.tenantId),
159
+ ownerId: r(D.ownerId)
160
+ }), M = P(r(D.tutorialId)), R = r(D.requestId), G = B({
161
+ tutorialId: M.id,
162
+ version: M.version,
163
+ kind: w,
164
+ step: w === "skip" ? null : D.step,
165
+ mode: m,
166
+ source: m === "player" ? null : D.source,
167
+ originId: m === "player" ? null : D.originId
168
+ }), Z = O("pto_", k, j, R);
169
+ return A(async () => {
170
+ await W(k, j);
171
+ let c = async (e) => {
172
+ await K(e, k, j);
173
+ let n = await ee(e, Z, G, k, j);
174
+ if (n) return n;
175
+ let i = (await q(e, _, k, j)).find((e) => e.claimKey === V(k, j, G));
176
+ if (!i) return;
177
+ let a = (await e.query(E(v).where(T("id", r(i.operationId))).limit(1).descriptor))[0];
178
+ if (!a || a.tenantId !== k || a.ownerKey !== L(k, j) || V(k, j, B(a.intent)) !== V(k, j, G)) throw t("invalid_record");
179
+ await J(e, k, M, !1);
180
+ let o = X(a);
181
+ if ((await q(e, v, k, j)).length >= u.maxHistoryRows) throw t("limit_exceeded");
182
+ return await e.insert(v.tableName, {
183
+ id: Z,
184
+ tenantId: k,
185
+ ownerKey: L(k, j),
186
+ state: "replayed",
187
+ linkedOperationId: o.operationId,
188
+ intent: G,
189
+ digest: H(k, j, G),
190
+ receipt: null,
191
+ at: new Date(o.at)
192
+ }), o;
193
+ }, l = await a.transactional(c);
194
+ if (l) return l;
195
+ let D = null;
196
+ if (w === "step" && m === "player") {
197
+ if (!u.verifier) throw t("unavailable");
198
+ let r = await S.runPromise(S.result(u.verifier.verify(e, {
199
+ tenantId: k,
200
+ ownerId: j,
201
+ tutorialId: M.id,
202
+ version: M.version,
203
+ step: G.step,
204
+ requestId: R
205
+ })));
206
+ if (C.isFailure(r)) throw r.failure;
207
+ D = n(r.success);
208
+ }
209
+ let A = i(u.now(), 0, 864e13);
210
+ if (w === "step" && (D = s(m === "event" ? {
211
+ source: G.source,
212
+ originId: G.originId,
213
+ version: "trusted.event.v1",
214
+ from: A,
215
+ until: A + 1
216
+ } : D, A)), D && !N.has(D.source)) throw t("forbidden");
217
+ return await U(g, {
218
+ id: O("ptd_", k, M.id, String(M.version)),
219
+ tenantId: k,
220
+ tutorialId: M.id,
221
+ version: M.version,
222
+ config: M,
223
+ rulesVersion: u.strategies.version,
224
+ admission: 0
225
+ }), a.transactional(async (e) => {
226
+ let n = await c(e);
227
+ if (n) return n;
228
+ let r = await K(e, k, j);
229
+ await J(e, k, M, !0);
230
+ let a = await q(e, _, k, j), s = await q(e, v, k, j), l = await q(e, y, k, j);
231
+ if (a.length >= u.maxHistoryRows || s.length >= u.maxHistoryRows) throw t("limit_exceeded");
232
+ if (D && a.some((e) => {
233
+ let t = o(e.intent);
234
+ if (t.tutorialId !== M.id || t.kind !== "step") return !1;
235
+ let n = o(t.evidence);
236
+ return n.source === D.source && n.originId === D.originId;
237
+ })) throw t("conflict");
238
+ let m = z(k, j, M), g = (await e.query(E(b).where(T("id", m)).limit(1).descriptor))[0], S = g ? Y(g, k, j, M) : d(M), C = a.filter((e) => e.tutorialId === M.id).sort((e, t) => Number(e.sequence) - Number(t.sequence)).at(-1);
239
+ if (C && (!g || h(C.after) !== h(S)) || !C && g) throw t("invalid_record");
240
+ let N = w === "skip" ? f(M, S, A) : p({
241
+ tutorial: M,
242
+ before: S,
243
+ step: G.step,
244
+ evidence: D,
245
+ at: A
246
+ }, u.strategies.stepPolicy), P = N.completed && (!N.skipped || M.rewardOnSkip) ? M.reward.length : 0;
247
+ if (l.length + P > u.maxPendingRewards) throw t("limit_exceeded");
248
+ let F = i(r.sequence, 0, 2147483646) + 1, I = Object.freeze({
249
+ operationId: Z,
250
+ kind: w,
251
+ step: G.step,
252
+ after: N,
253
+ rewardsQueued: P,
254
+ at: A
255
+ });
256
+ if (g ? await e.update(b.tableName, m, {
257
+ state: N,
258
+ updatedAt: new Date(A)
259
+ }) : await e.insert(b.tableName, {
260
+ id: m,
261
+ tenantId: k,
262
+ ownerId: j,
263
+ ownerKey: L(k, j),
264
+ tutorialId: M.id,
265
+ version: M.version,
266
+ state: N,
267
+ updatedAt: new Date(A)
268
+ }), await e.insert(_.tableName, {
269
+ id: O("ptl_", Z),
270
+ tenantId: k,
271
+ ownerId: j,
272
+ ownerKey: L(k, j),
273
+ tutorialId: M.id,
274
+ version: M.version,
275
+ sequence: F,
276
+ operationId: Z,
277
+ claimKey: V(k, j, G),
278
+ intent: {
279
+ ...G,
280
+ evidence: D
281
+ },
282
+ before: S,
283
+ after: N,
284
+ at: new Date(A)
285
+ }), await e.insert(v.tableName, {
286
+ id: Z,
287
+ tenantId: k,
288
+ ownerKey: L(k, j),
289
+ state: "committed",
290
+ linkedOperationId: null,
291
+ intent: G,
292
+ digest: H(k, j, G),
293
+ receipt: I,
294
+ at: new Date(A)
295
+ }), P) for (let [t, n] of M.reward.entries()) await e.insert(y.tableName, {
296
+ id: O("ptr_", k, j, M.id, String(M.version), String(t)),
297
+ tenantId: k,
298
+ ownerId: j,
299
+ ownerKey: L(k, j),
300
+ tutorialId: M.id,
301
+ version: M.version,
302
+ operationId: Z,
303
+ reward: n,
304
+ state: "pending",
305
+ createdAt: new Date(A),
306
+ deliveredAt: null,
307
+ deliveryIntent: null,
308
+ deliveryDigest: null,
309
+ deliveryReceipt: null
310
+ });
311
+ return await e.update(x.tableName, L(k, j), { sequence: F }), I;
312
+ });
313
+ });
314
+ } catch (n) {
315
+ return S.fail(n instanceof e ? n : t("invalid_input"));
316
+ }
317
+ }, te = (r) => {
318
+ try {
319
+ let { tenantId: e, ownerId: i } = F(n(r));
320
+ return A(async () => a.transactional(async (n) => {
321
+ let r = (await n.query(E(x).where(T("id", L(e, i))).forUpdate().limit(1).descriptor))[0];
322
+ G(r, e, i);
323
+ let a = await q(n, b, e, i), o = await q(n, _, e, i);
324
+ return Object.freeze(u.definitions.map((n) => {
325
+ let r = a.find((e) => e.tutorialId === n.id), s = r ? Y(r, e, i, n) : d(n), c = o.filter((e) => e.tutorialId === n.id).sort((e, t) => Number(e.sequence) - Number(t.sequence)).at(-1);
326
+ if (c && (!r || h(c.after) !== h(s)) || !c && r) throw t("invalid_record");
327
+ return Object.freeze({
328
+ ...s,
329
+ nextStep: s.completed ? null : n.steps[s.completedSteps.length],
330
+ skippable: n.skippable,
331
+ hints: n.hints
332
+ });
333
+ }));
334
+ }));
335
+ } catch (n) {
336
+ return S.fail(n instanceof e ? n : t("invalid_input"));
337
+ }
338
+ }, Q = (n, i) => {
339
+ try {
340
+ let e = o(n, [
341
+ "tenantId",
342
+ "subjectId",
343
+ "kind"
344
+ ]), s = r(e.tenantId), c = r(e.subjectId);
345
+ if (e.kind !== "user" && e.kind !== "guest") throw t("invalid_input");
346
+ return A(async () => (await W(s, c), a.transactional(async (e) => {
347
+ if (await e.updateMany(x.tableName, { admission: 0 }, { where: T("id", L(s, c)) }) !== 1) throw t("invalid_record");
348
+ let n = (await e.query(E(x).where(T("id", L(s, c))).limit(1).descriptor))[0];
349
+ if (!n || n.tenantId !== s || typeof n.erased != "boolean" || n.erased && n.ownerId !== null || !n.erased && n.ownerId !== c) throw t("invalid_record");
350
+ let r = await q(e, b, s, c), a = await q(e, _, s, c), o = await q(e, v, s, c), l = await q(e, y, s, c);
351
+ if (!i) return {
352
+ tutorials: r.map((e) => e.state),
353
+ history: a.map((e) => ({
354
+ intent: e.intent,
355
+ before: e.before,
356
+ after: e.after
357
+ })),
358
+ requests: o.map((e) => ({
359
+ intent: e.intent,
360
+ receipt: e.receipt
361
+ })),
362
+ rewards: l.map((e) => ({
363
+ reward: e.reward,
364
+ state: e.state,
365
+ deliveryIntent: e.deliveryIntent,
366
+ deliveryReceipt: e.deliveryReceipt
367
+ }))
368
+ };
369
+ await e.deleteMany(b.tableName, { where: R(s, c) }), await e.updateMany(_.tableName, {
370
+ ownerId: null,
371
+ ownerKey: null,
372
+ intent: null,
373
+ before: null,
374
+ after: null
375
+ }, { where: R(s, c) }), await e.updateMany(v.tableName, {
376
+ ownerKey: null,
377
+ state: "erased",
378
+ linkedOperationId: null,
379
+ intent: null,
380
+ digest: null,
381
+ receipt: null
382
+ }, { where: R(s, c) }), await e.updateMany(y.tableName, {
383
+ ownerId: null,
384
+ ownerKey: null,
385
+ reward: null,
386
+ state: "erased",
387
+ deliveredAt: null,
388
+ deliveryIntent: null,
389
+ deliveryDigest: null,
390
+ deliveryReceipt: null
391
+ }, { where: R(s, c) }), await e.update(x.tableName, L(s, c), {
392
+ ownerId: null,
393
+ erased: !0
394
+ });
395
+ })));
396
+ } catch (n) {
397
+ return S.fail(n instanceof e ? n : t("invalid_input"));
398
+ }
399
+ }, $ = (i, c, l, u) => {
400
+ try {
401
+ I(n(i));
402
+ let e = r(c), g = r(l);
403
+ return A(async () => (await W(e, g), a.transactional(async (n) => {
404
+ let i = await K(n, e, g), a = await q(n, _, e, g), c = await q(n, v, e, g), l = await q(n, y, e, g), x = [...a].sort((e, t) => Number(e.sequence) - Number(t.sequence)), S = /* @__PURE__ */ new Map(), C = /* @__PURE__ */ new Set(), w = /* @__PURE__ */ new Set(), T = 0;
405
+ if (x.length !== i.sequence) throw t("invalid_record");
406
+ for (let [i, a] of x.entries()) {
407
+ let u = P(r(a.tutorialId)), { evidence: _, ...v } = o(a.intent, [
408
+ "tutorialId",
409
+ "version",
410
+ "kind",
411
+ "step",
412
+ "mode",
413
+ "source",
414
+ "originId",
415
+ "evidence"
416
+ ]), y = B(v), b = k(a.at), x = S.get(u.id) ?? d(u), E = c.find((e) => e.id === a.operationId);
417
+ if (await J(n, e, u, !1), a.sequence !== i + 1 || a.id !== O("ptl_", r(a.operationId)) || a.ownerId !== g || a.version !== u.version || y.tutorialId !== u.id || a.claimKey !== V(e, g, y) || w.has(String(a.claimKey)) || h(a.before) !== h(x) || !E || E.digest !== H(e, g, y) || h(B(E.intent)) !== h(y)) throw t("invalid_record");
418
+ let D;
419
+ if (y.kind === "step") {
420
+ let e = s(_, b), n = O("pti_", u.id, e.source, e.originId);
421
+ if (!N.has(e.source) || C.has(n) || y.mode === "event" && (y.source !== e.source || y.originId !== e.originId || e.version !== "trusted.event.v1")) throw t("invalid_record");
422
+ D = p({
423
+ tutorial: u,
424
+ before: x,
425
+ step: y.step,
426
+ evidence: e,
427
+ at: b
428
+ }), C.add(n);
429
+ } else {
430
+ if (_ !== null) throw t("invalid_record");
431
+ D = f(u, x, b);
432
+ }
433
+ let A = X(E);
434
+ if (h(a.after) !== h(D) || h(A.after) !== h(D) || A.at !== b) throw t("invalid_record");
435
+ let j = l.filter((e) => e.operationId === E.id);
436
+ if (j.length !== A.rewardsQueued) throw t("invalid_record");
437
+ if (A.rewardsQueued) for (let [n, r] of u.reward.entries()) {
438
+ let i = j.find((t) => t.id === O("ptr_", e, g, u.id, String(u.version), String(n)));
439
+ if (!i || i.ownerId !== g || i.tutorialId !== u.id || i.version !== u.version || h(i.reward) !== h(r) || k(i.createdAt) !== b) throw t("invalid_record");
440
+ m(i, "tutorial", h), T++;
441
+ }
442
+ w.add(String(a.claimKey)), S.set(u.id, D);
443
+ }
444
+ if (l.length !== T) throw t("invalid_record");
445
+ for (let n of c) {
446
+ let r = B(n.intent);
447
+ if (n.digest !== H(e, g, r)) throw t("invalid_record");
448
+ if (n.state === "replayed") {
449
+ let i = c.find((e) => e.id === n.linkedOperationId);
450
+ if (!i || i.state !== "committed" || n.receipt !== null || V(e, g, r) !== V(e, g, B(i.intent)) || k(n.at) !== k(i.at)) throw t("invalid_record");
451
+ } else if (n.state !== "committed" || !x.some((e) => e.operationId === n.id)) throw t("invalid_record");
452
+ }
453
+ let E = await q(n, b, e, g), D = /* @__PURE__ */ new Map();
454
+ for (let n of E) {
455
+ let i = P(r(n.tutorialId));
456
+ if (D.has(i.id)) throw t("invalid_record");
457
+ D.set(i.id, Y(n, e, g, i));
458
+ }
459
+ let A = D.size === S.size && [...S].every(([e, t]) => h(t) === h(D.get(e) ?? null));
460
+ if (u && !A) {
461
+ await n.deleteMany(b.tableName, { where: R(e, g) });
462
+ for (let [t, r] of S) await n.insert(b.tableName, {
463
+ id: z(e, g, P(t)),
464
+ tenantId: e,
465
+ ownerId: g,
466
+ ownerKey: L(e, g),
467
+ tutorialId: t,
468
+ version: r.version,
469
+ state: r,
470
+ updatedAt: new Date(r.updatedAt)
471
+ });
472
+ }
473
+ return Object.freeze({
474
+ operations: x.length,
475
+ tutorials: S.size,
476
+ rewardRows: T,
477
+ matches: u ? !0 : A
478
+ });
479
+ })));
480
+ } catch (n) {
481
+ return S.fail(n instanceof e ? n : t("invalid_input"));
482
+ }
483
+ };
484
+ return Object.freeze({
485
+ completeStep: (e, t) => Z(e, t, "player", "step"),
486
+ record: (e, t) => Z(e, t, "event", "step"),
487
+ skip: (e, t) => Z(e, t, "player", "skip"),
488
+ mine: te,
489
+ exportSubject: (e) => Q(e, !1),
490
+ eraseSubject: (e) => S.asVoid(Q(e, !0)),
491
+ auditTutorials: (e, t, n) => $(e, t, n, !1),
492
+ rebuildTutorials: (e, t, n) => $(e, t, n, !0)
493
+ });
494
+ };
495
+ export { j as dataStoreTutorials, D as resolveTutorialOptions };
@@ -0,0 +1,99 @@
1
+ import { grant as e } from "./reward.js";
2
+ import { TutorialPort as t, defaultTutorialStepPolicy as n, resolveTutorialStep as r } from "./tutorial.js";
3
+ import { trackDeclaration as i } from "./track.js";
4
+ import { progressionPlugin as a } from "./plugin.js";
5
+ import { progressionFixture as o, tutorialFixture as s } from "./declarationFixtures.js";
6
+ import { Effect as c, Layer as l, Result as u } from "effect";
7
+ import { systemSubject as d } from "@voltro/protocol";
8
+ import { ensureTableRegistered as f } from "@voltro/database";
9
+ import { InMemoryDataStore as p } from "@voltro/runtime";
10
+ // src/tutorialSystem.fixture.ts
11
+ var m = Date.UTC(2026, 9, 10), h = o(), g = s(), _ = {
12
+ app: {
13
+ name: "tutorial-port-fixture",
14
+ type: "api",
15
+ voltroVersion: "0.83.1"
16
+ },
17
+ config: {},
18
+ env: {},
19
+ logger: {
20
+ info() {},
21
+ warn() {},
22
+ error() {},
23
+ debug() {}
24
+ }
25
+ }, v = l.effectContext(c.acquireRelease(c.sync(() => a({
26
+ tracks: [i(h.track)],
27
+ stats: [{ id: "wins" }],
28
+ statSources: ["match.finished"],
29
+ now: () => m,
30
+ tutorials: {
31
+ definitions: [{
32
+ id: "fixture.onboarding",
33
+ steps: ["move", "shoot"],
34
+ reward: [e("gold", 5)]
35
+ }],
36
+ eventSources: ["match.finished"],
37
+ now: () => m
38
+ }
39
+ })), (e) => c.suspend(() => e.onDeactivate(_))).pipe(c.tap((e) => c.gen(function* () {
40
+ e.extendSchema.tables.forEach((e) => f(e)), yield* c.promise(() => Promise.resolve(e.bindDataStore(new p())));
41
+ }).pipe(c.orDie)), c.flatMap((e) => l.build(e.services)))), y = {
42
+ type: "user",
43
+ id: "a",
44
+ tenantId: "fixture",
45
+ kind: "guest"
46
+ }, b = d("tutorial.system.fixture"), x = (e) => ({
47
+ tenantId: "fixture",
48
+ ownerId: "a",
49
+ tutorialId: "fixture.onboarding",
50
+ step: e,
51
+ requestId: "fixture." + e,
52
+ source: "match.finished",
53
+ originId: "fixture." + e
54
+ }), S = Object.freeze({
55
+ kind: "port",
56
+ tag: t,
57
+ fakeLayer: v,
58
+ probe: {
59
+ run: () => c.scoped(c.gen(function* () {
60
+ let e = yield* t, n = yield* e.record(b, x("move")), r = yield* e.record(b, x("move")), i = yield* e.record(b, x("shoot"));
61
+ return {
62
+ original: n.operationId === r.operationId,
63
+ completed: i.after.completed,
64
+ rewardsQueued: i.rewardsQueued,
65
+ tutorials: (yield* e.mine(y)).map((e) => ({
66
+ completedSteps: e.completedSteps,
67
+ nextStep: e.nextStep
68
+ }))
69
+ };
70
+ }).pipe(c.provide(v))),
71
+ expected: {
72
+ original: !0,
73
+ completed: !0,
74
+ rewardsQueued: 1,
75
+ tutorials: [{
76
+ completedSteps: ["move", "shoot"],
77
+ nextStep: null
78
+ }]
79
+ }
80
+ }
81
+ }), C = (e) => c.sync(() => u.try({
82
+ try: e,
83
+ catch: (e) => e.code ?? "unexpected"
84
+ })).pipe(c.map(u.match({
85
+ onFailure: (e) => e,
86
+ onSuccess: () => "accepted"
87
+ }))), w = Object.freeze({
88
+ kind: "slot",
89
+ defaultImplementation: n,
90
+ defaultProbe: {
91
+ run: () => c.sync(() => r(g.stepPolicy).completedSteps),
92
+ expected: ["move"]
93
+ },
94
+ overrideProbe: {
95
+ run: () => C(() => r(g.stepPolicy, () => !1)),
96
+ expected: "forbidden"
97
+ }
98
+ });
99
+ export { v as tutorialFakeLayer, S as tutorialPortSystemFixture, w as tutorialStepPolicySystemFixture };
@@ -0,0 +1,90 @@
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/tutorialTables.entity.ts
3
+ var s = () => a().maxLength(120).externalId().sensitive("pii"), c = () => a().maxLength(120).externalId().sensitive("pii"), l = () => a().maxLength(64).nullable().serverOnly(), u = i("_voltro_prog_tutorial_definitions", {
4
+ id: t({ prefix: "ptd" }),
5
+ tenantId: s(),
6
+ tutorialId: a().maxLength(120).safe(),
7
+ version: n().safe(),
8
+ config: r().serverOnly(),
9
+ rulesVersion: a().maxLength(120).safe(),
10
+ admission: n().serverOnly()
11
+ }).index("voltro_prog_tutorial_definitions_version_idx", [
12
+ "tenantId",
13
+ "tutorialId",
14
+ "version"
15
+ ]), d = i("_voltro_prog_tutorial_subjects", {
16
+ id: t({ prefix: "pts" }),
17
+ tenantId: s(),
18
+ ownerId: c().nullable(),
19
+ erased: e().serverOnly(),
20
+ sequence: n().serverOnly(),
21
+ admission: n().serverOnly()
22
+ }).index(["tenantId", "ownerId"]), f = i("_voltro_prog_tutorial", {
23
+ id: t({ prefix: "ptv" }),
24
+ tenantId: s(),
25
+ ownerId: c(),
26
+ ownerKey: l(),
27
+ tutorialId: a().maxLength(120).safe(),
28
+ version: n().safe(),
29
+ state: r().serverOnly(),
30
+ updatedAt: o().safe()
31
+ }).index(["tenantId", "ownerKey"]), p = i("_voltro_prog_tutorial_ledger", {
32
+ id: t({ prefix: "ptl" }),
33
+ tenantId: s(),
34
+ ownerId: c().nullable(),
35
+ ownerKey: l(),
36
+ tutorialId: a().maxLength(120).safe(),
37
+ version: n().safe(),
38
+ sequence: n().serverOnly(),
39
+ operationId: a().maxLength(64).safe(),
40
+ claimKey: a().maxLength(64).serverOnly(),
41
+ intent: r().nullable().serverOnly(),
42
+ before: r().nullable().serverOnly(),
43
+ after: r().nullable().serverOnly(),
44
+ at: o().safe()
45
+ }).index(["tenantId", "ownerKey"]), m = i("_voltro_prog_tutorial_operations", {
46
+ id: t({ prefix: "pto" }),
47
+ tenantId: s(),
48
+ ownerKey: l(),
49
+ state: a().maxLength(16).oneOf([
50
+ "committed",
51
+ "replayed",
52
+ "erased"
53
+ ]).serverOnly(),
54
+ linkedOperationId: a().maxLength(64).nullable().serverOnly(),
55
+ intent: r().nullable().serverOnly(),
56
+ digest: a().maxLength(64).nullable().serverOnly(),
57
+ receipt: r().nullable().serverOnly(),
58
+ at: o().safe()
59
+ }).index(["tenantId", "ownerKey"]), h = i("_voltro_prog_tutorial_rewards", {
60
+ id: t({ prefix: "ptr" }),
61
+ tenantId: s(),
62
+ ownerId: c().nullable(),
63
+ ownerKey: l(),
64
+ tutorialId: a().maxLength(120).safe(),
65
+ version: n().safe(),
66
+ operationId: a().maxLength(64).safe(),
67
+ reward: r().nullable().serverOnly(),
68
+ state: a().maxLength(16).oneOf([
69
+ "pending",
70
+ "delivered",
71
+ "erased"
72
+ ]).serverOnly(),
73
+ createdAt: o().safe(),
74
+ deliveredAt: o().nullable().safe(),
75
+ deliveryIntent: r().nullable().serverOnly(),
76
+ deliveryDigest: a().maxLength(64).nullable().serverOnly(),
77
+ deliveryReceipt: r().nullable().serverOnly()
78
+ }).index([
79
+ "tenantId",
80
+ "ownerKey",
81
+ "state"
82
+ ]), g = [
83
+ u,
84
+ d,
85
+ f,
86
+ p,
87
+ m,
88
+ h
89
+ ];
90
+ export { u as tutorialDefinitionsTable, p as tutorialLedgerTable, m as tutorialOperationsTable, h as tutorialRewardsTable, f as tutorialStatesTable, d as tutorialSubjectsTable, g as tutorialTables };