@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.
Files changed (67) hide show
  1. package/CHANGELOG.md +24492 -0
  2. package/LICENSE +61 -0
  3. package/README.md +1087 -2
  4. package/SECURITY.md +56 -0
  5. package/THIRD-PARTY-NOTICES.md +5603 -0
  6. package/dist/administration.js +4 -0
  7. package/dist/bracketAdmission.js +293 -0
  8. package/dist/bracketAdmissionPolicy.js +21 -0
  9. package/dist/bracketInstallation.js +132 -0
  10. package/dist/bracketMatchPorts.js +4 -0
  11. package/dist/bracketMatches.js +545 -0
  12. package/dist/brackets.d.ts +149 -0
  13. package/dist/brackets.js +378 -0
  14. package/dist/bucketStore.js +92 -0
  15. package/dist/buckets.js +8 -0
  16. package/dist/errors.d.ts +13 -0
  17. package/dist/errors.js +16 -0
  18. package/dist/glicko2.js +71 -0
  19. package/dist/index.d.ts +1208 -0
  20. package/dist/index.js +15 -0
  21. package/dist/inspect.js +83 -0
  22. package/dist/json.js +38 -0
  23. package/dist/leaderboard.d.ts +49 -0
  24. package/dist/leaderboard.js +56 -0
  25. package/dist/leagueSchedule.js +39 -0
  26. package/dist/leagues.js +30 -0
  27. package/dist/openskill.js +73 -0
  28. package/dist/plugin.js +337 -0
  29. package/dist/portLifetime.js +66 -0
  30. package/dist/ports.d.ts +294 -0
  31. package/dist/ports.js +6 -0
  32. package/dist/rankQuery.js +23 -0
  33. package/dist/ranking.d.ts +42 -0
  34. package/dist/ranking.js +70 -0
  35. package/dist/rating.d.ts +103 -0
  36. package/dist/rating.js +49 -0
  37. package/dist/ratingPools.js +133 -0
  38. package/dist/ratingStore.js +333 -0
  39. package/dist/ratingStrategies.js +107 -0
  40. package/dist/records.d.ts +116 -0
  41. package/dist/records.js +87 -0
  42. package/dist/rpc.d.ts +321 -0
  43. package/dist/rpc.js +305 -0
  44. package/dist/score.d.ts +13 -0
  45. package/dist/score.js +26 -0
  46. package/dist/server.d.ts +1477 -0
  47. package/dist/server.js +17 -0
  48. package/dist/service.js +948 -0
  49. package/dist/subjectFences.js +43 -0
  50. package/dist/tables.entity.js +187 -0
  51. package/dist/testing.d.ts +872 -0
  52. package/dist/testing.js +367 -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 +167 -0
  58. package/dist/tournamentPrizes.js +33 -0
  59. package/dist/tournamentStage.js +150 -0
  60. package/dist/tournamentStagePorts.js +4 -0
  61. package/dist/tournamentWorkflow.js +100 -0
  62. package/dist/tournamentWorkflowSchemas.js +53 -0
  63. package/dist/tournaments.d.ts +32 -0
  64. package/dist/tournaments.js +20 -0
  65. package/dist/web.d.ts +175 -0
  66. package/dist/web.js +16 -0
  67. package/package.json +113 -4
@@ -0,0 +1,150 @@
1
+ import { LeaderboardError as e, leaderboardError as t } from "./errors.js";
2
+ import { canonicalLeaderboardJson as n, captureLeaderboardJson as r } from "./json.js";
3
+ import { TournamentStagePayload as i, TournamentWorkflowSummary as a } from "./tournamentWorkflowSchemas.js";
4
+ import { MyBracketMatchesSchema as o, TournamentBracketViewSchema as s } from "./rpc.js";
5
+ import { TournamentStagePort as c } from "./tournamentStagePorts.js";
6
+ import { readBracketAdmissionWindow as l } from "./bracketAdmission.js";
7
+ import { Effect as u, Layer as d, Schema as f } from "effect";
8
+ import { Subject as p } from "@voltro/protocol";
9
+ import { createHash as m } from "node:crypto";
10
+ // src/tournamentStage.ts
11
+ var h = (e) => {
12
+ if (!e || !Array.isArray(e.matches) || e.matches.length > 127 || new Set(e.matches.map((e) => e.id)).size !== e.matches.length) throw t("unavailable");
13
+ for (let n of e.matches) if (!n || typeof n.id != "string" || !n.id.length || n.id.length > 120 || !Number.isSafeInteger(n.round) || n.round < 1 || n.round > 128 || ![
14
+ "pending",
15
+ "ready",
16
+ "walkover",
17
+ "complete",
18
+ "empty",
19
+ "inactive"
20
+ ].includes(n.status) || n.left !== null && (typeof n.left != "string" || !n.left.length) || n.right !== null && (typeof n.right != "string" || !n.right.length) || n.status === "ready" && (n.left === null || n.right === null || n.winnerId !== null) || ["complete", "walkover"].includes(n.status) && (n.winnerId === null || ![n.left, n.right].includes(n.winnerId)) || !["complete", "walkover"].includes(n.status) && n.winnerId !== null || n.roomId !== null && (typeof n.roomId != "string" || !n.roomId.length)) throw t("unavailable");
21
+ return f.decodeUnknownSync(f.toType(a))({
22
+ phase: e.phase,
23
+ ready: e.matches.filter((e) => e.status === "ready").length,
24
+ completed: e.matches.filter((e) => e.status === "complete" || e.status === "walkover").length,
25
+ waiting: e.matches.filter((e) => e.status === "ready" && e.roomId === null).length + +(e.phase === "active" && e.champion !== null),
26
+ unresolvedDisputes: e.unresolvedDisputes
27
+ });
28
+ }, g = (e, t) => (h(e), f.decodeUnknownSync(f.toType(o))({
29
+ phase: e.phase,
30
+ champion: e.champion === t,
31
+ matches: e.matches.filter((e) => e.left === t || e.right === t).map((e) => ({
32
+ matchId: e.id,
33
+ round: e.round,
34
+ status: e.status,
35
+ opponentId: e.left === t ? e.right : e.left,
36
+ roomId: e.roomId,
37
+ won: e.winnerId === null ? null : e.winnerId === t
38
+ }))
39
+ })), _ = (e) => (h(e), f.decodeUnknownSync(f.toType(s))({
40
+ phase: e.phase,
41
+ format: e.format,
42
+ champion: e.champion,
43
+ matches: e.matches.map((e) => ({
44
+ matchId: e.id,
45
+ round: e.round,
46
+ status: e.status,
47
+ left: e.left,
48
+ right: e.right,
49
+ winnerId: e.winnerId
50
+ }))
51
+ })), v = (e) => {
52
+ let n = [
53
+ "epochNow",
54
+ "authorize",
55
+ "seedRoster",
56
+ "notifyEntrants",
57
+ "admissions",
58
+ "brackets"
59
+ ];
60
+ if (!e || Object.getPrototypeOf(e) !== Object.prototype || Object.getOwnPropertySymbols(e).length || Object.entries(Object.getOwnPropertyDescriptors(e)).some(([e, t]) => !n.some((t) => t === e) || !t.enumerable || !("value" in t)) || n.slice(0, 4).some((t) => typeof e[t] != "function") || !e.admissions || !e.brackets) throw t("invalid_input");
61
+ for (let [n, r] of [[e.admissions, "seal"], [e.brackets, "create"]]) {
62
+ let e = Object.getOwnPropertyDescriptor(n, r);
63
+ if (!e || !("value" in e) || typeof e.value != "function") throw t("invalid_input");
64
+ }
65
+ return Object.freeze({
66
+ epochNow: e.epochNow.bind(e),
67
+ authorize: e.authorize.bind(e),
68
+ seedRoster: e.seedRoster.bind(e),
69
+ notifyEntrants: e.notifyEntrants.bind(e),
70
+ admissions: Object.freeze({ seal: e.admissions.seal.bind(e.admissions) }),
71
+ brackets: Object.freeze({ create: e.brackets.create.bind(e.brackets) })
72
+ });
73
+ }, y = (a, o) => {
74
+ let s = v(o), c = s.epochNow, d = s.authorize, g = s.seedRoster, _ = s.notifyEntrants, y = s.admissions.seal.bind(s.admissions), b = s.brackets.create.bind(s.brackets), x = (e, n) => {
75
+ let a = f.decodeUnknownSync(f.toType(p))(structuredClone(e)), o = f.decodeUnknownSync(i)(r(n, 2048), { onExcessProperty: "error" });
76
+ if (a.type !== "system" && a.type !== "service") throw t("forbidden");
77
+ return {
78
+ who: a,
79
+ payload: Object.freeze(o),
80
+ scope: Object.freeze({
81
+ tenantId: o.tenantId,
82
+ tournamentId: o.tournamentId
83
+ })
84
+ };
85
+ }, S = (e, t) => "tstage:" + m("sha256").update(n([
86
+ e.tenantId,
87
+ e.tournamentId,
88
+ e.format,
89
+ e.ruleVersion,
90
+ t
91
+ ])).digest("hex"), C = (n) => {
92
+ try {
93
+ return n().pipe(u.uninterruptible);
94
+ } catch (n) {
95
+ return u.fail(n instanceof e ? n : t("invalid_input"));
96
+ }
97
+ };
98
+ return Object.freeze({
99
+ window: (e, n) => C(() => {
100
+ let { who: r, scope: i } = x(e, n);
101
+ return d(r, i, "window").pipe(u.flatMap(() => l(a, i)), u.flatMap((e) => ["erasing", "cancelled"].includes(e.phase) ? u.fail(t("erased")) : u.succeed(e.definition)));
102
+ }),
103
+ notify: (e, n, r) => C(() => {
104
+ let { who: i, payload: o, scope: s } = x(e, n);
105
+ if (r !== "registration" && r !== "check-in") throw t("invalid_input");
106
+ return d(i, s, "notify").pipe(u.flatMap(() => l(a, s)), u.flatMap((e) => u.try({
107
+ try: () => {
108
+ let n = c();
109
+ if (!Number.isSafeInteger(n) || n < 0 || n > 864e13) throw t("unavailable");
110
+ let i = r === "registration" ? e.definition.registrationOpensAt : e.definition.checkInOpensAt, a = r === "registration" ? e.definition.registrationClosesAt : e.definition.checkInClosesAt;
111
+ return e.phase === "open" && n >= i && n < a;
112
+ },
113
+ catch: () => t("unavailable")
114
+ })), u.flatMap((e) => e ? _(Object.freeze({
115
+ ...o,
116
+ phase: r,
117
+ requestId: S(o, "notify:" + r)
118
+ })).pipe(u.as({ notified: !0 })) : u.succeed({ notified: !1 })));
119
+ }),
120
+ seed: (i, a) => C(() => {
121
+ let { who: o, payload: s, scope: c } = x(i, a), l = (i) => g(r({
122
+ ...s,
123
+ ...i,
124
+ requestId: S(s, "seed")
125
+ }, 65536)).pipe(u.flatMap((a) => u.try({
126
+ try: () => {
127
+ let e = r(a, 16384), o = [...i.checkedIn].sort();
128
+ if (!Array.isArray(e) || e.length !== o.length || n(e.map((e) => e.entrantId).sort()) !== n(o)) throw t("conflict");
129
+ return {
130
+ format: s.format,
131
+ ruleVersion: s.ruleVersion,
132
+ entrants: e
133
+ };
134
+ },
135
+ catch: (n) => n instanceof e ? n : t("invalid_input")
136
+ })), u.flatMap((e) => b(o, c, e)), u.flatMap((e) => u.try({
137
+ try: () => h(e),
138
+ catch: () => t("unavailable")
139
+ }))), f = Object.freeze({
140
+ phase: "cancelled",
141
+ ready: 0,
142
+ completed: 0,
143
+ waiting: 0,
144
+ unresolvedDisputes: 0
145
+ });
146
+ return d(o, c, "seed").pipe(u.flatMap(() => y(o, c)), u.flatMap((e) => e.checkedIn.length < 2 ? u.succeed(f) : l(e)));
147
+ })
148
+ });
149
+ }, b = (e, t) => d.succeed(c, y(e, t));
150
+ export { y as dataStoreTournamentStages, g as myBracketMatches, v as resolveTournamentStageOptions, h as summarizeTournamentStatus, _ as tournamentBracketView, b as tournamentStageLayer };
@@ -0,0 +1,4 @@
1
+ import { Context as e } from "effect";
2
+ // src/tournamentStagePorts.ts
3
+ var t = class extends e.Service()("@voltro/TournamentStagePort") {};
4
+ export { t as TournamentStagePort };
@@ -0,0 +1,100 @@
1
+ import { LeaderboardError as e, leaderboardError as t } from "./errors.js";
2
+ import { captureLeaderboardJson as n } from "./json.js";
3
+ import { TournamentAdvancePayload as r, TournamentStageNotification as i, TournamentStagePayload as a, TournamentStageWindow as o, TournamentWorkflowSummary as s } from "./tournamentWorkflowSchemas.js";
4
+ import { TournamentBracketPort as c } from "./bracketMatchPorts.js";
5
+ import { TournamentStagePort as l } from "./tournamentStagePorts.js";
6
+ import { summarizeTournamentStatus as u } from "./tournamentStage.js";
7
+ import { Effect as d, Schema as f } from "effect";
8
+ import { Subject as p } from "@voltro/protocol";
9
+ import { fileURLToPath as m } from "node:url";
10
+ import { sleepUntil as h, step as g } from "@voltro/workflow";
11
+ // src/tournamentWorkflow.ts
12
+ var _ = m(new URL("./tournament-app", "" + import.meta.url)), v = (e) => (i) => {
13
+ let a, o;
14
+ try {
15
+ a = f.decodeUnknownSync(r)(n(i, 2048), { onExcessProperty: "error" }), o = f.decodeUnknownSync(f.toType(p))(structuredClone(e.request.subject));
16
+ } catch {
17
+ return d.fail(t("invalid_input"));
18
+ }
19
+ return y(o, a, void 0);
20
+ }, y = (n, r, i) => d.gen(function* () {
21
+ let a = yield* c, o = Object.freeze({
22
+ tenantId: r.tenantId,
23
+ tournamentId: r.tournamentId
24
+ }), l = i;
25
+ for (let i = 1; i <= 129 && (l === void 0 || l.phase === "active" && l.waiting > 0); i++) {
26
+ let c = yield* g({
27
+ name: "advance-" + i,
28
+ input: r,
29
+ success: s,
30
+ error: e,
31
+ execute: a.advance(n, o).pipe(d.flatMap((e) => d.try({
32
+ try: () => u(e),
33
+ catch: () => t("unavailable")
34
+ })))
35
+ }), f = l !== void 0 && c.waiting >= l.waiting;
36
+ if (l = c, f) break;
37
+ }
38
+ return l;
39
+ }), b = (r) => (c) => {
40
+ let u, m;
41
+ try {
42
+ u = f.decodeUnknownSync(a)(n(c, 2048), { onExcessProperty: "error" }), m = f.decodeUnknownSync(f.toType(p))(structuredClone(r.request.subject));
43
+ } catch {
44
+ return d.fail(t("invalid_input"));
45
+ }
46
+ return d.gen(function* () {
47
+ let t = yield* l, n = yield* g({
48
+ name: "load-admission-window",
49
+ input: u,
50
+ success: o,
51
+ error: e,
52
+ execute: t.window(m, u)
53
+ });
54
+ yield* h({
55
+ name: "registration-opens",
56
+ until: n.registrationOpensAt
57
+ }), yield* g({
58
+ name: "notify-registration",
59
+ input: u,
60
+ success: i,
61
+ error: e,
62
+ execute: t.notify(m, u, "registration")
63
+ }), yield* h({
64
+ name: "check-in-opens",
65
+ until: n.checkInOpensAt
66
+ }), yield* g({
67
+ name: "notify-check-in",
68
+ input: u,
69
+ success: i,
70
+ error: e,
71
+ execute: t.notify(m, u, "check-in")
72
+ }), yield* h({
73
+ name: "check-in-closes",
74
+ until: n.checkInClosesAt
75
+ });
76
+ let r = yield* g({
77
+ name: "seal-and-seed",
78
+ input: u,
79
+ success: s,
80
+ error: e,
81
+ execute: t.seed(m, u)
82
+ });
83
+ return yield* y(m, {
84
+ tenantId: u.tenantId,
85
+ tournamentId: u.tournamentId,
86
+ runKey: u.runKey
87
+ }, r);
88
+ });
89
+ }, x = (e, n, r, i, a) => d.suspend(() => {
90
+ let o = e?.workflows;
91
+ return !o || typeof o.start != "function" ? d.fail(t("unavailable")) : n.report(r, i, a).pipe(d.tap(() => d.tryPromise({
92
+ try: () => o.start("tournament.advance", {
93
+ tenantId: i.tenantId,
94
+ tournamentId: i.tournamentId,
95
+ runKey: a.reportId
96
+ }),
97
+ catch: () => t("unavailable")
98
+ })));
99
+ });
100
+ export { _ as leaderboardsTournamentSources, x as reportTournamentResult, v as tournamentAdvanceExecutor, b as tournamentStageExecutor };
@@ -0,0 +1,53 @@
1
+ import { Schema as e } from "effect";
2
+ // src/tournamentWorkflowSchemas.ts
3
+ var t = e.String.pipe(e.check(e.isMinLength(1)), e.check(e.isMaxLength(120)), e.check(e.makeFilter((e) => e.trim() === e && e.isWellFormed() && !/[\u0000-\u001f\u007f]/.test(e)))), n = e.Int.pipe(e.check(e.isBetween({
4
+ minimum: 0,
5
+ maximum: 864e13
6
+ }))), r = e.Struct({
7
+ tenantId: t,
8
+ tournamentId: t,
9
+ runKey: t
10
+ }), i = e.Struct({
11
+ ...r.fields,
12
+ format: e.Literals([
13
+ "single-elimination",
14
+ "double-elimination",
15
+ "round-robin",
16
+ "swiss"
17
+ ]),
18
+ ruleVersion: t
19
+ }), a = e.Struct({
20
+ phase: e.Literals([
21
+ "active",
22
+ "paused",
23
+ "complete",
24
+ "erasing",
25
+ "cancelled"
26
+ ]),
27
+ ready: e.Int.pipe(e.check(e.isBetween({
28
+ minimum: 0,
29
+ maximum: 127
30
+ }))),
31
+ completed: e.Int.pipe(e.check(e.isBetween({
32
+ minimum: 0,
33
+ maximum: 127
34
+ }))),
35
+ waiting: e.Int.pipe(e.check(e.isBetween({
36
+ minimum: 0,
37
+ maximum: 128
38
+ }))),
39
+ unresolvedDisputes: e.Int.pipe(e.check(e.isBetween({
40
+ minimum: 0,
41
+ maximum: 254
42
+ })))
43
+ }), o = e.Struct({
44
+ registrationOpensAt: n,
45
+ registrationClosesAt: n,
46
+ checkInOpensAt: n,
47
+ checkInClosesAt: n,
48
+ maxEntries: e.Int.pipe(e.check(e.isBetween({
49
+ minimum: 2,
50
+ maximum: 256
51
+ })))
52
+ }).pipe(e.check(e.makeFilter((e) => e.registrationOpensAt < e.registrationClosesAt && e.registrationClosesAt <= e.checkInOpensAt && e.checkInOpensAt < e.checkInClosesAt))), s = e.Struct({ notified: e.Boolean });
53
+ export { r as TournamentAdvancePayload, s as TournamentStageNotification, i as TournamentStagePayload, o as TournamentStageWindow, a as TournamentWorkflowSummary };
@@ -0,0 +1,32 @@
1
+ export declare interface BracketAdmissionDefinition {
2
+ readonly registrationOpensAt: number;
3
+ readonly registrationClosesAt: number;
4
+ readonly checkInOpensAt: number;
5
+ readonly checkInClosesAt: number;
6
+ readonly maxEntries: number;
7
+ }
8
+
9
+ /** Author-owned windows and bounded roster only; no schedule or price guessed. */
10
+ export declare const defineBracketAdmission: (input: BracketAdmissionDefinition) => BracketAdmissionDefinition;
11
+
12
+ /** Declarative entry/attempt budgets only. No price, prize or reward is invented. */
13
+ export declare const defineTournament: (input: TournamentDefinition) => TournamentDescriptor;
14
+
15
+ export declare interface TournamentDefinition {
16
+ readonly name: string;
17
+ readonly board: string;
18
+ readonly startsAt: number;
19
+ readonly endsAt: number;
20
+ readonly maxEntries: number;
21
+ readonly joinRequired?: boolean;
22
+ readonly attemptsPerEntry?: number;
23
+ }
24
+
25
+ export declare interface TournamentDescriptor extends Omit<TournamentDefinition, 'joinRequired' | 'attemptsPerEntry'> {
26
+ readonly kind: 'tournament';
27
+ readonly format: 'time-window';
28
+ readonly joinRequired: boolean;
29
+ readonly attemptsPerEntry: number;
30
+ }
31
+
32
+ export { }
@@ -0,0 +1,20 @@
1
+ import { leaderboardError as e } from "./errors.js";
2
+ import { leaderboardIdentifier as t } from "./leaderboard.js";
3
+ import { defineBracketAdmission as n } from "./bracketAdmissionPolicy.js";
4
+ // src/tournaments.ts
5
+ var r = (n) => {
6
+ let r = n?.attemptsPerEntry ?? 1, i = n?.joinRequired ?? !0;
7
+ if (!n || !t(n.name) || !t(n.board) || !Number.isSafeInteger(n.startsAt) || n.startsAt < 0 || !Number.isSafeInteger(n.endsAt) || n.endsAt > 864e13 || n.endsAt <= n.startsAt || !Number.isSafeInteger(n.maxEntries) || n.maxEntries < 1 || n.maxEntries > 1e6 || !Number.isSafeInteger(r) || r < 1 || r > 2147483647 || typeof i != "boolean") throw e("invalid_input");
8
+ return Object.freeze({
9
+ kind: "tournament",
10
+ format: "time-window",
11
+ name: n.name,
12
+ board: n.board,
13
+ startsAt: n.startsAt,
14
+ endsAt: n.endsAt,
15
+ maxEntries: n.maxEntries,
16
+ joinRequired: i,
17
+ attemptsPerEntry: r
18
+ });
19
+ };
20
+ export { n as defineBracketAdmission, r as defineTournament };
package/dist/web.d.ts ADDED
@@ -0,0 +1,175 @@
1
+ import { ActionState } from '@voltro/client';
2
+ import { Schema } from 'effect';
3
+ import { SubscriptionState } from '@voltro/client';
4
+
5
+ export declare interface LeaderboardRankedView extends LeaderboardRecord {
6
+ readonly rank: number;
7
+ }
8
+
9
+ declare type LeaderboardRecord = typeof LeaderboardRecordSchema.Type;
10
+
11
+ declare const LeaderboardRecordSchema: Schema.Struct<{
12
+ readonly id: Schema.String;
13
+ readonly board: Schema.String;
14
+ readonly periodId: Schema.String;
15
+ readonly ownerId: Schema.String;
16
+ readonly score: Schema.String;
17
+ readonly subscore: Schema.String;
18
+ readonly numSubmits: Schema.Number;
19
+ readonly metadata: Schema.$Record<Schema.String, Schema.Unknown>;
20
+ readonly updatedAt: Schema.Number;
21
+ readonly bucket: Schema.NullOr<Schema.String>;
22
+ }>;
23
+
24
+ export declare const useBracketAdmissionStatus: (tournamentId: string, apiName?: string) => SubscriptionState<{
25
+ readonly phase: "open" | "cancelled" | "erasing" | "sealed";
26
+ readonly entries: number;
27
+ readonly checkedIn: number;
28
+ readonly own: {
29
+ readonly ownerId: string;
30
+ readonly joinedAt: number;
31
+ readonly checkedInAt: number | null;
32
+ } | null;
33
+ } | null>;
34
+
35
+ export declare const useCheckInBracket: (apiName?: string) => ActionState<{
36
+ tournamentId: string;
37
+ }, {
38
+ readonly phase: "open" | "cancelled" | "erasing" | "sealed";
39
+ readonly entries: number;
40
+ readonly checkedIn: number;
41
+ readonly own: {
42
+ readonly ownerId: string;
43
+ readonly joinedAt: number;
44
+ readonly checkedInAt: number | null;
45
+ } | null;
46
+ }>;
47
+
48
+ export declare const useDisputeBracket: (apiName?: string) => ActionState<{
49
+ readonly tournamentId: string;
50
+ readonly matchId: string;
51
+ readonly disputeId: string;
52
+ readonly reason: "connection" | "result_mismatch" | "no_show";
53
+ }, {
54
+ readonly phase: "cancelled" | "complete" | "active" | "paused" | "erasing";
55
+ readonly ready: number;
56
+ readonly completed: number;
57
+ readonly waiting: number;
58
+ readonly unresolvedDisputes: number;
59
+ }>;
60
+
61
+ export declare const useJoinLeaderboardTournament: (apiName?: string) => ActionState<{
62
+ tournament: string;
63
+ }, {
64
+ readonly tournament: string;
65
+ readonly joinedAt: number;
66
+ readonly attemptsLeft: number;
67
+ }>;
68
+
69
+ export declare const useLeaderboardAround: (board: string, radius?: number, apiName?: string) => SubscriptionState<readonly LeaderboardRankedView[]>;
70
+
71
+ export declare const useLeaderboardHistory: (board: string, limit?: number, apiName?: string) => SubscriptionState<readonly {
72
+ readonly id: string;
73
+ readonly board: string;
74
+ readonly startsAt: number;
75
+ readonly endsAt: number | null;
76
+ readonly state: "closed" | "active" | "archived";
77
+ readonly summary: {
78
+ readonly participants: number;
79
+ readonly top: readonly {
80
+ readonly id: string;
81
+ readonly tenantId: string;
82
+ readonly board: string;
83
+ readonly periodId: string;
84
+ readonly ownerId: string;
85
+ readonly score: string;
86
+ readonly subscore: string;
87
+ readonly updatedAt: number;
88
+ readonly rank: number;
89
+ }[];
90
+ } | null;
91
+ }[]>;
92
+
93
+ export declare const useLeaderboardMe: (board: string, apiName?: string) => SubscriptionState<LeaderboardRankedView | null>;
94
+
95
+ export declare const useLeaderboardTop: (board: string, limit?: number, apiName?: string, scope?: "global" | "bucket") => SubscriptionState<readonly LeaderboardRankedView[]>;
96
+
97
+ export declare const useLeaderboardTournamentStatus: (tournament: string, apiName?: string) => SubscriptionState<{
98
+ readonly tournament: string;
99
+ readonly board: string;
100
+ readonly startsAt: number;
101
+ readonly endsAt: number;
102
+ readonly observedAt: number;
103
+ readonly joinRequired: boolean;
104
+ readonly state: "open" | "upcoming" | "ended";
105
+ readonly entry: {
106
+ readonly tournament: string;
107
+ readonly joinedAt: number;
108
+ readonly attemptsLeft: number;
109
+ } | null;
110
+ }>;
111
+
112
+ /** The caller's own bracket matches, live: opponent, Room once provisioned, result; `null`
113
+ * until the bracket exists. */
114
+ export declare const useMyBracketMatches: (tournamentId: string, apiName?: string) => SubscriptionState<{
115
+ readonly phase: "cancelled" | "complete" | "active" | "paused" | "erasing";
116
+ readonly champion: boolean;
117
+ readonly matches: readonly {
118
+ readonly matchId: string;
119
+ readonly round: number;
120
+ readonly status: "ready" | "empty" | "pending" | "complete" | "walkover" | "inactive";
121
+ readonly opponentId: string | null;
122
+ readonly roomId: string | null;
123
+ readonly won: boolean | null;
124
+ }[];
125
+ } | null>;
126
+
127
+ export declare const useMyLeaderboardRating: (pool: string, apiName?: string) => SubscriptionState<{
128
+ readonly pool: string;
129
+ readonly state: {
130
+ readonly algorithm: "elo";
131
+ readonly rating: number;
132
+ } | {
133
+ readonly algorithm: "openskill";
134
+ readonly mu: number;
135
+ readonly sigma: number;
136
+ } | {
137
+ readonly algorithm: "glicko2";
138
+ readonly rating: number;
139
+ readonly deviation: number;
140
+ readonly volatility: number;
141
+ };
142
+ readonly games: number;
143
+ readonly updatedAt: number | null;
144
+ }>;
145
+
146
+ export declare const useRegisterBracket: (apiName?: string) => ActionState<{
147
+ tournamentId: string;
148
+ }, {
149
+ readonly phase: "open" | "cancelled" | "erasing" | "sealed";
150
+ readonly entries: number;
151
+ readonly checkedIn: number;
152
+ readonly own: {
153
+ readonly ownerId: string;
154
+ readonly joinedAt: number;
155
+ readonly checkedInAt: number | null;
156
+ } | null;
157
+ }>;
158
+
159
+ /** The whole bracket, live: rounds, pairings, results and the champion (no Room IDs); `null`
160
+ * until the bracket exists. */
161
+ export declare const useTournamentBracket: (tournamentId: string, apiName?: string) => SubscriptionState<{
162
+ readonly phase: "cancelled" | "complete" | "active" | "paused" | "erasing";
163
+ readonly format: "single-elimination" | "double-elimination" | "round-robin" | "swiss";
164
+ readonly champion: string | null;
165
+ readonly matches: readonly {
166
+ readonly matchId: string;
167
+ readonly round: number;
168
+ readonly status: "ready" | "empty" | "pending" | "complete" | "walkover" | "inactive";
169
+ readonly left: string | null;
170
+ readonly right: string | null;
171
+ readonly winnerId: string | null;
172
+ }[];
173
+ } | null>;
174
+
175
+ export { }
package/dist/web.js ADDED
@@ -0,0 +1,16 @@
1
+ import { BASE_NAME as e } from "./rpc.js";
2
+ import { pluginTag as t } from "@voltro/protocol";
3
+ import { useAction as n, useSubscription as r } from "@voltro/client";
4
+ // src/web.ts
5
+ var i = (n, i = "app") => r(i, t(e, "myRating"), { pool: n }), a = (n, i = 10, a = "app", o = "global") => r(a, t(e, "top"), {
6
+ board: n,
7
+ limit: i,
8
+ scope: o
9
+ }), o = (n, i = "app") => r(i, t(e, "me"), { board: n }), s = (n, i = 5, a = "app") => r(a, t(e, "around"), {
10
+ board: n,
11
+ radius: i
12
+ }), c = (n, i = 12, a = "app") => r(a, t(e, "history"), {
13
+ board: n,
14
+ limit: i
15
+ }), l = (r = "app") => n(r, t(e, "joinTournament")), u = (n, i = "app") => r(i, t(e, "tournamentStatus"), { tournament: n }), d = (r = "app") => n(r, t(e, "registerBracket")), f = (r = "app") => n(r, t(e, "checkInBracket")), p = (n, i = "app") => r(i, t(e, "bracketStatus"), { tournamentId: n }), m = (r = "app") => n(r, t(e, "disputeBracket")), h = (n, i = "app") => r(i, t(e, "myBracketMatches"), { tournamentId: n }), g = (n, i = "app") => r(i, t(e, "bracketView"), { tournamentId: n });
16
+ export { p as useBracketAdmissionStatus, f as useCheckInBracket, m as useDisputeBracket, l as useJoinLeaderboardTournament, s as useLeaderboardAround, c as useLeaderboardHistory, o as useLeaderboardMe, a as useLeaderboardTop, u as useLeaderboardTournamentStatus, h as useMyBracketMatches, i as useMyLeaderboardRating, d as useRegisterBracket, g as useTournamentBracket };
package/package.json CHANGED
@@ -1,6 +1,115 @@
1
1
  {
2
2
  "name": "@voltro/plugin-leaderboards",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.88.0",
4
+ "description": "Exact leaderboard score arithmetic, bounded snapshot ranking and pure rating foundations",
5
+ "keywords": [
6
+ "voltro",
7
+ "typescript",
8
+ "framework"
9
+ ],
10
+ "license": "SEE LICENSE IN LICENSE",
11
+ "homepage": "https://voltro.dev",
12
+ "bugs": {
13
+ "email": "support@voltro.dev"
14
+ },
15
+ "author": {
16
+ "name": "Voltro UG",
17
+ "url": "https://voltro.dev"
18
+ },
19
+ "type": "module",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.ts",
23
+ "import": "./dist/index.js",
24
+ "default": "./dist/index.js"
25
+ },
26
+ "./package.json": "./package.json",
27
+ "./leaderboard": {
28
+ "types": "./dist/leaderboard.d.ts",
29
+ "import": "./dist/leaderboard.js",
30
+ "default": "./dist/leaderboard.js"
31
+ },
32
+ "./score": {
33
+ "types": "./dist/score.d.ts",
34
+ "import": "./dist/score.js",
35
+ "default": "./dist/score.js"
36
+ },
37
+ "./ranking": {
38
+ "types": "./dist/ranking.d.ts",
39
+ "import": "./dist/ranking.js",
40
+ "default": "./dist/ranking.js"
41
+ },
42
+ "./rating": {
43
+ "types": "./dist/rating.d.ts",
44
+ "import": "./dist/rating.js",
45
+ "default": "./dist/rating.js"
46
+ },
47
+ "./errors": {
48
+ "types": "./dist/errors.d.ts",
49
+ "import": "./dist/errors.js",
50
+ "default": "./dist/errors.js"
51
+ },
52
+ "./testing": {
53
+ "types": "./dist/testing.d.ts",
54
+ "import": "./dist/testing.js",
55
+ "default": "./dist/testing.js"
56
+ },
57
+ "./server": {
58
+ "types": "./dist/server.d.ts",
59
+ "import": "./dist/server.js",
60
+ "default": "./dist/server.js"
61
+ },
62
+ "./records": {
63
+ "types": "./dist/records.d.ts",
64
+ "import": "./dist/records.js",
65
+ "default": "./dist/records.js"
66
+ },
67
+ "./rpc": {
68
+ "types": "./dist/rpc.d.ts",
69
+ "import": "./dist/rpc.js",
70
+ "default": "./dist/rpc.js"
71
+ },
72
+ "./web": {
73
+ "types": "./dist/web.d.ts",
74
+ "import": "./dist/web.js",
75
+ "default": "./dist/web.js"
76
+ },
77
+ "./tournaments": {
78
+ "types": "./dist/tournaments.d.ts",
79
+ "import": "./dist/tournaments.js",
80
+ "default": "./dist/tournaments.js"
81
+ },
82
+ "./brackets": {
83
+ "types": "./dist/brackets.d.ts",
84
+ "import": "./dist/brackets.js",
85
+ "default": "./dist/brackets.js"
86
+ },
87
+ "./ports": {
88
+ "types": "./dist/ports.d.ts",
89
+ "import": "./dist/ports.js",
90
+ "default": "./dist/ports.js"
91
+ }
92
+ },
93
+ "main": "./dist/index.js",
94
+ "module": "./dist/index.js",
95
+ "types": "./dist/index.d.ts",
96
+ "sideEffects": false,
97
+ "engines": {
98
+ "node": ">=24.0.0"
99
+ },
100
+ "dependencies": {
101
+ "@voltro/workflow": "0.88.0",
102
+ "@voltro/database": "0.88.0",
103
+ "@voltro/protocol": "0.88.0",
104
+ "@voltro/client": "0.88.0",
105
+ "@voltro/plugin-licensing": "0.88.0",
106
+ "@voltro/runtime": "0.88.0"
107
+ },
108
+ "peerDependencies": {
109
+ "effect": "4.1.0",
110
+ "react": "^19.0.0"
111
+ },
112
+ "publishConfig": {
113
+ "access": "public"
114
+ }
115
+ }