@voltro/plugin-progression 0.0.0-stage → 0.87.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24329 -0
- package/LICENSE +61 -0
- package/README.md +478 -2
- package/SECURITY.md +56 -0
- package/THIRD-PARTY-NOTICES.md +9189 -0
- package/dist/achievementComponents.js +92 -0
- package/dist/achievementStore.js +603 -0
- package/dist/achievementTables.entity.js +98 -0
- package/dist/achievements.d.ts +188 -0
- package/dist/achievements.js +205 -0
- package/dist/capture.js +64 -0
- package/dist/cmsQuests.d.ts +91 -0
- package/dist/cmsQuests.js +83 -0
- package/dist/components.d.ts +172 -0
- package/dist/components.js +4 -0
- package/dist/curve.d.ts +27 -0
- package/dist/curve.js +38 -0
- package/dist/daily.d.ts +152 -0
- package/dist/daily.js +126 -0
- package/dist/dailyStore.js +421 -0
- package/dist/dailySystem.fixture.js +138 -0
- package/dist/dailyTables.entity.js +90 -0
- package/dist/declarationFixtures.js +143 -0
- package/dist/errors.d.ts +13 -0
- package/dist/errors.js +20 -0
- package/dist/index.d.ts +1833 -0
- package/dist/index.js +19 -0
- package/dist/pass.d.ts +186 -0
- package/dist/pass.js +122 -0
- package/dist/passRules.js +96 -0
- package/dist/passStore.js +485 -0
- package/dist/passTables.entity.js +96 -0
- package/dist/plugin.js +1086 -0
- package/dist/port.js +5 -0
- package/dist/progressionStore.js +706 -0
- package/dist/questComponents.js +163 -0
- package/dist/questPort.d.ts +126 -0
- package/dist/questPort.js +5 -0
- package/dist/questRefresh.js +56 -0
- package/dist/questRefresh.server.js +126 -0
- package/dist/questRerollJournal.js +77 -0
- package/dist/questStore.js +592 -0
- package/dist/questTables.entity.js +117 -0
- package/dist/quests.d.ts +142 -0
- package/dist/quests.js +153 -0
- package/dist/rank-app/schedules/progression.dispatchRankClosures.cron.tsx +19 -0
- package/dist/rank-app/workflows/progression.closeRankSeason.workflow.server.tsx +10 -0
- package/dist/rank-app/workflows/progression.closeRankSeason.workflow.tsx +1 -0
- package/dist/rankClosure.d.ts +69 -0
- package/dist/rankClosure.js +35 -0
- package/dist/rankClosure.server.js +139 -0
- package/dist/rankClosureWorkflow.d.ts +34 -0
- package/dist/rankClosureWorkflow.js +16 -0
- package/dist/rankReader.server.js +57 -0
- package/dist/rankRewardParent.server.js +65 -0
- package/dist/rankSeasons.d.ts +144 -0
- package/dist/rankSeasons.js +91 -0
- package/dist/rankStore.js +521 -0
- package/dist/rankSystem.fixture.js +170 -0
- package/dist/rankTables.entity.js +101 -0
- package/dist/ranks.d.ts +167 -0
- package/dist/ranks.js +219 -0
- package/dist/resourceStore.js +464 -0
- package/dist/resourceTables.entity.js +89 -0
- package/dist/resources.d.ts +105 -0
- package/dist/resources.js +60 -0
- package/dist/reward-app/schedules/progression.dispatchRewards.cron.tsx +19 -0
- package/dist/reward-app/workflows/progression.deliverReward.workflow.server.tsx +10 -0
- package/dist/reward-app/workflows/progression.deliverReward.workflow.tsx +1 -0
- package/dist/reward.d.ts +27 -0
- package/dist/reward.js +39 -0
- package/dist/rewardDelivery.d.ts +163 -0
- package/dist/rewardDelivery.js +38 -0
- package/dist/rewardDelivery.server.js +430 -0
- package/dist/rewardDeliveryRecord.js +48 -0
- package/dist/rewardDeliveryWorkflow.d.ts +36 -0
- package/dist/rewardDeliveryWorkflow.js +16 -0
- package/dist/rpc.d.ts +880 -0
- package/dist/rpc.js +498 -0
- package/dist/server.d.ts +2812 -0
- package/dist/server.js +21 -0
- package/dist/stats.js +45 -0
- package/dist/strategies.d.ts +131 -0
- package/dist/strategies.js +4 -0
- package/dist/systemFixtures.js +474 -0
- package/dist/tables.entity.js +155 -0
- package/dist/testing.d.ts +1566 -0
- package/dist/testing.js +11 -0
- package/dist/track.js +72 -0
- package/dist/tutorial.d.ts +157 -0
- package/dist/tutorial.js +110 -0
- package/dist/tutorialStore.js +496 -0
- package/dist/tutorialSystem.fixture.js +100 -0
- package/dist/tutorialTables.entity.js +91 -0
- package/dist/web.d.ts +363 -0
- package/dist/web.js +7 -0
- package/package.json +167 -4
|
@@ -0,0 +1,430 @@
|
|
|
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 { captureReward as s } from "./reward.js";
|
|
4
|
+
import { captureTutorialEvidence as c, captureTutorialState as l, defineTutorial as u, resolveTutorialSkip as d, resolveTutorialStep as f } from "./tutorial.js";
|
|
5
|
+
import { capturePassState as p, defineSeasonPass as m } from "./pass.js";
|
|
6
|
+
import { defineQuest as h } from "./quests.js";
|
|
7
|
+
import { captureDailyState as g, defineLoginCalendar as _ } from "./daily.js";
|
|
8
|
+
import { admitRewardDeliveryRecord as v } from "./rewardDeliveryRecord.js";
|
|
9
|
+
import { progressionOperationsTable as y, progressionRewardsTable as b, progressionSubjectsTable as x } from "./tables.entity.js";
|
|
10
|
+
import { questOperationsTable as S, questRewardsTable as C, questSubjectsTable as w, questsTable as T } from "./questTables.entity.js";
|
|
11
|
+
import { questHash as E } from "./questStore.js";
|
|
12
|
+
import { defineAchievement as D } from "./achievements.js";
|
|
13
|
+
import { achievementDefinitionsTable as O, achievementOperationsTable as k, achievementRewardsTable as ee, achievementSubjectsTable as te, achievementUnlockLedgerTable as ne } from "./achievementTables.entity.js";
|
|
14
|
+
import { dailyClaimsTable as re, dailyDefinitionsTable as ie, dailyOperationsTable as ae, dailyRewardsTable as oe, dailySubjectsTable as se } from "./dailyTables.entity.js";
|
|
15
|
+
import { applyPassIntent as ce, capturePassIntent as le } from "./passRules.js";
|
|
16
|
+
import { passDefinitionsTable as ue, passLedgerTable as de, passOperationsTable as fe, passRewardsTable as pe, passSubjectsTable as me } from "./passTables.entity.js";
|
|
17
|
+
import { tutorialDefinitionsTable as he, tutorialLedgerTable as ge, tutorialOperationsTable as _e, tutorialRewardsTable as ve, tutorialSubjectsTable as ye } from "./tutorialTables.entity.js";
|
|
18
|
+
import { rankOperationsTable as A, rankRewardsTable as j, rankSubjectsTable as M } from "./rankTables.entity.js";
|
|
19
|
+
import { admitRankRewardParent as N } from "./rankRewardParent.server.js";
|
|
20
|
+
import { Effect as P, Result as be, Schema as xe } from "effect";
|
|
21
|
+
import { WorkflowRunHandleSchema as Se, systemSubject as Ce } from "@voltro/protocol";
|
|
22
|
+
import { and as we, eq as F, gt as Te, queryFor as I } from "@voltro/database";
|
|
23
|
+
//#region src/rewardDelivery.server.ts
|
|
24
|
+
var L = [
|
|
25
|
+
"level",
|
|
26
|
+
"quest",
|
|
27
|
+
"achievement",
|
|
28
|
+
"daily",
|
|
29
|
+
"pass",
|
|
30
|
+
"tutorial",
|
|
31
|
+
"rank"
|
|
32
|
+
], R = (e) => {
|
|
33
|
+
let i = o(e, [
|
|
34
|
+
"systemAccount",
|
|
35
|
+
"economy",
|
|
36
|
+
"inventory",
|
|
37
|
+
"progression",
|
|
38
|
+
"allowedServices",
|
|
39
|
+
"now",
|
|
40
|
+
"version",
|
|
41
|
+
"service"
|
|
42
|
+
]), a = (e, n) => {
|
|
43
|
+
if (e === void 0) return;
|
|
44
|
+
let r = o(e);
|
|
45
|
+
if (n.some((e) => typeof r[e] != "function")) throw t("invalid_input");
|
|
46
|
+
return Object.freeze(Object.fromEntries(n.map((e) => [e, r[e]])));
|
|
47
|
+
}, s = a(i.economy, [
|
|
48
|
+
"transfer",
|
|
49
|
+
"mine",
|
|
50
|
+
"auditWallet",
|
|
51
|
+
"rebuildWallet"
|
|
52
|
+
]), c = a(i.inventory, [
|
|
53
|
+
"grant",
|
|
54
|
+
"consume",
|
|
55
|
+
"mine",
|
|
56
|
+
"loadout",
|
|
57
|
+
"equip",
|
|
58
|
+
"expire",
|
|
59
|
+
"exportSubject",
|
|
60
|
+
"eraseSubject"
|
|
61
|
+
]), l = a(i.progression, [
|
|
62
|
+
"applyXp",
|
|
63
|
+
"applyStat",
|
|
64
|
+
"mine",
|
|
65
|
+
"stats",
|
|
66
|
+
"exportSubject",
|
|
67
|
+
"eraseSubject"
|
|
68
|
+
]), u = a(i.service, ["execute", "dispatch"]), d = i.now ?? Date.now, f = n(i.allowedServices ?? []);
|
|
69
|
+
if (!Array.isArray(f) || f.length > 256 || new Set(f).size !== f.length || typeof d != "function" || !u && !s && !c && !l) throw t("invalid_input");
|
|
70
|
+
return Object.freeze({
|
|
71
|
+
systemAccount: r(i.systemAccount),
|
|
72
|
+
version: r(i.version ?? "default.v1"),
|
|
73
|
+
economy: s,
|
|
74
|
+
inventory: c,
|
|
75
|
+
progression: l,
|
|
76
|
+
service: u,
|
|
77
|
+
allowedServices: Object.freeze(f.map(r)),
|
|
78
|
+
now: d
|
|
79
|
+
});
|
|
80
|
+
}, z = (e, ...t) => e + E(t).slice(0, 56), B = (e) => {
|
|
81
|
+
let n = e instanceof Date ? e.getTime() : typeof e == "string" ? Date.parse(e) : NaN;
|
|
82
|
+
if (!Number.isSafeInteger(n) || n < 0 || n > 864e13) throw t("invalid_record");
|
|
83
|
+
return n;
|
|
84
|
+
}, V = (n) => P.tryPromise({
|
|
85
|
+
try: n,
|
|
86
|
+
catch: (n) => n instanceof e ? n : t("unavailable")
|
|
87
|
+
}), H = (e) => {
|
|
88
|
+
let n = o(e, ["kind", "rewardId"]);
|
|
89
|
+
if (!L.some((e) => e === n.kind)) throw t("invalid_input");
|
|
90
|
+
return Object.freeze({
|
|
91
|
+
kind: n.kind,
|
|
92
|
+
rewardId: r(n.rewardId)
|
|
93
|
+
});
|
|
94
|
+
}, U = (U, Ee, De) => {
|
|
95
|
+
let W = R(Ee), G = Object.freeze(De.map((e) => {
|
|
96
|
+
if (!L.includes(e)) throw t("invalid_input");
|
|
97
|
+
return e;
|
|
98
|
+
})), K = new Set(W.allowedServices);
|
|
99
|
+
if (new Set(G).size !== G.length || G.length === 0) throw t("invalid_input");
|
|
100
|
+
let q = () => i(W.now(), 0, 864e13), J = Ce("progression.reward.delivery"), Y = (e) => {
|
|
101
|
+
if (e.type !== "system" && !(e.type === "service" && K.has(e.service))) throw t("forbidden");
|
|
102
|
+
}, X = (e) => {
|
|
103
|
+
if (!G.includes(e)) throw t("invalid_input");
|
|
104
|
+
return e === "level" ? {
|
|
105
|
+
rewards: b,
|
|
106
|
+
subjects: x,
|
|
107
|
+
operations: y,
|
|
108
|
+
prefix: "pgs_"
|
|
109
|
+
} : e === "quest" ? {
|
|
110
|
+
rewards: C,
|
|
111
|
+
subjects: w,
|
|
112
|
+
operations: S,
|
|
113
|
+
prefix: "qps_"
|
|
114
|
+
} : e === "achievement" ? {
|
|
115
|
+
rewards: ee,
|
|
116
|
+
subjects: te,
|
|
117
|
+
operations: k,
|
|
118
|
+
prefix: "pas_"
|
|
119
|
+
} : e === "daily" ? {
|
|
120
|
+
rewards: oe,
|
|
121
|
+
subjects: se,
|
|
122
|
+
operations: ae,
|
|
123
|
+
prefix: "pds_"
|
|
124
|
+
} : e === "pass" ? {
|
|
125
|
+
rewards: pe,
|
|
126
|
+
subjects: me,
|
|
127
|
+
operations: fe,
|
|
128
|
+
prefix: "pps_"
|
|
129
|
+
} : e === "tutorial" ? {
|
|
130
|
+
rewards: ve,
|
|
131
|
+
subjects: ye,
|
|
132
|
+
operations: _e,
|
|
133
|
+
prefix: "pts_"
|
|
134
|
+
} : {
|
|
135
|
+
rewards: j,
|
|
136
|
+
subjects: M,
|
|
137
|
+
operations: A,
|
|
138
|
+
prefix: "prs_"
|
|
139
|
+
};
|
|
140
|
+
}, Z = async (e, t) => (await e.query(I(X(t.kind).rewards).where(F("id", t.rewardId)).limit(1).descriptor))[0], Oe = (e, n) => {
|
|
141
|
+
let i = r(n.tenantId), a = r(n.ownerId), o = z(X(e).prefix, i, a);
|
|
142
|
+
if (n.ownerKey !== o) throw t("invalid_record");
|
|
143
|
+
return {
|
|
144
|
+
tenantId: i,
|
|
145
|
+
ownerId: a,
|
|
146
|
+
ownerKey: o
|
|
147
|
+
};
|
|
148
|
+
}, Q = async (e, n, v) => {
|
|
149
|
+
let y = Oe(n.kind, v), b = X(n.kind);
|
|
150
|
+
if (await e.updateMany(b.subjects.tableName, { admission: 0 }, { where: F("id", y.ownerKey) }) !== 1) throw t("invalid_record");
|
|
151
|
+
let x = (await e.query(I(b.subjects).where(F("id", y.ownerKey)).limit(1).descriptor))[0];
|
|
152
|
+
if (!x || x.tenantId !== y.tenantId || x.ownerId !== y.ownerId || x.erased !== !1) throw t(x?.erased === !0 ? "erased" : "invalid_record");
|
|
153
|
+
let S = (await e.query(I(b.operations).where(F("id", r(v.operationId))).limit(1).descriptor))[0];
|
|
154
|
+
if (!S || S.tenantId !== y.tenantId || S.ownerKey !== y.ownerKey || S.state !== "committed" || S.receipt === null) throw t("invalid_record");
|
|
155
|
+
let C = o(S.receipt), w = B(v.createdAt);
|
|
156
|
+
if (C.operationId !== S.id || C.at !== w) throw t("invalid_record");
|
|
157
|
+
if (n.kind === "level") {
|
|
158
|
+
let e = o(C.change), n = i(C.rewardsQueued, 1, 256);
|
|
159
|
+
if (e.kind !== "xp" || v.id === void 0 || !Array.from({ length: n }, (e, t) => z("pgr_", r(v.operationId), String(t))).includes(String(v.id))) throw t("invalid_record");
|
|
160
|
+
i(v.level, i(e.fromLevel, 1, 1e4) + 1, i(e.toLevel, 1, 1e4));
|
|
161
|
+
} else if (n.kind === "quest") {
|
|
162
|
+
let n = r(v.assignmentId), i = (await e.query(I(T).where(F("id", n)).limit(1).descriptor))[0];
|
|
163
|
+
if (C.kind !== "record" || !a(C.completedIds, 8).includes(n) || !i || i.tenantId !== y.tenantId || i.ownerId !== y.ownerId || i.state !== "completed" || B(i.completedAt) !== w) throw t("invalid_record");
|
|
164
|
+
let o = h(i.definition), c = o.reward.findIndex((e, t) => z("qpr_", n, String(t)) === v.id);
|
|
165
|
+
if (c < 0 || E(s(v.reward)) !== E(o.reward[c])) throw t("invalid_record");
|
|
166
|
+
} else if (n.kind === "achievement") {
|
|
167
|
+
let n = r(v.unlockId), o = (await e.query(I(ne).where(F("unlockId", n)).limit(1).descriptor))[0];
|
|
168
|
+
if (!a(C.unlockedIds, 256).includes(n) || !o || o.tenantId !== y.tenantId || o.ownerId !== y.ownerId || o.ownerKey !== y.ownerKey || o.operationId !== v.operationId) throw t("invalid_record");
|
|
169
|
+
let c = (await e.query(I(O).where(F("id", z("pad_", y.tenantId, r(o.achievementId), String(i(o.version, 1, 1e6))))).limit(1).descriptor))[0];
|
|
170
|
+
if (!c || c.tenantId !== y.tenantId || c.achievementId !== o.achievementId || c.version !== o.version) throw t("invalid_record");
|
|
171
|
+
let l = D(c.config).tiers.find((e) => e.id === o.tierId), u = l?.reward.findIndex((e, t) => z("par_", n, String(t)) === v.id) ?? -1;
|
|
172
|
+
if (!l || u < 0 || E(s(v.reward)) !== E(l.reward[u])) throw t("invalid_record");
|
|
173
|
+
} else if (n.kind === "daily") {
|
|
174
|
+
let n = r(v.claimId), a = (await e.query(I(re).where(F("id", n)).limit(1).descriptor))[0];
|
|
175
|
+
if (C.claimId !== n || !a || a.tenantId !== y.tenantId || a.ownerId !== y.ownerId || a.ownerKey !== y.ownerKey || a.operationId !== v.operationId || B(a.at) !== w) throw t("invalid_record");
|
|
176
|
+
let o = r(a.calendarId), c = i(a.version, 1, 1e6), l = (await e.query(I(ie).where(F("id", z("pdd_", y.tenantId, o, String(c)))).limit(1).descriptor))[0];
|
|
177
|
+
if (!l || l.tenantId !== y.tenantId || l.calendarId !== o || l.version !== c) throw t("invalid_record");
|
|
178
|
+
let u = _(l.config), d = g(a.after, u), f = u.days[d.dayIndex].findIndex((e, t) => z("pdr_", n, String(t)) === v.id);
|
|
179
|
+
if (n !== z("pdl_", y.tenantId, y.ownerId, o, String(c), String(d.dayOrdinal)) || a.dayOrdinal !== d.dayOrdinal || a.dayKey !== d.dayKey || C.dayKey !== d.dayKey || C.calendarId !== o || E(C.after) !== E(d) || f < 0 || E(s(v.reward)) !== E(u.days[d.dayIndex][f])) throw t("invalid_record");
|
|
180
|
+
} else if (n.kind === "pass") {
|
|
181
|
+
let n = r(v.passId), a = i(v.version, 1, 1e6), o = i(v.tier, 1, 256);
|
|
182
|
+
if (v.track !== "free" && v.track !== "premium") throw t("invalid_record");
|
|
183
|
+
let c = (await e.query(I(ue).where(F("id", z("ppd_", y.tenantId, n, String(a)))).limit(1).descriptor))[0], l = (await e.query(I(de).where(F("id", z("ppl_", r(v.operationId)))).limit(1).descriptor))[0];
|
|
184
|
+
if (!c || c.tenantId !== y.tenantId || c.passId !== n || c.version !== a || !l || l.tenantId !== y.tenantId || l.ownerId !== y.ownerId || l.ownerKey !== y.ownerKey || l.passId !== n || l.version !== a || l.operationId !== v.operationId || B(l.at) !== w) throw t("invalid_record");
|
|
185
|
+
let u = m(c.config), d = le(l.intent, u), f = ce(u, p(l.before, u), d, w);
|
|
186
|
+
if (C.kind !== d.kind || E(f.after) !== E(l.after) || E(f.after) !== E(C.after) || E(f.claims) !== E(C.claims)) throw t("invalid_record");
|
|
187
|
+
let h = f.claims.find((e) => e.track === v.track && e.tier === o), g = h?.rewards.findIndex((e, t) => z("ppr_", y.tenantId, y.ownerId, n, String(a), String(v.track), String(o), String(t)) === v.id) ?? -1;
|
|
188
|
+
if (!h || g < 0 || E(s(v.reward)) !== E(h.rewards[g])) throw t("invalid_record");
|
|
189
|
+
} else if (n.kind === "tutorial") {
|
|
190
|
+
let n = r(v.tutorialId), a = i(v.version, 1, 1e6), p = (await e.query(I(ge).where(F("id", z("ptl_", r(v.operationId)))).limit(1).descriptor))[0], m = (await e.query(I(he).where(F("id", z("ptd_", y.tenantId, n, String(a)))).limit(1).descriptor))[0];
|
|
191
|
+
if (!p || p.tenantId !== y.tenantId || p.ownerId !== y.ownerId || p.ownerKey !== y.ownerKey || p.operationId !== v.operationId || p.tutorialId !== n || p.version !== a || B(p.at) !== w || !m || m.tenantId !== y.tenantId || m.tutorialId !== n || m.version !== a) throw t("invalid_record");
|
|
192
|
+
let h = u(m.config), g = l(p.before, h), _ = o(p.intent, [
|
|
193
|
+
"tutorialId",
|
|
194
|
+
"version",
|
|
195
|
+
"kind",
|
|
196
|
+
"step",
|
|
197
|
+
"mode",
|
|
198
|
+
"source",
|
|
199
|
+
"originId",
|
|
200
|
+
"evidence"
|
|
201
|
+
]), b = o(S.intent, [
|
|
202
|
+
"tutorialId",
|
|
203
|
+
"version",
|
|
204
|
+
"kind",
|
|
205
|
+
"step",
|
|
206
|
+
"mode",
|
|
207
|
+
"source",
|
|
208
|
+
"originId"
|
|
209
|
+
]), { evidence: x, ...T } = _;
|
|
210
|
+
if (_.tutorialId !== n || _.version !== a || E(T) !== E(b) || C.kind !== _.kind || C.step !== _.step || _.mode !== "player" && _.mode !== "event" || _.mode === "player" && (_.source !== null || _.originId !== null) || _.kind === "skip" && (_.mode !== "player" || _.step !== null)) throw t("invalid_record");
|
|
211
|
+
if (_.kind === "step" && _.mode === "event") {
|
|
212
|
+
let e = c(x, w);
|
|
213
|
+
if (e.source !== _.source || e.originId !== _.originId || e.version !== "trusted.event.v1") throw t("invalid_record");
|
|
214
|
+
}
|
|
215
|
+
let D = _.kind === "step" ? f({
|
|
216
|
+
tutorial: h,
|
|
217
|
+
before: g,
|
|
218
|
+
step: r(_.step),
|
|
219
|
+
evidence: c(x, w),
|
|
220
|
+
at: w
|
|
221
|
+
}) : _.kind === "skip" && x === null ? d(h, g, w) : void 0, O = h.reward.findIndex((e, t) => z("ptr_", y.tenantId, y.ownerId, n, String(a), String(t)) === v.id);
|
|
222
|
+
if (!D || !D.completed || D.skipped && !h.rewardOnSkip || E(D) !== E(p.after) || E(D) !== E(C.after) || C.rewardsQueued !== h.reward.length || O < 0 || E(s(v.reward)) !== E(h.reward[O])) throw t("invalid_record");
|
|
223
|
+
} else await N(e, v, y, S, i(x.sequence, 1, 2147483647));
|
|
224
|
+
return y;
|
|
225
|
+
}, ke = (e, n) => {
|
|
226
|
+
let a = o(n.deliveryReceipt, [
|
|
227
|
+
"kind",
|
|
228
|
+
"rewardId",
|
|
229
|
+
"status",
|
|
230
|
+
"outputKind",
|
|
231
|
+
"externalOperationId",
|
|
232
|
+
"at"
|
|
233
|
+
]);
|
|
234
|
+
if (a.kind !== e.kind || a.rewardId !== e.rewardId || a.status !== "delivered" || a.outputKind !== "currency" && a.outputKind !== "item" && a.outputKind !== "xp" || a.at !== B(n.deliveredAt)) throw t("invalid_record");
|
|
235
|
+
return Object.freeze({
|
|
236
|
+
...e,
|
|
237
|
+
status: "delivered",
|
|
238
|
+
outputKind: a.outputKind,
|
|
239
|
+
externalOperationId: r(a.externalOperationId),
|
|
240
|
+
at: i(a.at, 0, 864e13)
|
|
241
|
+
});
|
|
242
|
+
}, Ae = (e) => Object.freeze({
|
|
243
|
+
...e,
|
|
244
|
+
status: "erased",
|
|
245
|
+
outputKind: null,
|
|
246
|
+
externalOperationId: null,
|
|
247
|
+
at: q()
|
|
248
|
+
}), $ = (e, n) => {
|
|
249
|
+
if (!n || n.id !== e.rewardId) throw t("invalid_input");
|
|
250
|
+
if (v(n, e.kind, E), n.state === "erased") {
|
|
251
|
+
if (n.ownerId !== null || n.ownerKey !== null || n.reward !== null || n.deliveryIntent != null || n.deliveryDigest != null || n.deliveryReceipt != null) throw t("invalid_record");
|
|
252
|
+
return Ae(e);
|
|
253
|
+
}
|
|
254
|
+
if (n.state === "delivered") return ke(e, n);
|
|
255
|
+
if (n.state !== "pending" || n.deliveredAt !== null || n.deliveryReceipt != null) throw t("invalid_record");
|
|
256
|
+
};
|
|
257
|
+
return Object.freeze({
|
|
258
|
+
execute: (c, l) => {
|
|
259
|
+
try {
|
|
260
|
+
let e = n(c);
|
|
261
|
+
Y(e);
|
|
262
|
+
let u = H(l);
|
|
263
|
+
return X(u.kind), V(async () => {
|
|
264
|
+
let e = await U.transactional(async (e) => {
|
|
265
|
+
let n = await Z(e, u), r = $(u, n);
|
|
266
|
+
if (r) return { receipt: r };
|
|
267
|
+
let i = await Q(e, u, n), a = await Z(e, u), o = $(u, a);
|
|
268
|
+
if (o) return { receipt: o };
|
|
269
|
+
if (!a || a.ownerId !== i.ownerId || a.tenantId !== i.tenantId || a.ownerKey !== i.ownerKey) throw t("invalid_record");
|
|
270
|
+
let c = s(a.reward);
|
|
271
|
+
if (c.kind === "currency" && !W.economy || c.kind === "item" && !W.inventory || c.kind === "xp" && !W.progression) throw t("unavailable");
|
|
272
|
+
let l = Object.freeze({
|
|
273
|
+
...i,
|
|
274
|
+
reward: c,
|
|
275
|
+
kind: u.kind,
|
|
276
|
+
rewardId: u.rewardId,
|
|
277
|
+
requestId: z("reward_", u.kind, u.rewardId),
|
|
278
|
+
sourceVersion: W.version,
|
|
279
|
+
systemAccount: W.systemAccount
|
|
280
|
+
}), d = E(l);
|
|
281
|
+
if (a.deliveryIntent != null || a.deliveryDigest != null) {
|
|
282
|
+
if (a.deliveryDigest !== d || E(a.deliveryIntent) !== d) throw t("conflict");
|
|
283
|
+
} else await e.update(X(u.kind).rewards.tableName, u.rewardId, {
|
|
284
|
+
deliveryIntent: l,
|
|
285
|
+
deliveryDigest: d
|
|
286
|
+
});
|
|
287
|
+
return {
|
|
288
|
+
intent: l,
|
|
289
|
+
digest: d
|
|
290
|
+
};
|
|
291
|
+
});
|
|
292
|
+
if ("receipt" in e) return e.receipt;
|
|
293
|
+
let { intent: n, digest: c } = e;
|
|
294
|
+
if (!n || !c) throw t("invalid_record");
|
|
295
|
+
let l = n.reward, d = async (e) => {
|
|
296
|
+
let n = await P.runPromise(P.result(e));
|
|
297
|
+
if (be.isFailure(n)) throw t("unavailable");
|
|
298
|
+
return o(n.success);
|
|
299
|
+
}, f;
|
|
300
|
+
if (l.kind === "currency") {
|
|
301
|
+
let e = await d(W.economy.transfer(J, {
|
|
302
|
+
tenantId: n.tenantId,
|
|
303
|
+
requestId: n.requestId,
|
|
304
|
+
currency: l.currency,
|
|
305
|
+
from: {
|
|
306
|
+
kind: "system",
|
|
307
|
+
id: n.systemAccount
|
|
308
|
+
},
|
|
309
|
+
to: {
|
|
310
|
+
kind: "wallet",
|
|
311
|
+
ownerId: n.ownerId,
|
|
312
|
+
ownerKind: "player"
|
|
313
|
+
},
|
|
314
|
+
amount: l.amount,
|
|
315
|
+
reason: "progression.reward",
|
|
316
|
+
ref: {
|
|
317
|
+
kind: "progression.reward",
|
|
318
|
+
id: n.rewardId
|
|
319
|
+
}
|
|
320
|
+
}));
|
|
321
|
+
if (e.currency !== l.currency || e.amount !== l.amount) throw t("invalid_record");
|
|
322
|
+
f = r(e.txId), i(e.at, 0, 864e13);
|
|
323
|
+
} else if (l.kind === "item") {
|
|
324
|
+
let e = await d(W.inventory.grant(J, {
|
|
325
|
+
tenantId: n.tenantId,
|
|
326
|
+
owner: {
|
|
327
|
+
ownerKind: "player",
|
|
328
|
+
ownerId: n.ownerId
|
|
329
|
+
},
|
|
330
|
+
requestId: n.requestId,
|
|
331
|
+
itemId: l.itemId,
|
|
332
|
+
qty: l.qty,
|
|
333
|
+
reason: "progression.reward",
|
|
334
|
+
ref: {
|
|
335
|
+
kind: "progression.reward",
|
|
336
|
+
id: n.rewardId
|
|
337
|
+
}
|
|
338
|
+
})), s = a(e.changes, 1e5);
|
|
339
|
+
if (e.kind !== "grant" || !Array.isArray(s) || s.length > 1e5 || s.reduce((e, t) => {
|
|
340
|
+
let n = o(t, ["instanceId", "delta"]);
|
|
341
|
+
return r(n.instanceId), e + i(n.delta, 1, 1e6);
|
|
342
|
+
}, 0) !== l.qty) throw t("invalid_record");
|
|
343
|
+
f = r(e.operationId), i(e.at, 0, 864e13);
|
|
344
|
+
} else {
|
|
345
|
+
let e = await d(W.progression.applyXp(J, {
|
|
346
|
+
tenantId: n.tenantId,
|
|
347
|
+
ownerId: n.ownerId,
|
|
348
|
+
trackId: l.trackId,
|
|
349
|
+
amount: l.amount,
|
|
350
|
+
requestId: n.requestId,
|
|
351
|
+
source: "progression.reward",
|
|
352
|
+
origin: n.rewardId
|
|
353
|
+
})), a = o(e.change, [
|
|
354
|
+
"kind",
|
|
355
|
+
"trackId",
|
|
356
|
+
"xp",
|
|
357
|
+
"delta",
|
|
358
|
+
"fromLevel",
|
|
359
|
+
"toLevel"
|
|
360
|
+
]);
|
|
361
|
+
if (a.kind !== "xp" || a.trackId !== l.trackId || a.delta !== l.amount) throw t("invalid_record");
|
|
362
|
+
f = r(e.operationId), i(e.at, 0, 864e13);
|
|
363
|
+
}
|
|
364
|
+
return U.transactional(async (e) => {
|
|
365
|
+
let n = await Z(e, u), r = $(u, n);
|
|
366
|
+
if (r) return r;
|
|
367
|
+
let i = await Q(e, u, n), a = await Z(e, u), o = $(u, a);
|
|
368
|
+
if (o) return o;
|
|
369
|
+
if (!a || a.tenantId !== i.tenantId || a.ownerId !== i.ownerId || a.deliveryDigest !== c || E(a.deliveryIntent) !== c) throw t("conflict");
|
|
370
|
+
let s = q(), d = Object.freeze({
|
|
371
|
+
...u,
|
|
372
|
+
status: "delivered",
|
|
373
|
+
outputKind: l.kind,
|
|
374
|
+
externalOperationId: f,
|
|
375
|
+
at: s
|
|
376
|
+
});
|
|
377
|
+
if (s < B(a.createdAt)) throw t("conflict");
|
|
378
|
+
return await e.update(X(u.kind).rewards.tableName, u.rewardId, {
|
|
379
|
+
state: "delivered",
|
|
380
|
+
deliveredAt: new Date(s),
|
|
381
|
+
deliveryReceipt: d
|
|
382
|
+
}), d;
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
} catch (n) {
|
|
386
|
+
return P.fail(n instanceof e ? n : t("invalid_input"));
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
dispatch: (r, a, s) => {
|
|
390
|
+
try {
|
|
391
|
+
let e = n(r);
|
|
392
|
+
Y(e);
|
|
393
|
+
let c = o(a, ["limit", "after"]), l = i(c.limit, 1, 1e3), u = c.after === void 0 ? void 0 : H(c.after);
|
|
394
|
+
if (typeof s != "function") throw t("invalid_input");
|
|
395
|
+
return V(async () => {
|
|
396
|
+
let e = [];
|
|
397
|
+
for (let t of G) {
|
|
398
|
+
if (u && L.indexOf(t) < L.indexOf(u.kind)) continue;
|
|
399
|
+
let n = u?.kind === t ? we(F("state", "pending"), Te("id", u.rewardId)) : F("state", "pending"), r = await U.query(I(X(t).rewards).where(n).orderBy("id", "asc").select("id").limit(l + 1).descriptor);
|
|
400
|
+
for (let n of r) e.push(H({
|
|
401
|
+
kind: t,
|
|
402
|
+
rewardId: n.id
|
|
403
|
+
}));
|
|
404
|
+
if (e.length > l) break;
|
|
405
|
+
}
|
|
406
|
+
let r = e.slice(0, l), i = [], a = [];
|
|
407
|
+
for (let e of r) try {
|
|
408
|
+
let r = xe.decodeUnknownSync(Se, { onExcessProperty: "error" })(n(await s(e)));
|
|
409
|
+
if (r.workflowName !== "progression.deliverReward") throw t("invalid_record");
|
|
410
|
+
i.push(Object.freeze({
|
|
411
|
+
request: e,
|
|
412
|
+
status: r.status
|
|
413
|
+
}));
|
|
414
|
+
} catch {
|
|
415
|
+
a.push(e);
|
|
416
|
+
}
|
|
417
|
+
return Object.freeze({
|
|
418
|
+
submitted: Object.freeze(i),
|
|
419
|
+
failed: Object.freeze(a),
|
|
420
|
+
next: e.length > l ? r.at(-1) : null
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
} catch (n) {
|
|
424
|
+
return P.fail(n instanceof e ? n : t("invalid_input"));
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
});
|
|
428
|
+
};
|
|
429
|
+
//#endregion
|
|
430
|
+
export { U as dataStoreRewardDelivery, R as resolveRewardDeliveryOptions };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { progressionError as e } from "./errors.js";
|
|
2
|
+
import { identifier as t, integer as n, object as r } from "./capture.js";
|
|
3
|
+
import { captureReward as i } from "./reward.js";
|
|
4
|
+
//#region src/rewardDeliveryRecord.ts
|
|
5
|
+
var a = (a, o, s) => {
|
|
6
|
+
let c = (e) => {
|
|
7
|
+
let t = e instanceof Date ? e.getTime() : typeof e == "string" ? Date.parse(e) : NaN;
|
|
8
|
+
return n(t, 0, 864e13);
|
|
9
|
+
};
|
|
10
|
+
if (a.state === "erased") {
|
|
11
|
+
if (a.ownerId !== null || a.ownerKey !== null || a.reward !== null || a.deliveryIntent != null || a.deliveryDigest != null || a.deliveryReceipt != null || a.deliveredAt !== null) throw e("invalid_record");
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (a.state !== "pending" && a.state !== "delivered") throw e("invalid_record");
|
|
15
|
+
let l = c(a.createdAt), u = i(a.reward);
|
|
16
|
+
if (a.deliveryIntent == null) {
|
|
17
|
+
if (a.deliveryDigest != null || a.deliveryReceipt != null || a.deliveredAt !== null || a.state !== "pending") throw e("invalid_record");
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
let d = r(a.deliveryIntent, [
|
|
21
|
+
"tenantId",
|
|
22
|
+
"ownerId",
|
|
23
|
+
"ownerKey",
|
|
24
|
+
"reward",
|
|
25
|
+
"kind",
|
|
26
|
+
"rewardId",
|
|
27
|
+
"requestId",
|
|
28
|
+
"sourceVersion",
|
|
29
|
+
"systemAccount"
|
|
30
|
+
]), f = o === "level" ? "pgs_" : o === "quest" ? "qps_" : o === "achievement" ? "pas_" : o === "daily" ? "pds_" : o === "pass" ? "pps_" : o === "rank" ? "prs_" : "pts_";
|
|
31
|
+
if (d.tenantId !== t(a.tenantId) || d.ownerId !== t(a.ownerId) || d.ownerKey !== a.ownerKey || d.ownerKey !== f + s([d.tenantId, d.ownerId]).slice(0, 56) || d.kind !== o || d.rewardId !== t(a.id) || d.requestId !== "reward_" + s([o, a.id]).slice(0, 56) || s(i(d.reward)) !== s(u) || s(d) !== a.deliveryDigest) throw e("invalid_record");
|
|
32
|
+
if (t(d.sourceVersion), t(d.systemAccount), a.state === "pending") {
|
|
33
|
+
if (a.deliveryReceipt != null || a.deliveredAt !== null) throw e("invalid_record");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
let p = r(a.deliveryReceipt, [
|
|
37
|
+
"kind",
|
|
38
|
+
"rewardId",
|
|
39
|
+
"status",
|
|
40
|
+
"outputKind",
|
|
41
|
+
"externalOperationId",
|
|
42
|
+
"at"
|
|
43
|
+
]);
|
|
44
|
+
if (p.kind !== o || p.rewardId !== a.id || p.status !== "delivered" || p.outputKind !== u.kind || p.at !== c(a.deliveredAt) || Number(p.at) < l) throw e("invalid_record");
|
|
45
|
+
t(p.externalOperationId);
|
|
46
|
+
};
|
|
47
|
+
//#endregion
|
|
48
|
+
export { a as admitRewardDeliveryRecord };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Literals } from 'effect/Schema';
|
|
2
|
+
import { NullOr } from 'effect/Schema';
|
|
3
|
+
import { Number as Number_2 } from 'effect/Schema';
|
|
4
|
+
import { Schema } from 'effect';
|
|
5
|
+
import { String as String_2 } from 'effect/Schema';
|
|
6
|
+
import { Struct } from 'effect/Schema';
|
|
7
|
+
import { Workflow } from 'effect/workflow/Workflow';
|
|
8
|
+
import { WorkflowMessagesCarrier } from '@voltro/workflow';
|
|
9
|
+
import { YieldableError } from 'effect/Cause';
|
|
10
|
+
|
|
11
|
+
declare class ProgressionError extends ProgressionError_base {
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
declare const ProgressionError_base: Schema.Class<ProgressionError, Schema.TaggedStruct<"ProgressionError", {
|
|
15
|
+
readonly code: Schema.Literals<readonly ["invalid_input", "invalid_record", "invalid_curve", "invalid_reward", "invalid_track", "invalid_stat", "overflow", "insufficient_resource", "forbidden", "conflict", "erased", "not_found", "limit_exceeded", "unavailable"]>;
|
|
16
|
+
}>, YieldableError>;
|
|
17
|
+
|
|
18
|
+
/** The durable journal carries opaque reward identity and redacted receipt, never owner/intent bodies. */
|
|
19
|
+
export declare const ProgressionRewardDelivery: Workflow<"progression.deliverReward", Struct<{
|
|
20
|
+
readonly kind: Literals<readonly ["level", "quest", "achievement", "daily", "pass", "tutorial", "rank"]>;
|
|
21
|
+
readonly rewardId: String_2;
|
|
22
|
+
}>, Struct<{
|
|
23
|
+
readonly kind: Literals<readonly ["level", "quest", "achievement", "daily", "pass", "tutorial", "rank"]>;
|
|
24
|
+
readonly rewardId: String_2;
|
|
25
|
+
readonly status: Literals<readonly ["delivered", "erased"]>;
|
|
26
|
+
readonly outputKind: NullOr<Literals<readonly ["currency", "item", "xp"]>>;
|
|
27
|
+
readonly externalOperationId: NullOr<String_2>;
|
|
28
|
+
readonly at: Number_2;
|
|
29
|
+
}>, typeof ProgressionError> & {
|
|
30
|
+
readonly name: "progression.deliverReward";
|
|
31
|
+
} & WorkflowMessagesCarrier<{
|
|
32
|
+
readonly signals: {};
|
|
33
|
+
readonly updates: {};
|
|
34
|
+
}>;
|
|
35
|
+
|
|
36
|
+
export { }
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ProgressionError as e } from "./errors.js";
|
|
2
|
+
import { RewardDeliveryReceiptSchema as t, RewardDeliveryRequestSchema as n } from "./rewardDelivery.js";
|
|
3
|
+
import { workflow as r } from "@voltro/workflow/define";
|
|
4
|
+
//#region src/rewardDeliveryWorkflow.ts
|
|
5
|
+
var i = r({
|
|
6
|
+
name: "progression.deliverReward",
|
|
7
|
+
payload: n,
|
|
8
|
+
success: t,
|
|
9
|
+
error: e,
|
|
10
|
+
idempotencyKey: ({ kind: e, rewardId: t }) => `progression.reward:${e}:${t}`,
|
|
11
|
+
concurrency: { limit: 4 },
|
|
12
|
+
suspendOnFailure: !0,
|
|
13
|
+
internal: !0
|
|
14
|
+
});
|
|
15
|
+
//#endregion
|
|
16
|
+
export { i as ProgressionRewardDelivery };
|