@magicworks/client-party-game 0.1.0-rc.4 → 0.1.0-rc.6
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 +18 -3
- package/dist/client.d.ts +37 -39
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +160 -183
- package/dist/client.js.map +1 -1
- package/dist/connection.d.ts +158 -0
- package/dist/connection.d.ts.map +1 -0
- package/dist/connection.js +375 -0
- package/dist/connection.js.map +1 -0
- package/dist/protocol/codes.d.ts +29 -0
- package/dist/protocol/codes.d.ts.map +1 -0
- package/dist/protocol/codes.js +56 -0
- package/dist/protocol/codes.js.map +1 -0
- package/dist/protocol/events.d.ts +129 -0
- package/dist/protocol/events.d.ts.map +1 -0
- package/dist/protocol/events.js +131 -0
- package/dist/protocol/events.js.map +1 -0
- package/dist/protocol/index.d.ts +25 -0
- package/dist/protocol/index.d.ts.map +1 -0
- package/dist/protocol/index.js +41 -0
- package/dist/protocol/index.js.map +1 -0
- package/dist/protocol/limits.d.ts +40 -0
- package/dist/protocol/limits.d.ts.map +1 -0
- package/dist/protocol/limits.js +43 -0
- package/dist/protocol/limits.js.map +1 -0
- package/dist/protocol/messages.d.ts +294 -0
- package/dist/protocol/messages.d.ts.map +1 -0
- package/dist/protocol/messages.js +3 -0
- package/dist/protocol/messages.js.map +1 -0
- package/dist/protocol/placement.d.ts +63 -0
- package/dist/protocol/placement.d.ts.map +1 -0
- package/dist/protocol/placement.js +82 -0
- package/dist/protocol/placement.js.map +1 -0
- package/dist/protocol/routing.d.ts +26 -0
- package/dist/protocol/routing.d.ts.map +1 -0
- package/dist/protocol/routing.js +19 -0
- package/dist/protocol/routing.js.map +1 -0
- package/dist/protocol/scoring.d.ts +7 -0
- package/dist/protocol/scoring.d.ts.map +1 -0
- package/dist/protocol/scoring.js +13 -0
- package/dist/protocol/scoring.js.map +1 -0
- package/dist/protocol/speedtest.d.ts +9 -0
- package/dist/protocol/speedtest.d.ts.map +1 -0
- package/dist/protocol/speedtest.js +22 -0
- package/dist/protocol/speedtest.js.map +1 -0
- package/dist/socket.d.ts +4 -2
- package/dist/socket.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/protocol.d.ts +0 -509
- package/dist/protocol.d.ts.map +0 -1
- package/dist/protocol.js +0 -262
- package/dist/protocol.js.map +0 -1
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import type { GameStatus, HostRefusal, PartyFeature, PlayerRefusal } from './events';
|
|
2
|
+
import type { PartyRouting } from './routing';
|
|
3
|
+
/** Enough of a room to open it again on another server. */
|
|
4
|
+
export interface RoomSnapshot {
|
|
5
|
+
code: string;
|
|
6
|
+
gameType: PartyGameType;
|
|
7
|
+
genre: string | null;
|
|
8
|
+
genreName: string | null;
|
|
9
|
+
totalRounds: number;
|
|
10
|
+
status: 'lobby' | 'over';
|
|
11
|
+
hostId: string | null;
|
|
12
|
+
touchedAt: number;
|
|
13
|
+
players: {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
score: number;
|
|
17
|
+
lastGain: number;
|
|
18
|
+
joinedAt: number;
|
|
19
|
+
ready: boolean;
|
|
20
|
+
canMove: boolean;
|
|
21
|
+
pending?: boolean;
|
|
22
|
+
}[];
|
|
23
|
+
}
|
|
24
|
+
export type PartyGameType = 'nametheartist' | 'namethesong' | 'namethesongandartist';
|
|
25
|
+
export type PartyStatus = (typeof GameStatus)[keyof typeof GameStatus];
|
|
26
|
+
export interface PartyPlayer {
|
|
27
|
+
id: string;
|
|
28
|
+
name: string;
|
|
29
|
+
score: number;
|
|
30
|
+
rank: number;
|
|
31
|
+
connected: boolean;
|
|
32
|
+
/** Has said they are here for this game. New joiners are ready already. */
|
|
33
|
+
ready: boolean;
|
|
34
|
+
answered: boolean;
|
|
35
|
+
/** null until the round is scored, then true/false for this player. */
|
|
36
|
+
correct: boolean | null;
|
|
37
|
+
/** points gained in the round that was just scored */
|
|
38
|
+
gained: number;
|
|
39
|
+
/** Brought back from a room that was lost, and not claimed by a phone yet: see HostEvent.Restore. */
|
|
40
|
+
pending?: boolean;
|
|
41
|
+
}
|
|
42
|
+
export interface PartyRoundOption {
|
|
43
|
+
id: string;
|
|
44
|
+
label: string;
|
|
45
|
+
/** A second line, under the label: the artist, in Name the Song and Artist. */
|
|
46
|
+
detail?: string;
|
|
47
|
+
}
|
|
48
|
+
/** Every ack carries `ok`; a refusal says why in `error`. */
|
|
49
|
+
export interface Ack {
|
|
50
|
+
ok: boolean;
|
|
51
|
+
error?: string;
|
|
52
|
+
}
|
|
53
|
+
export interface HostCreateRequest {
|
|
54
|
+
/** A room this TV knows: re-attach to it, or with `reopen`, reopen it. */
|
|
55
|
+
code?: string;
|
|
56
|
+
/** Opening a lobby rather than re-attaching: a recent room comes back cleared. */
|
|
57
|
+
reopen?: boolean;
|
|
58
|
+
/** Which TV this is: see `PartyGameClient`'s hostId. */
|
|
59
|
+
hostId?: string;
|
|
60
|
+
gameType?: PartyGameType;
|
|
61
|
+
genre?: string;
|
|
62
|
+
/** How the TV names the genre, for the phones: `genre` is only its id. */
|
|
63
|
+
genreName?: string | null;
|
|
64
|
+
totalRounds?: number;
|
|
65
|
+
features?: PartyFeature[];
|
|
66
|
+
}
|
|
67
|
+
export interface PartyHostSession {
|
|
68
|
+
code: string;
|
|
69
|
+
joinUrl: string;
|
|
70
|
+
/** True when this lobby picked up the previous game's room and players. */
|
|
71
|
+
reused: boolean;
|
|
72
|
+
maxPlayers: number;
|
|
73
|
+
totalRounds: number;
|
|
74
|
+
status: PartyStatus;
|
|
75
|
+
players: PartyPlayer[];
|
|
76
|
+
}
|
|
77
|
+
export type HostCreateAck = (Ack & {
|
|
78
|
+
ok: true;
|
|
79
|
+
} & PartyHostSession & PartyRouting) | (Ack & {
|
|
80
|
+
ok: false;
|
|
81
|
+
reason?: (typeof HostRefusal)[keyof typeof HostRefusal];
|
|
82
|
+
movedTo?: string;
|
|
83
|
+
});
|
|
84
|
+
export interface HostAdoptRequest {
|
|
85
|
+
token: string;
|
|
86
|
+
hostId?: string;
|
|
87
|
+
features?: PartyFeature[];
|
|
88
|
+
}
|
|
89
|
+
export interface HostMovedRequest {
|
|
90
|
+
code: string;
|
|
91
|
+
}
|
|
92
|
+
export interface RoomMoveMessage {
|
|
93
|
+
token: string;
|
|
94
|
+
}
|
|
95
|
+
export interface RoomMovedMessage {
|
|
96
|
+
code: string;
|
|
97
|
+
}
|
|
98
|
+
export interface HostStartRequest {
|
|
99
|
+
totalRounds?: number;
|
|
100
|
+
}
|
|
101
|
+
export interface HostStartAck extends Ack {
|
|
102
|
+
players?: PartyPlayer[];
|
|
103
|
+
totalRounds?: number;
|
|
104
|
+
}
|
|
105
|
+
export interface PartyRoundConfig {
|
|
106
|
+
round: number;
|
|
107
|
+
options: PartyRoundOption[];
|
|
108
|
+
correctIndex: number;
|
|
109
|
+
durationMs: number;
|
|
110
|
+
modifier: number;
|
|
111
|
+
/** Server time the round should not start before: when the TV will be there for it. */
|
|
112
|
+
notBefore?: number;
|
|
113
|
+
}
|
|
114
|
+
/** What one player answered in a round, for the TV's analytics. */
|
|
115
|
+
export interface PartyRoundAnswer {
|
|
116
|
+
playerId: string;
|
|
117
|
+
/** null: no answer */
|
|
118
|
+
answerIndex: number | null;
|
|
119
|
+
/** Server receipt minus the round's start, on the server's clock; null: no answer. */
|
|
120
|
+
answerMs: number | null;
|
|
121
|
+
}
|
|
122
|
+
export interface PartyRoundResults {
|
|
123
|
+
round: number;
|
|
124
|
+
correctIndex: number;
|
|
125
|
+
players: PartyPlayer[];
|
|
126
|
+
/**
|
|
127
|
+
* Host only - phones never see each other's answers. Missing from a server
|
|
128
|
+
* from before it was added.
|
|
129
|
+
*/
|
|
130
|
+
answers?: PartyRoundAnswer[];
|
|
131
|
+
}
|
|
132
|
+
export type HostRoundEndAck = Ack & Partial<PartyRoundResults>;
|
|
133
|
+
export interface HostGameEndAck extends Ack {
|
|
134
|
+
standings?: PartyPlayer[];
|
|
135
|
+
}
|
|
136
|
+
export interface HostRestartAck extends Ack {
|
|
137
|
+
players?: PartyPlayer[];
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* The players of the room that was lost, with their scores. Each waits in the
|
|
141
|
+
* room, pending, until a phone joins under the same name (case aside) and
|
|
142
|
+
* carries on as that player, score and all; one that never does stays in the
|
|
143
|
+
* standings. Only into a lobby, and never over a name already in it.
|
|
144
|
+
*/
|
|
145
|
+
export interface HostRestoreRequest {
|
|
146
|
+
players: {
|
|
147
|
+
name: string;
|
|
148
|
+
score: number;
|
|
149
|
+
}[];
|
|
150
|
+
}
|
|
151
|
+
export interface HostRestoreAck extends Ack {
|
|
152
|
+
players?: PartyPlayer[];
|
|
153
|
+
}
|
|
154
|
+
export interface TimePingAck {
|
|
155
|
+
now: number;
|
|
156
|
+
}
|
|
157
|
+
/** Server -> host: the roster, scores and who has answered. */
|
|
158
|
+
export interface PlayersMessage {
|
|
159
|
+
code: string;
|
|
160
|
+
status: PartyStatus;
|
|
161
|
+
players: PartyPlayer[];
|
|
162
|
+
}
|
|
163
|
+
/** The flags of a player that change without the standings changing. */
|
|
164
|
+
export type PartyPlayerFlags = Pick<PartyPlayer, 'id' | 'connected' | 'ready' | 'answered'>;
|
|
165
|
+
export interface PlayersChangedMessage {
|
|
166
|
+
code: string;
|
|
167
|
+
players: PartyPlayerFlags[];
|
|
168
|
+
}
|
|
169
|
+
/** When a round starts, on the server's clock. */
|
|
170
|
+
export interface PartyRoundGo {
|
|
171
|
+
round: number;
|
|
172
|
+
startAt: number;
|
|
173
|
+
/** How far past its planned start the round is starting. */
|
|
174
|
+
lateByMs?: number | null;
|
|
175
|
+
}
|
|
176
|
+
export interface AllAnsweredMessage {
|
|
177
|
+
round: number;
|
|
178
|
+
}
|
|
179
|
+
/** Server -> host: `game:countdown`. A phone's is empty. */
|
|
180
|
+
export interface CountdownMessage {
|
|
181
|
+
/**
|
|
182
|
+
* Phones that said they were ready before the countdown went out - all of
|
|
183
|
+
* them, or those ready by GAME_READY_TIMEOUT_MS. Missing from a server from
|
|
184
|
+
* before it was added.
|
|
185
|
+
*/
|
|
186
|
+
ready?: number;
|
|
187
|
+
}
|
|
188
|
+
/** An option as a phone shows it: no id, which only the TV has a use for. */
|
|
189
|
+
export type PlayerRoundOption = Omit<PartyRoundOption, 'id'>;
|
|
190
|
+
/** A round as a phone sees it: never the answer. */
|
|
191
|
+
export interface PlayerRound {
|
|
192
|
+
number: number;
|
|
193
|
+
totalRounds: number;
|
|
194
|
+
options: PlayerRoundOption[];
|
|
195
|
+
durationMs: number;
|
|
196
|
+
endsAt: number;
|
|
197
|
+
serverTime?: number;
|
|
198
|
+
}
|
|
199
|
+
/** An ad break on the TV, as a phone sees it: `ad:break`, and in its state. */
|
|
200
|
+
export interface PlayerAdBreak {
|
|
201
|
+
/** The round that starts once the break is over. */
|
|
202
|
+
nextRound: number;
|
|
203
|
+
totalRounds: number;
|
|
204
|
+
}
|
|
205
|
+
export interface PlayerLastResult {
|
|
206
|
+
correct: boolean;
|
|
207
|
+
points: number;
|
|
208
|
+
answerIndex: number | null;
|
|
209
|
+
}
|
|
210
|
+
/** Everything a phone needs to render itself from scratch after a reconnect. */
|
|
211
|
+
export interface PlayerState {
|
|
212
|
+
code: string;
|
|
213
|
+
status: PartyStatus;
|
|
214
|
+
gameType: PartyGameType;
|
|
215
|
+
/** The genre's id, which picks the game logo the phone shows. */
|
|
216
|
+
genre: string | null;
|
|
217
|
+
genreName: string | null;
|
|
218
|
+
totalRounds: number;
|
|
219
|
+
playerId: string;
|
|
220
|
+
name: string;
|
|
221
|
+
ready: boolean;
|
|
222
|
+
score: number;
|
|
223
|
+
rank: number | null;
|
|
224
|
+
playerCount: number;
|
|
225
|
+
answered: number | null;
|
|
226
|
+
round: PlayerRound | null;
|
|
227
|
+
lastResult: PlayerLastResult | null;
|
|
228
|
+
/** An ad is playing on the TV, between rounds: null when not. */
|
|
229
|
+
adBreak: PlayerAdBreak | null;
|
|
230
|
+
standings: PartyPlayer[] | null;
|
|
231
|
+
/**
|
|
232
|
+
* Whether the TV is there now. A room outlives its TV for a while, to be
|
|
233
|
+
* reopened by its next lobby: a phone coming back meanwhile is told.
|
|
234
|
+
*/
|
|
235
|
+
hostConnected: boolean;
|
|
236
|
+
}
|
|
237
|
+
export interface PlayerJoinRequest {
|
|
238
|
+
code: string;
|
|
239
|
+
name?: string;
|
|
240
|
+
/** Kept by the phone, so a rejoin keeps its score. */
|
|
241
|
+
playerId?: string | null;
|
|
242
|
+
/**
|
|
243
|
+
* Made up by the phone for a join from scratch, and sent again with a
|
|
244
|
+
* retry of it: a join the server took but whose answer never reached the
|
|
245
|
+
* phone left a seat under the name, which this retry gets back rather than
|
|
246
|
+
* be refused as a name taken. Nothing else matches it.
|
|
247
|
+
*/
|
|
248
|
+
joinId?: string;
|
|
249
|
+
features?: PartyFeature[];
|
|
250
|
+
}
|
|
251
|
+
export interface PlayerJoinAck extends Ack {
|
|
252
|
+
state?: PlayerState;
|
|
253
|
+
/** The party is open under this code now: join there. */
|
|
254
|
+
movedTo?: string;
|
|
255
|
+
/** The party is waiting for its TV: try again in a while. */
|
|
256
|
+
parked?: boolean;
|
|
257
|
+
reason?: (typeof PlayerRefusal)[keyof typeof PlayerRefusal];
|
|
258
|
+
}
|
|
259
|
+
export interface PlayerAnswerRequest {
|
|
260
|
+
index: number;
|
|
261
|
+
/**
|
|
262
|
+
* The round the answer is for. One that reaches the server once another
|
|
263
|
+
* round is on - held up on a stalled connection, say - is refused as
|
|
264
|
+
* `no-round` rather than counted for a round the player never saw. Pages
|
|
265
|
+
* before this field leave it out, and are scored against the round on.
|
|
266
|
+
*/
|
|
267
|
+
round?: number;
|
|
268
|
+
}
|
|
269
|
+
export interface PlayerAnswerAck extends Ack {
|
|
270
|
+
reason?: string;
|
|
271
|
+
answered: number | null;
|
|
272
|
+
}
|
|
273
|
+
export interface PlayerRoundResult {
|
|
274
|
+
round: number;
|
|
275
|
+
correctIndex: number;
|
|
276
|
+
correct: boolean;
|
|
277
|
+
answerIndex: number | null;
|
|
278
|
+
gained: number;
|
|
279
|
+
score: number;
|
|
280
|
+
rank: number;
|
|
281
|
+
playerCount: number;
|
|
282
|
+
}
|
|
283
|
+
export interface PlayerGameOver {
|
|
284
|
+
standings: PartyPlayer[];
|
|
285
|
+
gameType: PartyGameType;
|
|
286
|
+
genre: string | null;
|
|
287
|
+
genreName: string | null;
|
|
288
|
+
rank: number;
|
|
289
|
+
score: number;
|
|
290
|
+
}
|
|
291
|
+
export interface DroppedMessage {
|
|
292
|
+
reason: string;
|
|
293
|
+
}
|
|
294
|
+
//# sourceMappingURL=messages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/protocol/messages.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/protocol/messages.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { PartyRouting } from './routing';
|
|
2
|
+
/**
|
|
3
|
+
* Where a client asks which server to connect to, over plain HTTP, before it
|
|
4
|
+
* opens a socket: `GET <servers>/party/placement?c=<code>`, the code of the
|
|
5
|
+
* room it is after or nothing for a new one. A code routes the request to
|
|
6
|
+
* the room's server, as it would a socket; without one, or with a code no
|
|
7
|
+
* server holds now, the load balancer hands it to any server taking parties.
|
|
8
|
+
* The answer is the prefix of the server that answered (PartyPlacement), and
|
|
9
|
+
* the socket then carries that prefix as its `c`.
|
|
10
|
+
*
|
|
11
|
+
* So every socket carries the prefix of a server known to be there, and every
|
|
12
|
+
* request of it - long polling makes one per message - reaches that server.
|
|
13
|
+
* A socket routed by a code nobody holds any more would go to any server, a
|
|
14
|
+
* different one each request.
|
|
15
|
+
*
|
|
16
|
+
* A server that will not take the connection - on its way out, or named in
|
|
17
|
+
* `avoid` (PLACEMENT_AVOID_KEY, comma separated prefixes) because the client
|
|
18
|
+
* just failed to reach it - answers 503, and the client asks again.
|
|
19
|
+
*/
|
|
20
|
+
export declare const PLACEMENT_PATH = "/party/placement";
|
|
21
|
+
export declare const PLACEMENT_AVOID_KEY = "avoid";
|
|
22
|
+
/** A server's answer to a placement request: see PLACEMENT_PATH. */
|
|
23
|
+
export interface PartyPlacement extends PartyRouting {
|
|
24
|
+
/** The server's prefix: the `c` to open the socket with. */
|
|
25
|
+
prefix: string;
|
|
26
|
+
}
|
|
27
|
+
/** The placement request for `code`'s room (null for a new one) at `serverUrl`. */
|
|
28
|
+
export declare function placementUrl(serverUrl: string, code: string | null, avoid?: readonly string[]): string;
|
|
29
|
+
/** Whether `value` is a server's placement answer. */
|
|
30
|
+
export declare function isPlacement(value: unknown): value is PartyPlacement;
|
|
31
|
+
/** What placement needs of a `fetch` response. */
|
|
32
|
+
export interface PartyFetchResponse {
|
|
33
|
+
ok: boolean;
|
|
34
|
+
status: number;
|
|
35
|
+
json(): Promise<unknown>;
|
|
36
|
+
}
|
|
37
|
+
/** A `fetch`, or as much of one as placement needs. */
|
|
38
|
+
export type PartyFetch = (url: string) => Promise<PartyFetchResponse>;
|
|
39
|
+
/**
|
|
40
|
+
* How a placement request went: the server's answer; `refused` when a
|
|
41
|
+
* server answered but will not take the connection; or `failed`, with why,
|
|
42
|
+
* when none answered in time - or something else did, like the load
|
|
43
|
+
* balancer finding no server.
|
|
44
|
+
*/
|
|
45
|
+
export type PlacementResult = {
|
|
46
|
+
placed: PartyPlacement;
|
|
47
|
+
} | {
|
|
48
|
+
refused: true;
|
|
49
|
+
} | {
|
|
50
|
+
failed: string;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Asks the servers at `serverUrl` where to connect for `code`'s room (see
|
|
54
|
+
* PLACEMENT_PATH), giving the whole request `timeoutMs`. A timer rather than
|
|
55
|
+
* AbortSignal.timeout, which iOS before 16 lacks; never throws.
|
|
56
|
+
*/
|
|
57
|
+
export declare function requestPlacement(fetch: PartyFetch, serverUrl: string, code: string | null, avoid: readonly string[], timeoutMs: number): Promise<PlacementResult>;
|
|
58
|
+
/**
|
|
59
|
+
* How long to wait before the `retry`th try at connecting again, counting
|
|
60
|
+
* from 0: socket.io's own backoff, 1s doubling to 5s, give or take half.
|
|
61
|
+
*/
|
|
62
|
+
export declare function reconnectDelay(retry: number, random?: number): number;
|
|
63
|
+
//# sourceMappingURL=placement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"placement.d.ts","sourceRoot":"","sources":["../../src/protocol/placement.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,cAAc,qBAAqB,CAAC;AACjD,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAE3C,oEAAoE;AACpE,MAAM,WAAW,cAAe,SAAQ,YAAY;IAClD,4DAA4D;IAC5D,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,mFAAmF;AACnF,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,GAAG,IAAI,EACnB,KAAK,GAAE,SAAS,MAAM,EAAO,UAO9B;AAED,sDAAsD;AACtD,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,cAAc,CAEnE;AAED,kDAAkD;AAClD,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CAC1B;AAED,uDAAuD;AACvD,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAEtE;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GACvB;IAAE,MAAM,EAAE,cAAc,CAAA;CAAE,GAC1B;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,GACjB;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvB;;;;GAIG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,UAAU,EACjB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,GAAG,IAAI,EACnB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,eAAe,CAAC,CA6B1B;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,SAAgB,UAEnE"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PLACEMENT_AVOID_KEY = exports.PLACEMENT_PATH = void 0;
|
|
4
|
+
exports.placementUrl = placementUrl;
|
|
5
|
+
exports.isPlacement = isPlacement;
|
|
6
|
+
exports.requestPlacement = requestPlacement;
|
|
7
|
+
exports.reconnectDelay = reconnectDelay;
|
|
8
|
+
const codes_1 = require("./codes");
|
|
9
|
+
/**
|
|
10
|
+
* Where a client asks which server to connect to, over plain HTTP, before it
|
|
11
|
+
* opens a socket: `GET <servers>/party/placement?c=<code>`, the code of the
|
|
12
|
+
* room it is after or nothing for a new one. A code routes the request to
|
|
13
|
+
* the room's server, as it would a socket; without one, or with a code no
|
|
14
|
+
* server holds now, the load balancer hands it to any server taking parties.
|
|
15
|
+
* The answer is the prefix of the server that answered (PartyPlacement), and
|
|
16
|
+
* the socket then carries that prefix as its `c`.
|
|
17
|
+
*
|
|
18
|
+
* So every socket carries the prefix of a server known to be there, and every
|
|
19
|
+
* request of it - long polling makes one per message - reaches that server.
|
|
20
|
+
* A socket routed by a code nobody holds any more would go to any server, a
|
|
21
|
+
* different one each request.
|
|
22
|
+
*
|
|
23
|
+
* A server that will not take the connection - on its way out, or named in
|
|
24
|
+
* `avoid` (PLACEMENT_AVOID_KEY, comma separated prefixes) because the client
|
|
25
|
+
* just failed to reach it - answers 503, and the client asks again.
|
|
26
|
+
*/
|
|
27
|
+
exports.PLACEMENT_PATH = '/party/placement';
|
|
28
|
+
exports.PLACEMENT_AVOID_KEY = 'avoid';
|
|
29
|
+
/** The placement request for `code`'s room (null for a new one) at `serverUrl`. */
|
|
30
|
+
function placementUrl(serverUrl, code, avoid = []) {
|
|
31
|
+
const query = [`${codes_1.ROUTING_QUERY_KEY}=${encodeURIComponent(code || '')}`];
|
|
32
|
+
if (avoid.length) {
|
|
33
|
+
query.push(`${exports.PLACEMENT_AVOID_KEY}=${encodeURIComponent(avoid.join(','))}`);
|
|
34
|
+
}
|
|
35
|
+
return `${serverUrl.replace(/\/+$/, '')}${exports.PLACEMENT_PATH}?${query.join('&')}`;
|
|
36
|
+
}
|
|
37
|
+
/** Whether `value` is a server's placement answer. */
|
|
38
|
+
function isPlacement(value) {
|
|
39
|
+
return (0, codes_1.isCodePrefix)(value?.prefix);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Asks the servers at `serverUrl` where to connect for `code`'s room (see
|
|
43
|
+
* PLACEMENT_PATH), giving the whole request `timeoutMs`. A timer rather than
|
|
44
|
+
* AbortSignal.timeout, which iOS before 16 lacks; never throws.
|
|
45
|
+
*/
|
|
46
|
+
async function requestPlacement(fetch, serverUrl, code, avoid, timeoutMs) {
|
|
47
|
+
let timer;
|
|
48
|
+
const timeout = new Promise(resolve => {
|
|
49
|
+
timer = setTimeout(() => resolve({ failed: `no answer in ${timeoutMs}ms` }), timeoutMs);
|
|
50
|
+
});
|
|
51
|
+
const ask = (async () => {
|
|
52
|
+
try {
|
|
53
|
+
const response = await fetch(placementUrl(serverUrl, code, avoid));
|
|
54
|
+
const body = await response.json().catch(() => null);
|
|
55
|
+
if (response.ok && isPlacement(body)) {
|
|
56
|
+
return { placed: body };
|
|
57
|
+
}
|
|
58
|
+
// a server's own refusal, rather than the load balancer finding none
|
|
59
|
+
if (response.status === 503 && body) {
|
|
60
|
+
return { refused: true };
|
|
61
|
+
}
|
|
62
|
+
return { failed: `status ${response.status}` };
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
return { failed: error?.message || String(error) };
|
|
66
|
+
}
|
|
67
|
+
})();
|
|
68
|
+
try {
|
|
69
|
+
return await Promise.race([ask, timeout]);
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
clearTimeout(timer);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* How long to wait before the `retry`th try at connecting again, counting
|
|
77
|
+
* from 0: socket.io's own backoff, 1s doubling to 5s, give or take half.
|
|
78
|
+
*/
|
|
79
|
+
function reconnectDelay(retry, random = Math.random()) {
|
|
80
|
+
return Math.min(5000, 1000 * 2 ** Math.min(retry, 3)) * (0.5 + random);
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=placement.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"placement.js","sourceRoot":"","sources":["../../src/protocol/placement.ts"],"names":[],"mappings":";;;;;;;AAAA,mCAA0D;AAG1D;;;;;;;;;;;;;;;;;GAiBG;AACU,QAAA,cAAc,GAAG,kBAAkB,CAAC;AACpC,QAAA,mBAAmB,GAAG,OAAO,CAAC;AAQ3C,mFAAmF;AACnF,sBACE,SAAiB,EACjB,IAAmB,EACnB,KAAK,GAAsB,EAAE;IAE7B,MAAM,KAAK,GAAG,CAAC,GAAG,yBAAiB,IAAI,kBAAkB,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;IACzE,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjB,KAAK,CAAC,IAAI,CAAC,GAAG,QAAA,mBAAmB,IAAI,kBAAkB,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;IAC9E,CAAC;IACD,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,QAAA,cAAc,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;AAChF,CAAC;AAED,sDAAsD;AACtD,qBAA4B,KAAc;IACxC,OAAO,IAAA,oBAAY,EAAE,KAA+B,EAAE,MAAM,CAAC,CAAC;AAChE,CAAC;AAuBD;;;;GAIG;AACI,KAAK,2BACV,KAAiB,EACjB,SAAiB,EACjB,IAAmB,EACnB,KAAwB,EACxB,SAAiB;IAEjB,IAAI,KAAgD,CAAC;IACrD,MAAM,OAAO,GAAG,IAAI,OAAO,CAAkB,OAAO,CAAC,EAAE;QACrD,KAAK,GAAG,UAAU,CAChB,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,gBAAgB,SAAS,IAAI,EAAE,CAAC,EACxD,SAAS,CACV,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,CAAC,KAAK,IAA8B,EAAE;QAChD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,YAAY,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;YACnE,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;YACrD,IAAI,QAAQ,CAAC,EAAE,IAAI,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;YAC1B,CAAC;YACD,qEAAqE;YACrE,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC;gBACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;YAC3B,CAAC;YACD,OAAO,EAAE,MAAM,EAAE,UAAU,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;QACjD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,EAAE,MAAM,EAAG,KAAe,EAAE,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAChE,CAAC;IACH,CAAC,CAAC,EAAE,CAAC;IACL,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;IAC5C,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,wBAA+B,KAAa,EAAE,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE;IAClE,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,GAAG,MAAM,CAAC,CAAC;AACzE,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** What the phone page is told about the party servers: see phonePageConfigScript. */
|
|
2
|
+
export interface PhonePageConfig {
|
|
3
|
+
/** The servers' global name: for a code from no region it knows. */
|
|
4
|
+
socketUrl: string;
|
|
5
|
+
/** Each region's own name, for the codes that region hands out (see PARTY_REGIONS). */
|
|
6
|
+
regionSocketUrls?: Record<string, string>;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The phone page's `config.js`: where its sockets go. Written when the page
|
|
10
|
+
* is deployed, since the party servers' names depend on where; everything
|
|
11
|
+
* else the page needs from the protocol is bundled into it.
|
|
12
|
+
*/
|
|
13
|
+
export declare function phonePageConfigScript(config: PhonePageConfig): string;
|
|
14
|
+
/** The socket URL for a code, by its region. */
|
|
15
|
+
export declare function socketUrlForCode(code: string, config: PhonePageConfig): string;
|
|
16
|
+
/**
|
|
17
|
+
* Where each region's party servers are, as the servers know it: their answer
|
|
18
|
+
* to a TV opening or re-attaching to a room carries it, so a client reaches a
|
|
19
|
+
* room in its code's region (see PARTY_REGIONS) without a list of its own -
|
|
20
|
+
* and a region added or moved needs no new client.
|
|
21
|
+
*/
|
|
22
|
+
export interface PartyRouting {
|
|
23
|
+
/** Each region's own socket URL, e.g. `{ 'eu-west-1': 'https://party-ws.prod-eu-west-1.cat.roxi.tv' }`. */
|
|
24
|
+
regionSocketUrls?: Record<string, string>;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=routing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../../src/protocol/routing.ts"],"names":[],"mappings":"AAEA,sFAAsF;AACtF,MAAM,WAAW,eAAe;IAC9B,oEAAoE;IACpE,SAAS,EAAE,MAAM,CAAC;IAClB,uFAAuF;IACvF,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3C;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,eAAe,UAE5D;AAED,gDAAgD;AAChD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,UAGrE;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,2GAA2G;IAC3G,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3C"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.phonePageConfigScript = phonePageConfigScript;
|
|
4
|
+
exports.socketUrlForCode = socketUrlForCode;
|
|
5
|
+
const codes_1 = require("./codes");
|
|
6
|
+
/**
|
|
7
|
+
* The phone page's `config.js`: where its sockets go. Written when the page
|
|
8
|
+
* is deployed, since the party servers' names depend on where; everything
|
|
9
|
+
* else the page needs from the protocol is bundled into it.
|
|
10
|
+
*/
|
|
11
|
+
function phonePageConfigScript(config) {
|
|
12
|
+
return `window.PARTY_CONFIG=${JSON.stringify(config)};`;
|
|
13
|
+
}
|
|
14
|
+
/** The socket URL for a code, by its region. */
|
|
15
|
+
function socketUrlForCode(code, config) {
|
|
16
|
+
const region = (0, codes_1.regionOfCode)(code);
|
|
17
|
+
return (region && config.regionSocketUrls?.[region]) || config.socketUrl;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=routing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routing.js","sourceRoot":"","sources":["../../src/protocol/routing.ts"],"names":[],"mappings":";;;;AAAA,mCAAuC;AAUvC;;;;GAIG;AACH,+BAAsC,MAAuB;IAC3D,OAAO,uBAAuB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC;AAC1D,CAAC;AAED,gDAAgD;AAChD,0BAAiC,IAAY,EAAE,MAAuB;IACpE,MAAM,MAAM,GAAG,IAAA,oBAAY,EAAC,IAAI,CAAC,CAAC;IAClC,OAAO,CAAC,MAAM,IAAI,MAAM,CAAC,gBAAgB,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC;AAC3E,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Speed based scoring, mirroring the single player formula in GameCore:
|
|
3
|
+
* points scale with the time left on the clock, with a bonus for answering
|
|
4
|
+
* in the first half of the round.
|
|
5
|
+
*/
|
|
6
|
+
export declare function scoreForAnswer(secondsLeft: number, totalRoundTime: number, modifier: number): number;
|
|
7
|
+
//# sourceMappingURL=scoring.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scoring.d.ts","sourceRoot":"","sources":["../../src/protocol/scoring.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,EACnB,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,GACf,MAAM,CAGR"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.scoreForAnswer = scoreForAnswer;
|
|
4
|
+
/**
|
|
5
|
+
* Speed based scoring, mirroring the single player formula in GameCore:
|
|
6
|
+
* points scale with the time left on the clock, with a bonus for answering
|
|
7
|
+
* in the first half of the round.
|
|
8
|
+
*/
|
|
9
|
+
function scoreForAnswer(secondsLeft, totalRoundTime, modifier) {
|
|
10
|
+
const speedBonus = secondsLeft >= totalRoundTime / 2 ? 1.2 : 1.0;
|
|
11
|
+
return Math.max(1, Math.round(secondsLeft * modifier * speedBonus));
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=scoring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scoring.js","sourceRoot":"","sources":["../../src/protocol/scoring.ts"],"names":[],"mappings":";;;AAAA;;;;GAIG;AACH,wBACE,WAAmB,EACnB,cAAsB,EACtB,QAAgB;IAEhB,MAAM,UAAU,GAAG,WAAW,IAAI,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IACjE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC,CAAC,CAAC;AACtE,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The speed test a TV times in the lobby, to pick a video rendition it can
|
|
3
|
+
* load quickly all game: files of random bytes on the CDN that serves the
|
|
4
|
+
* phone page, closer to where the videos come from than the party servers
|
|
5
|
+
* are. One file per size, at `speedtestPath(size)`.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SPEEDTEST_SIZES: readonly [number, number, number, number, number, number];
|
|
8
|
+
export declare function speedtestPath(size: number): string;
|
|
9
|
+
//# sourceMappingURL=speedtest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"speedtest.d.ts","sourceRoot":"","sources":["../../src/protocol/speedtest.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,eAAe,2DAOlB,CAAC;AAEX,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,UAEzC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SPEEDTEST_SIZES = void 0;
|
|
4
|
+
exports.speedtestPath = speedtestPath;
|
|
5
|
+
/**
|
|
6
|
+
* The speed test a TV times in the lobby, to pick a video rendition it can
|
|
7
|
+
* load quickly all game: files of random bytes on the CDN that serves the
|
|
8
|
+
* phone page, closer to where the videos come from than the party servers
|
|
9
|
+
* are. One file per size, at `speedtestPath(size)`.
|
|
10
|
+
*/
|
|
11
|
+
exports.SPEEDTEST_SIZES = [
|
|
12
|
+
64 * 1024,
|
|
13
|
+
256 * 1024,
|
|
14
|
+
1024 * 1024,
|
|
15
|
+
2 * 1024 * 1024,
|
|
16
|
+
4 * 1024 * 1024,
|
|
17
|
+
8 * 1024 * 1024,
|
|
18
|
+
];
|
|
19
|
+
function speedtestPath(size) {
|
|
20
|
+
return `/speedtest/${size}.bin`;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=speedtest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"speedtest.js","sourceRoot":"","sources":["../../src/protocol/speedtest.ts"],"names":[],"mappings":";;;;AAAA;;;;;GAKG;AACU,QAAA,eAAe,GAAG;IAC7B,EAAE,GAAG,IAAI;IACT,GAAG,GAAG,IAAI;IACV,IAAI,GAAG,IAAI;IACX,CAAC,GAAG,IAAI,GAAG,IAAI;IACf,CAAC,GAAG,IAAI,GAAG,IAAI;IACf,CAAC,GAAG,IAAI,GAAG,IAAI;CACP,CAAC;AAEX,uBAA8B,IAAY;IACxC,OAAO,cAAc,IAAI,MAAM,CAAC;AAClC,CAAC"}
|
package/dist/socket.d.ts
CHANGED
|
@@ -19,11 +19,13 @@ export interface PartySocketOptions {
|
|
|
19
19
|
/** Always a connection of its own, never one shared with another socket to the same URL. */
|
|
20
20
|
forceNew: boolean;
|
|
21
21
|
reconnection: boolean;
|
|
22
|
-
reconnectionDelay
|
|
23
|
-
reconnectionDelayMax
|
|
22
|
+
reconnectionDelay?: number;
|
|
23
|
+
reconnectionDelayMax?: number;
|
|
24
24
|
timeout: number;
|
|
25
25
|
/** socket.io-client 2.x only: no JSONP polling fallback. */
|
|
26
26
|
jsonp?: boolean;
|
|
27
|
+
/** Cookies and the like on long polling's requests: 2.x sends them unless this is false. */
|
|
28
|
+
withCredentials?: boolean;
|
|
27
29
|
}
|
|
28
30
|
/** socket.io-client's `io`, of either major version. */
|
|
29
31
|
export type PartySocketFactory = (url: string, options: PartySocketOptions) => PartySocket;
|
package/dist/socket.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket.d.ts","sourceRoot":"","sources":["../src/socket.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,GAAG,OAAO,CAAC;IAC/D,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;IAC7C,KAAK,IAAI,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,4FAA4F;IAC5F,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"socket.d.ts","sourceRoot":"","sources":["../src/socket.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,GAAG,OAAO,CAAC;IAC/D,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;IAC7C,KAAK,IAAI,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,+DAA+D;IAC/D,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,4FAA4F;IAC5F,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,4FAA4F;IAC5F,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,wDAAwD;AACxD,MAAM,MAAM,kBAAkB,GAAG,CAC/B,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,kBAAkB,KACxB,WAAW,CAAC"}
|
package/package.json
CHANGED