xpt-shared-types 1.3.1 → 1.6.1
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/dist/data/gameGenres.d.ts +8 -0
- package/dist/data/gameGenres.js +21 -0
- package/dist/gameCatalogue.d.ts +109 -0
- package/dist/gameCatalogue.js +164 -0
- package/dist/generated/enums.d.ts +10 -0
- package/dist/generated/inputs.d.ts +18 -2
- package/dist/generated/models.d.ts +18 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/package.json +1 -1
- package/src/data/gameGenres.ts +20 -0
- package/src/gameCatalogue.ts +217 -0
- package/src/generated/enums.ts +47 -0
- package/src/generated/inputs.ts +22 -1
- package/src/generated/models.ts +22 -1
- package/src/index.ts +19 -14
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { GameGenre } from "../generated/enums";
|
|
2
|
+
/**
|
|
3
|
+
* Every genre a game can carry, in the order the games page offers them as
|
|
4
|
+
* filter chips. Typed against the generated enum so a schema change that
|
|
5
|
+
* adds or renames a value fails here at compile time rather than leaving a
|
|
6
|
+
* chip that matches nothing.
|
|
7
|
+
*/
|
|
8
|
+
export declare const GAME_GENRES: readonly GameGenre[];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GAME_GENRES = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Every genre a game can carry, in the order the games page offers them as
|
|
6
|
+
* filter chips. Typed against the generated enum so a schema change that
|
|
7
|
+
* adds or renames a value fails here at compile time rather than leaving a
|
|
8
|
+
* chip that matches nothing.
|
|
9
|
+
*/
|
|
10
|
+
exports.GAME_GENRES = [
|
|
11
|
+
"FPS",
|
|
12
|
+
"MOBA",
|
|
13
|
+
"Battle Royale",
|
|
14
|
+
"Fighting (FGC)",
|
|
15
|
+
"Sports",
|
|
16
|
+
"Strategy/RTS",
|
|
17
|
+
"Sim Racing",
|
|
18
|
+
"Card Game",
|
|
19
|
+
"Retro",
|
|
20
|
+
"Other",
|
|
21
|
+
];
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which platforms a game runs on, and which account networks a player needs
|
|
3
|
+
* on each of them.
|
|
4
|
+
*
|
|
5
|
+
* A tournament carries three catalogue relations — `game`, `platform` and
|
|
6
|
+
* `gameAccount` — and until these functions existed they were three
|
|
7
|
+
* unconnected choices. A host could put Super Smash Bros on PlayStation, or
|
|
8
|
+
* ask for a Steam ID in an Xbox tournament, and nothing anywhere objected.
|
|
9
|
+
*
|
|
10
|
+
* The chain is game -> platform -> account, each step narrowing the next:
|
|
11
|
+
*
|
|
12
|
+
* - a game is tagged with the platforms it runs on, and with the account
|
|
13
|
+
* networks it uses (`Valorant` -> Riot, `Counter-Strike 2` -> Steam);
|
|
14
|
+
* - a platform is tagged with the networks that exist on that box
|
|
15
|
+
* (PlayStation -> PSN, EA, Epic, Riot);
|
|
16
|
+
* - the accounts valid for a tournament are the *intersection* of the two.
|
|
17
|
+
*
|
|
18
|
+
* Neither list is derivable from the other. PC hosts {Steam, Epic, Battle.net,
|
|
19
|
+
* Riot, EA}, but Overwatch 2 on PC is only Battle.net or Steam — so the
|
|
20
|
+
* platform alone over-generates. And a game's platforms are not the union of
|
|
21
|
+
* its networks' platforms: 2XKO is PC-only, yet Riot must be tagged with
|
|
22
|
+
* PlayStation and Xbox for Valorant, which would wrongly put 2XKO on consoles.
|
|
23
|
+
* Both edges are real; the intersection is the answer.
|
|
24
|
+
*
|
|
25
|
+
* Lives here rather than in xpt-strapi because the client filters its radio
|
|
26
|
+
* groups with the identical rule. Two implementations of an intersection is
|
|
27
|
+
* two chances to disagree about what a host is allowed to pick.
|
|
28
|
+
*
|
|
29
|
+
* Pure on purpose: no Strapi, no I/O, so the rules are unit-tested directly
|
|
30
|
+
* (see xpt-strapi `tests/unit/utils/gameCatalogue.test.ts`).
|
|
31
|
+
*/
|
|
32
|
+
/** The identifying fields of a populated Strapi relation, all optional. */
|
|
33
|
+
export interface CatalogueRef {
|
|
34
|
+
id?: number | string | null;
|
|
35
|
+
documentId?: string | null;
|
|
36
|
+
name?: string | null;
|
|
37
|
+
}
|
|
38
|
+
/** A platform, with the account networks that exist on it. */
|
|
39
|
+
export interface CataloguePlatformLike extends CatalogueRef {
|
|
40
|
+
game_accounts?: readonly CatalogueRef[] | null;
|
|
41
|
+
}
|
|
42
|
+
/** A game, with the platforms it runs on and the networks it uses. */
|
|
43
|
+
export interface CatalogueGameLike extends CatalogueRef {
|
|
44
|
+
title?: string | null;
|
|
45
|
+
platforms?: readonly CatalogueRef[] | null;
|
|
46
|
+
game_accounts?: readonly CatalogueRef[] | null;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Whether two relation references point at the same row.
|
|
50
|
+
*
|
|
51
|
+
* `documentId` wins when both sides carry one, because a numeric `id` differs
|
|
52
|
+
* between a draft and its published entry while the documentId does not. Two
|
|
53
|
+
* references that share *neither* field are never equal — the tempting
|
|
54
|
+
* shorthand `a.id === b.id` reads as true when both are `undefined`, which is
|
|
55
|
+
* how `hasGameAccountFor` used to admit any player to a tournament that had no
|
|
56
|
+
* account set at all.
|
|
57
|
+
*/
|
|
58
|
+
export declare function sameRef(a?: CatalogueRef | null, b?: CatalogueRef | null): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* The platforms a game can be played on.
|
|
61
|
+
*
|
|
62
|
+
* An untagged game returns the whole catalogue — see `isTagged`. Results keep
|
|
63
|
+
* the order of `allPlatforms` so the radio group a host sees is stable and
|
|
64
|
+
* matches the unfiltered list they saw before any tagging happened.
|
|
65
|
+
*/
|
|
66
|
+
export declare function platformsForGame<P extends CataloguePlatformLike>(game: CatalogueGameLike | null | undefined, allPlatforms: readonly P[]): P[];
|
|
67
|
+
/**
|
|
68
|
+
* The account networks valid for a game on a platform.
|
|
69
|
+
*
|
|
70
|
+
* With no platform chosen yet the game's own list is returned unfiltered —
|
|
71
|
+
* that is what lets a single-network game (Fortnite, Valorant, Dota 2) resolve
|
|
72
|
+
* its account at creation time, before a platform exists.
|
|
73
|
+
*
|
|
74
|
+
* An empty result when both sides *are* tagged is returned as-is rather than
|
|
75
|
+
* falling back to the full catalogue. It means the two tag lists disagree,
|
|
76
|
+
* which is a data bug: surfacing it as "no account type available" gets it
|
|
77
|
+
* fixed, whereas quietly showing all ten hides it forever.
|
|
78
|
+
*/
|
|
79
|
+
export declare function gameAccountsFor<A extends CatalogueRef>(game: CatalogueGameLike | null | undefined, platform: CataloguePlatformLike | null | undefined, allGameAccounts: readonly A[]): A[];
|
|
80
|
+
/**
|
|
81
|
+
* The one candidate, when there is exactly one.
|
|
82
|
+
*
|
|
83
|
+
* Drives auto-connect: a single valid option is not a choice, so the server
|
|
84
|
+
* connects it and the host is never shown a dialog with one radio in it.
|
|
85
|
+
*/
|
|
86
|
+
export declare function soleCandidate<T>(candidates: readonly T[]): T | null;
|
|
87
|
+
/**
|
|
88
|
+
* The candidate platforms that carry this account network — the chain read
|
|
89
|
+
* backwards.
|
|
90
|
+
*
|
|
91
|
+
* Most networks answer the platform question by themselves: a PSN ID only
|
|
92
|
+
* exists on a PlayStation, a Steam ID only on PC. Pair that with
|
|
93
|
+
* `soleCandidate` and choosing the account fills the platform in, which is the
|
|
94
|
+
* other half of what makes these two cards feel like one decision.
|
|
95
|
+
*
|
|
96
|
+
* The four cross-platform networks (EA, Epic, Battle.net, Riot) match several
|
|
97
|
+
* and so infer nothing — correctly, because an EA ID really does not say which
|
|
98
|
+
* box you are on.
|
|
99
|
+
*
|
|
100
|
+
* An **untagged** platform is excluded rather than treated as permissive. That
|
|
101
|
+
* is the opposite of `gameAccountsFor`, and deliberate: not knowing what a
|
|
102
|
+
* platform carries is a reason to stay quiet, not a licence to claim it carries
|
|
103
|
+
* this.
|
|
104
|
+
*/
|
|
105
|
+
export declare function platformsCarrying<P extends CataloguePlatformLike>(gameAccount: CatalogueRef | null | undefined, candidates: readonly P[]): P[];
|
|
106
|
+
/** Whether this platform is one the game actually runs on. */
|
|
107
|
+
export declare function isPlatformValidForGame(game: CatalogueGameLike | null | undefined, platform: CatalogueRef | null | undefined, allPlatforms: readonly CataloguePlatformLike[]): boolean;
|
|
108
|
+
/** Whether this account network is one the game uses on this platform. */
|
|
109
|
+
export declare function isGameAccountValidFor(game: CatalogueGameLike | null | undefined, platform: CataloguePlatformLike | null | undefined, gameAccount: CatalogueRef | null | undefined, allGameAccounts: readonly CatalogueRef[]): boolean;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Which platforms a game runs on, and which account networks a player needs
|
|
4
|
+
* on each of them.
|
|
5
|
+
*
|
|
6
|
+
* A tournament carries three catalogue relations — `game`, `platform` and
|
|
7
|
+
* `gameAccount` — and until these functions existed they were three
|
|
8
|
+
* unconnected choices. A host could put Super Smash Bros on PlayStation, or
|
|
9
|
+
* ask for a Steam ID in an Xbox tournament, and nothing anywhere objected.
|
|
10
|
+
*
|
|
11
|
+
* The chain is game -> platform -> account, each step narrowing the next:
|
|
12
|
+
*
|
|
13
|
+
* - a game is tagged with the platforms it runs on, and with the account
|
|
14
|
+
* networks it uses (`Valorant` -> Riot, `Counter-Strike 2` -> Steam);
|
|
15
|
+
* - a platform is tagged with the networks that exist on that box
|
|
16
|
+
* (PlayStation -> PSN, EA, Epic, Riot);
|
|
17
|
+
* - the accounts valid for a tournament are the *intersection* of the two.
|
|
18
|
+
*
|
|
19
|
+
* Neither list is derivable from the other. PC hosts {Steam, Epic, Battle.net,
|
|
20
|
+
* Riot, EA}, but Overwatch 2 on PC is only Battle.net or Steam — so the
|
|
21
|
+
* platform alone over-generates. And a game's platforms are not the union of
|
|
22
|
+
* its networks' platforms: 2XKO is PC-only, yet Riot must be tagged with
|
|
23
|
+
* PlayStation and Xbox for Valorant, which would wrongly put 2XKO on consoles.
|
|
24
|
+
* Both edges are real; the intersection is the answer.
|
|
25
|
+
*
|
|
26
|
+
* Lives here rather than in xpt-strapi because the client filters its radio
|
|
27
|
+
* groups with the identical rule. Two implementations of an intersection is
|
|
28
|
+
* two chances to disagree about what a host is allowed to pick.
|
|
29
|
+
*
|
|
30
|
+
* Pure on purpose: no Strapi, no I/O, so the rules are unit-tested directly
|
|
31
|
+
* (see xpt-strapi `tests/unit/utils/gameCatalogue.test.ts`).
|
|
32
|
+
*/
|
|
33
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
|
+
exports.sameRef = sameRef;
|
|
35
|
+
exports.platformsForGame = platformsForGame;
|
|
36
|
+
exports.gameAccountsFor = gameAccountsFor;
|
|
37
|
+
exports.soleCandidate = soleCandidate;
|
|
38
|
+
exports.platformsCarrying = platformsCarrying;
|
|
39
|
+
exports.isPlatformValidForGame = isPlatformValidForGame;
|
|
40
|
+
exports.isGameAccountValidFor = isGameAccountValidFor;
|
|
41
|
+
/**
|
|
42
|
+
* Whether two relation references point at the same row.
|
|
43
|
+
*
|
|
44
|
+
* `documentId` wins when both sides carry one, because a numeric `id` differs
|
|
45
|
+
* between a draft and its published entry while the documentId does not. Two
|
|
46
|
+
* references that share *neither* field are never equal — the tempting
|
|
47
|
+
* shorthand `a.id === b.id` reads as true when both are `undefined`, which is
|
|
48
|
+
* how `hasGameAccountFor` used to admit any player to a tournament that had no
|
|
49
|
+
* account set at all.
|
|
50
|
+
*/
|
|
51
|
+
function sameRef(a, b) {
|
|
52
|
+
if (!a || !b)
|
|
53
|
+
return false;
|
|
54
|
+
if (a.documentId && b.documentId)
|
|
55
|
+
return a.documentId === b.documentId;
|
|
56
|
+
const aId = a.id;
|
|
57
|
+
const bId = b.id;
|
|
58
|
+
if (aId === null || aId === undefined)
|
|
59
|
+
return false;
|
|
60
|
+
if (bId === null || bId === undefined)
|
|
61
|
+
return false;
|
|
62
|
+
return String(aId) === String(bId);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Whether a catalogue tag list says anything.
|
|
66
|
+
*
|
|
67
|
+
* An absent list and an empty one mean the same thing: nobody has tagged this
|
|
68
|
+
* row yet. Strapi serialises an unpopulated relation and a populated-but-empty
|
|
69
|
+
* one identically once it reaches the client, so there is no way to tell them
|
|
70
|
+
* apart and no reason to try — either way we have no opinion, and an untagged
|
|
71
|
+
* row must never lock a host out of a game we simply forgot to configure.
|
|
72
|
+
*/
|
|
73
|
+
function isTagged(list) {
|
|
74
|
+
return Array.isArray(list) && list.length > 0;
|
|
75
|
+
}
|
|
76
|
+
/** The master list, minus any row that repeats one already seen. */
|
|
77
|
+
function dedupe(rows) {
|
|
78
|
+
const seen = [];
|
|
79
|
+
for (const row of rows) {
|
|
80
|
+
if (!seen.some((kept) => sameRef(kept, row)))
|
|
81
|
+
seen.push(row);
|
|
82
|
+
}
|
|
83
|
+
return seen;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The platforms a game can be played on.
|
|
87
|
+
*
|
|
88
|
+
* An untagged game returns the whole catalogue — see `isTagged`. Results keep
|
|
89
|
+
* the order of `allPlatforms` so the radio group a host sees is stable and
|
|
90
|
+
* matches the unfiltered list they saw before any tagging happened.
|
|
91
|
+
*/
|
|
92
|
+
function platformsForGame(game, allPlatforms) {
|
|
93
|
+
const all = dedupe(allPlatforms);
|
|
94
|
+
if (!game || !isTagged(game.platforms))
|
|
95
|
+
return all;
|
|
96
|
+
return all.filter((platform) => game.platforms.some((tag) => sameRef(tag, platform)));
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The account networks valid for a game on a platform.
|
|
100
|
+
*
|
|
101
|
+
* With no platform chosen yet the game's own list is returned unfiltered —
|
|
102
|
+
* that is what lets a single-network game (Fortnite, Valorant, Dota 2) resolve
|
|
103
|
+
* its account at creation time, before a platform exists.
|
|
104
|
+
*
|
|
105
|
+
* An empty result when both sides *are* tagged is returned as-is rather than
|
|
106
|
+
* falling back to the full catalogue. It means the two tag lists disagree,
|
|
107
|
+
* which is a data bug: surfacing it as "no account type available" gets it
|
|
108
|
+
* fixed, whereas quietly showing all ten hides it forever.
|
|
109
|
+
*/
|
|
110
|
+
function gameAccountsFor(game, platform, allGameAccounts) {
|
|
111
|
+
const all = dedupe(allGameAccounts);
|
|
112
|
+
const byGame = !game || !isTagged(game.game_accounts)
|
|
113
|
+
? all
|
|
114
|
+
: all.filter((account) => game.game_accounts.some((tag) => sameRef(tag, account)));
|
|
115
|
+
// No platform yet, or a platform nobody has tagged: the game is the only
|
|
116
|
+
// authority we have.
|
|
117
|
+
if (!platform || !isTagged(platform.game_accounts))
|
|
118
|
+
return byGame;
|
|
119
|
+
return byGame.filter((account) => platform.game_accounts.some((tag) => sameRef(tag, account)));
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The one candidate, when there is exactly one.
|
|
123
|
+
*
|
|
124
|
+
* Drives auto-connect: a single valid option is not a choice, so the server
|
|
125
|
+
* connects it and the host is never shown a dialog with one radio in it.
|
|
126
|
+
*/
|
|
127
|
+
function soleCandidate(candidates) {
|
|
128
|
+
return candidates.length === 1 ? candidates[0] : null;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* The candidate platforms that carry this account network — the chain read
|
|
132
|
+
* backwards.
|
|
133
|
+
*
|
|
134
|
+
* Most networks answer the platform question by themselves: a PSN ID only
|
|
135
|
+
* exists on a PlayStation, a Steam ID only on PC. Pair that with
|
|
136
|
+
* `soleCandidate` and choosing the account fills the platform in, which is the
|
|
137
|
+
* other half of what makes these two cards feel like one decision.
|
|
138
|
+
*
|
|
139
|
+
* The four cross-platform networks (EA, Epic, Battle.net, Riot) match several
|
|
140
|
+
* and so infer nothing — correctly, because an EA ID really does not say which
|
|
141
|
+
* box you are on.
|
|
142
|
+
*
|
|
143
|
+
* An **untagged** platform is excluded rather than treated as permissive. That
|
|
144
|
+
* is the opposite of `gameAccountsFor`, and deliberate: not knowing what a
|
|
145
|
+
* platform carries is a reason to stay quiet, not a licence to claim it carries
|
|
146
|
+
* this.
|
|
147
|
+
*/
|
|
148
|
+
function platformsCarrying(gameAccount, candidates) {
|
|
149
|
+
if (!gameAccount)
|
|
150
|
+
return [];
|
|
151
|
+
return dedupe(candidates).filter((platform) => { var _a; return ((_a = platform.game_accounts) !== null && _a !== void 0 ? _a : []).some((tag) => sameRef(tag, gameAccount)); });
|
|
152
|
+
}
|
|
153
|
+
/** Whether this platform is one the game actually runs on. */
|
|
154
|
+
function isPlatformValidForGame(game, platform, allPlatforms) {
|
|
155
|
+
if (!platform)
|
|
156
|
+
return false;
|
|
157
|
+
return platformsForGame(game, allPlatforms).some((valid) => sameRef(valid, platform));
|
|
158
|
+
}
|
|
159
|
+
/** Whether this account network is one the game uses on this platform. */
|
|
160
|
+
function isGameAccountValidFor(game, platform, gameAccount, allGameAccounts) {
|
|
161
|
+
if (!gameAccount)
|
|
162
|
+
return false;
|
|
163
|
+
return gameAccountsFor(game, platform, allGameAccounts).some((valid) => sameRef(valid, gameAccount));
|
|
164
|
+
}
|
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
/** `Friendship.status` */
|
|
2
2
|
export type FriendshipStatus = "pending" | "accepted";
|
|
3
|
+
/** `Game.genre` */
|
|
4
|
+
export type GameGenre = "FPS" | "MOBA" | "Battle Royale" | "Fighting (FGC)" | "Sports" | "Strategy/RTS" | "Sim Racing" | "Card Game" | "Retro" | "Other";
|
|
3
5
|
/** `GameRequest.customLobbies` */
|
|
4
6
|
export type GameRequestCustomLobbies = "Yes" | "No" | "Not sure";
|
|
5
7
|
/** `GameRequest.genre` */
|
|
6
8
|
export type GameRequestGenre = "FPS" | "MOBA" | "Battle Royale" | "Fighting (FGC)" | "Sports" | "Strategy/RTS" | "Sim Racing" | "Card Game" | "Other";
|
|
7
9
|
/** `GameRequest.teamPlay` */
|
|
8
10
|
export type GameRequestTeamPlay = "No (solo)" | "Yes, 2v2" | "Yes, 3v3" | "Yes, 4v4+" | "Don't know";
|
|
11
|
+
/** `Game.visibility` */
|
|
12
|
+
export type GameVisibility = "listed" | "searchOnly" | "archived";
|
|
13
|
+
/** `Match.disputeReason` */
|
|
14
|
+
export type MatchDisputeReason = "scoreMismatch" | "opponentNoShow" | "opponentLaggedOut" | "rulesViolation" | "other";
|
|
9
15
|
/** `Match.lobbyStatus` */
|
|
10
16
|
export type MatchLobbyStatus = "waiting" | "ready" | "disputed" | "completed";
|
|
17
|
+
/** `Match.resultType` */
|
|
18
|
+
export type MatchResultType = "reported" | "hostRecorded" | "bye" | "noShowHome" | "noShowAway" | "forfeitHome" | "forfeitAway" | "disputeResolved" | "adminOverride";
|
|
11
19
|
/** `Match.round` */
|
|
12
20
|
export type MatchRound = "Round 128" | "Round 64" | "Round 32" | "Round 16" | "Quarter Final" | "Semi-Final" | "Third Round" | "Finals" | "League";
|
|
13
21
|
/** `Match.streamPlatform` */
|
|
14
22
|
export type MatchStreamPlatform = "twitch" | "youtube" | "kick" | "other";
|
|
15
23
|
/** `Match.winnerSlot` */
|
|
16
24
|
export type MatchWinnerSlot = "home" | "away";
|
|
25
|
+
/** `Platform.code` */
|
|
26
|
+
export type PlatformCode = "pc" | "playstation" | "xbox" | "nintendo" | "mobile";
|
|
17
27
|
/** `Referral.status` */
|
|
18
28
|
export type ReferralStatus = "pending" | "completed";
|
|
19
29
|
/** `Team.current_status` */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BlocksContent, MediaInput, RelationInput } from '../contracts';
|
|
2
|
-
import type { FriendshipStatus, GameRequestCustomLobbies, GameRequestGenre, GameRequestTeamPlay, MatchLobbyStatus, MatchRound, MatchStreamPlatform, MatchWinnerSlot, ReferralStatus, TeamCurrentStatus, TeamInviteStatus, TeamInviteTeamRole, TeamPlayerTeamRole, TournamentCurrentStatus, TournamentParticipantEntryType, TournamentParticipantStatus, TournamentRoleRole, TournamentTeamSize, TournamentType, UserTransactionStripeStatus, UserTransactionType } from './enums';
|
|
2
|
+
import type { FriendshipStatus, GameGenre, GameRequestCustomLobbies, GameRequestGenre, GameRequestTeamPlay, GameVisibility, MatchDisputeReason, MatchLobbyStatus, MatchResultType, MatchRound, MatchStreamPlatform, MatchWinnerSlot, PlatformCode, ReferralStatus, TeamCurrentStatus, TeamInviteStatus, TeamInviteTeamRole, TeamPlayerTeamRole, TournamentCurrentStatus, TournamentParticipantEntryType, TournamentParticipantStatus, TournamentRoleRole, TournamentTeamSize, TournamentType, UserTransactionStripeStatus, UserTransactionType } from './enums';
|
|
3
3
|
/** Write payload for `about`. */
|
|
4
4
|
export interface AboutInput {
|
|
5
5
|
heroTitle?: string;
|
|
@@ -44,10 +44,14 @@ export interface GameInput {
|
|
|
44
44
|
imgThumb?: MediaInput;
|
|
45
45
|
imgMain?: MediaInput;
|
|
46
46
|
tournaments?: RelationInput | RelationInput[];
|
|
47
|
-
isTrending?: boolean;
|
|
48
47
|
user_game_stats?: RelationInput | RelationInput[];
|
|
49
48
|
team_game_stats?: RelationInput | RelationInput[];
|
|
50
49
|
imgMainPosition?: string;
|
|
50
|
+
visibility?: GameVisibility;
|
|
51
|
+
genre?: GameGenre;
|
|
52
|
+
sortOrder?: number;
|
|
53
|
+
platforms?: RelationInput | RelationInput[];
|
|
54
|
+
game_accounts?: RelationInput | RelationInput[];
|
|
51
55
|
}
|
|
52
56
|
/** Write payload for `game-account`. */
|
|
53
57
|
export interface GameAccountInput {
|
|
@@ -55,6 +59,8 @@ export interface GameAccountInput {
|
|
|
55
59
|
tournaments?: RelationInput | RelationInput[];
|
|
56
60
|
user_game_accounts?: RelationInput | RelationInput[];
|
|
57
61
|
imgThumb?: MediaInput;
|
|
62
|
+
games?: RelationInput | RelationInput[];
|
|
63
|
+
platforms?: RelationInput | RelationInput[];
|
|
58
64
|
}
|
|
59
65
|
/** Write payload for `game-request`. */
|
|
60
66
|
export interface GameRequestInput {
|
|
@@ -126,6 +132,13 @@ export interface MatchInput {
|
|
|
126
132
|
isStreaming?: boolean;
|
|
127
133
|
streamHandle?: string;
|
|
128
134
|
streamPlatform?: MatchStreamPlatform;
|
|
135
|
+
startedAt?: string;
|
|
136
|
+
endedAt?: string;
|
|
137
|
+
resultType?: MatchResultType;
|
|
138
|
+
resultNote?: string;
|
|
139
|
+
disputeReason?: MatchDisputeReason;
|
|
140
|
+
disputeNote?: string;
|
|
141
|
+
disputedBy?: RelationInput;
|
|
129
142
|
}
|
|
130
143
|
/** Write payload for `match-message`. */
|
|
131
144
|
export interface MatchMessageInput {
|
|
@@ -148,7 +161,10 @@ export interface NotificationInput {
|
|
|
148
161
|
/** Write payload for `platform`. */
|
|
149
162
|
export interface PlatformInput {
|
|
150
163
|
name?: string;
|
|
164
|
+
code?: PlatformCode;
|
|
151
165
|
tournaments?: RelationInput | RelationInput[];
|
|
166
|
+
games?: RelationInput | RelationInput[];
|
|
167
|
+
game_accounts?: RelationInput | RelationInput[];
|
|
152
168
|
}
|
|
153
169
|
/** Write payload for `preset-avatar`. */
|
|
154
170
|
export interface PresetAvatarInput {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BlocksContent, StrapiDocument, StrapiMedia, UserRole } from '../contracts';
|
|
2
|
-
import type { FriendshipStatus, GameRequestCustomLobbies, GameRequestGenre, GameRequestTeamPlay, MatchLobbyStatus, MatchRound, MatchStreamPlatform, MatchWinnerSlot, ReferralStatus, TeamCurrentStatus, TeamInviteStatus, TeamInviteTeamRole, TeamPlayerTeamRole, TournamentCurrentStatus, TournamentParticipantEntryType, TournamentParticipantStatus, TournamentRoleRole, TournamentTeamSize, TournamentType, UserTransactionStripeStatus, UserTransactionType } from './enums';
|
|
2
|
+
import type { FriendshipStatus, GameGenre, GameRequestCustomLobbies, GameRequestGenre, GameRequestTeamPlay, GameVisibility, MatchDisputeReason, MatchLobbyStatus, MatchResultType, MatchRound, MatchStreamPlatform, MatchWinnerSlot, PlatformCode, ReferralStatus, TeamCurrentStatus, TeamInviteStatus, TeamInviteTeamRole, TeamPlayerTeamRole, TournamentCurrentStatus, TournamentParticipantEntryType, TournamentParticipantStatus, TournamentRoleRole, TournamentTeamSize, TournamentType, UserTransactionStripeStatus, UserTransactionType } from './enums';
|
|
3
3
|
/** `about` */
|
|
4
4
|
export interface About extends StrapiDocument {
|
|
5
5
|
heroTitle?: string | null;
|
|
@@ -44,10 +44,14 @@ export interface Game extends StrapiDocument {
|
|
|
44
44
|
imgThumb?: StrapiMedia | null;
|
|
45
45
|
imgMain?: StrapiMedia | null;
|
|
46
46
|
tournaments?: Tournament[];
|
|
47
|
-
isTrending?: boolean | null;
|
|
48
47
|
user_game_stats?: UserGameStat[];
|
|
49
48
|
team_game_stats?: TeamGameStat[];
|
|
50
49
|
imgMainPosition?: string | null;
|
|
50
|
+
visibility?: GameVisibility | null;
|
|
51
|
+
genre?: GameGenre | null;
|
|
52
|
+
sortOrder?: number | null;
|
|
53
|
+
platforms?: Platform[];
|
|
54
|
+
game_accounts?: GameAccount[];
|
|
51
55
|
}
|
|
52
56
|
/** `game-account` */
|
|
53
57
|
export interface GameAccount extends StrapiDocument {
|
|
@@ -55,6 +59,8 @@ export interface GameAccount extends StrapiDocument {
|
|
|
55
59
|
tournaments?: Tournament[];
|
|
56
60
|
user_game_accounts?: UserGameAccount[];
|
|
57
61
|
imgThumb?: StrapiMedia | null;
|
|
62
|
+
games?: Game[];
|
|
63
|
+
platforms?: Platform[];
|
|
58
64
|
}
|
|
59
65
|
/** `game-request` */
|
|
60
66
|
export interface GameRequest extends StrapiDocument {
|
|
@@ -126,6 +132,13 @@ export interface Match extends StrapiDocument {
|
|
|
126
132
|
isStreaming?: boolean | null;
|
|
127
133
|
streamHandle?: string | null;
|
|
128
134
|
streamPlatform?: MatchStreamPlatform | null;
|
|
135
|
+
startedAt?: string | null;
|
|
136
|
+
endedAt?: string | null;
|
|
137
|
+
resultType?: MatchResultType | null;
|
|
138
|
+
resultNote?: string | null;
|
|
139
|
+
disputeReason?: MatchDisputeReason | null;
|
|
140
|
+
disputeNote?: string | null;
|
|
141
|
+
disputedBy?: User | null;
|
|
129
142
|
}
|
|
130
143
|
/** `match-message` */
|
|
131
144
|
export interface MatchMessage extends StrapiDocument {
|
|
@@ -148,7 +161,10 @@ export interface Notification extends StrapiDocument {
|
|
|
148
161
|
/** `platform` */
|
|
149
162
|
export interface Platform extends StrapiDocument {
|
|
150
163
|
name?: string | null;
|
|
164
|
+
code?: PlatformCode | null;
|
|
151
165
|
tournaments?: Tournament[];
|
|
166
|
+
games?: Game[];
|
|
167
|
+
game_accounts?: GameAccount[];
|
|
152
168
|
}
|
|
153
169
|
/** `preset-avatar` */
|
|
154
170
|
export interface PresetAvatar extends StrapiDocument {
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -22,5 +22,9 @@ __exportStar(require("./contracts"), exports);
|
|
|
22
22
|
__exportStar(require("./manual"), exports);
|
|
23
23
|
// Bracket shape — shared by the backend generator and Storybook fixtures
|
|
24
24
|
__exportStar(require("./bracket"), exports);
|
|
25
|
+
// Game/platform/account catalogue — the game -> platform -> account chain,
|
|
26
|
+
// applied identically by the backend resolver and the client's pickers
|
|
27
|
+
__exportStar(require("./gameCatalogue"), exports);
|
|
25
28
|
// Data
|
|
26
29
|
__exportStar(require("./data/countriesList"), exports);
|
|
30
|
+
__exportStar(require("./data/gameGenres"), exports);
|
package/package.json
CHANGED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { GameGenre } from "../generated/enums";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Every genre a game can carry, in the order the games page offers them as
|
|
5
|
+
* filter chips. Typed against the generated enum so a schema change that
|
|
6
|
+
* adds or renames a value fails here at compile time rather than leaving a
|
|
7
|
+
* chip that matches nothing.
|
|
8
|
+
*/
|
|
9
|
+
export const GAME_GENRES: readonly GameGenre[] = [
|
|
10
|
+
"FPS",
|
|
11
|
+
"MOBA",
|
|
12
|
+
"Battle Royale",
|
|
13
|
+
"Fighting (FGC)",
|
|
14
|
+
"Sports",
|
|
15
|
+
"Strategy/RTS",
|
|
16
|
+
"Sim Racing",
|
|
17
|
+
"Card Game",
|
|
18
|
+
"Retro",
|
|
19
|
+
"Other",
|
|
20
|
+
];
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which platforms a game runs on, and which account networks a player needs
|
|
3
|
+
* on each of them.
|
|
4
|
+
*
|
|
5
|
+
* A tournament carries three catalogue relations — `game`, `platform` and
|
|
6
|
+
* `gameAccount` — and until these functions existed they were three
|
|
7
|
+
* unconnected choices. A host could put Super Smash Bros on PlayStation, or
|
|
8
|
+
* ask for a Steam ID in an Xbox tournament, and nothing anywhere objected.
|
|
9
|
+
*
|
|
10
|
+
* The chain is game -> platform -> account, each step narrowing the next:
|
|
11
|
+
*
|
|
12
|
+
* - a game is tagged with the platforms it runs on, and with the account
|
|
13
|
+
* networks it uses (`Valorant` -> Riot, `Counter-Strike 2` -> Steam);
|
|
14
|
+
* - a platform is tagged with the networks that exist on that box
|
|
15
|
+
* (PlayStation -> PSN, EA, Epic, Riot);
|
|
16
|
+
* - the accounts valid for a tournament are the *intersection* of the two.
|
|
17
|
+
*
|
|
18
|
+
* Neither list is derivable from the other. PC hosts {Steam, Epic, Battle.net,
|
|
19
|
+
* Riot, EA}, but Overwatch 2 on PC is only Battle.net or Steam — so the
|
|
20
|
+
* platform alone over-generates. And a game's platforms are not the union of
|
|
21
|
+
* its networks' platforms: 2XKO is PC-only, yet Riot must be tagged with
|
|
22
|
+
* PlayStation and Xbox for Valorant, which would wrongly put 2XKO on consoles.
|
|
23
|
+
* Both edges are real; the intersection is the answer.
|
|
24
|
+
*
|
|
25
|
+
* Lives here rather than in xpt-strapi because the client filters its radio
|
|
26
|
+
* groups with the identical rule. Two implementations of an intersection is
|
|
27
|
+
* two chances to disagree about what a host is allowed to pick.
|
|
28
|
+
*
|
|
29
|
+
* Pure on purpose: no Strapi, no I/O, so the rules are unit-tested directly
|
|
30
|
+
* (see xpt-strapi `tests/unit/utils/gameCatalogue.test.ts`).
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/** The identifying fields of a populated Strapi relation, all optional. */
|
|
34
|
+
export interface CatalogueRef {
|
|
35
|
+
id?: number | string | null;
|
|
36
|
+
documentId?: string | null;
|
|
37
|
+
name?: string | null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** A platform, with the account networks that exist on it. */
|
|
41
|
+
export interface CataloguePlatformLike extends CatalogueRef {
|
|
42
|
+
game_accounts?: readonly CatalogueRef[] | null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** A game, with the platforms it runs on and the networks it uses. */
|
|
46
|
+
export interface CatalogueGameLike extends CatalogueRef {
|
|
47
|
+
title?: string | null;
|
|
48
|
+
platforms?: readonly CatalogueRef[] | null;
|
|
49
|
+
game_accounts?: readonly CatalogueRef[] | null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Whether two relation references point at the same row.
|
|
54
|
+
*
|
|
55
|
+
* `documentId` wins when both sides carry one, because a numeric `id` differs
|
|
56
|
+
* between a draft and its published entry while the documentId does not. Two
|
|
57
|
+
* references that share *neither* field are never equal — the tempting
|
|
58
|
+
* shorthand `a.id === b.id` reads as true when both are `undefined`, which is
|
|
59
|
+
* how `hasGameAccountFor` used to admit any player to a tournament that had no
|
|
60
|
+
* account set at all.
|
|
61
|
+
*/
|
|
62
|
+
export function sameRef(
|
|
63
|
+
a?: CatalogueRef | null,
|
|
64
|
+
b?: CatalogueRef | null
|
|
65
|
+
): boolean {
|
|
66
|
+
if (!a || !b) return false;
|
|
67
|
+
|
|
68
|
+
if (a.documentId && b.documentId) return a.documentId === b.documentId;
|
|
69
|
+
|
|
70
|
+
const aId = a.id;
|
|
71
|
+
const bId = b.id;
|
|
72
|
+
if (aId === null || aId === undefined) return false;
|
|
73
|
+
if (bId === null || bId === undefined) return false;
|
|
74
|
+
|
|
75
|
+
return String(aId) === String(bId);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Whether a catalogue tag list says anything.
|
|
80
|
+
*
|
|
81
|
+
* An absent list and an empty one mean the same thing: nobody has tagged this
|
|
82
|
+
* row yet. Strapi serialises an unpopulated relation and a populated-but-empty
|
|
83
|
+
* one identically once it reaches the client, so there is no way to tell them
|
|
84
|
+
* apart and no reason to try — either way we have no opinion, and an untagged
|
|
85
|
+
* row must never lock a host out of a game we simply forgot to configure.
|
|
86
|
+
*/
|
|
87
|
+
function isTagged(list?: readonly CatalogueRef[] | null): boolean {
|
|
88
|
+
return Array.isArray(list) && list.length > 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** The master list, minus any row that repeats one already seen. */
|
|
92
|
+
function dedupe<T extends CatalogueRef>(rows: readonly T[]): T[] {
|
|
93
|
+
const seen: T[] = [];
|
|
94
|
+
for (const row of rows) {
|
|
95
|
+
if (!seen.some((kept) => sameRef(kept, row))) seen.push(row);
|
|
96
|
+
}
|
|
97
|
+
return seen;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* The platforms a game can be played on.
|
|
102
|
+
*
|
|
103
|
+
* An untagged game returns the whole catalogue — see `isTagged`. Results keep
|
|
104
|
+
* the order of `allPlatforms` so the radio group a host sees is stable and
|
|
105
|
+
* matches the unfiltered list they saw before any tagging happened.
|
|
106
|
+
*/
|
|
107
|
+
export function platformsForGame<P extends CataloguePlatformLike>(
|
|
108
|
+
game: CatalogueGameLike | null | undefined,
|
|
109
|
+
allPlatforms: readonly P[]
|
|
110
|
+
): P[] {
|
|
111
|
+
const all = dedupe(allPlatforms);
|
|
112
|
+
|
|
113
|
+
if (!game || !isTagged(game.platforms)) return all;
|
|
114
|
+
|
|
115
|
+
return all.filter((platform) =>
|
|
116
|
+
game.platforms!.some((tag) => sameRef(tag, platform))
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* The account networks valid for a game on a platform.
|
|
122
|
+
*
|
|
123
|
+
* With no platform chosen yet the game's own list is returned unfiltered —
|
|
124
|
+
* that is what lets a single-network game (Fortnite, Valorant, Dota 2) resolve
|
|
125
|
+
* its account at creation time, before a platform exists.
|
|
126
|
+
*
|
|
127
|
+
* An empty result when both sides *are* tagged is returned as-is rather than
|
|
128
|
+
* falling back to the full catalogue. It means the two tag lists disagree,
|
|
129
|
+
* which is a data bug: surfacing it as "no account type available" gets it
|
|
130
|
+
* fixed, whereas quietly showing all ten hides it forever.
|
|
131
|
+
*/
|
|
132
|
+
export function gameAccountsFor<A extends CatalogueRef>(
|
|
133
|
+
game: CatalogueGameLike | null | undefined,
|
|
134
|
+
platform: CataloguePlatformLike | null | undefined,
|
|
135
|
+
allGameAccounts: readonly A[]
|
|
136
|
+
): A[] {
|
|
137
|
+
const all = dedupe(allGameAccounts);
|
|
138
|
+
|
|
139
|
+
const byGame =
|
|
140
|
+
!game || !isTagged(game.game_accounts)
|
|
141
|
+
? all
|
|
142
|
+
: all.filter((account) =>
|
|
143
|
+
game.game_accounts!.some((tag) => sameRef(tag, account))
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
// No platform yet, or a platform nobody has tagged: the game is the only
|
|
147
|
+
// authority we have.
|
|
148
|
+
if (!platform || !isTagged(platform.game_accounts)) return byGame;
|
|
149
|
+
|
|
150
|
+
return byGame.filter((account) =>
|
|
151
|
+
platform.game_accounts!.some((tag) => sameRef(tag, account))
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* The one candidate, when there is exactly one.
|
|
157
|
+
*
|
|
158
|
+
* Drives auto-connect: a single valid option is not a choice, so the server
|
|
159
|
+
* connects it and the host is never shown a dialog with one radio in it.
|
|
160
|
+
*/
|
|
161
|
+
export function soleCandidate<T>(candidates: readonly T[]): T | null {
|
|
162
|
+
return candidates.length === 1 ? candidates[0] : null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* The candidate platforms that carry this account network — the chain read
|
|
167
|
+
* backwards.
|
|
168
|
+
*
|
|
169
|
+
* Most networks answer the platform question by themselves: a PSN ID only
|
|
170
|
+
* exists on a PlayStation, a Steam ID only on PC. Pair that with
|
|
171
|
+
* `soleCandidate` and choosing the account fills the platform in, which is the
|
|
172
|
+
* other half of what makes these two cards feel like one decision.
|
|
173
|
+
*
|
|
174
|
+
* The four cross-platform networks (EA, Epic, Battle.net, Riot) match several
|
|
175
|
+
* and so infer nothing — correctly, because an EA ID really does not say which
|
|
176
|
+
* box you are on.
|
|
177
|
+
*
|
|
178
|
+
* An **untagged** platform is excluded rather than treated as permissive. That
|
|
179
|
+
* is the opposite of `gameAccountsFor`, and deliberate: not knowing what a
|
|
180
|
+
* platform carries is a reason to stay quiet, not a licence to claim it carries
|
|
181
|
+
* this.
|
|
182
|
+
*/
|
|
183
|
+
export function platformsCarrying<P extends CataloguePlatformLike>(
|
|
184
|
+
gameAccount: CatalogueRef | null | undefined,
|
|
185
|
+
candidates: readonly P[]
|
|
186
|
+
): P[] {
|
|
187
|
+
if (!gameAccount) return [];
|
|
188
|
+
|
|
189
|
+
return dedupe(candidates).filter((platform) =>
|
|
190
|
+
(platform.game_accounts ?? []).some((tag) => sameRef(tag, gameAccount))
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Whether this platform is one the game actually runs on. */
|
|
195
|
+
export function isPlatformValidForGame(
|
|
196
|
+
game: CatalogueGameLike | null | undefined,
|
|
197
|
+
platform: CatalogueRef | null | undefined,
|
|
198
|
+
allPlatforms: readonly CataloguePlatformLike[]
|
|
199
|
+
): boolean {
|
|
200
|
+
if (!platform) return false;
|
|
201
|
+
return platformsForGame(game, allPlatforms).some((valid) =>
|
|
202
|
+
sameRef(valid, platform)
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Whether this account network is one the game uses on this platform. */
|
|
207
|
+
export function isGameAccountValidFor(
|
|
208
|
+
game: CatalogueGameLike | null | undefined,
|
|
209
|
+
platform: CataloguePlatformLike | null | undefined,
|
|
210
|
+
gameAccount: CatalogueRef | null | undefined,
|
|
211
|
+
allGameAccounts: readonly CatalogueRef[]
|
|
212
|
+
): boolean {
|
|
213
|
+
if (!gameAccount) return false;
|
|
214
|
+
return gameAccountsFor(game, platform, allGameAccounts).some((valid) =>
|
|
215
|
+
sameRef(valid, gameAccount)
|
|
216
|
+
);
|
|
217
|
+
}
|
package/src/generated/enums.ts
CHANGED
|
@@ -7,6 +7,19 @@ export type FriendshipStatus =
|
|
|
7
7
|
| "pending"
|
|
8
8
|
| "accepted";
|
|
9
9
|
|
|
10
|
+
/** `Game.genre` */
|
|
11
|
+
export type GameGenre =
|
|
12
|
+
| "FPS"
|
|
13
|
+
| "MOBA"
|
|
14
|
+
| "Battle Royale"
|
|
15
|
+
| "Fighting (FGC)"
|
|
16
|
+
| "Sports"
|
|
17
|
+
| "Strategy/RTS"
|
|
18
|
+
| "Sim Racing"
|
|
19
|
+
| "Card Game"
|
|
20
|
+
| "Retro"
|
|
21
|
+
| "Other";
|
|
22
|
+
|
|
10
23
|
/** `GameRequest.customLobbies` */
|
|
11
24
|
export type GameRequestCustomLobbies =
|
|
12
25
|
| "Yes"
|
|
@@ -33,6 +46,20 @@ export type GameRequestTeamPlay =
|
|
|
33
46
|
| "Yes, 4v4+"
|
|
34
47
|
| "Don't know";
|
|
35
48
|
|
|
49
|
+
/** `Game.visibility` */
|
|
50
|
+
export type GameVisibility =
|
|
51
|
+
| "listed"
|
|
52
|
+
| "searchOnly"
|
|
53
|
+
| "archived";
|
|
54
|
+
|
|
55
|
+
/** `Match.disputeReason` */
|
|
56
|
+
export type MatchDisputeReason =
|
|
57
|
+
| "scoreMismatch"
|
|
58
|
+
| "opponentNoShow"
|
|
59
|
+
| "opponentLaggedOut"
|
|
60
|
+
| "rulesViolation"
|
|
61
|
+
| "other";
|
|
62
|
+
|
|
36
63
|
/** `Match.lobbyStatus` */
|
|
37
64
|
export type MatchLobbyStatus =
|
|
38
65
|
| "waiting"
|
|
@@ -40,6 +67,18 @@ export type MatchLobbyStatus =
|
|
|
40
67
|
| "disputed"
|
|
41
68
|
| "completed";
|
|
42
69
|
|
|
70
|
+
/** `Match.resultType` */
|
|
71
|
+
export type MatchResultType =
|
|
72
|
+
| "reported"
|
|
73
|
+
| "hostRecorded"
|
|
74
|
+
| "bye"
|
|
75
|
+
| "noShowHome"
|
|
76
|
+
| "noShowAway"
|
|
77
|
+
| "forfeitHome"
|
|
78
|
+
| "forfeitAway"
|
|
79
|
+
| "disputeResolved"
|
|
80
|
+
| "adminOverride";
|
|
81
|
+
|
|
43
82
|
/** `Match.round` */
|
|
44
83
|
export type MatchRound =
|
|
45
84
|
| "Round 128"
|
|
@@ -64,6 +103,14 @@ export type MatchWinnerSlot =
|
|
|
64
103
|
| "home"
|
|
65
104
|
| "away";
|
|
66
105
|
|
|
106
|
+
/** `Platform.code` */
|
|
107
|
+
export type PlatformCode =
|
|
108
|
+
| "pc"
|
|
109
|
+
| "playstation"
|
|
110
|
+
| "xbox"
|
|
111
|
+
| "nintendo"
|
|
112
|
+
| "mobile";
|
|
113
|
+
|
|
67
114
|
/** `Referral.status` */
|
|
68
115
|
export type ReferralStatus =
|
|
69
116
|
| "pending"
|
package/src/generated/inputs.ts
CHANGED
|
@@ -9,13 +9,18 @@ import type {
|
|
|
9
9
|
} from '../contracts';
|
|
10
10
|
import type {
|
|
11
11
|
FriendshipStatus,
|
|
12
|
+
GameGenre,
|
|
12
13
|
GameRequestCustomLobbies,
|
|
13
14
|
GameRequestGenre,
|
|
14
15
|
GameRequestTeamPlay,
|
|
16
|
+
GameVisibility,
|
|
17
|
+
MatchDisputeReason,
|
|
15
18
|
MatchLobbyStatus,
|
|
19
|
+
MatchResultType,
|
|
16
20
|
MatchRound,
|
|
17
21
|
MatchStreamPlatform,
|
|
18
22
|
MatchWinnerSlot,
|
|
23
|
+
PlatformCode,
|
|
19
24
|
ReferralStatus,
|
|
20
25
|
TeamCurrentStatus,
|
|
21
26
|
TeamInviteStatus,
|
|
@@ -81,10 +86,14 @@ export interface GameInput {
|
|
|
81
86
|
imgThumb?: MediaInput;
|
|
82
87
|
imgMain?: MediaInput;
|
|
83
88
|
tournaments?: RelationInput | RelationInput[];
|
|
84
|
-
isTrending?: boolean;
|
|
85
89
|
user_game_stats?: RelationInput | RelationInput[];
|
|
86
90
|
team_game_stats?: RelationInput | RelationInput[];
|
|
87
91
|
imgMainPosition?: string;
|
|
92
|
+
visibility?: GameVisibility;
|
|
93
|
+
genre?: GameGenre;
|
|
94
|
+
sortOrder?: number;
|
|
95
|
+
platforms?: RelationInput | RelationInput[];
|
|
96
|
+
game_accounts?: RelationInput | RelationInput[];
|
|
88
97
|
}
|
|
89
98
|
|
|
90
99
|
/** Write payload for `game-account`. */
|
|
@@ -93,6 +102,8 @@ export interface GameAccountInput {
|
|
|
93
102
|
tournaments?: RelationInput | RelationInput[];
|
|
94
103
|
user_game_accounts?: RelationInput | RelationInput[];
|
|
95
104
|
imgThumb?: MediaInput;
|
|
105
|
+
games?: RelationInput | RelationInput[];
|
|
106
|
+
platforms?: RelationInput | RelationInput[];
|
|
96
107
|
}
|
|
97
108
|
|
|
98
109
|
/** Write payload for `game-request`. */
|
|
@@ -169,6 +180,13 @@ export interface MatchInput {
|
|
|
169
180
|
isStreaming?: boolean;
|
|
170
181
|
streamHandle?: string;
|
|
171
182
|
streamPlatform?: MatchStreamPlatform;
|
|
183
|
+
startedAt?: string;
|
|
184
|
+
endedAt?: string;
|
|
185
|
+
resultType?: MatchResultType;
|
|
186
|
+
resultNote?: string;
|
|
187
|
+
disputeReason?: MatchDisputeReason;
|
|
188
|
+
disputeNote?: string;
|
|
189
|
+
disputedBy?: RelationInput;
|
|
172
190
|
}
|
|
173
191
|
|
|
174
192
|
/** Write payload for `match-message`. */
|
|
@@ -194,7 +212,10 @@ export interface NotificationInput {
|
|
|
194
212
|
/** Write payload for `platform`. */
|
|
195
213
|
export interface PlatformInput {
|
|
196
214
|
name?: string;
|
|
215
|
+
code?: PlatformCode;
|
|
197
216
|
tournaments?: RelationInput | RelationInput[];
|
|
217
|
+
games?: RelationInput | RelationInput[];
|
|
218
|
+
game_accounts?: RelationInput | RelationInput[];
|
|
198
219
|
}
|
|
199
220
|
|
|
200
221
|
/** Write payload for `preset-avatar`. */
|
package/src/generated/models.ts
CHANGED
|
@@ -10,13 +10,18 @@ import type {
|
|
|
10
10
|
} from '../contracts';
|
|
11
11
|
import type {
|
|
12
12
|
FriendshipStatus,
|
|
13
|
+
GameGenre,
|
|
13
14
|
GameRequestCustomLobbies,
|
|
14
15
|
GameRequestGenre,
|
|
15
16
|
GameRequestTeamPlay,
|
|
17
|
+
GameVisibility,
|
|
18
|
+
MatchDisputeReason,
|
|
16
19
|
MatchLobbyStatus,
|
|
20
|
+
MatchResultType,
|
|
17
21
|
MatchRound,
|
|
18
22
|
MatchStreamPlatform,
|
|
19
23
|
MatchWinnerSlot,
|
|
24
|
+
PlatformCode,
|
|
20
25
|
ReferralStatus,
|
|
21
26
|
TeamCurrentStatus,
|
|
22
27
|
TeamInviteStatus,
|
|
@@ -82,10 +87,14 @@ export interface Game extends StrapiDocument {
|
|
|
82
87
|
imgThumb?: StrapiMedia | null;
|
|
83
88
|
imgMain?: StrapiMedia | null;
|
|
84
89
|
tournaments?: Tournament[];
|
|
85
|
-
isTrending?: boolean | null;
|
|
86
90
|
user_game_stats?: UserGameStat[];
|
|
87
91
|
team_game_stats?: TeamGameStat[];
|
|
88
92
|
imgMainPosition?: string | null;
|
|
93
|
+
visibility?: GameVisibility | null;
|
|
94
|
+
genre?: GameGenre | null;
|
|
95
|
+
sortOrder?: number | null;
|
|
96
|
+
platforms?: Platform[];
|
|
97
|
+
game_accounts?: GameAccount[];
|
|
89
98
|
}
|
|
90
99
|
|
|
91
100
|
/** `game-account` */
|
|
@@ -94,6 +103,8 @@ export interface GameAccount extends StrapiDocument {
|
|
|
94
103
|
tournaments?: Tournament[];
|
|
95
104
|
user_game_accounts?: UserGameAccount[];
|
|
96
105
|
imgThumb?: StrapiMedia | null;
|
|
106
|
+
games?: Game[];
|
|
107
|
+
platforms?: Platform[];
|
|
97
108
|
}
|
|
98
109
|
|
|
99
110
|
/** `game-request` */
|
|
@@ -170,6 +181,13 @@ export interface Match extends StrapiDocument {
|
|
|
170
181
|
isStreaming?: boolean | null;
|
|
171
182
|
streamHandle?: string | null;
|
|
172
183
|
streamPlatform?: MatchStreamPlatform | null;
|
|
184
|
+
startedAt?: string | null;
|
|
185
|
+
endedAt?: string | null;
|
|
186
|
+
resultType?: MatchResultType | null;
|
|
187
|
+
resultNote?: string | null;
|
|
188
|
+
disputeReason?: MatchDisputeReason | null;
|
|
189
|
+
disputeNote?: string | null;
|
|
190
|
+
disputedBy?: User | null;
|
|
173
191
|
}
|
|
174
192
|
|
|
175
193
|
/** `match-message` */
|
|
@@ -195,7 +213,10 @@ export interface Notification extends StrapiDocument {
|
|
|
195
213
|
/** `platform` */
|
|
196
214
|
export interface Platform extends StrapiDocument {
|
|
197
215
|
name?: string | null;
|
|
216
|
+
code?: PlatformCode | null;
|
|
198
217
|
tournaments?: Tournament[];
|
|
218
|
+
games?: Game[];
|
|
219
|
+
game_accounts?: GameAccount[];
|
|
199
220
|
}
|
|
200
221
|
|
|
201
222
|
/** `preset-avatar` */
|
package/src/index.ts
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
// Generated from the Strapi schemas — see scripts/sync-from-strapi.js
|
|
2
|
-
export * from './generated';
|
|
3
|
-
|
|
4
|
-
// Hand-written contract primitives (response envelopes, Populated, media)
|
|
5
|
-
export * from './contracts';
|
|
6
|
-
|
|
7
|
-
// Hand-written types with no Strapi content type behind them
|
|
8
|
-
export * from './manual';
|
|
9
|
-
|
|
10
|
-
// Bracket shape — shared by the backend generator and Storybook fixtures
|
|
11
|
-
export * from './bracket';
|
|
12
|
-
|
|
13
|
-
//
|
|
14
|
-
|
|
1
|
+
// Generated from the Strapi schemas — see scripts/sync-from-strapi.js
|
|
2
|
+
export * from './generated';
|
|
3
|
+
|
|
4
|
+
// Hand-written contract primitives (response envelopes, Populated, media)
|
|
5
|
+
export * from './contracts';
|
|
6
|
+
|
|
7
|
+
// Hand-written types with no Strapi content type behind them
|
|
8
|
+
export * from './manual';
|
|
9
|
+
|
|
10
|
+
// Bracket shape — shared by the backend generator and Storybook fixtures
|
|
11
|
+
export * from './bracket';
|
|
12
|
+
|
|
13
|
+
// Game/platform/account catalogue — the game -> platform -> account chain,
|
|
14
|
+
// applied identically by the backend resolver and the client's pickers
|
|
15
|
+
export * from './gameCatalogue';
|
|
16
|
+
|
|
17
|
+
// Data
|
|
18
|
+
export * from './data/countriesList';
|
|
19
|
+
export * from './data/gameGenres';
|