@voltro/plugin-leaderboards 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.
- package/CHANGELOG.md +24492 -0
- package/LICENSE +61 -0
- package/README.md +1087 -2
- package/SECURITY.md +56 -0
- package/THIRD-PARTY-NOTICES.md +5603 -0
- package/dist/administration.js +4 -0
- package/dist/bracketAdmission.js +293 -0
- package/dist/bracketAdmissionPolicy.js +21 -0
- package/dist/bracketInstallation.js +132 -0
- package/dist/bracketMatchPorts.js +4 -0
- package/dist/bracketMatches.js +545 -0
- package/dist/brackets.d.ts +149 -0
- package/dist/brackets.js +378 -0
- package/dist/bucketStore.js +92 -0
- package/dist/buckets.js +8 -0
- package/dist/errors.d.ts +13 -0
- package/dist/errors.js +16 -0
- package/dist/glicko2.js +71 -0
- package/dist/index.d.ts +1208 -0
- package/dist/index.js +15 -0
- package/dist/inspect.js +83 -0
- package/dist/json.js +38 -0
- package/dist/leaderboard.d.ts +49 -0
- package/dist/leaderboard.js +56 -0
- package/dist/leagueSchedule.js +39 -0
- package/dist/leagues.js +30 -0
- package/dist/openskill.js +73 -0
- package/dist/plugin.js +337 -0
- package/dist/portLifetime.js +66 -0
- package/dist/ports.d.ts +294 -0
- package/dist/ports.js +6 -0
- package/dist/rankQuery.js +23 -0
- package/dist/ranking.d.ts +42 -0
- package/dist/ranking.js +70 -0
- package/dist/rating.d.ts +103 -0
- package/dist/rating.js +49 -0
- package/dist/ratingPools.js +133 -0
- package/dist/ratingStore.js +333 -0
- package/dist/ratingStrategies.js +107 -0
- package/dist/records.d.ts +116 -0
- package/dist/records.js +87 -0
- package/dist/rpc.d.ts +321 -0
- package/dist/rpc.js +305 -0
- package/dist/score.d.ts +13 -0
- package/dist/score.js +26 -0
- package/dist/server.d.ts +1477 -0
- package/dist/server.js +17 -0
- package/dist/service.js +948 -0
- package/dist/subjectFences.js +43 -0
- package/dist/tables.entity.js +187 -0
- package/dist/testing.d.ts +872 -0
- package/dist/testing.js +367 -0
- package/dist/tournament-app/tournament.advance.workflow.server.tsx +3 -0
- package/dist/tournament-app/tournament.advance.workflow.tsx +12 -0
- package/dist/tournament-app/tournament.stage.workflow.server.tsx +3 -0
- package/dist/tournament-app/tournament.stage.workflow.tsx +10 -0
- package/dist/tournamentInstallation.js +167 -0
- package/dist/tournamentPrizes.js +33 -0
- package/dist/tournamentStage.js +150 -0
- package/dist/tournamentStagePorts.js +4 -0
- package/dist/tournamentWorkflow.js +100 -0
- package/dist/tournamentWorkflowSchemas.js +53 -0
- package/dist/tournaments.d.ts +32 -0
- package/dist/tournaments.js +20 -0
- package/dist/web.d.ts +175 -0
- package/dist/web.js +16 -0
- package/package.json +113 -4
package/dist/brackets.js
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
import { leaderboardError as e } from "./errors.js";
|
|
2
|
+
import { canonicalLeaderboardJson as t, captureLeaderboardJson as n } from "./json.js";
|
|
3
|
+
import { Schema as r } from "effect";
|
|
4
|
+
// src/brackets.ts
|
|
5
|
+
var i = Object.freeze({
|
|
6
|
+
maxEntrants: 256,
|
|
7
|
+
maxMatches: 65536,
|
|
8
|
+
maxSearchNodes: 1e5,
|
|
9
|
+
maxInputBytes: 65536
|
|
10
|
+
});
|
|
11
|
+
function a() {
|
|
12
|
+
throw e("invalid_input");
|
|
13
|
+
}
|
|
14
|
+
var o = (e, t) => Number.isSafeInteger(e) && e > 0 && e <= t ? e : a(), s = (e) => typeof e == "string" && e.length > 0 && e.length <= 120 && e.trim() === e && e.isWellFormed() && !/[\u0000-\u001f\u007f]/.test(e) ? e : a(), c = (e, t) => {
|
|
15
|
+
try {
|
|
16
|
+
return r.decodeUnknownSync(e)(t, { onExcessProperty: "error" });
|
|
17
|
+
} catch {
|
|
18
|
+
return a();
|
|
19
|
+
}
|
|
20
|
+
}, l = r.Struct({
|
|
21
|
+
entrantId: r.String,
|
|
22
|
+
seed: r.Int
|
|
23
|
+
}), u = (e) => {
|
|
24
|
+
let t = n(e, 1024);
|
|
25
|
+
(typeof t != "object" || !t || Array.isArray(t)) && a();
|
|
26
|
+
for (let e of Object.keys(t)) Object.hasOwn(i, e) || a();
|
|
27
|
+
let r = {
|
|
28
|
+
...i,
|
|
29
|
+
...t
|
|
30
|
+
};
|
|
31
|
+
return o(r.maxEntrants, 256), o(r.maxMatches, 65536), o(r.maxSearchNodes, 1e6), o(r.maxInputBytes, 65536), Object.freeze(r);
|
|
32
|
+
}, d = (e, t) => {
|
|
33
|
+
let i = c(r.Array(l), n(e, t.maxInputBytes));
|
|
34
|
+
(i.length < 2 || i.length > t.maxEntrants) && a();
|
|
35
|
+
let u = /* @__PURE__ */ new Set(), d = /* @__PURE__ */ new Set();
|
|
36
|
+
for (let e of i) s(e.entrantId), o(e.seed, i.length), (u.has(e.seed) || d.has(e.entrantId)) && a(), u.add(e.seed), d.add(e.entrantId);
|
|
37
|
+
return Object.freeze(i.map((e) => Object.freeze({ ...e })).sort((e, t) => e.seed - t.seed));
|
|
38
|
+
}, f = (e, t) => Object.freeze({
|
|
39
|
+
left: e,
|
|
40
|
+
right: t
|
|
41
|
+
}), p = (t, i = {}) => {
|
|
42
|
+
let o = c(r.Struct({
|
|
43
|
+
doubleRound: r.optional(r.Boolean),
|
|
44
|
+
limits: r.optional(r.Unknown)
|
|
45
|
+
}), n(i, 2048)), s = u(o.limits ?? {}), l = d(t, s);
|
|
46
|
+
if (l.length * (l.length - 1) / 2 * (o.doubleRound ? 2 : 1) > s.maxMatches) throw e("limit_exceeded");
|
|
47
|
+
let p = l.map((e) => e.entrantId);
|
|
48
|
+
p.length % 2 == 1 && p.push(null);
|
|
49
|
+
let m = [];
|
|
50
|
+
for (let e = 0; e < p.length - 1; e++) {
|
|
51
|
+
let t = [], n = null;
|
|
52
|
+
for (let r = 0; r < p.length / 2; r++) {
|
|
53
|
+
let i = p[r], o = p[p.length - r - 1];
|
|
54
|
+
(i === void 0 || o === void 0 || i === null && o === null) && a(), i === null ? n = o : o === null ? n = i : t.push(e % 2 == 0 ? f(i, o) : f(o, i));
|
|
55
|
+
}
|
|
56
|
+
m.push(Object.freeze({
|
|
57
|
+
round: e + 1,
|
|
58
|
+
pairs: Object.freeze(t),
|
|
59
|
+
bye: n
|
|
60
|
+
}));
|
|
61
|
+
let r = p.pop();
|
|
62
|
+
r === void 0 && a(), p.splice(1, 0, r);
|
|
63
|
+
}
|
|
64
|
+
if (o.doubleRound) {
|
|
65
|
+
let e = m.length;
|
|
66
|
+
for (let t of m.slice()) m.push(Object.freeze({
|
|
67
|
+
round: t.round + e,
|
|
68
|
+
bye: t.bye,
|
|
69
|
+
pairs: Object.freeze(t.pairs.map((e) => f(e.right, e.left)))
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
return Object.freeze(m);
|
|
73
|
+
}, m = r.Struct({
|
|
74
|
+
entrantId: r.String,
|
|
75
|
+
seed: r.Int,
|
|
76
|
+
points: r.Int,
|
|
77
|
+
byes: r.Int,
|
|
78
|
+
opponents: r.Array(r.String)
|
|
79
|
+
}), h = (t, i = {}) => {
|
|
80
|
+
let o = u(i), s = c(r.Array(m), n(t, o.maxInputBytes)), l = d(s.map((e) => ({
|
|
81
|
+
entrantId: e.entrantId,
|
|
82
|
+
seed: e.seed
|
|
83
|
+
})), o);
|
|
84
|
+
if (s.length > 128) throw e("limit_exceeded");
|
|
85
|
+
let p = new Set(l.map((e) => e.entrantId)), h = /* @__PURE__ */ new Map();
|
|
86
|
+
for (let e of s) {
|
|
87
|
+
(!Number.isSafeInteger(e.points) || e.points < 0 || !Number.isSafeInteger(e.byes) || e.byes < 0 || e.byes > 1e6 || e.opponents.length >= s.length) && a();
|
|
88
|
+
let t = /* @__PURE__ */ new Set();
|
|
89
|
+
for (let n of e.opponents) (!p.has(n) || n === e.entrantId || t.has(n)) && a(), t.add(n);
|
|
90
|
+
h.set(e.entrantId, t);
|
|
91
|
+
}
|
|
92
|
+
for (let [e, t] of h) for (let n of t) h.get(n)?.has(e) || a();
|
|
93
|
+
let g = s.map((e) => Object.freeze({
|
|
94
|
+
...e,
|
|
95
|
+
opponents: Object.freeze([...e.opponents])
|
|
96
|
+
})).sort((e, t) => e.points === t.points ? e.seed - t.seed : e.points > t.points ? -1 : 1), _ = 0, v = (t, n) => {
|
|
97
|
+
if (_ >= o.maxSearchNodes) throw e("limit_exceeded");
|
|
98
|
+
_++;
|
|
99
|
+
let r = t[0];
|
|
100
|
+
if (!r) return n;
|
|
101
|
+
let i = t.slice(1).filter((e) => !h.get(r.entrantId)?.has(e.entrantId)).sort((e, t) => {
|
|
102
|
+
let n = r.points > e.points ? BigInt(r.points) - BigInt(e.points) : BigInt(e.points) - BigInt(r.points), i = r.points > t.points ? BigInt(r.points) - BigInt(t.points) : BigInt(t.points) - BigInt(r.points);
|
|
103
|
+
return n === i ? Math.abs(e.seed - r.seed) - Math.abs(t.seed - r.seed) || e.seed - t.seed : n < i ? -1 : 1;
|
|
104
|
+
});
|
|
105
|
+
for (let e of i) {
|
|
106
|
+
let i = v(t.filter((t) => t.entrantId !== r.entrantId && t.entrantId !== e.entrantId), [...n, f(r.entrantId, e.entrantId)]);
|
|
107
|
+
if (i) return i;
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
};
|
|
111
|
+
if (Math.floor(s.length / 2) > o.maxMatches) throw e("limit_exceeded");
|
|
112
|
+
let y = Math.min(...g.map((e) => e.byes)), b = g.length % 2 == 0 ? [null] : g.filter((e) => e.byes === y).sort((e, t) => e.points === t.points ? t.seed - e.seed : e.points < t.points ? -1 : 1);
|
|
113
|
+
for (let e of b) {
|
|
114
|
+
let t = v(g.filter((t) => t.entrantId !== e?.entrantId), []);
|
|
115
|
+
if (t) return Object.freeze({
|
|
116
|
+
pairs: Object.freeze(t),
|
|
117
|
+
bye: e?.entrantId ?? null,
|
|
118
|
+
searchNodes: _
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
throw e("conflict");
|
|
122
|
+
}, g = (e, t, i) => {
|
|
123
|
+
let o = c(r.Array(r.Struct({
|
|
124
|
+
matchId: r.String,
|
|
125
|
+
winnerId: r.String
|
|
126
|
+
})), n(e, i.maxInputBytes)), l = /* @__PURE__ */ new Map();
|
|
127
|
+
for (let e of o) s(e.winnerId), (!t.has(e.matchId) || l.has(e.matchId)) && a(), l.set(e.matchId, e.winnerId);
|
|
128
|
+
return l;
|
|
129
|
+
}, _ = (t, n, r, i, a) => {
|
|
130
|
+
if (a !== void 0 && a !== r && a !== i) throw e("conflict");
|
|
131
|
+
return Object.freeze({
|
|
132
|
+
id: t,
|
|
133
|
+
round: n,
|
|
134
|
+
left: r,
|
|
135
|
+
right: i,
|
|
136
|
+
status: a === void 0 ? "ready" : "complete",
|
|
137
|
+
winnerId: a ?? null
|
|
138
|
+
});
|
|
139
|
+
}, v = (e, t, n) => {
|
|
140
|
+
let r = Math.max(...e.map((e) => t.get(e.entrantId) ?? 0)), i = new Set(e.filter((e) => (t.get(e.entrantId) ?? 0) === r).map((e) => e.entrantId)), a = (e) => n.filter((t) => t.winnerId === e && i.has(t.left) && i.has(t.right)).length, o = (e) => n.filter((t) => t.left === e || t.right === e).reduce((n, r) => n + (t.get(r.left === e ? r.right : r.left) ?? 0), 0);
|
|
141
|
+
return [...e].filter((e) => i.has(e.entrantId)).sort((e, t) => a(t.entrantId) - a(e.entrantId) || o(t.entrantId) - o(e.entrantId) || e.seed - t.seed)[0].entrantId;
|
|
142
|
+
}, y = (e, t, n = {}) => {
|
|
143
|
+
let r = u(n), i = d(e, r), a = p(i, { limits: r }).flatMap((e) => e.pairs.map((t, n) => ({
|
|
144
|
+
id: "rr-" + e.round + "-" + (n + 1),
|
|
145
|
+
round: e.round,
|
|
146
|
+
pairing: t
|
|
147
|
+
}))), o = g(t, new Set(a.map((e) => e.id)), r), s = a.map((e) => _(e.id, e.round, e.pairing.left, e.pairing.right, o.get(e.id))), c = /* @__PURE__ */ new Map();
|
|
148
|
+
for (let e of s) e.winnerId && c.set(e.winnerId, (c.get(e.winnerId) ?? 0) + 1);
|
|
149
|
+
let l = s.every((e) => e.status === "complete");
|
|
150
|
+
return Object.freeze({
|
|
151
|
+
matches: Object.freeze(s),
|
|
152
|
+
champion: l ? v(i, c, s) : null
|
|
153
|
+
});
|
|
154
|
+
}, b = (t, i, o, l = {}) => {
|
|
155
|
+
let f = u(l), p = d(t, f);
|
|
156
|
+
(!Number.isSafeInteger(o) || o < 1 || o >= p.length) && a();
|
|
157
|
+
let m = c(r.Array(r.Struct({
|
|
158
|
+
matchId: r.String,
|
|
159
|
+
winnerId: r.String
|
|
160
|
+
})), n(i, f.maxInputBytes)), g = /* @__PURE__ */ new Map();
|
|
161
|
+
for (let e of m) s(e.winnerId), (!/^sw-[0-9]+-[0-9]+$/.test(e.matchId) || g.has(e.matchId)) && a(), g.set(e.matchId, e.winnerId);
|
|
162
|
+
let y = /* @__PURE__ */ new Map(), b = /* @__PURE__ */ new Map(), x = /* @__PURE__ */ new Map(), S = [];
|
|
163
|
+
for (let e = 1; e <= o; e++) {
|
|
164
|
+
let t = h(p.map((e) => ({
|
|
165
|
+
...e,
|
|
166
|
+
points: y.get(e.entrantId) ?? 0,
|
|
167
|
+
byes: b.get(e.entrantId) ?? 0,
|
|
168
|
+
opponents: x.get(e.entrantId) ?? []
|
|
169
|
+
})), f), n = t.pairs.map((t, n) => {
|
|
170
|
+
let r = "sw-" + e + "-" + (n + 1), i = g.get(r);
|
|
171
|
+
return g.delete(r), _(r, e, t.left, t.right, i);
|
|
172
|
+
});
|
|
173
|
+
if (t.bye !== null && S.push(Object.freeze({
|
|
174
|
+
id: "sw-" + e + "-bye",
|
|
175
|
+
round: e,
|
|
176
|
+
left: t.bye,
|
|
177
|
+
right: null,
|
|
178
|
+
status: "walkover",
|
|
179
|
+
winnerId: t.bye
|
|
180
|
+
})), S.push(...n), n.some((e) => e.status !== "complete")) break;
|
|
181
|
+
for (let e of n) y.set(e.winnerId, (y.get(e.winnerId) ?? 0) + 1), x.set(e.left, [...x.get(e.left) ?? [], e.right]), x.set(e.right, [...x.get(e.right) ?? [], e.left]);
|
|
182
|
+
t.bye !== null && (y.set(t.bye, (y.get(t.bye) ?? 0) + 1), b.set(t.bye, (b.get(t.bye) ?? 0) + 1));
|
|
183
|
+
}
|
|
184
|
+
if (g.size > 0) throw e("conflict");
|
|
185
|
+
let C = S.filter((e) => e.right !== null), w = S.filter((e) => e.right !== null && e.round === o).length > 0 && C.every((e) => e.status === "complete");
|
|
186
|
+
return Object.freeze({
|
|
187
|
+
matches: Object.freeze(S),
|
|
188
|
+
champion: w ? v(p, y, C) : null
|
|
189
|
+
});
|
|
190
|
+
}, x = (t, n = {}) => {
|
|
191
|
+
let r = u(n), i = d(t, r), o = 2, s = [1, 2];
|
|
192
|
+
for (; o < i.length;) o *= 2, s = s.flatMap((e) => [e, o + 1 - e]);
|
|
193
|
+
if (o - 1 > r.maxMatches) throw e("limit_exceeded");
|
|
194
|
+
let c = [], l = (e) => {
|
|
195
|
+
let t = i[e - 1];
|
|
196
|
+
return Object.freeze(t ? {
|
|
197
|
+
kind: "entrant",
|
|
198
|
+
entrantId: t.entrantId
|
|
199
|
+
} : { kind: "bye" });
|
|
200
|
+
}, f = [];
|
|
201
|
+
for (let e = o / 2, t = 1; e >= 1; e /= 2, t++) {
|
|
202
|
+
let n = [];
|
|
203
|
+
for (let r = 0; r < e; r++) {
|
|
204
|
+
let e = `w${t}.${r + 1}`, i = (e) => t === 1 ? l(s[e] ?? a()) : Object.freeze({
|
|
205
|
+
kind: "winner",
|
|
206
|
+
matchId: f[e] ?? a()
|
|
207
|
+
});
|
|
208
|
+
c.push(Object.freeze({
|
|
209
|
+
id: e,
|
|
210
|
+
round: t,
|
|
211
|
+
left: i(r * 2),
|
|
212
|
+
right: i(r * 2 + 1)
|
|
213
|
+
})), n.push(e);
|
|
214
|
+
}
|
|
215
|
+
f = n;
|
|
216
|
+
}
|
|
217
|
+
return Object.freeze({
|
|
218
|
+
format: "single-elimination",
|
|
219
|
+
version: 1,
|
|
220
|
+
entrants: i,
|
|
221
|
+
matches: Object.freeze(c)
|
|
222
|
+
});
|
|
223
|
+
}, S = (i, o, l = {}) => {
|
|
224
|
+
let d = u(l), f = n(i, d.maxInputBytes);
|
|
225
|
+
(typeof f != "object" || !f || Array.isArray(f)) && a();
|
|
226
|
+
let p = x(f.entrants, d);
|
|
227
|
+
t(f) !== t(p) && a();
|
|
228
|
+
let m = c(r.Array(r.Struct({
|
|
229
|
+
matchId: r.String,
|
|
230
|
+
winnerId: r.String
|
|
231
|
+
})), n(o, d.maxInputBytes)), h = /* @__PURE__ */ new Map(), g = new Set(p.matches.map((e) => e.id));
|
|
232
|
+
for (let e of m) s(e.winnerId), (!g.has(e.matchId) || h.has(e.matchId)) && a(), h.set(e.matchId, e.winnerId);
|
|
233
|
+
let _ = /* @__PURE__ */ new Map(), v = [], y = (e) => {
|
|
234
|
+
if (e.kind === "entrant") return {
|
|
235
|
+
kind: "present",
|
|
236
|
+
id: e.entrantId
|
|
237
|
+
};
|
|
238
|
+
if (e.kind === "bye") return e;
|
|
239
|
+
let t = _.get(e.matchId)?.winnerId;
|
|
240
|
+
return t ? {
|
|
241
|
+
kind: "present",
|
|
242
|
+
id: t
|
|
243
|
+
} : { kind: "waiting" };
|
|
244
|
+
};
|
|
245
|
+
for (let t of p.matches) {
|
|
246
|
+
let n = y(t.left), r = y(t.right), i = h.get(t.id), a = "pending", o = null;
|
|
247
|
+
if (n.kind === "present" && r.kind === "present") {
|
|
248
|
+
if (a = i === void 0 ? "ready" : "complete", i !== void 0 && i !== n.id && i !== r.id) throw e("conflict");
|
|
249
|
+
o = i ?? null;
|
|
250
|
+
} else n.kind === "present" && r.kind === "bye" ? (a = "walkover", o = n.id) : r.kind === "present" && n.kind === "bye" && (a = "walkover", o = r.id);
|
|
251
|
+
if (i !== void 0 && a !== "complete") throw e("conflict");
|
|
252
|
+
let s = Object.freeze({
|
|
253
|
+
id: t.id,
|
|
254
|
+
round: t.round,
|
|
255
|
+
left: n.kind === "present" ? n.id : null,
|
|
256
|
+
right: r.kind === "present" ? r.id : null,
|
|
257
|
+
status: a,
|
|
258
|
+
winnerId: o
|
|
259
|
+
});
|
|
260
|
+
_.set(t.id, s), v.push(s);
|
|
261
|
+
}
|
|
262
|
+
return Object.freeze({
|
|
263
|
+
matches: Object.freeze(v),
|
|
264
|
+
champion: v.at(-1)?.winnerId ?? null
|
|
265
|
+
});
|
|
266
|
+
}, C = (t, n = {}) => {
|
|
267
|
+
let r = u(n), i = x(t, r), o = i.matches.at(-1)?.round ?? a(), s = 2 ** o;
|
|
268
|
+
if (2 * s - 1 > r.maxMatches) throw e("limit_exceeded");
|
|
269
|
+
let c = i.matches.map((e) => Object.freeze({
|
|
270
|
+
...e,
|
|
271
|
+
stage: "winners"
|
|
272
|
+
})), l = (e, t) => Object.freeze({
|
|
273
|
+
kind: e,
|
|
274
|
+
matchId: t
|
|
275
|
+
}), d = (e, t, n, r) => {
|
|
276
|
+
let i = `l${e}.${t + 1}`;
|
|
277
|
+
return c.push(Object.freeze({
|
|
278
|
+
id: i,
|
|
279
|
+
stage: "losers",
|
|
280
|
+
round: e,
|
|
281
|
+
left: n,
|
|
282
|
+
right: r
|
|
283
|
+
})), i;
|
|
284
|
+
}, f = [], p = 1;
|
|
285
|
+
for (let e = 0; e < Math.floor(s / 4); e++) f.push(d(p, e, l("loser", `w1.${e * 2 + 1}`), l("loser", `w1.${e * 2 + 2}`)));
|
|
286
|
+
for (let e = 2; e <= o; e++) {
|
|
287
|
+
let t = s / 2 ** e, n = [];
|
|
288
|
+
p++;
|
|
289
|
+
for (let r = 0; r < t; r++) {
|
|
290
|
+
let i = t > 1 ? r ^ 1 : r;
|
|
291
|
+
n.push(d(p, r, l("winner", f[r] ?? a()), l("loser", `w${e}.${i + 1}`)));
|
|
292
|
+
}
|
|
293
|
+
if (e === o) {
|
|
294
|
+
f = n;
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
let r = [];
|
|
298
|
+
p++;
|
|
299
|
+
for (let e = 0; e < n.length / 2; e++) r.push(d(p, e, l("winner", n[e * 2] ?? a()), l("winner", n[e * 2 + 1] ?? a())));
|
|
300
|
+
f = r;
|
|
301
|
+
}
|
|
302
|
+
let m = o === 1 ? l("loser", "w1.1") : l("winner", f[0] ?? a());
|
|
303
|
+
return c.push(Object.freeze({
|
|
304
|
+
id: "g1.1",
|
|
305
|
+
stage: "final",
|
|
306
|
+
round: 1,
|
|
307
|
+
left: l("winner", `w${o}.1`),
|
|
308
|
+
right: m
|
|
309
|
+
})), c.push(Object.freeze({
|
|
310
|
+
id: "g2.1",
|
|
311
|
+
stage: "reset",
|
|
312
|
+
round: 2,
|
|
313
|
+
left: l("loser", "g1.1"),
|
|
314
|
+
right: l("winner", "g1.1")
|
|
315
|
+
})), Object.freeze({
|
|
316
|
+
format: "double-elimination",
|
|
317
|
+
version: 1,
|
|
318
|
+
entrants: i.entrants,
|
|
319
|
+
matches: Object.freeze(c)
|
|
320
|
+
});
|
|
321
|
+
}, w = (i, o, l = {}) => {
|
|
322
|
+
let d = u(l), f = n(i, d.maxInputBytes);
|
|
323
|
+
(typeof f != "object" || !f || Array.isArray(f)) && a();
|
|
324
|
+
let p = C(f.entrants, d);
|
|
325
|
+
t(f) !== t(p) && a();
|
|
326
|
+
let m = c(r.Array(r.Struct({
|
|
327
|
+
matchId: r.String,
|
|
328
|
+
winnerId: r.String
|
|
329
|
+
})), n(o, d.maxInputBytes)), h = /* @__PURE__ */ new Map(), g = new Set(p.matches.map((e) => e.id));
|
|
330
|
+
for (let e of m) s(e.winnerId), (!g.has(e.matchId) || h.has(e.matchId)) && a(), h.set(e.matchId, e.winnerId);
|
|
331
|
+
let _ = [], v = /* @__PURE__ */ new Map(), y = new Map(p.entrants.map((e) => [e.entrantId, 0])), b = (e) => {
|
|
332
|
+
if (e.kind === "entrant") return {
|
|
333
|
+
kind: "present",
|
|
334
|
+
id: e.entrantId
|
|
335
|
+
};
|
|
336
|
+
if (e.kind === "bye") return e;
|
|
337
|
+
let t = v.get(e.matchId);
|
|
338
|
+
if (!t || t.status === "pending" || t.status === "ready") return { kind: "waiting" };
|
|
339
|
+
let n = e.kind === "winner" ? t.winnerId : t.loserId;
|
|
340
|
+
return n ? {
|
|
341
|
+
kind: "present",
|
|
342
|
+
id: n
|
|
343
|
+
} : { kind: "bye" };
|
|
344
|
+
};
|
|
345
|
+
for (let t of p.matches) {
|
|
346
|
+
let n = b(t.left), r = b(t.right), i = h.get(t.id), a = v.get("g1.1"), o = "pending", s = null, c = null;
|
|
347
|
+
if (t.stage === "reset" && a?.status === "complete" && a.winnerId === a.left) o = "inactive";
|
|
348
|
+
else if (n.kind === "present" && r.kind === "present") {
|
|
349
|
+
if (n.id === r.id || (y.get(n.id) ?? 2) >= 2 || (y.get(r.id) ?? 2) >= 2) throw e("conflict");
|
|
350
|
+
if (o = i === void 0 ? "ready" : "complete", i !== void 0) {
|
|
351
|
+
if (i !== n.id && i !== r.id) throw e("conflict");
|
|
352
|
+
s = i, c = i === n.id ? r.id : n.id, y.set(c, (y.get(c) ?? 0) + 1);
|
|
353
|
+
}
|
|
354
|
+
} else n.kind === "present" && r.kind === "bye" ? (o = "walkover", s = n.id) : r.kind === "present" && n.kind === "bye" ? (o = "walkover", s = r.id) : n.kind === "bye" && r.kind === "bye" && (o = "empty");
|
|
355
|
+
if (i !== void 0 && o !== "complete") throw e("conflict");
|
|
356
|
+
let l = Object.freeze({
|
|
357
|
+
id: t.id,
|
|
358
|
+
round: t.round,
|
|
359
|
+
stage: t.stage,
|
|
360
|
+
left: n.kind === "present" ? n.id : null,
|
|
361
|
+
right: r.kind === "present" ? r.id : null,
|
|
362
|
+
status: o,
|
|
363
|
+
winnerId: s,
|
|
364
|
+
loserId: c
|
|
365
|
+
});
|
|
366
|
+
_.push(l), v.set(t.id, l);
|
|
367
|
+
}
|
|
368
|
+
let x = v.get("g1.1"), S = v.get("g2.1"), w = x?.status === "complete" ? x.winnerId === x.left ? x.winnerId : S?.status === "complete" ? S.winnerId : null : null;
|
|
369
|
+
return Object.freeze({
|
|
370
|
+
matches: Object.freeze(_),
|
|
371
|
+
champion: w,
|
|
372
|
+
losses: Object.freeze(p.entrants.map((e) => Object.freeze({
|
|
373
|
+
entrantId: e.entrantId,
|
|
374
|
+
losses: y.get(e.entrantId) ?? 0
|
|
375
|
+
})))
|
|
376
|
+
});
|
|
377
|
+
};
|
|
378
|
+
export { w as advanceDoubleElimination, y as advanceRoundRobin, S as advanceSingleElimination, b as advanceSwiss, p as roundRobin, C as seedDoubleElimination, x as seedSingleElimination, h as swissPairings };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { leaderboardError as e } from "./errors.js";
|
|
2
|
+
import { leaderboardBucketAssignmentsTable as t } from "./tables.entity.js";
|
|
3
|
+
import { and as n, eq as r, queryFor as i } from "@voltro/database";
|
|
4
|
+
// src/bucketStore.ts
|
|
5
|
+
var a = (a) => {
|
|
6
|
+
let o = (e, t, n, r) => a.key("lbba_", [
|
|
7
|
+
e,
|
|
8
|
+
t,
|
|
9
|
+
n,
|
|
10
|
+
r
|
|
11
|
+
]), s = async (n, a, s, c, l) => {
|
|
12
|
+
let u = o(a, s, c, l), d = (await n.query(i(t).where(r("id", u)).limit(1).descriptor))[0];
|
|
13
|
+
if (d && (d.tenantId !== a || d.board !== s || d.periodId !== c || d.ownerId !== l || typeof d.bucket != "string" || d.bucket.length > 64)) throw e("unavailable");
|
|
14
|
+
return d;
|
|
15
|
+
};
|
|
16
|
+
return {
|
|
17
|
+
readOwn: s,
|
|
18
|
+
admit: async (c, l, u, d, f) => {
|
|
19
|
+
if (l.buckets === void 0) return null;
|
|
20
|
+
let p = await s(c, u.tenantId, l.name, d, u.ownerId);
|
|
21
|
+
if (p) return String(p.bucket);
|
|
22
|
+
let m = l.buckets, h = m.assign === "hash" ? null : m.assign === "cohort" ? u.bucketCohort : "skill:" + Math.floor(u.bucketSkill / m.skillWidth), g = a.hash([
|
|
23
|
+
u.tenantId,
|
|
24
|
+
l.name,
|
|
25
|
+
d,
|
|
26
|
+
m.assign,
|
|
27
|
+
h
|
|
28
|
+
]), _ = await c.query(i(t).where(n(r("tenantId", u.tenantId), r("board", l.name), r("periodId", d))).orderBy("id", "asc").limit(l.maxRankRecords + 1).descriptor);
|
|
29
|
+
if (_.length >= l.maxRankRecords) throw e("limit_exceeded");
|
|
30
|
+
let v = /* @__PURE__ */ new Map(), y = -1;
|
|
31
|
+
for (let t of _) {
|
|
32
|
+
if (t.tenantId !== u.tenantId || t.board !== l.name || t.periodId !== d || !Number.isSafeInteger(t.ordinal) || Number(t.ordinal) < 0 || Number(t.ordinal) >= l.maxRankRecords || typeof t.groupKey != "string" || typeof t.bucket != "string") throw e("unavailable");
|
|
33
|
+
let n = a.key("lbbk_", [
|
|
34
|
+
u.tenantId,
|
|
35
|
+
l.name,
|
|
36
|
+
d,
|
|
37
|
+
t.groupKey,
|
|
38
|
+
String(t.ordinal)
|
|
39
|
+
]);
|
|
40
|
+
if (t.bucket !== n) throw e("unavailable");
|
|
41
|
+
if (t.groupKey !== g) continue;
|
|
42
|
+
if (t.group !== h) throw e("unavailable");
|
|
43
|
+
let r = Number(t.ordinal), i = v.get(t.bucket);
|
|
44
|
+
i ? i.entries++ : v.set(t.bucket, {
|
|
45
|
+
id: t.bucket,
|
|
46
|
+
ordinal: r,
|
|
47
|
+
entries: 1
|
|
48
|
+
}), y = Math.max(y, r);
|
|
49
|
+
}
|
|
50
|
+
if ([...v.values()].some((e) => e.entries > m.size)) throw e("unavailable");
|
|
51
|
+
let b = [...v.values()].filter((e) => e.entries < m.size).sort((e, t) => e.ordinal - t.ordinal);
|
|
52
|
+
if (b.length === 0) {
|
|
53
|
+
let e = y + 1;
|
|
54
|
+
b = [{
|
|
55
|
+
id: a.key("lbbk_", [
|
|
56
|
+
u.tenantId,
|
|
57
|
+
l.name,
|
|
58
|
+
d,
|
|
59
|
+
g,
|
|
60
|
+
String(e)
|
|
61
|
+
]),
|
|
62
|
+
ordinal: e,
|
|
63
|
+
entries: 0
|
|
64
|
+
}];
|
|
65
|
+
}
|
|
66
|
+
let x = Object.freeze(b.map((e) => Object.freeze({ ...e }))), S = a.assign(Object.freeze({
|
|
67
|
+
tenantId: u.tenantId,
|
|
68
|
+
board: l.name,
|
|
69
|
+
periodId: d,
|
|
70
|
+
ownerId: u.ownerId,
|
|
71
|
+
assign: m.assign,
|
|
72
|
+
size: m.size,
|
|
73
|
+
group: h,
|
|
74
|
+
candidates: x
|
|
75
|
+
})), C = x.find((e) => e.id === S);
|
|
76
|
+
if (!C) throw e("invalid_record");
|
|
77
|
+
return await c.insert(t.tableName, {
|
|
78
|
+
id: o(u.tenantId, l.name, d, u.ownerId),
|
|
79
|
+
tenantId: u.tenantId,
|
|
80
|
+
board: l.name,
|
|
81
|
+
periodId: d,
|
|
82
|
+
ownerId: u.ownerId,
|
|
83
|
+
bucket: S,
|
|
84
|
+
ordinal: C.ordinal,
|
|
85
|
+
groupKey: g,
|
|
86
|
+
group: h,
|
|
87
|
+
assignedAt: new Date(f)
|
|
88
|
+
}), S;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
export { a as makeBucketStore };
|
package/dist/buckets.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { leaderboardError as e } from "./errors.js";
|
|
2
|
+
import { hashBucket as t } from "./ranking.js";
|
|
3
|
+
// src/buckets.ts
|
|
4
|
+
var n = (n) => {
|
|
5
|
+
if (n.candidates.length === 0) throw e("limit_exceeded");
|
|
6
|
+
return n.candidates[t(n.tenantId, n.periodId, n.ownerId, n.candidates.length)].id;
|
|
7
|
+
};
|
|
8
|
+
export { n as defaultLeaderboardBucketAssign };
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Schema } from 'effect';
|
|
2
|
+
import { YieldableError } from 'effect/Cause';
|
|
3
|
+
|
|
4
|
+
export declare class LeaderboardError extends LeaderboardError_base {
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export declare const leaderboardError: (code: LeaderboardError["code"]) => LeaderboardError;
|
|
8
|
+
|
|
9
|
+
declare const LeaderboardError_base: Schema.Class<LeaderboardError, Schema.TaggedStruct<"LeaderboardError", {
|
|
10
|
+
readonly code: Schema.Literals<readonly ["invalid_board", "invalid_score", "invalid_record", "invalid_input", "overflow", "unavailable", "forbidden", "not_found", "conflict", "erased", "limit_exceeded"]>;
|
|
11
|
+
}>, YieldableError>;
|
|
12
|
+
|
|
13
|
+
export { }
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Schema as e } from "effect";
|
|
2
|
+
// src/errors.ts
|
|
3
|
+
var t = class extends e.TaggedError()("LeaderboardError", { code: e.Literals([
|
|
4
|
+
"invalid_board",
|
|
5
|
+
"invalid_score",
|
|
6
|
+
"invalid_record",
|
|
7
|
+
"invalid_input",
|
|
8
|
+
"overflow",
|
|
9
|
+
"unavailable",
|
|
10
|
+
"forbidden",
|
|
11
|
+
"not_found",
|
|
12
|
+
"conflict",
|
|
13
|
+
"erased",
|
|
14
|
+
"limit_exceeded"
|
|
15
|
+
]) }) {}, n = (e) => new t({ code: e });
|
|
16
|
+
export { t as LeaderboardError, n as leaderboardError };
|
package/dist/glicko2.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { leaderboardError as e } from "./errors.js";
|
|
2
|
+
// src/glicko2.ts
|
|
3
|
+
var t = 173.7178, n = (e, t, n) => Number.isFinite(e) && e >= t && e <= n, r = (e, t) => Number.isSafeInteger(e) && e >= 1 && e <= t, i = (e) => n(e, -1e9, 1e9), a = (e) => n(e, 1e-6, 1e6), o = (e) => n(e, 1e-12, 10), s = (t) => {
|
|
4
|
+
if (!Number.isFinite(t)) throw e("overflow");
|
|
5
|
+
return t;
|
|
6
|
+
}, c = (e) => e >= 0 ? 1 / (1 + Math.exp(-e)) : Math.exp(e) / (1 + Math.exp(e)), l = (l = {}) => {
|
|
7
|
+
let u = l.tau ?? .5, d = l.epsilon ?? 1e-6, f = l.maxIterations ?? 100, p = l.maxOpponents ?? 4096;
|
|
8
|
+
if (!n(u, 1e-6, 10) || !n(d, 1e-12, .01) || !r(f, 1e4) || !r(p, 1e5)) throw e("invalid_input");
|
|
9
|
+
return (n) => {
|
|
10
|
+
let r = n?.player, l = n?.opponents;
|
|
11
|
+
if (!r || !i(r.rating) || !a(r.deviation) || !o(r.volatility) || !Array.isArray(l)) throw e("invalid_input");
|
|
12
|
+
if (l.length > p) throw e("limit_exceeded");
|
|
13
|
+
let m = Object.freeze({
|
|
14
|
+
rating: r.rating,
|
|
15
|
+
deviation: r.deviation,
|
|
16
|
+
volatility: r.volatility
|
|
17
|
+
}), h = l.map((t) => {
|
|
18
|
+
if (!t || !i(t.rating) || !a(t.deviation) || ![
|
|
19
|
+
0,
|
|
20
|
+
.5,
|
|
21
|
+
1
|
|
22
|
+
].includes(t.score)) throw e("invalid_input");
|
|
23
|
+
return {
|
|
24
|
+
rating: t.rating,
|
|
25
|
+
deviation: t.deviation,
|
|
26
|
+
score: t.score
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
for (let t = 0; t < l.length; t++) if (!Object.hasOwn(l, t)) throw e("invalid_input");
|
|
30
|
+
let g = (m.rating - 1500) / t, _ = m.deviation / t, v = (n, r, c) => {
|
|
31
|
+
let l = s(n * t + 1500), u = s(r * t);
|
|
32
|
+
if (!i(l) || !a(u) || !o(c)) throw e("overflow");
|
|
33
|
+
return Object.freeze({
|
|
34
|
+
previous: m,
|
|
35
|
+
rating: l,
|
|
36
|
+
deviation: u,
|
|
37
|
+
volatility: c
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
if (h.length === 0) return v(g, Math.hypot(_, m.volatility), m.volatility);
|
|
41
|
+
let y = 0, b = 0;
|
|
42
|
+
for (let e of h) {
|
|
43
|
+
let n = e.deviation / t, r = 1 / Math.sqrt(1 + 3 * n ** 2 / Math.PI ** 2), i = c(r * (g - (e.rating - 1500) / t));
|
|
44
|
+
y += r ** 2 * i * (1 - i), b += r * (e.score - i);
|
|
45
|
+
}
|
|
46
|
+
if (!Number.isFinite(y) || y <= 0) throw e("overflow");
|
|
47
|
+
let x = s(1 / y), S = s(s(x * b) ** 2), C = Math.log(m.volatility ** 2), w = _ ** 2, T = (e) => {
|
|
48
|
+
let t = s(Math.exp(e)), n = w + x + t;
|
|
49
|
+
return s(t * (S - n) / (2 * n ** 2) - (e - C) / u ** 2);
|
|
50
|
+
}, E = 0, D = () => {
|
|
51
|
+
if (++E > f) throw e("limit_exceeded");
|
|
52
|
+
}, O = C, k;
|
|
53
|
+
if (S > w + x) k = Math.log(S - w - x);
|
|
54
|
+
else {
|
|
55
|
+
let e = 1;
|
|
56
|
+
for (k = C - e * u; T(k) < 0;) D(), k = C - ++e * u;
|
|
57
|
+
}
|
|
58
|
+
let A = T(O), j = T(k);
|
|
59
|
+
if (A * j > 0) throw e("overflow");
|
|
60
|
+
for (; Math.abs(k - O) > d && A !== 0 && j !== 0;) {
|
|
61
|
+
D();
|
|
62
|
+
let t = j - A;
|
|
63
|
+
if (t === 0) throw e("overflow");
|
|
64
|
+
let n = s(O + (O - k) * A / t), r = T(n);
|
|
65
|
+
r * j <= 0 ? (O = k, A = j) : A /= 2, k = n, j = r;
|
|
66
|
+
}
|
|
67
|
+
let M = s(Math.exp((j === 0 ? k : O) / 2)), N = 1 / Math.sqrt(1 / Math.hypot(_, M) ** 2 + y);
|
|
68
|
+
return v(g + N ** 2 * b, N, M);
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
export { l as glicko2 };
|