@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
package/dist/plugin.js ADDED
@@ -0,0 +1,338 @@
1
+ import { LeaderboardError as e, leaderboardError as t } from "./errors.js";
2
+ import { LeaderboardRecordSchema as n } from "./records.js";
3
+ import { leaderboardTables as r } from "./tables.entity.js";
4
+ import { dataStoreLeaderboards as i, resolveLeaderboardStoreOptions as a } from "./service.js";
5
+ import { LeaderboardAdministration as o } from "./administration.js";
6
+ import { BASE_NAME as s, aroundDescriptor as c, bracketRpcClientImports as l, bracketStatusDescriptor as u, bracketViewDescriptor as d, checkInBracketDescriptor as f, disputeBracketDescriptor as p, historyDescriptor as m, joinTournamentDescriptor as h, leaderboardRpcClientImports as g, meDescriptor as _, myBracketMatchesDescriptor as ee, myRatingDescriptor as v, ratingRpcClientImports as y, registerBracketDescriptor as b, topDescriptor as x, tournamentCoordinationRpcClientImports as S, tournamentRpcClientImports as te, tournamentStatusDescriptor as ne } from "./rpc.js";
7
+ import { leaderboardsStatus as C } from "./inspect.js";
8
+ import { TournamentBracketPort as w } from "./bracketMatchPorts.js";
9
+ import { TournamentStagePort as T } from "./tournamentStagePorts.js";
10
+ import { BracketAdmissionPort as E } from "./ports.js";
11
+ import { bracketAdmissionsTable as D } from "./bracketAdmission.js";
12
+ import { makeBracketAdmissionInstallation as O } from "./bracketInstallation.js";
13
+ import { tournamentBracketsTable as k } from "./bracketMatches.js";
14
+ import { myBracketMatches as A, summarizeTournamentStatus as j, tournamentBracketView as M } from "./tournamentStage.js";
15
+ import { makeTournamentInstallation as N } from "./tournamentInstallation.js";
16
+ import { Effect as P, Layer as F, Schema as I } from "effect";
17
+ import { defineLicensedGamePlugin as L } from "@voltro/plugin-licensing/gate";
18
+ import { defineAction as R, systemSubject as z } from "@voltro/protocol";
19
+ //#region src/plugin.ts
20
+ var B = R({
21
+ name: "leaderboards.submit",
22
+ guards: [{ scope: "leaderboards:submit" }],
23
+ input: I.Struct({
24
+ tenantId: I.NonEmptyString,
25
+ board: I.NonEmptyString,
26
+ ownerId: I.NonEmptyString,
27
+ submissionId: I.NonEmptyString,
28
+ score: I.String,
29
+ subscore: I.optional(I.String),
30
+ metadata: I.Record(I.String, I.Unknown),
31
+ roomId: I.optional(I.NonEmptyString),
32
+ matchId: I.optional(I.NonEmptyString),
33
+ tournamentId: I.optional(I.NonEmptyString),
34
+ bucketSkill: I.optional(I.Number.pipe(I.check(I.isFinite()), I.check(I.isBetween({
35
+ minimum: -1e9,
36
+ maximum: 1e9
37
+ })))),
38
+ bucketCohort: I.optional(I.NonEmptyString)
39
+ }),
40
+ output: n,
41
+ error: e
42
+ }), V = Object.freeze({
43
+ slots: {
44
+ tiebreak: {
45
+ defaultExport: "defaultLeaderboardTiebreak",
46
+ fixtureExport: "tiebreakSystemFixture"
47
+ },
48
+ rankQuery: {
49
+ defaultExport: "defaultLeaderboardRankQuery",
50
+ fixtureExport: "rankQuerySystemFixture"
51
+ },
52
+ bucketAssign: {
53
+ defaultExport: "defaultLeaderboardBucketAssign",
54
+ fixtureExport: "bucketAssignSystemFixture"
55
+ },
56
+ periodSummary: {
57
+ defaultExport: "defaultPeriodSummary",
58
+ fixtureExport: "periodSummarySystemFixture"
59
+ },
60
+ validate: {
61
+ defaultExport: "acceptLeaderboardSubmission",
62
+ fixtureExport: "validateSystemFixture"
63
+ },
64
+ ratingRate: {
65
+ defaultExport: "defaultRatingRate",
66
+ fixtureExport: "ratingRateSystemFixture"
67
+ },
68
+ ratingPlacement: {
69
+ defaultExport: "defaultRatingPlacement",
70
+ fixtureExport: "ratingPlacementSystemFixture"
71
+ },
72
+ ratingDecay: {
73
+ defaultExport: "defaultRatingDecay",
74
+ fixtureExport: "ratingDecaySystemFixture"
75
+ }
76
+ },
77
+ ports: {
78
+ bracketAdmission: {
79
+ tagExport: "BracketAdmissionPort",
80
+ fakeLayerExport: "bracketAdmissionSystemFakeLayer",
81
+ fixtureExport: "bracketAdmissionPortSystemFixture"
82
+ },
83
+ tournamentBracket: {
84
+ tagExport: "TournamentBracketPort",
85
+ fakeLayerExport: "tournamentBracketSystemFakeLayer",
86
+ fixtureExport: "tournamentBracketPortSystemFixture"
87
+ },
88
+ tournamentStage: {
89
+ tagExport: "TournamentStagePort",
90
+ fakeLayerExport: "tournamentStageSystemFakeLayer",
91
+ fixtureExport: "tournamentStagePortSystemFixture"
92
+ }
93
+ },
94
+ events: []
95
+ }), H = (n) => {
96
+ let I = (e) => {
97
+ let r = Object.getOwnPropertyDescriptor(n, e);
98
+ if (r !== void 0 && (!r.enumerable || !("value" in r))) throw t("invalid_input");
99
+ return r?.value;
100
+ }, R = I("brackets"), V = I("tournamentCoordination"), H = a(n), U = O(R), W = N(V, U.service, U.configuration?.backend), G = [
101
+ ...r,
102
+ ...U.configuration?.backend === "sql" ? [D] : [],
103
+ ...W.configuration?.backend === "sql" ? [k] : []
104
+ ], K, q, J = (e) => P.suspend(() => q === void 0 ? P.fail(t("unavailable")) : e(q)), Y = (e, n, r) => {
105
+ try {
106
+ let t = structuredClone(e), i = structuredClone(n);
107
+ return J((e) => r(e, t, i));
108
+ } catch {
109
+ return P.fail(t("invalid_input"));
110
+ }
111
+ }, X = {
112
+ assignLeague: (e, t) => Y(e, t, (e, t, n) => e.assignLeague(t, n)),
113
+ myLeague: (e, t) => Y(e, t, (e, t, n) => e.myLeague(t, n)),
114
+ rollLeague: (e, t) => Y(e, t, (e, t, n) => e.rollLeague(t, n)),
115
+ leagueTenants: (e, t) => Y(e, t, (e, t, n) => e.leagueTenants(t, n)),
116
+ prepareRatingMatch: (e, t) => Y(e, t, (e, t, n) => e.prepareRatingMatch(t, n)),
117
+ applyRatingMatch: (e, t) => Y(e, t, (e, t, n) => e.applyRatingMatch(t, n)),
118
+ myRating: (e, t) => Y(e, t, (e, t, n) => e.myRating(t, n)),
119
+ pendingRatingMatches: (e, t, n, r) => Y(e, {
120
+ tenantId: t,
121
+ pool: n,
122
+ limit: r
123
+ }, (e, t, n) => e.pendingRatingMatches(t, n.tenantId, n.pool, n.limit)),
124
+ submit: (e, t) => Y(e, t, (e, t, n) => e.submit(t, n)),
125
+ top: (e, t, n, r) => Y(e, {
126
+ board: t,
127
+ limit: n,
128
+ scope: r
129
+ }, (e, t, n) => e.top(t, n.board, n.limit, n.scope)),
130
+ me: (e, t) => Y(e, t, (e, t, n) => e.me(t, n)),
131
+ around: (e, t, n) => Y(e, {
132
+ board: t,
133
+ radius: n
134
+ }, (e, t, n) => e.around(t, n.board, n.radius)),
135
+ rollPeriod: (e, t) => Y(e, t, (e, t, n) => e.rollPeriod(t, n)),
136
+ history: (e, t, n) => Y(e, {
137
+ board: t,
138
+ limit: n
139
+ }, (e, t, n) => e.history(t, n.board, n.limit)),
140
+ joinTournament: (e, t) => Y(e, t, (e, t, n) => e.joinTournament(t, n)),
141
+ tournamentStatus: (e, t) => Y(e, t, (e, t, n) => e.tournamentStatus(t, n)),
142
+ closeTournament: (e, t) => Y(e, t, (e, t, n) => e.closeTournament(t, n))
143
+ }, Z = [
144
+ {
145
+ ...v,
146
+ execute: (e, t) => X.myRating(t.request.subject, e.pool)
147
+ },
148
+ {
149
+ ...B,
150
+ execute: (e, t) => X.submit(t.request.subject, e)
151
+ },
152
+ {
153
+ ...x,
154
+ execute: (e, t) => X.top(t.request.subject, e.board, e.limit, e.scope)
155
+ },
156
+ {
157
+ ..._,
158
+ execute: (e, t) => X.me(t.request.subject, e.board)
159
+ },
160
+ {
161
+ ...c,
162
+ execute: (e, t) => X.around(t.request.subject, e.board, e.radius)
163
+ },
164
+ {
165
+ ...m,
166
+ execute: (e, t) => X.history(t.request.subject, e.board, e.limit)
167
+ },
168
+ {
169
+ ...h,
170
+ execute: (e, t) => X.joinTournament(t.request.subject, e.tournament)
171
+ },
172
+ {
173
+ ...ne,
174
+ execute: (e, t) => X.tournamentStatus(t.request.subject, e.tournament)
175
+ }
176
+ ], Q = (e, n, r) => e.type !== "user" || e.id === null || e.tenantId === null ? P.fail(t("forbidden")) : U.service[r](e, {
177
+ tenantId: e.tenantId,
178
+ tournamentId: n
179
+ }), $ = (e) => e.pipe(P.catchTag("LeaderboardError", (e) => e.code === "not_found" ? P.succeed(null) : P.fail(e))), re = U.configuration === null ? [] : [
180
+ {
181
+ ...b,
182
+ execute: (e, t) => Q(t.request.subject, e.tournamentId, "register")
183
+ },
184
+ {
185
+ ...f,
186
+ execute: (e, t) => Q(t.request.subject, e.tournamentId, "checkIn")
187
+ },
188
+ {
189
+ ...u,
190
+ source: [...U.sources],
191
+ execute: (e, t) => $(Q(t.request.subject, e.tournamentId, "status"))
192
+ }
193
+ ];
194
+ return {
195
+ ...L({
196
+ name: s,
197
+ description: "SQL-owned service scores with bounded reactive rank views.",
198
+ permissions: ["store:write", "inspect:read"],
199
+ ...W.sourceRoot === void 0 ? {} : { sources: { tournaments: W.sourceRoot } },
200
+ routes: [
201
+ ...Z,
202
+ ...re,
203
+ ...W.configuration === null ? [] : [
204
+ {
205
+ ...p,
206
+ execute: (e, n) => {
207
+ let r = n.request.subject;
208
+ return r.type !== "user" || r.id === null || r.tenantId === null ? P.fail(t("forbidden")) : W.service.dispute(r, {
209
+ tenantId: r.tenantId,
210
+ tournamentId: e.tournamentId
211
+ }, {
212
+ matchId: e.matchId,
213
+ disputeId: e.disputeId,
214
+ reason: e.reason
215
+ }).pipe(P.flatMap((e) => P.try({
216
+ try: () => j(e),
217
+ catch: () => t("unavailable")
218
+ })));
219
+ }
220
+ },
221
+ {
222
+ ...ee,
223
+ source: [...W.sources],
224
+ execute: (e, n) => {
225
+ let r = n.request.subject;
226
+ return r.type !== "user" || r.id === null || r.tenantId === null ? P.fail(t("forbidden")) : $(W.service.status(z("leaderboards.myBracketMatches"), {
227
+ tenantId: r.tenantId,
228
+ tournamentId: e.tournamentId
229
+ }).pipe(P.map((e) => A(e, r.id))));
230
+ }
231
+ },
232
+ {
233
+ ...d,
234
+ source: [...W.sources],
235
+ execute: (e, n) => {
236
+ let r = n.request.subject;
237
+ return r.type !== "user" || r.id === null || r.tenantId === null ? P.fail(t("forbidden")) : $(W.service.status(z("leaderboards.bracketView"), {
238
+ tenantId: r.tenantId,
239
+ tournamentId: e.tournamentId
240
+ }).pipe(P.map(M)));
241
+ }
242
+ }
243
+ ]
244
+ ],
245
+ rpcClientDescriptors: [
246
+ ...g,
247
+ ...te,
248
+ ...y,
249
+ ...U.configuration === null ? [] : l,
250
+ ...W.configuration === null ? [] : S
251
+ ],
252
+ inspectEndpoints: [{
253
+ method: "GET",
254
+ path: "/status",
255
+ scope: "process",
256
+ description: "Captured board configuration and binding; no owner, score or submission enumeration.",
257
+ handler: () => P.try({
258
+ try: () => ({
259
+ kind: "json",
260
+ data: C(H, q !== void 0, (H.now ?? Date.now)(), U.configuration === null ? null : {
261
+ ...U.configuration,
262
+ bound: U.isBound()
263
+ }, W.configuration === null ? null : {
264
+ ...W.configuration,
265
+ bound: W.isBound(),
266
+ stageBound: W.isStageBound()
267
+ })
268
+ }),
269
+ catch: () => t("unavailable")
270
+ })
271
+ }],
272
+ services: F.mergeAll(F.succeed(o, X), U.configuration === null ? F.empty : F.succeed(E, U.service), W.configuration === null ? F.empty : F.succeed(w, W.service), W.configuration?.stage == null ? F.empty : F.succeed(T, W.stageService)),
273
+ errorSchemas: [{
274
+ schema: e,
275
+ import: {
276
+ module: "@voltro/plugin-leaderboards/errors",
277
+ name: "LeaderboardError"
278
+ }
279
+ }],
280
+ extendSchema: { tables: G },
281
+ subjectPrivacy: {
282
+ tables: G.map((e) => e.tableName),
283
+ kinds: ["user", "guest"]
284
+ },
285
+ bindDataStore: (e) => {
286
+ if (K !== void 0 && K !== e) throw Error("Leaderboard plugin instance cannot cross app stores");
287
+ if (K === e && q !== void 0) return;
288
+ let t = i(e, H);
289
+ U.bind(e), W.bind(e, U.boundService()), K = e, q = t;
290
+ },
291
+ onExport: (e, t) => {
292
+ let n = {
293
+ subjectId: e,
294
+ tenantId: t.tenantId,
295
+ kind: t.kind
296
+ }, r = J((e) => e.exportSubject(n)), i = {
297
+ requestId: t.requestId,
298
+ target: n
299
+ }, a = U.configuration === null ? r : U.service.exportSubject(i).pipe(P.flatMap((e) => r.pipe(P.map((t) => ({
300
+ ...t,
301
+ bracketAdmissions: e
302
+ }))))), o = W.configuration === null ? a : W.service.exportSubject(i).pipe(P.flatMap((e) => a.pipe(P.map((t) => ({
303
+ ...t,
304
+ tournamentBrackets: e
305
+ })))));
306
+ return W.configuration?.stage == null ? o : W.exportStageSubject(i).pipe(P.flatMap((e) => o.pipe(P.map((t) => ({
307
+ ...t,
308
+ tournamentStages: e
309
+ })))));
310
+ },
311
+ onErase: (e, t) => {
312
+ let n = {
313
+ subjectId: e,
314
+ tenantId: t.tenantId,
315
+ kind: t.kind
316
+ }, r = J((e) => e.eraseSubject(n)), i = {
317
+ requestId: t.requestId,
318
+ target: n
319
+ }, a = U.configuration === null ? r : U.service.eraseSubject(i).pipe(P.flatMap(() => r)), o = W.configuration === null ? a : W.service.eraseSubject(i).pipe(P.flatMap(() => a));
320
+ return W.configuration?.stage == null ? o : W.eraseStageSubject(i).pipe(P.flatMap(() => o));
321
+ },
322
+ onDeactivate: () => P.sync(() => {
323
+ q = void 0, W.close(), U.close();
324
+ }).pipe(P.flatMap(() => P.all([W.awaitIdle(), U.deactivate()], {
325
+ concurrency: "unbounded",
326
+ discard: !0
327
+ })))
328
+ }),
329
+ leaderboards: Object.freeze({
330
+ service: () => X,
331
+ brackets: () => U.service,
332
+ tournaments: () => W.service,
333
+ tournamentStages: () => W.stageService
334
+ })
335
+ };
336
+ };
337
+ //#endregion
338
+ export { H as leaderboardsPlugin, V as leaderboardsSystemContract };
@@ -0,0 +1,67 @@
1
+ import { LeaderboardError as e, leaderboardError as t } from "./errors.js";
2
+ import { captureLeaderboardJson as n } from "./json.js";
3
+ import { Effect as r, Schema as i } from "effect";
4
+ import { Subject as a } from "@voltro/protocol";
5
+ //#region src/portLifetime.ts
6
+ function o() {
7
+ throw t("invalid_input");
8
+ }
9
+ var s = (e, t, n = []) => {
10
+ (!e || Object.getPrototypeOf(e) !== Object.prototype || Object.getOwnPropertySymbols(e).length) && o();
11
+ let r = Object.getOwnPropertyDescriptors(e);
12
+ (Object.entries(r).some(([e, r]) => ![...t, ...n].includes(e) || !r.enumerable || !("value" in r)) || t.some((e) => !Object.hasOwn(r, e))) && o();
13
+ }, c = (e, t) => {
14
+ (!e || typeof e != "object") && o();
15
+ let n = t.map((t) => {
16
+ let n = Object.getOwnPropertyDescriptor(e, t);
17
+ return (!n || !("value" in n) || typeof n.value != "function") && o(), [t, n.value.bind(e)];
18
+ });
19
+ return Object.freeze(Object.fromEntries(n));
20
+ }, l = (e) => ((typeof e != "string" || !/^[a-zA-Z0-9][a-zA-Z0-9:._-]{0,119}$/.test(e) || e.startsWith("voltro:")) && o(), e), u = (e) => {
21
+ let t = n(e, 4096);
22
+ return (!Array.isArray(t) || t.length < 1 || t.length > 32 || new Set(t).size !== t.length || t.some((e) => typeof e != "string" || !/^[a-zA-Z_][a-zA-Z0-9_]{0,119}$/.test(e))) && o(), Object.freeze(t);
23
+ }, d = (e) => i.decodeUnknownSync(i.toType(a))(structuredClone(e)), f = (e) => i.decodeUnknownSync(i.Struct({
24
+ requestId: i.NonEmptyString,
25
+ target: i.Struct({
26
+ subjectId: i.NonEmptyString,
27
+ tenantId: i.NullOr(i.NonEmptyString),
28
+ kind: i.Literals(["user", "guest"])
29
+ })
30
+ }))(n(e, 2048), { onExcessProperty: "error" }), p = () => {
31
+ let n, i = !1, a = 0, o = /* @__PURE__ */ new Set(), s = () => {
32
+ if (a--, a === 0) for (let e of [...o]) e();
33
+ };
34
+ return Object.freeze({
35
+ call: (o) => {
36
+ let c;
37
+ try {
38
+ c = o();
39
+ } catch (n) {
40
+ return r.fail(n instanceof e ? n : t("invalid_input"));
41
+ }
42
+ return r.acquireUseRelease(r.suspend(() => i || n === void 0 ? r.fail(t("unavailable")) : (a++, r.succeed(n))), (e) => r.suspend(() => c(e)), () => r.sync(s)).pipe(r.uninterruptible);
43
+ },
44
+ isBound: () => n !== void 0 && !i,
45
+ bind: (e) => {
46
+ if (i || n !== void 0) throw t("unavailable");
47
+ n = e;
48
+ },
49
+ close: () => {
50
+ i = !0, n = void 0;
51
+ },
52
+ awaitIdle: () => r.callback((e) => {
53
+ if (a === 0) {
54
+ e(r.void);
55
+ return;
56
+ }
57
+ let t = () => {
58
+ o.delete(t), e(r.void);
59
+ };
60
+ return o.add(t), r.sync(() => {
61
+ o.delete(t);
62
+ });
63
+ })
64
+ });
65
+ };
66
+ //#endregion
67
+ export { s as assertPortBindingKeys, c as capturePortMethods, f as capturePortPrivacyRequest, u as capturePortSources, d as capturePortSubject, l as capturePortVersion, o as invalidPortBinding, p as makeLeaderboardPortLifetime };
@@ -0,0 +1,294 @@
1
+ import { Context } from 'effect';
2
+ import { Effect } from 'effect';
3
+ import { PluginPrivacyRequest } from '@voltro/protocol';
4
+ import { Schema } from 'effect';
5
+ import { Subject } from '@voltro/protocol';
6
+ import { YieldableError } from 'effect/Cause';
7
+
8
+ declare interface AdmissionState {
9
+ readonly version: 1;
10
+ readonly definition: BracketAdmissionDefinition;
11
+ readonly phase: 'open' | 'sealed' | 'erasing' | 'cancelled';
12
+ readonly entries: ReadonlyArray<BracketAdmissionEntry>;
13
+ readonly eraseRequest: string | null;
14
+ readonly eraseTarget: string | null;
15
+ }
16
+
17
+ declare interface BracketAdmissionDefinition {
18
+ readonly registrationOpensAt: number;
19
+ readonly registrationClosesAt: number;
20
+ readonly checkInOpensAt: number;
21
+ readonly checkInClosesAt: number;
22
+ readonly maxEntries: number;
23
+ }
24
+
25
+ declare interface BracketAdmissionEntrants {
26
+ readonly phase: AdmissionState['phase'];
27
+ readonly entries: ReadonlyArray<BracketAdmissionEntry>;
28
+ }
29
+
30
+ declare interface BracketAdmissionEntry {
31
+ readonly ownerId: string;
32
+ readonly joinedAt: number;
33
+ readonly checkedInAt: number | null;
34
+ }
35
+
36
+ declare interface BracketAdmissionExport extends BracketAdmissionEntry {
37
+ readonly tournamentId: string;
38
+ readonly phase: BracketAdmissionStatus['phase'];
39
+ }
40
+
41
+ /** Browser-safe Tag; consumers depend on admission, never a SQL adapter. */
42
+ export declare class BracketAdmissionPort extends BracketAdmissionPort_base {
43
+ }
44
+
45
+ declare const BracketAdmissionPort_base: Context.ServiceClass<BracketAdmissionPort, "@voltro/BracketAdmissionPort", {
46
+ create(subject: Subject, scope: BracketAdmissionScope, definition: BracketAdmissionDefinition): Effect.Effect<void, LeaderboardError>;
47
+ register(subject: Subject, scope: BracketAdmissionScope): Effect.Effect<BracketAdmissionStatus, LeaderboardError>;
48
+ checkIn(subject: Subject, scope: BracketAdmissionScope): Effect.Effect<BracketAdmissionStatus, LeaderboardError>;
49
+ status(subject: Subject, scope: BracketAdmissionScope): Effect.Effect<BracketAdmissionStatus, LeaderboardError>;
50
+ seal(subject: Subject, scope: BracketAdmissionScope): Effect.Effect<BracketAdmissionRoster, LeaderboardError>;
51
+ /** Trusted read of every registration with its check-in, for notifications. */
52
+ entrants(subject: Subject, scope: BracketAdmissionScope): Effect.Effect<BracketAdmissionEntrants, LeaderboardError>;
53
+ exportSubject(request: PluginPrivacyRequest): Effect.Effect<ReadonlyArray<BracketAdmissionExport>, LeaderboardError>;
54
+ eraseSubject(request: PluginPrivacyRequest): Effect.Effect<void, LeaderboardError>;
55
+ }>;
56
+
57
+ declare interface BracketAdmissionRoster {
58
+ readonly checkedIn: ReadonlyArray<string>;
59
+ readonly noShows: ReadonlyArray<string>;
60
+ }
61
+
62
+ declare interface BracketAdmissionScope {
63
+ readonly tenantId: string;
64
+ readonly tournamentId: string;
65
+ }
66
+
67
+ export declare type BracketAdmissionService = Context.Service.Shape<typeof BracketAdmissionPort>;
68
+
69
+ declare interface BracketAdmissionStatus {
70
+ readonly phase: AdmissionState['phase'];
71
+ readonly entries: number;
72
+ readonly checkedIn: number;
73
+ readonly own: BracketAdmissionEntry | null;
74
+ }
75
+
76
+ declare interface BracketMatchObservation {
77
+ readonly id: string;
78
+ readonly round: number;
79
+ readonly left: string | null;
80
+ readonly right: string | null;
81
+ readonly status: 'pending' | 'ready' | 'walkover' | 'complete';
82
+ readonly winnerId: string | null;
83
+ }
84
+
85
+ declare interface BracketProgress {
86
+ readonly matches: ReadonlyArray<BracketMatchObservation>;
87
+ readonly champion: string | null;
88
+ }
89
+
90
+ declare type BracketSlot = {
91
+ readonly kind: 'entrant';
92
+ readonly entrantId: string;
93
+ } | {
94
+ readonly kind: 'winner';
95
+ readonly matchId: string;
96
+ } | {
97
+ readonly kind: 'bye';
98
+ };
99
+
100
+ declare interface BracketState extends TournamentBracketDefinition {
101
+ readonly version: 1;
102
+ readonly phase: 'active' | 'paused' | 'complete' | 'erasing' | 'cancelled';
103
+ readonly withdrawn: boolean;
104
+ readonly provisions: ReadonlyArray<Provision>;
105
+ readonly results: ReadonlyArray<Result>;
106
+ readonly disputes: ReadonlyArray<Dispute>;
107
+ readonly settlement: {
108
+ readonly requestId: string;
109
+ readonly winnerId: string;
110
+ readonly complete: boolean;
111
+ } | null;
112
+ readonly erase: {
113
+ readonly requestId: string;
114
+ readonly targetDigest: string;
115
+ } | null;
116
+ }
117
+
118
+ declare interface Dispute extends TournamentDisputeIntent {
119
+ readonly ownerId: string;
120
+ readonly resolutionId: string | null;
121
+ readonly winnerId: string | null;
122
+ }
123
+
124
+ declare interface DoubleEliminationMatch {
125
+ readonly id: string;
126
+ readonly stage: 'winners' | 'losers' | 'final' | 'reset';
127
+ readonly round: number;
128
+ readonly left: DoubleEliminationSlot;
129
+ readonly right: DoubleEliminationSlot;
130
+ }
131
+
132
+ declare interface DoubleEliminationObservation extends Omit<BracketMatchObservation, 'status'> {
133
+ readonly stage: DoubleEliminationMatch['stage'];
134
+ readonly status: BracketMatchObservation['status'] | 'empty' | 'inactive';
135
+ readonly loserId: string | null;
136
+ }
137
+
138
+ declare interface DoubleEliminationProgress {
139
+ readonly matches: ReadonlyArray<DoubleEliminationObservation>;
140
+ readonly champion: string | null;
141
+ readonly losses: ReadonlyArray<{
142
+ readonly entrantId: string;
143
+ readonly losses: number;
144
+ }>;
145
+ }
146
+
147
+ declare type DoubleEliminationSlot = BracketSlot | {
148
+ readonly kind: 'loser';
149
+ readonly matchId: string;
150
+ };
151
+
152
+ declare class LeaderboardError extends LeaderboardError_base {
153
+ }
154
+
155
+ declare const LeaderboardError_base: Schema.Class<LeaderboardError, Schema.TaggedStruct<"LeaderboardError", {
156
+ readonly code: Schema.Literals<readonly ["invalid_board", "invalid_score", "invalid_record", "invalid_input", "overflow", "unavailable", "forbidden", "not_found", "conflict", "erased", "limit_exceeded"]>;
157
+ }>, YieldableError>;
158
+
159
+ declare interface Provision extends TournamentRoomWork {
160
+ readonly roomId: string | null;
161
+ readonly epoch: number | null;
162
+ }
163
+
164
+ declare interface Result extends TournamentResultReport {
165
+ readonly reportedWinnerId: string;
166
+ }
167
+
168
+ declare interface TournamentBracketDefinition {
169
+ readonly format: TournamentBracketFormat;
170
+ readonly entrants: ReadonlyArray<TournamentEntrant>;
171
+ /** Swiss only: rounds to play (default: enough to separate a winner, ceil(log2 entrants)). */
172
+ readonly rounds?: number | undefined;
173
+ /** Immutable application prize/Room/no-show policy identity, not a price. */
174
+ readonly ruleVersion: string;
175
+ }
176
+
177
+ declare interface TournamentBracketExport {
178
+ readonly tournamentId: string;
179
+ readonly phase: BracketState['phase'];
180
+ readonly seed: number;
181
+ readonly matches: ReadonlyArray<{
182
+ readonly matchId: string;
183
+ readonly roomId: string;
184
+ readonly won: boolean;
185
+ }>;
186
+ readonly disputes: ReadonlyArray<TournamentDisputeIntent & {
187
+ readonly resolutionId: string | null;
188
+ }>;
189
+ }
190
+
191
+ declare type TournamentBracketFormat = 'single-elimination' | 'double-elimination' | 'round-robin' | 'swiss';
192
+
193
+ /** A bracket match with the Room it plays in, once one was provisioned. */
194
+ declare type TournamentBracketMatch = (BracketProgress['matches'][number] | DoubleEliminationProgress['matches'][number]) & {
195
+ readonly roomId: string | null;
196
+ };
197
+
198
+ /** Trusted server coordinator contract; SQL types are erased imports only. */
199
+ export declare class TournamentBracketPort extends TournamentBracketPort_base {
200
+ }
201
+
202
+ declare const TournamentBracketPort_base: Context.ServiceClass<TournamentBracketPort, "@voltro/TournamentBracketPort", {
203
+ create(subject: Subject, scope: BracketAdmissionScope, definition: TournamentBracketDefinition): Effect.Effect<TournamentBracketStatus, LeaderboardError>;
204
+ report(subject: Subject, scope: BracketAdmissionScope, report: TournamentResultReport): Effect.Effect<TournamentBracketStatus, LeaderboardError>;
205
+ dispute(subject: Subject, scope: BracketAdmissionScope, intent: TournamentDisputeIntent): Effect.Effect<TournamentBracketStatus, LeaderboardError>;
206
+ resolve(subject: Subject, scope: BracketAdmissionScope, intent: TournamentDisputeResolution): Effect.Effect<TournamentBracketStatus, LeaderboardError>;
207
+ advance(subject: Subject, scope: BracketAdmissionScope): Effect.Effect<TournamentBracketStatus, LeaderboardError>;
208
+ status(subject: Subject, scope: BracketAdmissionScope): Effect.Effect<TournamentBracketStatus, LeaderboardError>;
209
+ exportSubject(request: PluginPrivacyRequest): Effect.Effect<ReadonlyArray<TournamentBracketExport>, LeaderboardError>;
210
+ eraseSubject(request: PluginPrivacyRequest): Effect.Effect<void, LeaderboardError>;
211
+ }>;
212
+
213
+ export declare type TournamentBracketService = Context.Service.Shape<typeof TournamentBracketPort>;
214
+
215
+ declare interface TournamentBracketStatus {
216
+ readonly phase: BracketState['phase'];
217
+ readonly format: TournamentBracketFormat;
218
+ readonly champion: string | null;
219
+ readonly matches: ReadonlyArray<TournamentBracketMatch>;
220
+ readonly unresolvedDisputes: number;
221
+ }
222
+
223
+ declare interface TournamentDisputeIntent {
224
+ readonly disputeId: string;
225
+ readonly matchId: string;
226
+ readonly reason: 'result_mismatch' | 'no_show' | 'connection';
227
+ }
228
+
229
+ declare interface TournamentDisputeResolution {
230
+ readonly disputeId: string;
231
+ readonly resolutionId: string;
232
+ readonly winnerId: string;
233
+ }
234
+
235
+ declare interface TournamentEntrant {
236
+ readonly entrantId: string;
237
+ readonly seed: number;
238
+ }
239
+
240
+ declare interface TournamentResultReport extends TournamentRoomReceipt {
241
+ readonly matchId: string;
242
+ readonly reportId: string;
243
+ readonly winnerId: string;
244
+ }
245
+
246
+ declare interface TournamentRoomReceipt {
247
+ readonly roomId: string;
248
+ readonly epoch: number;
249
+ }
250
+
251
+ declare interface TournamentRoomWork extends BracketAdmissionScope {
252
+ readonly matchId: string;
253
+ readonly requestId: string;
254
+ readonly left: string;
255
+ readonly right: string;
256
+ readonly ruleVersion: string;
257
+ }
258
+
259
+ declare const TournamentStagePayload: Schema.Struct<{
260
+ readonly format: Schema.Literals<readonly ["single-elimination", "double-elimination", "round-robin", "swiss"]>;
261
+ readonly ruleVersion: Schema.String;
262
+ readonly tenantId: Schema.String;
263
+ readonly tournamentId: Schema.String;
264
+ readonly runKey: Schema.String;
265
+ }>;
266
+
267
+ declare type TournamentStagePayload = typeof TournamentStagePayload.Type;
268
+
269
+ export declare class TournamentStagePort extends TournamentStagePort_base {
270
+ }
271
+
272
+ declare const TournamentStagePort_base: Context.ServiceClass<TournamentStagePort, "@voltro/TournamentStagePort", {
273
+ window(subject: Subject, payload: TournamentStagePayload): Effect.Effect<BracketAdmissionDefinition, LeaderboardError>;
274
+ notify(subject: Subject, payload: TournamentStagePayload, phase: "registration" | "check-in"): Effect.Effect<{
275
+ readonly notified: boolean;
276
+ }, LeaderboardError>;
277
+ seed(subject: Subject, payload: TournamentStagePayload): Effect.Effect<TournamentWorkflowSummary, LeaderboardError>;
278
+ }>;
279
+
280
+ export declare type TournamentStageService = Context.Service.Shape<typeof TournamentStagePort>;
281
+
282
+ /** Counts only: no roster/winner/ratings/Room credentials in workflow journals. */
283
+ declare const TournamentWorkflowSummary: Schema.Struct<{
284
+ readonly phase: Schema.Literals<readonly ["active", "paused", "complete", "erasing", "cancelled"]>;
285
+ readonly ready: Schema.Int;
286
+ readonly completed: Schema.Int;
287
+ /** Advance work still open: ready matches without a Room, plus the champion's settlement. */
288
+ readonly waiting: Schema.Int;
289
+ readonly unresolvedDisputes: Schema.Int;
290
+ }>;
291
+
292
+ declare type TournamentWorkflowSummary = typeof TournamentWorkflowSummary.Type;
293
+
294
+ export { }