@diffenderfer-games/hub-contract 2.0.0-next.0 → 2.0.0-next.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/CHANGELOG.md +7 -0
- package/LICENSE +21 -21
- package/README.md +26 -26
- package/dist/index.d.ts +95 -95
- package/package.json +53 -53
- package/src/api/analytics.ts +81 -81
- package/src/api/auth.ts +77 -77
- package/src/api/catalog.ts +171 -171
- package/src/api/daily.ts +122 -122
- package/src/api/economy/bundle.ts +69 -69
- package/src/api/index.ts +85 -85
- package/src/api/leaderboards.ts +151 -151
- package/src/api/messages.ts +142 -142
- package/src/api/mp/chat.ts +36 -36
- package/src/api/mp/races.ts +211 -211
- package/src/api/mp/rooms.ts +90 -90
- package/src/api/mp/snapshot.ts +17 -17
- package/src/api/mp.ts +110 -110
- package/src/api/notify.ts +111 -111
- package/src/api/progress/game-data.ts +189 -189
- package/src/api/progress.ts +59 -59
- package/src/api/race.ts +47 -47
- package/src/api/reports.ts +208 -208
- package/src/api/social/config.ts +47 -47
- package/src/api/social/endpoints.ts +179 -179
- package/src/api/social/people.ts +118 -118
- package/src/api/social/presence.ts +95 -95
- package/src/api/social.ts +9 -9
- package/src/api/version.ts +40 -40
- package/src/coerce.ts +50 -50
- package/src/common.ts +42 -42
- package/src/entities.ts +62 -62
- package/src/errors/codes/core.ts +10 -10
- package/src/errors/codes/multiplayer.ts +43 -43
- package/src/errors/codes/play.ts +5 -5
- package/src/errors/codes/social.ts +31 -31
- package/src/errors/index.ts +88 -88
- package/src/index.ts +16 -16
- package/src/limits.ts +100 -100
- package/src/logic/daily.ts +54 -54
- package/src/logic/index.ts +8 -8
- package/src/logic/presence.ts +62 -62
- package/src/logic/time.ts +81 -81
- package/src/manifest/changes.ts +55 -55
- package/src/manifest/controls.ts +42 -42
- package/src/manifest/fields.ts +89 -89
- package/src/manifest/game.ts +132 -132
- package/src/manifest/index.ts +12 -12
- package/src/manifest/inject.ts +31 -31
- package/src/manifest/leaderboards.ts +29 -29
- package/src/manifest/multiplayer.ts +71 -71
- package/src/manifest/race.ts +93 -93
- package/src/middleware.ts +47 -47
- package/src/realtime/define.ts +52 -52
- package/src/realtime/envelope.ts +67 -67
- package/src/realtime/events/multiplayer.ts +8 -8
- package/src/realtime/events/parties.ts +31 -31
- package/src/realtime/events/races.ts +27 -27
- package/src/realtime/events/rooms.ts +48 -48
- package/src/realtime/events/social.ts +81 -81
- package/src/realtime/index.ts +24 -24
- package/src/realtime/ops/empty.ts +8 -8
- package/src/realtime/ops/hello.ts +40 -40
- package/src/realtime/ops/multiplayer.ts +8 -8
- package/src/realtime/ops/parties.ts +13 -13
- package/src/realtime/ops/races.ts +99 -99
- package/src/realtime/ops/rooms.ts +1 -1
- package/src/realtime/ops/social.ts +58 -58
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
/** Error codes from invites, parties, hub rooms, races and the socket envelope. */
|
|
2
|
-
export const MULTIPLAYER_CODES = [
|
|
3
|
-
// Socket envelope.
|
|
4
|
-
'unknown_op',
|
|
5
|
-
'hello_required',
|
|
6
|
-
// Games and modes.
|
|
7
|
-
'not_multiplayer',
|
|
8
|
-
'not_supported',
|
|
9
|
-
'bad_mode',
|
|
10
|
-
// Invites, parties, launches, joins.
|
|
11
|
-
'bad_target',
|
|
12
|
-
'cannot_invite',
|
|
13
|
-
'already_member',
|
|
14
|
-
'party_full',
|
|
15
|
-
'invite_gone',
|
|
16
|
-
'party_gone',
|
|
17
|
-
'not_leader',
|
|
18
|
-
'bad_info',
|
|
19
|
-
'launch_invalid',
|
|
20
|
-
'not_joinable',
|
|
21
|
-
// Group chat.
|
|
22
|
-
'bad_quick',
|
|
23
|
-
// Hub rooms.
|
|
24
|
-
'room_not_found',
|
|
25
|
-
'not_in_room',
|
|
26
|
-
'not_host',
|
|
27
|
-
'too_many_rooms',
|
|
28
|
-
'banned',
|
|
29
|
-
'in_progress',
|
|
30
|
-
'room_full',
|
|
31
|
-
'not_ready',
|
|
32
|
-
'ended',
|
|
33
|
-
'bad_patch',
|
|
34
|
-
'race_room',
|
|
35
|
-
// Races.
|
|
36
|
-
'not_race',
|
|
37
|
-
'not_in_race',
|
|
38
|
-
'not_started',
|
|
39
|
-
'race_over',
|
|
40
|
-
'too_fast',
|
|
41
|
-
'bad_stats',
|
|
42
|
-
'bad_param',
|
|
43
|
-
] as const;
|
|
1
|
+
/** Error codes from invites, parties, hub rooms, races and the socket envelope. */
|
|
2
|
+
export const MULTIPLAYER_CODES = [
|
|
3
|
+
// Socket envelope.
|
|
4
|
+
'unknown_op',
|
|
5
|
+
'hello_required',
|
|
6
|
+
// Games and modes.
|
|
7
|
+
'not_multiplayer',
|
|
8
|
+
'not_supported',
|
|
9
|
+
'bad_mode',
|
|
10
|
+
// Invites, parties, launches, joins.
|
|
11
|
+
'bad_target',
|
|
12
|
+
'cannot_invite',
|
|
13
|
+
'already_member',
|
|
14
|
+
'party_full',
|
|
15
|
+
'invite_gone',
|
|
16
|
+
'party_gone',
|
|
17
|
+
'not_leader',
|
|
18
|
+
'bad_info',
|
|
19
|
+
'launch_invalid',
|
|
20
|
+
'not_joinable',
|
|
21
|
+
// Group chat.
|
|
22
|
+
'bad_quick',
|
|
23
|
+
// Hub rooms.
|
|
24
|
+
'room_not_found',
|
|
25
|
+
'not_in_room',
|
|
26
|
+
'not_host',
|
|
27
|
+
'too_many_rooms',
|
|
28
|
+
'banned',
|
|
29
|
+
'in_progress',
|
|
30
|
+
'room_full',
|
|
31
|
+
'not_ready',
|
|
32
|
+
'ended',
|
|
33
|
+
'bad_patch',
|
|
34
|
+
'race_room',
|
|
35
|
+
// Races.
|
|
36
|
+
'not_race',
|
|
37
|
+
'not_in_race',
|
|
38
|
+
'not_started',
|
|
39
|
+
'race_over',
|
|
40
|
+
'too_fast',
|
|
41
|
+
'bad_stats',
|
|
42
|
+
'bad_param',
|
|
43
|
+
] as const;
|
package/src/errors/codes/play.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/** Error codes from accounts and the single-player API (saves, stats, boards, daily, economy, trades). */
|
|
2
|
-
export const PLAY_CODES = [
|
|
3
|
-
/** A score was sent to a board only the game's own server may update (`source: "server"`). */
|
|
4
|
-
'server_board',
|
|
5
|
-
] as const;
|
|
1
|
+
/** Error codes from accounts and the single-player API (saves, stats, boards, daily, economy, trades). */
|
|
2
|
+
export const PLAY_CODES = [
|
|
3
|
+
/** A score was sent to a board only the game's own server may update (`source: "server"`). */
|
|
4
|
+
'server_board',
|
|
5
|
+
] as const;
|
|
@@ -1,31 +1,31 @@
|
|
|
1
|
-
/** Error codes from friends, messages, notifications, reports and suggestions. */
|
|
2
|
-
export const SOCIAL_CODES = [
|
|
3
|
-
/** No such player, or one who blocked you or is suspended (they look the same). */
|
|
4
|
-
'no_player',
|
|
5
|
-
/** Not one of the preset avatars. */
|
|
6
|
-
'bad_avatar',
|
|
7
|
-
/** The chat-lock password was wrong. */
|
|
8
|
-
'bad_password',
|
|
9
|
-
/** An admin turned new friend requests off. */
|
|
10
|
-
'friend_requests_off',
|
|
11
|
-
/** You can't do that to yourself (friend, block; also invites, joins and room targets). */
|
|
12
|
-
'bad_target',
|
|
13
|
-
/** The other player is a guest and must create an account first. */
|
|
14
|
-
'guest_target',
|
|
15
|
-
/** You blocked them: unblock first. */
|
|
16
|
-
'blocked',
|
|
17
|
-
/** You or they have the most friends allowed. */
|
|
18
|
-
'friends_limit',
|
|
19
|
-
/** Too many outgoing friend requests waiting. */
|
|
20
|
-
'pending_limit',
|
|
21
|
-
/** The friend request is gone. */
|
|
22
|
-
'no_request',
|
|
23
|
-
/** You're not friends (messages, friend bells). */
|
|
24
|
-
'not_friends',
|
|
25
|
-
/** You've blocked the most players allowed. */
|
|
26
|
-
'blocks_limit',
|
|
27
|
-
/** The player isn't taking messages. */
|
|
28
|
-
'dms_closed',
|
|
29
|
-
/** Too many of your suggestions are waiting for review. */
|
|
30
|
-
'too_many_pending',
|
|
31
|
-
] as const;
|
|
1
|
+
/** Error codes from friends, messages, notifications, reports and suggestions. */
|
|
2
|
+
export const SOCIAL_CODES = [
|
|
3
|
+
/** No such player, or one who blocked you or is suspended (they look the same). */
|
|
4
|
+
'no_player',
|
|
5
|
+
/** Not one of the preset avatars. */
|
|
6
|
+
'bad_avatar',
|
|
7
|
+
/** The chat-lock password was wrong. */
|
|
8
|
+
'bad_password',
|
|
9
|
+
/** An admin turned new friend requests off. */
|
|
10
|
+
'friend_requests_off',
|
|
11
|
+
/** You can't do that to yourself (friend, block; also invites, joins and room targets). */
|
|
12
|
+
'bad_target',
|
|
13
|
+
/** The other player is a guest and must create an account first. */
|
|
14
|
+
'guest_target',
|
|
15
|
+
/** You blocked them: unblock first. */
|
|
16
|
+
'blocked',
|
|
17
|
+
/** You or they have the most friends allowed. */
|
|
18
|
+
'friends_limit',
|
|
19
|
+
/** Too many outgoing friend requests waiting. */
|
|
20
|
+
'pending_limit',
|
|
21
|
+
/** The friend request is gone. */
|
|
22
|
+
'no_request',
|
|
23
|
+
/** You're not friends (messages, friend bells). */
|
|
24
|
+
'not_friends',
|
|
25
|
+
/** You've blocked the most players allowed. */
|
|
26
|
+
'blocks_limit',
|
|
27
|
+
/** The player isn't taking messages. */
|
|
28
|
+
'dms_closed',
|
|
29
|
+
/** Too many of your suggestions are waiting for review. */
|
|
30
|
+
'too_many_pending',
|
|
31
|
+
] as const;
|
package/src/errors/index.ts
CHANGED
|
@@ -1,88 +1,88 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The error model (plan §5.3). Every failed REST call and every failed socket
|
|
3
|
-
* ack carries an {@link ErrorBody}. `error` is always safe to show a player.
|
|
4
|
-
* Public codes are listed per area in `codes/`; admin-only codes live in the
|
|
5
|
-
* server-only internal contract.
|
|
6
|
-
*
|
|
7
|
-
* Each schema here also has a named `…Schema` interface. Endpoints reference
|
|
8
|
-
* these schemas hundreds of times; naming their types lets the published
|
|
9
|
-
* `.d.ts` refer to them instead of printing every code at every use.
|
|
10
|
-
*/
|
|
11
|
-
import { z } from 'zod';
|
|
12
|
-
import { Millis } from '../common';
|
|
13
|
-
import { CORE_CODES } from './codes/core';
|
|
14
|
-
import { MULTIPLAYER_CODES } from './codes/multiplayer';
|
|
15
|
-
import { PLAY_CODES } from './codes/play';
|
|
16
|
-
import { SOCIAL_CODES } from './codes/social';
|
|
17
|
-
|
|
18
|
-
const errorCode = z.enum([...CORE_CODES, ...PLAY_CODES, ...SOCIAL_CODES, ...MULTIPLAYER_CODES]);
|
|
19
|
-
|
|
20
|
-
/** The type of the {@link ErrorCode} schema. */
|
|
21
|
-
export interface ErrorCodeSchema extends Readonly<typeof errorCode> {}
|
|
22
|
-
|
|
23
|
-
/** Every public error code. */
|
|
24
|
-
export const ErrorCode: ErrorCodeSchema = errorCode;
|
|
25
|
-
|
|
26
|
-
/** A public error code. */
|
|
27
|
-
export type ErrorCode = z.infer<ErrorCodeSchema>;
|
|
28
|
-
|
|
29
|
-
const rejectReason = z.enum([
|
|
30
|
-
'link', 'personal_info', 'contact', 'language', 'slur', 'sexual', 'grown_up', 'unkind',
|
|
31
|
-
'threat', 'drugs', 'spam', 'shouting', 'too_long', 'empty', 'chars', 'reserved',
|
|
32
|
-
'rate_limited', 'muted', 'check_unavailable', 'chat_off', 'not_friends', 'suspended',
|
|
33
|
-
'quick_only', 'dms_closed', 'other',
|
|
34
|
-
]);
|
|
35
|
-
|
|
36
|
-
/** The type of the {@link RejectReason} schema. */
|
|
37
|
-
export interface RejectReasonSchema extends Readonly<typeof rejectReason> {}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Why text was rejected or a chat action was refused. Coarse on purpose: a
|
|
41
|
-
* reason names a category, never the word or list that matched.
|
|
42
|
-
*/
|
|
43
|
-
export const RejectReason: RejectReasonSchema = rejectReason;
|
|
44
|
-
|
|
45
|
-
/** A content-reject reason. */
|
|
46
|
-
export type RejectReason = z.infer<RejectReasonSchema>;
|
|
47
|
-
|
|
48
|
-
const errorBody = z.object({
|
|
49
|
-
/** A sentence that is always safe to show the player. */
|
|
50
|
-
error: z.string(),
|
|
51
|
-
/** What went wrong, for code to branch on. */
|
|
52
|
-
code: ErrorCode.optional(),
|
|
53
|
-
/** Content rejects and suspensions: the category. */
|
|
54
|
-
reason: RejectReason.optional(),
|
|
55
|
-
/** A kid-friendly suggestion, e.g. "try saying it another way". */
|
|
56
|
-
hint: z.string().optional(),
|
|
57
|
-
/** Rate limits: how long to wait, in ms (0 when not limited). */
|
|
58
|
-
retryAfterMs: z.number().int().nonnegative().optional(),
|
|
59
|
-
/** Suspensions and mutes: when they end (epoch ms). */
|
|
60
|
-
until: Millis.optional(),
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
/** The type of the {@link ErrorBody} schema. */
|
|
64
|
-
export interface ErrorBodySchema extends Readonly<typeof errorBody> {}
|
|
65
|
-
|
|
66
|
-
/** The body of every error response and failed socket ack. */
|
|
67
|
-
export const ErrorBody: ErrorBodySchema = errorBody;
|
|
68
|
-
|
|
69
|
-
/** An error body. */
|
|
70
|
-
export type ErrorBody = z.infer<ErrorBodySchema>;
|
|
71
|
-
|
|
72
|
-
const contentRejected = errorBody.extend({
|
|
73
|
-
code: z.literal('content_rejected'),
|
|
74
|
-
reason: RejectReason,
|
|
75
|
-
hint: z.string(),
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
/** The type of the {@link ContentRejected} schema. */
|
|
79
|
-
export interface ContentRejectedSchema extends Readonly<typeof contentRejected> {}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* The 422 content-rejected body (multiplayer.md §4.6), shared by DMs, room
|
|
83
|
-
* chat, suggestions, names and trade notes.
|
|
84
|
-
*/
|
|
85
|
-
export const ContentRejected: ContentRejectedSchema = contentRejected;
|
|
86
|
-
|
|
87
|
-
/** A content-rejected body. */
|
|
88
|
-
export type ContentRejected = z.infer<ContentRejectedSchema>;
|
|
1
|
+
/**
|
|
2
|
+
* The error model (plan §5.3). Every failed REST call and every failed socket
|
|
3
|
+
* ack carries an {@link ErrorBody}. `error` is always safe to show a player.
|
|
4
|
+
* Public codes are listed per area in `codes/`; admin-only codes live in the
|
|
5
|
+
* server-only internal contract.
|
|
6
|
+
*
|
|
7
|
+
* Each schema here also has a named `…Schema` interface. Endpoints reference
|
|
8
|
+
* these schemas hundreds of times; naming their types lets the published
|
|
9
|
+
* `.d.ts` refer to them instead of printing every code at every use.
|
|
10
|
+
*/
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
import { Millis } from '../common';
|
|
13
|
+
import { CORE_CODES } from './codes/core';
|
|
14
|
+
import { MULTIPLAYER_CODES } from './codes/multiplayer';
|
|
15
|
+
import { PLAY_CODES } from './codes/play';
|
|
16
|
+
import { SOCIAL_CODES } from './codes/social';
|
|
17
|
+
|
|
18
|
+
const errorCode = z.enum([...CORE_CODES, ...PLAY_CODES, ...SOCIAL_CODES, ...MULTIPLAYER_CODES]);
|
|
19
|
+
|
|
20
|
+
/** The type of the {@link ErrorCode} schema. */
|
|
21
|
+
export interface ErrorCodeSchema extends Readonly<typeof errorCode> {}
|
|
22
|
+
|
|
23
|
+
/** Every public error code. */
|
|
24
|
+
export const ErrorCode: ErrorCodeSchema = errorCode;
|
|
25
|
+
|
|
26
|
+
/** A public error code. */
|
|
27
|
+
export type ErrorCode = z.infer<ErrorCodeSchema>;
|
|
28
|
+
|
|
29
|
+
const rejectReason = z.enum([
|
|
30
|
+
'link', 'personal_info', 'contact', 'language', 'slur', 'sexual', 'grown_up', 'unkind',
|
|
31
|
+
'threat', 'drugs', 'spam', 'shouting', 'too_long', 'empty', 'chars', 'reserved',
|
|
32
|
+
'rate_limited', 'muted', 'check_unavailable', 'chat_off', 'not_friends', 'suspended',
|
|
33
|
+
'quick_only', 'dms_closed', 'other',
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
/** The type of the {@link RejectReason} schema. */
|
|
37
|
+
export interface RejectReasonSchema extends Readonly<typeof rejectReason> {}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Why text was rejected or a chat action was refused. Coarse on purpose: a
|
|
41
|
+
* reason names a category, never the word or list that matched.
|
|
42
|
+
*/
|
|
43
|
+
export const RejectReason: RejectReasonSchema = rejectReason;
|
|
44
|
+
|
|
45
|
+
/** A content-reject reason. */
|
|
46
|
+
export type RejectReason = z.infer<RejectReasonSchema>;
|
|
47
|
+
|
|
48
|
+
const errorBody = z.object({
|
|
49
|
+
/** A sentence that is always safe to show the player. */
|
|
50
|
+
error: z.string(),
|
|
51
|
+
/** What went wrong, for code to branch on. */
|
|
52
|
+
code: ErrorCode.optional(),
|
|
53
|
+
/** Content rejects and suspensions: the category. */
|
|
54
|
+
reason: RejectReason.optional(),
|
|
55
|
+
/** A kid-friendly suggestion, e.g. "try saying it another way". */
|
|
56
|
+
hint: z.string().optional(),
|
|
57
|
+
/** Rate limits: how long to wait, in ms (0 when not limited). */
|
|
58
|
+
retryAfterMs: z.number().int().nonnegative().optional(),
|
|
59
|
+
/** Suspensions and mutes: when they end (epoch ms). */
|
|
60
|
+
until: Millis.optional(),
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
/** The type of the {@link ErrorBody} schema. */
|
|
64
|
+
export interface ErrorBodySchema extends Readonly<typeof errorBody> {}
|
|
65
|
+
|
|
66
|
+
/** The body of every error response and failed socket ack. */
|
|
67
|
+
export const ErrorBody: ErrorBodySchema = errorBody;
|
|
68
|
+
|
|
69
|
+
/** An error body. */
|
|
70
|
+
export type ErrorBody = z.infer<ErrorBodySchema>;
|
|
71
|
+
|
|
72
|
+
const contentRejected = errorBody.extend({
|
|
73
|
+
code: z.literal('content_rejected'),
|
|
74
|
+
reason: RejectReason,
|
|
75
|
+
hint: z.string(),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
/** The type of the {@link ContentRejected} schema. */
|
|
79
|
+
export interface ContentRejectedSchema extends Readonly<typeof contentRejected> {}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The 422 content-rejected body (multiplayer.md §4.6), shared by DMs, room
|
|
83
|
+
* chat, suggestions, names and trade notes.
|
|
84
|
+
*/
|
|
85
|
+
export const ContentRejected: ContentRejectedSchema = contentRejected;
|
|
86
|
+
|
|
87
|
+
/** A content-rejected body. */
|
|
88
|
+
export type ContentRejected = z.infer<ContentRejectedSchema>;
|
package/src/index.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The diffenderfer.games hub wire contract: zod schemas and types for the
|
|
3
|
-
* public REST API, the `/_ws` socket envelope, ops and events, error bodies
|
|
4
|
-
* and the `game.*` manifest.
|
|
5
|
-
*
|
|
6
|
-
* @packageDocumentation
|
|
7
|
-
*/
|
|
8
|
-
export * from './common';
|
|
9
|
-
export * from './entities';
|
|
10
|
-
export * from './errors';
|
|
11
|
-
export * from './middleware';
|
|
12
|
-
export * from './api';
|
|
13
|
-
export * from './realtime';
|
|
14
|
-
export * from './manifest';
|
|
15
|
-
export * from './limits';
|
|
16
|
-
export * from './logic';
|
|
1
|
+
/**
|
|
2
|
+
* The diffenderfer.games hub wire contract: zod schemas and types for the
|
|
3
|
+
* public REST API, the `/_ws` socket envelope, ops and events, error bodies
|
|
4
|
+
* and the `game.*` manifest.
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*/
|
|
8
|
+
export * from './common';
|
|
9
|
+
export * from './entities';
|
|
10
|
+
export * from './errors';
|
|
11
|
+
export * from './middleware';
|
|
12
|
+
export * from './api';
|
|
13
|
+
export * from './realtime';
|
|
14
|
+
export * from './manifest';
|
|
15
|
+
export * from './limits';
|
|
16
|
+
export * from './logic';
|
package/src/limits.ts
CHANGED
|
@@ -1,100 +1,100 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Limits a UI has to know about: text lengths, name rules, seat counts and
|
|
3
|
-
* the rules for a game's `package.json` `game.*` block. Each number is
|
|
4
|
-
* defined once here; the schemas and the server import it.
|
|
5
|
-
*
|
|
6
|
-
* Rate thresholds, caps and timers are server-only and not part of this package.
|
|
7
|
-
*/
|
|
8
|
-
import { z } from 'zod';
|
|
9
|
-
|
|
10
|
-
/** Account name and password rules (sign-up, claim, rename). */
|
|
11
|
-
export const ACCOUNT_LIMITS = {
|
|
12
|
-
/** Shortest username, after trimming. */
|
|
13
|
-
usernameMin: 2,
|
|
14
|
-
/** Longest username, after trimming. */
|
|
15
|
-
usernameMax: 24,
|
|
16
|
-
/** Shortest password. */
|
|
17
|
-
passwordMin: 6,
|
|
18
|
-
/** How many built-in avatars there are (`a0` … `a23`). */
|
|
19
|
-
avatarCount: 24,
|
|
20
|
-
} as const;
|
|
21
|
-
|
|
22
|
-
/** The characters a username may use: letters, digits, `_`, `-` and spaces. */
|
|
23
|
-
export const USERNAME_PATTERN = /^[A-Za-z0-9_\- ]+$/;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* The text limits every social UI renders from. The hub sends the live values
|
|
27
|
-
* in `GET /social/config` and the socket `hello` (`limits`); the server can
|
|
28
|
-
* change them (`HUB_LIMITS_JSON`), so a UI should prefer the sent values.
|
|
29
|
-
*/
|
|
30
|
-
export const UiLimits = z.object({
|
|
31
|
-
/** Longest direct message, in characters. */
|
|
32
|
-
dmMaxChars: z.number().int().positive(),
|
|
33
|
-
/** Longest room or party chat line, in characters. */
|
|
34
|
-
roomChatMaxChars: z.number().int().positive(),
|
|
35
|
-
/** Longest suggestion title, in characters. */
|
|
36
|
-
suggestionTitleMax: z.number().int().positive(),
|
|
37
|
-
/** Longest suggestion body, in characters. */
|
|
38
|
-
suggestionBodyMax: z.number().int().positive(),
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
/** The text limits every social UI renders from. */
|
|
42
|
-
export type UiLimits = z.infer<typeof UiLimits>;
|
|
43
|
-
|
|
44
|
-
/** Today's default {@link UiLimits}, used until the hub sends its own. */
|
|
45
|
-
export const UI_LIMIT_DEFAULTS = {
|
|
46
|
-
dmMaxChars: 200,
|
|
47
|
-
roomChatMaxChars: 120,
|
|
48
|
-
suggestionTitleMax: 60,
|
|
49
|
-
suggestionBodyMax: 500,
|
|
50
|
-
} as const satisfies UiLimits;
|
|
51
|
-
|
|
52
|
-
/** Seats in hub rooms and parties. */
|
|
53
|
-
export const SEAT_LIMITS = {
|
|
54
|
-
/** Most players in one hub room, and the top of any `players` range. */
|
|
55
|
-
maxSeats: 8,
|
|
56
|
-
/** Most members in one party. */
|
|
57
|
-
partyMax: 8,
|
|
58
|
-
} as const;
|
|
59
|
-
|
|
60
|
-
/** What a racer's live stats (`hub.race.status(stats)`) may hold. */
|
|
61
|
-
export const RACE_STATS_LIMITS = {
|
|
62
|
-
/** Most stat keys in one update. */
|
|
63
|
-
maxKeys: 12,
|
|
64
|
-
/** Largest update, in bytes of JSON. */
|
|
65
|
-
maxBytes: 600,
|
|
66
|
-
} as const;
|
|
67
|
-
|
|
68
|
-
/** Limits on the `package.json` `game.*` block (see the manifest schemas). */
|
|
69
|
-
export const MANIFEST_LIMITS = {
|
|
70
|
-
/** Most `game.changes` entries the hub reads. */
|
|
71
|
-
changesMax: 200,
|
|
72
|
-
/** Longest `game.changes` entry, in characters. */
|
|
73
|
-
changeTextMax: 280,
|
|
74
|
-
/** Longest leaderboard title, in characters. */
|
|
75
|
-
leaderboardTitleMax: 80,
|
|
76
|
-
/** Furthest the menu button may be pushed from its corner, in px. */
|
|
77
|
-
menuOffsetMax: 600,
|
|
78
|
-
/** Longest lobby URL suffix, in characters. */
|
|
79
|
-
lobbyMax: 200,
|
|
80
|
-
/** Most `game.multiplayer.modes`. */
|
|
81
|
-
modesMax: 16,
|
|
82
|
-
/** Longest mode title, in characters. */
|
|
83
|
-
modeTitleMax: 40,
|
|
84
|
-
/** Most `game.multiplayer.quickChat` phrases. */
|
|
85
|
-
quickChatMax: 24,
|
|
86
|
-
/** Longest quick-chat phrase, in characters. */
|
|
87
|
-
quickChatTextMax: 40,
|
|
88
|
-
/** Most `game.multiplayer.chatAllow` words. */
|
|
89
|
-
chatAllowMax: 20,
|
|
90
|
-
/** Most `game.race.params`. */
|
|
91
|
-
raceParamsMax: 6,
|
|
92
|
-
/** Most options in one race param. */
|
|
93
|
-
raceOptionsMax: 12,
|
|
94
|
-
/** Most `game.race.stats` (the live HUD shows the first 3). */
|
|
95
|
-
raceStatsMax: 8,
|
|
96
|
-
/** Longest race param, option or stat title, in characters. */
|
|
97
|
-
raceTitleMax: 24,
|
|
98
|
-
/** Largest `game.race.minMs`, in ms (10 minutes). */
|
|
99
|
-
raceMinMsMax: 600_000,
|
|
100
|
-
} as const;
|
|
1
|
+
/**
|
|
2
|
+
* Limits a UI has to know about: text lengths, name rules, seat counts and
|
|
3
|
+
* the rules for a game's `package.json` `game.*` block. Each number is
|
|
4
|
+
* defined once here; the schemas and the server import it.
|
|
5
|
+
*
|
|
6
|
+
* Rate thresholds, caps and timers are server-only and not part of this package.
|
|
7
|
+
*/
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
|
|
10
|
+
/** Account name and password rules (sign-up, claim, rename). */
|
|
11
|
+
export const ACCOUNT_LIMITS = {
|
|
12
|
+
/** Shortest username, after trimming. */
|
|
13
|
+
usernameMin: 2,
|
|
14
|
+
/** Longest username, after trimming. */
|
|
15
|
+
usernameMax: 24,
|
|
16
|
+
/** Shortest password. */
|
|
17
|
+
passwordMin: 6,
|
|
18
|
+
/** How many built-in avatars there are (`a0` … `a23`). */
|
|
19
|
+
avatarCount: 24,
|
|
20
|
+
} as const;
|
|
21
|
+
|
|
22
|
+
/** The characters a username may use: letters, digits, `_`, `-` and spaces. */
|
|
23
|
+
export const USERNAME_PATTERN = /^[A-Za-z0-9_\- ]+$/;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The text limits every social UI renders from. The hub sends the live values
|
|
27
|
+
* in `GET /social/config` and the socket `hello` (`limits`); the server can
|
|
28
|
+
* change them (`HUB_LIMITS_JSON`), so a UI should prefer the sent values.
|
|
29
|
+
*/
|
|
30
|
+
export const UiLimits = z.object({
|
|
31
|
+
/** Longest direct message, in characters. */
|
|
32
|
+
dmMaxChars: z.number().int().positive(),
|
|
33
|
+
/** Longest room or party chat line, in characters. */
|
|
34
|
+
roomChatMaxChars: z.number().int().positive(),
|
|
35
|
+
/** Longest suggestion title, in characters. */
|
|
36
|
+
suggestionTitleMax: z.number().int().positive(),
|
|
37
|
+
/** Longest suggestion body, in characters. */
|
|
38
|
+
suggestionBodyMax: z.number().int().positive(),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
/** The text limits every social UI renders from. */
|
|
42
|
+
export type UiLimits = z.infer<typeof UiLimits>;
|
|
43
|
+
|
|
44
|
+
/** Today's default {@link UiLimits}, used until the hub sends its own. */
|
|
45
|
+
export const UI_LIMIT_DEFAULTS = {
|
|
46
|
+
dmMaxChars: 200,
|
|
47
|
+
roomChatMaxChars: 120,
|
|
48
|
+
suggestionTitleMax: 60,
|
|
49
|
+
suggestionBodyMax: 500,
|
|
50
|
+
} as const satisfies UiLimits;
|
|
51
|
+
|
|
52
|
+
/** Seats in hub rooms and parties. */
|
|
53
|
+
export const SEAT_LIMITS = {
|
|
54
|
+
/** Most players in one hub room, and the top of any `players` range. */
|
|
55
|
+
maxSeats: 8,
|
|
56
|
+
/** Most members in one party. */
|
|
57
|
+
partyMax: 8,
|
|
58
|
+
} as const;
|
|
59
|
+
|
|
60
|
+
/** What a racer's live stats (`hub.race.status(stats)`) may hold. */
|
|
61
|
+
export const RACE_STATS_LIMITS = {
|
|
62
|
+
/** Most stat keys in one update. */
|
|
63
|
+
maxKeys: 12,
|
|
64
|
+
/** Largest update, in bytes of JSON. */
|
|
65
|
+
maxBytes: 600,
|
|
66
|
+
} as const;
|
|
67
|
+
|
|
68
|
+
/** Limits on the `package.json` `game.*` block (see the manifest schemas). */
|
|
69
|
+
export const MANIFEST_LIMITS = {
|
|
70
|
+
/** Most `game.changes` entries the hub reads. */
|
|
71
|
+
changesMax: 200,
|
|
72
|
+
/** Longest `game.changes` entry, in characters. */
|
|
73
|
+
changeTextMax: 280,
|
|
74
|
+
/** Longest leaderboard title, in characters. */
|
|
75
|
+
leaderboardTitleMax: 80,
|
|
76
|
+
/** Furthest the menu button may be pushed from its corner, in px. */
|
|
77
|
+
menuOffsetMax: 600,
|
|
78
|
+
/** Longest lobby URL suffix, in characters. */
|
|
79
|
+
lobbyMax: 200,
|
|
80
|
+
/** Most `game.multiplayer.modes`. */
|
|
81
|
+
modesMax: 16,
|
|
82
|
+
/** Longest mode title, in characters. */
|
|
83
|
+
modeTitleMax: 40,
|
|
84
|
+
/** Most `game.multiplayer.quickChat` phrases. */
|
|
85
|
+
quickChatMax: 24,
|
|
86
|
+
/** Longest quick-chat phrase, in characters. */
|
|
87
|
+
quickChatTextMax: 40,
|
|
88
|
+
/** Most `game.multiplayer.chatAllow` words. */
|
|
89
|
+
chatAllowMax: 20,
|
|
90
|
+
/** Most `game.race.params`. */
|
|
91
|
+
raceParamsMax: 6,
|
|
92
|
+
/** Most options in one race param. */
|
|
93
|
+
raceOptionsMax: 12,
|
|
94
|
+
/** Most `game.race.stats` (the live HUD shows the first 3). */
|
|
95
|
+
raceStatsMax: 8,
|
|
96
|
+
/** Longest race param, option or stat title, in characters. */
|
|
97
|
+
raceTitleMax: 24,
|
|
98
|
+
/** Largest `game.race.minMs`, in ms (10 minutes). */
|
|
99
|
+
raceMinMsMax: 600_000,
|
|
100
|
+
} as const;
|