@vielzeug/mesh 26.10.14 → 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.
- package/dist/_base45.cjs.map +1 -1
- package/dist/_base45.js +37 -1
- package/dist/_base45.js.map +1 -1
- package/dist/_defaults.cjs.map +1 -1
- package/dist/_defaults.js +5 -1
- package/dist/_defaults.js.map +1 -1
- package/dist/_node.cjs.map +1 -1
- package/dist/_node.js +166 -1
- package/dist/_node.js.map +1 -1
- package/dist/_proof.cjs.map +1 -1
- package/dist/_proof.js +16 -1
- package/dist/_proof.js.map +1 -1
- package/dist/_random.cjs.map +1 -1
- package/dist/_random.js +14 -1
- package/dist/_random.js.map +1 -1
- package/dist/_rtc.cjs.map +1 -1
- package/dist/_rtc.js +41 -3
- package/dist/_rtc.js.map +1 -1
- package/dist/_sdp.cjs.map +1 -1
- package/dist/_sdp.js +54 -2
- package/dist/_sdp.js.map +1 -1
- package/dist/_wire.cjs.map +1 -1
- package/dist/_wire.js +20 -1
- package/dist/_wire.js.map +1 -1
- package/dist/codec.cjs.map +1 -1
- package/dist/codec.js +34 -1
- package/dist/codec.js.map +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.js +18 -1
- package/dist/errors.js.map +1 -1
- package/dist/guest.cjs.map +1 -1
- package/dist/guest.js +153 -1
- package/dist/guest.js.map +1 -1
- package/dist/host.cjs.map +1 -1
- package/dist/host.js +202 -1
- package/dist/host.js.map +1 -1
- package/dist/index.js +6 -1
- package/dist/mesh.cjs.map +1 -1
- package/dist/mesh.iife.js.map +1 -1
- package/dist/mesh.js +756 -4
- package/dist/mesh.js.map +1 -1
- package/dist/qr-codec.cjs.map +1 -1
- package/dist/qr-codec.js +52 -1
- package/dist/qr-codec.js.map +1 -1
- package/dist/testing/fake-rtc.cjs +1 -1
- package/dist/testing/fake-rtc.cjs.map +1 -1
- package/dist/testing/fake-rtc.js +153 -1
- package/dist/testing/fake-rtc.js.map +1 -1
- package/dist/testing.js +1 -1
- package/package.json +5 -5
package/dist/_sdp.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_sdp.cjs","names":[],"sources":["../src/_sdp.ts"],"sourcesContent":["import { MeshPairingError } from './errors';\n\n/**\n * Compact SDP serialization for pairing payloads. A data-channel-only\n * SDP is mostly boilerplate; the remote side only needs the ICE credentials,\n * DTLS fingerprint, setup role, mid, and candidates. `compactSdp` extracts\n * those fields and `expandSdp` rebuilds a minimal valid SDP for\n * `setRemoteDescription`.\n *\n * `compactSdp` returns `null` for shapes it cannot safely represent\n * (multiple m-lines, missing credentials), letting callers keep the raw SDP.\n */\n\nexport interface CompactSdp {\n /** `a=candidate:` lines, prefix stripped. */\n c: string[];\n /** sha-256 fingerprint, hex without colons. */\n f: string;\n /** `a=mid`. */\n m: string;\n /** `a=max-message-size`. */\n mm?: number;\n /** `a=ice-pwd`. */\n p: string;\n /** `a=setup` role. */\n s: string;\n /** `a=sctp-port`. */\n sp?: number;\n /** `a=ice-ufrag`. */\n u: string;\n}\n\nfunction isCompactSdp(value: unknown): value is CompactSdp {\n if (typeof value !== 'object' || value === null) return false;\n const c = value as Record<string, unknown>;\n return (\n typeof c.u === 'string' &&\n typeof c.p === 'string' &&\n typeof c.f === 'string' &&\n typeof c.s === 'string' &&\n typeof c.m === 'string' &&\n Array.isArray(c.c) &&\n c.c.every((line) => typeof line === 'string')\n );\n}\n\n/** Returns the expanded SDP unchanged when `value` is already a string. */\nexport function restoreSdp(value: unknown): string {\n if (typeof value === 'string') return value;\n if (isCompactSdp(value)) return expandSdp(value);\n throw new MeshPairingError('Malformed pairing payload');\n}\n\nexport function compactSdp(sdp: string): CompactSdp | null {\n let ufrag = '';\n let pwd = '';\n let fingerprint = '';\n let setup = '';\n let mid = '0';\n let sctpPort: number | undefined;\n let maxMessageSize: number | undefined;\n const candidates: string[] = [];\n let mLines = 0;\n\n for (const line of sdp.split(/\\r?\\n/)) {\n if (line.startsWith('m=')) mLines += 1;\n else if (line.startsWith('a=ice-ufrag:')) ufrag = line.slice(12);\n else if (line.startsWith('a=ice-pwd:')) pwd = line.slice(10);\n else if (line.startsWith('a=fingerprint:sha-256 ')) fingerprint = line.slice(22).replaceAll(':', '').toUpperCase();\n else if (line.startsWith('a=setup:')) setup = line.slice(8);\n else if (line.startsWith('a=mid:')) mid = line.slice(6);\n else if (line.startsWith('a=sctp-port:')) sctpPort = Number(line.slice(12));\n else if (line.startsWith('a=max-message-size:')) maxMessageSize = Number(line.slice(19));\n else if (line.startsWith('a=candidate:')) candidates.push(line.slice(12));\n }\n\n if (mLines !== 1 || !ufrag || !pwd || !fingerprint) return null;\n return {\n c: candidates,\n f: fingerprint,\n m: mid,\n p: pwd,\n s: setup || 'actpass',\n ...(maxMessageSize ? { mm: maxMessageSize } : {}),\n ...(sctpPort ? { sp: sctpPort } : {}),\n u: ufrag,\n };\n}\n\n/**\n * Canonical signing form: the compact serialization when the SDP is\n * recognizable, else the raw text. `compactSdp` is lossless over the fields\n * it keeps, so the rebuilt `mq2.` SDP canonicalizes identically to the\n * original: a proof computed over one verifies against the other.\n */\nexport function canonicalSdp(sdp: string): string {\n const compact = compactSdp(sdp);\n return compact ? JSON.stringify(compact) : sdp;\n}\n\nexport function expandSdp(compact: CompactSdp): string {\n const colonHex = compact.f.replace(/(..)/g, '$1:').slice(0, -1);\n return `${[\n 'v=0',\n 'o=- 0 2 IN IP4 0.0.0.0',\n 's=-',\n 't=0 0',\n `a=group:BUNDLE ${compact.m}`,\n 'm=application 9 UDP/DTLS/SCTP webrtc-datachannel',\n 'c=IN IP4 0.0.0.0',\n `a=ice-ufrag:${compact.u}`,\n `a=ice-pwd:${compact.p}`,\n `a=fingerprint:sha-256 ${colonHex}`,\n `a=setup:${compact.s}`,\n `a=mid:${compact.m}`,\n `a=sctp-port:${compact.sp ?? 5000}`,\n ...(compact.mm ? [`a=max-message-size:${compact.mm}`] : []),\n ...compact.c.map((line) => `a=candidate:${line}`),\n '',\n ].join('\\r\\n')}`;\n}\n"],"mappings":"gCAgCA,SAAS,aAAa,EAAqC,CACzD,GAAI,
|
|
1
|
+
{"version":3,"file":"_sdp.cjs","names":["MeshPairingError","require_errors","value","c","line","ufrag","pwd","fingerprint","setup","mid","sctpPort","maxMessageSize","candidates","mLines","sdp","compact","colonHex"],"sources":["../src/_sdp.ts"],"sourcesContent":["import { MeshPairingError } from './errors';\n\n/**\n * Compact SDP serialization for pairing payloads. A data-channel-only\n * SDP is mostly boilerplate; the remote side only needs the ICE credentials,\n * DTLS fingerprint, setup role, mid, and candidates. `compactSdp` extracts\n * those fields and `expandSdp` rebuilds a minimal valid SDP for\n * `setRemoteDescription`.\n *\n * `compactSdp` returns `null` for shapes it cannot safely represent\n * (multiple m-lines, missing credentials), letting callers keep the raw SDP.\n */\n\nexport interface CompactSdp {\n /** `a=candidate:` lines, prefix stripped. */\n c: string[];\n /** sha-256 fingerprint, hex without colons. */\n f: string;\n /** `a=mid`. */\n m: string;\n /** `a=max-message-size`. */\n mm?: number;\n /** `a=ice-pwd`. */\n p: string;\n /** `a=setup` role. */\n s: string;\n /** `a=sctp-port`. */\n sp?: number;\n /** `a=ice-ufrag`. */\n u: string;\n}\n\nfunction isCompactSdp(value: unknown): value is CompactSdp {\n if (typeof value !== 'object' || value === null) return false;\n const c = value as Record<string, unknown>;\n return (\n typeof c.u === 'string' &&\n typeof c.p === 'string' &&\n typeof c.f === 'string' &&\n typeof c.s === 'string' &&\n typeof c.m === 'string' &&\n Array.isArray(c.c) &&\n c.c.every((line) => typeof line === 'string')\n );\n}\n\n/** Returns the expanded SDP unchanged when `value` is already a string. */\nexport function restoreSdp(value: unknown): string {\n if (typeof value === 'string') return value;\n if (isCompactSdp(value)) return expandSdp(value);\n throw new MeshPairingError('Malformed pairing payload');\n}\n\nexport function compactSdp(sdp: string): CompactSdp | null {\n let ufrag = '';\n let pwd = '';\n let fingerprint = '';\n let setup = '';\n let mid = '0';\n let sctpPort: number | undefined;\n let maxMessageSize: number | undefined;\n const candidates: string[] = [];\n let mLines = 0;\n\n for (const line of sdp.split(/\\r?\\n/)) {\n if (line.startsWith('m=')) mLines += 1;\n else if (line.startsWith('a=ice-ufrag:')) ufrag = line.slice(12);\n else if (line.startsWith('a=ice-pwd:')) pwd = line.slice(10);\n else if (line.startsWith('a=fingerprint:sha-256 ')) fingerprint = line.slice(22).replaceAll(':', '').toUpperCase();\n else if (line.startsWith('a=setup:')) setup = line.slice(8);\n else if (line.startsWith('a=mid:')) mid = line.slice(6);\n else if (line.startsWith('a=sctp-port:')) sctpPort = Number(line.slice(12));\n else if (line.startsWith('a=max-message-size:')) maxMessageSize = Number(line.slice(19));\n else if (line.startsWith('a=candidate:')) candidates.push(line.slice(12));\n }\n\n if (mLines !== 1 || !ufrag || !pwd || !fingerprint) return null;\n return {\n c: candidates,\n f: fingerprint,\n m: mid,\n p: pwd,\n s: setup || 'actpass',\n ...(maxMessageSize ? { mm: maxMessageSize } : {}),\n ...(sctpPort ? { sp: sctpPort } : {}),\n u: ufrag,\n };\n}\n\n/**\n * Canonical signing form: the compact serialization when the SDP is\n * recognizable, else the raw text. `compactSdp` is lossless over the fields\n * it keeps, so the rebuilt `mq2.` SDP canonicalizes identically to the\n * original: a proof computed over one verifies against the other.\n */\nexport function canonicalSdp(sdp: string): string {\n const compact = compactSdp(sdp);\n return compact ? JSON.stringify(compact) : sdp;\n}\n\nexport function expandSdp(compact: CompactSdp): string {\n const colonHex = compact.f.replace(/(..)/g, '$1:').slice(0, -1);\n return `${[\n 'v=0',\n 'o=- 0 2 IN IP4 0.0.0.0',\n 's=-',\n 't=0 0',\n `a=group:BUNDLE ${compact.m}`,\n 'm=application 9 UDP/DTLS/SCTP webrtc-datachannel',\n 'c=IN IP4 0.0.0.0',\n `a=ice-ufrag:${compact.u}`,\n `a=ice-pwd:${compact.p}`,\n `a=fingerprint:sha-256 ${colonHex}`,\n `a=setup:${compact.s}`,\n `a=mid:${compact.m}`,\n `a=sctp-port:${compact.sp ?? 5000}`,\n ...(compact.mm ? [`a=max-message-size:${compact.mm}`] : []),\n ...compact.c.map((line) => `a=candidate:${line}`),\n '',\n ].join('\\r\\n')}`;\n}\n"],"mappings":"A,gCAgCA,SAAS,aAAa,EAAqC,CACzD,GAAI,OAAOE,GAAU,WAAYA,EAAgB,MAAO,GACxD,IAAMC,EAAID,EACV,OACE,OAAOC,EAAE,GAAM,UACf,OAAOA,EAAE,GAAM,UACf,OAAOA,EAAE,GAAM,UACf,OAAOA,EAAE,GAAM,UACf,OAAOA,EAAE,GAAM,UACf,MAAM,QAAQA,EAAE,CAAC,GACjBA,EAAE,EAAE,MAAO,GAAS,OAAOC,GAAS,QAAQ,CAEhD,CAGA,SAAgB,WAAW,EAAwB,CACjD,GAAI,OAAOF,GAAU,SAAU,OAAOA,EACtC,GAAI,aAAaA,CAAK,EAAG,OAAO,UAAUA,CAAK,EAC/C,MAAM,IAAIF,EAAAA,iBAAiB,2BAA2B,CACxD,CAEA,SAAgB,WAAW,EAAgC,CACzD,IAAIK,EAAQ,GACRC,EAAM,GACNC,EAAc,GACdC,EAAQ,GACRC,EAAM,IACNC,EACAC,EACEC,EAAuB,CAAC,EAC1BC,EAAS,EAEb,IAAK,IAAMT,KAAQU,EAAI,MAAM,OAAO,EAC9BV,EAAK,WAAW,IAAI,EAAG,GAAU,EAC5BA,EAAK,WAAW,cAAc,EAAG,EAAQA,EAAK,MAAM,EAAE,EACtDA,EAAK,WAAW,YAAY,EAAG,EAAMA,EAAK,MAAM,EAAE,EAClDA,EAAK,WAAW,wBAAwB,EAAG,EAAcA,EAAK,MAAM,EAAE,CAAC,CAAC,WAAW,IAAK,EAAE,CAAC,CAAC,YAAY,EACxGA,EAAK,WAAW,UAAU,EAAG,EAAQA,EAAK,MAAM,CAAC,EACjDA,EAAK,WAAW,QAAQ,EAAG,EAAMA,EAAK,MAAM,CAAC,EAC7CA,EAAK,WAAW,cAAc,EAAG,EAAW,OAAOA,EAAK,MAAM,EAAE,CAAC,EACjEA,EAAK,WAAW,qBAAqB,EAAG,EAAiB,OAAOA,EAAK,MAAM,EAAE,CAAC,EAC9EA,EAAK,WAAW,cAAc,GAAGQ,EAAW,KAAKR,EAAK,MAAM,EAAE,CAAC,EAI1E,OADIS,IAAW,GAAK,CAACR,GAAS,CAACC,GAAO,CAACC,EAAoB,KACpD,CACFK,EACH,EAAGL,EACH,EAAGE,EACH,EAAGH,EACH,EAAGE,GAAS,UACZ,GAAIG,EAAiB,CAAE,GAAIA,CAAe,EAAI,CAAC,EAC/C,GAAID,EAAW,CAAE,GAAIA,CAAS,EAAI,CAAC,EACnC,EAAGL,CACL,CACF,CAQA,SAAgB,aAAa,EAAqB,CAChD,IAAMU,EAAU,WAAWD,CAAG,EAC9B,OAAOC,EAAU,KAAK,UAAUA,CAAO,EAAID,CAC7C,CAEA,SAAgB,UAAU,EAA6B,CACrD,IAAME,EAAWD,EAAQ,EAAE,QAAQ,QAAS,KAAK,CAAC,CAAC,MAAM,EAAG,EAAE,EAC9D,MAAO,GAAG,CACR,MACA,yBACA,MACA,QACA,kBAAkBA,EAAQ,IAC1B,mDACA,mBACA,eAAeA,EAAQ,IACvB,aAAaA,EAAQ,IACrB,yBAAyBC,IACzB,WAAWD,EAAQ,IACnB,SAASA,EAAQ,IACjB,eAAeA,EAAQ,IAAM,MAC7B,GAAIA,EAAQ,GAAK,CAAC,sBAAsBA,EAAQ,IAAI,EAAI,CAAC,EACzD,GAAGA,EAAQ,EAAE,IAAK,GAAS,eAAeX,GAAM,EAChD,EACF,CAAC,CAAC,KAAK;CAAM,GACf,C"}
|
package/dist/_sdp.js
CHANGED
|
@@ -1,3 +1,55 @@
|
|
|
1
|
-
import{MeshPairingError as e}from"./errors.js";
|
|
2
|
-
|
|
1
|
+
import { MeshPairingError as e } from "./errors.js";
|
|
2
|
+
//#region src/_sdp.ts
|
|
3
|
+
function isCompactSdp(e) {
|
|
4
|
+
if (typeof e != "object" || !e) return !1;
|
|
5
|
+
let t = e;
|
|
6
|
+
return typeof t.u == "string" && typeof t.p == "string" && typeof t.f == "string" && typeof t.s == "string" && typeof t.m == "string" && Array.isArray(t.c) && t.c.every((e) => typeof e == "string");
|
|
7
|
+
}
|
|
8
|
+
function restoreSdp(t) {
|
|
9
|
+
if (typeof t == "string") return t;
|
|
10
|
+
if (isCompactSdp(t)) return expandSdp(t);
|
|
11
|
+
throw new e("Malformed pairing payload");
|
|
12
|
+
}
|
|
13
|
+
function compactSdp(e) {
|
|
14
|
+
let t = "", n = "", r = "", i = "", a = "0", o, s, c = [], l = 0;
|
|
15
|
+
for (let u of e.split(/\r?\n/)) u.startsWith("m=") ? l += 1 : u.startsWith("a=ice-ufrag:") ? t = u.slice(12) : u.startsWith("a=ice-pwd:") ? n = u.slice(10) : u.startsWith("a=fingerprint:sha-256 ") ? r = u.slice(22).replaceAll(":", "").toUpperCase() : u.startsWith("a=setup:") ? i = u.slice(8) : u.startsWith("a=mid:") ? a = u.slice(6) : u.startsWith("a=sctp-port:") ? o = Number(u.slice(12)) : u.startsWith("a=max-message-size:") ? s = Number(u.slice(19)) : u.startsWith("a=candidate:") && c.push(u.slice(12));
|
|
16
|
+
return l !== 1 || !t || !n || !r ? null : {
|
|
17
|
+
c,
|
|
18
|
+
f: r,
|
|
19
|
+
m: a,
|
|
20
|
+
p: n,
|
|
21
|
+
s: i || "actpass",
|
|
22
|
+
...s ? { mm: s } : {},
|
|
23
|
+
...o ? { sp: o } : {},
|
|
24
|
+
u: t
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function canonicalSdp(e) {
|
|
28
|
+
let t = compactSdp(e);
|
|
29
|
+
return t ? JSON.stringify(t) : e;
|
|
30
|
+
}
|
|
31
|
+
function expandSdp(e) {
|
|
32
|
+
let t = e.f.replace(/(..)/g, "$1:").slice(0, -1);
|
|
33
|
+
return `${[
|
|
34
|
+
"v=0",
|
|
35
|
+
"o=- 0 2 IN IP4 0.0.0.0",
|
|
36
|
+
"s=-",
|
|
37
|
+
"t=0 0",
|
|
38
|
+
`a=group:BUNDLE ${e.m}`,
|
|
39
|
+
"m=application 9 UDP/DTLS/SCTP webrtc-datachannel",
|
|
40
|
+
"c=IN IP4 0.0.0.0",
|
|
41
|
+
`a=ice-ufrag:${e.u}`,
|
|
42
|
+
`a=ice-pwd:${e.p}`,
|
|
43
|
+
`a=fingerprint:sha-256 ${t}`,
|
|
44
|
+
`a=setup:${e.s}`,
|
|
45
|
+
`a=mid:${e.m}`,
|
|
46
|
+
`a=sctp-port:${e.sp ?? 5e3}`,
|
|
47
|
+
...e.mm ? [`a=max-message-size:${e.mm}`] : [],
|
|
48
|
+
...e.c.map((e) => `a=candidate:${e}`),
|
|
49
|
+
""
|
|
50
|
+
].join("\r\n")}`;
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
export { canonicalSdp, compactSdp, expandSdp, restoreSdp };
|
|
54
|
+
|
|
3
55
|
//# sourceMappingURL=_sdp.js.map
|
package/dist/_sdp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_sdp.js","names":[],"sources":["../src/_sdp.ts"],"sourcesContent":["import { MeshPairingError } from './errors';\n\n/**\n * Compact SDP serialization for pairing payloads. A data-channel-only\n * SDP is mostly boilerplate; the remote side only needs the ICE credentials,\n * DTLS fingerprint, setup role, mid, and candidates. `compactSdp` extracts\n * those fields and `expandSdp` rebuilds a minimal valid SDP for\n * `setRemoteDescription`.\n *\n * `compactSdp` returns `null` for shapes it cannot safely represent\n * (multiple m-lines, missing credentials), letting callers keep the raw SDP.\n */\n\nexport interface CompactSdp {\n /** `a=candidate:` lines, prefix stripped. */\n c: string[];\n /** sha-256 fingerprint, hex without colons. */\n f: string;\n /** `a=mid`. */\n m: string;\n /** `a=max-message-size`. */\n mm?: number;\n /** `a=ice-pwd`. */\n p: string;\n /** `a=setup` role. */\n s: string;\n /** `a=sctp-port`. */\n sp?: number;\n /** `a=ice-ufrag`. */\n u: string;\n}\n\nfunction isCompactSdp(value: unknown): value is CompactSdp {\n if (typeof value !== 'object' || value === null) return false;\n const c = value as Record<string, unknown>;\n return (\n typeof c.u === 'string' &&\n typeof c.p === 'string' &&\n typeof c.f === 'string' &&\n typeof c.s === 'string' &&\n typeof c.m === 'string' &&\n Array.isArray(c.c) &&\n c.c.every((line) => typeof line === 'string')\n );\n}\n\n/** Returns the expanded SDP unchanged when `value` is already a string. */\nexport function restoreSdp(value: unknown): string {\n if (typeof value === 'string') return value;\n if (isCompactSdp(value)) return expandSdp(value);\n throw new MeshPairingError('Malformed pairing payload');\n}\n\nexport function compactSdp(sdp: string): CompactSdp | null {\n let ufrag = '';\n let pwd = '';\n let fingerprint = '';\n let setup = '';\n let mid = '0';\n let sctpPort: number | undefined;\n let maxMessageSize: number | undefined;\n const candidates: string[] = [];\n let mLines = 0;\n\n for (const line of sdp.split(/\\r?\\n/)) {\n if (line.startsWith('m=')) mLines += 1;\n else if (line.startsWith('a=ice-ufrag:')) ufrag = line.slice(12);\n else if (line.startsWith('a=ice-pwd:')) pwd = line.slice(10);\n else if (line.startsWith('a=fingerprint:sha-256 ')) fingerprint = line.slice(22).replaceAll(':', '').toUpperCase();\n else if (line.startsWith('a=setup:')) setup = line.slice(8);\n else if (line.startsWith('a=mid:')) mid = line.slice(6);\n else if (line.startsWith('a=sctp-port:')) sctpPort = Number(line.slice(12));\n else if (line.startsWith('a=max-message-size:')) maxMessageSize = Number(line.slice(19));\n else if (line.startsWith('a=candidate:')) candidates.push(line.slice(12));\n }\n\n if (mLines !== 1 || !ufrag || !pwd || !fingerprint) return null;\n return {\n c: candidates,\n f: fingerprint,\n m: mid,\n p: pwd,\n s: setup || 'actpass',\n ...(maxMessageSize ? { mm: maxMessageSize } : {}),\n ...(sctpPort ? { sp: sctpPort } : {}),\n u: ufrag,\n };\n}\n\n/**\n * Canonical signing form: the compact serialization when the SDP is\n * recognizable, else the raw text. `compactSdp` is lossless over the fields\n * it keeps, so the rebuilt `mq2.` SDP canonicalizes identically to the\n * original: a proof computed over one verifies against the other.\n */\nexport function canonicalSdp(sdp: string): string {\n const compact = compactSdp(sdp);\n return compact ? JSON.stringify(compact) : sdp;\n}\n\nexport function expandSdp(compact: CompactSdp): string {\n const colonHex = compact.f.replace(/(..)/g, '$1:').slice(0, -1);\n return `${[\n 'v=0',\n 'o=- 0 2 IN IP4 0.0.0.0',\n 's=-',\n 't=0 0',\n `a=group:BUNDLE ${compact.m}`,\n 'm=application 9 UDP/DTLS/SCTP webrtc-datachannel',\n 'c=IN IP4 0.0.0.0',\n `a=ice-ufrag:${compact.u}`,\n `a=ice-pwd:${compact.p}`,\n `a=fingerprint:sha-256 ${colonHex}`,\n `a=setup:${compact.s}`,\n `a=mid:${compact.m}`,\n `a=sctp-port:${compact.sp ?? 5000}`,\n ...(compact.mm ? [`a=max-message-size:${compact.mm}`] : []),\n ...compact.c.map((line) => `a=candidate:${line}`),\n '',\n ].join('\\r\\n')}`;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"_sdp.js","names":["value","c","line","MeshPairingError","ufrag","pwd","fingerprint","setup","mid","sctpPort","maxMessageSize","candidates","mLines","sdp","compact","colonHex"],"sources":["../src/_sdp.ts"],"sourcesContent":["import { MeshPairingError } from './errors';\n\n/**\n * Compact SDP serialization for pairing payloads. A data-channel-only\n * SDP is mostly boilerplate; the remote side only needs the ICE credentials,\n * DTLS fingerprint, setup role, mid, and candidates. `compactSdp` extracts\n * those fields and `expandSdp` rebuilds a minimal valid SDP for\n * `setRemoteDescription`.\n *\n * `compactSdp` returns `null` for shapes it cannot safely represent\n * (multiple m-lines, missing credentials), letting callers keep the raw SDP.\n */\n\nexport interface CompactSdp {\n /** `a=candidate:` lines, prefix stripped. */\n c: string[];\n /** sha-256 fingerprint, hex without colons. */\n f: string;\n /** `a=mid`. */\n m: string;\n /** `a=max-message-size`. */\n mm?: number;\n /** `a=ice-pwd`. */\n p: string;\n /** `a=setup` role. */\n s: string;\n /** `a=sctp-port`. */\n sp?: number;\n /** `a=ice-ufrag`. */\n u: string;\n}\n\nfunction isCompactSdp(value: unknown): value is CompactSdp {\n if (typeof value !== 'object' || value === null) return false;\n const c = value as Record<string, unknown>;\n return (\n typeof c.u === 'string' &&\n typeof c.p === 'string' &&\n typeof c.f === 'string' &&\n typeof c.s === 'string' &&\n typeof c.m === 'string' &&\n Array.isArray(c.c) &&\n c.c.every((line) => typeof line === 'string')\n );\n}\n\n/** Returns the expanded SDP unchanged when `value` is already a string. */\nexport function restoreSdp(value: unknown): string {\n if (typeof value === 'string') return value;\n if (isCompactSdp(value)) return expandSdp(value);\n throw new MeshPairingError('Malformed pairing payload');\n}\n\nexport function compactSdp(sdp: string): CompactSdp | null {\n let ufrag = '';\n let pwd = '';\n let fingerprint = '';\n let setup = '';\n let mid = '0';\n let sctpPort: number | undefined;\n let maxMessageSize: number | undefined;\n const candidates: string[] = [];\n let mLines = 0;\n\n for (const line of sdp.split(/\\r?\\n/)) {\n if (line.startsWith('m=')) mLines += 1;\n else if (line.startsWith('a=ice-ufrag:')) ufrag = line.slice(12);\n else if (line.startsWith('a=ice-pwd:')) pwd = line.slice(10);\n else if (line.startsWith('a=fingerprint:sha-256 ')) fingerprint = line.slice(22).replaceAll(':', '').toUpperCase();\n else if (line.startsWith('a=setup:')) setup = line.slice(8);\n else if (line.startsWith('a=mid:')) mid = line.slice(6);\n else if (line.startsWith('a=sctp-port:')) sctpPort = Number(line.slice(12));\n else if (line.startsWith('a=max-message-size:')) maxMessageSize = Number(line.slice(19));\n else if (line.startsWith('a=candidate:')) candidates.push(line.slice(12));\n }\n\n if (mLines !== 1 || !ufrag || !pwd || !fingerprint) return null;\n return {\n c: candidates,\n f: fingerprint,\n m: mid,\n p: pwd,\n s: setup || 'actpass',\n ...(maxMessageSize ? { mm: maxMessageSize } : {}),\n ...(sctpPort ? { sp: sctpPort } : {}),\n u: ufrag,\n };\n}\n\n/**\n * Canonical signing form: the compact serialization when the SDP is\n * recognizable, else the raw text. `compactSdp` is lossless over the fields\n * it keeps, so the rebuilt `mq2.` SDP canonicalizes identically to the\n * original: a proof computed over one verifies against the other.\n */\nexport function canonicalSdp(sdp: string): string {\n const compact = compactSdp(sdp);\n return compact ? JSON.stringify(compact) : sdp;\n}\n\nexport function expandSdp(compact: CompactSdp): string {\n const colonHex = compact.f.replace(/(..)/g, '$1:').slice(0, -1);\n return `${[\n 'v=0',\n 'o=- 0 2 IN IP4 0.0.0.0',\n 's=-',\n 't=0 0',\n `a=group:BUNDLE ${compact.m}`,\n 'm=application 9 UDP/DTLS/SCTP webrtc-datachannel',\n 'c=IN IP4 0.0.0.0',\n `a=ice-ufrag:${compact.u}`,\n `a=ice-pwd:${compact.p}`,\n `a=fingerprint:sha-256 ${colonHex}`,\n `a=setup:${compact.s}`,\n `a=mid:${compact.m}`,\n `a=sctp-port:${compact.sp ?? 5000}`,\n ...(compact.mm ? [`a=max-message-size:${compact.mm}`] : []),\n ...compact.c.map((line) => `a=candidate:${line}`),\n '',\n ].join('\\r\\n')}`;\n}\n"],"mappings":";;AAgCA,SAAS,aAAa,GAAqC;CACzD,IAAI,OAAOA,KAAU,aAAYA,GAAgB,OAAO;CACxD,IAAMC,IAAID;CACV,OACE,OAAOC,EAAE,KAAM,YACf,OAAOA,EAAE,KAAM,YACf,OAAOA,EAAE,KAAM,YACf,OAAOA,EAAE,KAAM,YACf,OAAOA,EAAE,KAAM,YACf,MAAM,QAAQA,EAAE,CAAC,KACjBA,EAAE,EAAE,OAAO,MAAS,OAAOC,KAAS,QAAQ;AAEhD;AAGA,SAAgB,WAAW,GAAwB;CACjD,IAAI,OAAOF,KAAU,UAAU,OAAOA;CACtC,IAAI,aAAaA,CAAK,GAAG,OAAO,UAAUA,CAAK;CAC/C,MAAM,IAAIG,EAAiB,2BAA2B;AACxD;AAEA,SAAgB,WAAW,GAAgC;CACzD,IAAIC,IAAQ,IACRC,IAAM,IACNC,IAAc,IACdC,IAAQ,IACRC,IAAM,KACNC,GACAC,GACEC,IAAuB,CAAC,GAC1BC,IAAS;CAEb,KAAK,IAAMV,KAAQW,EAAI,MAAM,OAAO,GAClC,AAAIX,EAAK,WAAW,IAAI,IAAG,KAAU,IAC5BA,EAAK,WAAW,cAAc,IAAG,IAAQA,EAAK,MAAM,EAAE,IACtDA,EAAK,WAAW,YAAY,IAAG,IAAMA,EAAK,MAAM,EAAE,IAClDA,EAAK,WAAW,wBAAwB,IAAG,IAAcA,EAAK,MAAM,EAAE,CAAC,CAAC,WAAW,KAAK,EAAE,CAAC,CAAC,YAAY,IACxGA,EAAK,WAAW,UAAU,IAAG,IAAQA,EAAK,MAAM,CAAC,IACjDA,EAAK,WAAW,QAAQ,IAAG,IAAMA,EAAK,MAAM,CAAC,IAC7CA,EAAK,WAAW,cAAc,IAAG,IAAW,OAAOA,EAAK,MAAM,EAAE,CAAC,IACjEA,EAAK,WAAW,qBAAqB,IAAG,IAAiB,OAAOA,EAAK,MAAM,EAAE,CAAC,IAC9EA,EAAK,WAAW,cAAc,KAAGS,EAAW,KAAKT,EAAK,MAAM,EAAE,CAAC;CAI1E,OADIU,MAAW,KAAK,CAACR,KAAS,CAACC,KAAO,CAACC,IAAoB,OACpD;EACFK;EACH,GAAGL;EACH,GAAGE;EACH,GAAGH;EACH,GAAGE,KAAS;EACZ,GAAIG,IAAiB,EAAE,IAAIA,EAAe,IAAI,CAAC;EAC/C,GAAID,IAAW,EAAE,IAAIA,EAAS,IAAI,CAAC;EACnC,GAAGL;CACL;AACF;AAQA,SAAgB,aAAa,GAAqB;CAChD,IAAMU,IAAU,WAAWD,CAAG;CAC9B,OAAOC,IAAU,KAAK,UAAUA,CAAO,IAAID;AAC7C;AAEA,SAAgB,UAAU,GAA6B;CACrD,IAAME,IAAWD,EAAQ,EAAE,QAAQ,SAAS,KAAK,CAAC,CAAC,MAAM,GAAG,EAAE;CAC9D,OAAO,GAAG;EACR;EACA;EACA;EACA;EACA,kBAAkBA,EAAQ;EAC1B;EACA;EACA,eAAeA,EAAQ;EACvB,aAAaA,EAAQ;EACrB,yBAAyBC;EACzB,WAAWD,EAAQ;EACnB,SAASA,EAAQ;EACjB,eAAeA,EAAQ,MAAM;EAC7B,GAAIA,EAAQ,KAAK,CAAC,sBAAsBA,EAAQ,IAAI,IAAI,CAAC;EACzD,GAAGA,EAAQ,EAAE,KAAK,MAAS,eAAeZ,GAAM;EAChD;CACF,CAAC,CAAC,KAAK,MAAM;AACf"}
|
package/dist/_wire.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_wire.cjs","names":[],"sources":["../src/_wire.ts"],"sourcesContent":["/** Wire envelope serialized through the configured `serialize` function. */\nexport interface WireEnvelope {\n readonly id: string;\n readonly p: unknown;\n readonly t: string;\n readonly ts: number;\n readonly v: 1;\n}\n\n/** Returns the envelope, or `null` when the value violates the wire shape. */\nexport function toEnvelope(value: unknown): WireEnvelope | null {\n if (typeof value !== 'object' || value === null) return null;\n const env = value as Record<string, unknown>;\n if (env.v !== 1 || typeof env.id !== 'string' || typeof env.t !== 'string' || typeof env.ts !== 'number') return null;\n return value as WireEnvelope;\n}\n\n/** Per-peer duplicate guard: remembers the last `capacity` message ids (LRU). */\nexport class MessageDedupe {\n private readonly seen = new Set<string>();\n private readonly order: string[] = [];\n private readonly capacity: number;\n\n constructor(capacity = 256) {\n this.capacity = capacity;\n }\n\n /** `true` when the id is new (and records it); `false` for duplicates. */\n check(id: string): boolean {\n if (this.seen.has(id)) return false;\n this.seen.add(id);\n this.order.push(id);\n if (this.order.length > this.capacity) this.seen.delete(this.order.shift()!);\n return true;\n }\n}\n"],"mappings":"AAUA,SAAgB,WAAW,EAAqC,CAC9D,GAAI,
|
|
1
|
+
{"version":3,"file":"_wire.cjs","names":["value","env","capacity","id"],"sources":["../src/_wire.ts"],"sourcesContent":["/** Wire envelope serialized through the configured `serialize` function. */\nexport interface WireEnvelope {\n readonly id: string;\n readonly p: unknown;\n readonly t: string;\n readonly ts: number;\n readonly v: 1;\n}\n\n/** Returns the envelope, or `null` when the value violates the wire shape. */\nexport function toEnvelope(value: unknown): WireEnvelope | null {\n if (typeof value !== 'object' || value === null) return null;\n const env = value as Record<string, unknown>;\n if (env.v !== 1 || typeof env.id !== 'string' || typeof env.t !== 'string' || typeof env.ts !== 'number') return null;\n return value as WireEnvelope;\n}\n\n/** Per-peer duplicate guard: remembers the last `capacity` message ids (LRU). */\nexport class MessageDedupe {\n private readonly seen = new Set<string>();\n private readonly order: string[] = [];\n private readonly capacity: number;\n\n constructor(capacity = 256) {\n this.capacity = capacity;\n }\n\n /** `true` when the id is new (and records it); `false` for duplicates. */\n check(id: string): boolean {\n if (this.seen.has(id)) return false;\n this.seen.add(id);\n this.order.push(id);\n if (this.order.length > this.capacity) this.seen.delete(this.order.shift()!);\n return true;\n }\n}\n"],"mappings":"AAUA,SAAgB,WAAW,EAAqC,CAC9D,GAAI,OAAOA,GAAU,WAAYA,EAAgB,OAAO,KACxD,IAAMC,EAAMD,EAEZ,OADIC,EAAI,IAAM,GAAK,OAAOA,EAAI,IAAO,UAAY,OAAOA,EAAI,GAAM,UAAY,OAAOA,EAAI,IAAO,SAAiB,KAC1GD,CACT,CAGA,IAAa,cAAb,KAA2B,CACzB,KAAwB,IAAI,IAC5B,MAAmC,CAAC,EACpC,SAEA,YAAY,EAAW,IAAK,CAC1B,KAAK,SAAWE,CAClB,CAGA,MAAM,EAAqB,CAKzB,MAJA,CAAI,KAAK,KAAK,IAAIC,CAAE,IACpB,KAAK,KAAK,IAAIA,CAAE,EAChB,KAAK,MAAM,KAAKA,CAAE,EACd,KAAK,MAAM,OAAS,KAAK,UAAU,KAAK,KAAK,OAAO,KAAK,MAAM,MAAM,CAAE,EACpE,GACT,CACF,E"}
|
package/dist/_wire.js
CHANGED
|
@@ -1,2 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
//#region src/_wire.ts
|
|
2
|
+
function toEnvelope(e) {
|
|
3
|
+
if (typeof e != "object" || !e) return null;
|
|
4
|
+
let t = e;
|
|
5
|
+
return t.v !== 1 || typeof t.id != "string" || typeof t.t != "string" || typeof t.ts != "number" ? null : e;
|
|
6
|
+
}
|
|
7
|
+
var MessageDedupe = class {
|
|
8
|
+
seen = /* @__PURE__ */ new Set();
|
|
9
|
+
order = [];
|
|
10
|
+
capacity;
|
|
11
|
+
constructor(e = 256) {
|
|
12
|
+
this.capacity = e;
|
|
13
|
+
}
|
|
14
|
+
check(e) {
|
|
15
|
+
return !this.seen.has(e) && (this.seen.add(e), this.order.push(e), this.order.length > this.capacity && this.seen.delete(this.order.shift()), !0);
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
//#endregion
|
|
19
|
+
export { MessageDedupe, toEnvelope };
|
|
20
|
+
|
|
2
21
|
//# sourceMappingURL=_wire.js.map
|
package/dist/_wire.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_wire.js","names":[],"sources":["../src/_wire.ts"],"sourcesContent":["/** Wire envelope serialized through the configured `serialize` function. */\nexport interface WireEnvelope {\n readonly id: string;\n readonly p: unknown;\n readonly t: string;\n readonly ts: number;\n readonly v: 1;\n}\n\n/** Returns the envelope, or `null` when the value violates the wire shape. */\nexport function toEnvelope(value: unknown): WireEnvelope | null {\n if (typeof value !== 'object' || value === null) return null;\n const env = value as Record<string, unknown>;\n if (env.v !== 1 || typeof env.id !== 'string' || typeof env.t !== 'string' || typeof env.ts !== 'number') return null;\n return value as WireEnvelope;\n}\n\n/** Per-peer duplicate guard: remembers the last `capacity` message ids (LRU). */\nexport class MessageDedupe {\n private readonly seen = new Set<string>();\n private readonly order: string[] = [];\n private readonly capacity: number;\n\n constructor(capacity = 256) {\n this.capacity = capacity;\n }\n\n /** `true` when the id is new (and records it); `false` for duplicates. */\n check(id: string): boolean {\n if (this.seen.has(id)) return false;\n this.seen.add(id);\n this.order.push(id);\n if (this.order.length > this.capacity) this.seen.delete(this.order.shift()!);\n return true;\n }\n}\n"],"mappings":"AAUA,SAAgB,WAAW,
|
|
1
|
+
{"version":3,"file":"_wire.js","names":["value","env","capacity","id"],"sources":["../src/_wire.ts"],"sourcesContent":["/** Wire envelope serialized through the configured `serialize` function. */\nexport interface WireEnvelope {\n readonly id: string;\n readonly p: unknown;\n readonly t: string;\n readonly ts: number;\n readonly v: 1;\n}\n\n/** Returns the envelope, or `null` when the value violates the wire shape. */\nexport function toEnvelope(value: unknown): WireEnvelope | null {\n if (typeof value !== 'object' || value === null) return null;\n const env = value as Record<string, unknown>;\n if (env.v !== 1 || typeof env.id !== 'string' || typeof env.t !== 'string' || typeof env.ts !== 'number') return null;\n return value as WireEnvelope;\n}\n\n/** Per-peer duplicate guard: remembers the last `capacity` message ids (LRU). */\nexport class MessageDedupe {\n private readonly seen = new Set<string>();\n private readonly order: string[] = [];\n private readonly capacity: number;\n\n constructor(capacity = 256) {\n this.capacity = capacity;\n }\n\n /** `true` when the id is new (and records it); `false` for duplicates. */\n check(id: string): boolean {\n if (this.seen.has(id)) return false;\n this.seen.add(id);\n this.order.push(id);\n if (this.order.length > this.capacity) this.seen.delete(this.order.shift()!);\n return true;\n }\n}\n"],"mappings":";AAUA,SAAgB,WAAW,GAAqC;CAC9D,IAAI,OAAOA,KAAU,aAAYA,GAAgB,OAAO;CACxD,IAAMC,IAAMD;CAEZ,OADIC,EAAI,MAAM,KAAK,OAAOA,EAAI,MAAO,YAAY,OAAOA,EAAI,KAAM,YAAY,OAAOA,EAAI,MAAO,WAAiB,OAC1GD;AACT;AAGA,IAAa,gBAAb,MAA2B;CACzB,uBAAwB,IAAI,IAAY;CACxC,QAAmC,CAAC;CACpC;CAEA,YAAY,IAAW,KAAK;EAC1B,KAAK,WAAWE;CAClB;CAGA,MAAM,GAAqB;EAKzB,OAJA,CAAI,KAAK,KAAK,IAAIC,CAAE,MACpB,KAAK,KAAK,IAAIA,CAAE,GAChB,KAAK,MAAM,KAAKA,CAAE,GACd,KAAK,MAAM,SAAS,KAAK,YAAU,KAAK,KAAK,OAAO,KAAK,MAAM,MAAM,CAAE,GACpE;CACT;AACF"}
|
package/dist/codec.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codec.cjs","names":[],"sources":["../src/codec.ts"],"sourcesContent":["import { base64UrlToText, textToBase64Url } from '@vielzeug/arsenal';\nimport { MeshPairingError } from './errors';\nimport type { MeshAnswer, MeshInvitation } from './types';\n\n/** Structural guard shared by the plain and QR pairing codecs. */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction isInvitation(value: Record<string, unknown>): value is Record<string, unknown> & MeshInvitation {\n return (\n value.v === 1 &&\n typeof value.sessionId === 'string' &&\n typeof value.secret === 'string' &&\n typeof value.expiresAt === 'number' &&\n typeof value.sdp === 'string'\n );\n}\n\nfunction isAnswer(value: Record<string, unknown>): value is Record<string, unknown> & MeshAnswer {\n return (\n value.v === 1 &&\n typeof value.sessionId === 'string' &&\n typeof value.proof === 'string' &&\n isRecord(value.peer) &&\n typeof value.peer.id === 'string' &&\n value.peer.id !== '' &&\n (value.peer.name === undefined || typeof value.peer.name === 'string') &&\n typeof value.sdp === 'string'\n );\n}\n\n/**\n * Validate a decoded pairing payload. Shared by `meshCodec` and `meshQrCodec`\n * so both encodings enforce the same shape/version contract.\n *\n * @throws {MeshPairingError} On unsupported version or unrecognized shape.\n */\nexport function parsePairingPayload(parsed: unknown): MeshInvitation | MeshAnswer {\n if (!isRecord(parsed) || parsed.v !== 1) {\n throw new MeshPairingError('Unsupported pairing payload version');\n }\n if (isInvitation(parsed) || isAnswer(parsed)) {\n return parsed;\n }\n throw new MeshPairingError('Unrecognized pairing payload shape');\n}\n\n/**\n * Compact JSON → base64url codec for pairing payloads. Pure and transport-free\n * so alternative encodings (compressed, QR-optimized) can be added later\n * without touching the transport.\n *\n * @example\n * ```ts\n * const invitation = await host.createInvitation();\n * const text = meshCodec.encode(invitation); // copy/paste or navigator.share\n * const answer = await guest.acceptInvitation(meshCodec.decode(text));\n * await host.acceptAnswer(meshCodec.decode(meshCodec.encode(answer)));\n * ```\n *\n * @throws {MeshPairingError} On malformed text or unsupported payload shape/version.\n */\nexport const meshCodec = {\n decode(text: string): MeshInvitation | MeshAnswer {\n let parsed: unknown;\n try {\n parsed = JSON.parse(base64UrlToText(text.trim()));\n } catch (cause) {\n throw new MeshPairingError('Malformed pairing payload', { cause });\n }\n return parsePairingPayload(parsed);\n },\n encode(payload: MeshInvitation | MeshAnswer): string {\n return textToBase64Url(JSON.stringify(payload));\n },\n};\n"],"mappings":"mEAKA,SAAgB,SAAS,EAAkD,CACzE,OAAO,
|
|
1
|
+
{"version":3,"file":"codec.cjs","names":["MeshPairingError","base64UrlToText","textToBase64Url","require_errors","_vielzeug_arsenal","value","parsed","meshCodec","text","cause","payload"],"sources":["../src/codec.ts"],"sourcesContent":["import { base64UrlToText, textToBase64Url } from '@vielzeug/arsenal';\nimport { MeshPairingError } from './errors';\nimport type { MeshAnswer, MeshInvitation } from './types';\n\n/** Structural guard shared by the plain and QR pairing codecs. */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction isInvitation(value: Record<string, unknown>): value is Record<string, unknown> & MeshInvitation {\n return (\n value.v === 1 &&\n typeof value.sessionId === 'string' &&\n typeof value.secret === 'string' &&\n typeof value.expiresAt === 'number' &&\n typeof value.sdp === 'string'\n );\n}\n\nfunction isAnswer(value: Record<string, unknown>): value is Record<string, unknown> & MeshAnswer {\n return (\n value.v === 1 &&\n typeof value.sessionId === 'string' &&\n typeof value.proof === 'string' &&\n isRecord(value.peer) &&\n typeof value.peer.id === 'string' &&\n value.peer.id !== '' &&\n (value.peer.name === undefined || typeof value.peer.name === 'string') &&\n typeof value.sdp === 'string'\n );\n}\n\n/**\n * Validate a decoded pairing payload. Shared by `meshCodec` and `meshQrCodec`\n * so both encodings enforce the same shape/version contract.\n *\n * @throws {MeshPairingError} On unsupported version or unrecognized shape.\n */\nexport function parsePairingPayload(parsed: unknown): MeshInvitation | MeshAnswer {\n if (!isRecord(parsed) || parsed.v !== 1) {\n throw new MeshPairingError('Unsupported pairing payload version');\n }\n if (isInvitation(parsed) || isAnswer(parsed)) {\n return parsed;\n }\n throw new MeshPairingError('Unrecognized pairing payload shape');\n}\n\n/**\n * Compact JSON → base64url codec for pairing payloads. Pure and transport-free\n * so alternative encodings (compressed, QR-optimized) can be added later\n * without touching the transport.\n *\n * @example\n * ```ts\n * const invitation = await host.createInvitation();\n * const text = meshCodec.encode(invitation); // copy/paste or navigator.share\n * const answer = await guest.acceptInvitation(meshCodec.decode(text));\n * await host.acceptAnswer(meshCodec.decode(meshCodec.encode(answer)));\n * ```\n *\n * @throws {MeshPairingError} On malformed text or unsupported payload shape/version.\n */\nexport const meshCodec = {\n decode(text: string): MeshInvitation | MeshAnswer {\n let parsed: unknown;\n try {\n parsed = JSON.parse(base64UrlToText(text.trim()));\n } catch (cause) {\n throw new MeshPairingError('Malformed pairing payload', { cause });\n }\n return parsePairingPayload(parsed);\n },\n encode(payload: MeshInvitation | MeshAnswer): string {\n return textToBase64Url(JSON.stringify(payload));\n },\n};\n"],"mappings":"A,mEAKA,SAAgB,SAAS,EAAkD,CACzE,OAAO,OAAOK,GAAU,YAAYA,CACtC,CAEA,SAAS,aAAa,EAAmF,CACvG,OACEA,EAAM,IAAM,GACZ,OAAOA,EAAM,WAAc,UAC3B,OAAOA,EAAM,QAAW,UACxB,OAAOA,EAAM,WAAc,UAC3B,OAAOA,EAAM,KAAQ,QAEzB,CAEA,SAAS,SAAS,EAA+E,CAC/F,OACEA,EAAM,IAAM,GACZ,OAAOA,EAAM,WAAc,UAC3B,OAAOA,EAAM,OAAU,UACvB,SAASA,EAAM,IAAI,GACnB,OAAOA,EAAM,KAAK,IAAO,UACzBA,EAAM,KAAK,KAAO,KACjBA,EAAM,KAAK,OAAS,IAAA,IAAa,OAAOA,EAAM,KAAK,MAAS,WAC7D,OAAOA,EAAM,KAAQ,QAEzB,CAQA,SAAgB,oBAAoB,EAA8C,CAChF,GAAI,CAAC,SAASC,CAAM,GAAKA,EAAO,IAAM,EACpC,MAAM,IAAIN,EAAAA,iBAAiB,qCAAqC,EAElE,GAAI,aAAaM,CAAM,GAAK,SAASA,CAAM,EACzC,OAAOA,EAET,MAAM,IAAIN,EAAAA,iBAAiB,oCAAoC,CACjE,CAiBA,IAAaO,EAAY,CACvB,OAAO,EAA2C,CAChD,IAAID,EACJ,GAAI,CACF,EAAS,KAAK,OAAA,EAAML,EAAAA,gBAAAA,CAAgBO,EAAK,KAAK,CAAC,CAAC,CAClD,OAASC,EAAO,CACd,MAAM,IAAIT,EAAAA,iBAAiB,4BAA6B,CAAE,OAAM,CAAC,CACnE,CACA,OAAO,oBAAoBM,CAAM,CACnC,EACA,OAAO,EAA8C,CACnD,OAAA,EAAOJ,EAAAA,gBAAAA,CAAgB,KAAK,UAAUQ,CAAO,CAAC,CAChD,CACF,E"}
|
package/dist/codec.js
CHANGED
|
@@ -1,2 +1,35 @@
|
|
|
1
|
-
import{MeshPairingError as e}from"./errors.js";
|
|
1
|
+
import { MeshPairingError as e } from "./errors.js";
|
|
2
|
+
import { base64UrlToText as t, textToBase64Url as n } from "@vielzeug/arsenal";
|
|
3
|
+
//#region src/codec.ts
|
|
4
|
+
function isRecord(e) {
|
|
5
|
+
return typeof e == "object" && !!e;
|
|
6
|
+
}
|
|
7
|
+
function isInvitation(e) {
|
|
8
|
+
return e.v === 1 && typeof e.sessionId == "string" && typeof e.secret == "string" && typeof e.expiresAt == "number" && typeof e.sdp == "string";
|
|
9
|
+
}
|
|
10
|
+
function isAnswer(e) {
|
|
11
|
+
return e.v === 1 && typeof e.sessionId == "string" && typeof e.proof == "string" && isRecord(e.peer) && typeof e.peer.id == "string" && e.peer.id !== "" && (e.peer.name === void 0 || typeof e.peer.name == "string") && typeof e.sdp == "string";
|
|
12
|
+
}
|
|
13
|
+
function parsePairingPayload(t) {
|
|
14
|
+
if (!isRecord(t) || t.v !== 1) throw new e("Unsupported pairing payload version");
|
|
15
|
+
if (isInvitation(t) || isAnswer(t)) return t;
|
|
16
|
+
throw new e("Unrecognized pairing payload shape");
|
|
17
|
+
}
|
|
18
|
+
var r = {
|
|
19
|
+
decode(n) {
|
|
20
|
+
let r;
|
|
21
|
+
try {
|
|
22
|
+
r = JSON.parse(t(n.trim()));
|
|
23
|
+
} catch (t) {
|
|
24
|
+
throw new e("Malformed pairing payload", { cause: t });
|
|
25
|
+
}
|
|
26
|
+
return parsePairingPayload(r);
|
|
27
|
+
},
|
|
28
|
+
encode(e) {
|
|
29
|
+
return n(JSON.stringify(e));
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
//#endregion
|
|
33
|
+
export { isRecord, r as meshCodec, parsePairingPayload };
|
|
34
|
+
|
|
2
35
|
//# sourceMappingURL=codec.js.map
|
package/dist/codec.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codec.js","names":[],"sources":["../src/codec.ts"],"sourcesContent":["import { base64UrlToText, textToBase64Url } from '@vielzeug/arsenal';\nimport { MeshPairingError } from './errors';\nimport type { MeshAnswer, MeshInvitation } from './types';\n\n/** Structural guard shared by the plain and QR pairing codecs. */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction isInvitation(value: Record<string, unknown>): value is Record<string, unknown> & MeshInvitation {\n return (\n value.v === 1 &&\n typeof value.sessionId === 'string' &&\n typeof value.secret === 'string' &&\n typeof value.expiresAt === 'number' &&\n typeof value.sdp === 'string'\n );\n}\n\nfunction isAnswer(value: Record<string, unknown>): value is Record<string, unknown> & MeshAnswer {\n return (\n value.v === 1 &&\n typeof value.sessionId === 'string' &&\n typeof value.proof === 'string' &&\n isRecord(value.peer) &&\n typeof value.peer.id === 'string' &&\n value.peer.id !== '' &&\n (value.peer.name === undefined || typeof value.peer.name === 'string') &&\n typeof value.sdp === 'string'\n );\n}\n\n/**\n * Validate a decoded pairing payload. Shared by `meshCodec` and `meshQrCodec`\n * so both encodings enforce the same shape/version contract.\n *\n * @throws {MeshPairingError} On unsupported version or unrecognized shape.\n */\nexport function parsePairingPayload(parsed: unknown): MeshInvitation | MeshAnswer {\n if (!isRecord(parsed) || parsed.v !== 1) {\n throw new MeshPairingError('Unsupported pairing payload version');\n }\n if (isInvitation(parsed) || isAnswer(parsed)) {\n return parsed;\n }\n throw new MeshPairingError('Unrecognized pairing payload shape');\n}\n\n/**\n * Compact JSON → base64url codec for pairing payloads. Pure and transport-free\n * so alternative encodings (compressed, QR-optimized) can be added later\n * without touching the transport.\n *\n * @example\n * ```ts\n * const invitation = await host.createInvitation();\n * const text = meshCodec.encode(invitation); // copy/paste or navigator.share\n * const answer = await guest.acceptInvitation(meshCodec.decode(text));\n * await host.acceptAnswer(meshCodec.decode(meshCodec.encode(answer)));\n * ```\n *\n * @throws {MeshPairingError} On malformed text or unsupported payload shape/version.\n */\nexport const meshCodec = {\n decode(text: string): MeshInvitation | MeshAnswer {\n let parsed: unknown;\n try {\n parsed = JSON.parse(base64UrlToText(text.trim()));\n } catch (cause) {\n throw new MeshPairingError('Malformed pairing payload', { cause });\n }\n return parsePairingPayload(parsed);\n },\n encode(payload: MeshInvitation | MeshAnswer): string {\n return textToBase64Url(JSON.stringify(payload));\n },\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"codec.js","names":["value","parsed","MeshPairingError","meshCodec","base64UrlToText","text","cause","textToBase64Url","payload"],"sources":["../src/codec.ts"],"sourcesContent":["import { base64UrlToText, textToBase64Url } from '@vielzeug/arsenal';\nimport { MeshPairingError } from './errors';\nimport type { MeshAnswer, MeshInvitation } from './types';\n\n/** Structural guard shared by the plain and QR pairing codecs. */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction isInvitation(value: Record<string, unknown>): value is Record<string, unknown> & MeshInvitation {\n return (\n value.v === 1 &&\n typeof value.sessionId === 'string' &&\n typeof value.secret === 'string' &&\n typeof value.expiresAt === 'number' &&\n typeof value.sdp === 'string'\n );\n}\n\nfunction isAnswer(value: Record<string, unknown>): value is Record<string, unknown> & MeshAnswer {\n return (\n value.v === 1 &&\n typeof value.sessionId === 'string' &&\n typeof value.proof === 'string' &&\n isRecord(value.peer) &&\n typeof value.peer.id === 'string' &&\n value.peer.id !== '' &&\n (value.peer.name === undefined || typeof value.peer.name === 'string') &&\n typeof value.sdp === 'string'\n );\n}\n\n/**\n * Validate a decoded pairing payload. Shared by `meshCodec` and `meshQrCodec`\n * so both encodings enforce the same shape/version contract.\n *\n * @throws {MeshPairingError} On unsupported version or unrecognized shape.\n */\nexport function parsePairingPayload(parsed: unknown): MeshInvitation | MeshAnswer {\n if (!isRecord(parsed) || parsed.v !== 1) {\n throw new MeshPairingError('Unsupported pairing payload version');\n }\n if (isInvitation(parsed) || isAnswer(parsed)) {\n return parsed;\n }\n throw new MeshPairingError('Unrecognized pairing payload shape');\n}\n\n/**\n * Compact JSON → base64url codec for pairing payloads. Pure and transport-free\n * so alternative encodings (compressed, QR-optimized) can be added later\n * without touching the transport.\n *\n * @example\n * ```ts\n * const invitation = await host.createInvitation();\n * const text = meshCodec.encode(invitation); // copy/paste or navigator.share\n * const answer = await guest.acceptInvitation(meshCodec.decode(text));\n * await host.acceptAnswer(meshCodec.decode(meshCodec.encode(answer)));\n * ```\n *\n * @throws {MeshPairingError} On malformed text or unsupported payload shape/version.\n */\nexport const meshCodec = {\n decode(text: string): MeshInvitation | MeshAnswer {\n let parsed: unknown;\n try {\n parsed = JSON.parse(base64UrlToText(text.trim()));\n } catch (cause) {\n throw new MeshPairingError('Malformed pairing payload', { cause });\n }\n return parsePairingPayload(parsed);\n },\n encode(payload: MeshInvitation | MeshAnswer): string {\n return textToBase64Url(JSON.stringify(payload));\n },\n};\n"],"mappings":";;;AAKA,SAAgB,SAAS,GAAkD;CACzE,OAAO,OAAOA,KAAU,cAAYA;AACtC;AAEA,SAAS,aAAa,GAAmF;CACvG,OACEA,EAAM,MAAM,KACZ,OAAOA,EAAM,aAAc,YAC3B,OAAOA,EAAM,UAAW,YACxB,OAAOA,EAAM,aAAc,YAC3B,OAAOA,EAAM,OAAQ;AAEzB;AAEA,SAAS,SAAS,GAA+E;CAC/F,OACEA,EAAM,MAAM,KACZ,OAAOA,EAAM,aAAc,YAC3B,OAAOA,EAAM,SAAU,YACvB,SAASA,EAAM,IAAI,KACnB,OAAOA,EAAM,KAAK,MAAO,YACzBA,EAAM,KAAK,OAAO,OACjBA,EAAM,KAAK,SAAS,KAAA,KAAa,OAAOA,EAAM,KAAK,QAAS,aAC7D,OAAOA,EAAM,OAAQ;AAEzB;AAQA,SAAgB,oBAAoB,GAA8C;CAChF,IAAI,CAAC,SAASC,CAAM,KAAKA,EAAO,MAAM,GACpC,MAAM,IAAIC,EAAiB,qCAAqC;CAElE,IAAI,aAAaD,CAAM,KAAK,SAASA,CAAM,GACzC,OAAOA;CAET,MAAM,IAAIC,EAAiB,oCAAoC;AACjE;AAiBA,IAAaC,IAAY;CACvB,OAAO,GAA2C;EAChD,IAAIF;EACJ,IAAI;GACF,IAAS,KAAK,MAAMG,EAAgBC,EAAK,KAAK,CAAC,CAAC;EAClD,SAASC,GAAO;GACd,MAAM,IAAIJ,EAAiB,6BAA6B,EAAE,SAAM,CAAC;EACnE;EACA,OAAO,oBAAoBD,CAAM;CACnC;CACA,OAAO,GAA8C;EACnD,OAAOM,EAAgB,KAAK,UAAUC,CAAO,CAAC;CAChD;AACF"}
|
package/dist/errors.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Base class for all mesh errors.\n * Use `instanceof MeshError` to catch any mesh-originated error in one branch.\n */\nexport class MeshError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when a pairing payload is malformed, expired, unknown, already\n * consumed, carries a mismatched proof, or is rejected by the host's\n * `approvePeer` hook.\n */\nexport class MeshPairingError extends MeshError {}\n\n/**\n * Thrown when the peer connection fails (ICE/DTLS failure, channel closed\n * unexpectedly) or a send targets an unknown or unconnected peer.\n */\nexport class MeshConnectionError extends MeshError {\n readonly peerId: string | null;\n\n constructor(message: string, peerId: string | null = null, opts?: ErrorOptions) {\n super(message, opts);\n this.peerId = peerId;\n }\n}\n\n/**\n * Thrown when an outbound message exceeds `maxMessageBytes` or cannot be\n * serialized.\n */\nexport class MeshPayloadError extends MeshError {}\n\n/**\n * Thrown when ICE gathering or the data-channel open handshake exceeds the\n * configured timeout.\n */\nexport class MeshTimeoutError extends MeshError {}\n\n/**\n * Thrown when a method is called on a disposed mesh node.\n */\nexport class MeshDisposedError extends MeshError {\n constructor(target = 'Mesh node', opts?: ErrorOptions) {\n super(`${target} is disposed`, opts);\n }\n}\n\n/**\n * Thrown when WebRTC is unavailable in the current environment and no `rtc`\n * factory was injected. Raised at first use, never at import time.\n */\nexport class MeshUnsupportedError extends MeshError {}\n"],"mappings":"AAIA,IAAa,UAAb,cAA+B,KAAM,CACnC,YAAY,EAAiB,EAAqB,CAChD,
|
|
1
|
+
{"version":3,"file":"errors.cjs","names":["message","opts","peerId","target"],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Base class for all mesh errors.\n * Use `instanceof MeshError` to catch any mesh-originated error in one branch.\n */\nexport class MeshError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when a pairing payload is malformed, expired, unknown, already\n * consumed, carries a mismatched proof, or is rejected by the host's\n * `approvePeer` hook.\n */\nexport class MeshPairingError extends MeshError {}\n\n/**\n * Thrown when the peer connection fails (ICE/DTLS failure, channel closed\n * unexpectedly) or a send targets an unknown or unconnected peer.\n */\nexport class MeshConnectionError extends MeshError {\n readonly peerId: string | null;\n\n constructor(message: string, peerId: string | null = null, opts?: ErrorOptions) {\n super(message, opts);\n this.peerId = peerId;\n }\n}\n\n/**\n * Thrown when an outbound message exceeds `maxMessageBytes` or cannot be\n * serialized.\n */\nexport class MeshPayloadError extends MeshError {}\n\n/**\n * Thrown when ICE gathering or the data-channel open handshake exceeds the\n * configured timeout.\n */\nexport class MeshTimeoutError extends MeshError {}\n\n/**\n * Thrown when a method is called on a disposed mesh node.\n */\nexport class MeshDisposedError extends MeshError {\n constructor(target = 'Mesh node', opts?: ErrorOptions) {\n super(`${target} is disposed`, opts);\n }\n}\n\n/**\n * Thrown when WebRTC is unavailable in the current environment and no `rtc`\n * factory was injected. Raised at first use, never at import time.\n */\nexport class MeshUnsupportedError extends MeshError {}\n"],"mappings":"AAIA,IAAa,UAAb,cAA+B,KAAM,CACnC,YAAY,EAAiB,EAAqB,CAChD,MAAMA,EAASC,CAAI,EACnB,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAOa,iBAAb,cAAsC,SAAU,CAAC,EAMpC,oBAAb,cAAyC,SAAU,CACjD,OAEA,YAAY,EAAiB,EAAwB,KAAM,EAAqB,CAC9E,MAAMD,EAASC,CAAI,EACnB,KAAK,OAASC,CAChB,CACF,EAMa,iBAAb,cAAsC,SAAU,CAAC,EAMpC,iBAAb,cAAsC,SAAU,CAAC,EAKpC,kBAAb,cAAuC,SAAU,CAC/C,YAAY,EAAS,YAAa,EAAqB,CACrD,MAAM,GAAGC,EAAO,cAAeF,CAAI,CACrC,CACF,EAMa,qBAAb,cAA0C,SAAU,CAAC,E"}
|
package/dist/errors.js
CHANGED
|
@@ -1,2 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
//#region src/errors.ts
|
|
2
|
+
var MeshError = class extends Error {
|
|
3
|
+
constructor(e, t) {
|
|
4
|
+
super(e, t), this.name = new.target.name, Object.setPrototypeOf(this, new.target.prototype);
|
|
5
|
+
}
|
|
6
|
+
}, MeshPairingError = class extends MeshError {}, MeshConnectionError = class extends MeshError {
|
|
7
|
+
peerId;
|
|
8
|
+
constructor(e, t = null, n) {
|
|
9
|
+
super(e, n), this.peerId = t;
|
|
10
|
+
}
|
|
11
|
+
}, MeshPayloadError = class extends MeshError {}, MeshTimeoutError = class extends MeshError {}, MeshDisposedError = class extends MeshError {
|
|
12
|
+
constructor(e = "Mesh node", t) {
|
|
13
|
+
super(`${e} is disposed`, t);
|
|
14
|
+
}
|
|
15
|
+
}, MeshUnsupportedError = class extends MeshError {};
|
|
16
|
+
//#endregion
|
|
17
|
+
export { MeshConnectionError, MeshDisposedError, MeshError, MeshPairingError, MeshPayloadError, MeshTimeoutError, MeshUnsupportedError };
|
|
18
|
+
|
|
2
19
|
//# sourceMappingURL=errors.js.map
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Base class for all mesh errors.\n * Use `instanceof MeshError` to catch any mesh-originated error in one branch.\n */\nexport class MeshError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when a pairing payload is malformed, expired, unknown, already\n * consumed, carries a mismatched proof, or is rejected by the host's\n * `approvePeer` hook.\n */\nexport class MeshPairingError extends MeshError {}\n\n/**\n * Thrown when the peer connection fails (ICE/DTLS failure, channel closed\n * unexpectedly) or a send targets an unknown or unconnected peer.\n */\nexport class MeshConnectionError extends MeshError {\n readonly peerId: string | null;\n\n constructor(message: string, peerId: string | null = null, opts?: ErrorOptions) {\n super(message, opts);\n this.peerId = peerId;\n }\n}\n\n/**\n * Thrown when an outbound message exceeds `maxMessageBytes` or cannot be\n * serialized.\n */\nexport class MeshPayloadError extends MeshError {}\n\n/**\n * Thrown when ICE gathering or the data-channel open handshake exceeds the\n * configured timeout.\n */\nexport class MeshTimeoutError extends MeshError {}\n\n/**\n * Thrown when a method is called on a disposed mesh node.\n */\nexport class MeshDisposedError extends MeshError {\n constructor(target = 'Mesh node', opts?: ErrorOptions) {\n super(`${target} is disposed`, opts);\n }\n}\n\n/**\n * Thrown when WebRTC is unavailable in the current environment and no `rtc`\n * factory was injected. Raised at first use, never at import time.\n */\nexport class MeshUnsupportedError extends MeshError {}\n"],"mappings":"AAIA,IAAa,
|
|
1
|
+
{"version":3,"file":"errors.js","names":["message","opts","peerId","target"],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Base class for all mesh errors.\n * Use `instanceof MeshError` to catch any mesh-originated error in one branch.\n */\nexport class MeshError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/**\n * Thrown when a pairing payload is malformed, expired, unknown, already\n * consumed, carries a mismatched proof, or is rejected by the host's\n * `approvePeer` hook.\n */\nexport class MeshPairingError extends MeshError {}\n\n/**\n * Thrown when the peer connection fails (ICE/DTLS failure, channel closed\n * unexpectedly) or a send targets an unknown or unconnected peer.\n */\nexport class MeshConnectionError extends MeshError {\n readonly peerId: string | null;\n\n constructor(message: string, peerId: string | null = null, opts?: ErrorOptions) {\n super(message, opts);\n this.peerId = peerId;\n }\n}\n\n/**\n * Thrown when an outbound message exceeds `maxMessageBytes` or cannot be\n * serialized.\n */\nexport class MeshPayloadError extends MeshError {}\n\n/**\n * Thrown when ICE gathering or the data-channel open handshake exceeds the\n * configured timeout.\n */\nexport class MeshTimeoutError extends MeshError {}\n\n/**\n * Thrown when a method is called on a disposed mesh node.\n */\nexport class MeshDisposedError extends MeshError {\n constructor(target = 'Mesh node', opts?: ErrorOptions) {\n super(`${target} is disposed`, opts);\n }\n}\n\n/**\n * Thrown when WebRTC is unavailable in the current environment and no `rtc`\n * factory was injected. Raised at first use, never at import time.\n */\nexport class MeshUnsupportedError extends MeshError {}\n"],"mappings":";AAIA,IAAa,YAAb,cAA+B,MAAM;CACnC,YAAY,GAAiB,GAAqB;EAGhD,AAFA,MAAMA,GAASC,CAAI,GACnB,KAAK,OAAO,WAAW,MACvB,OAAO,eAAe,MAAM,WAAW,SAAS;CAClD;AACF,GAOa,mBAAb,cAAsC,UAAU,CAAC,GAMpC,sBAAb,cAAyC,UAAU;CACjD;CAEA,YAAY,GAAiB,IAAwB,MAAM,GAAqB;EAE9E,AADA,MAAMD,GAASC,CAAI,GACnB,KAAK,SAASC;CAChB;AACF,GAMa,mBAAb,cAAsC,UAAU,CAAC,GAMpC,mBAAb,cAAsC,UAAU,CAAC,GAKpC,oBAAb,cAAuC,UAAU;CAC/C,YAAY,IAAS,aAAa,GAAqB;EACrD,MAAM,GAAGC,EAAO,eAAeF,CAAI;CACrC;AACF,GAMa,uBAAb,cAA0C,UAAU,CAAC"}
|
package/dist/guest.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guest.cjs","names":[],"sources":["../src/guest.ts"],"sourcesContent":["import {\n DEFAULT_CHANNEL_OPEN_TIMEOUT_MS,\n DEFAULT_ICE_GATHERING_TIMEOUT_MS,\n DEFAULT_MAX_MESSAGE_BYTES,\n} from './_defaults';\nimport { createMessenger, createNodeCore, createPeerRecord, type PeerRecord } from './_node';\nimport { createProof } from './_proof';\nimport { randomId } from './_random';\nimport { nativeRtcFactory, normalizeSdp, waitIceGathering } from './_rtc';\nimport { MeshConnectionError, MeshDisposedError, type MeshError, MeshPairingError, MeshTimeoutError } from './errors';\nimport type {\n MeshGuest,\n MeshGuestOptions,\n MeshInbound,\n MeshPeer,\n MeshProtocol,\n RTCDataChannelLike,\n RTCPeerConnectionLike,\n} from './types';\n\n/**\n * Create a guest mesh node. The guest pairs with one host by consuming an\n * out-of-band invitation and returning an answer.\n */\nexport function createMeshGuest<P extends MeshProtocol>(options: MeshGuestOptions = {}): MeshGuest<P> {\n const rtc = options.rtc ?? nativeRtcFactory;\n const clock = options.clock ?? (() => Date.now());\n const maxMessageBytes = options.maxMessageBytes ?? DEFAULT_MAX_MESSAGE_BYTES;\n const channelOpenTimeoutMs = options.channelOpenTimeoutMs ?? DEFAULT_CHANNEL_OPEN_TIMEOUT_MS;\n const iceGatheringTimeoutMs = options.iceGatheringTimeoutMs ?? DEFAULT_ICE_GATHERING_TIMEOUT_MS;\n\n const core = createNodeCore(options.signal);\n const messenger = createMessenger({\n clock,\n emitTap: core.emitTap,\n maxMessageBytes,\n random: options.random,\n });\n\n let hostPeer: PeerRecord | null = null;\n let openTimer: ReturnType<typeof setTimeout> | null = null;\n\n const openTimeoutError = (): MeshTimeoutError =>\n new MeshTimeoutError(`Timed out waiting ${channelOpenTimeoutMs}ms for the data channel to open`);\n\n function failPeer(peer: PeerRecord, error: MeshError): void {\n // Ignore events from a superseded peer after re-pairing replaced hostPeer.\n if (core.disposed || peer !== hostPeer || peer.status === 'failed') return;\n peer.status = 'failed';\n if (openTimer) {\n clearTimeout(openTimer);\n openTimer = null;\n }\n // Teardown is deferred a macrotask so a peer racing its own open\n // deadline reports its timeout rather than this hangup.\n setTimeout(() => {\n peer.dc?.close();\n peer.pc?.close();\n }, 0);\n core.emitTap({ peerId: peer.id, status: 'failed', type: 'peer-status-change' });\n core.emitTap({ error, type: 'error' });\n core.emitTap({ peer: peer.publicPeer, reason: error.message, type: 'peer-left' });\n core.setStatus('failed');\n }\n\n function disconnectPeer(peer: PeerRecord, reason: string): void {\n if (core.disposed || peer !== hostPeer) return;\n if (peer.status === 'disconnected' || peer.status === 'failed') return;\n peer.status = 'disconnected';\n peer.pc?.close();\n core.emitTap({ peerId: peer.id, status: 'disconnected', type: 'peer-status-change' });\n core.emitTap({ peer: peer.publicPeer, reason, type: 'peer-left' });\n core.setStatus('disconnected');\n }\n\n function wireChannel(peer: PeerRecord, pc: RTCPeerConnectionLike, dc: RTCDataChannelLike): void {\n peer.dc = dc;\n\n dc.addEventListener('open', () => {\n if (core.disposed) return;\n if (openTimer) {\n clearTimeout(openTimer);\n openTimer = null;\n }\n peer.status = 'connected';\n core.emitTap({ peerId: peer.id, status: 'connected', type: 'peer-status-change' });\n core.emitTap({ peer: peer.publicPeer, type: 'peer-joined' });\n core.setStatus('connected');\n });\n dc.addEventListener('message', (event) => {\n if (core.disposed) return;\n messenger.handleInbound(peer, event.data);\n });\n dc.addEventListener('close', () => {\n if (core.disposed) return;\n if (peer.status === 'connecting')\n failPeer(\n peer,\n openTimer ? openTimeoutError() : new MeshConnectionError('channel closed before opening', peer.id),\n );\n else disconnectPeer(peer, 'channel closed');\n });\n dc.addEventListener('error', () => {\n if (core.disposed) return;\n failPeer(peer, new MeshConnectionError('channel error', peer.id));\n });\n\n // The ice-state emission lives on the pairing-scoped listener above; this\n // one owns channel-lifecycle transitions only, so each ICE change emits\n // exactly one ice-state event.\n pc.addEventListener('iceconnectionstatechange', () => {\n if (core.disposed) return;\n const state = pc.iceConnectionState;\n if (state === 'failed' || state === 'closed') {\n failPeer(peer, new MeshConnectionError(`ice ${state}`, peer.id));\n } else if (state === 'disconnected' && peer.status === 'connected') {\n disconnectPeer(peer, 'ice disconnected');\n }\n });\n }\n\n const guest: MeshGuest<P> = {\n async acceptInvitation(invitation, meta) {\n core.ensureLive();\n // A terminal host peer (failed/disconnected) may be replaced by a fresh\n // pairing on the same node: listeners stay attached and keep working.\n if (hostPeer && hostPeer.status !== 'failed' && hostPeer.status !== 'disconnected') {\n throw new MeshPairingError('Guest is already paired');\n }\n if (invitation?.v !== 1) throw new MeshPairingError('Unsupported invitation version');\n if (clock() >= invitation.expiresAt) throw new MeshPairingError('Invitation expired');\n\n if (hostPeer) {\n if (openTimer) {\n clearTimeout(openTimer);\n openTimer = null;\n }\n hostPeer.pc?.close();\n hostPeer = null;\n }\n\n core.setStatus('pairing');\n let pc: RTCPeerConnectionLike;\n try {\n pc = rtc.createPeerConnection({ iceServers: [...(options.iceServers ?? [])] });\n } catch (error) {\n core.setStatus('idle');\n throw error;\n }\n\n // The invitation carries no host identity beyond an optional display\n // name: the session id doubles as the host-side peer id for inbound\n // metadata and tap events.\n const peer = createPeerRecord(invitation.sessionId, invitation.hostName, 'host');\n peer.pc = pc;\n hostPeer = peer;\n\n pc.addEventListener('datachannel', (event) => {\n wireChannel(peer, pc, event.channel);\n });\n pc.addEventListener('iceconnectionstatechange', () => {\n if (core.disposed) return;\n core.emitTap({ peerId: peer.id, state: pc.iceConnectionState, type: 'ice-state' });\n });\n\n try {\n await pc.setRemoteDescription({ sdp: normalizeSdp(invitation.sdp), type: 'offer' });\n } catch (cause) {\n hostPeer = null;\n core.setStatus('idle');\n throw new MeshPairingError('Invalid invitation SDP', { cause });\n }\n\n let answerSdp: string;\n try {\n await pc.setLocalDescription(await pc.createAnswer());\n await waitIceGathering(pc, iceGatheringTimeoutMs);\n answerSdp = pc.localDescription?.sdp ?? '';\n } catch (cause) {\n hostPeer = null;\n pc.close();\n core.setStatus('idle');\n throw new MeshConnectionError('Failed to create answer', null, { cause });\n }\n if (core.disposed) {\n pc.close();\n throw new MeshDisposedError();\n }\n if (!answerSdp) {\n hostPeer = null;\n pc.close();\n core.setStatus('idle');\n throw new MeshConnectionError('Local description has no SDP');\n }\n\n const peerId = randomId(options.random);\n let proof: string;\n try {\n proof = await createProof(invitation.secret, answerSdp);\n } catch (cause) {\n hostPeer = null;\n pc.close();\n core.setStatus('idle');\n throw cause;\n }\n core.setStatus('connecting');\n\n // If the channel never opens (host never accepts the answer), fail loudly\n // instead of hanging in 'connecting' forever. The deadline must cover\n // the human carry-back of the answer, not just machine negotiation.\n openTimer = setTimeout(() => {\n if (hostPeer?.status === 'connecting') {\n failPeer(hostPeer, openTimeoutError());\n }\n }, channelOpenTimeoutMs);\n\n return {\n peer: meta?.name === undefined ? { id: peerId } : { id: peerId, name: meta.name },\n proof,\n sdp: answerSdp,\n sessionId: invitation.sessionId,\n v: 1,\n };\n },\n get disposalSignal() {\n return core.disposalCtrl.signal;\n },\n\n dispose() {\n if (core.disposed) return;\n core.markDisposed();\n if (openTimer) {\n clearTimeout(openTimer);\n openTimer = null;\n }\n if (hostPeer) {\n hostPeer.dc?.close();\n hostPeer.pc?.close();\n }\n messenger.clear();\n },\n get disposed() {\n return core.disposed;\n },\n get host(): MeshPeer | null {\n return hostPeer?.publicPeer ?? null;\n },\n\n on(type, handler) {\n core.ensureLive();\n return messenger.on(type, handler as (message: MeshInbound<unknown>) => void);\n },\n\n send(type, payload) {\n core.ensureLive();\n if (!hostPeer) throw new MeshConnectionError('Guest is not paired');\n messenger.sendTo(hostPeer, type, payload);\n },\n get status() {\n return core.status;\n },\n\n tap(handler, tapOptions) {\n return core.tap(handler, tapOptions);\n },\n\n [Symbol.dispose]() {\n this.dispose();\n },\n };\n\n return guest;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"guest.cjs","names":["nativeRtcFactory","createNodeCore","createMessenger","MeshTimeoutError","MeshConnectionError","MeshPairingError","createPeerRecord","normalizeSdp","waitIceGathering","MeshDisposedError","randomId","createProof","require_errors","require__random","require__node","require__proof","require__rtc","rtc","options","clock","maxMessageBytes","channelOpenTimeoutMs","iceGatheringTimeoutMs","core","messenger","hostPeer","openTimer","peer","error","dc","event","pc","state","invitation","cause","answerSdp","peerId","proof","meta","type","handler","payload","tapOptions"],"sources":["../src/guest.ts"],"sourcesContent":["import {\n DEFAULT_CHANNEL_OPEN_TIMEOUT_MS,\n DEFAULT_ICE_GATHERING_TIMEOUT_MS,\n DEFAULT_MAX_MESSAGE_BYTES,\n} from './_defaults';\nimport { createMessenger, createNodeCore, createPeerRecord, type PeerRecord } from './_node';\nimport { createProof } from './_proof';\nimport { randomId } from './_random';\nimport { nativeRtcFactory, normalizeSdp, waitIceGathering } from './_rtc';\nimport { MeshConnectionError, MeshDisposedError, type MeshError, MeshPairingError, MeshTimeoutError } from './errors';\nimport type {\n MeshGuest,\n MeshGuestOptions,\n MeshInbound,\n MeshPeer,\n MeshProtocol,\n RTCDataChannelLike,\n RTCPeerConnectionLike,\n} from './types';\n\n/**\n * Create a guest mesh node. The guest pairs with one host by consuming an\n * out-of-band invitation and returning an answer.\n */\nexport function createMeshGuest<P extends MeshProtocol>(options: MeshGuestOptions = {}): MeshGuest<P> {\n const rtc = options.rtc ?? nativeRtcFactory;\n const clock = options.clock ?? (() => Date.now());\n const maxMessageBytes = options.maxMessageBytes ?? DEFAULT_MAX_MESSAGE_BYTES;\n const channelOpenTimeoutMs = options.channelOpenTimeoutMs ?? DEFAULT_CHANNEL_OPEN_TIMEOUT_MS;\n const iceGatheringTimeoutMs = options.iceGatheringTimeoutMs ?? DEFAULT_ICE_GATHERING_TIMEOUT_MS;\n\n const core = createNodeCore(options.signal);\n const messenger = createMessenger({\n clock,\n emitTap: core.emitTap,\n maxMessageBytes,\n random: options.random,\n });\n\n let hostPeer: PeerRecord | null = null;\n let openTimer: ReturnType<typeof setTimeout> | null = null;\n\n const openTimeoutError = (): MeshTimeoutError =>\n new MeshTimeoutError(`Timed out waiting ${channelOpenTimeoutMs}ms for the data channel to open`);\n\n function failPeer(peer: PeerRecord, error: MeshError): void {\n // Ignore events from a superseded peer after re-pairing replaced hostPeer.\n if (core.disposed || peer !== hostPeer || peer.status === 'failed') return;\n peer.status = 'failed';\n if (openTimer) {\n clearTimeout(openTimer);\n openTimer = null;\n }\n // Teardown is deferred a macrotask so a peer racing its own open\n // deadline reports its timeout rather than this hangup.\n setTimeout(() => {\n peer.dc?.close();\n peer.pc?.close();\n }, 0);\n core.emitTap({ peerId: peer.id, status: 'failed', type: 'peer-status-change' });\n core.emitTap({ error, type: 'error' });\n core.emitTap({ peer: peer.publicPeer, reason: error.message, type: 'peer-left' });\n core.setStatus('failed');\n }\n\n function disconnectPeer(peer: PeerRecord, reason: string): void {\n if (core.disposed || peer !== hostPeer) return;\n if (peer.status === 'disconnected' || peer.status === 'failed') return;\n peer.status = 'disconnected';\n peer.pc?.close();\n core.emitTap({ peerId: peer.id, status: 'disconnected', type: 'peer-status-change' });\n core.emitTap({ peer: peer.publicPeer, reason, type: 'peer-left' });\n core.setStatus('disconnected');\n }\n\n function wireChannel(peer: PeerRecord, pc: RTCPeerConnectionLike, dc: RTCDataChannelLike): void {\n peer.dc = dc;\n\n dc.addEventListener('open', () => {\n if (core.disposed) return;\n if (openTimer) {\n clearTimeout(openTimer);\n openTimer = null;\n }\n peer.status = 'connected';\n core.emitTap({ peerId: peer.id, status: 'connected', type: 'peer-status-change' });\n core.emitTap({ peer: peer.publicPeer, type: 'peer-joined' });\n core.setStatus('connected');\n });\n dc.addEventListener('message', (event) => {\n if (core.disposed) return;\n messenger.handleInbound(peer, event.data);\n });\n dc.addEventListener('close', () => {\n if (core.disposed) return;\n if (peer.status === 'connecting')\n failPeer(\n peer,\n openTimer ? openTimeoutError() : new MeshConnectionError('channel closed before opening', peer.id),\n );\n else disconnectPeer(peer, 'channel closed');\n });\n dc.addEventListener('error', () => {\n if (core.disposed) return;\n failPeer(peer, new MeshConnectionError('channel error', peer.id));\n });\n\n // The ice-state emission lives on the pairing-scoped listener above; this\n // one owns channel-lifecycle transitions only, so each ICE change emits\n // exactly one ice-state event.\n pc.addEventListener('iceconnectionstatechange', () => {\n if (core.disposed) return;\n const state = pc.iceConnectionState;\n if (state === 'failed' || state === 'closed') {\n failPeer(peer, new MeshConnectionError(`ice ${state}`, peer.id));\n } else if (state === 'disconnected' && peer.status === 'connected') {\n disconnectPeer(peer, 'ice disconnected');\n }\n });\n }\n\n const guest: MeshGuest<P> = {\n async acceptInvitation(invitation, meta) {\n core.ensureLive();\n // A terminal host peer (failed/disconnected) may be replaced by a fresh\n // pairing on the same node: listeners stay attached and keep working.\n if (hostPeer && hostPeer.status !== 'failed' && hostPeer.status !== 'disconnected') {\n throw new MeshPairingError('Guest is already paired');\n }\n if (invitation?.v !== 1) throw new MeshPairingError('Unsupported invitation version');\n if (clock() >= invitation.expiresAt) throw new MeshPairingError('Invitation expired');\n\n if (hostPeer) {\n if (openTimer) {\n clearTimeout(openTimer);\n openTimer = null;\n }\n hostPeer.pc?.close();\n hostPeer = null;\n }\n\n core.setStatus('pairing');\n let pc: RTCPeerConnectionLike;\n try {\n pc = rtc.createPeerConnection({ iceServers: [...(options.iceServers ?? [])] });\n } catch (error) {\n core.setStatus('idle');\n throw error;\n }\n\n // The invitation carries no host identity beyond an optional display\n // name: the session id doubles as the host-side peer id for inbound\n // metadata and tap events.\n const peer = createPeerRecord(invitation.sessionId, invitation.hostName, 'host');\n peer.pc = pc;\n hostPeer = peer;\n\n pc.addEventListener('datachannel', (event) => {\n wireChannel(peer, pc, event.channel);\n });\n pc.addEventListener('iceconnectionstatechange', () => {\n if (core.disposed) return;\n core.emitTap({ peerId: peer.id, state: pc.iceConnectionState, type: 'ice-state' });\n });\n\n try {\n await pc.setRemoteDescription({ sdp: normalizeSdp(invitation.sdp), type: 'offer' });\n } catch (cause) {\n hostPeer = null;\n core.setStatus('idle');\n throw new MeshPairingError('Invalid invitation SDP', { cause });\n }\n\n let answerSdp: string;\n try {\n await pc.setLocalDescription(await pc.createAnswer());\n await waitIceGathering(pc, iceGatheringTimeoutMs);\n answerSdp = pc.localDescription?.sdp ?? '';\n } catch (cause) {\n hostPeer = null;\n pc.close();\n core.setStatus('idle');\n throw new MeshConnectionError('Failed to create answer', null, { cause });\n }\n if (core.disposed) {\n pc.close();\n throw new MeshDisposedError();\n }\n if (!answerSdp) {\n hostPeer = null;\n pc.close();\n core.setStatus('idle');\n throw new MeshConnectionError('Local description has no SDP');\n }\n\n const peerId = randomId(options.random);\n let proof: string;\n try {\n proof = await createProof(invitation.secret, answerSdp);\n } catch (cause) {\n hostPeer = null;\n pc.close();\n core.setStatus('idle');\n throw cause;\n }\n core.setStatus('connecting');\n\n // If the channel never opens (host never accepts the answer), fail loudly\n // instead of hanging in 'connecting' forever. The deadline must cover\n // the human carry-back of the answer, not just machine negotiation.\n openTimer = setTimeout(() => {\n if (hostPeer?.status === 'connecting') {\n failPeer(hostPeer, openTimeoutError());\n }\n }, channelOpenTimeoutMs);\n\n return {\n peer: meta?.name === undefined ? { id: peerId } : { id: peerId, name: meta.name },\n proof,\n sdp: answerSdp,\n sessionId: invitation.sessionId,\n v: 1,\n };\n },\n get disposalSignal() {\n return core.disposalCtrl.signal;\n },\n\n dispose() {\n if (core.disposed) return;\n core.markDisposed();\n if (openTimer) {\n clearTimeout(openTimer);\n openTimer = null;\n }\n if (hostPeer) {\n hostPeer.dc?.close();\n hostPeer.pc?.close();\n }\n messenger.clear();\n },\n get disposed() {\n return core.disposed;\n },\n get host(): MeshPeer | null {\n return hostPeer?.publicPeer ?? null;\n },\n\n on(type, handler) {\n core.ensureLive();\n return messenger.on(type, handler as (message: MeshInbound<unknown>) => void);\n },\n\n send(type, payload) {\n core.ensureLive();\n if (!hostPeer) throw new MeshConnectionError('Guest is not paired');\n messenger.sendTo(hostPeer, type, payload);\n },\n get status() {\n return core.status;\n },\n\n tap(handler, tapOptions) {\n return core.tap(handler, tapOptions);\n },\n\n [Symbol.dispose]() {\n this.dispose();\n },\n };\n\n return guest;\n}\n"],"mappings":"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"}
|
package/dist/guest.js
CHANGED
|
@@ -1,2 +1,154 @@
|
|
|
1
|
-
import{MeshConnectionError as e,MeshDisposedError as t,MeshPairingError as n,MeshTimeoutError as r
|
|
1
|
+
import { MeshConnectionError as e, MeshDisposedError as t, MeshPairingError as n, MeshTimeoutError as r } from "./errors.js";
|
|
2
|
+
import "./_defaults.js";
|
|
3
|
+
import { randomId as i } from "./_random.js";
|
|
4
|
+
import { createMessenger as a, createNodeCore as o, createPeerRecord as s } from "./_node.js";
|
|
5
|
+
import { createProof as c } from "./_proof.js";
|
|
6
|
+
import { nativeRtcFactory as l, normalizeSdp as u, waitIceGathering as d } from "./_rtc.js";
|
|
7
|
+
//#region src/guest.ts
|
|
8
|
+
function createMeshGuest(f = {}) {
|
|
9
|
+
let p = f.rtc ?? l, m = f.clock ?? (() => Date.now()), h = f.maxMessageBytes ?? 65536, g = f.channelOpenTimeoutMs ?? 6e4, _ = f.iceGatheringTimeoutMs ?? 5e3, v = o(f.signal), y = a({
|
|
10
|
+
clock: m,
|
|
11
|
+
emitTap: v.emitTap,
|
|
12
|
+
maxMessageBytes: h,
|
|
13
|
+
random: f.random
|
|
14
|
+
}), b = null, x = null, openTimeoutError = () => new r(`Timed out waiting ${g}ms for the data channel to open`);
|
|
15
|
+
function failPeer(e, t) {
|
|
16
|
+
v.disposed || e !== b || e.status === "failed" || (e.status = "failed", x &&= (clearTimeout(x), null), setTimeout(() => {
|
|
17
|
+
e.dc?.close(), e.pc?.close();
|
|
18
|
+
}, 0), v.emitTap({
|
|
19
|
+
peerId: e.id,
|
|
20
|
+
status: "failed",
|
|
21
|
+
type: "peer-status-change"
|
|
22
|
+
}), v.emitTap({
|
|
23
|
+
error: t,
|
|
24
|
+
type: "error"
|
|
25
|
+
}), v.emitTap({
|
|
26
|
+
peer: e.publicPeer,
|
|
27
|
+
reason: t.message,
|
|
28
|
+
type: "peer-left"
|
|
29
|
+
}), v.setStatus("failed"));
|
|
30
|
+
}
|
|
31
|
+
function disconnectPeer(e, t) {
|
|
32
|
+
v.disposed || e !== b || e.status !== "disconnected" && e.status !== "failed" && (e.status = "disconnected", e.pc?.close(), v.emitTap({
|
|
33
|
+
peerId: e.id,
|
|
34
|
+
status: "disconnected",
|
|
35
|
+
type: "peer-status-change"
|
|
36
|
+
}), v.emitTap({
|
|
37
|
+
peer: e.publicPeer,
|
|
38
|
+
reason: t,
|
|
39
|
+
type: "peer-left"
|
|
40
|
+
}), v.setStatus("disconnected"));
|
|
41
|
+
}
|
|
42
|
+
function wireChannel(t, n, r) {
|
|
43
|
+
t.dc = r, r.addEventListener("open", () => {
|
|
44
|
+
v.disposed || (x &&= (clearTimeout(x), null), t.status = "connected", v.emitTap({
|
|
45
|
+
peerId: t.id,
|
|
46
|
+
status: "connected",
|
|
47
|
+
type: "peer-status-change"
|
|
48
|
+
}), v.emitTap({
|
|
49
|
+
peer: t.publicPeer,
|
|
50
|
+
type: "peer-joined"
|
|
51
|
+
}), v.setStatus("connected"));
|
|
52
|
+
}), r.addEventListener("message", (e) => {
|
|
53
|
+
v.disposed || y.handleInbound(t, e.data);
|
|
54
|
+
}), r.addEventListener("close", () => {
|
|
55
|
+
v.disposed || (t.status === "connecting" ? failPeer(t, x ? openTimeoutError() : new e("channel closed before opening", t.id)) : disconnectPeer(t, "channel closed"));
|
|
56
|
+
}), r.addEventListener("error", () => {
|
|
57
|
+
v.disposed || failPeer(t, new e("channel error", t.id));
|
|
58
|
+
}), n.addEventListener("iceconnectionstatechange", () => {
|
|
59
|
+
if (v.disposed) return;
|
|
60
|
+
let r = n.iceConnectionState;
|
|
61
|
+
r === "failed" || r === "closed" ? failPeer(t, new e(`ice ${r}`, t.id)) : r === "disconnected" && t.status === "connected" && disconnectPeer(t, "ice disconnected");
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
async acceptInvitation(r, a) {
|
|
66
|
+
if (v.ensureLive(), b && b.status !== "failed" && b.status !== "disconnected") throw new n("Guest is already paired");
|
|
67
|
+
if (r?.v !== 1) throw new n("Unsupported invitation version");
|
|
68
|
+
if (m() >= r.expiresAt) throw new n("Invitation expired");
|
|
69
|
+
b &&= (x &&= (clearTimeout(x), null), b.pc?.close(), null), v.setStatus("pairing");
|
|
70
|
+
let o;
|
|
71
|
+
try {
|
|
72
|
+
o = p.createPeerConnection({ iceServers: [...f.iceServers ?? []] });
|
|
73
|
+
} catch (e) {
|
|
74
|
+
throw v.setStatus("idle"), e;
|
|
75
|
+
}
|
|
76
|
+
let l = s(r.sessionId, r.hostName, "host");
|
|
77
|
+
l.pc = o, b = l, o.addEventListener("datachannel", (e) => {
|
|
78
|
+
wireChannel(l, o, e.channel);
|
|
79
|
+
}), o.addEventListener("iceconnectionstatechange", () => {
|
|
80
|
+
v.disposed || v.emitTap({
|
|
81
|
+
peerId: l.id,
|
|
82
|
+
state: o.iceConnectionState,
|
|
83
|
+
type: "ice-state"
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
try {
|
|
87
|
+
await o.setRemoteDescription({
|
|
88
|
+
sdp: u(r.sdp),
|
|
89
|
+
type: "offer"
|
|
90
|
+
});
|
|
91
|
+
} catch (e) {
|
|
92
|
+
throw b = null, v.setStatus("idle"), new n("Invalid invitation SDP", { cause: e });
|
|
93
|
+
}
|
|
94
|
+
let h;
|
|
95
|
+
try {
|
|
96
|
+
await o.setLocalDescription(await o.createAnswer()), await d(o, _), h = o.localDescription?.sdp ?? "";
|
|
97
|
+
} catch (t) {
|
|
98
|
+
throw b = null, o.close(), v.setStatus("idle"), new e("Failed to create answer", null, { cause: t });
|
|
99
|
+
}
|
|
100
|
+
if (v.disposed) throw o.close(), new t();
|
|
101
|
+
if (!h) throw b = null, o.close(), v.setStatus("idle"), new e("Local description has no SDP");
|
|
102
|
+
let y = i(f.random), S;
|
|
103
|
+
try {
|
|
104
|
+
S = await c(r.secret, h);
|
|
105
|
+
} catch (e) {
|
|
106
|
+
throw b = null, o.close(), v.setStatus("idle"), e;
|
|
107
|
+
}
|
|
108
|
+
return v.setStatus("connecting"), x = setTimeout(() => {
|
|
109
|
+
b?.status === "connecting" && failPeer(b, openTimeoutError());
|
|
110
|
+
}, g), {
|
|
111
|
+
peer: a?.name === void 0 ? { id: y } : {
|
|
112
|
+
id: y,
|
|
113
|
+
name: a.name
|
|
114
|
+
},
|
|
115
|
+
proof: S,
|
|
116
|
+
sdp: h,
|
|
117
|
+
sessionId: r.sessionId,
|
|
118
|
+
v: 1
|
|
119
|
+
};
|
|
120
|
+
},
|
|
121
|
+
get disposalSignal() {
|
|
122
|
+
return v.disposalCtrl.signal;
|
|
123
|
+
},
|
|
124
|
+
dispose() {
|
|
125
|
+
v.disposed || (v.markDisposed(), x &&= (clearTimeout(x), null), b && (b.dc?.close(), b.pc?.close()), y.clear());
|
|
126
|
+
},
|
|
127
|
+
get disposed() {
|
|
128
|
+
return v.disposed;
|
|
129
|
+
},
|
|
130
|
+
get host() {
|
|
131
|
+
return b?.publicPeer ?? null;
|
|
132
|
+
},
|
|
133
|
+
on(e, t) {
|
|
134
|
+
return v.ensureLive(), y.on(e, t);
|
|
135
|
+
},
|
|
136
|
+
send(t, n) {
|
|
137
|
+
if (v.ensureLive(), !b) throw new e("Guest is not paired");
|
|
138
|
+
y.sendTo(b, t, n);
|
|
139
|
+
},
|
|
140
|
+
get status() {
|
|
141
|
+
return v.status;
|
|
142
|
+
},
|
|
143
|
+
tap(e, t) {
|
|
144
|
+
return v.tap(e, t);
|
|
145
|
+
},
|
|
146
|
+
[Symbol.dispose]() {
|
|
147
|
+
this.dispose();
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
//#endregion
|
|
152
|
+
export { createMeshGuest };
|
|
153
|
+
|
|
2
154
|
//# sourceMappingURL=guest.js.map
|