@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.
Files changed (68) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +21 -21
  3. package/README.md +26 -26
  4. package/dist/index.d.ts +95 -95
  5. package/package.json +53 -53
  6. package/src/api/analytics.ts +81 -81
  7. package/src/api/auth.ts +77 -77
  8. package/src/api/catalog.ts +171 -171
  9. package/src/api/daily.ts +122 -122
  10. package/src/api/economy/bundle.ts +69 -69
  11. package/src/api/index.ts +85 -85
  12. package/src/api/leaderboards.ts +151 -151
  13. package/src/api/messages.ts +142 -142
  14. package/src/api/mp/chat.ts +36 -36
  15. package/src/api/mp/races.ts +211 -211
  16. package/src/api/mp/rooms.ts +90 -90
  17. package/src/api/mp/snapshot.ts +17 -17
  18. package/src/api/mp.ts +110 -110
  19. package/src/api/notify.ts +111 -111
  20. package/src/api/progress/game-data.ts +189 -189
  21. package/src/api/progress.ts +59 -59
  22. package/src/api/race.ts +47 -47
  23. package/src/api/reports.ts +208 -208
  24. package/src/api/social/config.ts +47 -47
  25. package/src/api/social/endpoints.ts +179 -179
  26. package/src/api/social/people.ts +118 -118
  27. package/src/api/social/presence.ts +95 -95
  28. package/src/api/social.ts +9 -9
  29. package/src/api/version.ts +40 -40
  30. package/src/coerce.ts +50 -50
  31. package/src/common.ts +42 -42
  32. package/src/entities.ts +62 -62
  33. package/src/errors/codes/core.ts +10 -10
  34. package/src/errors/codes/multiplayer.ts +43 -43
  35. package/src/errors/codes/play.ts +5 -5
  36. package/src/errors/codes/social.ts +31 -31
  37. package/src/errors/index.ts +88 -88
  38. package/src/index.ts +16 -16
  39. package/src/limits.ts +100 -100
  40. package/src/logic/daily.ts +54 -54
  41. package/src/logic/index.ts +8 -8
  42. package/src/logic/presence.ts +62 -62
  43. package/src/logic/time.ts +81 -81
  44. package/src/manifest/changes.ts +55 -55
  45. package/src/manifest/controls.ts +42 -42
  46. package/src/manifest/fields.ts +89 -89
  47. package/src/manifest/game.ts +132 -132
  48. package/src/manifest/index.ts +12 -12
  49. package/src/manifest/inject.ts +31 -31
  50. package/src/manifest/leaderboards.ts +29 -29
  51. package/src/manifest/multiplayer.ts +71 -71
  52. package/src/manifest/race.ts +93 -93
  53. package/src/middleware.ts +47 -47
  54. package/src/realtime/define.ts +52 -52
  55. package/src/realtime/envelope.ts +67 -67
  56. package/src/realtime/events/multiplayer.ts +8 -8
  57. package/src/realtime/events/parties.ts +31 -31
  58. package/src/realtime/events/races.ts +27 -27
  59. package/src/realtime/events/rooms.ts +48 -48
  60. package/src/realtime/events/social.ts +81 -81
  61. package/src/realtime/index.ts +24 -24
  62. package/src/realtime/ops/empty.ts +8 -8
  63. package/src/realtime/ops/hello.ts +40 -40
  64. package/src/realtime/ops/multiplayer.ts +8 -8
  65. package/src/realtime/ops/parties.ts +13 -13
  66. package/src/realtime/ops/races.ts +99 -99
  67. package/src/realtime/ops/rooms.ts +1 -1
  68. package/src/realtime/ops/social.ts +58 -58
@@ -1,142 +1,142 @@
1
- /**
2
- * Direct messages between mutual friends (multiplayer-impl.md §5.2).
3
- *
4
- * "Checked before delivered": text is moderated before it is stored or
5
- * pushed. Free text needs the conversation's chat mode to be `on` (both
6
- * claimed, friends, no parent lock, site switch on); otherwise only
7
- * quick-chat ids go through. Hidden messages and messages from currently
8
- * suspended senders are left out of every read.
9
- */
10
- import { z } from 'zod';
11
- import { Millis, Ok, UserId } from '../common';
12
- import { LooseNumber, requiredId } from '../coerce';
13
- import { PlayerLite } from '../entities';
14
- import { ContentRejected, ErrorBody } from '../errors';
15
- import { requireClaimed } from '../middleware';
16
- import { ChatMode } from '../entities';
17
- import { QuickId } from './social/config';
18
-
19
- /** A message id: a positive integer, increasing over time. */
20
- export const MessageId = z.number().int().positive();
21
-
22
- /** A direct message. */
23
- export const Message = z.object({
24
- id: MessageId,
25
- /** The sender. */
26
- from: UserId,
27
- /** The recipient. */
28
- to: UserId,
29
- /** The delivered (possibly softened) text; a quick message carries its phrase's text. */
30
- text: z.string(),
31
- /** The quick-chat phrase id, for quick messages only. */
32
- quick: QuickId.optional(),
33
- /** When it was sent (epoch ms). */
34
- at: Millis,
35
- /** The recipient has read it. */
36
- read: z.boolean(),
37
- /** Words were swapped by the filter. Only on the sender's own copy. */
38
- softened: z.literal(true).optional(),
39
- });
40
-
41
- /** A direct message. */
42
- export type Message = z.infer<typeof Message>;
43
-
44
- /** A conversation summary: the other player, the last message and how many of theirs you haven't read. */
45
- export const Conversation = z.object({
46
- user: PlayerLite,
47
- last: Message,
48
- unread: z.number().int().nonnegative(),
49
- });
50
-
51
- /** A conversation summary. */
52
- export type Conversation = z.infer<typeof Conversation>;
53
-
54
- /** One page of a conversation's history. */
55
- export const MessagesPage = z.object({
56
- /** Oldest to newest, at most 50. */
57
- messages: z.array(Message),
58
- /** Older messages exist (page with `?before=` the first id). */
59
- more: z.boolean(),
60
- /** The mode the chat UI should render: `off` when DMs are off, `quick` while you are muted. */
61
- mode: ChatMode,
62
- });
63
-
64
- /** A page of history. */
65
- export type MessagesPage = z.infer<typeof MessagesPage>;
66
-
67
- /** A DM to send: free text, or a quick-chat phrase id (exactly one). */
68
- export const SendMessageInput = z.object({
69
- /** Free text (moderated; only when the conversation's mode is `on`). */
70
- text: z.string().optional(),
71
- /** A quick-chat phrase id (any mode but `off`). */
72
- quick: QuickId.optional(),
73
- }).refine((b) => (b.text === undefined) !== (b.quick === undefined), 'Send either text or a quick-chat id.');
74
-
75
- /** A DM to send. */
76
- export type SendMessageInput = z.infer<typeof SendMessageInput>;
77
-
78
- /**
79
- * Unread DMs (the `hello` snapshot's `unread`), ignoring hidden messages and
80
- * suspended senders.
81
- */
82
- export const UnreadSummary = z.object({
83
- /** Total unread. */
84
- dms: z.number().int().nonnegative(),
85
- /** Unread per sender, keyed by the sender's user id (as a decimal string). */
86
- byUser: z.record(z.string().regex(/^\d+$/), z.number().int().positive()),
87
- });
88
-
89
- /** Unread DM counts. */
90
- export type UnreadSummary = z.infer<typeof UnreadSummary>;
91
-
92
- /**
93
- * `:userId`, the other side of the conversation: a claimed, non-suspended
94
- * player other than you, else 404 "No such player." Read like `Number(x)`:
95
- * anything that is not a player's id is the same 404.
96
- */
97
- const Partner = z.object({ userId: LooseNumber.catch(Number.NaN) });
98
-
99
- /** The direct-message endpoints. */
100
- export const messagesEndpoints = {
101
- ...requireClaimed.group({
102
- listConversations: {
103
- method: 'GET', path: '/social/conversations',
104
- response: z.object({
105
- /** Newest first, one per partner, at most 200. */
106
- conversations: z.array(Conversation),
107
- }),
108
- errors: { 401: ErrorBody, 403: ErrorBody },
109
- doc: { summary: 'Your conversations, newest first', tags: ['messages'] },
110
- },
111
- listDirectMessages: {
112
- method: 'GET', path: '/social/messages/:userId', params: Partner,
113
- query: z.object({
114
- /** Only messages older than this message id; `0` or anything not a number means from the newest. */
115
- before: LooseNumber.optional().catch(undefined),
116
- }),
117
- response: MessagesPage,
118
- errors: { 401: ErrorBody, 403: ErrorBody, 404: ErrorBody },
119
- doc: { summary: 'One page of history with a player', tags: ['messages'] },
120
- },
121
- sendDirectMessage: {
122
- method: 'POST', path: '/social/messages/:userId', params: Partner, body: SendMessageInput,
123
- response: z.object({ message: Message }),
124
- errors: { 400: ErrorBody, 401: ErrorBody, 403: ErrorBody, 404: ErrorBody, 422: ContentRejected },
125
- doc: {
126
- summary: 'Send a DM to a friend',
127
- description: '403 `not_friends` or `dms_closed`; 422 when the text is rejected, the chat mode forbids it, or you are sending too fast.',
128
- tags: ['messages'],
129
- },
130
- },
131
- markDirectMessagesRead: {
132
- method: 'POST', path: '/social/messages/:userId/read', params: Partner,
133
- body: z.object({
134
- /** Mark their messages to you up to and including this id. */
135
- upTo: requiredId('upTo must be a message id.'),
136
- }),
137
- response: Ok,
138
- errors: { 400: ErrorBody, 401: ErrorBody, 403: ErrorBody, 404: ErrorBody },
139
- doc: { summary: "Mark a friend's messages to you as read", tags: ['messages'] },
140
- },
141
- }),
142
- };
1
+ /**
2
+ * Direct messages between mutual friends (multiplayer-impl.md §5.2).
3
+ *
4
+ * "Checked before delivered": text is moderated before it is stored or
5
+ * pushed. Free text needs the conversation's chat mode to be `on` (both
6
+ * claimed, friends, no parent lock, site switch on); otherwise only
7
+ * quick-chat ids go through. Hidden messages and messages from currently
8
+ * suspended senders are left out of every read.
9
+ */
10
+ import { z } from 'zod';
11
+ import { Millis, Ok, UserId } from '../common';
12
+ import { LooseNumber, requiredId } from '../coerce';
13
+ import { PlayerLite } from '../entities';
14
+ import { ContentRejected, ErrorBody } from '../errors';
15
+ import { requireClaimed } from '../middleware';
16
+ import { ChatMode } from '../entities';
17
+ import { QuickId } from './social/config';
18
+
19
+ /** A message id: a positive integer, increasing over time. */
20
+ export const MessageId = z.number().int().positive();
21
+
22
+ /** A direct message. */
23
+ export const Message = z.object({
24
+ id: MessageId,
25
+ /** The sender. */
26
+ from: UserId,
27
+ /** The recipient. */
28
+ to: UserId,
29
+ /** The delivered (possibly softened) text; a quick message carries its phrase's text. */
30
+ text: z.string(),
31
+ /** The quick-chat phrase id, for quick messages only. */
32
+ quick: QuickId.optional(),
33
+ /** When it was sent (epoch ms). */
34
+ at: Millis,
35
+ /** The recipient has read it. */
36
+ read: z.boolean(),
37
+ /** Words were swapped by the filter. Only on the sender's own copy. */
38
+ softened: z.literal(true).optional(),
39
+ });
40
+
41
+ /** A direct message. */
42
+ export type Message = z.infer<typeof Message>;
43
+
44
+ /** A conversation summary: the other player, the last message and how many of theirs you haven't read. */
45
+ export const Conversation = z.object({
46
+ user: PlayerLite,
47
+ last: Message,
48
+ unread: z.number().int().nonnegative(),
49
+ });
50
+
51
+ /** A conversation summary. */
52
+ export type Conversation = z.infer<typeof Conversation>;
53
+
54
+ /** One page of a conversation's history. */
55
+ export const MessagesPage = z.object({
56
+ /** Oldest to newest, at most 50. */
57
+ messages: z.array(Message),
58
+ /** Older messages exist (page with `?before=` the first id). */
59
+ more: z.boolean(),
60
+ /** The mode the chat UI should render: `off` when DMs are off, `quick` while you are muted. */
61
+ mode: ChatMode,
62
+ });
63
+
64
+ /** A page of history. */
65
+ export type MessagesPage = z.infer<typeof MessagesPage>;
66
+
67
+ /** A DM to send: free text, or a quick-chat phrase id (exactly one). */
68
+ export const SendMessageInput = z.object({
69
+ /** Free text (moderated; only when the conversation's mode is `on`). */
70
+ text: z.string().optional(),
71
+ /** A quick-chat phrase id (any mode but `off`). */
72
+ quick: QuickId.optional(),
73
+ }).refine((b) => (b.text === undefined) !== (b.quick === undefined), 'Send either text or a quick-chat id.');
74
+
75
+ /** A DM to send. */
76
+ export type SendMessageInput = z.infer<typeof SendMessageInput>;
77
+
78
+ /**
79
+ * Unread DMs (the `hello` snapshot's `unread`), ignoring hidden messages and
80
+ * suspended senders.
81
+ */
82
+ export const UnreadSummary = z.object({
83
+ /** Total unread. */
84
+ dms: z.number().int().nonnegative(),
85
+ /** Unread per sender, keyed by the sender's user id (as a decimal string). */
86
+ byUser: z.record(z.string().regex(/^\d+$/), z.number().int().positive()),
87
+ });
88
+
89
+ /** Unread DM counts. */
90
+ export type UnreadSummary = z.infer<typeof UnreadSummary>;
91
+
92
+ /**
93
+ * `:userId`, the other side of the conversation: a claimed, non-suspended
94
+ * player other than you, else 404 "No such player." Read like `Number(x)`:
95
+ * anything that is not a player's id is the same 404.
96
+ */
97
+ const Partner = z.object({ userId: LooseNumber.catch(Number.NaN) });
98
+
99
+ /** The direct-message endpoints. */
100
+ export const messagesEndpoints = {
101
+ ...requireClaimed.group({
102
+ listConversations: {
103
+ method: 'GET', path: '/social/conversations',
104
+ response: z.object({
105
+ /** Newest first, one per partner, at most 200. */
106
+ conversations: z.array(Conversation),
107
+ }),
108
+ errors: { 401: ErrorBody, 403: ErrorBody },
109
+ doc: { summary: 'Your conversations, newest first', tags: ['messages'] },
110
+ },
111
+ listDirectMessages: {
112
+ method: 'GET', path: '/social/messages/:userId', params: Partner,
113
+ query: z.object({
114
+ /** Only messages older than this message id; `0` or anything not a number means from the newest. */
115
+ before: LooseNumber.optional().catch(undefined),
116
+ }),
117
+ response: MessagesPage,
118
+ errors: { 401: ErrorBody, 403: ErrorBody, 404: ErrorBody },
119
+ doc: { summary: 'One page of history with a player', tags: ['messages'] },
120
+ },
121
+ sendDirectMessage: {
122
+ method: 'POST', path: '/social/messages/:userId', params: Partner, body: SendMessageInput,
123
+ response: z.object({ message: Message }),
124
+ errors: { 400: ErrorBody, 401: ErrorBody, 403: ErrorBody, 404: ErrorBody, 422: ContentRejected },
125
+ doc: {
126
+ summary: 'Send a DM to a friend',
127
+ description: '403 `not_friends` or `dms_closed`; 422 when the text is rejected, the chat mode forbids it, or you are sending too fast.',
128
+ tags: ['messages'],
129
+ },
130
+ },
131
+ markDirectMessagesRead: {
132
+ method: 'POST', path: '/social/messages/:userId/read', params: Partner,
133
+ body: z.object({
134
+ /** Mark their messages to you up to and including this id. */
135
+ upTo: requiredId('upTo must be a message id.'),
136
+ }),
137
+ response: Ok,
138
+ errors: { 400: ErrorBody, 401: ErrorBody, 403: ErrorBody, 404: ErrorBody },
139
+ doc: { summary: "Mark a friend's messages to you as read", tags: ['messages'] },
140
+ },
141
+ }),
142
+ };
@@ -1,36 +1,36 @@
1
- /**
2
- * Group chat in hub rooms and parties (multiplayer.md §4.3, §4.8): what a
3
- * player sends, and the moderated line everyone receives.
4
- */
5
- import { z } from 'zod';
6
- import { UserId } from '../../common';
7
-
8
-
9
- /**
10
- * A chat send: a quick-chat phrase id, or free text. When `quick` is set it
11
- * wins and `text` is ignored.
12
- */
13
- export const ChatSend = z.object({
14
- /** Free text; only allowed in mode `on`, then moderated. */
15
- text: z.string().optional(),
16
- /** A quick-chat phrase id (hub phrase, `e:<emoji>` emote or game phrase). */
17
- quick: z.string().optional(),
18
- });
19
-
20
- /** The line as delivered: the (possibly softened) text, and the phrase id for quick-chat. */
21
- export const ChatLine = z.object({
22
- text: z.string(),
23
- /** Set when the line is a quick-chat phrase. */
24
- quick: z.string().optional(),
25
- /** Moderation reworded the text. */
26
- softened: z.literal(true).optional(),
27
- });
28
-
29
- /** A delivered chat line. */
30
- export type ChatLine = z.infer<typeof ChatLine>;
31
-
32
- /** A chat line pushed to a room or party, with its sender. */
33
- export const ChatLineEvent = ChatLine.extend({ from: UserId });
34
-
35
- /** The error codes a chat send can fail with (besides its surface's own). */
36
- export const CHAT_ERRORS = ['rate_limited', 'content_rejected', 'bad_quick'] as const;
1
+ /**
2
+ * Group chat in hub rooms and parties (multiplayer.md §4.3, §4.8): what a
3
+ * player sends, and the moderated line everyone receives.
4
+ */
5
+ import { z } from 'zod';
6
+ import { UserId } from '../../common';
7
+
8
+
9
+ /**
10
+ * A chat send: a quick-chat phrase id, or free text. When `quick` is set it
11
+ * wins and `text` is ignored.
12
+ */
13
+ export const ChatSend = z.object({
14
+ /** Free text; only allowed in mode `on`, then moderated. */
15
+ text: z.string().optional(),
16
+ /** A quick-chat phrase id (hub phrase, `e:<emoji>` emote or game phrase). */
17
+ quick: z.string().optional(),
18
+ });
19
+
20
+ /** The line as delivered: the (possibly softened) text, and the phrase id for quick-chat. */
21
+ export const ChatLine = z.object({
22
+ text: z.string(),
23
+ /** Set when the line is a quick-chat phrase. */
24
+ quick: z.string().optional(),
25
+ /** Moderation reworded the text. */
26
+ softened: z.literal(true).optional(),
27
+ });
28
+
29
+ /** A delivered chat line. */
30
+ export type ChatLine = z.infer<typeof ChatLine>;
31
+
32
+ /** A chat line pushed to a room or party, with its sender. */
33
+ export const ChatLineEvent = ChatLine.extend({ from: UserId });
34
+
35
+ /** The error codes a chat send can fail with (besides its surface's own). */
36
+ export const CHAT_ERRORS = ['rate_limited', 'content_rejected', 'bad_quick'] as const;