onelobby 0.0.0-stage → 0.2.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/LICENSE +21 -0
- package/README.md +77 -2
- package/dist/chat.d.ts +60 -0
- package/dist/chat.js +163 -0
- package/dist/chat.js.map +1 -0
- package/dist/chunking.d.ts +17 -0
- package/dist/chunking.js +59 -0
- package/dist/chunking.js.map +1 -0
- package/dist/clock.d.ts +8 -0
- package/dist/clock.js +39 -0
- package/dist/clock.js.map +1 -0
- package/dist/codec.d.ts +141 -0
- package/dist/codec.js +308 -0
- package/dist/codec.js.map +1 -0
- package/dist/crockford.d.ts +14 -0
- package/dist/crockford.js +55 -0
- package/dist/crockford.js.map +1 -0
- package/dist/deflate.d.ts +4 -0
- package/dist/deflate.js +32 -0
- package/dist/deflate.js.map +1 -0
- package/dist/deprecated.d.ts +18 -0
- package/dist/deprecated.js +11 -0
- package/dist/deprecated.js.map +1 -0
- package/dist/direct/hub-host.d.ts +82 -0
- package/dist/direct/hub-host.js +393 -0
- package/dist/direct/hub-host.js.map +1 -0
- package/dist/direct/hub.d.ts +178 -0
- package/dist/direct/hub.js +862 -0
- package/dist/direct/hub.js.map +1 -0
- package/dist/direct/index.d.ts +57 -0
- package/dist/direct/index.js +71 -0
- package/dist/direct/index.js.map +1 -0
- package/dist/direct/portmap.d.ts +84 -0
- package/dist/direct/portmap.js +465 -0
- package/dist/direct/portmap.js.map +1 -0
- package/dist/direct-address.d.ts +20 -0
- package/dist/direct-address.js +61 -0
- package/dist/direct-address.js.map +1 -0
- package/dist/emitter.d.ts +9 -0
- package/dist/emitter.js +36 -0
- package/dist/emitter.js.map +1 -0
- package/dist/friendly.d.ts +20 -0
- package/dist/friendly.js +120 -0
- package/dist/friendly.js.map +1 -0
- package/dist/global.d.ts +8 -0
- package/dist/global.js +9 -0
- package/dist/global.js.map +1 -0
- package/dist/gridpaint.d.ts +27 -0
- package/dist/gridpaint.js +106 -0
- package/dist/gridpaint.js.map +1 -0
- package/dist/hub.d.ts +25 -0
- package/dist/hub.js +2 -0
- package/dist/hub.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/internals.d.ts +28 -0
- package/dist/internals.js +24 -0
- package/dist/internals.js.map +1 -0
- package/dist/joincode.d.ts +21 -0
- package/dist/joincode.js +84 -0
- package/dist/joincode.js.map +1 -0
- package/dist/lobby.d.ts +203 -0
- package/dist/lobby.js +840 -0
- package/dist/lobby.js.map +1 -0
- package/dist/lockstep.d.ts +132 -0
- package/dist/lockstep.js +471 -0
- package/dist/lockstep.js.map +1 -0
- package/dist/migration.d.ts +17 -0
- package/dist/migration.js +34 -0
- package/dist/migration.js.map +1 -0
- package/dist/onelobby.bundle.mjs +140 -0
- package/dist/onelobby.bundle.mjs.map +7 -0
- package/dist/onelobby.d.ts +151 -0
- package/dist/onelobby.global.js +3897 -0
- package/dist/onelobby.global.js.map +7 -0
- package/dist/onelobby.global.min.js +140 -0
- package/dist/onelobby.global.min.js.map +7 -0
- package/dist/onelobby.js +337 -0
- package/dist/onelobby.js.map +1 -0
- package/dist/signaling.d.ts +94 -0
- package/dist/signaling.js +134 -0
- package/dist/signaling.js.map +1 -0
- package/dist/socket.d.ts +63 -0
- package/dist/socket.js +142 -0
- package/dist/socket.js.map +1 -0
- package/dist/testing.d.ts +135 -0
- package/dist/testing.js +438 -0
- package/dist/testing.js.map +1 -0
- package/dist/transport.d.ts +80 -0
- package/dist/transport.js +7 -0
- package/dist/transport.js.map +1 -0
- package/dist/ui/controller.d.ts +137 -0
- package/dist/ui/controller.js +337 -0
- package/dist/ui/controller.js.map +1 -0
- package/dist/ui/element.d.ts +22 -0
- package/dist/ui/element.js +284 -0
- package/dist/ui/element.js.map +1 -0
- package/dist/ui/index.d.ts +5 -0
- package/dist/ui/index.js +9 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/server-url.d.ts +9 -0
- package/dist/ui/server-url.js +39 -0
- package/dist/ui/server-url.js.map +1 -0
- package/dist/ui/styles.d.ts +2 -0
- package/dist/ui/styles.js +70 -0
- package/dist/ui/styles.js.map +1 -0
- package/dist/voice-browser.d.ts +19 -0
- package/dist/voice-browser.js +151 -0
- package/dist/voice-browser.js.map +1 -0
- package/dist/voice-core.d.ts +98 -0
- package/dist/voice-core.js +233 -0
- package/dist/voice-core.js.map +1 -0
- package/dist/voice-types.d.ts +35 -0
- package/dist/voice-types.js +3 -0
- package/dist/voice-types.js.map +1 -0
- package/dist/voice.d.ts +68 -0
- package/dist/voice.js +223 -0
- package/dist/voice.js.map +1 -0
- package/dist/webrtc.d.ts +29 -0
- package/dist/webrtc.js +169 -0
- package/dist/webrtc.js.map +1 -0
- package/package.json +93 -4
package/dist/codec.js
ADDED
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Binary codec for the peer data channels (DESIGN s.8). Little-endian, one message per frame,
|
|
3
|
+
* 3-byte header `type:u8, route:u16`. Pure functions; no I/O.
|
|
4
|
+
*/
|
|
5
|
+
export const MAX_FRAME_BYTES = 16 * 1024;
|
|
6
|
+
/** Cap on one reassembled chunk set (spec/binary.md "Snapshot and chunks"). */
|
|
7
|
+
export const MAX_REASSEMBLED_BYTES = 64 * 1024 * 1024;
|
|
8
|
+
export const HEADER_BYTES = 3;
|
|
9
|
+
export const PROTO = 1;
|
|
10
|
+
export const MsgType = {
|
|
11
|
+
hi: 0x01,
|
|
12
|
+
welcome_dc: 0x02,
|
|
13
|
+
cmd: 0x10,
|
|
14
|
+
frame: 0x11,
|
|
15
|
+
hash: 0x12,
|
|
16
|
+
desync: 0x13,
|
|
17
|
+
snapshot_req: 0x14,
|
|
18
|
+
snapshot: 0x15,
|
|
19
|
+
chunk: 0x16,
|
|
20
|
+
raw: 0x20,
|
|
21
|
+
ping: 0x30,
|
|
22
|
+
pong: 0x31,
|
|
23
|
+
host_claim: 0x40,
|
|
24
|
+
voice: 0x50,
|
|
25
|
+
chat: 0x60,
|
|
26
|
+
chat_history: 0x61,
|
|
27
|
+
chat_reject: 0x62,
|
|
28
|
+
};
|
|
29
|
+
/** welcome_dc.mode: 1 = lockstep, 2 = raw (0 reserved) per spec/binary.md. */
|
|
30
|
+
export const MODE_LOCKSTEP = 1;
|
|
31
|
+
export const MODE_RAW = 2;
|
|
32
|
+
export class CodecError extends Error {
|
|
33
|
+
code;
|
|
34
|
+
constructor(code, detail = "") {
|
|
35
|
+
super(detail ? `${code}: ${detail}` : code);
|
|
36
|
+
this.code = code;
|
|
37
|
+
this.name = "CodecError";
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const enc = new TextEncoder();
|
|
41
|
+
const dec = new TextDecoder("utf-8", { fatal: true });
|
|
42
|
+
function decodeText(b) {
|
|
43
|
+
try {
|
|
44
|
+
return dec.decode(b);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
throw new CodecError("bad_utf8");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
class Writer {
|
|
51
|
+
buf;
|
|
52
|
+
dv;
|
|
53
|
+
pos = 0;
|
|
54
|
+
constructor(size) {
|
|
55
|
+
this.buf = new Uint8Array(size);
|
|
56
|
+
this.dv = new DataView(this.buf.buffer);
|
|
57
|
+
}
|
|
58
|
+
ensure(n) {
|
|
59
|
+
if (this.pos + n <= this.buf.length)
|
|
60
|
+
return;
|
|
61
|
+
const nb = new Uint8Array(Math.max(this.buf.length * 2, this.pos + n));
|
|
62
|
+
nb.set(this.buf);
|
|
63
|
+
this.buf = nb;
|
|
64
|
+
this.dv = new DataView(nb.buffer);
|
|
65
|
+
}
|
|
66
|
+
u8(v) { this.ensure(1); this.dv.setUint8(this.pos, v); this.pos += 1; }
|
|
67
|
+
u16(v) { this.ensure(2); this.dv.setUint16(this.pos, v, true); this.pos += 2; }
|
|
68
|
+
u32(v) { this.ensure(4); this.dv.setUint32(this.pos, v >>> 0, true); this.pos += 4; }
|
|
69
|
+
i64(v) { this.ensure(8); this.dv.setBigInt64(this.pos, BigInt(v), true); this.pos += 8; }
|
|
70
|
+
u64(v) { this.ensure(8); this.dv.setBigUint64(this.pos, BigInt.asUintN(64, v), true); this.pos += 8; }
|
|
71
|
+
bytes(b) { this.ensure(b.length); this.buf.set(b, this.pos); this.pos += b.length; }
|
|
72
|
+
done() { return this.buf.slice(0, this.pos); }
|
|
73
|
+
}
|
|
74
|
+
class Reader {
|
|
75
|
+
buf;
|
|
76
|
+
dv;
|
|
77
|
+
pos = 0;
|
|
78
|
+
constructor(buf) {
|
|
79
|
+
this.buf = buf;
|
|
80
|
+
this.dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
|
|
81
|
+
}
|
|
82
|
+
need(n) { if (this.pos + n > this.buf.length)
|
|
83
|
+
throw new CodecError("too_short", "truncated frame"); }
|
|
84
|
+
u8() { this.need(1); const v = this.dv.getUint8(this.pos); this.pos += 1; return v; }
|
|
85
|
+
u16() { this.need(2); const v = this.dv.getUint16(this.pos, true); this.pos += 2; return v; }
|
|
86
|
+
u32() { this.need(4); const v = this.dv.getUint32(this.pos, true); this.pos += 4; return v; }
|
|
87
|
+
i64() { this.need(8); const v = this.dv.getBigInt64(this.pos, true); this.pos += 8; return Number(v); }
|
|
88
|
+
u64() { this.need(8); const v = this.dv.getBigUint64(this.pos, true); this.pos += 8; return v; }
|
|
89
|
+
bytes(n) { this.need(n); const v = this.buf.slice(this.pos, this.pos + n); this.pos += n; return v; }
|
|
90
|
+
rest() { const v = this.buf.slice(this.pos); this.pos = this.buf.length; return v; }
|
|
91
|
+
}
|
|
92
|
+
export function encodeMessage(msg, route = 0, allowOversize = false) {
|
|
93
|
+
const w = new Writer(64);
|
|
94
|
+
w.u8(MsgType[msg.type]);
|
|
95
|
+
w.u16(route);
|
|
96
|
+
switch (msg.type) {
|
|
97
|
+
case "hi":
|
|
98
|
+
w.u8(msg.proto);
|
|
99
|
+
w.u16(msg.peerId);
|
|
100
|
+
w.i64(msg.lastTick);
|
|
101
|
+
break;
|
|
102
|
+
case "welcome_dc":
|
|
103
|
+
w.i64(msg.tick);
|
|
104
|
+
w.u8(msg.tickRate);
|
|
105
|
+
w.u8(msg.mode);
|
|
106
|
+
w.u16(msg.hostId);
|
|
107
|
+
break;
|
|
108
|
+
case "cmd":
|
|
109
|
+
w.u32(msg.seq);
|
|
110
|
+
w.u16(msg.bytes.length);
|
|
111
|
+
w.bytes(msg.bytes);
|
|
112
|
+
break;
|
|
113
|
+
case "frame":
|
|
114
|
+
w.i64(msg.tick);
|
|
115
|
+
w.u16(msg.commands.length);
|
|
116
|
+
for (const c of msg.commands) {
|
|
117
|
+
w.u16(c.peerId);
|
|
118
|
+
w.u16(c.bytes.length);
|
|
119
|
+
w.bytes(c.bytes);
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
case "hash":
|
|
123
|
+
w.i64(msg.tick);
|
|
124
|
+
w.u64(msg.hash);
|
|
125
|
+
break;
|
|
126
|
+
case "desync":
|
|
127
|
+
w.i64(msg.tick);
|
|
128
|
+
w.u64(msg.hostHash);
|
|
129
|
+
w.u64(msg.yourHash);
|
|
130
|
+
break;
|
|
131
|
+
case "snapshot_req":
|
|
132
|
+
w.i64(msg.sinceTick);
|
|
133
|
+
break;
|
|
134
|
+
case "snapshot":
|
|
135
|
+
w.i64(msg.tick);
|
|
136
|
+
w.u32(msg.total);
|
|
137
|
+
w.u8(msg.flags);
|
|
138
|
+
break;
|
|
139
|
+
case "chunk":
|
|
140
|
+
w.u32(msg.id);
|
|
141
|
+
w.u16(msg.index);
|
|
142
|
+
w.u16(msg.count);
|
|
143
|
+
w.bytes(msg.bytes);
|
|
144
|
+
break;
|
|
145
|
+
case "raw":
|
|
146
|
+
w.bytes(msg.bytes);
|
|
147
|
+
break;
|
|
148
|
+
case "ping":
|
|
149
|
+
case "pong":
|
|
150
|
+
w.u32(msg.n);
|
|
151
|
+
w.u32(msg.t);
|
|
152
|
+
break;
|
|
153
|
+
case "host_claim":
|
|
154
|
+
w.u16(msg.epoch);
|
|
155
|
+
w.u16(msg.hostId);
|
|
156
|
+
w.i64(msg.tick);
|
|
157
|
+
break;
|
|
158
|
+
case "voice":
|
|
159
|
+
w.u16(msg.seq);
|
|
160
|
+
w.u8(msg.flags);
|
|
161
|
+
w.u8(msg.frames.length);
|
|
162
|
+
for (const f of msg.frames) {
|
|
163
|
+
w.u16(f.length);
|
|
164
|
+
w.bytes(f);
|
|
165
|
+
}
|
|
166
|
+
break;
|
|
167
|
+
case "chat": {
|
|
168
|
+
const t = enc.encode(msg.text);
|
|
169
|
+
w.u32(msg.id);
|
|
170
|
+
w.i64(msg.ts);
|
|
171
|
+
w.u16(t.length);
|
|
172
|
+
w.bytes(t);
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
case "chat_history":
|
|
176
|
+
w.u16(msg.lines.length);
|
|
177
|
+
for (const l of msg.lines) {
|
|
178
|
+
const t = enc.encode(l.text);
|
|
179
|
+
w.u16(l.peerId);
|
|
180
|
+
w.u32(l.id);
|
|
181
|
+
w.i64(l.ts);
|
|
182
|
+
w.u16(t.length);
|
|
183
|
+
w.bytes(t);
|
|
184
|
+
}
|
|
185
|
+
break;
|
|
186
|
+
case "chat_reject":
|
|
187
|
+
w.u32(msg.id);
|
|
188
|
+
w.u8(msg.reason);
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
const out = w.done();
|
|
192
|
+
if (!allowOversize && out.length > MAX_FRAME_BYTES)
|
|
193
|
+
throw new CodecError("too_long", `${out.length} > ${MAX_FRAME_BYTES}`);
|
|
194
|
+
return out;
|
|
195
|
+
}
|
|
196
|
+
const TYPE_BY_ID = new Map(Object.keys(MsgType).map((k) => [MsgType[k], k]));
|
|
197
|
+
/**
|
|
198
|
+
* Decode one frame. Returns null for unknown types (ignored for forward compatibility); throws on a truncated body.
|
|
199
|
+
* A frame reassembled from chunks passes `reassembled` so the cap is MAX_REASSEMBLED_BYTES instead of one frame's.
|
|
200
|
+
*/
|
|
201
|
+
export function decodeMessage(buf, reassembled = false) {
|
|
202
|
+
const max = reassembled ? MAX_REASSEMBLED_BYTES : MAX_FRAME_BYTES;
|
|
203
|
+
if (buf.length > max)
|
|
204
|
+
throw new CodecError("too_long", `${buf.length} > ${max}`);
|
|
205
|
+
if (buf.length < HEADER_BYTES)
|
|
206
|
+
throw new CodecError("too_short", "header");
|
|
207
|
+
const r = new Reader(buf);
|
|
208
|
+
const type = TYPE_BY_ID.get(r.u8());
|
|
209
|
+
const route = r.u16();
|
|
210
|
+
if (!type)
|
|
211
|
+
return null;
|
|
212
|
+
let msg;
|
|
213
|
+
switch (type) {
|
|
214
|
+
case "hi":
|
|
215
|
+
msg = { type, proto: r.u8(), peerId: r.u16(), lastTick: r.i64() };
|
|
216
|
+
break;
|
|
217
|
+
case "welcome_dc":
|
|
218
|
+
msg = { type, tick: r.i64(), tickRate: r.u8(), mode: r.u8(), hostId: r.u16() };
|
|
219
|
+
break;
|
|
220
|
+
case "cmd": {
|
|
221
|
+
const seq = r.u32();
|
|
222
|
+
msg = { type, seq, bytes: r.bytes(r.u16()) };
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
case "frame": {
|
|
226
|
+
const tick = r.i64();
|
|
227
|
+
const n = r.u16();
|
|
228
|
+
const commands = [];
|
|
229
|
+
for (let i = 0; i < n; i++) {
|
|
230
|
+
const peerId = r.u16();
|
|
231
|
+
commands.push({ peerId, bytes: r.bytes(r.u16()) });
|
|
232
|
+
}
|
|
233
|
+
msg = { type, tick, commands };
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
case "hash":
|
|
237
|
+
msg = { type, tick: r.i64(), hash: r.u64() };
|
|
238
|
+
break;
|
|
239
|
+
case "desync":
|
|
240
|
+
msg = { type, tick: r.i64(), hostHash: r.u64(), yourHash: r.u64() };
|
|
241
|
+
break;
|
|
242
|
+
case "snapshot_req":
|
|
243
|
+
msg = { type, sinceTick: r.i64() };
|
|
244
|
+
break;
|
|
245
|
+
case "snapshot":
|
|
246
|
+
msg = { type, tick: r.i64(), total: r.u32(), flags: r.u8() };
|
|
247
|
+
break;
|
|
248
|
+
case "chunk":
|
|
249
|
+
msg = { type, id: r.u32(), index: r.u16(), count: r.u16(), bytes: r.rest() };
|
|
250
|
+
break;
|
|
251
|
+
case "raw":
|
|
252
|
+
msg = { type, bytes: r.rest() };
|
|
253
|
+
break;
|
|
254
|
+
case "ping":
|
|
255
|
+
case "pong":
|
|
256
|
+
msg = { type, n: r.u32(), t: r.u32() };
|
|
257
|
+
break;
|
|
258
|
+
case "host_claim":
|
|
259
|
+
msg = { type, epoch: r.u16(), hostId: r.u16(), tick: r.i64() };
|
|
260
|
+
break;
|
|
261
|
+
case "voice": {
|
|
262
|
+
const seq = r.u16();
|
|
263
|
+
const flags = r.u8();
|
|
264
|
+
const n = r.u8();
|
|
265
|
+
const frames = [];
|
|
266
|
+
for (let i = 0; i < n; i++)
|
|
267
|
+
frames.push(r.bytes(r.u16()));
|
|
268
|
+
msg = { type, seq, flags, frames };
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
case "chat": {
|
|
272
|
+
const id = r.u32();
|
|
273
|
+
const ts = r.i64();
|
|
274
|
+
msg = { type, id, ts, text: decodeText(r.bytes(r.u16())) };
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
case "chat_history": {
|
|
278
|
+
const n = r.u16();
|
|
279
|
+
const lines = [];
|
|
280
|
+
for (let i = 0; i < n; i++) {
|
|
281
|
+
const peerId = r.u16();
|
|
282
|
+
const id = r.u32();
|
|
283
|
+
const ts = r.i64();
|
|
284
|
+
lines.push({ peerId, id, ts, text: decodeText(r.bytes(r.u16())) });
|
|
285
|
+
}
|
|
286
|
+
msg = { type, lines };
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
case "chat_reject":
|
|
290
|
+
msg = { type, id: r.u32(), reason: r.u8() };
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
if (r.pos !== buf.length)
|
|
294
|
+
throw new CodecError("trailing", `${buf.length - r.pos} extra bytes`);
|
|
295
|
+
return { route, msg };
|
|
296
|
+
}
|
|
297
|
+
/** Rewrite the route field on a copy (the host does this when relaying). */
|
|
298
|
+
export function withRoute(buf, route) {
|
|
299
|
+
const out = buf.slice();
|
|
300
|
+
out[1] = route & 0xff;
|
|
301
|
+
out[2] = (route >> 8) & 0xff;
|
|
302
|
+
return out;
|
|
303
|
+
}
|
|
304
|
+
export const CHAT_REJECT = { rate: 1, length: 2, mutedByHost: 3 };
|
|
305
|
+
export function chatRejectName(code) {
|
|
306
|
+
return code === 1 ? "rate" : code === 2 ? "length" : code === 3 ? "muted_by_host" : "unknown";
|
|
307
|
+
}
|
|
308
|
+
//# sourceMappingURL=codec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codec.js","sourceRoot":"","sources":["../src/codec.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Binary codec for the peer data channels (DESIGN s.8). Little-endian, one message per frame,\n * 3-byte header `type:u8, route:u16`. Pure functions; no I/O.\n */\n\nexport const MAX_FRAME_BYTES = 16 * 1024;\n/** Cap on one reassembled chunk set (spec/binary.md \"Snapshot and chunks\"). */\nexport const MAX_REASSEMBLED_BYTES = 64 * 1024 * 1024;\nexport const HEADER_BYTES = 3;\nexport const PROTO = 1;\n\nexport const MsgType = {\n hi: 0x01,\n welcome_dc: 0x02,\n cmd: 0x10,\n frame: 0x11,\n hash: 0x12,\n desync: 0x13,\n snapshot_req: 0x14,\n snapshot: 0x15,\n chunk: 0x16,\n raw: 0x20,\n ping: 0x30,\n pong: 0x31,\n host_claim: 0x40,\n voice: 0x50,\n chat: 0x60,\n chat_history: 0x61,\n chat_reject: 0x62,\n} as const;\n\n/** welcome_dc.mode: 1 = lockstep, 2 = raw (0 reserved) per spec/binary.md. */\nexport const MODE_LOCKSTEP = 1;\nexport const MODE_RAW = 2;\n\nexport type CodecErrorCode = \"too_short\" | \"trailing\" | \"too_long\" | \"bad_utf8\" | \"bad_value\";\nexport class CodecError extends Error {\n constructor(public code: CodecErrorCode, detail = \"\") {\n super(detail ? `${code}: ${detail}` : code);\n this.name = \"CodecError\";\n }\n}\n\nexport interface FrameCommand {\n peerId: number;\n bytes: Uint8Array;\n}\nexport interface ChatLine {\n peerId: number;\n id: number;\n ts: number;\n text: string;\n}\n\nexport type Message =\n | { type: \"hi\"; proto: number; peerId: number; lastTick: number }\n | { type: \"welcome_dc\"; tick: number; tickRate: number; mode: number; hostId: number }\n | { type: \"cmd\"; seq: number; bytes: Uint8Array }\n | { type: \"frame\"; tick: number; commands: FrameCommand[] }\n | { type: \"hash\"; tick: number; hash: bigint }\n | { type: \"desync\"; tick: number; hostHash: bigint; yourHash: bigint }\n | { type: \"snapshot_req\"; sinceTick: number }\n | { type: \"snapshot\"; tick: number; total: number; flags: number }\n | { type: \"chunk\"; id: number; index: number; count: number; bytes: Uint8Array }\n | { type: \"raw\"; bytes: Uint8Array }\n | { type: \"ping\"; n: number; t: number }\n | { type: \"pong\"; n: number; t: number }\n | { type: \"host_claim\"; epoch: number; hostId: number; tick: number }\n | { type: \"voice\"; seq: number; flags: number; frames: Uint8Array[] }\n | { type: \"chat\"; id: number; ts: number; text: string }\n | { type: \"chat_history\"; lines: ChatLine[] }\n | { type: \"chat_reject\"; id: number; reason: number };\n\nexport interface Decoded {\n route: number;\n msg: Message;\n}\n\nconst enc = new TextEncoder();\nconst dec = new TextDecoder(\"utf-8\", { fatal: true });\nfunction decodeText(b: Uint8Array): string {\n try {\n return dec.decode(b);\n } catch {\n throw new CodecError(\"bad_utf8\");\n }\n}\n\nclass Writer {\n buf: Uint8Array;\n dv: DataView;\n pos = 0;\n constructor(size: number) {\n this.buf = new Uint8Array(size);\n this.dv = new DataView(this.buf.buffer);\n }\n ensure(n: number) {\n if (this.pos + n <= this.buf.length) return;\n const nb = new Uint8Array(Math.max(this.buf.length * 2, this.pos + n));\n nb.set(this.buf);\n this.buf = nb;\n this.dv = new DataView(nb.buffer);\n }\n u8(v: number) { this.ensure(1); this.dv.setUint8(this.pos, v); this.pos += 1; }\n u16(v: number) { this.ensure(2); this.dv.setUint16(this.pos, v, true); this.pos += 2; }\n u32(v: number) { this.ensure(4); this.dv.setUint32(this.pos, v >>> 0, true); this.pos += 4; }\n i64(v: number) { this.ensure(8); this.dv.setBigInt64(this.pos, BigInt(v), true); this.pos += 8; }\n u64(v: bigint) { this.ensure(8); this.dv.setBigUint64(this.pos, BigInt.asUintN(64, v), true); this.pos += 8; }\n bytes(b: Uint8Array) { this.ensure(b.length); this.buf.set(b, this.pos); this.pos += b.length; }\n done(): Uint8Array { return this.buf.slice(0, this.pos); }\n}\n\nclass Reader {\n dv: DataView;\n pos = 0;\n constructor(public buf: Uint8Array) {\n this.dv = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);\n }\n need(n: number) { if (this.pos + n > this.buf.length) throw new CodecError(\"too_short\", \"truncated frame\"); }\n u8() { this.need(1); const v = this.dv.getUint8(this.pos); this.pos += 1; return v; }\n u16() { this.need(2); const v = this.dv.getUint16(this.pos, true); this.pos += 2; return v; }\n u32() { this.need(4); const v = this.dv.getUint32(this.pos, true); this.pos += 4; return v; }\n i64() { this.need(8); const v = this.dv.getBigInt64(this.pos, true); this.pos += 8; return Number(v); }\n u64() { this.need(8); const v = this.dv.getBigUint64(this.pos, true); this.pos += 8; return v; }\n bytes(n: number) { this.need(n); const v = this.buf.slice(this.pos, this.pos + n); this.pos += n; return v; }\n rest() { const v = this.buf.slice(this.pos); this.pos = this.buf.length; return v; }\n}\n\nexport function encodeMessage(msg: Message, route = 0, allowOversize = false): Uint8Array {\n const w = new Writer(64);\n w.u8(MsgType[msg.type]);\n w.u16(route);\n switch (msg.type) {\n case \"hi\": w.u8(msg.proto); w.u16(msg.peerId); w.i64(msg.lastTick); break;\n case \"welcome_dc\": w.i64(msg.tick); w.u8(msg.tickRate); w.u8(msg.mode); w.u16(msg.hostId); break;\n case \"cmd\": w.u32(msg.seq); w.u16(msg.bytes.length); w.bytes(msg.bytes); break;\n case \"frame\":\n w.i64(msg.tick); w.u16(msg.commands.length);\n for (const c of msg.commands) { w.u16(c.peerId); w.u16(c.bytes.length); w.bytes(c.bytes); }\n break;\n case \"hash\": w.i64(msg.tick); w.u64(msg.hash); break;\n case \"desync\": w.i64(msg.tick); w.u64(msg.hostHash); w.u64(msg.yourHash); break;\n case \"snapshot_req\": w.i64(msg.sinceTick); break;\n case \"snapshot\": w.i64(msg.tick); w.u32(msg.total); w.u8(msg.flags); break;\n case \"chunk\": w.u32(msg.id); w.u16(msg.index); w.u16(msg.count); w.bytes(msg.bytes); break;\n case \"raw\": w.bytes(msg.bytes); break;\n case \"ping\": case \"pong\": w.u32(msg.n); w.u32(msg.t); break;\n case \"host_claim\": w.u16(msg.epoch); w.u16(msg.hostId); w.i64(msg.tick); break;\n case \"voice\":\n w.u16(msg.seq); w.u8(msg.flags); w.u8(msg.frames.length);\n for (const f of msg.frames) { w.u16(f.length); w.bytes(f); }\n break;\n case \"chat\": { const t = enc.encode(msg.text); w.u32(msg.id); w.i64(msg.ts); w.u16(t.length); w.bytes(t); break; }\n case \"chat_history\":\n w.u16(msg.lines.length);\n for (const l of msg.lines) {\n const t = enc.encode(l.text);\n w.u16(l.peerId); w.u32(l.id); w.i64(l.ts); w.u16(t.length); w.bytes(t);\n }\n break;\n case \"chat_reject\": w.u32(msg.id); w.u8(msg.reason); break;\n }\n const out = w.done();\n if (!allowOversize && out.length > MAX_FRAME_BYTES) throw new CodecError(\"too_long\", `${out.length} > ${MAX_FRAME_BYTES}`);\n return out;\n}\n\nconst TYPE_BY_ID = new Map<number, keyof typeof MsgType>(\n (Object.keys(MsgType) as Array<keyof typeof MsgType>).map((k) => [MsgType[k], k]),\n);\n\n/**\n * Decode one frame. Returns null for unknown types (ignored for forward compatibility); throws on a truncated body.\n * A frame reassembled from chunks passes `reassembled` so the cap is MAX_REASSEMBLED_BYTES instead of one frame's.\n */\nexport function decodeMessage(buf: Uint8Array, reassembled = false): Decoded | null {\n const max = reassembled ? MAX_REASSEMBLED_BYTES : MAX_FRAME_BYTES;\n if (buf.length > max) throw new CodecError(\"too_long\", `${buf.length} > ${max}`);\n if (buf.length < HEADER_BYTES) throw new CodecError(\"too_short\", \"header\");\n const r = new Reader(buf);\n const type = TYPE_BY_ID.get(r.u8());\n const route = r.u16();\n if (!type) return null;\n let msg: Message;\n switch (type) {\n case \"hi\": msg = { type, proto: r.u8(), peerId: r.u16(), lastTick: r.i64() }; break;\n case \"welcome_dc\": msg = { type, tick: r.i64(), tickRate: r.u8(), mode: r.u8(), hostId: r.u16() }; break;\n case \"cmd\": { const seq = r.u32(); msg = { type, seq, bytes: r.bytes(r.u16()) }; break; }\n case \"frame\": {\n const tick = r.i64(); const n = r.u16(); const commands: FrameCommand[] = [];\n for (let i = 0; i < n; i++) { const peerId = r.u16(); commands.push({ peerId, bytes: r.bytes(r.u16()) }); }\n msg = { type, tick, commands }; break;\n }\n case \"hash\": msg = { type, tick: r.i64(), hash: r.u64() }; break;\n case \"desync\": msg = { type, tick: r.i64(), hostHash: r.u64(), yourHash: r.u64() }; break;\n case \"snapshot_req\": msg = { type, sinceTick: r.i64() }; break;\n case \"snapshot\": msg = { type, tick: r.i64(), total: r.u32(), flags: r.u8() }; break;\n case \"chunk\": msg = { type, id: r.u32(), index: r.u16(), count: r.u16(), bytes: r.rest() }; break;\n case \"raw\": msg = { type, bytes: r.rest() }; break;\n case \"ping\": case \"pong\": msg = { type, n: r.u32(), t: r.u32() }; break;\n case \"host_claim\": msg = { type, epoch: r.u16(), hostId: r.u16(), tick: r.i64() }; break;\n case \"voice\": {\n const seq = r.u16(); const flags = r.u8(); const n = r.u8(); const frames: Uint8Array[] = [];\n for (let i = 0; i < n; i++) frames.push(r.bytes(r.u16()));\n msg = { type, seq, flags, frames }; break;\n }\n case \"chat\": { const id = r.u32(); const ts = r.i64(); msg = { type, id, ts, text: decodeText(r.bytes(r.u16())) }; break; }\n case \"chat_history\": {\n const n = r.u16(); const lines: ChatLine[] = [];\n for (let i = 0; i < n; i++) {\n const peerId = r.u16(); const id = r.u32(); const ts = r.i64();\n lines.push({ peerId, id, ts, text: decodeText(r.bytes(r.u16())) });\n }\n msg = { type, lines }; break;\n }\n case \"chat_reject\": msg = { type, id: r.u32(), reason: r.u8() }; break;\n }\n if (r.pos !== buf.length) throw new CodecError(\"trailing\", `${buf.length - r.pos} extra bytes`);\n return { route, msg };\n}\n\n/** Rewrite the route field on a copy (the host does this when relaying). */\nexport function withRoute(buf: Uint8Array, route: number): Uint8Array {\n const out = buf.slice();\n out[1] = route & 0xff;\n out[2] = (route >> 8) & 0xff;\n return out;\n}\n\nexport const CHAT_REJECT = { rate: 1, length: 2, mutedByHost: 3 } as const;\nexport type ChatRejectReason = \"rate\" | \"length\" | \"muted_by_host\" | \"unknown\";\nexport function chatRejectName(code: number): ChatRejectReason {\n return code === 1 ? \"rate\" : code === 2 ? \"length\" : code === 3 ? \"muted_by_host\" : \"unknown\";\n}\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Crockford Base32 (DESIGN s.6): digits + 22 letters, no I L O U. */
|
|
2
|
+
export declare const CROCKFORD_ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
3
|
+
export declare const CODE_LENGTH = 6;
|
|
4
|
+
/**
|
|
5
|
+
* Decode user input to the canonical code form (upper case, no separators, `i`/`l` -> `1`, `o` -> `0`).
|
|
6
|
+
* Returns null when the input is not a valid 6-character code.
|
|
7
|
+
*/
|
|
8
|
+
export declare function normalizeCode(input: string): string | null;
|
|
9
|
+
/** Canonical code -> `7HQ 4K2` display form. */
|
|
10
|
+
export declare function formatCode(code: string): string;
|
|
11
|
+
/** Numeric value of a Crockford string (same alias rules). Throws on invalid characters. */
|
|
12
|
+
export declare function decodeCrockford(input: string): bigint;
|
|
13
|
+
/** Inverse of decodeCrockford, left-padded to `length` if given. */
|
|
14
|
+
export declare function encodeCrockford(value: bigint, length?: number): string;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/** Crockford Base32 (DESIGN s.6): digits + 22 letters, no I L O U. */
|
|
2
|
+
export const CROCKFORD_ALPHABET = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
3
|
+
export const CODE_LENGTH = 6;
|
|
4
|
+
/** ASCII-only uppercasing: `ß` must not fold to `SS`. */
|
|
5
|
+
function asciiUpper(s) {
|
|
6
|
+
return s.replace(/[a-z]/g, (c) => c.toUpperCase());
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Decode user input to the canonical code form (upper case, no separators, `i`/`l` -> `1`, `o` -> `0`).
|
|
10
|
+
* Returns null when the input is not a valid 6-character code.
|
|
11
|
+
*/
|
|
12
|
+
export function normalizeCode(input) {
|
|
13
|
+
const s = asciiUpper(input)
|
|
14
|
+
.replace(/[ \-]/g, "")
|
|
15
|
+
.replace(/[IL]/g, "1")
|
|
16
|
+
.replace(/O/g, "0");
|
|
17
|
+
if (s.length !== CODE_LENGTH)
|
|
18
|
+
return null;
|
|
19
|
+
for (const ch of s)
|
|
20
|
+
if (CROCKFORD_ALPHABET.indexOf(ch) < 0)
|
|
21
|
+
return null;
|
|
22
|
+
return s;
|
|
23
|
+
}
|
|
24
|
+
/** Canonical code -> `7HQ 4K2` display form. */
|
|
25
|
+
export function formatCode(code) {
|
|
26
|
+
const c = normalizeCode(code) ?? code.toUpperCase();
|
|
27
|
+
return c.length === CODE_LENGTH ? `${c.slice(0, 3)} ${c.slice(3)}` : c;
|
|
28
|
+
}
|
|
29
|
+
/** Numeric value of a Crockford string (same alias rules). Throws on invalid characters. */
|
|
30
|
+
export function decodeCrockford(input) {
|
|
31
|
+
const s = asciiUpper(input).replace(/[ \-]/g, "").replace(/[IL]/g, "1").replace(/O/g, "0");
|
|
32
|
+
let v = 0n;
|
|
33
|
+
for (const ch of s) {
|
|
34
|
+
const i = CROCKFORD_ALPHABET.indexOf(ch);
|
|
35
|
+
if (i < 0)
|
|
36
|
+
throw new Error(`invalid Crockford character '${ch}'`);
|
|
37
|
+
v = v * 32n + BigInt(i);
|
|
38
|
+
}
|
|
39
|
+
return v;
|
|
40
|
+
}
|
|
41
|
+
/** Inverse of decodeCrockford, left-padded to `length` if given. */
|
|
42
|
+
export function encodeCrockford(value, length = 0) {
|
|
43
|
+
let s = "";
|
|
44
|
+
let v = value;
|
|
45
|
+
if (v < 0n)
|
|
46
|
+
throw new Error("negative value");
|
|
47
|
+
while (v > 0n) {
|
|
48
|
+
s = CROCKFORD_ALPHABET[Number(v % 32n)] + s;
|
|
49
|
+
v /= 32n;
|
|
50
|
+
}
|
|
51
|
+
if (s === "")
|
|
52
|
+
s = "0";
|
|
53
|
+
return s.padStart(length, "0");
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=crockford.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crockford.js","sourceRoot":"","sources":["../src/crockford.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,MAAM,CAAC,MAAM,kBAAkB,GAAG,kCAAkC,CAAC;AACrE,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC;AAE7B,yDAAyD;AACzD,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;AACrD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC;SACxB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;SACrB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACtB,IAAI,CAAC,CAAC,MAAM,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IAC1C,KAAK,MAAM,EAAE,IAAI,CAAC;QAAE,IAAI,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;IACxE,OAAO,CAAC,CAAC;AACX,CAAC;AAED,gDAAgD;AAChD,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,CAAC,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;IACpD,OAAO,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AACzE,CAAC;AAED,4FAA4F;AAC5F,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,MAAM,CAAC,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC3F,IAAI,CAAC,GAAG,EAAE,CAAC;IACX,KAAK,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC;QACnB,MAAM,CAAC,GAAG,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACzC,IAAI,CAAC,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,EAAE,GAAG,CAAC,CAAC;QAClE,CAAC,GAAG,CAAC,GAAG,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,eAAe,CAAC,KAAa,EAAE,MAAM,GAAG,CAAC;IACvD,IAAI,CAAC,GAAG,EAAE,CAAC;IACX,IAAI,CAAC,GAAG,KAAK,CAAC;IACd,IAAI,CAAC,GAAG,EAAE;QAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAC9C,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC;QACd,CAAC,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QAC5C,CAAC,IAAI,GAAG,CAAC;IACX,CAAC;IACD,IAAI,CAAC,KAAK,EAAE;QAAE,CAAC,GAAG,GAAG,CAAC;IACtB,OAAO,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACjC,CAAC","sourcesContent":["/** Crockford Base32 (DESIGN s.6): digits + 22 letters, no I L O U. */\nexport const CROCKFORD_ALPHABET = \"0123456789ABCDEFGHJKMNPQRSTVWXYZ\";\nexport const CODE_LENGTH = 6;\n\n/** ASCII-only uppercasing: `ß` must not fold to `SS`. */\nfunction asciiUpper(s: string): string {\n return s.replace(/[a-z]/g, (c) => c.toUpperCase());\n}\n\n/**\n * Decode user input to the canonical code form (upper case, no separators, `i`/`l` -> `1`, `o` -> `0`).\n * Returns null when the input is not a valid 6-character code.\n */\nexport function normalizeCode(input: string): string | null {\n const s = asciiUpper(input)\n .replace(/[ \\-]/g, \"\")\n .replace(/[IL]/g, \"1\")\n .replace(/O/g, \"0\");\n if (s.length !== CODE_LENGTH) return null;\n for (const ch of s) if (CROCKFORD_ALPHABET.indexOf(ch) < 0) return null;\n return s;\n}\n\n/** Canonical code -> `7HQ 4K2` display form. */\nexport function formatCode(code: string): string {\n const c = normalizeCode(code) ?? code.toUpperCase();\n return c.length === CODE_LENGTH ? `${c.slice(0, 3)} ${c.slice(3)}` : c;\n}\n\n/** Numeric value of a Crockford string (same alias rules). Throws on invalid characters. */\nexport function decodeCrockford(input: string): bigint {\n const s = asciiUpper(input).replace(/[ \\-]/g, \"\").replace(/[IL]/g, \"1\").replace(/O/g, \"0\");\n let v = 0n;\n for (const ch of s) {\n const i = CROCKFORD_ALPHABET.indexOf(ch);\n if (i < 0) throw new Error(`invalid Crockford character '${ch}'`);\n v = v * 32n + BigInt(i);\n }\n return v;\n}\n\n/** Inverse of decodeCrockford, left-padded to `length` if given. */\nexport function encodeCrockford(value: bigint, length = 0): string {\n let s = \"\";\n let v = value;\n if (v < 0n) throw new Error(\"negative value\");\n while (v > 0n) {\n s = CROCKFORD_ALPHABET[Number(v % 32n)] + s;\n v /= 32n;\n }\n if (s === \"\") s = \"0\";\n return s.padStart(length, \"0\");\n}\n"]}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Optional zlib deflate for snapshots (CompressionStream where present; .NET ZLibStream is the matching side). */
|
|
2
|
+
export declare function deflateAvailable(): boolean;
|
|
3
|
+
export declare function deflate(bytes: Uint8Array): Promise<Uint8Array>;
|
|
4
|
+
export declare function inflate(bytes: Uint8Array): Promise<Uint8Array>;
|
package/dist/deflate.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/** Optional zlib deflate for snapshots (CompressionStream where present; .NET ZLibStream is the matching side). */
|
|
2
|
+
export function deflateAvailable() {
|
|
3
|
+
return typeof CompressionStream !== "undefined" && typeof DecompressionStream !== "undefined";
|
|
4
|
+
}
|
|
5
|
+
async function pipe(bytes, stream) {
|
|
6
|
+
const w = stream.writable.getWriter();
|
|
7
|
+
void w.write(bytes).then(() => w.close());
|
|
8
|
+
const chunks = [];
|
|
9
|
+
const r = stream.readable.getReader();
|
|
10
|
+
let total = 0;
|
|
11
|
+
for (;;) {
|
|
12
|
+
const { done, value } = await r.read();
|
|
13
|
+
if (done)
|
|
14
|
+
break;
|
|
15
|
+
chunks.push(value);
|
|
16
|
+
total += value.length;
|
|
17
|
+
}
|
|
18
|
+
const out = new Uint8Array(total);
|
|
19
|
+
let pos = 0;
|
|
20
|
+
for (const c of chunks) {
|
|
21
|
+
out.set(c, pos);
|
|
22
|
+
pos += c.length;
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
export function deflate(bytes) {
|
|
27
|
+
return pipe(bytes, new CompressionStream("deflate"));
|
|
28
|
+
}
|
|
29
|
+
export function inflate(bytes) {
|
|
30
|
+
return pipe(bytes, new DecompressionStream("deflate"));
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=deflate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deflate.js","sourceRoot":"","sources":["../src/deflate.ts"],"names":[],"mappings":"AAAA,mHAAmH;AAEnH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,OAAO,iBAAiB,KAAK,WAAW,IAAI,OAAO,mBAAmB,KAAK,WAAW,CAAC;AAChG,CAAC;AAED,KAAK,UAAU,IAAI,CAAC,KAAiB,EAAE,MAAsF;IAC3H,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC;IACtC,KAAK,CAAC,CAAC,KAAK,CAAC,KAA2C,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IAChF,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,MAAM,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,SAAS,EAAE,CAAC;IACtC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,SAAS,CAAC;QACR,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QACvC,IAAI,IAAI;YAAE,MAAM;QAChB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACnB,KAAK,IAAI,KAAK,CAAC,MAAM,CAAC;IACxB,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;IAClC,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAChB,GAAG,IAAI,CAAC,CAAC,MAAM,CAAC;IAClB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,KAAiB;IACvC,OAAO,IAAI,CAAC,KAAK,EAAE,IAAI,iBAAiB,CAAC,SAAS,CAAU,CAAC,CAAC;AAChE,CAAC;AACD,MAAM,UAAU,OAAO,CAAC,KAAiB;IACvC,OAAO,IAAI,CAAC,KAAK,EAAE,IAAI,mBAAmB,CAAC,SAAS,CAAU,CAAC,CAAC;AAClE,CAAC","sourcesContent":["/** Optional zlib deflate for snapshots (CompressionStream where present; .NET ZLibStream is the matching side). */\n\nexport function deflateAvailable(): boolean {\n return typeof CompressionStream !== \"undefined\" && typeof DecompressionStream !== \"undefined\";\n}\n\nasync function pipe(bytes: Uint8Array, stream: { readable: ReadableStream<Uint8Array>; writable: WritableStream<Uint8Array> }) {\n const w = stream.writable.getWriter();\n void w.write(bytes as unknown as Uint8Array<ArrayBuffer>).then(() => w.close());\n const chunks: Uint8Array[] = [];\n const r = stream.readable.getReader();\n let total = 0;\n for (;;) {\n const { done, value } = await r.read();\n if (done) break;\n chunks.push(value);\n total += value.length;\n }\n const out = new Uint8Array(total);\n let pos = 0;\n for (const c of chunks) {\n out.set(c, pos);\n pos += c.length;\n }\n return out;\n}\n\nexport function deflate(bytes: Uint8Array): Promise<Uint8Array> {\n return pipe(bytes, new CompressionStream(\"deflate\") as never);\n}\nexport function inflate(bytes: Uint8Array): Promise<Uint8Array> {\n return pipe(bytes, new DecompressionStream(\"deflate\") as never);\n}\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Names from before the Keepalive -> OneLobby rename. They are the same classes and types, so old code keeps
|
|
3
|
+
* working unchanged; they will be removed in the next minor release.
|
|
4
|
+
*/
|
|
5
|
+
import { OneLobby, type OneLobbyEvents, type OneLobbyOptions } from "./onelobby.js";
|
|
6
|
+
import { OneLobbyError } from "./signaling.js";
|
|
7
|
+
/** @deprecated renamed to `OneLobby`; this alias goes away in the next minor release. */
|
|
8
|
+
export declare const Keepalive: typeof OneLobby;
|
|
9
|
+
/** @deprecated renamed to `OneLobby`. */
|
|
10
|
+
export type Keepalive = OneLobby;
|
|
11
|
+
/** @deprecated renamed to `OneLobbyError`; this alias goes away in the next minor release. */
|
|
12
|
+
export declare const KeepaliveError: typeof OneLobbyError;
|
|
13
|
+
/** @deprecated renamed to `OneLobbyError`. */
|
|
14
|
+
export type KeepaliveError = OneLobbyError;
|
|
15
|
+
/** @deprecated renamed to `OneLobbyOptions`. */
|
|
16
|
+
export type KeepaliveOptions = OneLobbyOptions;
|
|
17
|
+
/** @deprecated renamed to `OneLobbyEvents`. */
|
|
18
|
+
export type KeepaliveEvents = OneLobbyEvents;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Names from before the Keepalive -> OneLobby rename. They are the same classes and types, so old code keeps
|
|
3
|
+
* working unchanged; they will be removed in the next minor release.
|
|
4
|
+
*/
|
|
5
|
+
import { OneLobby } from "./onelobby.js";
|
|
6
|
+
import { OneLobbyError } from "./signaling.js";
|
|
7
|
+
/** @deprecated renamed to `OneLobby`; this alias goes away in the next minor release. */
|
|
8
|
+
export const Keepalive = OneLobby;
|
|
9
|
+
/** @deprecated renamed to `OneLobbyError`; this alias goes away in the next minor release. */
|
|
10
|
+
export const KeepaliveError = OneLobbyError;
|
|
11
|
+
//# sourceMappingURL=deprecated.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deprecated.js","sourceRoot":"","sources":["../src/deprecated.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,QAAQ,EAA6C,MAAM,eAAe,CAAC;AACpF,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/C,yFAAyF;AACzF,MAAM,CAAC,MAAM,SAAS,GAAG,QAAQ,CAAC;AAGlC,8FAA8F;AAC9F,MAAM,CAAC,MAAM,cAAc,GAAG,aAAa,CAAC","sourcesContent":["/**\n * Names from before the Keepalive -> OneLobby rename. They are the same classes and types, so old code keeps\n * working unchanged; they will be removed in the next minor release.\n */\nimport { OneLobby, type OneLobbyEvents, type OneLobbyOptions } from \"./onelobby.js\";\nimport { OneLobbyError } from \"./signaling.js\";\n\n/** @deprecated renamed to `OneLobby`; this alias goes away in the next minor release. */\nexport const Keepalive = OneLobby;\n/** @deprecated renamed to `OneLobby`. */\nexport type Keepalive = OneLobby;\n/** @deprecated renamed to `OneLobbyError`; this alias goes away in the next minor release. */\nexport const KeepaliveError = OneLobbyError;\n/** @deprecated renamed to `OneLobbyError`. */\nexport type KeepaliveError = OneLobbyError;\n/** @deprecated renamed to `OneLobbyOptions`. */\nexport type KeepaliveOptions = OneLobbyOptions;\n/** @deprecated renamed to `OneLobbyEvents`. */\nexport type KeepaliveEvents = OneLobbyEvents;\n"]}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import type { SocketLike } from "../transport.js";
|
|
2
|
+
import { SignalingHub, type HubDeps, type HubOptions } from "./hub.js";
|
|
3
|
+
import { PortMapper, type PortMapperOptions, type PortMapResult } from "./portmap.js";
|
|
4
|
+
export declare class PortInUseError extends Error {
|
|
5
|
+
readonly code = "port_in_use";
|
|
6
|
+
constructor(port: number, detail: string);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Runs a {@link SignalingHub} inside a Node process and listens for joiners' signaling WebSockets on TCP `port`: the
|
|
10
|
+
* Node twin of C#'s `DirectHubHost` (docs/direct-ip.md s.2, s.5 step 3). A plain `net` server with a minimal HTTP/1.1
|
|
11
|
+
* upgrade and RFC 6455 framing (text, close, ping/pong, fragments; client frames must be masked), so the package keeps
|
|
12
|
+
* zero runtime dependencies. Any path upgrades; `GET /healthz` answers `ok`. The host's own client talks to the hub in
|
|
13
|
+
* memory through {@link connectLocal}. `hub.tick()` runs once a second on an unref'd timer.
|
|
14
|
+
*/
|
|
15
|
+
/** One plain-HTTP answer from the host's port, e.g. a browser join page. */
|
|
16
|
+
export interface HttpReply {
|
|
17
|
+
/** Default 200. */
|
|
18
|
+
status?: number;
|
|
19
|
+
/** Content-Type, e.g. "text/html; charset=utf-8". */
|
|
20
|
+
type: string;
|
|
21
|
+
body: string | Uint8Array;
|
|
22
|
+
}
|
|
23
|
+
/** Serves plain `GET` requests on the direct port (path without the query); null answers 404. */
|
|
24
|
+
export type HttpHandler = (path: string) => HttpReply | null;
|
|
25
|
+
export declare class DirectHubHost {
|
|
26
|
+
readonly hub: SignalingHub;
|
|
27
|
+
/**
|
|
28
|
+
* Plain `GET`s that are not WebSocket upgrades go here, so the host can serve its own join page: a browser opening
|
|
29
|
+
* `http://<host address>:<port>/` gets a page that joins with `kit.joinDirect(location.host)`. Null (default): 426.
|
|
30
|
+
*/
|
|
31
|
+
http: HttpHandler | null;
|
|
32
|
+
/** The bound TCP port (the requested one, or the one the OS picked for 0). */
|
|
33
|
+
readonly port: number;
|
|
34
|
+
portMapper: PortMapper | null;
|
|
35
|
+
private readonly server;
|
|
36
|
+
private readonly sockets;
|
|
37
|
+
private readonly ticker;
|
|
38
|
+
private readonly maxMessageBytes;
|
|
39
|
+
private closed;
|
|
40
|
+
private constructor();
|
|
41
|
+
/**
|
|
42
|
+
* Binds TCP `port` on every interface (IPv6 dual-stack where available). Hub options default to
|
|
43
|
+
* {@link directHubOptions} (one lobby, no codes, no ICE servers). Rejects with {@link PortInUseError}.
|
|
44
|
+
*/
|
|
45
|
+
static listen(port: number, options?: Partial<HubOptions>, deps?: HubDeps & {
|
|
46
|
+
host?: string;
|
|
47
|
+
}): Promise<DirectHubHost>;
|
|
48
|
+
/** Joiner sockets currently open. */
|
|
49
|
+
get connectionCount(): number;
|
|
50
|
+
/**
|
|
51
|
+
* The host's own signaling socket: a WebSocket look-alike wired straight into the hub (no network). Pass it to the
|
|
52
|
+
* kit as `advanced.socketFactory: () => host.connectLocal()`.
|
|
53
|
+
*/
|
|
54
|
+
connectLocal(): SocketLike;
|
|
55
|
+
/** Asks the home router to forward TCP and UDP `port` here (UPnP, then NAT-PMP; docs/direct-ip.md s.2a). */
|
|
56
|
+
openPort(options?: PortMapperOptions): Promise<PortMapResult>;
|
|
57
|
+
/** Stops listening, closes every joiner socket and removes the router mapping. */
|
|
58
|
+
close(): Promise<void>;
|
|
59
|
+
private accept;
|
|
60
|
+
/** RFC 6455 s.4.2: answers 101 with Sec-WebSocket-Accept, or 200 for /healthz, or 400/426. */
|
|
61
|
+
private handshake;
|
|
62
|
+
private run;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The direct-IP host's own signaling socket (C#'s `HubSignalingSocket`): sends go straight into the hub; the hub's
|
|
66
|
+
* replies are delivered on a microtask, so callbacks never run inside the hub.
|
|
67
|
+
*/
|
|
68
|
+
export declare class LocalHubSocket implements SocketLike {
|
|
69
|
+
private readonly hub;
|
|
70
|
+
/** Address the hub sees for the host itself (rate limits and kicks are per address). */
|
|
71
|
+
static readonly ADDRESS = "local";
|
|
72
|
+
readyState: number;
|
|
73
|
+
onopen: SocketLike["onopen"];
|
|
74
|
+
onmessage: SocketLike["onmessage"];
|
|
75
|
+
onclose: SocketLike["onclose"];
|
|
76
|
+
onerror: SocketLike["onerror"];
|
|
77
|
+
private session;
|
|
78
|
+
constructor(hub: SignalingHub);
|
|
79
|
+
send(data: string): void;
|
|
80
|
+
close(): void;
|
|
81
|
+
private finish;
|
|
82
|
+
}
|