xpt-shared-types 1.16.0 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/generated/enums.d.ts +8 -0
- package/dist/generated/inputs.d.ts +26 -1
- package/dist/generated/models.d.ts +26 -1
- package/dist/manual/gameAccountHandle.d.ts +128 -0
- package/dist/manual/gameAccountHandle.js +221 -0
- package/dist/manual/index.d.ts +2 -0
- package/dist/manual/index.js +2 -0
- package/dist/manual/playerBan.d.ts +120 -0
- package/dist/manual/playerBan.js +45 -0
- package/dist/manual/tournamentStaff.d.ts +3 -1
- package/dist/manual/tournamentStaff.js +1 -0
- package/package.json +1 -1
- package/src/generated/enums.ts +31 -0
- package/src/generated/inputs.ts +31 -0
- package/src/generated/models.ts +31 -0
- package/src/manual/gameAccountHandle.ts +306 -0
- package/src/manual/index.ts +2 -0
- package/src/manual/playerBan.ts +123 -0
- package/src/manual/tournamentStaff.ts +4 -1
|
@@ -4,6 +4,10 @@ export type AdminActionTargetType = "user" | "tournament" | "match" | "transacti
|
|
|
4
4
|
export type FaqCategory = "gettingStarted" | "tournamentsMatches" | "hosting" | "walletCoins" | "account";
|
|
5
5
|
/** `Friendship.status` */
|
|
6
6
|
export type FriendshipStatus = "pending" | "accepted";
|
|
7
|
+
/** `GameAccount.code` */
|
|
8
|
+
export type GameAccountCode = "steam" | "psn" | "xbox" | "battlenet" | "riot" | "epic" | "ea" | "activision" | "ubisoft" | "nintendo" | "fightcade" | "apple" | "googleplay";
|
|
9
|
+
/** `GameAccount.verification` */
|
|
10
|
+
export type GameAccountVerification = "none" | "format" | "linked";
|
|
7
11
|
/** `Game.genre` */
|
|
8
12
|
export type GameGenre = "FPS" | "MOBA" | "Battle Royale" | "Fighting (FGC)" | "Sports" | "Strategy/RTS" | "Sim Racing" | "Card Game" | "Retro" | "Other";
|
|
9
13
|
/** `GameRequest.customLobbies` */
|
|
@@ -28,6 +32,8 @@ export type MatchStreamPlatform = "twitch" | "youtube" | "kick" | "other";
|
|
|
28
32
|
export type MatchWinnerSlot = "home" | "away";
|
|
29
33
|
/** `Platform.code` */
|
|
30
34
|
export type PlatformCode = "pc" | "playstation" | "xbox" | "nintendo" | "mobile";
|
|
35
|
+
/** `PlayerBan.scope` */
|
|
36
|
+
export type PlayerBanScope = "tournament" | "host";
|
|
31
37
|
/** `Referral.status` */
|
|
32
38
|
export type ReferralStatus = "pending" | "completed";
|
|
33
39
|
/** `Team.current_status` */
|
|
@@ -60,6 +66,8 @@ export type TournamentTeamSize = "one" | "two" | "three" | "four" | "five";
|
|
|
60
66
|
export type TournamentType = "Single Elimination" | "League" | "Double Elimination";
|
|
61
67
|
/** `Tournament.visibility` */
|
|
62
68
|
export type TournamentVisibility = "public" | "unlisted";
|
|
69
|
+
/** `UserGameAccount.verificationMethod` */
|
|
70
|
+
export type UserGameAccountVerificationMethod = "linked";
|
|
63
71
|
/** `UserTransaction.stripeStatus` */
|
|
64
72
|
export type UserTransactionStripeStatus = "pending" | "completed" | "failed" | "refunded" | "internal";
|
|
65
73
|
/** `UserTransaction.type` */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BlocksContent, MediaInput, RelationInput } from '../contracts';
|
|
2
|
-
import type { AdminActionTargetType, FaqCategory, FriendshipStatus, GameGenre, GameRequestCustomLobbies, GameRequestGenre, GameRequestTeamPlay, GameVisibility, MatchDisputeReason, MatchLobbyStatus, MatchResultType, MatchRound, MatchStreamPlatform, MatchWinnerSlot, PlatformCode, ReferralStatus, TeamCurrentStatus, TeamInviteStatus, TeamInviteTeamRole, TeamPlayerTeamRole, TournamentAccess, TournamentCurrentStatus, TournamentInviteStatus, TournamentJoinRequestEntryType, TournamentJoinRequestStatus, TournamentParticipantEntryType, TournamentParticipantStatus, TournamentRoleRole, TournamentTeamSize, TournamentType, TournamentVisibility, UserTransactionStripeStatus, UserTransactionType } from './enums';
|
|
2
|
+
import type { AdminActionTargetType, FaqCategory, FriendshipStatus, GameAccountCode, GameAccountVerification, GameGenre, GameRequestCustomLobbies, GameRequestGenre, GameRequestTeamPlay, GameVisibility, MatchDisputeReason, MatchLobbyStatus, MatchResultType, MatchRound, MatchStreamPlatform, MatchWinnerSlot, PlatformCode, PlayerBanScope, ReferralStatus, TeamCurrentStatus, TeamInviteStatus, TeamInviteTeamRole, TeamPlayerTeamRole, TournamentAccess, TournamentCurrentStatus, TournamentInviteStatus, TournamentJoinRequestEntryType, TournamentJoinRequestStatus, TournamentParticipantEntryType, TournamentParticipantStatus, TournamentRoleRole, TournamentTeamSize, TournamentType, TournamentVisibility, UserGameAccountVerificationMethod, UserTransactionStripeStatus, UserTransactionType } from './enums';
|
|
3
3
|
/** Write payload for `about`. */
|
|
4
4
|
export interface AboutInput {
|
|
5
5
|
heroTitle?: string;
|
|
@@ -75,12 +75,22 @@ export interface GameInput {
|
|
|
75
75
|
/** Write payload for `game-account`. */
|
|
76
76
|
export interface GameAccountInput {
|
|
77
77
|
name?: string;
|
|
78
|
+
code?: GameAccountCode;
|
|
79
|
+
verification?: GameAccountVerification;
|
|
78
80
|
tournaments?: RelationInput | RelationInput[];
|
|
79
81
|
user_game_accounts?: RelationInput | RelationInput[];
|
|
80
82
|
imgThumb?: MediaInput;
|
|
81
83
|
games?: RelationInput | RelationInput[];
|
|
82
84
|
platforms?: RelationInput | RelationInput[];
|
|
83
85
|
}
|
|
86
|
+
/** Write payload for `game-account-link`. */
|
|
87
|
+
export interface GameAccountLinkInput {
|
|
88
|
+
token?: string;
|
|
89
|
+
users_permissions_user?: RelationInput;
|
|
90
|
+
game_account?: RelationInput;
|
|
91
|
+
expiresAt?: string;
|
|
92
|
+
returnTo?: string;
|
|
93
|
+
}
|
|
84
94
|
/** Write payload for `game-request`. */
|
|
85
95
|
export interface GameRequestInput {
|
|
86
96
|
gameTitle?: string;
|
|
@@ -187,6 +197,18 @@ export interface PlatformInput {
|
|
|
187
197
|
games?: RelationInput | RelationInput[];
|
|
188
198
|
game_accounts?: RelationInput | RelationInput[];
|
|
189
199
|
}
|
|
200
|
+
/** Write payload for `player-ban`. */
|
|
201
|
+
export interface PlayerBanInput {
|
|
202
|
+
scope?: PlayerBanScope;
|
|
203
|
+
tournament?: RelationInput;
|
|
204
|
+
host?: RelationInput;
|
|
205
|
+
users_permissions_user?: RelationInput;
|
|
206
|
+
bannedBy?: RelationInput;
|
|
207
|
+
reason?: string;
|
|
208
|
+
expiresAt?: string;
|
|
209
|
+
liftedAt?: string;
|
|
210
|
+
liftedBy?: RelationInput;
|
|
211
|
+
}
|
|
190
212
|
/** Write payload for `preset-avatar`. */
|
|
191
213
|
export interface PresetAvatarInput {
|
|
192
214
|
image?: MediaInput;
|
|
@@ -413,6 +435,9 @@ export interface UserInput {
|
|
|
413
435
|
export interface UserGameAccountInput {
|
|
414
436
|
accountId?: string;
|
|
415
437
|
isPrivate?: boolean;
|
|
438
|
+
verifiedAt?: string;
|
|
439
|
+
verificationMethod?: UserGameAccountVerificationMethod;
|
|
440
|
+
providerAccountId?: string;
|
|
416
441
|
users_permissions_user?: RelationInput;
|
|
417
442
|
game_account?: RelationInput;
|
|
418
443
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BlocksContent, StrapiDocument, StrapiMedia, UserRole } from '../contracts';
|
|
2
|
-
import type { AdminActionTargetType, FaqCategory, FriendshipStatus, GameGenre, GameRequestCustomLobbies, GameRequestGenre, GameRequestTeamPlay, GameVisibility, MatchDisputeReason, MatchLobbyStatus, MatchResultType, MatchRound, MatchStreamPlatform, MatchWinnerSlot, PlatformCode, ReferralStatus, TeamCurrentStatus, TeamInviteStatus, TeamInviteTeamRole, TeamPlayerTeamRole, TournamentAccess, TournamentCurrentStatus, TournamentInviteStatus, TournamentJoinRequestEntryType, TournamentJoinRequestStatus, TournamentParticipantEntryType, TournamentParticipantStatus, TournamentRoleRole, TournamentTeamSize, TournamentType, TournamentVisibility, UserTransactionStripeStatus, UserTransactionType } from './enums';
|
|
2
|
+
import type { AdminActionTargetType, FaqCategory, FriendshipStatus, GameAccountCode, GameAccountVerification, GameGenre, GameRequestCustomLobbies, GameRequestGenre, GameRequestTeamPlay, GameVisibility, MatchDisputeReason, MatchLobbyStatus, MatchResultType, MatchRound, MatchStreamPlatform, MatchWinnerSlot, PlatformCode, PlayerBanScope, ReferralStatus, TeamCurrentStatus, TeamInviteStatus, TeamInviteTeamRole, TeamPlayerTeamRole, TournamentAccess, TournamentCurrentStatus, TournamentInviteStatus, TournamentJoinRequestEntryType, TournamentJoinRequestStatus, TournamentParticipantEntryType, TournamentParticipantStatus, TournamentRoleRole, TournamentTeamSize, TournamentType, TournamentVisibility, UserGameAccountVerificationMethod, UserTransactionStripeStatus, UserTransactionType } from './enums';
|
|
3
3
|
/** `about` */
|
|
4
4
|
export interface About extends StrapiDocument {
|
|
5
5
|
heroTitle?: string | null;
|
|
@@ -75,12 +75,22 @@ export interface Game extends StrapiDocument {
|
|
|
75
75
|
/** `game-account` */
|
|
76
76
|
export interface GameAccount extends StrapiDocument {
|
|
77
77
|
name?: string | null;
|
|
78
|
+
code?: GameAccountCode | null;
|
|
79
|
+
verification?: GameAccountVerification | null;
|
|
78
80
|
tournaments?: Tournament[];
|
|
79
81
|
user_game_accounts?: UserGameAccount[];
|
|
80
82
|
imgThumb?: StrapiMedia | null;
|
|
81
83
|
games?: Game[];
|
|
82
84
|
platforms?: Platform[];
|
|
83
85
|
}
|
|
86
|
+
/** `game-account-link` */
|
|
87
|
+
export interface GameAccountLink extends StrapiDocument {
|
|
88
|
+
token?: string | null;
|
|
89
|
+
users_permissions_user?: User | null;
|
|
90
|
+
game_account?: GameAccount | null;
|
|
91
|
+
expiresAt?: string | null;
|
|
92
|
+
returnTo?: string | null;
|
|
93
|
+
}
|
|
84
94
|
/** `game-request` */
|
|
85
95
|
export interface GameRequest extends StrapiDocument {
|
|
86
96
|
gameTitle?: string | null;
|
|
@@ -187,6 +197,18 @@ export interface Platform extends StrapiDocument {
|
|
|
187
197
|
games?: Game[];
|
|
188
198
|
game_accounts?: GameAccount[];
|
|
189
199
|
}
|
|
200
|
+
/** `player-ban` */
|
|
201
|
+
export interface PlayerBan extends StrapiDocument {
|
|
202
|
+
scope?: PlayerBanScope | null;
|
|
203
|
+
tournament?: Tournament | null;
|
|
204
|
+
host?: User | null;
|
|
205
|
+
users_permissions_user?: User | null;
|
|
206
|
+
bannedBy?: User | null;
|
|
207
|
+
reason?: string | null;
|
|
208
|
+
expiresAt?: string | null;
|
|
209
|
+
liftedAt?: string | null;
|
|
210
|
+
liftedBy?: User | null;
|
|
211
|
+
}
|
|
190
212
|
/** `preset-avatar` */
|
|
191
213
|
export interface PresetAvatar extends StrapiDocument {
|
|
192
214
|
image?: StrapiMedia | null;
|
|
@@ -413,6 +435,9 @@ export interface User extends StrapiDocument {
|
|
|
413
435
|
export interface UserGameAccount extends StrapiDocument {
|
|
414
436
|
accountId?: string | null;
|
|
415
437
|
isPrivate?: boolean | null;
|
|
438
|
+
verifiedAt?: string | null;
|
|
439
|
+
verificationMethod?: UserGameAccountVerificationMethod | null;
|
|
440
|
+
providerAccountId?: string | null;
|
|
416
441
|
users_permissions_user?: User | null;
|
|
417
442
|
game_account?: GameAccount | null;
|
|
418
443
|
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a player's handle on each account network looks like.
|
|
3
|
+
*
|
|
4
|
+
* A handle is the one thing opponents actually exchange — the PSN ID, the
|
|
5
|
+
* BattleTag, the Riot ID — so a typo in it is a match that cannot start. Until
|
|
6
|
+
* these rules existed one regex covered every network, and it happily accepted
|
|
7
|
+
* `Name#1234` for PSN (where `#` is not allowed) while rejecting nothing that a
|
|
8
|
+
* real PSN or Xbox rule would.
|
|
9
|
+
*
|
|
10
|
+
* None of this proves ownership. A network whose handles can be linked by
|
|
11
|
+
* OAuth (Steam, Battle.net, Xbox) gets a `linked` tier later; this file is the
|
|
12
|
+
* `format` tier that every network can have today. Which tier applies is a
|
|
13
|
+
* property of the catalogue row (`GameAccount.verification`), set by editors,
|
|
14
|
+
* so a rule that turns out wrong for some network can be switched off without a
|
|
15
|
+
* deploy: set the row to `none` and the generic rule applies.
|
|
16
|
+
*
|
|
17
|
+
* Lives here because the settings page validates as the player types and the
|
|
18
|
+
* backend validates again on write. Two copies of a regex is two chances for a
|
|
19
|
+
* handle to pass one and fail the other.
|
|
20
|
+
*
|
|
21
|
+
* Pure on purpose: no I/O, no profanity list (each app keeps its own), so the
|
|
22
|
+
* rules are unit-tested directly (xpt-strapi
|
|
23
|
+
* `tests/unit/utils/gameAccountHandle.test.ts`).
|
|
24
|
+
*/
|
|
25
|
+
import type { GameAccountCode, GameAccountVerification } from '../generated/enums';
|
|
26
|
+
export interface HandleRule {
|
|
27
|
+
/** The network this rule is for; absent on the generic fallback. */
|
|
28
|
+
code?: GameAccountCode;
|
|
29
|
+
/** What the field is called in copy: "PSN online ID", "BattleTag". */
|
|
30
|
+
label: string;
|
|
31
|
+
/** The field's placeholder. */
|
|
32
|
+
placeholder: string;
|
|
33
|
+
/** One sentence under the field saying what a valid value looks like. */
|
|
34
|
+
hint: string;
|
|
35
|
+
/** Applied to the normalized value. */
|
|
36
|
+
pattern: RegExp;
|
|
37
|
+
min: number;
|
|
38
|
+
max: number;
|
|
39
|
+
/**
|
|
40
|
+
* Rewrites the trimmed input before `pattern` is applied: a Steam profile
|
|
41
|
+
* URL down to its id, a bare Nintendo friend code into `SW-xxxx-xxxx-xxxx`.
|
|
42
|
+
* The result is what gets stored.
|
|
43
|
+
*/
|
|
44
|
+
normalize?: (value: string) => string;
|
|
45
|
+
}
|
|
46
|
+
export type HandleCheck = {
|
|
47
|
+
ok: true;
|
|
48
|
+
value: string;
|
|
49
|
+
} | {
|
|
50
|
+
ok: false;
|
|
51
|
+
message: string;
|
|
52
|
+
};
|
|
53
|
+
/** `error.details.name` values the game-account write endpoints raise. */
|
|
54
|
+
export type GameAccountErrorName = 'HandleInvalid' | 'HandleTaken'
|
|
55
|
+
/** The row is verified by a link; unlink before changing the ID. */
|
|
56
|
+
| 'HandleLocked'
|
|
57
|
+
/** No configured link provider for this network. */
|
|
58
|
+
| 'LinkUnavailable'
|
|
59
|
+
/** The provider account is already linked to another XPT user. */
|
|
60
|
+
| 'LinkConflict';
|
|
61
|
+
/**
|
|
62
|
+
* Networks whose handles can be verified by signing in to the network. The
|
|
63
|
+
* catalogue's `verification: 'linked'` is only valid for these; whether a
|
|
64
|
+
* provider is actually configured on a given environment is answered at
|
|
65
|
+
* runtime by `GET /user-game-accounts/link/providers`.
|
|
66
|
+
*/
|
|
67
|
+
export declare const LINK_PROVIDER_CODES: readonly ["steam", "battlenet"];
|
|
68
|
+
export type LinkProviderCode = (typeof LINK_PROVIDER_CODES)[number];
|
|
69
|
+
export declare function isLinkableCode(code: string | null | undefined): code is LinkProviderCode;
|
|
70
|
+
/**
|
|
71
|
+
* Why a link attempt came back without a verified account — the `linkError`
|
|
72
|
+
* query value the callback redirects to the settings page with.
|
|
73
|
+
*/
|
|
74
|
+
export type AccountLinkError =
|
|
75
|
+
/** The attempt was unknown, already used, or older than its window. */
|
|
76
|
+
'expired'
|
|
77
|
+
/** The player cancelled at the provider. */
|
|
78
|
+
| 'denied'
|
|
79
|
+
/** The provider's answer did not verify. */
|
|
80
|
+
| 'invalid'
|
|
81
|
+
/** That provider account is already linked to another XPT user. */
|
|
82
|
+
| 'taken'
|
|
83
|
+
/** The provider is not configured, or the network is not linkable. */
|
|
84
|
+
| 'unavailable'
|
|
85
|
+
/** Accounts are frozen while the player is in a running tournament. */
|
|
86
|
+
| 'activeTournament';
|
|
87
|
+
/** True when a row's handle was proved by signing in to the network. */
|
|
88
|
+
export declare function isVerifiedAccount(row: {
|
|
89
|
+
verifiedAt?: string | Date | null;
|
|
90
|
+
verificationMethod?: string | null;
|
|
91
|
+
} | null | undefined): boolean;
|
|
92
|
+
/**
|
|
93
|
+
* The rule every network gets when it has no stricter one. Also the backstop
|
|
94
|
+
* the backend applies to every write regardless of network, so every rule
|
|
95
|
+
* below must produce a value that passes this one.
|
|
96
|
+
*
|
|
97
|
+
* Letters are `\p{L}` rather than `A-Z`: BattleTags and Riot IDs allow accented
|
|
98
|
+
* names, and a rule that rejected "Ærøn#1234" would be wrong, not strict.
|
|
99
|
+
*/
|
|
100
|
+
export declare const GENERIC_HANDLE_RULE: HandleRule;
|
|
101
|
+
/**
|
|
102
|
+
* Per-network rules. Initial values, and product decisions rather than facts:
|
|
103
|
+
* each is the documented shape as of writing, kept a little looser than the
|
|
104
|
+
* network's own signup validator so a real handle is never refused.
|
|
105
|
+
*/
|
|
106
|
+
export declare const HANDLE_RULES: Partial<Record<GameAccountCode, HandleRule>>;
|
|
107
|
+
/**
|
|
108
|
+
* The rule a network's handles are checked against: its own when the
|
|
109
|
+
* catalogue row asks for format checks (or is linkable, which implies them
|
|
110
|
+
* for anything typed by hand) and a rule exists for its code, otherwise the
|
|
111
|
+
* generic one.
|
|
112
|
+
*/
|
|
113
|
+
export declare function handleRuleFor(network: {
|
|
114
|
+
code?: GameAccountCode | string | null;
|
|
115
|
+
verification?: GameAccountVerification | string | null;
|
|
116
|
+
} | null | undefined): HandleRule;
|
|
117
|
+
/** True when the code has a rule of its own, so `format` means something. */
|
|
118
|
+
export declare function hasHandleRule(code: string | null | undefined): boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Checks `raw` against `rule` and returns the value to store: trimmed, runs of
|
|
121
|
+
* whitespace collapsed to one space, then the rule's own normalization.
|
|
122
|
+
*
|
|
123
|
+
* The message is the one the player reads, so it says what a valid value looks
|
|
124
|
+
* like rather than which check failed.
|
|
125
|
+
*/
|
|
126
|
+
export declare function checkHandle(raw: string | null | undefined, rule: HandleRule): HandleCheck;
|
|
127
|
+
/** The key two handles are compared on: same network, same letters, any case. */
|
|
128
|
+
export declare function normalizeHandleKey(value: string): string;
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* What a player's handle on each account network looks like.
|
|
4
|
+
*
|
|
5
|
+
* A handle is the one thing opponents actually exchange — the PSN ID, the
|
|
6
|
+
* BattleTag, the Riot ID — so a typo in it is a match that cannot start. Until
|
|
7
|
+
* these rules existed one regex covered every network, and it happily accepted
|
|
8
|
+
* `Name#1234` for PSN (where `#` is not allowed) while rejecting nothing that a
|
|
9
|
+
* real PSN or Xbox rule would.
|
|
10
|
+
*
|
|
11
|
+
* None of this proves ownership. A network whose handles can be linked by
|
|
12
|
+
* OAuth (Steam, Battle.net, Xbox) gets a `linked` tier later; this file is the
|
|
13
|
+
* `format` tier that every network can have today. Which tier applies is a
|
|
14
|
+
* property of the catalogue row (`GameAccount.verification`), set by editors,
|
|
15
|
+
* so a rule that turns out wrong for some network can be switched off without a
|
|
16
|
+
* deploy: set the row to `none` and the generic rule applies.
|
|
17
|
+
*
|
|
18
|
+
* Lives here because the settings page validates as the player types and the
|
|
19
|
+
* backend validates again on write. Two copies of a regex is two chances for a
|
|
20
|
+
* handle to pass one and fail the other.
|
|
21
|
+
*
|
|
22
|
+
* Pure on purpose: no I/O, no profanity list (each app keeps its own), so the
|
|
23
|
+
* rules are unit-tested directly (xpt-strapi
|
|
24
|
+
* `tests/unit/utils/gameAccountHandle.test.ts`).
|
|
25
|
+
*/
|
|
26
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
+
exports.HANDLE_RULES = exports.GENERIC_HANDLE_RULE = exports.LINK_PROVIDER_CODES = void 0;
|
|
28
|
+
exports.isLinkableCode = isLinkableCode;
|
|
29
|
+
exports.isVerifiedAccount = isVerifiedAccount;
|
|
30
|
+
exports.handleRuleFor = handleRuleFor;
|
|
31
|
+
exports.hasHandleRule = hasHandleRule;
|
|
32
|
+
exports.checkHandle = checkHandle;
|
|
33
|
+
exports.normalizeHandleKey = normalizeHandleKey;
|
|
34
|
+
/**
|
|
35
|
+
* Networks whose handles can be verified by signing in to the network. The
|
|
36
|
+
* catalogue's `verification: 'linked'` is only valid for these; whether a
|
|
37
|
+
* provider is actually configured on a given environment is answered at
|
|
38
|
+
* runtime by `GET /user-game-accounts/link/providers`.
|
|
39
|
+
*/
|
|
40
|
+
exports.LINK_PROVIDER_CODES = ['steam', 'battlenet'];
|
|
41
|
+
function isLinkableCode(code) {
|
|
42
|
+
return exports.LINK_PROVIDER_CODES.includes(code !== null && code !== void 0 ? code : '');
|
|
43
|
+
}
|
|
44
|
+
/** True when a row's handle was proved by signing in to the network. */
|
|
45
|
+
function isVerifiedAccount(row) {
|
|
46
|
+
return !!(row === null || row === void 0 ? void 0 : row.verifiedAt) && row.verificationMethod === 'linked';
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The rule every network gets when it has no stricter one. Also the backstop
|
|
50
|
+
* the backend applies to every write regardless of network, so every rule
|
|
51
|
+
* below must produce a value that passes this one.
|
|
52
|
+
*
|
|
53
|
+
* Letters are `\p{L}` rather than `A-Z`: BattleTags and Riot IDs allow accented
|
|
54
|
+
* names, and a rule that rejected "Ærøn#1234" would be wrong, not strict.
|
|
55
|
+
*/
|
|
56
|
+
exports.GENERIC_HANDLE_RULE = {
|
|
57
|
+
label: 'Game ID',
|
|
58
|
+
placeholder: 'Game ID',
|
|
59
|
+
hint: 'Enter your ID exactly as it appears in-game. Letters, numbers, spaces and _ . # - are allowed.',
|
|
60
|
+
pattern: /^[\p{L}\p{N}_ .#-]+$/u,
|
|
61
|
+
min: 3,
|
|
62
|
+
max: 32,
|
|
63
|
+
};
|
|
64
|
+
const STEAM_PROFILE_URL = /^(?:https?:\/\/)?(?:www\.)?steamcommunity\.com\/(?:id|profiles)\/([^/?#\s]+)\/?(?:[?#].*)?$/i;
|
|
65
|
+
/**
|
|
66
|
+
* Per-network rules. Initial values, and product decisions rather than facts:
|
|
67
|
+
* each is the documented shape as of writing, kept a little looser than the
|
|
68
|
+
* network's own signup validator so a real handle is never refused.
|
|
69
|
+
*/
|
|
70
|
+
exports.HANDLE_RULES = {
|
|
71
|
+
psn: {
|
|
72
|
+
code: 'psn',
|
|
73
|
+
label: 'PSN online ID',
|
|
74
|
+
placeholder: 'PSN online ID',
|
|
75
|
+
hint: '3 to 16 letters, numbers, hyphens or underscores, starting with a letter. No # tag.',
|
|
76
|
+
pattern: /^[A-Za-z][A-Za-z0-9_-]{2,15}$/,
|
|
77
|
+
min: 3,
|
|
78
|
+
max: 16,
|
|
79
|
+
},
|
|
80
|
+
xbox: {
|
|
81
|
+
code: 'xbox',
|
|
82
|
+
label: 'Gamertag',
|
|
83
|
+
placeholder: 'Gamertag or Gamertag#1234',
|
|
84
|
+
hint: 'Letters, numbers and single spaces, with the #1234 suffix if your gamertag has one.',
|
|
85
|
+
pattern: /^[A-Za-z0-9]+(?: [A-Za-z0-9]+)*(?:#\d{3,4})?$/,
|
|
86
|
+
min: 3,
|
|
87
|
+
max: 20,
|
|
88
|
+
},
|
|
89
|
+
battlenet: {
|
|
90
|
+
code: 'battlenet',
|
|
91
|
+
label: 'BattleTag',
|
|
92
|
+
placeholder: 'Name#1234',
|
|
93
|
+
hint: 'Your BattleTag including the number, e.g. Name#1234.',
|
|
94
|
+
pattern: /^\p{L}[\p{L}\p{N}]{2,11}#\d{4,7}$/u,
|
|
95
|
+
min: 8,
|
|
96
|
+
max: 20,
|
|
97
|
+
},
|
|
98
|
+
riot: {
|
|
99
|
+
code: 'riot',
|
|
100
|
+
label: 'Riot ID',
|
|
101
|
+
placeholder: 'GameName#TAG',
|
|
102
|
+
hint: 'Your Riot ID including the tagline, e.g. GameName#TAG.',
|
|
103
|
+
pattern: /^[\p{L}\p{N}](?:[\p{L}\p{N} ]{1,14}[\p{L}\p{N}])?#[\p{L}\p{N}]{3,5}$/u,
|
|
104
|
+
min: 7,
|
|
105
|
+
max: 22,
|
|
106
|
+
},
|
|
107
|
+
epic: {
|
|
108
|
+
code: 'epic',
|
|
109
|
+
label: 'Epic display name',
|
|
110
|
+
placeholder: 'Epic display name',
|
|
111
|
+
hint: '3 to 16 letters, numbers, spaces or - _ . as shown on your Epic account.',
|
|
112
|
+
pattern: /^[\p{L}\p{N}][\p{L}\p{N} ._-]{1,14}[\p{L}\p{N}._-]$/u,
|
|
113
|
+
min: 3,
|
|
114
|
+
max: 16,
|
|
115
|
+
},
|
|
116
|
+
ea: {
|
|
117
|
+
code: 'ea',
|
|
118
|
+
label: 'EA ID',
|
|
119
|
+
placeholder: 'EA ID',
|
|
120
|
+
hint: '4 to 16 letters, numbers, hyphens or underscores.',
|
|
121
|
+
pattern: /^[A-Za-z0-9_-]{4,16}$/,
|
|
122
|
+
min: 4,
|
|
123
|
+
max: 16,
|
|
124
|
+
},
|
|
125
|
+
activision: {
|
|
126
|
+
code: 'activision',
|
|
127
|
+
label: 'Activision ID',
|
|
128
|
+
placeholder: 'Name#1234567',
|
|
129
|
+
hint: 'Your Activision ID including the number, e.g. Name#1234567.',
|
|
130
|
+
pattern: /^[A-Za-z0-9 _-]{2,16}#\d{4,8}$/,
|
|
131
|
+
min: 7,
|
|
132
|
+
max: 25,
|
|
133
|
+
},
|
|
134
|
+
ubisoft: {
|
|
135
|
+
code: 'ubisoft',
|
|
136
|
+
label: 'Ubisoft username',
|
|
137
|
+
placeholder: 'Ubisoft username',
|
|
138
|
+
hint: '3 to 15 letters, numbers or - _ . as shown in Ubisoft Connect.',
|
|
139
|
+
pattern: /^[A-Za-z0-9._-]{3,15}$/,
|
|
140
|
+
min: 3,
|
|
141
|
+
max: 15,
|
|
142
|
+
},
|
|
143
|
+
steam: {
|
|
144
|
+
code: 'steam',
|
|
145
|
+
label: 'Steam ID',
|
|
146
|
+
placeholder: 'Friend code or profile URL',
|
|
147
|
+
hint: 'Your Steam friend code (Friends > Add a Friend), your SteamID64, or your steamcommunity.com profile URL.',
|
|
148
|
+
// A profile URL is the easiest thing for a player to paste; what gets
|
|
149
|
+
// stored is the id or custom name at the end of it.
|
|
150
|
+
normalize: (value) => value.replace(STEAM_PROFILE_URL, '$1'),
|
|
151
|
+
pattern: /^[A-Za-z0-9_-]{3,32}$/,
|
|
152
|
+
min: 3,
|
|
153
|
+
max: 32,
|
|
154
|
+
},
|
|
155
|
+
nintendo: {
|
|
156
|
+
code: 'nintendo',
|
|
157
|
+
label: 'Nintendo friend code',
|
|
158
|
+
placeholder: 'SW-0000-0000-0000',
|
|
159
|
+
hint: 'Your 12-digit Switch friend code, e.g. SW-1234-5678-9012.',
|
|
160
|
+
normalize: (value) => {
|
|
161
|
+
const digits = value.replace(/^sw/i, '').replace(/[\s-]/g, '');
|
|
162
|
+
return /^\d{12}$/.test(digits)
|
|
163
|
+
? `SW-${digits.slice(0, 4)}-${digits.slice(4, 8)}-${digits.slice(8)}`
|
|
164
|
+
: value;
|
|
165
|
+
},
|
|
166
|
+
pattern: /^SW-\d{4}-\d{4}-\d{4}$/,
|
|
167
|
+
min: 17,
|
|
168
|
+
max: 17,
|
|
169
|
+
},
|
|
170
|
+
// fightcade, apple and googleplay have no documented handle shape and stay
|
|
171
|
+
// on the generic rule.
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* The rule a network's handles are checked against: its own when the
|
|
175
|
+
* catalogue row asks for format checks (or is linkable, which implies them
|
|
176
|
+
* for anything typed by hand) and a rule exists for its code, otherwise the
|
|
177
|
+
* generic one.
|
|
178
|
+
*/
|
|
179
|
+
function handleRuleFor(network) {
|
|
180
|
+
var _a;
|
|
181
|
+
const checked = (network === null || network === void 0 ? void 0 : network.verification) === 'format' || (network === null || network === void 0 ? void 0 : network.verification) === 'linked';
|
|
182
|
+
if (!checked || !(network === null || network === void 0 ? void 0 : network.code)) {
|
|
183
|
+
return exports.GENERIC_HANDLE_RULE;
|
|
184
|
+
}
|
|
185
|
+
return (_a = exports.HANDLE_RULES[network.code]) !== null && _a !== void 0 ? _a : exports.GENERIC_HANDLE_RULE;
|
|
186
|
+
}
|
|
187
|
+
/** True when the code has a rule of its own, so `format` means something. */
|
|
188
|
+
function hasHandleRule(code) {
|
|
189
|
+
return !!code && Object.prototype.hasOwnProperty.call(exports.HANDLE_RULES, code);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Checks `raw` against `rule` and returns the value to store: trimmed, runs of
|
|
193
|
+
* whitespace collapsed to one space, then the rule's own normalization.
|
|
194
|
+
*
|
|
195
|
+
* The message is the one the player reads, so it says what a valid value looks
|
|
196
|
+
* like rather than which check failed.
|
|
197
|
+
*/
|
|
198
|
+
function checkHandle(raw, rule) {
|
|
199
|
+
const collapsed = (raw !== null && raw !== void 0 ? raw : '').trim().replace(/\s+/g, ' ');
|
|
200
|
+
if (!collapsed) {
|
|
201
|
+
return { ok: false, message: `Please enter your ${rule.label}` };
|
|
202
|
+
}
|
|
203
|
+
const value = rule.normalize ? rule.normalize(collapsed) : collapsed;
|
|
204
|
+
if (value.length < rule.min) {
|
|
205
|
+
return { ok: false, message: `${rule.label} is too short. ${rule.hint}` };
|
|
206
|
+
}
|
|
207
|
+
if (value.length > rule.max) {
|
|
208
|
+
return { ok: false, message: `${rule.label} is too long. ${rule.hint}` };
|
|
209
|
+
}
|
|
210
|
+
if (!rule.pattern.test(value)) {
|
|
211
|
+
return {
|
|
212
|
+
ok: false,
|
|
213
|
+
message: `That doesn't look like a valid ${rule.label}. ${rule.hint}`,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
return { ok: true, value };
|
|
217
|
+
}
|
|
218
|
+
/** The key two handles are compared on: same network, same letters, any case. */
|
|
219
|
+
function normalizeHandleKey(value) {
|
|
220
|
+
return value.trim().replace(/\s+/g, ' ').toLowerCase();
|
|
221
|
+
}
|
package/dist/manual/index.d.ts
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
* Hand-written types that are not derived from a Strapi content type.
|
|
3
3
|
*/
|
|
4
4
|
export * from './country';
|
|
5
|
+
export * from './gameAccountHandle';
|
|
5
6
|
export * from './lobby';
|
|
6
7
|
export * from './notification';
|
|
8
|
+
export * from './playerBan';
|
|
7
9
|
export * from './tournamentEntry';
|
|
8
10
|
export * from './tournamentInvite';
|
|
9
11
|
export * from './tournamentJoinRequest';
|
package/dist/manual/index.js
CHANGED
|
@@ -18,8 +18,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
18
18
|
* Hand-written types that are not derived from a Strapi content type.
|
|
19
19
|
*/
|
|
20
20
|
__exportStar(require("./country"), exports);
|
|
21
|
+
__exportStar(require("./gameAccountHandle"), exports);
|
|
21
22
|
__exportStar(require("./lobby"), exports);
|
|
22
23
|
__exportStar(require("./notification"), exports);
|
|
24
|
+
__exportStar(require("./playerBan"), exports);
|
|
23
25
|
__exportStar(require("./tournamentEntry"), exports);
|
|
24
26
|
__exportStar(require("./tournamentInvite"), exports);
|
|
25
27
|
__exportStar(require("./tournamentJoinRequest"), exports);
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Player bans — the contract between `utils/playerBan.ts` in xpt-strapi and
|
|
3
|
+
* the ban dialog, ban list and host-bans page in xpt-client.
|
|
4
|
+
*
|
|
5
|
+
* Two scopes (`PlayerBanScope`, generated): **tournament** keeps a player out
|
|
6
|
+
* of one event and is set by its owner or an admin; **host** keeps them out
|
|
7
|
+
* of every tournament one host owns, now and later, and only that host sets
|
|
8
|
+
* it. The platform level is the account block, not a ban.
|
|
9
|
+
*
|
|
10
|
+
* A ban is active until it is lifted or its `expiresAt` passes; `null` is
|
|
11
|
+
* permanent. Lifted bans are kept, so a list can show history.
|
|
12
|
+
*
|
|
13
|
+
* Routes:
|
|
14
|
+
* - `GET|POST /tournaments/:id/bans`, `DELETE /tournaments/:id/bans/:userId`
|
|
15
|
+
* - `GET|POST /host-bans`, `DELETE /host-bans/:userId`
|
|
16
|
+
* Both GETs take `?includeLifted=true`.
|
|
17
|
+
*/
|
|
18
|
+
import type { PlayerBanScope } from '../generated/enums';
|
|
19
|
+
export declare const MAX_BAN_REASON_LENGTH = 500;
|
|
20
|
+
/** A ban as the list and ban routes return it. */
|
|
21
|
+
export interface PlayerBanSummary {
|
|
22
|
+
documentId: string;
|
|
23
|
+
scope: PlayerBanScope;
|
|
24
|
+
reason: string | null;
|
|
25
|
+
createdAt: string | null;
|
|
26
|
+
/** Null for a permanent ban. */
|
|
27
|
+
expiresAt: string | null;
|
|
28
|
+
liftedAt: string | null;
|
|
29
|
+
/** Not lifted and not expired. */
|
|
30
|
+
active: boolean;
|
|
31
|
+
users_permissions_user: {
|
|
32
|
+
id: number;
|
|
33
|
+
documentId: string | null;
|
|
34
|
+
username: string | null;
|
|
35
|
+
image: unknown;
|
|
36
|
+
} | null;
|
|
37
|
+
bannedBy: {
|
|
38
|
+
id: number;
|
|
39
|
+
username: string | null;
|
|
40
|
+
} | null;
|
|
41
|
+
liftedBy: {
|
|
42
|
+
id: number;
|
|
43
|
+
username: string | null;
|
|
44
|
+
} | null;
|
|
45
|
+
/** Set on tournament-scope bans. */
|
|
46
|
+
tournament: {
|
|
47
|
+
documentId: string;
|
|
48
|
+
title: string | null;
|
|
49
|
+
} | null;
|
|
50
|
+
/**
|
|
51
|
+
* Whether the viewer may lift it. On a tournament's list a host ban is
|
|
52
|
+
* liftable only by the owner — an admin sees it but cannot lift it.
|
|
53
|
+
*/
|
|
54
|
+
canLift: boolean;
|
|
55
|
+
}
|
|
56
|
+
/** `POST /tournaments/:id/bans` and `POST /host-bans`. */
|
|
57
|
+
export interface BanPlayerBody {
|
|
58
|
+
userId: number;
|
|
59
|
+
reason?: string | null;
|
|
60
|
+
/** ISO date in the future; omit or null for permanent. */
|
|
61
|
+
expiresAt?: string | null;
|
|
62
|
+
/**
|
|
63
|
+
* Also withdraw the entry the player registered (with its refund), while
|
|
64
|
+
* the tournament still takes entries. Host bans apply it across all of the
|
|
65
|
+
* host's open tournaments.
|
|
66
|
+
*/
|
|
67
|
+
removeEntry?: boolean;
|
|
68
|
+
}
|
|
69
|
+
/** Why `removeEntry` left an entry where it was. */
|
|
70
|
+
export type BanRemovalSkip =
|
|
71
|
+
/** No entry in this tournament. Never returned for host bans. */
|
|
72
|
+
'notRegistered'
|
|
73
|
+
/** On a team someone else registered: the team's leader must swap them out. */
|
|
74
|
+
| 'notRegistrant'
|
|
75
|
+
/** Past check-in; the bracket is seeded. */
|
|
76
|
+
| 'entryLocked'
|
|
77
|
+
/** The withdrawal refused; `message` says why. */
|
|
78
|
+
| 'failed';
|
|
79
|
+
export interface BanRemoval {
|
|
80
|
+
tournamentDocumentId: string;
|
|
81
|
+
tournamentTitle: string | null;
|
|
82
|
+
removed: boolean;
|
|
83
|
+
/** Coins returned to whoever paid. */
|
|
84
|
+
refunded: number;
|
|
85
|
+
skipped?: BanRemovalSkip;
|
|
86
|
+
message?: string;
|
|
87
|
+
}
|
|
88
|
+
/** The `data` of a successful ban. */
|
|
89
|
+
export interface BanResult {
|
|
90
|
+
ban: PlayerBanSummary;
|
|
91
|
+
/** Pending join requests declined and refunded because of the ban. */
|
|
92
|
+
declinedRequests: number;
|
|
93
|
+
/** Empty unless `removeEntry` was asked for. */
|
|
94
|
+
removals: BanRemoval[];
|
|
95
|
+
}
|
|
96
|
+
/** The durations the ban dialog offers. */
|
|
97
|
+
export declare const BAN_DURATIONS: readonly [{
|
|
98
|
+
readonly key: "1d";
|
|
99
|
+
readonly label: "24 hours";
|
|
100
|
+
readonly days: 1;
|
|
101
|
+
}, {
|
|
102
|
+
readonly key: "7d";
|
|
103
|
+
readonly label: "7 days";
|
|
104
|
+
readonly days: 7;
|
|
105
|
+
}, {
|
|
106
|
+
readonly key: "30d";
|
|
107
|
+
readonly label: "30 days";
|
|
108
|
+
readonly days: 30;
|
|
109
|
+
}, {
|
|
110
|
+
readonly key: "permanent";
|
|
111
|
+
readonly label: "Permanent";
|
|
112
|
+
readonly days: null;
|
|
113
|
+
}];
|
|
114
|
+
export type BanDurationKey = (typeof BAN_DURATIONS)[number]['key'];
|
|
115
|
+
/** `expiresAt` for a duration, or null for permanent. */
|
|
116
|
+
export declare function banExpiryFor(key: BanDurationKey, now?: Date): string | null;
|
|
117
|
+
export declare function isBanActive(ban: {
|
|
118
|
+
liftedAt?: string | null;
|
|
119
|
+
expiresAt?: string | null;
|
|
120
|
+
}, now?: Date): boolean;
|