@vielzeug/mesh 26.10.15 → 26.10.16

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 (50) hide show
  1. package/dist/_base45.cjs.map +1 -1
  2. package/dist/_base45.js +37 -1
  3. package/dist/_base45.js.map +1 -1
  4. package/dist/_defaults.cjs.map +1 -1
  5. package/dist/_defaults.js +5 -1
  6. package/dist/_defaults.js.map +1 -1
  7. package/dist/_node.cjs.map +1 -1
  8. package/dist/_node.js +166 -1
  9. package/dist/_node.js.map +1 -1
  10. package/dist/_proof.cjs.map +1 -1
  11. package/dist/_proof.js +16 -1
  12. package/dist/_proof.js.map +1 -1
  13. package/dist/_random.cjs.map +1 -1
  14. package/dist/_random.js +14 -1
  15. package/dist/_random.js.map +1 -1
  16. package/dist/_rtc.cjs.map +1 -1
  17. package/dist/_rtc.js +41 -3
  18. package/dist/_rtc.js.map +1 -1
  19. package/dist/_sdp.cjs.map +1 -1
  20. package/dist/_sdp.js +54 -2
  21. package/dist/_sdp.js.map +1 -1
  22. package/dist/_wire.cjs.map +1 -1
  23. package/dist/_wire.js +20 -1
  24. package/dist/_wire.js.map +1 -1
  25. package/dist/codec.cjs.map +1 -1
  26. package/dist/codec.js +34 -1
  27. package/dist/codec.js.map +1 -1
  28. package/dist/errors.cjs.map +1 -1
  29. package/dist/errors.js +18 -1
  30. package/dist/errors.js.map +1 -1
  31. package/dist/guest.cjs.map +1 -1
  32. package/dist/guest.js +153 -1
  33. package/dist/guest.js.map +1 -1
  34. package/dist/host.cjs.map +1 -1
  35. package/dist/host.js +202 -1
  36. package/dist/host.js.map +1 -1
  37. package/dist/index.js +6 -1
  38. package/dist/mesh.cjs.map +1 -1
  39. package/dist/mesh.iife.js.map +1 -1
  40. package/dist/mesh.js +756 -4
  41. package/dist/mesh.js.map +1 -1
  42. package/dist/qr-codec.cjs.map +1 -1
  43. package/dist/qr-codec.js +52 -1
  44. package/dist/qr-codec.js.map +1 -1
  45. package/dist/testing/fake-rtc.cjs +1 -1
  46. package/dist/testing/fake-rtc.cjs.map +1 -1
  47. package/dist/testing/fake-rtc.js +153 -1
  48. package/dist/testing/fake-rtc.js.map +1 -1
  49. package/dist/testing.js +1 -1
  50. package/package.json +5 -5
@@ -1,2 +1,154 @@
1
- var ListenerBag=class{listeners=new Map;add(e,t){let n=this.listeners.get(e);n||(n=new Set,this.listeners.set(e,n)),n.add(t)}remove(e,t){this.listeners.get(e)?.delete(t)}emit(e,t={}){for(let n of this.listeners.get(e)??[])n(t)}addEventListener(e,t){this.add(e,t)}removeEventListener(e,t){this.remove(e,t)}};function createFakeRtc(e={}){let t=[],n=new Map,r=[],i=0,a=0,o=!1,s=!1,c=!1,l={closeChannel(e){let t=[...n.values()][e];t?.hostChannel?.close(),t?.guestChannel?.close(),t&&(t.hostPc.iceConnectionState=`disconnected`,t.hostPc.emit(`iceconnectionstatechange`),t.guestPc&&(t.guestPc.iceConnectionState=`disconnected`,t.guestPc.emit(`iceconnectionstatechange`)))},dropNext(e){a+=e},failIce(){o=!0},hangOpen(){c=!0},latencyMs:e.latencyMs??0,get linkCount(){return n.size},get remoteSdps(){return r},rtc:{createPeerConnection:()=>new FakePeerConnection(`pc${++i}`)},stallIce(){s=!0},tick(){for(;t.length;)t.shift()?.()}};function schedule(e){l.latencyMs>0?(t.push(e),setTimeout(l.tick,l.latencyMs)):queueMicrotask(e)}class FakeDataChannel extends ListenerBag{readyState=`connecting`;remote=null;send(e){if(this.readyState!==`open`)throw Error(`Channel is ${this.readyState}`);if(a>0){a--;return}let t=this.remote;t&&t.readyState===`open`&&schedule(()=>t.emit(`message`,{data:e}))}open(){this.readyState===`connecting`&&(this.readyState=`open`,this.emit(`open`))}close(){if(this.readyState===`closed`)return;this.readyState=`closed`,this.emit(`close`);let e=this.remote;e&&e.readyState!==`closed`&&(e.readyState=`closed`,e.emit(`close`))}}class FakePeerConnection extends ListenerBag{connectionState=`new`;iceConnectionState=`new`;iceGatheringState=`new`;localDescription=null;remoteDescription=null;pendingChannel=null;closed=!1;token;constructor(e){super(),this.token=e}createDataChannel(){return this.pendingChannel=new FakeDataChannel,this.pendingChannel}createOffer(){return n.set(this.token,{completed:!1,guestChannel:null,guestPc:null,hostChannel:null,hostPc:this,hostToken:this.token}),Promise.resolve({sdp:`fake-offer:${this.token}`,type:`offer`})}createAnswer(){let e=this.linkFor();return Promise.resolve({sdp:`fake-answer:${e?.hostToken??`none`}:guest:${this.token}`,type:`answer`})}setLocalDescription(e){return this.localDescription=e,s?(s=!1,this.iceGatheringState=`gathering`):this.iceGatheringState=`complete`,this.emit(`icegatheringstatechange`),Promise.resolve()}setRemoteDescription(e){if(this.remoteDescription=e,r.push(e.sdp??``),e.type===`offer`){let t=/fake-offer:(\w+)/.exec(e.sdp??``)?.[1],r=t?n.get(t):void 0;return r?(r.guestPc=this,Promise.resolve()):Promise.reject(Error(`Unknown offer`))}let t=/fake-answer:(\w+):guest:(\w+)/.exec(e.sdp??``),i=t?.[1]?n.get(t[1]):void 0;return!i||i.guestPc?.token!==t?.[2]?Promise.reject(Error(`Unknown answer`)):(this.completeLink(i),Promise.resolve())}close(){if(this.closed)return;this.closed=!0,this.connectionState=`closed`,this.iceConnectionState=`closed`,this.emit(`iceconnectionstatechange`),this.emit(`connectionstatechange`),this.pendingChannel?.close();let e=this.linkFor();if(e){e.guestChannel?.close(),e.hostChannel?.close();let t=e.guestPc===this?e.hostPc:e.guestPc;t&&!t.closed&&(t.iceConnectionState=`disconnected`,t.emit(`iceconnectionstatechange`))}}linkFor(){return[...n.values()].find(e=>e.hostPc===this||e.guestPc===this)}completeLink(e){if(e.completed)return;if(e.completed=!0,o){o=!1;for(let t of[e.hostPc,e.guestPc])t&&(t.iceConnectionState=`failed`,t.emit(`iceconnectionstatechange`));return}let t=e.hostPc.pendingChannel??new FakeDataChannel,n=new FakeDataChannel;t.remote=n,n.remote=t,e.hostChannel=t,e.guestChannel=n,e.guestPc?.emit(`datachannel`,{channel:n}),schedule(()=>{for(let t of[e.hostPc,e.guestPc])t&&!t.closed&&(t.iceConnectionState=`connected`,t.connectionState=`connected`,t.emit(`iceconnectionstatechange`),t.emit(`connectionstatechange`));if(c){c=!1;return}t.open(),n.open()})}}return l}export{createFakeRtc};
1
+ //#region src/testing/fake-rtc.ts
2
+ var ListenerBag = class {
3
+ listeners = /* @__PURE__ */ new Map();
4
+ add(e, t) {
5
+ let n = this.listeners.get(e);
6
+ n || (n = /* @__PURE__ */ new Set(), this.listeners.set(e, n)), n.add(t);
7
+ }
8
+ remove(e, t) {
9
+ this.listeners.get(e)?.delete(t);
10
+ }
11
+ emit(e, t = {}) {
12
+ for (let n of this.listeners.get(e) ?? []) n(t);
13
+ }
14
+ addEventListener(e, t) {
15
+ this.add(e, t);
16
+ }
17
+ removeEventListener(e, t) {
18
+ this.remove(e, t);
19
+ }
20
+ };
21
+ function createFakeRtc(e = {}) {
22
+ let t = [], n = /* @__PURE__ */ new Map(), r = [], i = 0, a = 0, o = !1, s = !1, c = !1, l = {
23
+ closeChannel(e) {
24
+ let t = [...n.values()][e];
25
+ t?.hostChannel?.close(), t?.guestChannel?.close(), t && (t.hostPc.iceConnectionState = "disconnected", t.hostPc.emit("iceconnectionstatechange"), t.guestPc && (t.guestPc.iceConnectionState = "disconnected", t.guestPc.emit("iceconnectionstatechange")));
26
+ },
27
+ dropNext(e) {
28
+ a += e;
29
+ },
30
+ failIce() {
31
+ o = !0;
32
+ },
33
+ hangOpen() {
34
+ c = !0;
35
+ },
36
+ latencyMs: e.latencyMs ?? 0,
37
+ get linkCount() {
38
+ return n.size;
39
+ },
40
+ get remoteSdps() {
41
+ return r;
42
+ },
43
+ rtc: { createPeerConnection: () => new FakePeerConnection(`pc${++i}`) },
44
+ stallIce() {
45
+ s = !0;
46
+ },
47
+ tick() {
48
+ for (; t.length;) t.shift()?.();
49
+ }
50
+ };
51
+ function schedule(e) {
52
+ l.latencyMs > 0 ? (t.push(e), setTimeout(l.tick, l.latencyMs)) : queueMicrotask(e);
53
+ }
54
+ class FakeDataChannel extends ListenerBag {
55
+ readyState = "connecting";
56
+ remote = null;
57
+ send(e) {
58
+ if (this.readyState !== "open") throw Error(`Channel is ${this.readyState}`);
59
+ if (a > 0) {
60
+ a--;
61
+ return;
62
+ }
63
+ let t = this.remote;
64
+ t && t.readyState === "open" && schedule(() => t.emit("message", { data: e }));
65
+ }
66
+ open() {
67
+ this.readyState === "connecting" && (this.readyState = "open", this.emit("open"));
68
+ }
69
+ close() {
70
+ if (this.readyState === "closed") return;
71
+ this.readyState = "closed", this.emit("close");
72
+ let e = this.remote;
73
+ e && e.readyState !== "closed" && (e.readyState = "closed", e.emit("close"));
74
+ }
75
+ }
76
+ class FakePeerConnection extends ListenerBag {
77
+ connectionState = "new";
78
+ iceConnectionState = "new";
79
+ iceGatheringState = "new";
80
+ localDescription = null;
81
+ remoteDescription = null;
82
+ pendingChannel = null;
83
+ closed = !1;
84
+ token;
85
+ constructor(e) {
86
+ super(), this.token = e;
87
+ }
88
+ createDataChannel() {
89
+ return this.pendingChannel = new FakeDataChannel(), this.pendingChannel;
90
+ }
91
+ createOffer() {
92
+ return n.set(this.token, {
93
+ completed: !1,
94
+ guestChannel: null,
95
+ guestPc: null,
96
+ hostChannel: null,
97
+ hostPc: this,
98
+ hostToken: this.token
99
+ }), Promise.resolve({
100
+ sdp: `fake-offer:${this.token}`,
101
+ type: "offer"
102
+ });
103
+ }
104
+ createAnswer() {
105
+ let e = this.linkFor();
106
+ return Promise.resolve({
107
+ sdp: `fake-answer:${e?.hostToken ?? "none"}:guest:${this.token}`,
108
+ type: "answer"
109
+ });
110
+ }
111
+ setLocalDescription(e) {
112
+ return this.localDescription = e, s ? (s = !1, this.iceGatheringState = "gathering") : this.iceGatheringState = "complete", this.emit("icegatheringstatechange"), Promise.resolve();
113
+ }
114
+ setRemoteDescription(e) {
115
+ if (this.remoteDescription = e, r.push(e.sdp ?? ""), e.type === "offer") {
116
+ let t = /fake-offer:(\w+)/.exec(e.sdp ?? "")?.[1], r = t ? n.get(t) : void 0;
117
+ return r ? (r.guestPc = this, Promise.resolve()) : Promise.reject(/* @__PURE__ */ Error("Unknown offer"));
118
+ }
119
+ let t = /fake-answer:(\w+):guest:(\w+)/.exec(e.sdp ?? ""), i = t?.[1] ? n.get(t[1]) : void 0;
120
+ return !i || i.guestPc?.token !== t?.[2] ? Promise.reject(/* @__PURE__ */ Error("Unknown answer")) : (this.completeLink(i), Promise.resolve());
121
+ }
122
+ close() {
123
+ if (this.closed) return;
124
+ this.closed = !0, this.connectionState = "closed", this.iceConnectionState = "closed", this.emit("iceconnectionstatechange"), this.emit("connectionstatechange"), this.pendingChannel?.close();
125
+ let e = this.linkFor();
126
+ if (e) {
127
+ e.guestChannel?.close(), e.hostChannel?.close();
128
+ let t = e.guestPc === this ? e.hostPc : e.guestPc;
129
+ t && !t.closed && (t.iceConnectionState = "disconnected", t.emit("iceconnectionstatechange"));
130
+ }
131
+ }
132
+ linkFor() {
133
+ return [...n.values()].find((e) => e.hostPc === this || e.guestPc === this);
134
+ }
135
+ completeLink(e) {
136
+ if (e.completed) return;
137
+ if (e.completed = !0, o) {
138
+ o = !1;
139
+ for (let t of [e.hostPc, e.guestPc]) t && (t.iceConnectionState = "failed", t.emit("iceconnectionstatechange"));
140
+ return;
141
+ }
142
+ let t = e.hostPc.pendingChannel ?? new FakeDataChannel(), n = new FakeDataChannel();
143
+ t.remote = n, n.remote = t, e.hostChannel = t, e.guestChannel = n, e.guestPc?.emit("datachannel", { channel: n }), schedule(() => {
144
+ for (let t of [e.hostPc, e.guestPc]) t && !t.closed && (t.iceConnectionState = "connected", t.connectionState = "connected", t.emit("iceconnectionstatechange"), t.emit("connectionstatechange"));
145
+ c ? c = !1 : (t.open(), n.open());
146
+ });
147
+ }
148
+ }
149
+ return l;
150
+ }
151
+ //#endregion
152
+ export { createFakeRtc };
153
+
2
154
  //# sourceMappingURL=fake-rtc.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fake-rtc.js","names":[],"sources":["../../src/testing/fake-rtc.ts"],"sourcesContent":["import type {\n MeshRtcEvent,\n MeshRtcFactory,\n RTCDataChannelLike,\n RTCPeerConnectionLike,\n RTCSessionDescriptionLike,\n} from '../types';\n\ntype Listener = (event: MeshRtcEvent) => void;\n\nclass ListenerBag {\n private readonly listeners = new Map<string, Set<Listener>>();\n\n add(type: string, listener: Listener): void {\n let set = this.listeners.get(type);\n if (!set) {\n set = new Set();\n this.listeners.set(type, set);\n }\n set.add(listener);\n }\n\n remove(type: string, listener: Listener): void {\n this.listeners.get(type)?.delete(listener);\n }\n\n emit(type: string, event: MeshRtcEvent = {}): void {\n for (const listener of this.listeners.get(type) ?? []) listener(event);\n }\n\n // `never` accepts every listener shape declared by the `*Like` overloads.\n addEventListener(type: string, listener: (event: never) => void): void {\n this.add(type, listener as Listener);\n }\n\n removeEventListener(type: string, listener: (event: never) => void): void {\n this.remove(type, listener as Listener);\n }\n}\n\nexport interface FakeRtc {\n /** Close both ends of the link created at `index` (0-based, pairing order). */\n closeChannel(index: number): void;\n /** Silently drop the next `n` channel sends. */\n dropNext(n: number): void;\n /** Make the next pairing fail at the ICE stage. */\n failIce(): void;\n /** Make the next completed link connect at ICE level but never open the channel. */\n hangOpen(): void;\n /** Delivery delay for messages and channel-open in milliseconds. Default: 0 (microtask). */\n latencyMs: number;\n /** Number of links initiated (one per created offer). */\n readonly linkCount: number;\n /** Every SDP handed to `setRemoteDescription`, in call order. */\n readonly remoteSdps: readonly string[];\n readonly rtc: MeshRtcFactory;\n /** Make the next `setLocalDescription` never finish ICE gathering. */\n stallIce(): void;\n /** Drain all latency-queued deliveries immediately. */\n tick(): void;\n}\n\n/**\n * In-memory WebRTC stand-in for testing mesh nodes without a browser.\n * Peer connections created from the same fixture wire together through fake\n * SDP tokens: no network, no real ICE. Pass `fixture.rtc` as the `rtc`\n * option to `createMeshHost`/`createMeshGuest`.\n */\nexport function createFakeRtc(options: { latencyMs?: number } = {}): FakeRtc {\n interface FakeLink {\n completed: boolean;\n guestChannel: FakeDataChannel | null;\n guestPc: FakePeerConnection | null;\n hostChannel: FakeDataChannel | null;\n hostPc: FakePeerConnection;\n readonly hostToken: string;\n }\n\n const pendingDeliveries: Array<() => void> = [];\n const links = new Map<string, FakeLink>();\n const remoteSdps: string[] = [];\n let pcCounter = 0;\n let dropCount = 0;\n let failNextIce = false;\n let stallNextGathering = false;\n let hangNextOpen = false;\n\n const fixture: FakeRtc = {\n closeChannel(index) {\n const link = [...links.values()][index];\n link?.hostChannel?.close();\n link?.guestChannel?.close();\n if (link) {\n link.hostPc.iceConnectionState = 'disconnected';\n link.hostPc.emit('iceconnectionstatechange');\n if (link.guestPc) {\n link.guestPc.iceConnectionState = 'disconnected';\n link.guestPc.emit('iceconnectionstatechange');\n }\n }\n },\n dropNext(n) {\n dropCount += n;\n },\n failIce() {\n failNextIce = true;\n },\n hangOpen() {\n hangNextOpen = true;\n },\n latencyMs: options.latencyMs ?? 0,\n get linkCount() {\n return links.size;\n },\n get remoteSdps() {\n return remoteSdps;\n },\n rtc: {\n createPeerConnection: () => new FakePeerConnection(`pc${++pcCounter}`),\n },\n stallIce() {\n stallNextGathering = true;\n },\n tick() {\n while (pendingDeliveries.length) pendingDeliveries.shift()?.();\n },\n };\n\n function schedule(delivery: () => void): void {\n if (fixture.latencyMs > 0) {\n pendingDeliveries.push(delivery);\n setTimeout(fixture.tick, fixture.latencyMs);\n } else {\n queueMicrotask(delivery);\n }\n }\n\n class FakeDataChannel extends ListenerBag implements RTCDataChannelLike {\n readyState = 'connecting';\n remote: FakeDataChannel | null = null;\n\n send(data: string): void {\n if (this.readyState !== 'open') throw new Error(`Channel is ${this.readyState}`);\n if (dropCount > 0) {\n dropCount--;\n return;\n }\n const remote = this.remote;\n if (!remote || remote.readyState !== 'open') return;\n schedule(() => remote.emit('message', { data }));\n }\n\n open(): void {\n if (this.readyState !== 'connecting') return;\n this.readyState = 'open';\n this.emit('open');\n }\n\n close(): void {\n if (this.readyState === 'closed') return;\n this.readyState = 'closed';\n this.emit('close');\n const remote = this.remote;\n if (remote && remote.readyState !== 'closed') {\n remote.readyState = 'closed';\n remote.emit('close');\n }\n }\n }\n\n class FakePeerConnection extends ListenerBag implements RTCPeerConnectionLike {\n connectionState = 'new';\n iceConnectionState = 'new';\n iceGatheringState = 'new';\n localDescription: RTCSessionDescriptionLike | null = null;\n /** Last description received through `setRemoteDescription`, for assertions. */\n remoteDescription: RTCSessionDescriptionLike | null = null;\n pendingChannel: FakeDataChannel | null = null;\n closed = false;\n\n readonly token: string;\n\n constructor(token: string) {\n super();\n this.token = token;\n }\n\n createDataChannel(): RTCDataChannelLike {\n this.pendingChannel = new FakeDataChannel();\n return this.pendingChannel;\n }\n\n createOffer(): Promise<RTCSessionDescriptionLike> {\n links.set(this.token, {\n completed: false,\n guestChannel: null,\n guestPc: null,\n hostChannel: null,\n hostPc: this,\n hostToken: this.token,\n });\n return Promise.resolve({ sdp: `fake-offer:${this.token}`, type: 'offer' });\n }\n\n createAnswer(): Promise<RTCSessionDescriptionLike> {\n const link = this.linkFor();\n return Promise.resolve({\n sdp: `fake-answer:${link?.hostToken ?? 'none'}:guest:${this.token}`,\n type: 'answer',\n });\n }\n\n setLocalDescription(description: RTCSessionDescriptionLike): Promise<void> {\n this.localDescription = description;\n if (stallNextGathering) {\n stallNextGathering = false;\n this.iceGatheringState = 'gathering';\n } else {\n this.iceGatheringState = 'complete';\n }\n this.emit('icegatheringstatechange');\n return Promise.resolve();\n }\n\n setRemoteDescription(description: RTCSessionDescriptionLike): Promise<void> {\n this.remoteDescription = description;\n remoteSdps.push(description.sdp ?? '');\n if (description.type === 'offer') {\n const hostToken = /fake-offer:(\\w+)/.exec(description.sdp ?? '')?.[1];\n const link = hostToken ? links.get(hostToken) : undefined;\n if (!link) return Promise.reject(new Error('Unknown offer'));\n link.guestPc = this;\n return Promise.resolve();\n }\n\n const match = /fake-answer:(\\w+):guest:(\\w+)/.exec(description.sdp ?? '');\n const link = match?.[1] ? links.get(match[1]) : undefined;\n if (!link || link.guestPc?.token !== match?.[2]) {\n return Promise.reject(new Error('Unknown answer'));\n }\n this.completeLink(link);\n return Promise.resolve();\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.connectionState = 'closed';\n this.iceConnectionState = 'closed';\n this.emit('iceconnectionstatechange');\n this.emit('connectionstatechange');\n this.pendingChannel?.close();\n const link = this.linkFor();\n if (link) {\n link.guestChannel?.close();\n link.hostChannel?.close();\n const remote = link.guestPc === this ? link.hostPc : link.guestPc;\n if (remote && !remote.closed) {\n remote.iceConnectionState = 'disconnected';\n remote.emit('iceconnectionstatechange');\n }\n }\n }\n\n private linkFor(): FakeLink | undefined {\n return [...links.values()].find((link) => link.hostPc === this || link.guestPc === this);\n }\n\n private completeLink(link: FakeLink): void {\n if (link.completed) return;\n link.completed = true;\n\n if (failNextIce) {\n failNextIce = false;\n for (const pc of [link.hostPc, link.guestPc]) {\n if (!pc) continue;\n pc.iceConnectionState = 'failed';\n pc.emit('iceconnectionstatechange');\n }\n return;\n }\n\n const hostChannel = link.hostPc.pendingChannel ?? new FakeDataChannel();\n const guestChannel = new FakeDataChannel();\n hostChannel.remote = guestChannel;\n guestChannel.remote = hostChannel;\n link.hostChannel = hostChannel;\n link.guestChannel = guestChannel;\n link.guestPc?.emit('datachannel', { channel: guestChannel });\n\n schedule(() => {\n for (const pc of [link.hostPc, link.guestPc]) {\n if (!pc || pc.closed) continue;\n pc.iceConnectionState = 'connected';\n pc.connectionState = 'connected';\n pc.emit('iceconnectionstatechange');\n pc.emit('connectionstatechange');\n }\n if (hangNextOpen) {\n hangNextOpen = false;\n return;\n }\n hostChannel.open();\n guestChannel.open();\n });\n }\n }\n\n return fixture;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"fake-rtc.js","names":["set","type","listener","event","pendingDeliveries","links","remoteSdps","pcCounter","dropCount","failNextIce","stallNextGathering","hangNextOpen","fixture","link","index","n","options","delivery","remote","token","description","hostToken","match","pc","hostChannel","guestChannel"],"sources":["../../src/testing/fake-rtc.ts"],"sourcesContent":["import type {\n MeshRtcEvent,\n MeshRtcFactory,\n RTCDataChannelLike,\n RTCPeerConnectionLike,\n RTCSessionDescriptionLike,\n} from '../types';\n\ntype Listener = (event: MeshRtcEvent) => void;\n\nclass ListenerBag {\n private readonly listeners = new Map<string, Set<Listener>>();\n\n add(type: string, listener: Listener): void {\n let set = this.listeners.get(type);\n if (!set) {\n set = new Set();\n this.listeners.set(type, set);\n }\n set.add(listener);\n }\n\n remove(type: string, listener: Listener): void {\n this.listeners.get(type)?.delete(listener);\n }\n\n emit(type: string, event: MeshRtcEvent = {}): void {\n for (const listener of this.listeners.get(type) ?? []) listener(event);\n }\n\n // `never` accepts every listener shape declared by the `*Like` overloads.\n addEventListener(type: string, listener: (event: never) => void): void {\n this.add(type, listener as Listener);\n }\n\n removeEventListener(type: string, listener: (event: never) => void): void {\n this.remove(type, listener as Listener);\n }\n}\n\nexport interface FakeRtc {\n /** Close both ends of the link created at `index` (0-based, pairing order). */\n closeChannel(index: number): void;\n /** Silently drop the next `n` channel sends. */\n dropNext(n: number): void;\n /** Make the next pairing fail at the ICE stage. */\n failIce(): void;\n /** Make the next completed link connect at ICE level but never open the channel. */\n hangOpen(): void;\n /** Delivery delay for messages and channel-open in milliseconds. Default: 0 (microtask). */\n latencyMs: number;\n /** Number of links initiated (one per created offer). */\n readonly linkCount: number;\n /** Every SDP handed to `setRemoteDescription`, in call order. */\n readonly remoteSdps: readonly string[];\n readonly rtc: MeshRtcFactory;\n /** Make the next `setLocalDescription` never finish ICE gathering. */\n stallIce(): void;\n /** Drain all latency-queued deliveries immediately. */\n tick(): void;\n}\n\n/**\n * In-memory WebRTC stand-in for testing mesh nodes without a browser.\n * Peer connections created from the same fixture wire together through fake\n * SDP tokens: no network, no real ICE. Pass `fixture.rtc` as the `rtc`\n * option to `createMeshHost`/`createMeshGuest`.\n */\nexport function createFakeRtc(options: { latencyMs?: number } = {}): FakeRtc {\n interface FakeLink {\n completed: boolean;\n guestChannel: FakeDataChannel | null;\n guestPc: FakePeerConnection | null;\n hostChannel: FakeDataChannel | null;\n hostPc: FakePeerConnection;\n readonly hostToken: string;\n }\n\n const pendingDeliveries: Array<() => void> = [];\n const links = new Map<string, FakeLink>();\n const remoteSdps: string[] = [];\n let pcCounter = 0;\n let dropCount = 0;\n let failNextIce = false;\n let stallNextGathering = false;\n let hangNextOpen = false;\n\n const fixture: FakeRtc = {\n closeChannel(index) {\n const link = [...links.values()][index];\n link?.hostChannel?.close();\n link?.guestChannel?.close();\n if (link) {\n link.hostPc.iceConnectionState = 'disconnected';\n link.hostPc.emit('iceconnectionstatechange');\n if (link.guestPc) {\n link.guestPc.iceConnectionState = 'disconnected';\n link.guestPc.emit('iceconnectionstatechange');\n }\n }\n },\n dropNext(n) {\n dropCount += n;\n },\n failIce() {\n failNextIce = true;\n },\n hangOpen() {\n hangNextOpen = true;\n },\n latencyMs: options.latencyMs ?? 0,\n get linkCount() {\n return links.size;\n },\n get remoteSdps() {\n return remoteSdps;\n },\n rtc: {\n createPeerConnection: () => new FakePeerConnection(`pc${++pcCounter}`),\n },\n stallIce() {\n stallNextGathering = true;\n },\n tick() {\n while (pendingDeliveries.length) pendingDeliveries.shift()?.();\n },\n };\n\n function schedule(delivery: () => void): void {\n if (fixture.latencyMs > 0) {\n pendingDeliveries.push(delivery);\n setTimeout(fixture.tick, fixture.latencyMs);\n } else {\n queueMicrotask(delivery);\n }\n }\n\n class FakeDataChannel extends ListenerBag implements RTCDataChannelLike {\n readyState = 'connecting';\n remote: FakeDataChannel | null = null;\n\n send(data: string): void {\n if (this.readyState !== 'open') throw new Error(`Channel is ${this.readyState}`);\n if (dropCount > 0) {\n dropCount--;\n return;\n }\n const remote = this.remote;\n if (!remote || remote.readyState !== 'open') return;\n schedule(() => remote.emit('message', { data }));\n }\n\n open(): void {\n if (this.readyState !== 'connecting') return;\n this.readyState = 'open';\n this.emit('open');\n }\n\n close(): void {\n if (this.readyState === 'closed') return;\n this.readyState = 'closed';\n this.emit('close');\n const remote = this.remote;\n if (remote && remote.readyState !== 'closed') {\n remote.readyState = 'closed';\n remote.emit('close');\n }\n }\n }\n\n class FakePeerConnection extends ListenerBag implements RTCPeerConnectionLike {\n connectionState = 'new';\n iceConnectionState = 'new';\n iceGatheringState = 'new';\n localDescription: RTCSessionDescriptionLike | null = null;\n /** Last description received through `setRemoteDescription`, for assertions. */\n remoteDescription: RTCSessionDescriptionLike | null = null;\n pendingChannel: FakeDataChannel | null = null;\n closed = false;\n\n readonly token: string;\n\n constructor(token: string) {\n super();\n this.token = token;\n }\n\n createDataChannel(): RTCDataChannelLike {\n this.pendingChannel = new FakeDataChannel();\n return this.pendingChannel;\n }\n\n createOffer(): Promise<RTCSessionDescriptionLike> {\n links.set(this.token, {\n completed: false,\n guestChannel: null,\n guestPc: null,\n hostChannel: null,\n hostPc: this,\n hostToken: this.token,\n });\n return Promise.resolve({ sdp: `fake-offer:${this.token}`, type: 'offer' });\n }\n\n createAnswer(): Promise<RTCSessionDescriptionLike> {\n const link = this.linkFor();\n return Promise.resolve({\n sdp: `fake-answer:${link?.hostToken ?? 'none'}:guest:${this.token}`,\n type: 'answer',\n });\n }\n\n setLocalDescription(description: RTCSessionDescriptionLike): Promise<void> {\n this.localDescription = description;\n if (stallNextGathering) {\n stallNextGathering = false;\n this.iceGatheringState = 'gathering';\n } else {\n this.iceGatheringState = 'complete';\n }\n this.emit('icegatheringstatechange');\n return Promise.resolve();\n }\n\n setRemoteDescription(description: RTCSessionDescriptionLike): Promise<void> {\n this.remoteDescription = description;\n remoteSdps.push(description.sdp ?? '');\n if (description.type === 'offer') {\n const hostToken = /fake-offer:(\\w+)/.exec(description.sdp ?? '')?.[1];\n const link = hostToken ? links.get(hostToken) : undefined;\n if (!link) return Promise.reject(new Error('Unknown offer'));\n link.guestPc = this;\n return Promise.resolve();\n }\n\n const match = /fake-answer:(\\w+):guest:(\\w+)/.exec(description.sdp ?? '');\n const link = match?.[1] ? links.get(match[1]) : undefined;\n if (!link || link.guestPc?.token !== match?.[2]) {\n return Promise.reject(new Error('Unknown answer'));\n }\n this.completeLink(link);\n return Promise.resolve();\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n this.connectionState = 'closed';\n this.iceConnectionState = 'closed';\n this.emit('iceconnectionstatechange');\n this.emit('connectionstatechange');\n this.pendingChannel?.close();\n const link = this.linkFor();\n if (link) {\n link.guestChannel?.close();\n link.hostChannel?.close();\n const remote = link.guestPc === this ? link.hostPc : link.guestPc;\n if (remote && !remote.closed) {\n remote.iceConnectionState = 'disconnected';\n remote.emit('iceconnectionstatechange');\n }\n }\n }\n\n private linkFor(): FakeLink | undefined {\n return [...links.values()].find((link) => link.hostPc === this || link.guestPc === this);\n }\n\n private completeLink(link: FakeLink): void {\n if (link.completed) return;\n link.completed = true;\n\n if (failNextIce) {\n failNextIce = false;\n for (const pc of [link.hostPc, link.guestPc]) {\n if (!pc) continue;\n pc.iceConnectionState = 'failed';\n pc.emit('iceconnectionstatechange');\n }\n return;\n }\n\n const hostChannel = link.hostPc.pendingChannel ?? new FakeDataChannel();\n const guestChannel = new FakeDataChannel();\n hostChannel.remote = guestChannel;\n guestChannel.remote = hostChannel;\n link.hostChannel = hostChannel;\n link.guestChannel = guestChannel;\n link.guestPc?.emit('datachannel', { channel: guestChannel });\n\n schedule(() => {\n for (const pc of [link.hostPc, link.guestPc]) {\n if (!pc || pc.closed) continue;\n pc.iceConnectionState = 'connected';\n pc.connectionState = 'connected';\n pc.emit('iceconnectionstatechange');\n pc.emit('connectionstatechange');\n }\n if (hangNextOpen) {\n hangNextOpen = false;\n return;\n }\n hostChannel.open();\n guestChannel.open();\n });\n }\n }\n\n return fixture;\n}\n"],"mappings":"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"}
package/dist/testing.js CHANGED
@@ -1 +1 @@
1
- import{createFakeRtc as e}from"./testing/fake-rtc.js";export{e as createFakeRtc};
1
+ export { createFakeRtc } from "./testing/fake-rtc.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vielzeug/mesh",
3
- "version": "26.10.15",
3
+ "version": "26.10.16",
4
4
  "description": "Backendless peer-to-peer session transport over WebRTC data channels with manual pairing and host-authoritative star topology",
5
5
  "repository": {
6
6
  "type": "git",
@@ -51,12 +51,12 @@
51
51
  "test": "vitest"
52
52
  },
53
53
  "dependencies": {
54
- "@vielzeug/arsenal": "26.10.15"
54
+ "@vielzeug/arsenal": "26.10.16"
55
55
  },
56
56
  "devDependencies": {
57
- "@types/node": "^26.6.2",
57
+ "@types/node": "^26.6.5",
58
58
  "typescript": "^6.0.3",
59
- "vite": "^8.3.0",
60
- "vitest": "^5.0.1"
59
+ "vite": "^8.3.4",
60
+ "vitest": "^5.0.5"
61
61
  }
62
62
  }