xpt-shared-types 1.3.0 → 1.4.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/README.md +6 -6
- package/dist/gameCatalogue.d.ts +109 -0
- package/dist/gameCatalogue.js +164 -0
- package/dist/generated/enums.d.ts +2 -4
- package/dist/generated/inputs.d.ts +12 -17
- package/dist/generated/models.d.ts +12 -17
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/package.json +1 -1
- package/src/gameCatalogue.ts +217 -0
- package/src/generated/enums.ts +7 -17
- package/src/generated/inputs.ts +12 -20
- package/src/generated/models.ts +12 -20
- package/src/index.ts +18 -14
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ frontend cannot consume.
|
|
|
54
54
|
**Everything except `id` and `documentId` is optional.** Not just relations.
|
|
55
55
|
This codebase uses Strapi's `fields` selection pervasively, so even a
|
|
56
56
|
schema-`required` attribute like `user.email` is absent from most responses.
|
|
57
|
-
The base model is the
|
|
57
|
+
The base model is the _minimum guarantee_, not the full row.
|
|
58
58
|
|
|
59
59
|
**Single values are `?: T | null`.** Strapi sends `null` for an unset value
|
|
60
60
|
rather than omitting the key. Lists come back as `[]`, so they are not nullable.
|
|
@@ -83,7 +83,7 @@ asked for:
|
|
|
83
83
|
```ts
|
|
84
84
|
const tournament = await getTournament(slug); // Populated<Tournament, 'game' | 'prizes'>
|
|
85
85
|
|
|
86
|
-
tournament.game.title;
|
|
86
|
+
tournament.game.title; // ok — the annotation says it was populated
|
|
87
87
|
tournament.region?.name; // still optional — this query did not populate it
|
|
88
88
|
```
|
|
89
89
|
|
|
@@ -95,10 +95,10 @@ silently disables exactly the checking this package exists to provide.
|
|
|
95
95
|
|
|
96
96
|
The two apps are on different Yarn majors, so they link differently:
|
|
97
97
|
|
|
98
|
-
| Repo
|
|
99
|
-
|
|
|
100
|
-
| `xpt-strapi` | 1.22.5 (classic) | `"xpt-shared-types": "link:../xpt-shared-types"`
|
|
101
|
-
| `xpt-client` | 4.1.1 (Berry)
|
|
98
|
+
| Repo | Yarn | Dependency |
|
|
99
|
+
| ------------ | ---------------- | -------------------------------------------------- |
|
|
100
|
+
| `xpt-strapi` | 1.22.5 (classic) | `"xpt-shared-types": "link:../xpt-shared-types"` |
|
|
101
|
+
| `xpt-client` | 4.1.1 (Berry) | `"xpt-shared-types": "portal:../xpt-shared-types"` |
|
|
102
102
|
|
|
103
103
|
`portal:` is Berry-only and fails on Yarn 1. Both symlink to this directory, so
|
|
104
104
|
edits are picked up after `yarn build` with no publish step.
|
|
@@ -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
|
+
}
|
|
@@ -14,8 +14,8 @@ export type MatchRound = "Round 128" | "Round 64" | "Round 32" | "Round 16" | "Q
|
|
|
14
14
|
export type MatchStreamPlatform = "twitch" | "youtube" | "kick" | "other";
|
|
15
15
|
/** `Match.winnerSlot` */
|
|
16
16
|
export type MatchWinnerSlot = "home" | "away";
|
|
17
|
-
/** `
|
|
18
|
-
export type
|
|
17
|
+
/** `Platform.code` */
|
|
18
|
+
export type PlatformCode = "pc" | "playstation" | "xbox" | "nintendo" | "mobile";
|
|
19
19
|
/** `Referral.status` */
|
|
20
20
|
export type ReferralStatus = "pending" | "completed";
|
|
21
21
|
/** `Team.current_status` */
|
|
@@ -34,8 +34,6 @@ export type TournamentParticipantEntryType = "solo" | "team";
|
|
|
34
34
|
export type TournamentParticipantStatus = "registered" | "active" | "eliminated" | "completed";
|
|
35
35
|
/** `TournamentRole.role` */
|
|
36
36
|
export type TournamentRoleRole = "moderator" | "admin";
|
|
37
|
-
/** `TournamentStage.stageName` */
|
|
38
|
-
export type TournamentStageStageName = "Round 128" | "Round 64" | "Round 32" | "Round 16" | "Quarter Final" | "Semi-Final" | "Third Round" | "Finals" | "League";
|
|
39
37
|
/** `Tournament.teamSize` */
|
|
40
38
|
export type TournamentTeamSize = "one" | "two" | "three" | "four" | "five";
|
|
41
39
|
/** `Tournament.type` */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BlocksContent, MediaInput, RelationInput } from '../contracts';
|
|
2
|
-
import type { FriendshipStatus, GameRequestCustomLobbies, GameRequestGenre, GameRequestTeamPlay, MatchLobbyStatus, MatchRound, MatchStreamPlatform, MatchWinnerSlot,
|
|
2
|
+
import type { FriendshipStatus, GameRequestCustomLobbies, GameRequestGenre, GameRequestTeamPlay, MatchLobbyStatus, 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;
|
|
@@ -48,6 +48,8 @@ export interface GameInput {
|
|
|
48
48
|
user_game_stats?: RelationInput | RelationInput[];
|
|
49
49
|
team_game_stats?: RelationInput | RelationInput[];
|
|
50
50
|
imgMainPosition?: string;
|
|
51
|
+
platforms?: RelationInput | RelationInput[];
|
|
52
|
+
game_accounts?: RelationInput | RelationInput[];
|
|
51
53
|
}
|
|
52
54
|
/** Write payload for `game-account`. */
|
|
53
55
|
export interface GameAccountInput {
|
|
@@ -55,6 +57,8 @@ export interface GameAccountInput {
|
|
|
55
57
|
tournaments?: RelationInput | RelationInput[];
|
|
56
58
|
user_game_accounts?: RelationInput | RelationInput[];
|
|
57
59
|
imgThumb?: MediaInput;
|
|
60
|
+
games?: RelationInput | RelationInput[];
|
|
61
|
+
platforms?: RelationInput | RelationInput[];
|
|
58
62
|
}
|
|
59
63
|
/** Write payload for `game-request`. */
|
|
60
64
|
export interface GameRequestInput {
|
|
@@ -148,7 +152,10 @@ export interface NotificationInput {
|
|
|
148
152
|
/** Write payload for `platform`. */
|
|
149
153
|
export interface PlatformInput {
|
|
150
154
|
name?: string;
|
|
155
|
+
code?: PlatformCode;
|
|
151
156
|
tournaments?: RelationInput | RelationInput[];
|
|
157
|
+
games?: RelationInput | RelationInput[];
|
|
158
|
+
game_accounts?: RelationInput | RelationInput[];
|
|
152
159
|
}
|
|
153
160
|
/** Write payload for `preset-avatar`. */
|
|
154
161
|
export interface PresetAvatarInput {
|
|
@@ -171,13 +178,6 @@ export interface PrivacyInput {
|
|
|
171
178
|
content?: BlocksContent;
|
|
172
179
|
title?: string;
|
|
173
180
|
}
|
|
174
|
-
/** Write payload for `prize`. */
|
|
175
|
-
export interface PrizeInput {
|
|
176
|
-
description?: string;
|
|
177
|
-
value?: number;
|
|
178
|
-
tournament?: RelationInput;
|
|
179
|
-
rank?: PrizeRank;
|
|
180
|
-
}
|
|
181
181
|
/** Write payload for `referral`. */
|
|
182
182
|
export interface ReferralInput {
|
|
183
183
|
referrer?: RelationInput;
|
|
@@ -277,18 +277,19 @@ export interface TournamentInput {
|
|
|
277
277
|
game?: RelationInput;
|
|
278
278
|
currentStatus?: TournamentCurrentStatus;
|
|
279
279
|
group?: RelationInput;
|
|
280
|
-
prizes?:
|
|
280
|
+
prizes?: unknown;
|
|
281
281
|
tournament_participants?: RelationInput | RelationInput[];
|
|
282
282
|
tournament_roles?: RelationInput | RelationInput[];
|
|
283
283
|
league_tables?: RelationInput | RelationInput[];
|
|
284
284
|
rules?: string;
|
|
285
285
|
hasThirdPlace?: boolean;
|
|
286
|
+
hasMatchLobby?: boolean;
|
|
286
287
|
isPrivate?: boolean;
|
|
287
288
|
checkInTime?: number;
|
|
288
289
|
region?: RelationInput;
|
|
289
290
|
platform?: RelationInput;
|
|
290
291
|
gameAccount?: RelationInput;
|
|
291
|
-
tournament_stages?:
|
|
292
|
+
tournament_stages?: unknown;
|
|
292
293
|
prizesDistributed?: boolean;
|
|
293
294
|
discordUrl?: string;
|
|
294
295
|
twitterUrl?: string;
|
|
@@ -320,13 +321,6 @@ export interface TournamentRoleInput {
|
|
|
320
321
|
users_permissions_user?: RelationInput;
|
|
321
322
|
tournament?: RelationInput;
|
|
322
323
|
}
|
|
323
|
-
/** Write payload for `tournament-stage`. */
|
|
324
|
-
export interface TournamentStageInput {
|
|
325
|
-
stageName?: TournamentStageStageName;
|
|
326
|
-
bestOf?: number;
|
|
327
|
-
tournament?: RelationInput;
|
|
328
|
-
isThirdPlace?: boolean;
|
|
329
|
-
}
|
|
330
324
|
/** Write payload for `user`. */
|
|
331
325
|
export interface UserInput {
|
|
332
326
|
username?: string;
|
|
@@ -354,6 +348,7 @@ export interface UserInput {
|
|
|
354
348
|
notificationPreferences?: unknown;
|
|
355
349
|
referralCode?: string;
|
|
356
350
|
referralRewardClaimed?: boolean;
|
|
351
|
+
lastSeenAt?: string;
|
|
357
352
|
}
|
|
358
353
|
/** Write payload for `user-game-account`. */
|
|
359
354
|
export interface UserGameAccountInput {
|
|
@@ -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,
|
|
2
|
+
import type { FriendshipStatus, GameRequestCustomLobbies, GameRequestGenre, GameRequestTeamPlay, MatchLobbyStatus, 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;
|
|
@@ -48,6 +48,8 @@ export interface Game extends StrapiDocument {
|
|
|
48
48
|
user_game_stats?: UserGameStat[];
|
|
49
49
|
team_game_stats?: TeamGameStat[];
|
|
50
50
|
imgMainPosition?: string | null;
|
|
51
|
+
platforms?: Platform[];
|
|
52
|
+
game_accounts?: GameAccount[];
|
|
51
53
|
}
|
|
52
54
|
/** `game-account` */
|
|
53
55
|
export interface GameAccount extends StrapiDocument {
|
|
@@ -55,6 +57,8 @@ export interface GameAccount extends StrapiDocument {
|
|
|
55
57
|
tournaments?: Tournament[];
|
|
56
58
|
user_game_accounts?: UserGameAccount[];
|
|
57
59
|
imgThumb?: StrapiMedia | null;
|
|
60
|
+
games?: Game[];
|
|
61
|
+
platforms?: Platform[];
|
|
58
62
|
}
|
|
59
63
|
/** `game-request` */
|
|
60
64
|
export interface GameRequest extends StrapiDocument {
|
|
@@ -148,7 +152,10 @@ export interface Notification extends StrapiDocument {
|
|
|
148
152
|
/** `platform` */
|
|
149
153
|
export interface Platform extends StrapiDocument {
|
|
150
154
|
name?: string | null;
|
|
155
|
+
code?: PlatformCode | null;
|
|
151
156
|
tournaments?: Tournament[];
|
|
157
|
+
games?: Game[];
|
|
158
|
+
game_accounts?: GameAccount[];
|
|
152
159
|
}
|
|
153
160
|
/** `preset-avatar` */
|
|
154
161
|
export interface PresetAvatar extends StrapiDocument {
|
|
@@ -171,13 +178,6 @@ export interface Privacy extends StrapiDocument {
|
|
|
171
178
|
content?: BlocksContent | null;
|
|
172
179
|
title?: string | null;
|
|
173
180
|
}
|
|
174
|
-
/** `prize` */
|
|
175
|
-
export interface Prize extends StrapiDocument {
|
|
176
|
-
description?: string | null;
|
|
177
|
-
value?: number | null;
|
|
178
|
-
tournament?: Tournament | null;
|
|
179
|
-
rank?: PrizeRank | null;
|
|
180
|
-
}
|
|
181
181
|
/** `referral` */
|
|
182
182
|
export interface Referral extends StrapiDocument {
|
|
183
183
|
referrer?: User | null;
|
|
@@ -277,18 +277,19 @@ export interface Tournament extends StrapiDocument {
|
|
|
277
277
|
game?: Game | null;
|
|
278
278
|
currentStatus?: TournamentCurrentStatus | null;
|
|
279
279
|
group?: Group | null;
|
|
280
|
-
prizes?:
|
|
280
|
+
prizes?: unknown | null;
|
|
281
281
|
tournament_participants?: TournamentParticipant[];
|
|
282
282
|
tournament_roles?: TournamentRole[];
|
|
283
283
|
league_tables?: LeagueTable[];
|
|
284
284
|
rules?: string | null;
|
|
285
285
|
hasThirdPlace?: boolean | null;
|
|
286
|
+
hasMatchLobby?: boolean | null;
|
|
286
287
|
isPrivate?: boolean | null;
|
|
287
288
|
checkInTime?: number | null;
|
|
288
289
|
region?: Region | null;
|
|
289
290
|
platform?: Platform | null;
|
|
290
291
|
gameAccount?: GameAccount | null;
|
|
291
|
-
tournament_stages?:
|
|
292
|
+
tournament_stages?: unknown | null;
|
|
292
293
|
prizesDistributed?: boolean | null;
|
|
293
294
|
discordUrl?: string | null;
|
|
294
295
|
twitterUrl?: string | null;
|
|
@@ -320,13 +321,6 @@ export interface TournamentRole extends StrapiDocument {
|
|
|
320
321
|
users_permissions_user?: User | null;
|
|
321
322
|
tournament?: Tournament | null;
|
|
322
323
|
}
|
|
323
|
-
/** `tournament-stage` */
|
|
324
|
-
export interface TournamentStage extends StrapiDocument {
|
|
325
|
-
stageName?: TournamentStageStageName | null;
|
|
326
|
-
bestOf?: number | null;
|
|
327
|
-
tournament?: Tournament | null;
|
|
328
|
-
isThirdPlace?: boolean | null;
|
|
329
|
-
}
|
|
330
324
|
/** `user` */
|
|
331
325
|
export interface User extends StrapiDocument {
|
|
332
326
|
username?: string | null;
|
|
@@ -354,6 +348,7 @@ export interface User extends StrapiDocument {
|
|
|
354
348
|
notificationPreferences?: unknown | null;
|
|
355
349
|
referralCode?: string | null;
|
|
356
350
|
referralRewardClaimed?: boolean | null;
|
|
351
|
+
lastSeenAt?: string | null;
|
|
357
352
|
}
|
|
358
353
|
/** `user-game-account` */
|
|
359
354
|
export interface UserGameAccount extends StrapiDocument {
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -22,5 +22,8 @@ __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);
|
package/package.json
CHANGED
|
@@ -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
|
@@ -64,11 +64,13 @@ export type MatchWinnerSlot =
|
|
|
64
64
|
| "home"
|
|
65
65
|
| "away";
|
|
66
66
|
|
|
67
|
-
/** `
|
|
68
|
-
export type
|
|
69
|
-
| "
|
|
70
|
-
| "
|
|
71
|
-
| "
|
|
67
|
+
/** `Platform.code` */
|
|
68
|
+
export type PlatformCode =
|
|
69
|
+
| "pc"
|
|
70
|
+
| "playstation"
|
|
71
|
+
| "xbox"
|
|
72
|
+
| "nintendo"
|
|
73
|
+
| "mobile";
|
|
72
74
|
|
|
73
75
|
/** `Referral.status` */
|
|
74
76
|
export type ReferralStatus =
|
|
@@ -129,18 +131,6 @@ export type TournamentRoleRole =
|
|
|
129
131
|
| "moderator"
|
|
130
132
|
| "admin";
|
|
131
133
|
|
|
132
|
-
/** `TournamentStage.stageName` */
|
|
133
|
-
export type TournamentStageStageName =
|
|
134
|
-
| "Round 128"
|
|
135
|
-
| "Round 64"
|
|
136
|
-
| "Round 32"
|
|
137
|
-
| "Round 16"
|
|
138
|
-
| "Quarter Final"
|
|
139
|
-
| "Semi-Final"
|
|
140
|
-
| "Third Round"
|
|
141
|
-
| "Finals"
|
|
142
|
-
| "League";
|
|
143
|
-
|
|
144
134
|
/** `Tournament.teamSize` */
|
|
145
135
|
export type TournamentTeamSize =
|
|
146
136
|
| "one"
|
package/src/generated/inputs.ts
CHANGED
|
@@ -16,7 +16,7 @@ import type {
|
|
|
16
16
|
MatchRound,
|
|
17
17
|
MatchStreamPlatform,
|
|
18
18
|
MatchWinnerSlot,
|
|
19
|
-
|
|
19
|
+
PlatformCode,
|
|
20
20
|
ReferralStatus,
|
|
21
21
|
TeamCurrentStatus,
|
|
22
22
|
TeamInviteStatus,
|
|
@@ -26,7 +26,6 @@ import type {
|
|
|
26
26
|
TournamentParticipantEntryType,
|
|
27
27
|
TournamentParticipantStatus,
|
|
28
28
|
TournamentRoleRole,
|
|
29
|
-
TournamentStageStageName,
|
|
30
29
|
TournamentTeamSize,
|
|
31
30
|
TournamentType,
|
|
32
31
|
UserTransactionStripeStatus,
|
|
@@ -87,6 +86,8 @@ export interface GameInput {
|
|
|
87
86
|
user_game_stats?: RelationInput | RelationInput[];
|
|
88
87
|
team_game_stats?: RelationInput | RelationInput[];
|
|
89
88
|
imgMainPosition?: string;
|
|
89
|
+
platforms?: RelationInput | RelationInput[];
|
|
90
|
+
game_accounts?: RelationInput | RelationInput[];
|
|
90
91
|
}
|
|
91
92
|
|
|
92
93
|
/** Write payload for `game-account`. */
|
|
@@ -95,6 +96,8 @@ export interface GameAccountInput {
|
|
|
95
96
|
tournaments?: RelationInput | RelationInput[];
|
|
96
97
|
user_game_accounts?: RelationInput | RelationInput[];
|
|
97
98
|
imgThumb?: MediaInput;
|
|
99
|
+
games?: RelationInput | RelationInput[];
|
|
100
|
+
platforms?: RelationInput | RelationInput[];
|
|
98
101
|
}
|
|
99
102
|
|
|
100
103
|
/** Write payload for `game-request`. */
|
|
@@ -196,7 +199,10 @@ export interface NotificationInput {
|
|
|
196
199
|
/** Write payload for `platform`. */
|
|
197
200
|
export interface PlatformInput {
|
|
198
201
|
name?: string;
|
|
202
|
+
code?: PlatformCode;
|
|
199
203
|
tournaments?: RelationInput | RelationInput[];
|
|
204
|
+
games?: RelationInput | RelationInput[];
|
|
205
|
+
game_accounts?: RelationInput | RelationInput[];
|
|
200
206
|
}
|
|
201
207
|
|
|
202
208
|
/** Write payload for `preset-avatar`. */
|
|
@@ -225,14 +231,6 @@ export interface PrivacyInput {
|
|
|
225
231
|
title?: string;
|
|
226
232
|
}
|
|
227
233
|
|
|
228
|
-
/** Write payload for `prize`. */
|
|
229
|
-
export interface PrizeInput {
|
|
230
|
-
description?: string;
|
|
231
|
-
value?: number;
|
|
232
|
-
tournament?: RelationInput;
|
|
233
|
-
rank?: PrizeRank;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
234
|
/** Write payload for `referral`. */
|
|
237
235
|
export interface ReferralInput {
|
|
238
236
|
referrer?: RelationInput;
|
|
@@ -341,18 +339,19 @@ export interface TournamentInput {
|
|
|
341
339
|
game?: RelationInput;
|
|
342
340
|
currentStatus?: TournamentCurrentStatus;
|
|
343
341
|
group?: RelationInput;
|
|
344
|
-
prizes?:
|
|
342
|
+
prizes?: unknown;
|
|
345
343
|
tournament_participants?: RelationInput | RelationInput[];
|
|
346
344
|
tournament_roles?: RelationInput | RelationInput[];
|
|
347
345
|
league_tables?: RelationInput | RelationInput[];
|
|
348
346
|
rules?: string;
|
|
349
347
|
hasThirdPlace?: boolean;
|
|
348
|
+
hasMatchLobby?: boolean;
|
|
350
349
|
isPrivate?: boolean;
|
|
351
350
|
checkInTime?: number;
|
|
352
351
|
region?: RelationInput;
|
|
353
352
|
platform?: RelationInput;
|
|
354
353
|
gameAccount?: RelationInput;
|
|
355
|
-
tournament_stages?:
|
|
354
|
+
tournament_stages?: unknown;
|
|
356
355
|
prizesDistributed?: boolean;
|
|
357
356
|
discordUrl?: string;
|
|
358
357
|
twitterUrl?: string;
|
|
@@ -388,14 +387,6 @@ export interface TournamentRoleInput {
|
|
|
388
387
|
tournament?: RelationInput;
|
|
389
388
|
}
|
|
390
389
|
|
|
391
|
-
/** Write payload for `tournament-stage`. */
|
|
392
|
-
export interface TournamentStageInput {
|
|
393
|
-
stageName?: TournamentStageStageName;
|
|
394
|
-
bestOf?: number;
|
|
395
|
-
tournament?: RelationInput;
|
|
396
|
-
isThirdPlace?: boolean;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
390
|
/** Write payload for `user`. */
|
|
400
391
|
export interface UserInput {
|
|
401
392
|
username?: string;
|
|
@@ -423,6 +414,7 @@ export interface UserInput {
|
|
|
423
414
|
notificationPreferences?: unknown;
|
|
424
415
|
referralCode?: string;
|
|
425
416
|
referralRewardClaimed?: boolean;
|
|
417
|
+
lastSeenAt?: string;
|
|
426
418
|
}
|
|
427
419
|
|
|
428
420
|
/** Write payload for `user-game-account`. */
|
package/src/generated/models.ts
CHANGED
|
@@ -17,7 +17,7 @@ import type {
|
|
|
17
17
|
MatchRound,
|
|
18
18
|
MatchStreamPlatform,
|
|
19
19
|
MatchWinnerSlot,
|
|
20
|
-
|
|
20
|
+
PlatformCode,
|
|
21
21
|
ReferralStatus,
|
|
22
22
|
TeamCurrentStatus,
|
|
23
23
|
TeamInviteStatus,
|
|
@@ -27,7 +27,6 @@ import type {
|
|
|
27
27
|
TournamentParticipantEntryType,
|
|
28
28
|
TournamentParticipantStatus,
|
|
29
29
|
TournamentRoleRole,
|
|
30
|
-
TournamentStageStageName,
|
|
31
30
|
TournamentTeamSize,
|
|
32
31
|
TournamentType,
|
|
33
32
|
UserTransactionStripeStatus,
|
|
@@ -88,6 +87,8 @@ export interface Game extends StrapiDocument {
|
|
|
88
87
|
user_game_stats?: UserGameStat[];
|
|
89
88
|
team_game_stats?: TeamGameStat[];
|
|
90
89
|
imgMainPosition?: string | null;
|
|
90
|
+
platforms?: Platform[];
|
|
91
|
+
game_accounts?: GameAccount[];
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
/** `game-account` */
|
|
@@ -96,6 +97,8 @@ export interface GameAccount extends StrapiDocument {
|
|
|
96
97
|
tournaments?: Tournament[];
|
|
97
98
|
user_game_accounts?: UserGameAccount[];
|
|
98
99
|
imgThumb?: StrapiMedia | null;
|
|
100
|
+
games?: Game[];
|
|
101
|
+
platforms?: Platform[];
|
|
99
102
|
}
|
|
100
103
|
|
|
101
104
|
/** `game-request` */
|
|
@@ -197,7 +200,10 @@ export interface Notification extends StrapiDocument {
|
|
|
197
200
|
/** `platform` */
|
|
198
201
|
export interface Platform extends StrapiDocument {
|
|
199
202
|
name?: string | null;
|
|
203
|
+
code?: PlatformCode | null;
|
|
200
204
|
tournaments?: Tournament[];
|
|
205
|
+
games?: Game[];
|
|
206
|
+
game_accounts?: GameAccount[];
|
|
201
207
|
}
|
|
202
208
|
|
|
203
209
|
/** `preset-avatar` */
|
|
@@ -226,14 +232,6 @@ export interface Privacy extends StrapiDocument {
|
|
|
226
232
|
title?: string | null;
|
|
227
233
|
}
|
|
228
234
|
|
|
229
|
-
/** `prize` */
|
|
230
|
-
export interface Prize extends StrapiDocument {
|
|
231
|
-
description?: string | null;
|
|
232
|
-
value?: number | null;
|
|
233
|
-
tournament?: Tournament | null;
|
|
234
|
-
rank?: PrizeRank | null;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
235
|
/** `referral` */
|
|
238
236
|
export interface Referral extends StrapiDocument {
|
|
239
237
|
referrer?: User | null;
|
|
@@ -342,18 +340,19 @@ export interface Tournament extends StrapiDocument {
|
|
|
342
340
|
game?: Game | null;
|
|
343
341
|
currentStatus?: TournamentCurrentStatus | null;
|
|
344
342
|
group?: Group | null;
|
|
345
|
-
prizes?:
|
|
343
|
+
prizes?: unknown | null;
|
|
346
344
|
tournament_participants?: TournamentParticipant[];
|
|
347
345
|
tournament_roles?: TournamentRole[];
|
|
348
346
|
league_tables?: LeagueTable[];
|
|
349
347
|
rules?: string | null;
|
|
350
348
|
hasThirdPlace?: boolean | null;
|
|
349
|
+
hasMatchLobby?: boolean | null;
|
|
351
350
|
isPrivate?: boolean | null;
|
|
352
351
|
checkInTime?: number | null;
|
|
353
352
|
region?: Region | null;
|
|
354
353
|
platform?: Platform | null;
|
|
355
354
|
gameAccount?: GameAccount | null;
|
|
356
|
-
tournament_stages?:
|
|
355
|
+
tournament_stages?: unknown | null;
|
|
357
356
|
prizesDistributed?: boolean | null;
|
|
358
357
|
discordUrl?: string | null;
|
|
359
358
|
twitterUrl?: string | null;
|
|
@@ -389,14 +388,6 @@ export interface TournamentRole extends StrapiDocument {
|
|
|
389
388
|
tournament?: Tournament | null;
|
|
390
389
|
}
|
|
391
390
|
|
|
392
|
-
/** `tournament-stage` */
|
|
393
|
-
export interface TournamentStage extends StrapiDocument {
|
|
394
|
-
stageName?: TournamentStageStageName | null;
|
|
395
|
-
bestOf?: number | null;
|
|
396
|
-
tournament?: Tournament | null;
|
|
397
|
-
isThirdPlace?: boolean | null;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
391
|
/** `user` */
|
|
401
392
|
export interface User extends StrapiDocument {
|
|
402
393
|
username?: string | null;
|
|
@@ -424,6 +415,7 @@ export interface User extends StrapiDocument {
|
|
|
424
415
|
notificationPreferences?: unknown | null;
|
|
425
416
|
referralCode?: string | null;
|
|
426
417
|
referralRewardClaimed?: boolean | null;
|
|
418
|
+
lastSeenAt?: string | null;
|
|
427
419
|
}
|
|
428
420
|
|
|
429
421
|
/** `user-game-account` */
|
package/src/index.ts
CHANGED
|
@@ -1,14 +1,18 @@
|
|
|
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';
|