@vielzeug/mesh 3.1.0 → 26.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/dist/_base45.cjs +1 -1
  2. package/dist/_base45.cjs.map +1 -1
  3. package/dist/_base45.js +1 -37
  4. package/dist/_base45.js.map +1 -1
  5. package/dist/_defaults.cjs +2 -0
  6. package/dist/_defaults.cjs.map +1 -0
  7. package/dist/_defaults.d.ts +6 -0
  8. package/dist/_defaults.d.ts.map +1 -0
  9. package/dist/_defaults.js +2 -0
  10. package/dist/_defaults.js.map +1 -0
  11. package/dist/_node.cjs +1 -1
  12. package/dist/_node.cjs.map +1 -1
  13. package/dist/_node.d.ts +4 -11
  14. package/dist/_node.d.ts.map +1 -1
  15. package/dist/_node.js +1 -194
  16. package/dist/_node.js.map +1 -1
  17. package/dist/_proof.cjs +1 -1
  18. package/dist/_proof.cjs.map +1 -1
  19. package/dist/_proof.d.ts +8 -5
  20. package/dist/_proof.d.ts.map +1 -1
  21. package/dist/_proof.js +1 -21
  22. package/dist/_proof.js.map +1 -1
  23. package/dist/_random.cjs +1 -1
  24. package/dist/_random.cjs.map +1 -1
  25. package/dist/_random.d.ts.map +1 -1
  26. package/dist/_random.js +1 -15
  27. package/dist/_random.js.map +1 -1
  28. package/dist/_rtc.cjs +2 -2
  29. package/dist/_rtc.cjs.map +1 -1
  30. package/dist/_rtc.d.ts +7 -6
  31. package/dist/_rtc.d.ts.map +1 -1
  32. package/dist/_rtc.js +3 -41
  33. package/dist/_rtc.js.map +1 -1
  34. package/dist/_sdp.cjs +2 -2
  35. package/dist/_sdp.cjs.map +1 -1
  36. package/dist/_sdp.js +2 -54
  37. package/dist/_sdp.js.map +1 -1
  38. package/dist/_wire.cjs +1 -1
  39. package/dist/_wire.cjs.map +1 -1
  40. package/dist/_wire.js +1 -20
  41. package/dist/_wire.js.map +1 -1
  42. package/dist/codec.cjs +1 -1
  43. package/dist/codec.cjs.map +1 -1
  44. package/dist/codec.d.ts +14 -9
  45. package/dist/codec.d.ts.map +1 -1
  46. package/dist/codec.js +1 -34
  47. package/dist/codec.js.map +1 -1
  48. package/dist/errors.cjs +1 -1
  49. package/dist/errors.cjs.map +1 -1
  50. package/dist/errors.js +1 -18
  51. package/dist/errors.js.map +1 -1
  52. package/dist/guest.cjs +1 -1
  53. package/dist/guest.cjs.map +1 -1
  54. package/dist/guest.d.ts.map +1 -1
  55. package/dist/guest.js +1 -164
  56. package/dist/guest.js.map +1 -1
  57. package/dist/host.cjs +1 -1
  58. package/dist/host.cjs.map +1 -1
  59. package/dist/host.d.ts.map +1 -1
  60. package/dist/host.js +1 -240
  61. package/dist/host.js.map +1 -1
  62. package/dist/index.d.ts +2 -2
  63. package/dist/index.d.ts.map +1 -1
  64. package/dist/index.js +1 -6
  65. package/dist/mesh.cjs +3 -3
  66. package/dist/mesh.cjs.map +1 -1
  67. package/dist/mesh.iife.js +3 -3
  68. package/dist/mesh.iife.js.map +1 -1
  69. package/dist/mesh.js +3 -3
  70. package/dist/mesh.js.map +1 -1
  71. package/dist/qr-codec.cjs +1 -1
  72. package/dist/qr-codec.cjs.map +1 -1
  73. package/dist/qr-codec.d.ts +3 -3
  74. package/dist/qr-codec.d.ts.map +1 -1
  75. package/dist/qr-codec.js +1 -67
  76. package/dist/qr-codec.js.map +1 -1
  77. package/dist/testing/fake-rtc.cjs +2 -0
  78. package/dist/testing/fake-rtc.cjs.map +1 -0
  79. package/dist/testing/fake-rtc.d.ts +32 -0
  80. package/dist/testing/fake-rtc.d.ts.map +1 -0
  81. package/dist/testing/fake-rtc.js +2 -0
  82. package/dist/testing/fake-rtc.js.map +1 -0
  83. package/dist/testing/index.d.ts +2 -0
  84. package/dist/testing/index.d.ts.map +1 -0
  85. package/dist/testing.cjs +1 -0
  86. package/dist/testing.js +1 -0
  87. package/dist/types.d.ts +19 -37
  88. package/dist/types.d.ts.map +1 -1
  89. package/package.json +17 -5
  90. package/dist/_base64.cjs +0 -2
  91. package/dist/_base64.cjs.map +0 -1
  92. package/dist/_base64.d.ts +0 -9
  93. package/dist/_base64.d.ts.map +0 -1
  94. package/dist/_base64.js +0 -33
  95. package/dist/_base64.js.map +0 -1
package/dist/_base45.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./errors.cjs");var t=`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:`,n=new Map([...t].map((e,t)=>[e,t]));function r(e){let n=``;for(let r=0;r+1<e.length;r+=2){let i=e[r]*256+e[r+1];n+=t[i%45]+t[Math.floor(i/45)%45]+t[Math.floor(i/2025)]}if(e.length%2){let r=e[e.length-1];n+=t[r%45]+t[Math.floor(r/45)]}return n}function i(t){let n=[],r=0;for(;r+2<t.length;){let i=a(t,r)+45*a(t,r+1)+2025*a(t,r+2);if(i>65535)throw new e.MeshPairingError(`Malformed base45 payload`);n.push(i>>8,i&255),r+=3}if(t.length-r===2){let i=a(t,r)+45*a(t,r+1);if(i>255)throw new e.MeshPairingError(`Malformed base45 payload`);n.push(i)}else if(t.length-r===1)throw new e.MeshPairingError(`Malformed base45 payload`);return new Uint8Array(n)}function a(t,r){let i=n.get(t[r]);if(i===void 0)throw new e.MeshPairingError(`Malformed base45 payload`);return i}exports.base45ToBytes=i,exports.bytesToBase45=r;
1
+ const e=require("./errors.cjs");var t=`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:`,n=new Map([...t].map((e,t)=>[e,t]));function bytesToBase45(e){let n=``;for(let r=0;r+1<e.length;r+=2){let i=e[r]*256+e[r+1];n+=t[i%45]+t[Math.floor(i/45)%45]+t[Math.floor(i/2025)]}if(e.length%2){let r=e[e.length-1];n+=t[r%45]+t[Math.floor(r/45)]}return n}function base45ToBytes(t){let n=[],r=0;for(;r+2<t.length;){let i=digit(t,r)+45*digit(t,r+1)+2025*digit(t,r+2);if(i>65535)throw new e.MeshPairingError(`Malformed base45 payload`);n.push(i>>8,i&255),r+=3}if(t.length-r===2){let i=digit(t,r)+45*digit(t,r+1);if(i>255)throw new e.MeshPairingError(`Malformed base45 payload`);n.push(i)}else if(t.length-r===1)throw new e.MeshPairingError(`Malformed base45 payload`);return new Uint8Array(n)}function digit(t,r){let i=n.get(t[r]);if(i===void 0)throw new e.MeshPairingError(`Malformed base45 payload`);return i}exports.base45ToBytes=base45ToBytes,exports.bytesToBase45=bytesToBase45;
2
2
  //# sourceMappingURL=_base45.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"_base45.cjs","names":[],"sources":["../src/_base45.ts"],"sourcesContent":["import { MeshPairingError } from './errors';\n\n/**\n * Base45 (RFC 9285): two bytes → three chars, one leftover byte → two chars.\n * The alphabet sits inside the QR alphanumeric charset, so QR encoders can use\n * alphanumeric mode (~5.5 bits/char) instead of byte mode — about a third\n * denser than base64 for the same payload.\n */\n\nconst ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:';\nconst INDEX = new Map([...ALPHABET].map((ch, i) => [ch, i]));\n\nexport function bytesToBase45(bytes: Uint8Array): string {\n let out = '';\n for (let i = 0; i + 1 < bytes.length; i += 2) {\n const value = bytes[i] * 256 + bytes[i + 1];\n out += ALPHABET[value % 45] + ALPHABET[Math.floor(value / 45) % 45] + ALPHABET[Math.floor(value / 2025)];\n }\n if (bytes.length % 2) {\n const value = bytes[bytes.length - 1];\n out += ALPHABET[value % 45] + ALPHABET[Math.floor(value / 45)];\n }\n return out;\n}\n\nexport function base45ToBytes(text: string): Uint8Array {\n const out: number[] = [];\n let i = 0;\n while (i + 2 < text.length) {\n const value = digit(text, i) + 45 * digit(text, i + 1) + 2025 * digit(text, i + 2);\n if (value > 0xffff) throw new MeshPairingError('Malformed base45 payload');\n out.push(value >> 8, value & 0xff);\n i += 3;\n }\n if (text.length - i === 2) {\n const value = digit(text, i) + 45 * digit(text, i + 1);\n if (value > 0xff) throw new MeshPairingError('Malformed base45 payload');\n out.push(value);\n } else if (text.length - i === 1) {\n throw new MeshPairingError('Malformed base45 payload');\n }\n return new Uint8Array(out);\n}\n\nfunction digit(text: string, at: number): number {\n const value = INDEX.get(text[at]);\n if (value === undefined) throw new MeshPairingError('Malformed base45 payload');\n return value;\n}\n"],"mappings":"gCASA,IAAM,EAAW,gDACX,EAAQ,IAAI,IAAI,CAAC,GAAG,CAAQ,CAAC,CAAC,KAAK,EAAI,IAAM,CAAC,EAAI,CAAC,CAAC,CAAC,EAE3D,SAAgB,EAAc,EAA2B,CACvD,IAAI,EAAM,GACV,IAAK,IAAI,EAAI,EAAG,EAAI,EAAI,EAAM,OAAQ,GAAK,EAAG,CAC5C,IAAM,EAAQ,EAAM,GAAK,IAAM,EAAM,EAAI,GACzC,GAAO,EAAS,EAAQ,IAAM,EAAS,KAAK,MAAM,EAAQ,EAAE,EAAI,IAAM,EAAS,KAAK,MAAM,EAAQ,IAAI,EACxG,CACA,GAAI,EAAM,OAAS,EAAG,CACpB,IAAM,EAAQ,EAAM,EAAM,OAAS,GACnC,GAAO,EAAS,EAAQ,IAAM,EAAS,KAAK,MAAM,EAAQ,EAAE,EAC9D,CACA,OAAO,CACT,CAEA,SAAgB,EAAc,EAA0B,CACtD,IAAM,EAAgB,CAAC,EACnB,EAAI,EACR,KAAO,EAAI,EAAI,EAAK,QAAQ,CAC1B,IAAM,EAAQ,EAAM,EAAM,CAAC,EAAI,GAAK,EAAM,EAAM,EAAI,CAAC,EAAI,KAAO,EAAM,EAAM,EAAI,CAAC,EACjF,GAAI,EAAQ,MAAQ,MAAM,IAAI,EAAA,iBAAiB,0BAA0B,EACzE,EAAI,KAAK,GAAS,EAAG,EAAQ,GAAI,EACjC,GAAK,CACP,CACA,GAAI,EAAK,OAAS,IAAM,EAAG,CACzB,IAAM,EAAQ,EAAM,EAAM,CAAC,EAAI,GAAK,EAAM,EAAM,EAAI,CAAC,EACrD,GAAI,EAAQ,IAAM,MAAM,IAAI,EAAA,iBAAiB,0BAA0B,EACvE,EAAI,KAAK,CAAK,CAChB,MAAO,GAAI,EAAK,OAAS,IAAM,EAC7B,MAAM,IAAI,EAAA,iBAAiB,0BAA0B,EAEvD,OAAO,IAAI,WAAW,CAAG,CAC3B,CAEA,SAAS,EAAM,EAAc,EAAoB,CAC/C,IAAM,EAAQ,EAAM,IAAI,EAAK,EAAG,EAChC,GAAI,IAAU,IAAA,GAAW,MAAM,IAAI,EAAA,iBAAiB,0BAA0B,EAC9E,OAAO,CACT"}
1
+ {"version":3,"file":"_base45.cjs","names":[],"sources":["../src/_base45.ts"],"sourcesContent":["import { MeshPairingError } from './errors';\n\n/**\n * Base45 (RFC 9285): two bytes → three chars, one leftover byte → two chars.\n * The alphabet sits inside the QR alphanumeric charset, so QR encoders can use\n * alphanumeric mode (~5.5 bits/char) instead of byte mode — about a third\n * denser than base64 for the same payload.\n */\n\nconst ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:';\nconst INDEX = new Map([...ALPHABET].map((ch, i) => [ch, i]));\n\nexport function bytesToBase45(bytes: Uint8Array): string {\n let out = '';\n for (let i = 0; i + 1 < bytes.length; i += 2) {\n const value = bytes[i] * 256 + bytes[i + 1];\n out += ALPHABET[value % 45] + ALPHABET[Math.floor(value / 45) % 45] + ALPHABET[Math.floor(value / 2025)];\n }\n if (bytes.length % 2) {\n const value = bytes[bytes.length - 1];\n out += ALPHABET[value % 45] + ALPHABET[Math.floor(value / 45)];\n }\n return out;\n}\n\nexport function base45ToBytes(text: string): Uint8Array {\n const out: number[] = [];\n let i = 0;\n while (i + 2 < text.length) {\n const value = digit(text, i) + 45 * digit(text, i + 1) + 2025 * digit(text, i + 2);\n if (value > 0xffff) throw new MeshPairingError('Malformed base45 payload');\n out.push(value >> 8, value & 0xff);\n i += 3;\n }\n if (text.length - i === 2) {\n const value = digit(text, i) + 45 * digit(text, i + 1);\n if (value > 0xff) throw new MeshPairingError('Malformed base45 payload');\n out.push(value);\n } else if (text.length - i === 1) {\n throw new MeshPairingError('Malformed base45 payload');\n }\n return new Uint8Array(out);\n}\n\nfunction digit(text: string, at: number): number {\n const value = INDEX.get(text[at]);\n if (value === undefined) throw new MeshPairingError('Malformed base45 payload');\n return value;\n}\n"],"mappings":"gCASA,IAAM,EAAW,gDACX,EAAQ,IAAI,IAAI,CAAC,GAAG,CAAQ,CAAC,CAAC,KAAK,EAAI,IAAM,CAAC,EAAI,CAAC,CAAC,CAAC,EAE3D,SAAgB,cAAc,EAA2B,CACvD,IAAI,EAAM,GACV,IAAK,IAAI,EAAI,EAAG,EAAI,EAAI,EAAM,OAAQ,GAAK,EAAG,CAC5C,IAAM,EAAQ,EAAM,GAAK,IAAM,EAAM,EAAI,GACzC,GAAO,EAAS,EAAQ,IAAM,EAAS,KAAK,MAAM,EAAQ,EAAE,EAAI,IAAM,EAAS,KAAK,MAAM,EAAQ,IAAI,EACxG,CACA,GAAI,EAAM,OAAS,EAAG,CACpB,IAAM,EAAQ,EAAM,EAAM,OAAS,GACnC,GAAO,EAAS,EAAQ,IAAM,EAAS,KAAK,MAAM,EAAQ,EAAE,EAC9D,CACA,OAAO,CACT,CAEA,SAAgB,cAAc,EAA0B,CACtD,IAAM,EAAgB,CAAC,EACnB,EAAI,EACR,KAAO,EAAI,EAAI,EAAK,QAAQ,CAC1B,IAAM,EAAQ,MAAM,EAAM,CAAC,EAAI,GAAK,MAAM,EAAM,EAAI,CAAC,EAAI,KAAO,MAAM,EAAM,EAAI,CAAC,EACjF,GAAI,EAAQ,MAAQ,MAAM,IAAI,EAAA,iBAAiB,0BAA0B,EACzE,EAAI,KAAK,GAAS,EAAG,EAAQ,GAAI,EACjC,GAAK,CACP,CACA,GAAI,EAAK,OAAS,IAAM,EAAG,CACzB,IAAM,EAAQ,MAAM,EAAM,CAAC,EAAI,GAAK,MAAM,EAAM,EAAI,CAAC,EACrD,GAAI,EAAQ,IAAM,MAAM,IAAI,EAAA,iBAAiB,0BAA0B,EACvE,EAAI,KAAK,CAAK,CAChB,MAAO,GAAI,EAAK,OAAS,IAAM,EAC7B,MAAM,IAAI,EAAA,iBAAiB,0BAA0B,EAEvD,OAAO,IAAI,WAAW,CAAG,CAC3B,CAEA,SAAS,MAAM,EAAc,EAAoB,CAC/C,IAAM,EAAQ,EAAM,IAAI,EAAK,EAAG,EAChC,GAAI,IAAU,IAAA,GAAW,MAAM,IAAI,EAAA,iBAAiB,0BAA0B,EAC9E,OAAO,CACT"}
package/dist/_base45.js CHANGED
@@ -1,38 +1,2 @@
1
- import { MeshPairingError as e } from "./errors.js";
2
- //#region src/_base45.ts
3
- var t = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:", n = new Map([...t].map((e, t) => [e, t]));
4
- function r(e) {
5
- let n = "";
6
- for (let r = 0; r + 1 < e.length; r += 2) {
7
- let i = e[r] * 256 + e[r + 1];
8
- n += t[i % 45] + t[Math.floor(i / 45) % 45] + t[Math.floor(i / 2025)];
9
- }
10
- if (e.length % 2) {
11
- let r = e[e.length - 1];
12
- n += t[r % 45] + t[Math.floor(r / 45)];
13
- }
14
- return n;
15
- }
16
- function i(t) {
17
- let n = [], r = 0;
18
- for (; r + 2 < t.length;) {
19
- let i = a(t, r) + 45 * a(t, r + 1) + 2025 * a(t, r + 2);
20
- if (i > 65535) throw new e("Malformed base45 payload");
21
- n.push(i >> 8, i & 255), r += 3;
22
- }
23
- if (t.length - r === 2) {
24
- let i = a(t, r) + 45 * a(t, r + 1);
25
- if (i > 255) throw new e("Malformed base45 payload");
26
- n.push(i);
27
- } else if (t.length - r === 1) throw new e("Malformed base45 payload");
28
- return new Uint8Array(n);
29
- }
30
- function a(t, r) {
31
- let i = n.get(t[r]);
32
- if (i === void 0) throw new e("Malformed base45 payload");
33
- return i;
34
- }
35
- //#endregion
36
- export { i as base45ToBytes, r as bytesToBase45 };
37
-
1
+ import{MeshPairingError as e}from"./errors.js";var t=`0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:`,n=new Map([...t].map((e,t)=>[e,t]));function bytesToBase45(e){let n=``;for(let r=0;r+1<e.length;r+=2){let i=e[r]*256+e[r+1];n+=t[i%45]+t[Math.floor(i/45)%45]+t[Math.floor(i/2025)]}if(e.length%2){let r=e[e.length-1];n+=t[r%45]+t[Math.floor(r/45)]}return n}function base45ToBytes(t){let n=[],r=0;for(;r+2<t.length;){let i=digit(t,r)+45*digit(t,r+1)+2025*digit(t,r+2);if(i>65535)throw new e(`Malformed base45 payload`);n.push(i>>8,i&255),r+=3}if(t.length-r===2){let i=digit(t,r)+45*digit(t,r+1);if(i>255)throw new e(`Malformed base45 payload`);n.push(i)}else if(t.length-r===1)throw new e(`Malformed base45 payload`);return new Uint8Array(n)}function digit(t,r){let i=n.get(t[r]);if(i===void 0)throw new e(`Malformed base45 payload`);return i}export{base45ToBytes,bytesToBase45};
38
2
  //# sourceMappingURL=_base45.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"_base45.js","names":[],"sources":["../src/_base45.ts"],"sourcesContent":["import { MeshPairingError } from './errors';\n\n/**\n * Base45 (RFC 9285): two bytes → three chars, one leftover byte → two chars.\n * The alphabet sits inside the QR alphanumeric charset, so QR encoders can use\n * alphanumeric mode (~5.5 bits/char) instead of byte mode — about a third\n * denser than base64 for the same payload.\n */\n\nconst ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:';\nconst INDEX = new Map([...ALPHABET].map((ch, i) => [ch, i]));\n\nexport function bytesToBase45(bytes: Uint8Array): string {\n let out = '';\n for (let i = 0; i + 1 < bytes.length; i += 2) {\n const value = bytes[i] * 256 + bytes[i + 1];\n out += ALPHABET[value % 45] + ALPHABET[Math.floor(value / 45) % 45] + ALPHABET[Math.floor(value / 2025)];\n }\n if (bytes.length % 2) {\n const value = bytes[bytes.length - 1];\n out += ALPHABET[value % 45] + ALPHABET[Math.floor(value / 45)];\n }\n return out;\n}\n\nexport function base45ToBytes(text: string): Uint8Array {\n const out: number[] = [];\n let i = 0;\n while (i + 2 < text.length) {\n const value = digit(text, i) + 45 * digit(text, i + 1) + 2025 * digit(text, i + 2);\n if (value > 0xffff) throw new MeshPairingError('Malformed base45 payload');\n out.push(value >> 8, value & 0xff);\n i += 3;\n }\n if (text.length - i === 2) {\n const value = digit(text, i) + 45 * digit(text, i + 1);\n if (value > 0xff) throw new MeshPairingError('Malformed base45 payload');\n out.push(value);\n } else if (text.length - i === 1) {\n throw new MeshPairingError('Malformed base45 payload');\n }\n return new Uint8Array(out);\n}\n\nfunction digit(text: string, at: number): number {\n const value = INDEX.get(text[at]);\n if (value === undefined) throw new MeshPairingError('Malformed base45 payload');\n return value;\n}\n"],"mappings":";;AASA,IAAM,IAAW,iDACX,IAAQ,IAAI,IAAI,CAAC,GAAG,CAAQ,CAAC,CAAC,KAAK,GAAI,MAAM,CAAC,GAAI,CAAC,CAAC,CAAC;AAE3D,SAAgB,EAAc,GAA2B;CACvD,IAAI,IAAM;CACV,KAAK,IAAI,IAAI,GAAG,IAAI,IAAI,EAAM,QAAQ,KAAK,GAAG;EAC5C,IAAM,IAAQ,EAAM,KAAK,MAAM,EAAM,IAAI;EACzC,KAAO,EAAS,IAAQ,MAAM,EAAS,KAAK,MAAM,IAAQ,EAAE,IAAI,MAAM,EAAS,KAAK,MAAM,IAAQ,IAAI;CACxG;CACA,IAAI,EAAM,SAAS,GAAG;EACpB,IAAM,IAAQ,EAAM,EAAM,SAAS;EACnC,KAAO,EAAS,IAAQ,MAAM,EAAS,KAAK,MAAM,IAAQ,EAAE;CAC9D;CACA,OAAO;AACT;AAEA,SAAgB,EAAc,GAA0B;CACtD,IAAM,IAAgB,CAAC,GACnB,IAAI;CACR,OAAO,IAAI,IAAI,EAAK,SAAQ;EAC1B,IAAM,IAAQ,EAAM,GAAM,CAAC,IAAI,KAAK,EAAM,GAAM,IAAI,CAAC,IAAI,OAAO,EAAM,GAAM,IAAI,CAAC;EACjF,IAAI,IAAQ,OAAQ,MAAM,IAAI,EAAiB,0BAA0B;EAEzE,AADA,EAAI,KAAK,KAAS,GAAG,IAAQ,GAAI,GACjC,KAAK;CACP;CACA,IAAI,EAAK,SAAS,MAAM,GAAG;EACzB,IAAM,IAAQ,EAAM,GAAM,CAAC,IAAI,KAAK,EAAM,GAAM,IAAI,CAAC;EACrD,IAAI,IAAQ,KAAM,MAAM,IAAI,EAAiB,0BAA0B;EACvE,EAAI,KAAK,CAAK;CAChB,OAAO,IAAI,EAAK,SAAS,MAAM,GAC7B,MAAM,IAAI,EAAiB,0BAA0B;CAEvD,OAAO,IAAI,WAAW,CAAG;AAC3B;AAEA,SAAS,EAAM,GAAc,GAAoB;CAC/C,IAAM,IAAQ,EAAM,IAAI,EAAK,EAAG;CAChC,IAAI,MAAU,KAAA,GAAW,MAAM,IAAI,EAAiB,0BAA0B;CAC9E,OAAO;AACT"}
1
+ {"version":3,"file":"_base45.js","names":[],"sources":["../src/_base45.ts"],"sourcesContent":["import { MeshPairingError } from './errors';\n\n/**\n * Base45 (RFC 9285): two bytes → three chars, one leftover byte → two chars.\n * The alphabet sits inside the QR alphanumeric charset, so QR encoders can use\n * alphanumeric mode (~5.5 bits/char) instead of byte mode — about a third\n * denser than base64 for the same payload.\n */\n\nconst ALPHABET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:';\nconst INDEX = new Map([...ALPHABET].map((ch, i) => [ch, i]));\n\nexport function bytesToBase45(bytes: Uint8Array): string {\n let out = '';\n for (let i = 0; i + 1 < bytes.length; i += 2) {\n const value = bytes[i] * 256 + bytes[i + 1];\n out += ALPHABET[value % 45] + ALPHABET[Math.floor(value / 45) % 45] + ALPHABET[Math.floor(value / 2025)];\n }\n if (bytes.length % 2) {\n const value = bytes[bytes.length - 1];\n out += ALPHABET[value % 45] + ALPHABET[Math.floor(value / 45)];\n }\n return out;\n}\n\nexport function base45ToBytes(text: string): Uint8Array {\n const out: number[] = [];\n let i = 0;\n while (i + 2 < text.length) {\n const value = digit(text, i) + 45 * digit(text, i + 1) + 2025 * digit(text, i + 2);\n if (value > 0xffff) throw new MeshPairingError('Malformed base45 payload');\n out.push(value >> 8, value & 0xff);\n i += 3;\n }\n if (text.length - i === 2) {\n const value = digit(text, i) + 45 * digit(text, i + 1);\n if (value > 0xff) throw new MeshPairingError('Malformed base45 payload');\n out.push(value);\n } else if (text.length - i === 1) {\n throw new MeshPairingError('Malformed base45 payload');\n }\n return new Uint8Array(out);\n}\n\nfunction digit(text: string, at: number): number {\n const value = INDEX.get(text[at]);\n if (value === undefined) throw new MeshPairingError('Malformed base45 payload');\n return value;\n}\n"],"mappings":"+CASA,IAAM,EAAW,gDACX,EAAQ,IAAI,IAAI,CAAC,GAAG,CAAQ,CAAC,CAAC,KAAK,EAAI,IAAM,CAAC,EAAI,CAAC,CAAC,CAAC,EAE3D,SAAgB,cAAc,EAA2B,CACvD,IAAI,EAAM,GACV,IAAK,IAAI,EAAI,EAAG,EAAI,EAAI,EAAM,OAAQ,GAAK,EAAG,CAC5C,IAAM,EAAQ,EAAM,GAAK,IAAM,EAAM,EAAI,GACzC,GAAO,EAAS,EAAQ,IAAM,EAAS,KAAK,MAAM,EAAQ,EAAE,EAAI,IAAM,EAAS,KAAK,MAAM,EAAQ,IAAI,EACxG,CACA,GAAI,EAAM,OAAS,EAAG,CACpB,IAAM,EAAQ,EAAM,EAAM,OAAS,GACnC,GAAO,EAAS,EAAQ,IAAM,EAAS,KAAK,MAAM,EAAQ,EAAE,EAC9D,CACA,OAAO,CACT,CAEA,SAAgB,cAAc,EAA0B,CACtD,IAAM,EAAgB,CAAC,EACnB,EAAI,EACR,KAAO,EAAI,EAAI,EAAK,QAAQ,CAC1B,IAAM,EAAQ,MAAM,EAAM,CAAC,EAAI,GAAK,MAAM,EAAM,EAAI,CAAC,EAAI,KAAO,MAAM,EAAM,EAAI,CAAC,EACjF,GAAI,EAAQ,MAAQ,MAAM,IAAI,EAAiB,0BAA0B,EACzE,EAAI,KAAK,GAAS,EAAG,EAAQ,GAAI,EACjC,GAAK,CACP,CACA,GAAI,EAAK,OAAS,IAAM,EAAG,CACzB,IAAM,EAAQ,MAAM,EAAM,CAAC,EAAI,GAAK,MAAM,EAAM,EAAI,CAAC,EACrD,GAAI,EAAQ,IAAM,MAAM,IAAI,EAAiB,0BAA0B,EACvE,EAAI,KAAK,CAAK,CAChB,MAAO,GAAI,EAAK,OAAS,IAAM,EAC7B,MAAM,IAAI,EAAiB,0BAA0B,EAEvD,OAAO,IAAI,WAAW,CAAG,CAC3B,CAEA,SAAS,MAAM,EAAc,EAAoB,CAC/C,IAAM,EAAQ,EAAM,IAAI,EAAK,EAAG,EAChC,GAAI,IAAU,IAAA,GAAW,MAAM,IAAI,EAAiB,0BAA0B,EAC9E,OAAO,CACT"}
@@ -0,0 +1,2 @@
1
+ var e=6e4,t=5e3,n=3e5,r=65536;exports.DEFAULT_CHANNEL_OPEN_TIMEOUT_MS=e,exports.DEFAULT_ICE_GATHERING_TIMEOUT_MS=t,exports.DEFAULT_INVITATION_TTL_MS=n,exports.DEFAULT_MAX_MESSAGE_BYTES=r;
2
+ //# sourceMappingURL=_defaults.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_defaults.cjs","names":[],"sources":["../src/_defaults.ts"],"sourcesContent":["/** Option defaults shared by host and guest so the two never drift. */\nexport const DEFAULT_CHANNEL_OPEN_TIMEOUT_MS = 60_000;\nexport const DEFAULT_ICE_GATHERING_TIMEOUT_MS = 5_000;\nexport const DEFAULT_INVITATION_TTL_MS = 300_000;\nexport const DEFAULT_MAX_MESSAGE_BYTES = 65_536;\n"],"mappings":"AACA,IAAa,EAAkC,IAClC,EAAmC,IACnC,EAA4B,IAC5B,EAA4B"}
@@ -0,0 +1,6 @@
1
+ /** Option defaults shared by host and guest so the two never drift. */
2
+ export declare const DEFAULT_CHANNEL_OPEN_TIMEOUT_MS = 60000;
3
+ export declare const DEFAULT_ICE_GATHERING_TIMEOUT_MS = 5000;
4
+ export declare const DEFAULT_INVITATION_TTL_MS = 300000;
5
+ export declare const DEFAULT_MAX_MESSAGE_BYTES = 65536;
6
+ //# sourceMappingURL=_defaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_defaults.d.ts","sourceRoot":"","sources":["../src/_defaults.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,eAAO,MAAM,+BAA+B,QAAS,CAAC;AACtD,eAAO,MAAM,gCAAgC,OAAQ,CAAC;AACtD,eAAO,MAAM,yBAAyB,SAAU,CAAC;AACjD,eAAO,MAAM,yBAAyB,QAAS,CAAC"}
@@ -0,0 +1,2 @@
1
+ var e=6e4,t=5e3,n=3e5,r=65536;export{e as DEFAULT_CHANNEL_OPEN_TIMEOUT_MS,t as DEFAULT_ICE_GATHERING_TIMEOUT_MS,n as DEFAULT_INVITATION_TTL_MS,r as DEFAULT_MAX_MESSAGE_BYTES};
2
+ //# sourceMappingURL=_defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_defaults.js","names":[],"sources":["../src/_defaults.ts"],"sourcesContent":["/** Option defaults shared by host and guest so the two never drift. */\nexport const DEFAULT_CHANNEL_OPEN_TIMEOUT_MS = 60_000;\nexport const DEFAULT_ICE_GATHERING_TIMEOUT_MS = 5_000;\nexport const DEFAULT_INVITATION_TTL_MS = 300_000;\nexport const DEFAULT_MAX_MESSAGE_BYTES = 65_536;\n"],"mappings":"AACA,IAAa,EAAkC,IAClC,EAAmC,IACnC,EAA4B,IAC5B,EAA4B"}
package/dist/_node.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./_base64.cjs"),t=require("./errors.cjs"),n=require("./_random.cjs"),r=require("./_wire.cjs");function i(e,n){let r=new Set,i=new AbortController,a={disposalCtrl:i,disposed:!1,emitTap(e){if(r.size!==0)for(let t of r)try{t(e)}catch{}},ensureLive(){if(a.disposed)throw new t.MeshDisposedError},id:e,markDisposed(){a.disposed||(a.disposed=!0,a.status=`disposed`,a.emitTap({peerId:null,status:`disposed`,type:`status-change`}),a.emitTap({type:`dispose`}),r.clear(),i.abort())},setStatus(e){a.disposed||a.status===e||(a.status=e,a.emitTap({peerId:null,status:e,type:`status-change`}))},status:`idle`,tap(e,t){if(a.disposed)return()=>{};r.add(e);let n=()=>r.delete(e);if(t?.signal){if(t.signal.aborted)return n(),()=>{};let e=()=>n();return t.signal.addEventListener(`abort`,e,{once:!0}),()=>{n(),t.signal?.removeEventListener(`abort`,e)}}return n}};return n&&(n.aborted?a.markDisposed():n.addEventListener(`abort`,()=>a.markDisposed(),{once:!0})),a}function a(e,t,n){let i={dc:null,dedupe:new r.MessageDedupe,id:e,name:t,pc:null,publicPeer:{get id(){return i.id},get name(){return i.name},get role(){return i.role},get status(){return i.status}},role:n,status:`connecting`};return i}function o(i){let a=new Map;function o(e,t){i.emitTap({peerId:e,reason:t,type:`message-rejected`})}return{clear(){a.clear()},handleInbound(n,s){let c=i.peerIdOf(n);if(typeof s!=`string`){o(c,`malformed`);return}let l=e.utf8Bytes(s);if(l>i.maxMessageBytes){o(c,`too-large`);return}let u;try{u=i.deserialize(s)}catch{o(c,`malformed`);return}let d=r.toEnvelope(u);if(!d){o(c,`malformed`);return}if(!n.dedupe.check(d.id)){o(c,`duplicate`);return}let f=a.get(d.t);if(!f||f.size===0){o(c,`unknown-type`);return}i.emitTap({bytes:l,messageType:d.t,peerId:c,type:`message-received`});let p={messageId:d.id,payload:d.p,peerId:c,sentAt:d.ts};for(let e of f)try{e(p)}catch(e){i.emitTap({error:new t.MeshError(`Message handler threw`,{cause:e}),type:`error`})}},hasListeners(e){return(a.get(e)?.size??0)>0},on(e,t){let n=a.get(e);return n||(n=new Set,a.set(e,n)),n.add(t),()=>{n.delete(t),n.size===0&&a.delete(e)}},sendTo(r,a,o){let s=r.dc;if(!s||s.readyState!==`open`||r.status!==`connected`)throw new t.MeshConnectionError(`Peer "${r.id}" is not connected`,r.id);let c={id:n.randomId(i.random),p:o,t:a,ts:i.clock(),v:1},l;try{l=i.serialize(c)}catch(e){throw new t.MeshPayloadError(`Failed to serialize message "${a}"`,{cause:e})}let u=e.utf8Bytes(l);if(u>i.maxMessageBytes)throw new t.MeshPayloadError(`Message "${a}" is ${u} bytes (max ${i.maxMessageBytes})`);try{s.send(l)}catch(e){throw new t.MeshConnectionError(`Failed to send message "${a}"`,r.id,{cause:e})}i.emitTap({bytes:u,messageType:a,peerId:r.id,type:`message-sent`})},wireBytes(t,r){let a={id:n.randomId(i.random),p:r,t,ts:i.clock(),v:1};return e.utf8Bytes(i.serialize(a))}}}exports.createMessenger=o,exports.createNodeCore=i,exports.createPeerRecord=a;
1
+ const e=require("./errors.cjs"),t=require("./_random.cjs"),n=require("./_wire.cjs");let r=require("@vielzeug/arsenal");function createNodeCore(t){let n=(0,r.tapper)(),i=new AbortController,a={disposalCtrl:i,disposed:!1,emitTap(e){n.emit(e)},ensureLive(){if(a.disposed)throw new e.MeshDisposedError},markDisposed(){a.disposed||(a.disposed=!0,a.status=`disposed`,a.emitTap({status:`disposed`,type:`status-change`}),a.emitTap({type:`dispose`}),n.clear(),i.abort())},setStatus(e){a.disposed||a.status===e||(a.status=e,a.emitTap({status:e,type:`status-change`}))},status:`idle`,tap(e,t){return a.disposed?()=>{}:n.tap(e,t)}};return t&&(t.aborted?a.markDisposed():t.addEventListener(`abort`,()=>a.markDisposed(),{once:!0})),a}function createPeerRecord(e,t,r){let i={dc:null,dedupe:new n.MessageDedupe,id:e,name:t,pc:null,publicPeer:{get id(){return i.id},get name(){return i.name},get role(){return i.role},get status(){return i.status}},role:r,status:`connecting`};return i}function createMessenger(i){let a=new Map;function reject(e,t){i.emitTap({peerId:e,reason:t,type:`message-rejected`})}return{clear(){a.clear()},handleInbound(t,o){let s=t.id;if(typeof o!=`string`){reject(s,`malformed`);return}let c=(0,r.utf8Bytes)(o);if(c>i.maxMessageBytes){reject(s,`too-large`);return}let l;try{l=JSON.parse(o)}catch{reject(s,`malformed`);return}let u=n.toEnvelope(l);if(!u){reject(s,`malformed`);return}if(!t.dedupe.check(u.id)){reject(s,`duplicate`);return}let d=a.get(u.t);if(!d||d.size===0){reject(s,`unknown-type`);return}i.emitTap({bytes:c,messageType:u.t,peerId:s,type:`message-received`});let f={messageId:u.id,payload:u.p,peerId:s,sentAt:u.ts};for(let t of d)try{t(f)}catch(t){i.emitTap({error:new e.MeshError(`Message handler threw`,{cause:t}),type:`error`})}},on(e,t){let n=a.get(e);return n||(n=new Set,a.set(e,n)),n.add(t),()=>{n.delete(t),n.size===0&&a.delete(e)}},sendTo(n,a,o){let s=n.dc;if(!s||s.readyState!==`open`||n.status!==`connected`)throw new e.MeshConnectionError(`Peer "${n.id}" is not connected`,n.id);let c={id:t.randomId(i.random),p:o,t:a,ts:i.clock(),v:1},l;try{l=JSON.stringify(c)}catch(t){throw new e.MeshPayloadError(`Failed to serialize message "${a}"`,{cause:t})}let u=(0,r.utf8Bytes)(l);if(u>i.maxMessageBytes)throw new e.MeshPayloadError(`Message "${a}" is ${u} bytes (max ${i.maxMessageBytes})`);try{s.send(l)}catch(t){throw new e.MeshConnectionError(`Failed to send message "${a}"`,n.id,{cause:t})}i.emitTap({bytes:u,messageType:a,peerId:n.id,type:`message-sent`})}}}exports.createMessenger=createMessenger,exports.createNodeCore=createNodeCore,exports.createPeerRecord=createPeerRecord;
2
2
  //# sourceMappingURL=_node.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"_node.cjs","names":[],"sources":["../src/_node.ts"],"sourcesContent":["import type { RandomSource } from '@vielzeug/arsenal';\nimport { utf8Bytes } from './_base64';\nimport { randomId } from './_random';\nimport { MessageDedupe, toEnvelope } from './_wire';\nimport { MeshConnectionError, MeshDisposedError, MeshError, MeshPayloadError } from './errors';\nimport type {\n MeshEvent,\n MeshInbound,\n MeshPeer,\n MeshStatus,\n RTCDataChannelLike,\n RTCPeerConnectionLike,\n Unsubscribe,\n} from './types';\n\ntype Tapper = (event: MeshEvent) => void;\n\n/** Shared lifecycle core: status, tappers, disposal. */\nexport interface NodeCore {\n readonly disposalCtrl: AbortController;\n disposed: boolean;\n emitTap(event: MeshEvent): void;\n ensureLive(): void;\n readonly id: string;\n /** Emits the final status-change + dispose events, clears tappers, aborts. */\n markDisposed(): void;\n setStatus(next: MeshStatus): void;\n status: MeshStatus;\n tap(handler: Tapper, options?: { readonly signal?: AbortSignal }): Unsubscribe;\n}\n\nexport function createNodeCore(id: string, signal?: AbortSignal): NodeCore {\n const tappers = new Set<Tapper>();\n const disposalCtrl = new AbortController();\n\n const core: NodeCore = {\n disposalCtrl,\n disposed: false,\n\n emitTap(event) {\n if (tappers.size === 0) return;\n for (const tapper of tappers) {\n try {\n tapper(event);\n } catch {\n // Observability must not affect mesh behavior.\n }\n }\n },\n\n ensureLive() {\n if (core.disposed) throw new MeshDisposedError();\n },\n id,\n\n markDisposed() {\n if (core.disposed) return;\n core.disposed = true;\n core.status = 'disposed';\n core.emitTap({ peerId: null, status: 'disposed', type: 'status-change' });\n core.emitTap({ type: 'dispose' });\n tappers.clear();\n disposalCtrl.abort();\n },\n\n setStatus(next) {\n if (core.disposed || core.status === next) return;\n core.status = next;\n core.emitTap({ peerId: null, status: next, type: 'status-change' });\n },\n status: 'idle',\n\n tap(handler, options) {\n if (core.disposed) return () => {};\n tappers.add(handler);\n\n const detach = () => tappers.delete(handler);\n if (options?.signal) {\n if (options.signal.aborted) {\n detach();\n return () => {};\n }\n const onAbort = () => detach();\n options.signal.addEventListener('abort', onAbort, { once: true });\n return () => {\n detach();\n options.signal?.removeEventListener('abort', onAbort);\n };\n }\n return detach;\n },\n };\n\n if (signal) {\n if (signal.aborted) {\n core.markDisposed();\n } else {\n signal.addEventListener('abort', () => core.markDisposed(), { once: true });\n }\n }\n\n return core;\n}\n\n// ─── Peers ───────────────────────────────────────────────────────────────────\n\n/** Internal peer record; `publicPeer` exposes live status through getters. */\nexport interface PeerRecord {\n dc: RTCDataChannelLike | null;\n readonly dedupe: MessageDedupe;\n readonly id: string;\n readonly name: string | undefined;\n pc: RTCPeerConnectionLike | null;\n readonly publicPeer: MeshPeer;\n readonly role: 'host' | 'guest';\n status: MeshStatus;\n}\n\nexport function createPeerRecord(id: string, name: string | undefined, role: 'host' | 'guest'): PeerRecord {\n const record: PeerRecord = {\n dc: null,\n dedupe: new MessageDedupe(),\n id,\n name,\n pc: null,\n publicPeer: {\n get id() {\n return record.id;\n },\n get name() {\n return record.name;\n },\n get role() {\n return record.role;\n },\n get status() {\n return record.status;\n },\n },\n role,\n status: 'connecting',\n };\n return record;\n}\n\n// ─── Messenger ───────────────────────────────────────────────────────────────\n\ntype InboundHandler = (message: MeshInbound<unknown>) => void;\n\nexport interface MessengerOptions {\n readonly clock: () => number;\n readonly deserialize: (text: string) => unknown;\n readonly emitTap: (event: MeshEvent) => void;\n readonly maxMessageBytes: number;\n readonly peerIdOf: (peer: PeerRecord) => string;\n readonly random?: RandomSource;\n readonly serialize: (value: unknown) => string;\n}\n\nexport interface Messenger {\n clear(): void;\n /** Entry point for `dc` 'message' events. */\n handleInbound(peer: PeerRecord, data: unknown): void;\n hasListeners(type: string): boolean;\n on(type: string, handler: InboundHandler): Unsubscribe;\n /** Sends a typed envelope to a peer. Throws `MeshConnectionError`/`MeshPayloadError`. */\n sendTo(peer: PeerRecord, type: string, payload: unknown): void;\n /** Serialized-byte size of an outbound message — used by broadcast pre-checks. */\n wireBytes(type: string, payload: unknown): number;\n}\n\nexport function createMessenger(options: MessengerOptions): Messenger {\n const handlers = new Map<string, Set<InboundHandler>>();\n\n function reject(peerId: string, reason: 'too-large' | 'duplicate' | 'malformed' | 'unknown-type'): void {\n options.emitTap({ peerId, reason, type: 'message-rejected' });\n }\n\n return {\n clear() {\n handlers.clear();\n },\n\n handleInbound(peer, data) {\n const peerId = options.peerIdOf(peer);\n\n if (typeof data !== 'string') {\n reject(peerId, 'malformed');\n return;\n }\n\n const bytes = utf8Bytes(data);\n if (bytes > options.maxMessageBytes) {\n reject(peerId, 'too-large');\n return;\n }\n\n let decoded: unknown;\n try {\n decoded = options.deserialize(data);\n } catch {\n reject(peerId, 'malformed');\n return;\n }\n\n const envelope = toEnvelope(decoded);\n if (!envelope) {\n reject(peerId, 'malformed');\n return;\n }\n\n if (!peer.dedupe.check(envelope.id)) {\n reject(peerId, 'duplicate');\n return;\n }\n\n const set = handlers.get(envelope.t);\n if (!set || set.size === 0) {\n reject(peerId, 'unknown-type');\n return;\n }\n\n options.emitTap({ bytes, messageType: envelope.t, peerId, type: 'message-received' });\n const message: MeshInbound<unknown> = {\n messageId: envelope.id,\n payload: envelope.p,\n peerId,\n sentAt: envelope.ts,\n };\n for (const handler of set) {\n try {\n handler(message);\n } catch (cause) {\n options.emitTap({ error: new MeshError('Message handler threw', { cause }), type: 'error' });\n }\n }\n },\n\n hasListeners(type) {\n return (handlers.get(type)?.size ?? 0) > 0;\n },\n on(type, handler) {\n let set = handlers.get(type);\n if (!set) {\n set = new Set();\n handlers.set(type, set);\n }\n set.add(handler);\n return () => {\n set.delete(handler);\n if (set.size === 0) handlers.delete(type);\n };\n },\n\n sendTo(peer, type, payload) {\n const dc = peer.dc;\n if (!dc || dc.readyState !== 'open' || peer.status !== 'connected') {\n throw new MeshConnectionError(`Peer \"${peer.id}\" is not connected`, peer.id);\n }\n\n const envelope = { id: randomId(options.random), p: payload, t: type, ts: options.clock(), v: 1 as const };\n let text: string;\n try {\n text = options.serialize(envelope);\n } catch (cause) {\n throw new MeshPayloadError(`Failed to serialize message \"${type}\"`, { cause });\n }\n\n const bytes = utf8Bytes(text);\n if (bytes > options.maxMessageBytes) {\n throw new MeshPayloadError(`Message \"${type}\" is ${bytes} bytes (max ${options.maxMessageBytes})`);\n }\n\n try {\n dc.send(text);\n } catch (cause) {\n throw new MeshConnectionError(`Failed to send message \"${type}\"`, peer.id, { cause });\n }\n options.emitTap({ bytes, messageType: type, peerId: peer.id, type: 'message-sent' });\n },\n\n wireBytes(type, payload) {\n const envelope = { id: randomId(options.random), p: payload, t: type, ts: options.clock(), v: 1 as const };\n return utf8Bytes(options.serialize(envelope));\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"_node.cjs","names":[],"sources":["../src/_node.ts"],"sourcesContent":["import { type RandomSource, tapper, utf8Bytes } from '@vielzeug/arsenal';\nimport { randomId } from './_random';\nimport { MessageDedupe, toEnvelope } from './_wire';\nimport { MeshConnectionError, MeshDisposedError, MeshError, MeshPayloadError } from './errors';\nimport type {\n MeshEvent,\n MeshInbound,\n MeshPeer,\n MeshPeerStatus,\n MeshStatus,\n RTCDataChannelLike,\n RTCPeerConnectionLike,\n Unsubscribe,\n} from './types';\n\ntype Tapper = (event: MeshEvent) => void;\n\n/** Shared lifecycle core: status, tappers, disposal. */\nexport interface NodeCore {\n readonly disposalCtrl: AbortController;\n disposed: boolean;\n emitTap(event: MeshEvent): void;\n ensureLive(): void;\n /** Emits the final status-change + dispose events, clears tappers, aborts. */\n markDisposed(): void;\n setStatus(next: MeshStatus): void;\n status: MeshStatus;\n tap(handler: Tapper, options?: { readonly signal?: AbortSignal }): Unsubscribe;\n}\n\nexport function createNodeCore(signal?: AbortSignal): NodeCore {\n const tappers = tapper<MeshEvent>();\n const disposalCtrl = new AbortController();\n\n const core: NodeCore = {\n disposalCtrl,\n disposed: false,\n\n emitTap(event) {\n tappers.emit(event);\n },\n\n ensureLive() {\n if (core.disposed) throw new MeshDisposedError();\n },\n\n markDisposed() {\n if (core.disposed) return;\n core.disposed = true;\n core.status = 'disposed';\n core.emitTap({ status: 'disposed', type: 'status-change' });\n core.emitTap({ type: 'dispose' });\n tappers.clear();\n disposalCtrl.abort();\n },\n\n setStatus(next) {\n if (core.disposed || core.status === next) return;\n core.status = next;\n core.emitTap({ status: next, type: 'status-change' });\n },\n status: 'idle',\n\n tap(handler, options) {\n if (core.disposed) return () => {};\n return tappers.tap(handler, options);\n },\n };\n\n if (signal) {\n if (signal.aborted) {\n core.markDisposed();\n } else {\n signal.addEventListener('abort', () => core.markDisposed(), { once: true });\n }\n }\n\n return core;\n}\n\n// ─── Peers ───────────────────────────────────────────────────────────────────\n\n/** Internal peer record; `publicPeer` exposes live status through getters. */\nexport interface PeerRecord {\n dc: RTCDataChannelLike | null;\n readonly dedupe: MessageDedupe;\n readonly id: string;\n readonly name: string | undefined;\n pc: RTCPeerConnectionLike | null;\n readonly publicPeer: MeshPeer;\n readonly role: 'host' | 'guest';\n status: MeshPeerStatus;\n}\n\nexport function createPeerRecord(id: string, name: string | undefined, role: 'host' | 'guest'): PeerRecord {\n const record: PeerRecord = {\n dc: null,\n dedupe: new MessageDedupe(),\n id,\n name,\n pc: null,\n publicPeer: {\n get id() {\n return record.id;\n },\n get name() {\n return record.name;\n },\n get role() {\n return record.role;\n },\n get status() {\n return record.status;\n },\n },\n role,\n status: 'connecting',\n };\n return record;\n}\n\n// ─── Messenger ───────────────────────────────────────────────────────────────\n\ntype InboundHandler = (message: MeshInbound<unknown>) => void;\n\nexport interface MessengerOptions {\n readonly clock: () => number;\n readonly emitTap: (event: MeshEvent) => void;\n readonly maxMessageBytes: number;\n readonly random?: RandomSource;\n}\n\nexport interface Messenger {\n clear(): void;\n /** Entry point for `dc` 'message' events. */\n handleInbound(peer: PeerRecord, data: unknown): void;\n on(type: string, handler: InboundHandler): Unsubscribe;\n /** Sends a typed envelope to a peer. Throws `MeshConnectionError`/`MeshPayloadError`. */\n sendTo(peer: PeerRecord, type: string, payload: unknown): void;\n}\n\nexport function createMessenger(options: MessengerOptions): Messenger {\n const handlers = new Map<string, Set<InboundHandler>>();\n\n function reject(peerId: string, reason: 'too-large' | 'duplicate' | 'malformed' | 'unknown-type'): void {\n options.emitTap({ peerId, reason, type: 'message-rejected' });\n }\n\n return {\n clear() {\n handlers.clear();\n },\n\n handleInbound(peer, data) {\n const peerId = peer.id;\n\n if (typeof data !== 'string') {\n reject(peerId, 'malformed');\n return;\n }\n\n const bytes = utf8Bytes(data);\n if (bytes > options.maxMessageBytes) {\n reject(peerId, 'too-large');\n return;\n }\n\n let decoded: unknown;\n try {\n decoded = JSON.parse(data);\n } catch {\n reject(peerId, 'malformed');\n return;\n }\n\n const envelope = toEnvelope(decoded);\n if (!envelope) {\n reject(peerId, 'malformed');\n return;\n }\n\n if (!peer.dedupe.check(envelope.id)) {\n reject(peerId, 'duplicate');\n return;\n }\n\n const set = handlers.get(envelope.t);\n if (!set || set.size === 0) {\n reject(peerId, 'unknown-type');\n return;\n }\n\n options.emitTap({ bytes, messageType: envelope.t, peerId, type: 'message-received' });\n const message: MeshInbound<unknown> = {\n messageId: envelope.id,\n payload: envelope.p,\n peerId,\n sentAt: envelope.ts,\n };\n for (const handler of set) {\n try {\n handler(message);\n } catch (cause) {\n options.emitTap({ error: new MeshError('Message handler threw', { cause }), type: 'error' });\n }\n }\n },\n\n on(type, handler) {\n let set = handlers.get(type);\n if (!set) {\n set = new Set();\n handlers.set(type, set);\n }\n set.add(handler);\n return () => {\n set.delete(handler);\n if (set.size === 0) handlers.delete(type);\n };\n },\n\n sendTo(peer, type, payload) {\n const dc = peer.dc;\n if (!dc || dc.readyState !== 'open' || peer.status !== 'connected') {\n throw new MeshConnectionError(`Peer \"${peer.id}\" is not connected`, peer.id);\n }\n\n const envelope = { id: randomId(options.random), p: payload, t: type, ts: options.clock(), v: 1 as const };\n let text: string;\n try {\n text = JSON.stringify(envelope);\n } catch (cause) {\n throw new MeshPayloadError(`Failed to serialize message \"${type}\"`, { cause });\n }\n\n const bytes = utf8Bytes(text);\n if (bytes > options.maxMessageBytes) {\n throw new MeshPayloadError(`Message \"${type}\" is ${bytes} bytes (max ${options.maxMessageBytes})`);\n }\n\n try {\n dc.send(text);\n } catch (cause) {\n throw new MeshConnectionError(`Failed to send message \"${type}\"`, peer.id, { cause });\n }\n options.emitTap({ bytes, messageType: type, peerId: peer.id, type: 'message-sent' });\n },\n };\n}\n"],"mappings":"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"}
package/dist/_node.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import type { RandomSource } from '@vielzeug/arsenal';
1
+ import { type RandomSource } from '@vielzeug/arsenal';
2
2
  import { MessageDedupe } from './_wire';
3
- import type { MeshEvent, MeshInbound, MeshPeer, MeshStatus, RTCDataChannelLike, RTCPeerConnectionLike, Unsubscribe } from './types';
3
+ import type { MeshEvent, MeshInbound, MeshPeer, MeshPeerStatus, MeshStatus, RTCDataChannelLike, RTCPeerConnectionLike, Unsubscribe } from './types';
4
4
  type Tapper = (event: MeshEvent) => void;
5
5
  /** Shared lifecycle core: status, tappers, disposal. */
6
6
  export interface NodeCore {
@@ -8,7 +8,6 @@ export interface NodeCore {
8
8
  disposed: boolean;
9
9
  emitTap(event: MeshEvent): void;
10
10
  ensureLive(): void;
11
- readonly id: string;
12
11
  /** Emits the final status-change + dispose events, clears tappers, aborts. */
13
12
  markDisposed(): void;
14
13
  setStatus(next: MeshStatus): void;
@@ -17,7 +16,7 @@ export interface NodeCore {
17
16
  readonly signal?: AbortSignal;
18
17
  }): Unsubscribe;
19
18
  }
20
- export declare function createNodeCore(id: string, signal?: AbortSignal): NodeCore;
19
+ export declare function createNodeCore(signal?: AbortSignal): NodeCore;
21
20
  /** Internal peer record; `publicPeer` exposes live status through getters. */
22
21
  export interface PeerRecord {
23
22
  dc: RTCDataChannelLike | null;
@@ -27,29 +26,23 @@ export interface PeerRecord {
27
26
  pc: RTCPeerConnectionLike | null;
28
27
  readonly publicPeer: MeshPeer;
29
28
  readonly role: 'host' | 'guest';
30
- status: MeshStatus;
29
+ status: MeshPeerStatus;
31
30
  }
32
31
  export declare function createPeerRecord(id: string, name: string | undefined, role: 'host' | 'guest'): PeerRecord;
33
32
  type InboundHandler = (message: MeshInbound<unknown>) => void;
34
33
  export interface MessengerOptions {
35
34
  readonly clock: () => number;
36
- readonly deserialize: (text: string) => unknown;
37
35
  readonly emitTap: (event: MeshEvent) => void;
38
36
  readonly maxMessageBytes: number;
39
- readonly peerIdOf: (peer: PeerRecord) => string;
40
37
  readonly random?: RandomSource;
41
- readonly serialize: (value: unknown) => string;
42
38
  }
43
39
  export interface Messenger {
44
40
  clear(): void;
45
41
  /** Entry point for `dc` 'message' events. */
46
42
  handleInbound(peer: PeerRecord, data: unknown): void;
47
- hasListeners(type: string): boolean;
48
43
  on(type: string, handler: InboundHandler): Unsubscribe;
49
44
  /** Sends a typed envelope to a peer. Throws `MeshConnectionError`/`MeshPayloadError`. */
50
45
  sendTo(peer: PeerRecord, type: string, payload: unknown): void;
51
- /** Serialized-byte size of an outbound message — used by broadcast pre-checks. */
52
- wireBytes(type: string, payload: unknown): number;
53
46
  }
54
47
  export declare function createMessenger(options: MessengerOptions): Messenger;
55
48
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"_node.d.ts","sourceRoot":"","sources":["../src/_node.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"_node.d.ts","sourceRoot":"","sources":["../src/_node.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,YAAY,EAAqB,MAAM,mBAAmB,CAAC;AAEzE,OAAO,EAAE,aAAa,EAAc,MAAM,SAAS,CAAC;AAEpD,OAAO,KAAK,EACV,SAAS,EACT,WAAW,EACX,QAAQ,EACR,cAAc,EACd,UAAU,EACV,kBAAkB,EAClB,qBAAqB,EACrB,WAAW,EACZ,MAAM,SAAS,CAAC;AAEjB,KAAK,MAAM,GAAG,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;AAEzC,wDAAwD;AACxD,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,YAAY,EAAE,eAAe,CAAC;IACvC,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAAC;IAChC,UAAU,IAAI,IAAI,CAAC;IACnB,8EAA8E;IAC9E,YAAY,IAAI,IAAI,CAAC;IACrB,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IAClC,MAAM,EAAE,UAAU,CAAC;IACnB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,WAAW,CAAC;CAChF;AAED,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,QAAQ,CAgD7D;AAID,8EAA8E;AAC9E,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,kBAAkB,GAAG,IAAI,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,EAAE,EAAE,qBAAqB,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,MAAM,EAAE,cAAc,CAAC;CACxB;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,UAAU,CAyBzG;AAID,KAAK,cAAc,GAAG,CAAC,OAAO,EAAE,WAAW,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;AAE9D,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;IAC7C,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC;CAChC;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,IAAI,IAAI,CAAC;IACd,6CAA6C;IAC7C,aAAa,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IACrD,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,WAAW,CAAC;IACvD,yFAAyF;IACzF,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CAChE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CA2GpE"}
package/dist/_node.js CHANGED
@@ -1,195 +1,2 @@
1
- import { utf8Bytes as e } from "./_base64.js";
2
- import { MeshConnectionError as t, MeshDisposedError as n, MeshError as r, MeshPayloadError as i } from "./errors.js";
3
- import { randomId as a } from "./_random.js";
4
- import { MessageDedupe as o, toEnvelope as s } from "./_wire.js";
5
- //#region src/_node.ts
6
- function c(e, t) {
7
- let r = /* @__PURE__ */ new Set(), i = new AbortController(), a = {
8
- disposalCtrl: i,
9
- disposed: !1,
10
- emitTap(e) {
11
- if (r.size !== 0) for (let t of r) try {
12
- t(e);
13
- } catch {}
14
- },
15
- ensureLive() {
16
- if (a.disposed) throw new n();
17
- },
18
- id: e,
19
- markDisposed() {
20
- a.disposed || (a.disposed = !0, a.status = "disposed", a.emitTap({
21
- peerId: null,
22
- status: "disposed",
23
- type: "status-change"
24
- }), a.emitTap({ type: "dispose" }), r.clear(), i.abort());
25
- },
26
- setStatus(e) {
27
- a.disposed || a.status === e || (a.status = e, a.emitTap({
28
- peerId: null,
29
- status: e,
30
- type: "status-change"
31
- }));
32
- },
33
- status: "idle",
34
- tap(e, t) {
35
- if (a.disposed) return () => {};
36
- r.add(e);
37
- let n = () => r.delete(e);
38
- if (t?.signal) {
39
- if (t.signal.aborted) return n(), () => {};
40
- let e = () => n();
41
- return t.signal.addEventListener("abort", e, { once: !0 }), () => {
42
- n(), t.signal?.removeEventListener("abort", e);
43
- };
44
- }
45
- return n;
46
- }
47
- };
48
- return t && (t.aborted ? a.markDisposed() : t.addEventListener("abort", () => a.markDisposed(), { once: !0 })), a;
49
- }
50
- function l(e, t, n) {
51
- let r = {
52
- dc: null,
53
- dedupe: new o(),
54
- id: e,
55
- name: t,
56
- pc: null,
57
- publicPeer: {
58
- get id() {
59
- return r.id;
60
- },
61
- get name() {
62
- return r.name;
63
- },
64
- get role() {
65
- return r.role;
66
- },
67
- get status() {
68
- return r.status;
69
- }
70
- },
71
- role: n,
72
- status: "connecting"
73
- };
74
- return r;
75
- }
76
- function u(n) {
77
- let o = /* @__PURE__ */ new Map();
78
- function c(e, t) {
79
- n.emitTap({
80
- peerId: e,
81
- reason: t,
82
- type: "message-rejected"
83
- });
84
- }
85
- return {
86
- clear() {
87
- o.clear();
88
- },
89
- handleInbound(t, i) {
90
- let a = n.peerIdOf(t);
91
- if (typeof i != "string") {
92
- c(a, "malformed");
93
- return;
94
- }
95
- let l = e(i);
96
- if (l > n.maxMessageBytes) {
97
- c(a, "too-large");
98
- return;
99
- }
100
- let u;
101
- try {
102
- u = n.deserialize(i);
103
- } catch {
104
- c(a, "malformed");
105
- return;
106
- }
107
- let d = s(u);
108
- if (!d) {
109
- c(a, "malformed");
110
- return;
111
- }
112
- if (!t.dedupe.check(d.id)) {
113
- c(a, "duplicate");
114
- return;
115
- }
116
- let f = o.get(d.t);
117
- if (!f || f.size === 0) {
118
- c(a, "unknown-type");
119
- return;
120
- }
121
- n.emitTap({
122
- bytes: l,
123
- messageType: d.t,
124
- peerId: a,
125
- type: "message-received"
126
- });
127
- let p = {
128
- messageId: d.id,
129
- payload: d.p,
130
- peerId: a,
131
- sentAt: d.ts
132
- };
133
- for (let e of f) try {
134
- e(p);
135
- } catch (e) {
136
- n.emitTap({
137
- error: new r("Message handler threw", { cause: e }),
138
- type: "error"
139
- });
140
- }
141
- },
142
- hasListeners(e) {
143
- return (o.get(e)?.size ?? 0) > 0;
144
- },
145
- on(e, t) {
146
- let n = o.get(e);
147
- return n || (n = /* @__PURE__ */ new Set(), o.set(e, n)), n.add(t), () => {
148
- n.delete(t), n.size === 0 && o.delete(e);
149
- };
150
- },
151
- sendTo(r, o, s) {
152
- let c = r.dc;
153
- if (!c || c.readyState !== "open" || r.status !== "connected") throw new t(`Peer "${r.id}" is not connected`, r.id);
154
- let l = {
155
- id: a(n.random),
156
- p: s,
157
- t: o,
158
- ts: n.clock(),
159
- v: 1
160
- }, u;
161
- try {
162
- u = n.serialize(l);
163
- } catch (e) {
164
- throw new i(`Failed to serialize message "${o}"`, { cause: e });
165
- }
166
- let d = e(u);
167
- if (d > n.maxMessageBytes) throw new i(`Message "${o}" is ${d} bytes (max ${n.maxMessageBytes})`);
168
- try {
169
- c.send(u);
170
- } catch (e) {
171
- throw new t(`Failed to send message "${o}"`, r.id, { cause: e });
172
- }
173
- n.emitTap({
174
- bytes: d,
175
- messageType: o,
176
- peerId: r.id,
177
- type: "message-sent"
178
- });
179
- },
180
- wireBytes(t, r) {
181
- let i = {
182
- id: a(n.random),
183
- p: r,
184
- t,
185
- ts: n.clock(),
186
- v: 1
187
- };
188
- return e(n.serialize(i));
189
- }
190
- };
191
- }
192
- //#endregion
193
- export { u as createMessenger, c as createNodeCore, l as createPeerRecord };
194
-
1
+ import{MeshConnectionError as e,MeshDisposedError as t,MeshError as n,MeshPayloadError as r}from"./errors.js";import{randomId as i}from"./_random.js";import{MessageDedupe as a,toEnvelope as o}from"./_wire.js";import{tapper as s,utf8Bytes as c}from"@vielzeug/arsenal";function createNodeCore(e){let n=s(),r=new AbortController,i={disposalCtrl:r,disposed:!1,emitTap(e){n.emit(e)},ensureLive(){if(i.disposed)throw new t},markDisposed(){i.disposed||(i.disposed=!0,i.status=`disposed`,i.emitTap({status:`disposed`,type:`status-change`}),i.emitTap({type:`dispose`}),n.clear(),r.abort())},setStatus(e){i.disposed||i.status===e||(i.status=e,i.emitTap({status:e,type:`status-change`}))},status:`idle`,tap(e,t){return i.disposed?()=>{}:n.tap(e,t)}};return e&&(e.aborted?i.markDisposed():e.addEventListener(`abort`,()=>i.markDisposed(),{once:!0})),i}function createPeerRecord(e,t,n){let r={dc:null,dedupe:new a,id:e,name:t,pc:null,publicPeer:{get id(){return r.id},get name(){return r.name},get role(){return r.role},get status(){return r.status}},role:n,status:`connecting`};return r}function createMessenger(t){let a=new Map;function reject(e,n){t.emitTap({peerId:e,reason:n,type:`message-rejected`})}return{clear(){a.clear()},handleInbound(e,r){let i=e.id;if(typeof r!=`string`){reject(i,`malformed`);return}let s=c(r);if(s>t.maxMessageBytes){reject(i,`too-large`);return}let l;try{l=JSON.parse(r)}catch{reject(i,`malformed`);return}let u=o(l);if(!u){reject(i,`malformed`);return}if(!e.dedupe.check(u.id)){reject(i,`duplicate`);return}let d=a.get(u.t);if(!d||d.size===0){reject(i,`unknown-type`);return}t.emitTap({bytes:s,messageType:u.t,peerId:i,type:`message-received`});let f={messageId:u.id,payload:u.p,peerId:i,sentAt:u.ts};for(let e of d)try{e(f)}catch(e){t.emitTap({error:new n(`Message handler threw`,{cause:e}),type:`error`})}},on(e,t){let n=a.get(e);return n||(n=new Set,a.set(e,n)),n.add(t),()=>{n.delete(t),n.size===0&&a.delete(e)}},sendTo(n,a,o){let s=n.dc;if(!s||s.readyState!==`open`||n.status!==`connected`)throw new e(`Peer "${n.id}" is not connected`,n.id);let l={id:i(t.random),p:o,t:a,ts:t.clock(),v:1},u;try{u=JSON.stringify(l)}catch(e){throw new r(`Failed to serialize message "${a}"`,{cause:e})}let d=c(u);if(d>t.maxMessageBytes)throw new r(`Message "${a}" is ${d} bytes (max ${t.maxMessageBytes})`);try{s.send(u)}catch(t){throw new e(`Failed to send message "${a}"`,n.id,{cause:t})}t.emitTap({bytes:d,messageType:a,peerId:n.id,type:`message-sent`})}}}export{createMessenger,createNodeCore,createPeerRecord};
195
2
  //# sourceMappingURL=_node.js.map
package/dist/_node.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"_node.js","names":[],"sources":["../src/_node.ts"],"sourcesContent":["import type { RandomSource } from '@vielzeug/arsenal';\nimport { utf8Bytes } from './_base64';\nimport { randomId } from './_random';\nimport { MessageDedupe, toEnvelope } from './_wire';\nimport { MeshConnectionError, MeshDisposedError, MeshError, MeshPayloadError } from './errors';\nimport type {\n MeshEvent,\n MeshInbound,\n MeshPeer,\n MeshStatus,\n RTCDataChannelLike,\n RTCPeerConnectionLike,\n Unsubscribe,\n} from './types';\n\ntype Tapper = (event: MeshEvent) => void;\n\n/** Shared lifecycle core: status, tappers, disposal. */\nexport interface NodeCore {\n readonly disposalCtrl: AbortController;\n disposed: boolean;\n emitTap(event: MeshEvent): void;\n ensureLive(): void;\n readonly id: string;\n /** Emits the final status-change + dispose events, clears tappers, aborts. */\n markDisposed(): void;\n setStatus(next: MeshStatus): void;\n status: MeshStatus;\n tap(handler: Tapper, options?: { readonly signal?: AbortSignal }): Unsubscribe;\n}\n\nexport function createNodeCore(id: string, signal?: AbortSignal): NodeCore {\n const tappers = new Set<Tapper>();\n const disposalCtrl = new AbortController();\n\n const core: NodeCore = {\n disposalCtrl,\n disposed: false,\n\n emitTap(event) {\n if (tappers.size === 0) return;\n for (const tapper of tappers) {\n try {\n tapper(event);\n } catch {\n // Observability must not affect mesh behavior.\n }\n }\n },\n\n ensureLive() {\n if (core.disposed) throw new MeshDisposedError();\n },\n id,\n\n markDisposed() {\n if (core.disposed) return;\n core.disposed = true;\n core.status = 'disposed';\n core.emitTap({ peerId: null, status: 'disposed', type: 'status-change' });\n core.emitTap({ type: 'dispose' });\n tappers.clear();\n disposalCtrl.abort();\n },\n\n setStatus(next) {\n if (core.disposed || core.status === next) return;\n core.status = next;\n core.emitTap({ peerId: null, status: next, type: 'status-change' });\n },\n status: 'idle',\n\n tap(handler, options) {\n if (core.disposed) return () => {};\n tappers.add(handler);\n\n const detach = () => tappers.delete(handler);\n if (options?.signal) {\n if (options.signal.aborted) {\n detach();\n return () => {};\n }\n const onAbort = () => detach();\n options.signal.addEventListener('abort', onAbort, { once: true });\n return () => {\n detach();\n options.signal?.removeEventListener('abort', onAbort);\n };\n }\n return detach;\n },\n };\n\n if (signal) {\n if (signal.aborted) {\n core.markDisposed();\n } else {\n signal.addEventListener('abort', () => core.markDisposed(), { once: true });\n }\n }\n\n return core;\n}\n\n// ─── Peers ───────────────────────────────────────────────────────────────────\n\n/** Internal peer record; `publicPeer` exposes live status through getters. */\nexport interface PeerRecord {\n dc: RTCDataChannelLike | null;\n readonly dedupe: MessageDedupe;\n readonly id: string;\n readonly name: string | undefined;\n pc: RTCPeerConnectionLike | null;\n readonly publicPeer: MeshPeer;\n readonly role: 'host' | 'guest';\n status: MeshStatus;\n}\n\nexport function createPeerRecord(id: string, name: string | undefined, role: 'host' | 'guest'): PeerRecord {\n const record: PeerRecord = {\n dc: null,\n dedupe: new MessageDedupe(),\n id,\n name,\n pc: null,\n publicPeer: {\n get id() {\n return record.id;\n },\n get name() {\n return record.name;\n },\n get role() {\n return record.role;\n },\n get status() {\n return record.status;\n },\n },\n role,\n status: 'connecting',\n };\n return record;\n}\n\n// ─── Messenger ───────────────────────────────────────────────────────────────\n\ntype InboundHandler = (message: MeshInbound<unknown>) => void;\n\nexport interface MessengerOptions {\n readonly clock: () => number;\n readonly deserialize: (text: string) => unknown;\n readonly emitTap: (event: MeshEvent) => void;\n readonly maxMessageBytes: number;\n readonly peerIdOf: (peer: PeerRecord) => string;\n readonly random?: RandomSource;\n readonly serialize: (value: unknown) => string;\n}\n\nexport interface Messenger {\n clear(): void;\n /** Entry point for `dc` 'message' events. */\n handleInbound(peer: PeerRecord, data: unknown): void;\n hasListeners(type: string): boolean;\n on(type: string, handler: InboundHandler): Unsubscribe;\n /** Sends a typed envelope to a peer. Throws `MeshConnectionError`/`MeshPayloadError`. */\n sendTo(peer: PeerRecord, type: string, payload: unknown): void;\n /** Serialized-byte size of an outbound message — used by broadcast pre-checks. */\n wireBytes(type: string, payload: unknown): number;\n}\n\nexport function createMessenger(options: MessengerOptions): Messenger {\n const handlers = new Map<string, Set<InboundHandler>>();\n\n function reject(peerId: string, reason: 'too-large' | 'duplicate' | 'malformed' | 'unknown-type'): void {\n options.emitTap({ peerId, reason, type: 'message-rejected' });\n }\n\n return {\n clear() {\n handlers.clear();\n },\n\n handleInbound(peer, data) {\n const peerId = options.peerIdOf(peer);\n\n if (typeof data !== 'string') {\n reject(peerId, 'malformed');\n return;\n }\n\n const bytes = utf8Bytes(data);\n if (bytes > options.maxMessageBytes) {\n reject(peerId, 'too-large');\n return;\n }\n\n let decoded: unknown;\n try {\n decoded = options.deserialize(data);\n } catch {\n reject(peerId, 'malformed');\n return;\n }\n\n const envelope = toEnvelope(decoded);\n if (!envelope) {\n reject(peerId, 'malformed');\n return;\n }\n\n if (!peer.dedupe.check(envelope.id)) {\n reject(peerId, 'duplicate');\n return;\n }\n\n const set = handlers.get(envelope.t);\n if (!set || set.size === 0) {\n reject(peerId, 'unknown-type');\n return;\n }\n\n options.emitTap({ bytes, messageType: envelope.t, peerId, type: 'message-received' });\n const message: MeshInbound<unknown> = {\n messageId: envelope.id,\n payload: envelope.p,\n peerId,\n sentAt: envelope.ts,\n };\n for (const handler of set) {\n try {\n handler(message);\n } catch (cause) {\n options.emitTap({ error: new MeshError('Message handler threw', { cause }), type: 'error' });\n }\n }\n },\n\n hasListeners(type) {\n return (handlers.get(type)?.size ?? 0) > 0;\n },\n on(type, handler) {\n let set = handlers.get(type);\n if (!set) {\n set = new Set();\n handlers.set(type, set);\n }\n set.add(handler);\n return () => {\n set.delete(handler);\n if (set.size === 0) handlers.delete(type);\n };\n },\n\n sendTo(peer, type, payload) {\n const dc = peer.dc;\n if (!dc || dc.readyState !== 'open' || peer.status !== 'connected') {\n throw new MeshConnectionError(`Peer \"${peer.id}\" is not connected`, peer.id);\n }\n\n const envelope = { id: randomId(options.random), p: payload, t: type, ts: options.clock(), v: 1 as const };\n let text: string;\n try {\n text = options.serialize(envelope);\n } catch (cause) {\n throw new MeshPayloadError(`Failed to serialize message \"${type}\"`, { cause });\n }\n\n const bytes = utf8Bytes(text);\n if (bytes > options.maxMessageBytes) {\n throw new MeshPayloadError(`Message \"${type}\" is ${bytes} bytes (max ${options.maxMessageBytes})`);\n }\n\n try {\n dc.send(text);\n } catch (cause) {\n throw new MeshConnectionError(`Failed to send message \"${type}\"`, peer.id, { cause });\n }\n options.emitTap({ bytes, messageType: type, peerId: peer.id, type: 'message-sent' });\n },\n\n wireBytes(type, payload) {\n const envelope = { id: randomId(options.random), p: payload, t: type, ts: options.clock(), v: 1 as const };\n return utf8Bytes(options.serialize(envelope));\n },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"_node.js","names":[],"sources":["../src/_node.ts"],"sourcesContent":["import { type RandomSource, tapper, utf8Bytes } from '@vielzeug/arsenal';\nimport { randomId } from './_random';\nimport { MessageDedupe, toEnvelope } from './_wire';\nimport { MeshConnectionError, MeshDisposedError, MeshError, MeshPayloadError } from './errors';\nimport type {\n MeshEvent,\n MeshInbound,\n MeshPeer,\n MeshPeerStatus,\n MeshStatus,\n RTCDataChannelLike,\n RTCPeerConnectionLike,\n Unsubscribe,\n} from './types';\n\ntype Tapper = (event: MeshEvent) => void;\n\n/** Shared lifecycle core: status, tappers, disposal. */\nexport interface NodeCore {\n readonly disposalCtrl: AbortController;\n disposed: boolean;\n emitTap(event: MeshEvent): void;\n ensureLive(): void;\n /** Emits the final status-change + dispose events, clears tappers, aborts. */\n markDisposed(): void;\n setStatus(next: MeshStatus): void;\n status: MeshStatus;\n tap(handler: Tapper, options?: { readonly signal?: AbortSignal }): Unsubscribe;\n}\n\nexport function createNodeCore(signal?: AbortSignal): NodeCore {\n const tappers = tapper<MeshEvent>();\n const disposalCtrl = new AbortController();\n\n const core: NodeCore = {\n disposalCtrl,\n disposed: false,\n\n emitTap(event) {\n tappers.emit(event);\n },\n\n ensureLive() {\n if (core.disposed) throw new MeshDisposedError();\n },\n\n markDisposed() {\n if (core.disposed) return;\n core.disposed = true;\n core.status = 'disposed';\n core.emitTap({ status: 'disposed', type: 'status-change' });\n core.emitTap({ type: 'dispose' });\n tappers.clear();\n disposalCtrl.abort();\n },\n\n setStatus(next) {\n if (core.disposed || core.status === next) return;\n core.status = next;\n core.emitTap({ status: next, type: 'status-change' });\n },\n status: 'idle',\n\n tap(handler, options) {\n if (core.disposed) return () => {};\n return tappers.tap(handler, options);\n },\n };\n\n if (signal) {\n if (signal.aborted) {\n core.markDisposed();\n } else {\n signal.addEventListener('abort', () => core.markDisposed(), { once: true });\n }\n }\n\n return core;\n}\n\n// ─── Peers ───────────────────────────────────────────────────────────────────\n\n/** Internal peer record; `publicPeer` exposes live status through getters. */\nexport interface PeerRecord {\n dc: RTCDataChannelLike | null;\n readonly dedupe: MessageDedupe;\n readonly id: string;\n readonly name: string | undefined;\n pc: RTCPeerConnectionLike | null;\n readonly publicPeer: MeshPeer;\n readonly role: 'host' | 'guest';\n status: MeshPeerStatus;\n}\n\nexport function createPeerRecord(id: string, name: string | undefined, role: 'host' | 'guest'): PeerRecord {\n const record: PeerRecord = {\n dc: null,\n dedupe: new MessageDedupe(),\n id,\n name,\n pc: null,\n publicPeer: {\n get id() {\n return record.id;\n },\n get name() {\n return record.name;\n },\n get role() {\n return record.role;\n },\n get status() {\n return record.status;\n },\n },\n role,\n status: 'connecting',\n };\n return record;\n}\n\n// ─── Messenger ───────────────────────────────────────────────────────────────\n\ntype InboundHandler = (message: MeshInbound<unknown>) => void;\n\nexport interface MessengerOptions {\n readonly clock: () => number;\n readonly emitTap: (event: MeshEvent) => void;\n readonly maxMessageBytes: number;\n readonly random?: RandomSource;\n}\n\nexport interface Messenger {\n clear(): void;\n /** Entry point for `dc` 'message' events. */\n handleInbound(peer: PeerRecord, data: unknown): void;\n on(type: string, handler: InboundHandler): Unsubscribe;\n /** Sends a typed envelope to a peer. Throws `MeshConnectionError`/`MeshPayloadError`. */\n sendTo(peer: PeerRecord, type: string, payload: unknown): void;\n}\n\nexport function createMessenger(options: MessengerOptions): Messenger {\n const handlers = new Map<string, Set<InboundHandler>>();\n\n function reject(peerId: string, reason: 'too-large' | 'duplicate' | 'malformed' | 'unknown-type'): void {\n options.emitTap({ peerId, reason, type: 'message-rejected' });\n }\n\n return {\n clear() {\n handlers.clear();\n },\n\n handleInbound(peer, data) {\n const peerId = peer.id;\n\n if (typeof data !== 'string') {\n reject(peerId, 'malformed');\n return;\n }\n\n const bytes = utf8Bytes(data);\n if (bytes > options.maxMessageBytes) {\n reject(peerId, 'too-large');\n return;\n }\n\n let decoded: unknown;\n try {\n decoded = JSON.parse(data);\n } catch {\n reject(peerId, 'malformed');\n return;\n }\n\n const envelope = toEnvelope(decoded);\n if (!envelope) {\n reject(peerId, 'malformed');\n return;\n }\n\n if (!peer.dedupe.check(envelope.id)) {\n reject(peerId, 'duplicate');\n return;\n }\n\n const set = handlers.get(envelope.t);\n if (!set || set.size === 0) {\n reject(peerId, 'unknown-type');\n return;\n }\n\n options.emitTap({ bytes, messageType: envelope.t, peerId, type: 'message-received' });\n const message: MeshInbound<unknown> = {\n messageId: envelope.id,\n payload: envelope.p,\n peerId,\n sentAt: envelope.ts,\n };\n for (const handler of set) {\n try {\n handler(message);\n } catch (cause) {\n options.emitTap({ error: new MeshError('Message handler threw', { cause }), type: 'error' });\n }\n }\n },\n\n on(type, handler) {\n let set = handlers.get(type);\n if (!set) {\n set = new Set();\n handlers.set(type, set);\n }\n set.add(handler);\n return () => {\n set.delete(handler);\n if (set.size === 0) handlers.delete(type);\n };\n },\n\n sendTo(peer, type, payload) {\n const dc = peer.dc;\n if (!dc || dc.readyState !== 'open' || peer.status !== 'connected') {\n throw new MeshConnectionError(`Peer \"${peer.id}\" is not connected`, peer.id);\n }\n\n const envelope = { id: randomId(options.random), p: payload, t: type, ts: options.clock(), v: 1 as const };\n let text: string;\n try {\n text = JSON.stringify(envelope);\n } catch (cause) {\n throw new MeshPayloadError(`Failed to serialize message \"${type}\"`, { cause });\n }\n\n const bytes = utf8Bytes(text);\n if (bytes > options.maxMessageBytes) {\n throw new MeshPayloadError(`Message \"${type}\" is ${bytes} bytes (max ${options.maxMessageBytes})`);\n }\n\n try {\n dc.send(text);\n } catch (cause) {\n throw new MeshConnectionError(`Failed to send message \"${type}\"`, peer.id, { cause });\n }\n options.emitTap({ bytes, messageType: type, peerId: peer.id, type: 'message-sent' });\n },\n };\n}\n"],"mappings":"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"}
package/dist/_proof.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./_base64.cjs"),t=require("./_sdp.cjs");let n=require("@vielzeug/arsenal");async function r(r,i,a){let o=t.canonicalSdp(i),s=globalThis.crypto?.subtle;if(s){let t=new TextEncoder,n=await s.importKey(`raw`,t.encode(r),{hash:`SHA-256`,name:`HMAC`},!1,[`sign`]),i=await s.sign(`HMAC`,n,t.encode(o));return e.bytesToBase64Url(new Uint8Array(i))}return a(),(0,n.hash)({sdp:o,secret:r})}exports.createProof=r;
1
+ const e=require("./errors.cjs"),t=require("./_sdp.cjs");let n=require("@vielzeug/arsenal");async function createProof(r,i){let a=globalThis.crypto?.subtle;if(!a)throw new e.MeshUnsupportedError(`WebCrypto is unavailable — pairing requires a secure context`);let o=new TextEncoder,s=await a.importKey(`raw`,o.encode(r),{hash:`SHA-256`,name:`HMAC`},!1,[`sign`]),c=await a.sign(`HMAC`,s,o.encode(t.canonicalSdp(i)));return(0,n.bytesToBase64Url)(new Uint8Array(c))}exports.createProof=createProof;
2
2
  //# sourceMappingURL=_proof.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"_proof.cjs","names":[],"sources":["../src/_proof.ts"],"sourcesContent":["import { hash } from '@vielzeug/arsenal';\nimport { bytesToBase64Url } from './_base64';\nimport { canonicalSdp } from './_sdp';\n\n/**\n * Proof of invitation possession: HMAC-SHA-256 of the guest SDP keyed by the\n * invitation secret. The SDP is canonicalized first so the proof is stable\n * across pairing codecs that rebuild the description (see `mq2.`). The host\n * recomputes the proof without receiving the secret back. Falls back to a\n * deterministic non-cryptographic hash when `SubtleCrypto` is unavailable —\n * the caller reports a `'security-downgrade'` tap so consumers can refuse\n * degraded environments.\n */\nexport async function createProof(secret: string, sdp: string, onDowngrade: () => void): Promise<string> {\n const canonical = canonicalSdp(sdp);\n const subtle = globalThis.crypto?.subtle;\n if (subtle) {\n const encoder = new TextEncoder();\n const key = await subtle.importKey('raw', encoder.encode(secret), { hash: 'SHA-256', name: 'HMAC' }, false, [\n 'sign',\n ]);\n const signature = await subtle.sign('HMAC', key, encoder.encode(canonical));\n return bytesToBase64Url(new Uint8Array(signature));\n }\n\n onDowngrade();\n return hash({ sdp: canonical, secret });\n}\n"],"mappings":"4FAaA,eAAsB,EAAY,EAAgB,EAAa,EAA0C,CACvG,IAAM,EAAY,EAAA,aAAa,CAAG,EAC5B,EAAS,WAAW,QAAQ,OAClC,GAAI,EAAQ,CACV,IAAM,EAAU,IAAI,YACd,EAAM,MAAM,EAAO,UAAU,MAAO,EAAQ,OAAO,CAAM,EAAG,CAAE,KAAM,UAAW,KAAM,MAAO,EAAG,GAAO,CAC1G,MACF,CAAC,EACK,EAAY,MAAM,EAAO,KAAK,OAAQ,EAAK,EAAQ,OAAO,CAAS,CAAC,EAC1E,OAAO,EAAA,iBAAiB,IAAI,WAAW,CAAS,CAAC,CACnD,CAGA,OADA,EAAY,GACZ,EAAO,EAAA,KAAA,CAAK,CAAE,IAAK,EAAW,QAAO,CAAC,CACxC"}
1
+ {"version":3,"file":"_proof.cjs","names":[],"sources":["../src/_proof.ts"],"sourcesContent":["import { bytesToBase64Url } from '@vielzeug/arsenal';\nimport { canonicalSdp } from './_sdp';\nimport { MeshUnsupportedError } from './errors';\n\n/**\n * Proof of invitation possession: HMAC-SHA-256 of the guest SDP keyed by the\n * invitation secret. The SDP is canonicalized first so the proof is stable\n * across pairing codecs that rebuild the description (see `mq2.`). The host\n * recomputes the proof without receiving the secret back.\n *\n * WebCrypto is required — a non-cryptographic fallback would make the proof\n * forgeable, which defeats its purpose. Environments without `SubtleCrypto`\n * (insecure contexts) must pair over `https://` or `localhost` instead.\n *\n * @throws {MeshUnsupportedError} When `SubtleCrypto` is unavailable.\n */\nexport async function createProof(secret: string, sdp: string): Promise<string> {\n const subtle = globalThis.crypto?.subtle;\n if (!subtle) {\n throw new MeshUnsupportedError('WebCrypto is unavailable — pairing requires a secure context');\n }\n const encoder = new TextEncoder();\n const key = await subtle.importKey('raw', encoder.encode(secret), { hash: 'SHA-256', name: 'HMAC' }, false, ['sign']);\n const signature = await subtle.sign('HMAC', key, encoder.encode(canonicalSdp(sdp)));\n return bytesToBase64Url(new Uint8Array(signature));\n}\n"],"mappings":"2FAgBA,eAAsB,YAAY,EAAgB,EAA8B,CAC9E,IAAM,EAAS,WAAW,QAAQ,OAClC,GAAI,CAAC,EACH,MAAM,IAAI,EAAA,qBAAqB,8DAA8D,EAE/F,IAAM,EAAU,IAAI,YACd,EAAM,MAAM,EAAO,UAAU,MAAO,EAAQ,OAAO,CAAM,EAAG,CAAE,KAAM,UAAW,KAAM,MAAO,EAAG,GAAO,CAAC,MAAM,CAAC,EAC9G,EAAY,MAAM,EAAO,KAAK,OAAQ,EAAK,EAAQ,OAAO,EAAA,aAAa,CAAG,CAAC,CAAC,EAClF,OAAA,EAAO,EAAA,iBAAA,CAAiB,IAAI,WAAW,CAAS,CAAC,CACnD"}
package/dist/_proof.d.ts CHANGED
@@ -2,10 +2,13 @@
2
2
  * Proof of invitation possession: HMAC-SHA-256 of the guest SDP keyed by the
3
3
  * invitation secret. The SDP is canonicalized first so the proof is stable
4
4
  * across pairing codecs that rebuild the description (see `mq2.`). The host
5
- * recomputes the proof without receiving the secret back. Falls back to a
6
- * deterministic non-cryptographic hash when `SubtleCrypto` is unavailable —
7
- * the caller reports a `'security-downgrade'` tap so consumers can refuse
8
- * degraded environments.
5
+ * recomputes the proof without receiving the secret back.
6
+ *
7
+ * WebCrypto is required — a non-cryptographic fallback would make the proof
8
+ * forgeable, which defeats its purpose. Environments without `SubtleCrypto`
9
+ * (insecure contexts) must pair over `https://` or `localhost` instead.
10
+ *
11
+ * @throws {MeshUnsupportedError} When `SubtleCrypto` is unavailable.
9
12
  */
10
- export declare function createProof(secret: string, sdp: string, onDowngrade: () => void): Promise<string>;
13
+ export declare function createProof(secret: string, sdp: string): Promise<string>;
11
14
  //# sourceMappingURL=_proof.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"_proof.d.ts","sourceRoot":"","sources":["../src/_proof.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAcvG"}
1
+ {"version":3,"file":"_proof.d.ts","sourceRoot":"","sources":["../src/_proof.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAS9E"}
package/dist/_proof.js CHANGED
@@ -1,22 +1,2 @@
1
- import { bytesToBase64Url as e } from "./_base64.js";
2
- import { canonicalSdp as t } from "./_sdp.js";
3
- import { hash as n } from "@vielzeug/arsenal";
4
- //#region src/_proof.ts
5
- async function r(r, i, a) {
6
- let o = t(i), s = globalThis.crypto?.subtle;
7
- if (s) {
8
- let t = new TextEncoder(), n = await s.importKey("raw", t.encode(r), {
9
- hash: "SHA-256",
10
- name: "HMAC"
11
- }, !1, ["sign"]), i = await s.sign("HMAC", n, t.encode(o));
12
- return e(new Uint8Array(i));
13
- }
14
- return a(), n({
15
- sdp: o,
16
- secret: r
17
- });
18
- }
19
- //#endregion
20
- export { r as createProof };
21
-
1
+ import{MeshUnsupportedError as e}from"./errors.js";import{canonicalSdp as t}from"./_sdp.js";import{bytesToBase64Url as n}from"@vielzeug/arsenal";async function createProof(r,i){let a=globalThis.crypto?.subtle;if(!a)throw new e(`WebCrypto is unavailable — pairing requires a secure context`);let o=new TextEncoder,s=await a.importKey(`raw`,o.encode(r),{hash:`SHA-256`,name:`HMAC`},!1,[`sign`]),c=await a.sign(`HMAC`,s,o.encode(t(i)));return n(new Uint8Array(c))}export{createProof};
22
2
  //# sourceMappingURL=_proof.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"_proof.js","names":[],"sources":["../src/_proof.ts"],"sourcesContent":["import { hash } from '@vielzeug/arsenal';\nimport { bytesToBase64Url } from './_base64';\nimport { canonicalSdp } from './_sdp';\n\n/**\n * Proof of invitation possession: HMAC-SHA-256 of the guest SDP keyed by the\n * invitation secret. The SDP is canonicalized first so the proof is stable\n * across pairing codecs that rebuild the description (see `mq2.`). The host\n * recomputes the proof without receiving the secret back. Falls back to a\n * deterministic non-cryptographic hash when `SubtleCrypto` is unavailable —\n * the caller reports a `'security-downgrade'` tap so consumers can refuse\n * degraded environments.\n */\nexport async function createProof(secret: string, sdp: string, onDowngrade: () => void): Promise<string> {\n const canonical = canonicalSdp(sdp);\n const subtle = globalThis.crypto?.subtle;\n if (subtle) {\n const encoder = new TextEncoder();\n const key = await subtle.importKey('raw', encoder.encode(secret), { hash: 'SHA-256', name: 'HMAC' }, false, [\n 'sign',\n ]);\n const signature = await subtle.sign('HMAC', key, encoder.encode(canonical));\n return bytesToBase64Url(new Uint8Array(signature));\n }\n\n onDowngrade();\n return hash({ sdp: canonical, secret });\n}\n"],"mappings":";;;;AAaA,eAAsB,EAAY,GAAgB,GAAa,GAA0C;CACvG,IAAM,IAAY,EAAa,CAAG,GAC5B,IAAS,WAAW,QAAQ;CAClC,IAAI,GAAQ;EACV,IAAM,IAAU,IAAI,YAAY,GAC1B,IAAM,MAAM,EAAO,UAAU,OAAO,EAAQ,OAAO,CAAM,GAAG;GAAE,MAAM;GAAW,MAAM;EAAO,GAAG,IAAO,CAC1G,MACF,CAAC,GACK,IAAY,MAAM,EAAO,KAAK,QAAQ,GAAK,EAAQ,OAAO,CAAS,CAAC;EAC1E,OAAO,EAAiB,IAAI,WAAW,CAAS,CAAC;CACnD;CAGA,OADA,EAAY,GACL,EAAK;EAAE,KAAK;EAAW;CAAO,CAAC;AACxC"}
1
+ {"version":3,"file":"_proof.js","names":[],"sources":["../src/_proof.ts"],"sourcesContent":["import { bytesToBase64Url } from '@vielzeug/arsenal';\nimport { canonicalSdp } from './_sdp';\nimport { MeshUnsupportedError } from './errors';\n\n/**\n * Proof of invitation possession: HMAC-SHA-256 of the guest SDP keyed by the\n * invitation secret. The SDP is canonicalized first so the proof is stable\n * across pairing codecs that rebuild the description (see `mq2.`). The host\n * recomputes the proof without receiving the secret back.\n *\n * WebCrypto is required — a non-cryptographic fallback would make the proof\n * forgeable, which defeats its purpose. Environments without `SubtleCrypto`\n * (insecure contexts) must pair over `https://` or `localhost` instead.\n *\n * @throws {MeshUnsupportedError} When `SubtleCrypto` is unavailable.\n */\nexport async function createProof(secret: string, sdp: string): Promise<string> {\n const subtle = globalThis.crypto?.subtle;\n if (!subtle) {\n throw new MeshUnsupportedError('WebCrypto is unavailable — pairing requires a secure context');\n }\n const encoder = new TextEncoder();\n const key = await subtle.importKey('raw', encoder.encode(secret), { hash: 'SHA-256', name: 'HMAC' }, false, ['sign']);\n const signature = await subtle.sign('HMAC', key, encoder.encode(canonicalSdp(sdp)));\n return bytesToBase64Url(new Uint8Array(signature));\n}\n"],"mappings":"iJAgBA,eAAsB,YAAY,EAAgB,EAA8B,CAC9E,IAAM,EAAS,WAAW,QAAQ,OAClC,GAAI,CAAC,EACH,MAAM,IAAI,EAAqB,8DAA8D,EAE/F,IAAM,EAAU,IAAI,YACd,EAAM,MAAM,EAAO,UAAU,MAAO,EAAQ,OAAO,CAAM,EAAG,CAAE,KAAM,UAAW,KAAM,MAAO,EAAG,GAAO,CAAC,MAAM,CAAC,EAC9G,EAAY,MAAM,EAAO,KAAK,OAAQ,EAAK,EAAQ,OAAO,EAAa,CAAG,CAAC,CAAC,EAClF,OAAO,EAAiB,IAAI,WAAW,CAAS,CAAC,CACnD"}
package/dist/_random.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./_base64.cjs");let t=require("@vielzeug/arsenal");function n(e,n){let r=new Uint8Array(e);if(n)for(let i=0;i<e;i++)r[i]=Math.floor((0,t.randomFloat)(n)*256);else crypto.getRandomValues(r);return r}function r(t){return e.bytesToBase64Url(n(16,t))}exports.randomBytes=n,exports.randomId=r;
1
+ let e=require("@vielzeug/arsenal");function randomBytes(t,n){let r=new Uint8Array(t);if(n)for(let i=0;i<t;i++)r[i]=Math.floor((0,e.randomFloat)(n)*256);else crypto.getRandomValues(r);return r}function randomId(t){return(0,e.bytesToBase64Url)(randomBytes(16,t))}exports.randomBytes=randomBytes,exports.randomId=randomId;
2
2
  //# sourceMappingURL=_random.cjs.map