@voltro/plugin-leaderboards 0.0.0-stage → 0.87.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/CHANGELOG.md +24329 -0
  2. package/LICENSE +61 -0
  3. package/README.md +1087 -2
  4. package/SECURITY.md +56 -0
  5. package/THIRD-PARTY-NOTICES.md +5007 -0
  6. package/dist/administration.js +5 -0
  7. package/dist/bracketAdmission.js +294 -0
  8. package/dist/bracketAdmissionPolicy.js +22 -0
  9. package/dist/bracketInstallation.js +133 -0
  10. package/dist/bracketMatchPorts.js +5 -0
  11. package/dist/bracketMatches.js +546 -0
  12. package/dist/brackets.d.ts +149 -0
  13. package/dist/brackets.js +379 -0
  14. package/dist/bucketStore.js +93 -0
  15. package/dist/buckets.js +9 -0
  16. package/dist/errors.d.ts +13 -0
  17. package/dist/errors.js +17 -0
  18. package/dist/glicko2.js +72 -0
  19. package/dist/index.d.ts +1208 -0
  20. package/dist/index.js +15 -0
  21. package/dist/inspect.js +84 -0
  22. package/dist/json.js +39 -0
  23. package/dist/leaderboard.d.ts +49 -0
  24. package/dist/leaderboard.js +57 -0
  25. package/dist/leagueSchedule.js +40 -0
  26. package/dist/leagues.js +31 -0
  27. package/dist/openskill.js +74 -0
  28. package/dist/plugin.js +338 -0
  29. package/dist/portLifetime.js +67 -0
  30. package/dist/ports.d.ts +294 -0
  31. package/dist/ports.js +7 -0
  32. package/dist/rankQuery.js +24 -0
  33. package/dist/ranking.d.ts +42 -0
  34. package/dist/ranking.js +71 -0
  35. package/dist/rating.d.ts +103 -0
  36. package/dist/rating.js +50 -0
  37. package/dist/ratingPools.js +134 -0
  38. package/dist/ratingStore.js +334 -0
  39. package/dist/ratingStrategies.js +108 -0
  40. package/dist/records.d.ts +116 -0
  41. package/dist/records.js +88 -0
  42. package/dist/rpc.d.ts +321 -0
  43. package/dist/rpc.js +306 -0
  44. package/dist/score.d.ts +13 -0
  45. package/dist/score.js +27 -0
  46. package/dist/server.d.ts +1477 -0
  47. package/dist/server.js +17 -0
  48. package/dist/service.js +949 -0
  49. package/dist/subjectFences.js +44 -0
  50. package/dist/tables.entity.js +188 -0
  51. package/dist/testing.d.ts +872 -0
  52. package/dist/testing.js +368 -0
  53. package/dist/tournament-app/tournament.advance.workflow.server.tsx +3 -0
  54. package/dist/tournament-app/tournament.advance.workflow.tsx +12 -0
  55. package/dist/tournament-app/tournament.stage.workflow.server.tsx +3 -0
  56. package/dist/tournament-app/tournament.stage.workflow.tsx +10 -0
  57. package/dist/tournamentInstallation.js +168 -0
  58. package/dist/tournamentPrizes.js +34 -0
  59. package/dist/tournamentStage.js +151 -0
  60. package/dist/tournamentStagePorts.js +5 -0
  61. package/dist/tournamentWorkflow.js +101 -0
  62. package/dist/tournamentWorkflowSchemas.js +54 -0
  63. package/dist/tournaments.d.ts +32 -0
  64. package/dist/tournaments.js +21 -0
  65. package/dist/web.d.ts +175 -0
  66. package/dist/web.js +17 -0
  67. package/package.json +113 -4
@@ -0,0 +1,334 @@
1
+ import { LeaderboardError as e, leaderboardError as t } from "./errors.js";
2
+ import { leaderboardIdentifier as n } from "./leaderboard.js";
3
+ import { canonicalLeaderboardJson as r, captureLeaderboardJson as i } from "./json.js";
4
+ import { PrepareRatingMatchSchema as a, exactRatingState as o } from "./ratingPools.js";
5
+ import { admitRatingStrategyResults as s } from "./ratingStrategies.js";
6
+ import { leaderboardRatingMatchesTable as c, leaderboardRatingPoolsTable as l, leaderboardRatingResultsTable as u, leaderboardRatingsTable as d } from "./tables.entity.js";
7
+ import { Effect as f, Schema as p } from "effect";
8
+ import { and as m, eq as h, queryFor as g } from "@voltro/database";
9
+ //#region src/ratingStore.ts
10
+ var _ = (n) => n instanceof e ? n : t("unavailable"), v = (e) => f.tryPromise({
11
+ try: e,
12
+ catch: _
13
+ }), y = (e) => {
14
+ let n = e instanceof Date ? e.getTime() : typeof e == "string" ? Date.parse(e) : NaN;
15
+ if (!Number.isSafeInteger(n) || n < 0 || n > 864e13) throw t("unavailable");
16
+ return n;
17
+ }, b = (e, b, x) => {
18
+ let S = new Map(b.map((e) => [e.name, e])), C = (e) => ({
19
+ ...e,
20
+ strategyVersion: x.strategies.version
21
+ }), w = (e) => {
22
+ let n = S.get(e);
23
+ if (!n) throw t("not_found");
24
+ return n;
25
+ }, T = (e) => {
26
+ let r = i(e, 4096);
27
+ if (!r || Object.keys(r).length !== 3 || ![
28
+ r.tenantId,
29
+ r.pool,
30
+ r.matchId
31
+ ].every(n)) throw t("invalid_input");
32
+ return w(r.pool), Object.freeze(r);
33
+ }, E = (e, n = !0) => {
34
+ let o = i(e, 65536), s = p.decodeUnknownSync(a)(o);
35
+ if (r(s) !== r(o) || s.ranks.length !== s.teams.length || s.ranks.some((e) => e > s.teams.length)) throw t("invalid_input");
36
+ let c = s.teams.flat().map((e) => e.ownerId);
37
+ if (new Set(c).size !== c.length || c.length > 4096 || s.teams.some((e) => !e.some((e) => (e.weight ?? 1) > 0)) || n && w(s.pool).algorithm === "glicko2" && (s.teams.length !== 2 || s.teams.some((e) => e.length !== 1 || (e[0].weight ?? 1) !== 1))) throw t("invalid_input");
38
+ return s;
39
+ }, D = (e, n) => {
40
+ let r = E(e.input, !1);
41
+ if (r.tenantId !== n || r.pool !== e.pool || O(r) !== e.id || x.hash(r) !== e.requestKey) throw t("unavailable");
42
+ return r;
43
+ }, O = (e) => x.key("lbrm_", [
44
+ e.tenantId,
45
+ e.pool,
46
+ e.matchId
47
+ ]), k = async (t, n) => {
48
+ let r = x.key("lbrp_", [t, n.name]);
49
+ try {
50
+ await e.insertIgnore(l.tableName, {
51
+ id: r,
52
+ tenantId: t,
53
+ name: n.name,
54
+ config: C(n),
55
+ admission: 0
56
+ }, { conflictColumns: ["id"] });
57
+ } catch (t) {
58
+ if (!(await e.query(g(l).where(h("id", r)).limit(1).descriptor))[0]) throw t;
59
+ }
60
+ }, A = async (e, n, i) => {
61
+ let a = x.key("lbrp_", [n, i.name]);
62
+ if (await e.updateMany(l.tableName, { admission: 0 }, { where: h("id", a) }) !== 1) throw t("unavailable");
63
+ let o = (await e.query(g(l).where(h("id", a)).limit(1).descriptor))[0];
64
+ if (!o || o.tenantId !== n || o.name !== i.name || r(o.config) !== r(C(i))) throw t("conflict");
65
+ }, j = async (e, n) => {
66
+ let r = (await e.query(g(c).where(h("id", O(n))).limit(1).descriptor))[0];
67
+ if (r && (r.tenantId !== n.tenantId || r.pool !== n.pool || !Number.isSafeInteger(r.participants) || Number(r.participants) < 2 || Number(r.participants) > 4096)) throw t("unavailable");
68
+ return r;
69
+ }, M = (e, n) => {
70
+ if (e.state !== "prepared" && e.state !== "applied") throw t(e.state === "erased" ? "erased" : "unavailable");
71
+ return {
72
+ pool: n.pool,
73
+ matchId: n.matchId,
74
+ participants: Number(e.participants),
75
+ state: e.state,
76
+ preparedAt: y(e.preparedAt),
77
+ appliedAt: e.appliedAt === null ? null : y(e.appliedAt)
78
+ };
79
+ }, N = async (e, n, r, i) => {
80
+ let a = await e.query(g(n).where(m(h("tenantId", r), h("ownerId", i))).orderBy("id", "asc").limit(x.maxRows + 1).descriptor);
81
+ if (a.length > x.maxRows) throw t("limit_exceeded");
82
+ if (a.some((e) => e.tenantId !== r || e.ownerId !== i)) throw t("unavailable");
83
+ return a;
84
+ }, P = async (e, n) => {
85
+ let r = await e.query(g(c).where(h("tenantId", n)).orderBy("id", "asc").limit(x.maxRows + 1).descriptor);
86
+ if (r.length > x.maxRows) throw t("limit_exceeded");
87
+ if (r.some((e) => e.tenantId !== n)) throw t("unavailable");
88
+ return r;
89
+ }, F = async (e, n, r, i) => {
90
+ let a = x.key("lbrr_", [
91
+ n,
92
+ r.name,
93
+ i
94
+ ]), s = (await e.query(g(d).where(h("id", a)).limit(1).descriptor))[0];
95
+ if (s && (s.tenantId !== n || s.pool !== r.name || s.ownerId !== i || !Number.isSafeInteger(s.games) || Number(s.games) < 0 || Number(s.games) > 2147483647)) throw t("unavailable");
96
+ return {
97
+ id: a,
98
+ row: s,
99
+ state: s ? o(s.state, r.algorithm) : r.initial
100
+ };
101
+ };
102
+ return {
103
+ prepareRatingMatch: (n, r) => {
104
+ let i, a;
105
+ try {
106
+ if (!x.machine(n)) throw t("forbidden");
107
+ i = E(r), a = w(i.pool);
108
+ } catch (e) {
109
+ return f.fail(_(e));
110
+ }
111
+ let o = i.teams.flat().map((e) => e.ownerId).sort(), s = x.hash(i);
112
+ return v(async () => {
113
+ for (let e of o) await x.seedSubject(i.tenantId, e);
114
+ return await k(i.tenantId, a), e.transactional(async (e) => {
115
+ for (let t of o) await x.subjectFence(e, i.tenantId, t);
116
+ await A(e, i.tenantId, a);
117
+ let n = await j(e, i);
118
+ if (n) {
119
+ if (n.requestKey !== s) throw t("conflict");
120
+ return M(n, i);
121
+ }
122
+ if ((await P(e, i.tenantId)).length >= x.maxRows) throw t("limit_exceeded");
123
+ let r = x.now(), l = await e.insert(c.tableName, {
124
+ id: O(i),
125
+ tenantId: i.tenantId,
126
+ pool: i.pool,
127
+ matchId: i.matchId,
128
+ requestKey: s,
129
+ state: "prepared",
130
+ input: i,
131
+ participants: o.length,
132
+ preparedAt: new Date(r),
133
+ appliedAt: null
134
+ });
135
+ return M(l, i);
136
+ });
137
+ });
138
+ },
139
+ applyRatingMatch: (n, i) => {
140
+ let a, l;
141
+ try {
142
+ if (!x.machine(n)) throw t("forbidden");
143
+ a = T(i), l = w(a.pool);
144
+ } catch (e) {
145
+ return f.fail(_(e));
146
+ }
147
+ return v(async () => {
148
+ let n = await j(e, a);
149
+ if (!n) throw t("not_found");
150
+ if (n.state === "erased") throw t("erased");
151
+ if (n.state === "applied") return M(n, a);
152
+ let i = E(n.input), f = i.teams.flat().map((e) => e.ownerId).sort();
153
+ if (x.hash(i) !== n.requestKey || O(i) !== O(a)) throw t("unavailable");
154
+ return e.transactional(async (e) => {
155
+ for (let t of f) await x.subjectFence(e, a.tenantId, t);
156
+ await A(e, a.tenantId, l);
157
+ let p = await j(e, a);
158
+ if (!p) throw t("not_found");
159
+ if (p.state !== "prepared") return M(p, a);
160
+ if (p.requestKey !== n.requestKey || r(p.input) !== r(i)) throw t("conflict");
161
+ let m = x.now();
162
+ if (m < y(p.preparedAt)) throw t("invalid_input");
163
+ let h = /* @__PURE__ */ new Map();
164
+ for (let t of f) h.set(t, await F(e, a.tenantId, l, t));
165
+ let g = Object.freeze(i.teams.map((e) => Object.freeze(e.map((e) => {
166
+ let n = h.get(e.ownerId), r = Number(n.row?.games ?? 0), i = e.weight ?? 1, s = n.row ? y(n.row.updatedAt) : null;
167
+ if (s !== null && s > m) throw t("invalid_record");
168
+ let c = n.state;
169
+ if (i > 0) {
170
+ let i = Object.freeze({
171
+ tenantId: a.tenantId,
172
+ ownerId: e.ownerId,
173
+ pool: l,
174
+ at: m
175
+ }), u = n.row === void 0 ? x.strategies.placementPolicy(i) : x.strategies.decay(Object.freeze({
176
+ ...i,
177
+ state: c,
178
+ games: r,
179
+ updatedAt: s,
180
+ elapsedMs: m - s
181
+ }));
182
+ try {
183
+ c = o(u, l.algorithm);
184
+ } catch {
185
+ throw t("invalid_record");
186
+ }
187
+ }
188
+ return Object.freeze({
189
+ ownerId: e.ownerId,
190
+ state: c,
191
+ games: r,
192
+ updatedAt: s,
193
+ weight: i
194
+ });
195
+ })))), _ = Object.freeze({
196
+ tenantId: a.tenantId,
197
+ matchId: a.matchId,
198
+ pool: l,
199
+ at: m,
200
+ teams: g,
201
+ ranks: Object.freeze([...i.ranks])
202
+ }), v = s(x.strategies.rate(_), _);
203
+ for (let n of i.teams.flat()) {
204
+ let r = h.get(n.ownerId), i = v.get(n.ownerId), o = Number(r.row?.games ?? 0) + ((n.weight ?? 1) === 0 ? 0 : 1);
205
+ if (o > 2147483647) throw t("overflow");
206
+ if (r.row) {
207
+ if ((n.weight ?? 1) > 0 && !await e.update(d.tableName, r.id, {
208
+ state: i,
209
+ games: o,
210
+ updatedAt: new Date(m)
211
+ })) throw t("unavailable");
212
+ } else await e.insert(d.tableName, {
213
+ id: r.id,
214
+ tenantId: a.tenantId,
215
+ pool: a.pool,
216
+ ownerId: n.ownerId,
217
+ state: i,
218
+ games: o,
219
+ updatedAt: new Date(m)
220
+ });
221
+ await e.insert(u.tableName, {
222
+ id: x.key("lbrs_", [
223
+ a.tenantId,
224
+ a.pool,
225
+ a.matchId,
226
+ n.ownerId
227
+ ]),
228
+ tenantId: a.tenantId,
229
+ pool: a.pool,
230
+ matchKey: String(p.id),
231
+ ownerId: n.ownerId,
232
+ previous: r.state,
233
+ current: i,
234
+ at: new Date(m)
235
+ });
236
+ }
237
+ let b = await e.update(c.tableName, String(p.id), {
238
+ state: "applied",
239
+ input: null,
240
+ appliedAt: new Date(m)
241
+ });
242
+ if (!b) throw t("unavailable");
243
+ return M(b, a);
244
+ });
245
+ });
246
+ },
247
+ myRating: (i, a) => {
248
+ let o, s, c;
249
+ try {
250
+ if (i?.type !== "user" || !n(i.id) || !n(i.tenantId)) throw t("forbidden");
251
+ o = i.tenantId, s = i.id, c = w(a);
252
+ } catch (e) {
253
+ return f.fail(_(e));
254
+ }
255
+ return v(async () => {
256
+ let n = (await e.query(g(l).where(h("id", x.key("lbrp_", [o, c.name]))).limit(1).descriptor))[0];
257
+ if (n && (n.tenantId !== o || n.name !== c.name || r(n.config) !== r(C(c)))) throw t("conflict");
258
+ let i = await F(e, o, c, s);
259
+ if (i.row && !n) throw t("unavailable");
260
+ return {
261
+ pool: c.name,
262
+ state: i.state,
263
+ games: Number(i.row?.games ?? 0),
264
+ updatedAt: i.row ? y(i.row.updatedAt) : null
265
+ };
266
+ });
267
+ },
268
+ pendingRatingMatches: (r, i, a, o = 100) => {
269
+ try {
270
+ if (!x.machine(r)) throw t("forbidden");
271
+ if (!n(i) || !Number.isSafeInteger(o) || o < 1 || o > 1e3) throw t("invalid_input");
272
+ w(a);
273
+ } catch (e) {
274
+ return f.fail(_(e));
275
+ }
276
+ return v(async () => {
277
+ let r = await e.query(g(c).where(m(h("tenantId", i), h("pool", a), h("state", "prepared"))).orderBy("id", "asc").limit(o).descriptor);
278
+ if (r.some((e) => e.tenantId !== i || e.pool !== a || e.state !== "prepared" || !n(e.matchId))) throw t("unavailable");
279
+ return r.map((e) => ({
280
+ tenantId: i,
281
+ pool: a,
282
+ matchId: String(e.matchId)
283
+ }));
284
+ });
285
+ },
286
+ privacyFences: async (e, n) => {
287
+ let r = await e.query(g(l).where(h("tenantId", n)).orderBy("id", "asc").limit(x.maxRows + 1).descriptor);
288
+ if (r.length > x.maxRows) throw t("limit_exceeded");
289
+ for (let i of r) if (i.tenantId !== n || await e.updateMany(l.tableName, { admission: 0 }, { where: m(h("id", String(i.id)), h("tenantId", n)) }) !== 1) throw t("unavailable");
290
+ },
291
+ exportInTransaction: async (e, t, n) => {
292
+ let r = await N(e, d, t, n), a = await N(e, u, t, n), o = await P(e, t), s = new Map(o.map((e) => [e.id, e]));
293
+ return {
294
+ ratings: r.map((e) => ({
295
+ pool: e.pool,
296
+ state: i(e.state, 1024),
297
+ games: e.games,
298
+ updatedAt: y(e.updatedAt)
299
+ })),
300
+ ratingResults: a.map((e) => ({
301
+ pool: e.pool,
302
+ matchId: s.get(e.matchKey)?.matchId ?? null,
303
+ previous: i(e.previous, 1024),
304
+ current: i(e.current, 1024),
305
+ at: y(e.at)
306
+ })),
307
+ pendingRatingMatches: o.filter((e) => e.state === "prepared").flatMap((e) => {
308
+ let r = D(e, t);
309
+ return r.teams.flatMap((t, i) => t.filter((e) => e.ownerId === n).map((t) => ({
310
+ pool: e.pool,
311
+ matchId: e.matchId,
312
+ weight: t.weight ?? 1,
313
+ rank: r.ranks[i],
314
+ preparedAt: y(e.preparedAt)
315
+ })));
316
+ })
317
+ };
318
+ },
319
+ eraseInTransaction: async (e, n, r) => {
320
+ let i = await N(e, d, n, r), a = await N(e, u, n, r), o = new Set(a.map((e) => e.matchKey));
321
+ for (let i of await P(e, n)) if (i.state === "prepared") {
322
+ if (D(i, n).teams.flat().some((e) => e.ownerId === r) && !await e.update(c.tableName, String(i.id), {
323
+ state: "erased",
324
+ input: null,
325
+ matchId: null
326
+ })) throw t("unavailable");
327
+ } else if (o.has(i.id) && !await e.update(c.tableName, String(i.id), { matchId: null })) throw t("unavailable");
328
+ for (let t of a) await e.delete(u.tableName, String(t.id));
329
+ for (let t of i) await e.delete(d.tableName, String(t.id));
330
+ }
331
+ };
332
+ };
333
+ //#endregion
334
+ export { b as makeRatingStore };
@@ -0,0 +1,108 @@
1
+ import { leaderboardError as e } from "./errors.js";
2
+ import { leaderboardIdentifier as t } from "./leaderboard.js";
3
+ import { canonicalLeaderboardJson as n } from "./json.js";
4
+ import { glicko2 as r } from "./glicko2.js";
5
+ import { openskill as i } from "./openskill.js";
6
+ import { elo as a } from "./rating.js";
7
+ import { exactRatingState as o } from "./ratingPools.js";
8
+ //#region src/ratingStrategies.ts
9
+ var s = (e) => e.pool.initial, c = (e) => e.state, l = (t) => {
10
+ let { pool: n, teams: s, ranks: c } = t;
11
+ if (n.algorithm === "elo") return Object.freeze(a(n.options)({
12
+ teams: s.map((t) => t.map((t) => {
13
+ if (t.state.algorithm !== "elo") throw e("invalid_record");
14
+ return {
15
+ id: t.ownerId,
16
+ rating: t.state.rating,
17
+ weight: t.weight
18
+ };
19
+ })),
20
+ ranks: c
21
+ }).flat().map((e) => Object.freeze({
22
+ ownerId: e.id,
23
+ state: o({
24
+ algorithm: "elo",
25
+ rating: e.rating
26
+ }, "elo")
27
+ })));
28
+ if (n.algorithm === "openskill") return Object.freeze(i(n.options)({
29
+ teams: s.map((t) => t.map((t) => {
30
+ if (t.state.algorithm !== "openskill") throw e("invalid_record");
31
+ return {
32
+ id: t.ownerId,
33
+ mu: t.state.mu,
34
+ sigma: t.state.sigma,
35
+ weight: t.weight
36
+ };
37
+ })),
38
+ ranks: c
39
+ }).flat().map((e) => Object.freeze({
40
+ ownerId: e.id,
41
+ state: o({
42
+ algorithm: "openskill",
43
+ mu: e.mu,
44
+ sigma: e.sigma
45
+ }, "openskill")
46
+ })));
47
+ if (s.length !== 2 || s.some((e) => e.length !== 1 || e[0].weight !== 1)) throw e("invalid_input");
48
+ return Object.freeze(s.map((t, i) => {
49
+ let a = t[0], l = s[1 - i][0], u = a.state, d = l.state;
50
+ if (u.algorithm !== "glicko2" || d.algorithm !== "glicko2") throw e("invalid_record");
51
+ let f = c[i] < c[1 - i] ? 1 : c[i] === c[1 - i] ? .5 : 0, p = r(n.options)({
52
+ player: u,
53
+ opponents: [{
54
+ rating: d.rating,
55
+ deviation: d.deviation,
56
+ score: f
57
+ }]
58
+ });
59
+ return Object.freeze({
60
+ ownerId: a.ownerId,
61
+ state: o({
62
+ algorithm: "glicko2",
63
+ rating: p.rating,
64
+ deviation: p.deviation,
65
+ volatility: p.volatility
66
+ }, "glicko2")
67
+ });
68
+ }));
69
+ }, u = "voltro:rating:1", d = (n = {}) => {
70
+ if (!n || typeof n != "object" || Array.isArray(n)) throw e("invalid_input");
71
+ let r = n.rate ?? l, i = n.placementPolicy ?? s, a = n.decay ?? c;
72
+ if (![
73
+ r,
74
+ i,
75
+ a
76
+ ].every((e) => typeof e == "function")) throw e("invalid_input");
77
+ let o = r !== l || i !== s || a !== c, d = n.version ?? u;
78
+ if (!t(d) || o && (n.version === void 0 || d.startsWith("voltro:")) || !o && d !== u) throw e("invalid_input");
79
+ return Object.freeze({
80
+ version: d,
81
+ rate: r,
82
+ placementPolicy: i,
83
+ decay: a
84
+ });
85
+ }, f = (t, r) => {
86
+ let i = r.teams.flat(), a = new Map(i.map((e) => [e.ownerId, e])), s = /* @__PURE__ */ new Map();
87
+ if (!Array.isArray(t) || t.length !== i.length || a.size !== i.length) throw e("invalid_record");
88
+ for (let i = 0; i < t.length; i++) {
89
+ let c = Object.getOwnPropertyDescriptor(t, String(i));
90
+ if (!c || !("value" in c)) throw e("invalid_record");
91
+ let l = c.value;
92
+ if (!l || typeof l != "object" || Array.isArray(l) || Object.keys(l).length !== 2 || !Object.hasOwn(l, "ownerId") || !Object.hasOwn(l, "state")) throw e("invalid_record");
93
+ let u = Object.getOwnPropertyDescriptor(l, "ownerId"), d = Object.getOwnPropertyDescriptor(l, "state");
94
+ if (!u || !("value" in u) || !d || !("value" in d) || typeof u.value != "string" || !a.has(u.value) || s.has(u.value)) throw e("invalid_record");
95
+ let f;
96
+ try {
97
+ f = o(d.value, r.pool.algorithm);
98
+ } catch {
99
+ throw e("invalid_record");
100
+ }
101
+ let p = a.get(u.value);
102
+ if (p.weight === 0 && n(f) !== n(p.state)) throw e("invalid_record");
103
+ s.set(u.value, f);
104
+ }
105
+ return s;
106
+ };
107
+ //#endregion
108
+ export { f as admitRatingStrategyResults, c as defaultRatingDecay, s as defaultRatingPlacement, l as defaultRatingRate, d as resolveRatingStrategies };
@@ -0,0 +1,116 @@
1
+ import { Schema } from 'effect';
2
+
3
+ export declare const BOARDS_TABLE = "_voltro_lb_boards";
4
+
5
+ export declare const BUCKET_ASSIGNMENTS_TABLE = "_voltro_lb_bucket_assignments";
6
+
7
+ export declare type LeaderboardPeriod = typeof LeaderboardPeriodSchema.Type;
8
+
9
+ export declare const LeaderboardPeriodSchema: Schema.Struct<{
10
+ readonly id: Schema.String;
11
+ readonly board: Schema.String;
12
+ readonly startsAt: Schema.Number;
13
+ readonly endsAt: Schema.NullOr<Schema.Number>;
14
+ readonly state: Schema.Literals<readonly ["active", "closed", "archived"]>;
15
+ readonly summary: Schema.NullOr<Schema.Struct<{
16
+ readonly participants: Schema.Number;
17
+ readonly top: Schema.$Array<Schema.Struct<{
18
+ readonly id: Schema.String;
19
+ readonly tenantId: Schema.String;
20
+ readonly board: Schema.String;
21
+ readonly periodId: Schema.String;
22
+ readonly ownerId: Schema.String;
23
+ readonly score: Schema.String;
24
+ readonly subscore: Schema.String;
25
+ readonly updatedAt: Schema.Number;
26
+ readonly rank: Schema.Number;
27
+ }>>;
28
+ }>>;
29
+ }>;
30
+
31
+ export declare type LeaderboardRecord = typeof LeaderboardRecordSchema.Type;
32
+
33
+ export declare const LeaderboardRecordSchema: Schema.Struct<{
34
+ readonly id: Schema.String;
35
+ readonly board: Schema.String;
36
+ readonly periodId: Schema.String;
37
+ readonly ownerId: Schema.String;
38
+ readonly score: Schema.String;
39
+ readonly subscore: Schema.String;
40
+ readonly numSubmits: Schema.Number;
41
+ readonly metadata: Schema.$Record<Schema.String, Schema.Unknown>;
42
+ readonly updatedAt: Schema.Number;
43
+ readonly bucket: Schema.NullOr<Schema.String>;
44
+ }>;
45
+
46
+ export declare type LeaderboardTournamentEntry = typeof LeaderboardTournamentEntrySchema.Type;
47
+
48
+ export declare const LeaderboardTournamentEntrySchema: Schema.Struct<{
49
+ readonly tournament: Schema.String;
50
+ readonly joinedAt: Schema.Number;
51
+ readonly attemptsLeft: Schema.Number;
52
+ }>;
53
+
54
+ export declare const LeaderboardTournamentStatusSchema: Schema.Struct<{
55
+ readonly tournament: Schema.String;
56
+ readonly board: Schema.String;
57
+ readonly startsAt: Schema.Number;
58
+ readonly endsAt: Schema.Number;
59
+ readonly observedAt: Schema.Number;
60
+ readonly joinRequired: Schema.Boolean;
61
+ readonly state: Schema.Literals<readonly ["upcoming", "open", "ended"]>;
62
+ readonly entry: Schema.NullOr<Schema.Struct<{
63
+ readonly tournament: Schema.String;
64
+ readonly joinedAt: Schema.Number;
65
+ readonly attemptsLeft: Schema.Number;
66
+ }>>;
67
+ }>;
68
+
69
+ export declare const MyRatingSchema: Schema.Struct<{
70
+ readonly pool: Schema.NonEmptyString;
71
+ readonly state: Schema.Union<readonly [Schema.Struct<{
72
+ readonly algorithm: Schema.Literal<"elo">;
73
+ readonly rating: Schema.Number;
74
+ }>, Schema.Struct<{
75
+ readonly algorithm: Schema.Literal<"openskill">;
76
+ readonly mu: Schema.Number;
77
+ readonly sigma: Schema.Number;
78
+ }>, Schema.Struct<{
79
+ readonly algorithm: Schema.Literal<"glicko2">;
80
+ readonly rating: Schema.Number;
81
+ readonly deviation: Schema.Number;
82
+ readonly volatility: Schema.Number;
83
+ }>]>;
84
+ readonly games: Schema.Number;
85
+ readonly updatedAt: Schema.NullOr<Schema.Number>;
86
+ }>;
87
+
88
+ export declare const PERIODS_TABLE = "_voltro_lb_periods";
89
+
90
+ export declare const RECORDS_TABLE = "_voltro_lb_records";
91
+
92
+ export declare const SUBJECTS_TABLE = "_voltro_lb_subject_admission";
93
+
94
+ export declare const SUBMISSIONS_TABLE = "_voltro_lb_submissions";
95
+
96
+ /** Trusted service submission, never a player score-intent payload. */
97
+ export declare interface SubmitLeaderboardInput {
98
+ readonly tenantId: string;
99
+ readonly board: string;
100
+ readonly ownerId: string;
101
+ readonly submissionId: string;
102
+ readonly score: string;
103
+ readonly subscore?: string;
104
+ readonly metadata: Readonly<Record<string, unknown>>;
105
+ readonly roomId?: string;
106
+ readonly matchId?: string;
107
+ readonly tournamentId?: string;
108
+ readonly bucketSkill?: number;
109
+ readonly bucketCohort?: string;
110
+ }
111
+
112
+ export declare const TOURNAMENT_ENTRIES_TABLE = "_voltro_lb_tournament_entries";
113
+
114
+ export declare const TOURNAMENTS_TABLE = "_voltro_lb_tournaments";
115
+
116
+ export { }
@@ -0,0 +1,88 @@
1
+ import { scoreString as e } from "./score.js";
2
+ import { RatingStateSchema as t } from "./ratingPools.js";
3
+ import { Schema as n } from "effect";
4
+ //#region src/records.ts
5
+ var r = "_voltro_lb_boards", i = "_voltro_lb_records", a = "_voltro_lb_periods", o = "_voltro_lb_submissions", s = "_voltro_lb_subject_admission", c = "_voltro_lb_tournaments", l = "_voltro_lb_tournament_entries", u = "_voltro_lb_bucket_assignments", d = n.String.pipe(n.check(n.isMaxLength(20)), n.check(n.makeFilter((t) => {
6
+ try {
7
+ return e(t) === t;
8
+ } catch {
9
+ return !1;
10
+ }
11
+ }))), f = n.Number.pipe(n.check(n.isInt()), n.check(n.isBetween({
12
+ minimum: 0,
13
+ maximum: 864e13
14
+ }))), p = n.Struct({
15
+ pool: n.NonEmptyString,
16
+ state: t,
17
+ games: n.Number.pipe(n.check(n.isInt()), n.check(n.isBetween({
18
+ minimum: 0,
19
+ maximum: 2147483647
20
+ }))),
21
+ updatedAt: n.NullOr(f)
22
+ }), m = n.Struct({
23
+ id: n.String,
24
+ board: n.String,
25
+ periodId: n.String,
26
+ ownerId: n.String,
27
+ score: d,
28
+ subscore: d,
29
+ numSubmits: n.Number.pipe(n.check(n.isInt()), n.check(n.isBetween({
30
+ minimum: 1,
31
+ maximum: 2147483647
32
+ }))),
33
+ metadata: n.Record(n.String, n.Unknown),
34
+ updatedAt: f,
35
+ bucket: n.NullOr(n.String.pipe(n.check(n.isMaxLength(64))))
36
+ }), h = n.Struct({
37
+ id: n.String,
38
+ tenantId: n.String,
39
+ board: n.String,
40
+ periodId: n.String,
41
+ ownerId: n.String,
42
+ score: d,
43
+ subscore: d,
44
+ updatedAt: f,
45
+ rank: n.Number.pipe(n.check(n.isInt()), n.check(n.isBetween({
46
+ minimum: 1,
47
+ maximum: 1e5
48
+ })))
49
+ }), g = n.Struct({
50
+ id: n.String,
51
+ board: n.String,
52
+ startsAt: f,
53
+ endsAt: n.NullOr(f),
54
+ state: n.Literals([
55
+ "active",
56
+ "closed",
57
+ "archived"
58
+ ]),
59
+ summary: n.NullOr(n.Struct({
60
+ participants: n.Number.pipe(n.check(n.isInt()), n.check(n.isBetween({
61
+ minimum: 0,
62
+ maximum: 1e5
63
+ }))),
64
+ top: n.Array(h).pipe(n.check(n.isMaxLength(1e3)))
65
+ }))
66
+ }), _ = n.Struct({
67
+ tournament: n.String,
68
+ joinedAt: f,
69
+ attemptsLeft: n.Number.pipe(n.check(n.isInt()), n.check(n.isBetween({
70
+ minimum: 0,
71
+ maximum: 2147483647
72
+ })))
73
+ }), v = n.Struct({
74
+ tournament: n.String,
75
+ board: n.String,
76
+ startsAt: f,
77
+ endsAt: f,
78
+ observedAt: f,
79
+ joinRequired: n.Boolean,
80
+ state: n.Literals([
81
+ "upcoming",
82
+ "open",
83
+ "ended"
84
+ ]),
85
+ entry: n.NullOr(_)
86
+ });
87
+ //#endregion
88
+ export { r as BOARDS_TABLE, u as BUCKET_ASSIGNMENTS_TABLE, g as LeaderboardPeriodSchema, m as LeaderboardRecordSchema, _ as LeaderboardTournamentEntrySchema, v as LeaderboardTournamentStatusSchema, p as MyRatingSchema, a as PERIODS_TABLE, i as RECORDS_TABLE, s as SUBJECTS_TABLE, o as SUBMISSIONS_TABLE, c as TOURNAMENTS_TABLE, l as TOURNAMENT_ENTRIES_TABLE };