xpt-shared-types 1.11.0 → 1.12.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.
@@ -6,3 +6,4 @@ export * from './lobby';
6
6
  export * from './tournamentEntry';
7
7
  export * from './tournamentInvite';
8
8
  export * from './tournamentJoinRequest';
9
+ export * from './tournamentStaff';
@@ -22,3 +22,4 @@ __exportStar(require("./lobby"), exports);
22
22
  __exportStar(require("./tournamentEntry"), exports);
23
23
  __exportStar(require("./tournamentInvite"), exports);
24
24
  __exportStar(require("./tournamentJoinRequest"), exports);
25
+ __exportStar(require("./tournamentStaff"), exports);
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Tournament staff — who runs a tournament and what each of them may do.
3
+ *
4
+ * Three roles. The **owner** is the tournament's `users_permissions_user`
5
+ * and never a row; **admin** and **moderator** are `tournament-role` rows
6
+ * (the `TournamentRoleRole` enum is generated from the schema). A row with
7
+ * no role value is read as a moderator, the least the row can mean.
8
+ *
9
+ * The capability table is the contract between the server's guards and the
10
+ * client's buttons: both resolve the viewer's role with
11
+ * `resolveTournamentStaffRole` and ask `canTournamentStaff`, so a control is
12
+ * never offered for a request the server would refuse. Money and the
13
+ * irreversible lifecycle steps stay with the owner; admins run the event;
14
+ * moderators run matches.
15
+ */
16
+ import type { TournamentRoleRole } from '../generated/enums';
17
+ export type TournamentStaffRole = 'owner' | TournamentRoleRole;
18
+ /** The roles the owner can hand out — everything but their own. */
19
+ export type TournamentAppointedRole = TournamentRoleRole;
20
+ export declare const APPOINTABLE_STAFF_ROLES: readonly ["admin", "moderator"];
21
+ export type TournamentStaffAction =
22
+ /** Appoint, re-role or remove admins and moderators. */
23
+ 'manageStaff'
24
+ /** `PUT /tournaments/:id` — info, rules, stages, links. */
25
+ | 'editTournament'
26
+ /** Draft → open; debits the prize pool. */
27
+ | 'publish'
28
+ /** Drafts only. */
29
+ | 'delete'
30
+ /** The forward transitions: checkIn, seeding, live, completed. */
31
+ | 'changeStatus'
32
+ /** Refunds every entrant. */
33
+ | 'cancel' | 'distributePrizes' | 'manageInvites' | 'reviewJoinRequests' | 'resolveDispute'
34
+ /** Seed, initialise the bracket, place players in slots. */
35
+ | 'seedBracket'
36
+ /** Record scores, start matches, edit match details. */
37
+ | 'manageMatches'
38
+ /** Watch any lobby and post as staff in its chat. */
39
+ | 'enterLobby';
40
+ export declare const TOURNAMENT_STAFF_CAPABILITIES: Record<TournamentStaffAction, readonly TournamentStaffRole[]>;
41
+ export declare const TOURNAMENT_STAFF_ROLE_LABELS: Record<TournamentStaffRole, string>;
42
+ export declare const tournamentStaffRoleLabel: (role: string | null | undefined) => string;
43
+ /** Only what the resolver reads; both apps' tournament shapes satisfy it. */
44
+ export interface TournamentStaffLike {
45
+ users_permissions_user?: {
46
+ id?: number | string | null;
47
+ } | null;
48
+ tournament_roles?: Array<{
49
+ role?: string | null;
50
+ users_permissions_user?: {
51
+ id?: number | string | null;
52
+ } | null;
53
+ }> | null;
54
+ }
55
+ /** An appointed row's role, defaulting an unset value to the least it can mean. */
56
+ export declare const appointedRoleOf: (role: string | null | undefined) => TournamentAppointedRole;
57
+ /**
58
+ * The viewer's staff role on a tournament, or null for everyone else. The
59
+ * owner wins over any row they might also hold.
60
+ */
61
+ export declare function resolveTournamentStaffRole(tournament: TournamentStaffLike | null | undefined, userId: number | string | null | undefined): TournamentStaffRole | null;
62
+ export declare const canTournamentStaff: (role: TournamentStaffRole | null | undefined, action: TournamentStaffAction) => boolean;
63
+ /** `canTournamentStaff` for a viewer, resolving their role first. */
64
+ export declare const tournamentStaffCan: (tournament: TournamentStaffLike | null | undefined, userId: number | string | null | undefined, action: TournamentStaffAction) => boolean;
65
+ /** Staff can be changed until the tournament is settled. */
66
+ export declare const STAFF_EDITABLE_STATUSES: readonly ["draft", "open", "checkIn", "seeding", "live"];
67
+ export declare const isTournamentStaffEditable: (status: string | null | undefined) => boolean;
68
+ /** One appointee as the owner's dialog sends it. */
69
+ export interface TournamentStaffMemberInput {
70
+ /** The user's documentId — what the search results carry. */
71
+ userDocumentId: string;
72
+ role: TournamentAppointedRole;
73
+ }
74
+ /** The whole appointed list; the server diffs it against the rows. */
75
+ export interface UpdateTournamentStaffBody {
76
+ data: {
77
+ staff: TournamentStaffMemberInput[];
78
+ };
79
+ }
80
+ /** `error.details.name` values the staff routes raise. */
81
+ export type TournamentStaffErrorName = 'NotTournamentStaff';
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ /**
3
+ * Tournament staff — who runs a tournament and what each of them may do.
4
+ *
5
+ * Three roles. The **owner** is the tournament's `users_permissions_user`
6
+ * and never a row; **admin** and **moderator** are `tournament-role` rows
7
+ * (the `TournamentRoleRole` enum is generated from the schema). A row with
8
+ * no role value is read as a moderator, the least the row can mean.
9
+ *
10
+ * The capability table is the contract between the server's guards and the
11
+ * client's buttons: both resolve the viewer's role with
12
+ * `resolveTournamentStaffRole` and ask `canTournamentStaff`, so a control is
13
+ * never offered for a request the server would refuse. Money and the
14
+ * irreversible lifecycle steps stay with the owner; admins run the event;
15
+ * moderators run matches.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.isTournamentStaffEditable = exports.STAFF_EDITABLE_STATUSES = exports.tournamentStaffCan = exports.canTournamentStaff = exports.appointedRoleOf = exports.tournamentStaffRoleLabel = exports.TOURNAMENT_STAFF_ROLE_LABELS = exports.TOURNAMENT_STAFF_CAPABILITIES = exports.APPOINTABLE_STAFF_ROLES = void 0;
19
+ exports.resolveTournamentStaffRole = resolveTournamentStaffRole;
20
+ exports.APPOINTABLE_STAFF_ROLES = [
21
+ 'admin',
22
+ 'moderator',
23
+ ];
24
+ const OWNER_ONLY = ['owner'];
25
+ const ADMIN_UP = ['owner', 'admin'];
26
+ const ALL_STAFF = ['owner', 'admin', 'moderator'];
27
+ exports.TOURNAMENT_STAFF_CAPABILITIES = {
28
+ manageStaff: OWNER_ONLY,
29
+ publish: OWNER_ONLY,
30
+ delete: OWNER_ONLY,
31
+ cancel: OWNER_ONLY,
32
+ distributePrizes: OWNER_ONLY,
33
+ editTournament: ADMIN_UP,
34
+ changeStatus: ADMIN_UP,
35
+ manageInvites: ADMIN_UP,
36
+ reviewJoinRequests: ADMIN_UP,
37
+ resolveDispute: ADMIN_UP,
38
+ seedBracket: ADMIN_UP,
39
+ manageMatches: ALL_STAFF,
40
+ enterLobby: ALL_STAFF,
41
+ };
42
+ exports.TOURNAMENT_STAFF_ROLE_LABELS = {
43
+ owner: 'Owner',
44
+ admin: 'Admin',
45
+ moderator: 'Moderator',
46
+ };
47
+ const tournamentStaffRoleLabel = (role) => {
48
+ var _a, _b;
49
+ return (_b = exports.TOURNAMENT_STAFF_ROLE_LABELS[(_a = role) !== null && _a !== void 0 ? _a : 'moderator']) !== null && _b !== void 0 ? _b : 'Moderator';
50
+ };
51
+ exports.tournamentStaffRoleLabel = tournamentStaffRoleLabel;
52
+ const sameId = (a, b) => a != null && b != null && String(a) === String(b);
53
+ /** An appointed row's role, defaulting an unset value to the least it can mean. */
54
+ const appointedRoleOf = (role) => (role === 'admin' ? 'admin' : 'moderator');
55
+ exports.appointedRoleOf = appointedRoleOf;
56
+ /**
57
+ * The viewer's staff role on a tournament, or null for everyone else. The
58
+ * owner wins over any row they might also hold.
59
+ */
60
+ function resolveTournamentStaffRole(tournament, userId) {
61
+ var _a, _b;
62
+ if (!tournament || userId == null)
63
+ return null;
64
+ if (sameId((_a = tournament.users_permissions_user) === null || _a === void 0 ? void 0 : _a.id, userId))
65
+ return 'owner';
66
+ const row = ((_b = tournament.tournament_roles) !== null && _b !== void 0 ? _b : []).find((r) => { var _a; return sameId((_a = r === null || r === void 0 ? void 0 : r.users_permissions_user) === null || _a === void 0 ? void 0 : _a.id, userId); });
67
+ return row ? (0, exports.appointedRoleOf)(row.role) : null;
68
+ }
69
+ const canTournamentStaff = (role, action) => role != null && exports.TOURNAMENT_STAFF_CAPABILITIES[action].includes(role);
70
+ exports.canTournamentStaff = canTournamentStaff;
71
+ /** `canTournamentStaff` for a viewer, resolving their role first. */
72
+ const tournamentStaffCan = (tournament, userId, action) => (0, exports.canTournamentStaff)(resolveTournamentStaffRole(tournament, userId), action);
73
+ exports.tournamentStaffCan = tournamentStaffCan;
74
+ /** Staff can be changed until the tournament is settled. */
75
+ exports.STAFF_EDITABLE_STATUSES = [
76
+ 'draft',
77
+ 'open',
78
+ 'checkIn',
79
+ 'seeding',
80
+ 'live',
81
+ ];
82
+ const isTournamentStaffEditable = (status) => exports.STAFF_EDITABLE_STATUSES.includes(status !== null && status !== void 0 ? status : '');
83
+ exports.isTournamentStaffEditable = isTournamentStaffEditable;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xpt-shared-types",
3
- "version": "1.11.0",
3
+ "version": "1.12.0",
4
4
  "description": "Shared types and data for XPT projects",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -6,3 +6,4 @@ export * from './lobby';
6
6
  export * from './tournamentEntry';
7
7
  export * from './tournamentInvite';
8
8
  export * from './tournamentJoinRequest';
9
+ export * from './tournamentStaff';
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Tournament staff — who runs a tournament and what each of them may do.
3
+ *
4
+ * Three roles. The **owner** is the tournament's `users_permissions_user`
5
+ * and never a row; **admin** and **moderator** are `tournament-role` rows
6
+ * (the `TournamentRoleRole` enum is generated from the schema). A row with
7
+ * no role value is read as a moderator, the least the row can mean.
8
+ *
9
+ * The capability table is the contract between the server's guards and the
10
+ * client's buttons: both resolve the viewer's role with
11
+ * `resolveTournamentStaffRole` and ask `canTournamentStaff`, so a control is
12
+ * never offered for a request the server would refuse. Money and the
13
+ * irreversible lifecycle steps stay with the owner; admins run the event;
14
+ * moderators run matches.
15
+ */
16
+
17
+ import type {
18
+ TournamentCurrentStatus,
19
+ TournamentRoleRole,
20
+ } from '../generated/enums';
21
+
22
+ export type TournamentStaffRole = 'owner' | TournamentRoleRole;
23
+
24
+ /** The roles the owner can hand out — everything but their own. */
25
+ export type TournamentAppointedRole = TournamentRoleRole;
26
+
27
+ export const APPOINTABLE_STAFF_ROLES = [
28
+ 'admin',
29
+ 'moderator',
30
+ ] as const satisfies readonly TournamentAppointedRole[];
31
+
32
+ export type TournamentStaffAction =
33
+ /** Appoint, re-role or remove admins and moderators. */
34
+ | 'manageStaff'
35
+ /** `PUT /tournaments/:id` — info, rules, stages, links. */
36
+ | 'editTournament'
37
+ /** Draft → open; debits the prize pool. */
38
+ | 'publish'
39
+ /** Drafts only. */
40
+ | 'delete'
41
+ /** The forward transitions: checkIn, seeding, live, completed. */
42
+ | 'changeStatus'
43
+ /** Refunds every entrant. */
44
+ | 'cancel'
45
+ | 'distributePrizes'
46
+ | 'manageInvites'
47
+ | 'reviewJoinRequests'
48
+ | 'resolveDispute'
49
+ /** Seed, initialise the bracket, place players in slots. */
50
+ | 'seedBracket'
51
+ /** Record scores, start matches, edit match details. */
52
+ | 'manageMatches'
53
+ /** Watch any lobby and post as staff in its chat. */
54
+ | 'enterLobby';
55
+
56
+ const OWNER_ONLY = ['owner'] as const;
57
+ const ADMIN_UP = ['owner', 'admin'] as const;
58
+ const ALL_STAFF = ['owner', 'admin', 'moderator'] as const;
59
+
60
+ export const TOURNAMENT_STAFF_CAPABILITIES: Record<
61
+ TournamentStaffAction,
62
+ readonly TournamentStaffRole[]
63
+ > = {
64
+ manageStaff: OWNER_ONLY,
65
+ publish: OWNER_ONLY,
66
+ delete: OWNER_ONLY,
67
+ cancel: OWNER_ONLY,
68
+ distributePrizes: OWNER_ONLY,
69
+ editTournament: ADMIN_UP,
70
+ changeStatus: ADMIN_UP,
71
+ manageInvites: ADMIN_UP,
72
+ reviewJoinRequests: ADMIN_UP,
73
+ resolveDispute: ADMIN_UP,
74
+ seedBracket: ADMIN_UP,
75
+ manageMatches: ALL_STAFF,
76
+ enterLobby: ALL_STAFF,
77
+ };
78
+
79
+ export const TOURNAMENT_STAFF_ROLE_LABELS: Record<TournamentStaffRole, string> =
80
+ {
81
+ owner: 'Owner',
82
+ admin: 'Admin',
83
+ moderator: 'Moderator',
84
+ };
85
+
86
+ export const tournamentStaffRoleLabel = (
87
+ role: string | null | undefined
88
+ ): string =>
89
+ TOURNAMENT_STAFF_ROLE_LABELS[(role as TournamentStaffRole) ?? 'moderator'] ??
90
+ 'Moderator';
91
+
92
+ /** Only what the resolver reads; both apps' tournament shapes satisfy it. */
93
+ export interface TournamentStaffLike {
94
+ users_permissions_user?: { id?: number | string | null } | null;
95
+ tournament_roles?: Array<{
96
+ role?: string | null;
97
+ users_permissions_user?: { id?: number | string | null } | null;
98
+ }> | null;
99
+ }
100
+
101
+ const sameId = (
102
+ a: number | string | null | undefined,
103
+ b: number | string | null | undefined
104
+ ): boolean => a != null && b != null && String(a) === String(b);
105
+
106
+ /** An appointed row's role, defaulting an unset value to the least it can mean. */
107
+ export const appointedRoleOf = (
108
+ role: string | null | undefined
109
+ ): TournamentAppointedRole => (role === 'admin' ? 'admin' : 'moderator');
110
+
111
+ /**
112
+ * The viewer's staff role on a tournament, or null for everyone else. The
113
+ * owner wins over any row they might also hold.
114
+ */
115
+ export function resolveTournamentStaffRole(
116
+ tournament: TournamentStaffLike | null | undefined,
117
+ userId: number | string | null | undefined
118
+ ): TournamentStaffRole | null {
119
+ if (!tournament || userId == null) return null;
120
+ if (sameId(tournament.users_permissions_user?.id, userId)) return 'owner';
121
+ const row = (tournament.tournament_roles ?? []).find((r) =>
122
+ sameId(r?.users_permissions_user?.id, userId)
123
+ );
124
+ return row ? appointedRoleOf(row.role) : null;
125
+ }
126
+
127
+ export const canTournamentStaff = (
128
+ role: TournamentStaffRole | null | undefined,
129
+ action: TournamentStaffAction
130
+ ): boolean =>
131
+ role != null && TOURNAMENT_STAFF_CAPABILITIES[action].includes(role);
132
+
133
+ /** `canTournamentStaff` for a viewer, resolving their role first. */
134
+ export const tournamentStaffCan = (
135
+ tournament: TournamentStaffLike | null | undefined,
136
+ userId: number | string | null | undefined,
137
+ action: TournamentStaffAction
138
+ ): boolean =>
139
+ canTournamentStaff(resolveTournamentStaffRole(tournament, userId), action);
140
+
141
+ /** Staff can be changed until the tournament is settled. */
142
+ export const STAFF_EDITABLE_STATUSES = [
143
+ 'draft',
144
+ 'open',
145
+ 'checkIn',
146
+ 'seeding',
147
+ 'live',
148
+ ] as const satisfies readonly TournamentCurrentStatus[];
149
+
150
+ export const isTournamentStaffEditable = (
151
+ status: string | null | undefined
152
+ ): boolean => (STAFF_EDITABLE_STATUSES as readonly string[]).includes(status ?? '');
153
+
154
+ // ─── `PUT /tournaments/:id/staff` ────────────────────────────────────────────
155
+
156
+ /** One appointee as the owner's dialog sends it. */
157
+ export interface TournamentStaffMemberInput {
158
+ /** The user's documentId — what the search results carry. */
159
+ userDocumentId: string;
160
+ role: TournamentAppointedRole;
161
+ }
162
+
163
+ /** The whole appointed list; the server diffs it against the rows. */
164
+ export interface UpdateTournamentStaffBody {
165
+ data: { staff: TournamentStaffMemberInput[] };
166
+ }
167
+
168
+ /** `error.details.name` values the staff routes raise. */
169
+ export type TournamentStaffErrorName = 'NotTournamentStaff';