twinwire 0.0.0-stage → 1.0.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.
Files changed (95) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/LICENSE +21 -0
  3. package/README.md +128 -2
  4. package/dist/browser/twinwire.js +2075 -0
  5. package/dist/browser/twinwire.js.map +7 -0
  6. package/dist/browser/twinwire.min.js +1 -0
  7. package/dist/browser/twinwire.mjs +2052 -0
  8. package/dist/browser/twinwire.mjs.map +7 -0
  9. package/dist/browser.d.ts +5 -0
  10. package/dist/browser.js +6 -0
  11. package/dist/browser.js.map +1 -0
  12. package/dist/client/client.d.ts +128 -0
  13. package/dist/client/client.js +470 -0
  14. package/dist/client/client.js.map +1 -0
  15. package/dist/client/index.d.ts +3 -0
  16. package/dist/client/index.js +4 -0
  17. package/dist/client/index.js.map +1 -0
  18. package/dist/client/lobby.d.ts +40 -0
  19. package/dist/client/lobby.js +122 -0
  20. package/dist/client/lobby.js.map +1 -0
  21. package/dist/client/rtc.d.ts +17 -0
  22. package/dist/client/rtc.js +102 -0
  23. package/dist/client/rtc.js.map +1 -0
  24. package/dist/core/budget.d.ts +27 -0
  25. package/dist/core/budget.js +39 -0
  26. package/dist/core/budget.js.map +1 -0
  27. package/dist/core/endpoint.d.ts +144 -0
  28. package/dist/core/endpoint.js +501 -0
  29. package/dist/core/endpoint.js.map +1 -0
  30. package/dist/core/lobby.d.ts +59 -0
  31. package/dist/core/lobby.js +53 -0
  32. package/dist/core/lobby.js.map +1 -0
  33. package/dist/core/protocol.d.ts +52 -0
  34. package/dist/core/protocol.js +100 -0
  35. package/dist/core/protocol.js.map +1 -0
  36. package/dist/core/reliable.d.ts +52 -0
  37. package/dist/core/reliable.js +116 -0
  38. package/dist/core/reliable.js.map +1 -0
  39. package/dist/core/rtc.d.ts +28 -0
  40. package/dist/core/rtc.js +21 -0
  41. package/dist/core/rtc.js.map +1 -0
  42. package/dist/index.d.ts +6 -0
  43. package/dist/index.js +7 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/netcode/clock.d.ts +15 -0
  46. package/dist/netcode/clock.js +37 -0
  47. package/dist/netcode/clock.js.map +1 -0
  48. package/dist/netcode/codec.d.ts +10 -0
  49. package/dist/netcode/codec.js +95 -0
  50. package/dist/netcode/codec.js.map +1 -0
  51. package/dist/netcode/index.d.ts +9 -0
  52. package/dist/netcode/index.js +10 -0
  53. package/dist/netcode/index.js.map +1 -0
  54. package/dist/netcode/inputs.d.ts +7 -0
  55. package/dist/netcode/inputs.js +43 -0
  56. package/dist/netcode/inputs.js.map +1 -0
  57. package/dist/netcode/interpolation.d.ts +44 -0
  58. package/dist/netcode/interpolation.js +101 -0
  59. package/dist/netcode/interpolation.js.map +1 -0
  60. package/dist/netcode/prediction.d.ts +40 -0
  61. package/dist/netcode/prediction.js +97 -0
  62. package/dist/netcode/prediction.js.map +1 -0
  63. package/dist/netcode/replication.d.ts +36 -0
  64. package/dist/netcode/replication.js +90 -0
  65. package/dist/netcode/replication.js.map +1 -0
  66. package/dist/netcode/snapshots.d.ts +29 -0
  67. package/dist/netcode/snapshots.js +78 -0
  68. package/dist/netcode/snapshots.js.map +1 -0
  69. package/dist/netcode/spatial.d.ts +16 -0
  70. package/dist/netcode/spatial.js +63 -0
  71. package/dist/netcode/spatial.js.map +1 -0
  72. package/dist/netcode/timing.d.ts +37 -0
  73. package/dist/netcode/timing.js +41 -0
  74. package/dist/netcode/timing.js.map +1 -0
  75. package/dist/server/index.d.ts +4 -0
  76. package/dist/server/index.js +5 -0
  77. package/dist/server/index.js.map +1 -0
  78. package/dist/server/lobby.d.ts +134 -0
  79. package/dist/server/lobby.js +462 -0
  80. package/dist/server/lobby.js.map +1 -0
  81. package/dist/server/rtc.d.ts +38 -0
  82. package/dist/server/rtc.js +102 -0
  83. package/dist/server/rtc.js.map +1 -0
  84. package/dist/server/server.d.ts +145 -0
  85. package/dist/server/server.js +457 -0
  86. package/dist/server/server.js.map +1 -0
  87. package/dist/server/turn.d.ts +22 -0
  88. package/dist/server/turn.js +52 -0
  89. package/dist/server/turn.js.map +1 -0
  90. package/docs/ADOPTING.md +64 -0
  91. package/docs/API.md +181 -0
  92. package/docs/ARCHITECTURE.md +101 -0
  93. package/docs/OPERATIONS.md +93 -0
  94. package/docs/RELEASE-CHECKLIST.md +25 -0
  95. package/package.json +100 -4
@@ -0,0 +1,122 @@
1
+ import { LOBBY_KEY, isLobbyMessage, normalizeCode, } from '../core/lobby.js';
2
+ /**
3
+ * Rooms on top of a GameClient: create a private room with a code, join by code, quick play into a public room,
4
+ * ready up, and follow who is in the room. Requests ride on reliable events, so they survive reconnects.
5
+ */
6
+ export class LobbyClient {
7
+ client;
8
+ requestTimeoutMs;
9
+ room = null;
10
+ onRoom = () => { };
11
+ /** The room was left without asking (kicked, replaced, closed, session expired). */
12
+ onLeft = () => { };
13
+ pending = new Map();
14
+ nextId = 1;
15
+ off;
16
+ constructor(client, requestTimeoutMs = 20000) {
17
+ this.client = client;
18
+ this.requestTimeoutMs = requestTimeoutMs;
19
+ this.off = [
20
+ client.intercept(d => {
21
+ if (!isLobbyMessage(d))
22
+ return false;
23
+ this.message(d);
24
+ return true;
25
+ }),
26
+ client.on('session', ({ resumed }) => { if (!resumed)
27
+ this.drop('new session'); }),
28
+ client.on('lost', ({ reason }) => this.drop(reason)),
29
+ ];
30
+ }
31
+ dispose() { for (const f of this.off)
32
+ f(); this.drop('disposed', false); }
33
+ get me() { return this.room?.members.find(m => m.seat === this.room.you) ?? null; }
34
+ get isHost() { return !!this.me?.host; }
35
+ /** Milliseconds until the ready countdown ends, or null when none is running. */
36
+ countdownMs() {
37
+ const end = this.room?.countdownEndsAt;
38
+ if (end == null || !this.client.clock.synced)
39
+ return null;
40
+ return Math.max(0, end - this.client.clock.serverTime(performance.now()));
41
+ }
42
+ create(o = {}) { return this.request({ [LOBBY_KEY]: 'create', ...o }); }
43
+ join(code, o = {}) {
44
+ const c = normalizeCode(code);
45
+ if (!c)
46
+ return Promise.reject(new Error('That is not a room code'));
47
+ return this.request({ [LOBBY_KEY]: 'join', code: c, ...o });
48
+ }
49
+ quickPlay(o = {}) { return this.request({ [LOBBY_KEY]: 'quick', ...o }); }
50
+ async leave() { await this.request({ [LOBBY_KEY]: 'leave' }); this.set(null); }
51
+ setReady(ready) { return this.request({ [LOBBY_KEY]: 'ready', ready }); }
52
+ setName(name) { return this.request({ [LOBBY_KEY]: 'name', name }); }
53
+ setMeta(meta) { return this.request({ [LOBBY_KEY]: 'meta', meta }); }
54
+ kick(seat) { return this.request({ [LOBBY_KEY]: 'kick', seat }); }
55
+ request(body) {
56
+ const id = this.nextId++;
57
+ return new Promise((resolve, reject) => {
58
+ const timer = setTimeout(() => { this.pending.delete(id); reject(new Error('The room server did not answer')); }, this.requestTimeoutMs);
59
+ this.pending.set(id, { resolve, reject, timer });
60
+ try {
61
+ this.client.sendEvent({ ...body, id });
62
+ }
63
+ catch (e) {
64
+ clearTimeout(timer);
65
+ this.pending.delete(id);
66
+ reject(e instanceof Error ? e : new Error(String(e)));
67
+ }
68
+ });
69
+ }
70
+ set(room) { this.room = room; try {
71
+ this.onRoom(room);
72
+ }
73
+ catch { /* app callback */ } }
74
+ message(d) {
75
+ const op = d[LOBBY_KEY];
76
+ if (op === 'res' && typeof d.id === 'number') {
77
+ const p = this.pending.get(d.id);
78
+ if (!p)
79
+ return;
80
+ this.pending.delete(d.id);
81
+ clearTimeout(p.timer);
82
+ if (d.ok === true) {
83
+ const room = (d.room ?? null);
84
+ if (room || this.room)
85
+ this.set(room);
86
+ p.resolve(room);
87
+ }
88
+ else
89
+ p.reject(new Error(typeof d.error === 'string' ? d.error : 'Request refused'));
90
+ return;
91
+ }
92
+ if (op === 'room' && d.room && typeof d.room === 'object') {
93
+ this.set(d.room);
94
+ return;
95
+ }
96
+ if (op === 'left') {
97
+ const had = !!this.room;
98
+ this.set(null);
99
+ if (had)
100
+ try {
101
+ this.onLeft(typeof d.reason === 'string' ? d.reason : 'left');
102
+ }
103
+ catch { /* app */ }
104
+ }
105
+ }
106
+ drop(reason, notify = true) {
107
+ for (const p of this.pending.values()) {
108
+ clearTimeout(p.timer);
109
+ p.reject(new Error(reason));
110
+ }
111
+ this.pending.clear();
112
+ if (this.room) {
113
+ this.set(null);
114
+ if (notify)
115
+ try {
116
+ this.onLeft(reason);
117
+ }
118
+ catch { /* app */ }
119
+ }
120
+ }
121
+ }
122
+ //# sourceMappingURL=lobby.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lobby.js","sourceRoot":"","sources":["../../src/client/lobby.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { type RtcCallbacks, type RtcPeer, type RtcSignal } from '../core/rtc.js';
2
+ /** Browser adapter. Candidates are buffered until the remote description has finished applying. */
3
+ export declare class BrowserRtcPeer implements RtcPeer {
4
+ private cb;
5
+ private pc;
6
+ private dc;
7
+ private alive;
8
+ private offered;
9
+ private remoteReady;
10
+ private localCandidates;
11
+ private remoteCandidates;
12
+ private chain;
13
+ constructor(cb: RtcCallbacks, config?: RTCConfiguration);
14
+ offer(): Promise<void>;
15
+ signal(message: RtcSignal): Promise<void>;
16
+ close(): void;
17
+ }
@@ -0,0 +1,102 @@
1
+ import { validSignal } from '../core/rtc.js';
2
+ /** Browser adapter. Candidates are buffered until the remote description has finished applying. */
3
+ export class BrowserRtcPeer {
4
+ cb;
5
+ pc;
6
+ dc;
7
+ alive = true;
8
+ offered = false;
9
+ remoteReady = false;
10
+ localCandidates = [];
11
+ remoteCandidates = [];
12
+ chain = Promise.resolve();
13
+ constructor(cb, config = { iceServers: [] }) {
14
+ this.cb = cb;
15
+ this.pc = new RTCPeerConnection(config);
16
+ this.dc = this.pc.createDataChannel('twinwire', { negotiated: true, id: 0, ordered: false, maxRetransmits: 0 });
17
+ const pc = this.pc, dc = this.dc;
18
+ dc.binaryType = 'arraybuffer';
19
+ dc.onopen = () => {
20
+ if (this.alive)
21
+ cb.open({
22
+ get open() { return dc.readyState === 'open'; }, get bufferedAmount() { return dc.bufferedAmount; },
23
+ send(bytes) { if (dc.readyState !== 'open')
24
+ return false; dc.send(bytes.slice().buffer); return true; },
25
+ close: () => this.close(),
26
+ });
27
+ };
28
+ dc.onmessage = e => { if (this.alive && e.data instanceof ArrayBuffer)
29
+ cb.data(new Uint8Array(e.data)); };
30
+ dc.onclose = () => { if (this.alive) {
31
+ this.close();
32
+ cb.closed();
33
+ } };
34
+ dc.onerror = () => { if (this.alive)
35
+ cb.error(new Error('RTC data channel error')); };
36
+ pc.onconnectionstatechange = () => { if (this.alive && pc.connectionState === 'failed') {
37
+ this.close();
38
+ cb.closed();
39
+ } };
40
+ pc.onicecandidate = e => {
41
+ if (!this.alive || !e.candidate?.candidate)
42
+ return;
43
+ const m = { kind: 'candidate', candidate: e.candidate.candidate, mid: e.candidate.sdpMid ?? '0' };
44
+ if (this.offered)
45
+ cb.signal(m);
46
+ else if (this.localCandidates.length < 64)
47
+ this.localCandidates.push(m);
48
+ };
49
+ }
50
+ async offer() {
51
+ await this.pc.setLocalDescription(await this.pc.createOffer());
52
+ if (!this.alive)
53
+ return;
54
+ this.cb.signal({ kind: 'offer', sdp: this.pc.localDescription.sdp });
55
+ this.offered = true;
56
+ for (const c of this.localCandidates)
57
+ this.cb.signal(c);
58
+ this.localCandidates = [];
59
+ }
60
+ signal(message) {
61
+ this.chain = this.chain.then(async () => {
62
+ if (!this.alive || !validSignal(message))
63
+ return;
64
+ if (message.kind === 'answer') {
65
+ await this.pc.setRemoteDescription({ type: 'answer', sdp: message.sdp });
66
+ if (!this.alive)
67
+ return;
68
+ this.remoteReady = true;
69
+ for (const c of this.remoteCandidates)
70
+ await this.pc.addIceCandidate(c);
71
+ this.remoteCandidates = [];
72
+ }
73
+ else if (message.kind === 'candidate') {
74
+ const c = { candidate: message.candidate, sdpMid: message.mid };
75
+ if (this.remoteReady)
76
+ await this.pc.addIceCandidate(c);
77
+ else {
78
+ if (this.remoteCandidates.length >= 64)
79
+ throw new Error('Too many ICE candidates');
80
+ this.remoteCandidates.push(c);
81
+ }
82
+ }
83
+ }).catch(e => { if (this.alive)
84
+ this.cb.error(e instanceof Error ? e : new Error(String(e))); });
85
+ return this.chain;
86
+ }
87
+ close() {
88
+ if (!this.alive)
89
+ return;
90
+ this.alive = false;
91
+ this.localCandidates = [];
92
+ this.remoteCandidates = [];
93
+ this.dc.onopen = this.dc.onmessage = this.dc.onclose = this.dc.onerror = null;
94
+ this.pc.onicecandidate = this.pc.onconnectionstatechange = null;
95
+ try {
96
+ this.dc.close();
97
+ this.pc.close();
98
+ }
99
+ catch { /* no-op after disposal */ }
100
+ }
101
+ }
102
+ //# sourceMappingURL=rtc.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rtc.js","sourceRoot":"","sources":["../../src/client/rtc.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ export declare class TokenBucket {
2
+ readonly rate: number;
3
+ readonly capacity: number;
4
+ private tokens;
5
+ private at;
6
+ constructor(rate: number, capacity: number, now: number);
7
+ available(now: number): number;
8
+ /** A message larger than the burst may go once the bucket is full; the debt then delays what follows. */
9
+ take(bytes: number, now: number): boolean;
10
+ }
11
+ export interface EgressEstimate {
12
+ payloadGB: number;
13
+ plannedGB: number;
14
+ bill: number;
15
+ withinBudget: boolean;
16
+ }
17
+ /** Planning estimate, not a bill or a hard cap. Provider prices/allowances are supplied by the caller. */
18
+ export declare function estimateEgress(o: {
19
+ bytesPerSecondPerPlayer: number;
20
+ averagePlayers: number;
21
+ activeHours: number;
22
+ baseMonthlyUSD: number;
23
+ includedGB: number;
24
+ overageUSDPerGB: number;
25
+ budgetUSD?: number;
26
+ overheadFraction?: number;
27
+ }): EgressEstimate;
@@ -0,0 +1,39 @@
1
+ export class TokenBucket {
2
+ rate;
3
+ capacity;
4
+ tokens;
5
+ at;
6
+ constructor(rate, capacity, now) {
7
+ this.rate = rate;
8
+ this.capacity = capacity;
9
+ if (!(rate > 0) || !Number.isFinite(rate) || !(capacity > 0) || !Number.isFinite(capacity))
10
+ throw new RangeError('Invalid budget');
11
+ this.tokens = capacity;
12
+ this.at = now;
13
+ }
14
+ available(now) {
15
+ this.tokens = Math.min(this.capacity, this.tokens + Math.max(0, now - this.at) * this.rate / 1000);
16
+ this.at = Math.max(this.at, now);
17
+ return this.tokens;
18
+ }
19
+ /** A message larger than the burst may go once the bucket is full; the debt then delays what follows. */
20
+ take(bytes, now) {
21
+ if (!Number.isFinite(bytes) || bytes < 0)
22
+ throw new RangeError('Invalid byte count');
23
+ if (this.available(now) < Math.min(bytes, this.capacity))
24
+ return false;
25
+ this.tokens -= bytes;
26
+ return true;
27
+ }
28
+ }
29
+ /** Planning estimate, not a bill or a hard cap. Provider prices/allowances are supplied by the caller. */
30
+ export function estimateEgress(o) {
31
+ for (const v of Object.values(o))
32
+ if (!Number.isFinite(v) || v < 0)
33
+ throw new RangeError('Invalid cost input');
34
+ const payloadGB = o.bytesPerSecondPerPlayer * o.averagePlayers * o.activeHours * 3600 / 1e9;
35
+ const plannedGB = payloadGB * (1 + (o.overheadFraction ?? 0.2));
36
+ const bill = o.baseMonthlyUSD + Math.max(0, plannedGB - o.includedGB) * o.overageUSDPerGB;
37
+ return { payloadGB, plannedGB, bill, withinBudget: bill <= (o.budgetUSD ?? 5) };
38
+ }
39
+ //# sourceMappingURL=budget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"budget.js","sourceRoot":"","sources":["../../src/core/budget.ts"],"names":[],"mappings":"AAAA,MAAM,OAAO,WAAW;IAGD;IAAuB;IAFpC,MAAM,CAAS;IACf,EAAE,CAAS;IACnB,YAAqB,IAAY,EAAW,QAAgB,EAAE,GAAW;QAApD,SAAI,GAAJ,IAAI,CAAQ;QAAW,aAAQ,GAAR,QAAQ,CAAQ;QAC1D,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;YAAE,MAAM,IAAI,UAAU,CAAC,gBAAgB,CAAC,CAAC;QACnI,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC;QAAC,IAAI,CAAC,EAAE,GAAG,GAAG,CAAC;IACxC,CAAC;IACD,SAAS,CAAC,GAAW;QACnB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;QACnG,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QAAC,OAAO,IAAI,CAAC,MAAM,CAAC;IACvD,CAAC;IACD,yGAAyG;IACzG,IAAI,CAAC,KAAa,EAAE,GAAW;QAC7B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC;YAAE,MAAM,IAAI,UAAU,CAAC,oBAAoB,CAAC,CAAC;QACrF,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC;YAAE,OAAO,KAAK,CAAC;QACvE,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC;QAAC,OAAO,IAAI,CAAC;IACpC,CAAC;CACF;AAED,0GAA0G;AAC1G,MAAM,UAAU,cAAc,CAAC,CAG9B;IACC,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,MAAM,IAAI,UAAU,CAAC,oBAAoB,CAAC,CAAC;IAC/G,MAAM,SAAS,GAAG,CAAC,CAAC,uBAAuB,GAAG,CAAC,CAAC,cAAc,GAAG,CAAC,CAAC,WAAW,GAAG,IAAI,GAAG,GAAG,CAAC;IAC5F,MAAM,SAAS,GAAG,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,gBAAgB,IAAI,GAAG,CAAC,CAAC,CAAC;IAChE,MAAM,IAAI,GAAG,CAAC,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,eAAe,CAAC;IAC1F,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,EAAE,CAAC;AAClF,CAAC"}
@@ -0,0 +1,144 @@
1
+ import { ReliableEvents, type ReliableOptions } from './reliable.js';
2
+ import { type Json } from './protocol.js';
3
+ export interface ReliableLink {
4
+ readonly open: boolean;
5
+ readonly bufferedAmount: number;
6
+ send(data: string | Uint8Array): boolean;
7
+ close(): void;
8
+ }
9
+ export interface DatagramLink {
10
+ readonly open: boolean;
11
+ readonly bufferedAmount: number;
12
+ send(data: Uint8Array): boolean;
13
+ close(): void;
14
+ }
15
+ export interface StateMessage {
16
+ stream: number;
17
+ seq: number;
18
+ time: number;
19
+ data: Uint8Array;
20
+ via: 'ws' | 'rtc';
21
+ }
22
+ /** Simulated network trouble on this endpoint's outgoing traffic (tests and tuning). lagMs is one way. */
23
+ export interface Impairment {
24
+ lagMs?: number;
25
+ jitterMs?: number;
26
+ loss?: number;
27
+ duplicate?: number;
28
+ }
29
+ export interface StateOptions {
30
+ ttlMs?: number;
31
+ priority?: number;
32
+ ack?: boolean;
33
+ }
34
+ export interface EndpointOptions {
35
+ now?: () => number;
36
+ bytesPerSecond?: number;
37
+ burstBytes?: number;
38
+ maxBufferedBytes?: number;
39
+ maxStreams?: number;
40
+ probeIntervalMs?: number;
41
+ probeTimeoutMs?: number;
42
+ /** Largest state payload accepted (default 64 KiB, at most 256 KiB). */
43
+ maxStateBytes?: number;
44
+ /** States larger than this use the WebSocket even when datagrams are up (default 8 KiB). */
45
+ datagramStateBytes?: number;
46
+ /** Mirror reliable events on the datagram path so they arrive at UDP latency (default true). */
47
+ datagramEvents?: boolean;
48
+ /** How often state acknowledgements go back to the sender (default 50 ms). */
49
+ stateAckIntervalMs?: number;
50
+ events?: ReliableOptions;
51
+ impair?: Impairment;
52
+ }
53
+ export declare class Endpoint {
54
+ readonly events: ReliableEvents;
55
+ readonly stats: {
56
+ bytesSent: number;
57
+ bytesReceived: number;
58
+ statesSent: number;
59
+ statesReceived: number;
60
+ stale: number;
61
+ expired: number;
62
+ coalesced: number;
63
+ congested: number;
64
+ invalid: number;
65
+ fastTransitions: number;
66
+ datagramRttMs: number;
67
+ eventsViaDatagram: number;
68
+ datagramEventFrames: number;
69
+ stateAcksSent: number;
70
+ stateAcksReceived: number;
71
+ impairedDrops: number;
72
+ };
73
+ onState: (state: StateMessage) => void;
74
+ onEvent: (data: Json, seq: number) => void;
75
+ onFastDown: () => void;
76
+ onFastChange: (fast: boolean) => void;
77
+ onEventAck: (seq: number) => void;
78
+ /** The peer has seen state `seq` on `stream` (only for states sent with `ack: true`). */
79
+ onStateAck: (stream: number, seq: number) => void;
80
+ private reliable?;
81
+ private datagram?;
82
+ private epoch;
83
+ private sequence;
84
+ private received;
85
+ private queued;
86
+ private acksOut;
87
+ private acksIn;
88
+ private lastAckSentAt;
89
+ private lastEventSent;
90
+ private datagramAcked;
91
+ private ackDue;
92
+ private lastProbe;
93
+ private proofAt;
94
+ private attachedAt;
95
+ private probes;
96
+ private probeSeq;
97
+ private fast;
98
+ readonly now: () => number;
99
+ private budget;
100
+ private highWater;
101
+ private maxStreams;
102
+ private probeInterval;
103
+ private probeTimeout;
104
+ readonly maxStateBytes: number;
105
+ private datagramStateBytes;
106
+ private datagramEvents;
107
+ private stateAckInterval;
108
+ private impairment?;
109
+ constructor(o?: EndpointOptions);
110
+ get active(): boolean;
111
+ get fastActive(): boolean;
112
+ get pendingStates(): number;
113
+ get connectionEpoch(): number;
114
+ get maxFrameBytes(): number;
115
+ /** Newest seq the peer reported for an acknowledged stream in this connection epoch. */
116
+ stateAck(stream: number): number | undefined;
117
+ setImpairment(i: Impairment | null): void;
118
+ get impaired(): Impairment | undefined;
119
+ attach(epoch: number, reliable: ReliableLink): void;
120
+ detach(): void;
121
+ attachDatagram(link: DatagramLink): void;
122
+ dropDatagram(notify?: boolean): void;
123
+ private setFast;
124
+ sendControl(m: unknown): boolean;
125
+ sendEvent(data: Json): number;
126
+ /** Retains only the newest unsent payload per stream. Returns the state's seq (0 when offline).
127
+ * An accepted enqueue is not a delivery guarantee; use `ack: true` and onStateAck to learn what arrived. */
128
+ sendState(stream: number, data: Uint8Array, time: number, o?: StateOptions): number;
129
+ receiveControl(m: Record<string, unknown>): boolean;
130
+ receiveBinary(bytes: Uint8Array, via: 'ws' | 'rtc'): void;
131
+ private receiveDatagramEvents;
132
+ private receiveStateAcks;
133
+ private sendFast;
134
+ /** Binary on the datagram path when it is proven, else the WebSocket. */
135
+ private sendBinary;
136
+ pump(): void;
137
+ flush(maxPackets?: number): void;
138
+ /** Copies of WebSocket-sent, unacknowledged events, retried on the datagram path for UDP-like latency. */
139
+ private flushDatagramEvents;
140
+ private flushStateAcks;
141
+ /** Delays a stream-like link without ever reordering it: one queue, one timer. */
142
+ private impairReliable;
143
+ private impairDatagram;
144
+ }