@lumencast/runtime 0.9.0 → 0.11.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 (100) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/app.d.ts +6 -1
  3. package/dist/app.d.ts.map +1 -1
  4. package/dist/app.js +3 -1
  5. package/dist/app.js.map +1 -1
  6. package/dist/{broadcast-ryjLRD5q.js → broadcast-DtHoU_fS.js} +3 -3
  7. package/dist/{broadcast-ryjLRD5q.js.map → broadcast-DtHoU_fS.js.map} +1 -1
  8. package/dist/{control-AgxbXOVS.js → control-B9frEbNG.js} +4 -4
  9. package/dist/{control-AgxbXOVS.js.map → control-B9frEbNG.js.map} +1 -1
  10. package/dist/index-Dz27r92m.js +1327 -0
  11. package/dist/index-Dz27r92m.js.map +1 -0
  12. package/dist/index.d.ts +3 -0
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.html +1 -1
  15. package/dist/index.js +16 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/lumencast.js +21 -12
  18. package/dist/mount.d.ts.map +1 -1
  19. package/dist/mount.js +22 -0
  20. package/dist/mount.js.map +1 -1
  21. package/dist/overlay/runtime-context.d.ts +10 -0
  22. package/dist/overlay/runtime-context.d.ts.map +1 -1
  23. package/dist/overlay/runtime-context.js.map +1 -1
  24. package/dist/render/bundle.d.ts +1 -1
  25. package/dist/render/bundle.d.ts.map +1 -1
  26. package/dist/render/bundle.js.map +1 -1
  27. package/dist/render/primitives/capture.d.ts +13 -4
  28. package/dist/render/primitives/capture.d.ts.map +1 -1
  29. package/dist/render/primitives/capture.js +54 -22
  30. package/dist/render/primitives/capture.js.map +1 -1
  31. package/dist/render/primitives/index.d.ts.map +1 -1
  32. package/dist/render/primitives/index.js +10 -0
  33. package/dist/render/primitives/index.js.map +1 -1
  34. package/dist/render/primitives/live-peer-video.d.ts +27 -0
  35. package/dist/render/primitives/live-peer-video.d.ts.map +1 -0
  36. package/dist/render/primitives/live-peer-video.js +64 -0
  37. package/dist/render/primitives/live-peer-video.js.map +1 -0
  38. package/dist/render/primitives/media.d.ts +37 -12
  39. package/dist/render/primitives/media.d.ts.map +1 -1
  40. package/dist/render/primitives/media.js +43 -17
  41. package/dist/render/primitives/media.js.map +1 -1
  42. package/dist/render/primitives/meet-peer-slot.d.ts +29 -0
  43. package/dist/render/primitives/meet-peer-slot.d.ts.map +1 -0
  44. package/dist/render/primitives/meet-peer-slot.js +46 -0
  45. package/dist/render/primitives/meet-peer-slot.js.map +1 -0
  46. package/dist/render/primitives/meet-peer.d.ts +31 -0
  47. package/dist/render/primitives/meet-peer.d.ts.map +1 -0
  48. package/dist/render/primitives/meet-peer.js +46 -0
  49. package/dist/render/primitives/meet-peer.js.map +1 -0
  50. package/dist/render/prop-allowlist.d.ts.map +1 -1
  51. package/dist/render/prop-allowlist.js +27 -1
  52. package/dist/render/prop-allowlist.js.map +1 -1
  53. package/dist/render/tree.js +42 -8
  54. package/dist/render/tree.js.map +1 -1
  55. package/dist/state/reserved-leaves.d.ts +37 -0
  56. package/dist/state/reserved-leaves.d.ts.map +1 -0
  57. package/dist/state/reserved-leaves.js +96 -0
  58. package/dist/state/reserved-leaves.js.map +1 -0
  59. package/dist/{status-pill-BxCdj-KZ.js → status-pill-B2vBTwRC.js} +2 -2
  60. package/dist/{status-pill-BxCdj-KZ.js.map → status-pill-B2vBTwRC.js.map} +1 -1
  61. package/dist/{test-CaRHj_J6.js → test-DD2SBDku.js} +4 -4
  62. package/dist/{test-CaRHj_J6.js.map → test-DD2SBDku.js.map} +1 -1
  63. package/dist/{tree-BLIxJbD3.js → tree-CgU_sUwI.js} +581 -479
  64. package/dist/tree-CgU_sUwI.js.map +1 -0
  65. package/dist/types.d.ts +25 -0
  66. package/dist/types.d.ts.map +1 -1
  67. package/dist/webrtc/index.d.ts +76 -0
  68. package/dist/webrtc/index.d.ts.map +1 -0
  69. package/dist/webrtc/index.js +180 -0
  70. package/dist/webrtc/index.js.map +1 -0
  71. package/dist/webrtc/meet-viewer.d.ts +139 -0
  72. package/dist/webrtc/meet-viewer.d.ts.map +1 -0
  73. package/dist/webrtc/meet-viewer.js +379 -0
  74. package/dist/webrtc/meet-viewer.js.map +1 -0
  75. package/dist/webrtc/peer-stream-registry.d.ts +21 -0
  76. package/dist/webrtc/peer-stream-registry.d.ts.map +1 -0
  77. package/dist/webrtc/peer-stream-registry.js +77 -0
  78. package/dist/webrtc/peer-stream-registry.js.map +1 -0
  79. package/package.json +4 -4
  80. package/src/app.tsx +9 -0
  81. package/src/index.ts +47 -0
  82. package/src/mount.ts +23 -0
  83. package/src/overlay/runtime-context.tsx +10 -0
  84. package/src/render/bundle.ts +11 -1
  85. package/src/render/primitives/capture.tsx +73 -28
  86. package/src/render/primitives/index.ts +10 -0
  87. package/src/render/primitives/live-peer-video.tsx +90 -0
  88. package/src/render/primitives/media.tsx +66 -17
  89. package/src/render/primitives/meet-peer-slot.tsx +55 -0
  90. package/src/render/primitives/meet-peer.tsx +57 -0
  91. package/src/render/prop-allowlist.ts +27 -1
  92. package/src/render/tree.tsx +44 -8
  93. package/src/state/reserved-leaves.ts +121 -0
  94. package/src/types.ts +25 -0
  95. package/src/webrtc/index.ts +252 -0
  96. package/src/webrtc/meet-viewer.ts +497 -0
  97. package/src/webrtc/peer-stream-registry.ts +93 -0
  98. package/dist/index-DrXsLYhe.js +0 -903
  99. package/dist/index-DrXsLYhe.js.map +0 -1
  100. package/dist/tree-BLIxJbD3.js.map +0 -1
@@ -0,0 +1,1327 @@
1
+ import { batch as T, signal as w } from "@preact/signals-react";
2
+ import { createRoot as H } from "react-dom/client";
3
+ import { createContext as oe, useContext as G, lazy as _, Suspense as ae, createElement as ce, StrictMode as le } from "react";
4
+ import { jsx as f } from "react/jsx-runtime";
5
+ import { useSignals as ue } from "@preact/signals-react/runtime";
6
+ import { AnimatePresence as de, motion as he } from "framer-motion";
7
+ import { SequenceTracker as fe, encodeFrame as C, input as me, WS_SUBPROTOCOLS as pe, WS_SUBPROTOCOL_V1_1 as ge, subscribe as ve, decodeServerFrame as we, LumencastError as M } from "@lumencast/protocol";
8
+ const I = oe(null);
9
+ function X({
10
+ value: t,
11
+ children: e
12
+ }) {
13
+ return /* @__PURE__ */ f(I.Provider, { value: t, children: e });
14
+ }
15
+ function mt() {
16
+ const t = G(I);
17
+ if (!t)
18
+ throw new Error(
19
+ "Lumencast overlay components must be rendered inside LumencastRuntimeProvider"
20
+ );
21
+ return t;
22
+ }
23
+ function pt() {
24
+ return G(I);
25
+ }
26
+ const ye = _(
27
+ () => import("./broadcast-DtHoU_fS.js").then((t) => ({ default: t.BroadcastMode }))
28
+ ), be = _(
29
+ () => import("./control-B9frEbNG.js").then((t) => ({ default: t.ControlMode }))
30
+ ), Se = _(() => import("./test-DD2SBDku.js").then((t) => ({ default: t.TestMode })));
31
+ function ke({
32
+ mode: t,
33
+ store: e,
34
+ bundleSignal: s,
35
+ statusSignal: n,
36
+ crossfadeKeySignal: r,
37
+ sendInput: i,
38
+ resolveCaptureDevice: a,
39
+ resolvePeerStream: o,
40
+ subscribePeerStream: u
41
+ }) {
42
+ ue();
43
+ const c = s.value, d = n.value, m = r.value;
44
+ if (!c) return null;
45
+ const p = t === "broadcast" ? ye : t === "control" ? be : Se;
46
+ return /* @__PURE__ */ f(de, { mode: "sync", children: /* @__PURE__ */ f(
47
+ he.div,
48
+ {
49
+ initial: { opacity: 0 },
50
+ animate: { opacity: 1 },
51
+ exit: { opacity: 0 },
52
+ transition: { duration: 0.4, ease: "easeInOut" },
53
+ style: { position: "absolute", inset: 0 },
54
+ children: /* @__PURE__ */ f(
55
+ X,
56
+ {
57
+ value: {
58
+ mode: t,
59
+ store: e,
60
+ bundle: c,
61
+ status: d,
62
+ sendInput: i,
63
+ ...a !== void 0 ? { resolveCaptureDevice: a } : {},
64
+ ...o !== void 0 ? { resolvePeerStream: o } : {},
65
+ ...u !== void 0 ? { subscribePeerStream: u } : {}
66
+ },
67
+ children: /* @__PURE__ */ f(ae, { fallback: null, children: /* @__PURE__ */ f(p, {}) })
68
+ }
69
+ )
70
+ },
71
+ m
72
+ ) });
73
+ }
74
+ const Te = "<anon>", b = /* @__PURE__ */ new Set();
75
+ function V(t) {
76
+ return b.add(t), () => {
77
+ b.delete(t);
78
+ };
79
+ }
80
+ function K(t, e, s) {
81
+ const n = { nodeId: t ?? Te, field: e, reason: s };
82
+ if (b.size > 0) {
83
+ for (const r of b)
84
+ try {
85
+ r(n);
86
+ } catch {
87
+ }
88
+ return;
89
+ }
90
+ }
91
+ const Ee = 100, _e = 4, Ie = 64, Re = {
92
+ blur: Ee,
93
+ brightness: _e
94
+ };
95
+ function N(t, e) {
96
+ if (typeof e != "number" || !Number.isFinite(e) || e < 0 || Object.is(e, -0)) return null;
97
+ const s = Re[t];
98
+ return e > s ? s : e;
99
+ }
100
+ const Ae = /^blur\((\d{1,7}(?:\.\d{1,4})?)px\) brightness\((\d{1,7}(?:\.\d{1,4})?)\)$/, gt = "blur(0px) brightness(1)";
101
+ function Oe(t) {
102
+ if (typeof t != "string" || t.length === 0 || t.length > Ie) return null;
103
+ const e = Ae.exec(t);
104
+ if (!e) return null;
105
+ const s = N("blur", Number(e[1])), n = N("brightness", Number(e[2]));
106
+ return s === null || n === null ? null : `blur(${s}px) brightness(${n})`;
107
+ }
108
+ function Pe(t, e) {
109
+ K(
110
+ e,
111
+ t,
112
+ "rejected unsafe filter value : outside the R8 caps or not a finite number >= 0"
113
+ );
114
+ }
115
+ const Le = { duration: 0 }, Ce = {
116
+ linear: "linear",
117
+ "cubic-in": "easeIn",
118
+ "cubic-out": "easeOut",
119
+ "cubic-in-out": "easeInOut"
120
+ };
121
+ function vt(t) {
122
+ return !t || t.kind === "none" ? Le : t.kind === "tween" ? {
123
+ type: "tween",
124
+ duration: (t.duration_ms ?? 0) / 1e3,
125
+ ease: t.ease ? Ce[t.ease] ?? "easeOut" : "easeOut"
126
+ } : t.kind === "spring" ? {
127
+ type: "spring",
128
+ ...t.stiffness !== void 0 ? { stiffness: t.stiffness } : {},
129
+ ...t.damping !== void 0 ? { damping: t.damping } : {},
130
+ ...t.mass !== void 0 ? { mass: t.mass } : {}
131
+ } : {
132
+ type: "tween",
133
+ duration: (t.duration_ms ?? 400) / 1e3,
134
+ ease: "easeInOut"
135
+ };
136
+ }
137
+ const Me = {
138
+ opacity: 1,
139
+ scale: 1,
140
+ scaleX: 1,
141
+ scaleY: 1,
142
+ rotate: 0,
143
+ x: 0,
144
+ y: 0,
145
+ // LSML §6.1 filter identity — both functions are always present so
146
+ // framer interpolates between structurally-identical filter lists
147
+ // (the compiler emits the same two-function form, clamped per R8).
148
+ filter: "blur(0px) brightness(1)"
149
+ }, Ne = {
150
+ kind: "tween",
151
+ duration_ms: 400,
152
+ ease: "cubic-out"
153
+ };
154
+ function wt(t, e, s) {
155
+ for (const n of e) {
156
+ const r = t(n);
157
+ if (r !== void 0) return r;
158
+ }
159
+ if (s && Object.keys(s).length > 0) {
160
+ for (const n of Object.keys(s)) {
161
+ const r = t(n);
162
+ if (r !== void 0) return r;
163
+ }
164
+ return Ne;
165
+ }
166
+ }
167
+ function yt(t, e, s) {
168
+ if (!e || Object.keys(e).length === 0)
169
+ return { initial: t, animate: t };
170
+ let n = e;
171
+ if (e.filter !== void 0) {
172
+ const i = Oe(e.filter);
173
+ n = { ...e }, i === null ? (Pe("animate_initial.filter", s), delete n.filter) : n.filter = i;
174
+ }
175
+ const r = { ...t };
176
+ for (const i of Object.keys(n))
177
+ i in r || (r[i] = Me[i] ?? 0);
178
+ return { initial: n, animate: r };
179
+ }
180
+ function xe(t) {
181
+ if (typeof t != "object" || t === null) return;
182
+ const e = t, s = e.kind;
183
+ if (s === "snap")
184
+ return { kind: "none" };
185
+ if (s === "tween") {
186
+ const n = typeof e.duration_ms == "number" ? e.duration_ms : 0, r = je[e.easing] ?? "cubic-out";
187
+ return { kind: "tween", duration_ms: n, ease: r };
188
+ }
189
+ if (s === "spring") {
190
+ const n = { kind: "spring" };
191
+ return typeof e.stiffness == "number" && (n.stiffness = e.stiffness), typeof e.damping == "number" && (n.damping = e.damping), typeof e.mass == "number" && (n.mass = e.mass), n;
192
+ }
193
+ }
194
+ const je = {
195
+ linear: "linear",
196
+ "ease-in": "cubic-in",
197
+ "ease-out": "cubic-out",
198
+ "ease-in-out": "cubic-in-out"
199
+ };
200
+ function De(t, e) {
201
+ T(() => {
202
+ for (const s of e.patches) {
203
+ const n = xe(s.transition);
204
+ n !== void 0 ? t.setWithTransition(s.path, s.value, n) : t.set(s.path, s.value);
205
+ }
206
+ });
207
+ }
208
+ function Ue(t, e) {
209
+ t.reset(e.state);
210
+ }
211
+ const bt = "__cam.", E = "__cam.slots.", J = "__cam.viewer";
212
+ function x(t) {
213
+ return t === J || t.startsWith(E);
214
+ }
215
+ function $e(t) {
216
+ const e = {};
217
+ let s;
218
+ for (const [n, r] of t)
219
+ if (n === J)
220
+ r != null && (s = r);
221
+ else if (n.startsWith(E)) {
222
+ const i = n.slice(E.length);
223
+ i !== "" && typeof r == "string" && r !== "" && (e[i] = r);
224
+ }
225
+ return s !== void 0 ? { viewer: s, slots: e } : { slots: e };
226
+ }
227
+ function j(t) {
228
+ const e = Object.keys(t.slots).sort().map((n) => `${n}=${t.slots[n]}`).join("&"), s = t.viewer === void 0 ? "" : JSON.stringify(t.viewer);
229
+ return `${e}|${s}`;
230
+ }
231
+ function Fe(t) {
232
+ const e = /* @__PURE__ */ new Map();
233
+ let s = j({ slots: {} });
234
+ const n = () => {
235
+ const r = $e(e), i = j(r);
236
+ i !== s && (s = i, t(r));
237
+ };
238
+ return {
239
+ onSnapshot(r) {
240
+ e.clear();
241
+ for (const [i, a] of Object.entries(r))
242
+ x(i) && e.set(i, a);
243
+ n();
244
+ },
245
+ onDelta(r) {
246
+ let i = !1;
247
+ for (const a of r)
248
+ x(a.path) && (e.set(a.path, a.value), i = !0);
249
+ i && n();
250
+ }
251
+ };
252
+ }
253
+ class We {
254
+ signals = /* @__PURE__ */ new Map();
255
+ transitions = /* @__PURE__ */ new Map();
256
+ signal(e) {
257
+ let s = this.signals.get(e);
258
+ return s || (s = w(void 0), this.signals.set(e, s)), s;
259
+ }
260
+ transitionSignal(e) {
261
+ let s = this.transitions.get(e);
262
+ return s || (s = w(void 0), this.transitions.set(e, s)), s;
263
+ }
264
+ set(e, s) {
265
+ const n = this.signal(e);
266
+ k(n.peek(), s) || (n.value = s);
267
+ }
268
+ setWithTransition(e, s, n) {
269
+ T(() => {
270
+ const r = this.transitionSignal(e);
271
+ r.peek() !== n && (r.value = n);
272
+ const i = this.signal(e);
273
+ k(i.peek(), s) || (i.value = s);
274
+ });
275
+ }
276
+ reset(e) {
277
+ T(() => {
278
+ const s = /* @__PURE__ */ new Set();
279
+ for (const [n, r] of Object.entries(e)) {
280
+ s.add(n);
281
+ const i = this.signal(n);
282
+ k(i.peek(), r) || (i.value = r);
283
+ const a = this.transitions.get(n);
284
+ a && a.peek() !== void 0 && (a.value = void 0);
285
+ }
286
+ for (const n of this.signals.keys())
287
+ if (!s.has(n)) {
288
+ const r = this.signals.get(n);
289
+ r && r.peek() !== void 0 && (r.value = void 0);
290
+ }
291
+ });
292
+ }
293
+ toRecord() {
294
+ const e = {};
295
+ for (const [s, n] of this.signals.entries())
296
+ e[s] = n.peek();
297
+ return e;
298
+ }
299
+ }
300
+ function Y() {
301
+ return new We();
302
+ }
303
+ function k(t, e) {
304
+ if (t === e) return !0;
305
+ if (t === null || e === null || typeof t != typeof e || typeof t != "object" || Array.isArray(t) !== Array.isArray(e)) return !1;
306
+ if (Array.isArray(t) && Array.isArray(e)) {
307
+ if (t.length !== e.length) return !1;
308
+ for (let a = 0; a < t.length; a++)
309
+ if (t[a] !== e[a]) return !1;
310
+ return !0;
311
+ }
312
+ const s = t, n = e, r = Object.keys(s), i = Object.keys(n);
313
+ if (r.length !== i.length) return !1;
314
+ for (const a of r)
315
+ if (s[a] !== n[a]) return !1;
316
+ return !0;
317
+ }
318
+ const qe = /* @__PURE__ */ new Set([
319
+ "x-lumencast.color-srgb-1.0",
320
+ // RFC-0001 / ADR 004 — this runtime ships the Zab capture plugin, so a
321
+ // bundle declaring `x-zab.capture/1` in `profiles[]` is compatible (it is
322
+ // NOT rejected as BUNDLE_INCOMPATIBLE, §17.3.1).
323
+ "x-zab.capture/1"
324
+ ]), ze = /^x-[a-z0-9-]+(?:\.[a-z0-9-]+)*$/, Be = /^(?:0|[1-9][0-9]*)$/, D = ".authoring";
325
+ function He(t) {
326
+ const e = t.indexOf("/");
327
+ if (e < 0) return !1;
328
+ const s = t.slice(0, e), n = t.slice(e + 1);
329
+ return !Be.test(n) || !s.endsWith(D) ? !1 : ze.test(s.slice(0, -D.length));
330
+ }
331
+ class U extends Error {
332
+ code = "BUNDLE_INCOMPATIBLE";
333
+ unsupportedProfiles;
334
+ constructor(e) {
335
+ super(
336
+ `BUNDLE_INCOMPATIBLE: profile(s) not supported by this runtime: ${e.join(
337
+ ", "
338
+ )}`
339
+ ), this.name = "BundleIncompatibleError", this.unsupportedProfiles = e;
340
+ }
341
+ }
342
+ function $(t, e = qe) {
343
+ const s = t.profiles;
344
+ if (!s) return;
345
+ if (!Array.isArray(s))
346
+ throw new U(["<malformed: profiles is not an array>"]);
347
+ if (s.length === 0) return;
348
+ const n = s.filter((r) => typeof r != "string" || !He(r) && !e.has(r)).map((r) => typeof r == "string" ? r : "<malformed: non-string profile entry>");
349
+ if (n.length > 0)
350
+ throw new U(n);
351
+ }
352
+ class Ge {
353
+ cache = /* @__PURE__ */ new Map();
354
+ baseUrl;
355
+ pathPrefix;
356
+ resolveUrl;
357
+ getAuthToken;
358
+ fetchImpl;
359
+ constructor(e) {
360
+ this.baseUrl = e.baseUrl.replace(/\/$/, ""), this.pathPrefix = (e.pathPrefix ?? "/lsdp/v1/scenes").replace(/\/$/, ""), this.resolveUrl = e.resolveUrl, this.getAuthToken = e.getAuthToken, this.fetchImpl = e.fetchImpl ?? globalThis.fetch.bind(globalThis);
361
+ }
362
+ /** Build the request init carrying the bearer token, if any. Returns
363
+ * `undefined` when no token is available — the fetch stays header-less,
364
+ * preserving v0.5.0 behaviour. */
365
+ async buildInit() {
366
+ if (!this.getAuthToken) return;
367
+ const e = await this.getAuthToken();
368
+ if (e)
369
+ return { headers: { Authorization: `Bearer ${e}` } };
370
+ }
371
+ buildUrl(e, s) {
372
+ return this.resolveUrl ? this.resolveUrl(e, s) : `${this.baseUrl}${this.pathPrefix}/${encodeURIComponent(e)}/bundle?v=${encodeURIComponent(s)}`;
373
+ }
374
+ preload(e) {
375
+ $(e), this.cache.set(e.scene_version, e);
376
+ }
377
+ async get(e, s) {
378
+ const n = this.cache.get(s);
379
+ if (n) return n;
380
+ const r = this.buildUrl(e, s), i = await this.buildInit(), a = i ? await this.fetchImpl(r, i) : await this.fetchImpl(r);
381
+ if (!a.ok)
382
+ throw new Error(`bundle fetch failed: ${a.status} ${a.statusText}`);
383
+ const o = await a.json();
384
+ if (o.scene_version !== s)
385
+ throw new Error(
386
+ `bundle scene_version mismatch: expected ${s}, got ${o.scene_version}`
387
+ );
388
+ return $(o), this.cache.set(s, o), o;
389
+ }
390
+ }
391
+ function Xe(t) {
392
+ return new Ge(t);
393
+ }
394
+ const y = {
395
+ initial: 200,
396
+ max: 5e3,
397
+ factor: 2,
398
+ jitter: 0.2
399
+ };
400
+ class Ve {
401
+ constructor(e, s) {
402
+ this.opts = e, this.random = s;
403
+ }
404
+ _attempt = 0;
405
+ get attempt() {
406
+ return this._attempt;
407
+ }
408
+ delayFor(e) {
409
+ if (!Number.isInteger(e) || e < 1)
410
+ throw new RangeError(`attempt must be a positive integer, got ${e}`);
411
+ this._attempt = e;
412
+ const s = Math.min(
413
+ this.opts.initial * Math.pow(this.opts.factor, e - 1),
414
+ this.opts.max
415
+ );
416
+ if (this.opts.jitter <= 0) return s;
417
+ const n = (this.random() * 2 - 1) * this.opts.jitter * s;
418
+ return Math.max(0, s + n);
419
+ }
420
+ reset() {
421
+ this._attempt = 0;
422
+ }
423
+ }
424
+ function Ke(t = {}) {
425
+ const e = {
426
+ initial: t.initial ?? y.initial,
427
+ max: t.max ?? y.max,
428
+ factor: t.factor ?? y.factor,
429
+ jitter: t.jitter ?? y.jitter
430
+ };
431
+ if (e.initial <= 0) throw new RangeError("initial must be > 0");
432
+ if (e.max < e.initial) throw new RangeError("max must be >= initial");
433
+ if (e.factor < 1) throw new RangeError("factor must be >= 1");
434
+ if (e.jitter < 0 || e.jitter > 1) throw new RangeError("jitter must be within [0, 1]");
435
+ return new Ve(e, t.random ?? Math.random);
436
+ }
437
+ class g extends Error {
438
+ recoverable;
439
+ code;
440
+ cause;
441
+ constructor(e, s, n = "INTERNAL", r) {
442
+ super(e), this.name = "TransportError", this.recoverable = s, this.code = n, this.cause = r;
443
+ }
444
+ }
445
+ class Je {
446
+ status = "disconnected";
447
+ socket = null;
448
+ token;
449
+ url;
450
+ WebSocketCtor;
451
+ schedule;
452
+ seq = new fe();
453
+ opts;
454
+ scheduler;
455
+ reconnectTimer = null;
456
+ active = !0;
457
+ constructor(e) {
458
+ this.opts = e, this.url = e.url, this.token = e.token;
459
+ const s = e.webSocketImpl ?? globalThis.WebSocket;
460
+ if (!s)
461
+ throw new TypeError(
462
+ "Lumencast WsClient: no WebSocket implementation found in this environment"
463
+ );
464
+ this.WebSocketCtor = s, this.schedule = Ke(e.reconnect), this.scheduler = e.scheduler ?? {
465
+ setTimeout: globalThis.setTimeout.bind(globalThis),
466
+ clearTimeout: globalThis.clearTimeout.bind(globalThis)
467
+ };
468
+ }
469
+ /** Open and start the connection lifecycle. Idempotent. */
470
+ start() {
471
+ this.active && (this.socket || this.status === "connecting" || this.openSocket());
472
+ }
473
+ /** Resolve the current session token (the one used for the WS
474
+ * subscription). Mirrors `setToken` swaps. Used by the bundle fetcher to
475
+ * authenticate the render-bundle GET with the same credential. A
476
+ * `LumencastTokenProvider` is awaited. */
477
+ resolveCurrentToken() {
478
+ return F(this.token);
479
+ }
480
+ /** Send `input` patches to the server. No-op if not connected. */
481
+ sendInput(e) {
482
+ !this.socket || this.socket.readyState !== this.WebSocketCtor.OPEN || e.length !== 0 && this.socket.send(C(me(e)));
483
+ }
484
+ /** Replace the auth token. Closes and reopens with the new token. */
485
+ setToken(e) {
486
+ this.token = e, this.active && this.socket && (this.closeSocket(), this.scheduleReconnect(!0));
487
+ }
488
+ /** Tear down for good. No more reconnect attempts. */
489
+ close() {
490
+ this.active && (this.active = !1, this.cancelReconnect(), this.closeSocket(), this.setStatus("disconnected"));
491
+ }
492
+ // --- internals --------------------------------------------------
493
+ async openSocket() {
494
+ if (!this.active) return;
495
+ this.setStatus("connecting");
496
+ let e;
497
+ try {
498
+ e = await F(this.token);
499
+ } catch (n) {
500
+ this.opts.onTransportError?.(
501
+ new g(
502
+ `failed to resolve token: ${n.message}`,
503
+ !0,
504
+ "AUTH_DENIED",
505
+ n
506
+ )
507
+ ), this.scheduleReconnect();
508
+ return;
509
+ }
510
+ if (!this.active) return;
511
+ let s;
512
+ try {
513
+ s = new this.WebSocketCtor(this.url, [...pe]);
514
+ } catch (n) {
515
+ this.opts.onTransportError?.(
516
+ new g(
517
+ `failed to open WebSocket: ${n.message}`,
518
+ !0,
519
+ "INTERNAL",
520
+ n
521
+ )
522
+ ), this.scheduleReconnect();
523
+ return;
524
+ }
525
+ this.socket = s, s.onopen = () => this.handleOpen(e), s.onmessage = (n) => this.handleMessage(n), s.onerror = (n) => this.handleError(n), s.onclose = (n) => this.handleClose(n);
526
+ }
527
+ handleOpen(e) {
528
+ if (!this.socket) return;
529
+ const n = this.socket.protocol === ge && this.seq.last > 0, r = n ? this.seq.last : void 0;
530
+ n || this.seq.reset();
531
+ const i = ve({
532
+ token: e,
533
+ ...this.opts.scene !== void 0 ? { scene: this.opts.scene } : {},
534
+ ...this.opts.session !== void 0 ? { session: this.opts.session } : {},
535
+ ...r !== void 0 ? { since_sequence: r } : {}
536
+ });
537
+ this.socket.send(C(i));
538
+ }
539
+ handleMessage(e) {
540
+ const s = typeof e.data == "string" ? e.data : "";
541
+ if (!s) return;
542
+ let n;
543
+ try {
544
+ n = we(s);
545
+ } catch (r) {
546
+ const i = (r instanceof M, r.message), a = r instanceof M ? r.code : "INTERNAL";
547
+ this.opts.onTransportError?.(new g(`codec: ${i}`, !0, a, r)), this.closeSocket(), this.scheduleReconnect();
548
+ return;
549
+ }
550
+ if (n !== null)
551
+ switch (n.type) {
552
+ case "snapshot": {
553
+ if (n.seq < 1) {
554
+ this.opts.onTransportError?.(
555
+ new g(`snapshot seq must be >= 1, got ${n.seq}`, !0, "VERSION_GAP")
556
+ ), this.closeSocket(), this.scheduleReconnect();
557
+ return;
558
+ }
559
+ this.seq.observeSnapshot(n.seq), this.schedule.reset(), this.setStatus("live"), this.opts.onSnapshot?.(n);
560
+ return;
561
+ }
562
+ case "delta": {
563
+ const r = this.seq.observe(n.seq);
564
+ if (r.kind === "gap") {
565
+ this.opts.onTransportError?.(
566
+ new g(
567
+ `sequence gap: expected ${this.seq.last + 1}, got ${n.seq}`,
568
+ !0,
569
+ "VERSION_GAP"
570
+ )
571
+ ), this.closeSocket(), this.scheduleReconnect();
572
+ return;
573
+ }
574
+ if (r.kind === "duplicate") return;
575
+ this.opts.onDelta?.(n);
576
+ return;
577
+ }
578
+ case "scene_changed": {
579
+ this.seq.reset(), this.opts.onSceneChanged?.(n);
580
+ return;
581
+ }
582
+ case "error": {
583
+ this.opts.onServerError?.(n), n.recoverable || this.close();
584
+ return;
585
+ }
586
+ case "pong":
587
+ return;
588
+ }
589
+ }
590
+ handleError(e) {
591
+ }
592
+ handleClose(e) {
593
+ if (this.socket = null, !this.active) {
594
+ this.setStatus("disconnected");
595
+ return;
596
+ }
597
+ if (e.code === 4401 || e.code === 4403 || e.code === 1008) {
598
+ this.opts.onTransportError?.(
599
+ new g(`server closed: ${e.code} ${e.reason}`, !1, "AUTH_DENIED")
600
+ ), this.close();
601
+ return;
602
+ }
603
+ this.scheduleReconnect();
604
+ }
605
+ scheduleReconnect(e = !1) {
606
+ if (!this.active) return;
607
+ this.cancelReconnect();
608
+ const s = (this.schedule.attempt || 0) + 1, n = e ? 0 : this.schedule.delayFor(s);
609
+ this.setStatus("disconnected"), this.reconnectTimer = this.scheduler.setTimeout(() => {
610
+ this.reconnectTimer = null, this.openSocket();
611
+ }, n);
612
+ }
613
+ cancelReconnect() {
614
+ this.reconnectTimer && (this.scheduler.clearTimeout(this.reconnectTimer), this.reconnectTimer = null);
615
+ }
616
+ closeSocket() {
617
+ if (this.socket) {
618
+ try {
619
+ this.socket.close(1e3, "client closing");
620
+ } catch {
621
+ }
622
+ this.socket = null;
623
+ }
624
+ }
625
+ setStatus(e) {
626
+ this.status !== e && (this.status = e, this.opts.onStatus?.(e));
627
+ }
628
+ }
629
+ async function F(t) {
630
+ return typeof t == "string" ? t : await t.fetch();
631
+ }
632
+ function Ye(t) {
633
+ if (!(t.target instanceof HTMLElement))
634
+ throw new TypeError("mount: `target` must be an HTMLElement");
635
+ if (typeof t.serverUrl != "string" || t.serverUrl.length === 0)
636
+ throw new TypeError("mount: `serverUrl` must be a non-empty string");
637
+ if (t.mode === "test") {
638
+ if (!t.testSession)
639
+ throw new TypeError("mount: `testSession` is required when mode === 'test'");
640
+ if (!t.scene)
641
+ throw new TypeError("mount: `scene` is required when mode === 'test'");
642
+ }
643
+ }
644
+ function St(t) {
645
+ Ye(t), t.onStatus?.("disconnected");
646
+ const e = Y(), s = Qe(t.serverUrl), n = Xe({
647
+ baseUrl: s,
648
+ ...t.resolveBundleUrl !== void 0 ? { resolveUrl: t.resolveBundleUrl } : {},
649
+ getAuthToken: () => p.resolveCurrentToken()
650
+ }), r = w(null), i = w("disconnected"), a = w("__initial__"), o = (l) => {
651
+ i.value = l, t.onStatus?.(l);
652
+ }, u = (l) => {
653
+ t.onError?.(l);
654
+ };
655
+ let c = !0;
656
+ const d = t.onReservedLeaves ? Fe(t.onReservedLeaves) : void 0, m = t.onDiagnostic ? V(t.onDiagnostic) : void 0, p = new Je({
657
+ url: t.serverUrl,
658
+ token: t.token,
659
+ ...t.scene !== void 0 ? { scene: t.scene } : {},
660
+ ...t.testSession !== void 0 ? { session: t.testSession } : {},
661
+ onStatus: o,
662
+ onSnapshot: (l) => {
663
+ c && (d?.onSnapshot(l.state), se(
664
+ n,
665
+ r,
666
+ a,
667
+ l.scene_id,
668
+ l.scene_version,
669
+ () => Ue(e, l),
670
+ u
671
+ ), t.onMetric?.({
672
+ name: "snapshot_received",
673
+ scene_id: l.scene_id,
674
+ path_count: Object.keys(l.state).length
675
+ }));
676
+ },
677
+ onDelta: (l) => {
678
+ if (!c) return;
679
+ const S = performance.now();
680
+ De(e, l), d?.onDelta(l.patches), t.onMetric?.({
681
+ name: "delta_applied",
682
+ duration_ms: performance.now() - S
683
+ }), t.onMetric?.({ name: "delta_received", count: 1, path_count: l.patches.length });
684
+ },
685
+ onSceneChanged: (l) => {
686
+ c && t.onMetric?.({
687
+ name: "scene_changed",
688
+ from: r.value?.scene_version ?? null,
689
+ to: l.scene_version
690
+ });
691
+ },
692
+ onServerError: (l) => {
693
+ u({
694
+ code: l.code,
695
+ message: l.message,
696
+ recoverable: l.recoverable
697
+ });
698
+ },
699
+ onTransportError: (l) => {
700
+ u(Ze(l));
701
+ }
702
+ });
703
+ p.start();
704
+ const R = H(t.target);
705
+ return R.render(
706
+ ce(ke, {
707
+ mode: t.mode,
708
+ store: e,
709
+ bundleSignal: r,
710
+ statusSignal: i,
711
+ crossfadeKeySignal: a,
712
+ sendInput: (l) => p.sendInput(l),
713
+ // ADR 004 §A1.3 — thread the host capture resolver to the runtime context
714
+ // so the `x-zab.capture` primitive's ACQUIRE mode can pin a device.
715
+ ...t.resolveCaptureDevice !== void 0 ? { resolveCaptureDevice: t.resolveCaptureDevice } : {},
716
+ // ADR 006 #4 — thread the host peer-stream resolver (supplied by the
717
+ // WebRTC viewer #3) so the `media` primitive's LIVE mode can render a
718
+ // peer's MediaStream in `srcObject`.
719
+ ...t.resolvePeerStream !== void 0 ? { resolvePeerStream: t.resolvePeerStream } : {},
720
+ // ADR 006 #3 — reactive variant : the LIVE `media` node re-renders when a
721
+ // peer connects/leaves mid-show. `createPeerViewer()` supplies it.
722
+ ...t.subscribePeerStream !== void 0 ? { subscribePeerStream: t.subscribePeerStream } : {}
723
+ })
724
+ ), {
725
+ disconnect() {
726
+ c && (c = !1, m?.(), p.close(), R.unmount());
727
+ },
728
+ setToken(l) {
729
+ c && p.setToken(l);
730
+ }
731
+ };
732
+ async function se(l, S, ne, A, O, re, ie) {
733
+ let P;
734
+ try {
735
+ P = await l.get(A, O);
736
+ } catch (L) {
737
+ ie({
738
+ code: "BUNDLE_FETCH_FAILED",
739
+ message: L instanceof Error ? L.message : "render bundle fetch failed",
740
+ recoverable: !0
741
+ });
742
+ return;
743
+ }
744
+ c && (re(), S.value = P, ne.value = `${A}::${O}`);
745
+ }
746
+ }
747
+ function Ze(t) {
748
+ return {
749
+ code: t.code,
750
+ message: t.message,
751
+ recoverable: t.recoverable
752
+ };
753
+ }
754
+ function Qe(t) {
755
+ try {
756
+ const e = new URL(t);
757
+ return `${e.protocol === "wss:" ? "https:" : "http:"}//${e.host}`;
758
+ } catch {
759
+ return "";
760
+ }
761
+ }
762
+ const et = [
763
+ "visible",
764
+ "opacity",
765
+ "universal_opacity",
766
+ "rotation",
767
+ "sizing",
768
+ "x",
769
+ "y",
770
+ "width",
771
+ "height",
772
+ // ADR 002 §3.2 (D2 / #D) — `blendMode` is consumed universally by the
773
+ // wrapper (→ CSS `mix-blend-mode`) on every primitive.
774
+ "blendMode",
775
+ // ADR 002 §3.2 (#E) — a typed `mask` is lowered onto EVERY primitive by the
776
+ // compiler and consumed by the Tree (built into a `<mask>` SVG element).
777
+ "mask"
778
+ ];
779
+ function h(t) {
780
+ return /* @__PURE__ */ new Set([...et, ...t]);
781
+ }
782
+ const tt = {
783
+ stack: h(["direction", "gap", "wrap", "crossGap", "align", "justify"]),
784
+ grid: h(["cols", "rows", "gap"]),
785
+ frame: h([
786
+ "x",
787
+ "y",
788
+ "width",
789
+ "height",
790
+ "scale",
791
+ "rotate",
792
+ "background",
793
+ "backgrounds",
794
+ "clipsContent"
795
+ ]),
796
+ text: h([
797
+ "value",
798
+ "size",
799
+ "font",
800
+ "weight",
801
+ "colour",
802
+ "align",
803
+ "lineHeight",
804
+ "letterSpacing",
805
+ "textTransform",
806
+ "textDecoration",
807
+ "fontStyle",
808
+ "maxLines"
809
+ ]),
810
+ image: h(["src", "alt", "fit", "position", "width", "height"]),
811
+ shape: h([
812
+ "geometry",
813
+ "kind",
814
+ "width",
815
+ "height",
816
+ "radius",
817
+ "fill",
818
+ "fills",
819
+ "stroke",
820
+ "stroke_width",
821
+ "strokes",
822
+ "pathData",
823
+ "paths",
824
+ "ariaLabel"
825
+ ]),
826
+ // `peerLabel` (ADR 006 #4) selects the live MediaStream mode : a node whose
827
+ // source is a `meet.peer.peer_label` is rendered in `srcObject` from a host
828
+ // resolver instead of `<video src>`. Listed so it is NOT flagged as a silent
829
+ // drop by the anti-drop audit when a scene carries a live source.
830
+ media: h(["src", "peerLabel", "loop", "mute", "autoplay", "fit"]),
831
+ // ADR 006 §3.3/§3.5 — the unified source kind. `peer_label` is the stream
832
+ // reference (resolved to a MediaStream → srcObject) ; `object_fit`/`muted`
833
+ // drive the video ; `x-zab.sourceKind` is advisory ; `metadata` carries the
834
+ // editor round-trip (figma). Geometry is universal as flat `x/y/width/height`,
835
+ // but an UNCOMPILED from-scene node carries the NESTED `position`/`size` shape
836
+ // (the Tree flattens it as a fallback) — listed so neither form is flagged as
837
+ // a silent drop by the anti-drop audit.
838
+ "meet.peer": h([
839
+ "peer_label",
840
+ "object_fit",
841
+ "muted",
842
+ "x-zab.sourceKind",
843
+ "metadata",
844
+ "position",
845
+ "size"
846
+ ]),
847
+ instance: h(["scene_id", "scene_version", "size", "position"]),
848
+ // RFC-0001 / ADR 004 — vendor capture placeholder. `width`/`height` are the
849
+ // flattened geometry (universal) ; the `x-zab.*` props are carried as
850
+ // metadata (the renderer reserves the box, ignores deviceRef). Listed so
851
+ // they are NOT flagged as silent drops by the anti-drop audit.
852
+ "x-zab.capture": h(["x-zab.sourceKind", "x-zab.deviceRef", "width", "height"]),
853
+ // ADR Blue 009 §3.1 (Amendment 2) — vendor meet-peer SLOT placeholder.
854
+ // `width`/`height` are the flattened geometry (universal) ; `x-zab.slotRef`
855
+ // is the logical slot identity carried as metadata (the runtime resolves
856
+ // `slotRef → peer_label` from stream-level ZabCam state). NO cam/peer
857
+ // identity is carried. Listed so they are NOT flagged as silent drops.
858
+ "x-zab.meet-peer": h(["x-zab.slotRef", "width", "height"]),
859
+ // `repeat` is dispatched specially by the tree ; its only consumed
860
+ // binding is `items`.
861
+ repeat: /* @__PURE__ */ new Set(["items"])
862
+ };
863
+ function st(t, e) {
864
+ const s = tt[t];
865
+ return !!(s === void 0 || s.has(e) || t === "instance" && (e === "params" || e.startsWith("params.")));
866
+ }
867
+ const W = /* @__PURE__ */ new WeakSet();
868
+ function kt(t) {
869
+ if (W.has(t)) return;
870
+ W.add(t);
871
+ const e = /* @__PURE__ */ new Set([
872
+ ...Object.keys(t.props ?? {}),
873
+ ...Object.keys(t.bindings ?? {})
874
+ ]);
875
+ for (const s of e)
876
+ st(t.kind, s) || K(
877
+ t.id,
878
+ `${t.kind}.${s}`,
879
+ "is not consumed by this primitive's renderer ; the prop is ignored (anti-silent-drop, ADR 001 §3.4)"
880
+ );
881
+ }
882
+ class Z {
883
+ constructor(e) {
884
+ this.options = e, this.deps = {
885
+ WebSocket: e.deps?.WebSocket ?? globalThis.WebSocket,
886
+ RTCPeerConnection: e.deps?.RTCPeerConnection ?? globalThis.RTCPeerConnection,
887
+ MediaStream: e.deps?.MediaStream ?? globalThis.MediaStream
888
+ };
889
+ }
890
+ ws = null;
891
+ remotes = /* @__PURE__ */ new Map();
892
+ iceServers = [];
893
+ selfId = null;
894
+ listeners = /* @__PURE__ */ new Map();
895
+ deps;
896
+ on(e, s) {
897
+ const n = this.listeners.get(e) ?? /* @__PURE__ */ new Set();
898
+ return this.listeners.set(e, n), n.add(s), () => n.delete(s);
899
+ }
900
+ /** Join the room as a VIEWER (recvonly). No capture, no publish. */
901
+ join() {
902
+ return this.openSocket();
903
+ }
904
+ /** Leave and tear down every peer connection + aggregated stream. As the
905
+ * track owner, this is where the streams (and the device-side tracks) end. */
906
+ leave() {
907
+ this.send({ type: "leave" }), this.ws?.close(1e3, "viewer-leave");
908
+ }
909
+ /* ---- Socket ------------------------------------------------------- */
910
+ openSocket() {
911
+ const e = new URL(this.options.signalingUrl);
912
+ return e.searchParams.set("room", this.options.roomId), e.searchParams.set("token", this.options.token), new Promise((s, n) => {
913
+ const r = new this.deps.WebSocket(e.toString());
914
+ this.ws = r;
915
+ const i = () => {
916
+ r.removeEventListener("error", a), this.send({ type: "join", name: this.options.name, role: "viewer" }), s();
917
+ }, a = (o) => {
918
+ r.removeEventListener("open", i), n(o);
919
+ };
920
+ r.addEventListener("open", i, { once: !0 }), r.addEventListener("error", a, { once: !0 }), r.addEventListener("message", (o) => void this.onMessage(o.data)), r.addEventListener("close", (o) => {
921
+ this.tearDown(), this.emit("close", { code: o.code, reason: o.reason });
922
+ });
923
+ });
924
+ }
925
+ send(e) {
926
+ this.ws && this.ws.readyState === this.deps.WebSocket.OPEN && this.ws.send(JSON.stringify(e));
927
+ }
928
+ tearDown() {
929
+ for (const e of this.remotes.values()) e.pc.close();
930
+ this.remotes.clear();
931
+ }
932
+ /* ---- Protocol ----------------------------------------------------- */
933
+ async onMessage(e) {
934
+ let s;
935
+ try {
936
+ s = JSON.parse(String(e));
937
+ } catch {
938
+ return;
939
+ }
940
+ switch (s.type) {
941
+ case "joined": {
942
+ this.selfId = s.peerId, this.iceServers = s.turn.urls.map((n) => ({
943
+ urls: n,
944
+ username: s.turn.username,
945
+ credential: s.turn.credential
946
+ })), this.emit("joined", { peerId: s.peerId, peers: s.peers });
947
+ for (const n of s.peers) this.ensureRemote(n);
948
+ break;
949
+ }
950
+ case "peer-joined": {
951
+ this.emit("peer-joined", s.peer), this.ensureRemote(s.peer);
952
+ break;
953
+ }
954
+ case "peer-left": {
955
+ const n = this.remotes.get(s.peerId);
956
+ n && (n.pc.close(), this.remotes.delete(s.peerId), this.emit("peer-left", { peerId: s.peerId, peerName: n.info.name }));
957
+ break;
958
+ }
959
+ case "signal": {
960
+ await this.handleSignal(s.from, s.payload);
961
+ break;
962
+ }
963
+ case "error": {
964
+ this.emit("error", { code: s.code, message: s.message });
965
+ break;
966
+ }
967
+ }
968
+ }
969
+ async handleSignal(e, s) {
970
+ let n = this.remotes.get(e);
971
+ n || (n = this.ensureRemote({ id: e, name: e.slice(0, 8), role: "publisher" }));
972
+ const { pc: r } = n;
973
+ if (s.kind === "sdp") {
974
+ const i = s.description, a = i.type === "offer" && (n.makingOffer || r.signalingState !== "stable");
975
+ if (n.ignoreOffer = !this.isPolite(e) && a, n.ignoreOffer) return;
976
+ await r.setRemoteDescription(i);
977
+ for (const o of n.pendingCandidates)
978
+ try {
979
+ await r.addIceCandidate(o);
980
+ } catch {
981
+ }
982
+ n.pendingCandidates = [], i.type === "offer" && (await r.setLocalDescription(), r.localDescription && this.sendSignal(e, {
983
+ kind: "sdp",
984
+ description: {
985
+ type: r.localDescription.type,
986
+ sdp: r.localDescription.sdp
987
+ }
988
+ }));
989
+ return;
990
+ }
991
+ if (s.kind === "ice") {
992
+ const i = s.candidate;
993
+ if (r.remoteDescription)
994
+ try {
995
+ await r.addIceCandidate(i);
996
+ } catch (a) {
997
+ if (!n.ignoreOffer) throw a;
998
+ }
999
+ else
1000
+ n.pendingCandidates.push(i);
1001
+ return;
1002
+ }
1003
+ }
1004
+ /* ---- Peer setup --------------------------------------------------- */
1005
+ ensureRemote(e) {
1006
+ const s = this.remotes.get(e.id);
1007
+ if (s) return s;
1008
+ const n = new this.deps.RTCPeerConnection({
1009
+ iceServers: this.iceServers,
1010
+ iceTransportPolicy: "relay"
1011
+ }), r = new this.deps.MediaStream(), i = n.addTransceiver("audio", { direction: "recvonly" }), a = n.addTransceiver("video", { direction: "recvonly" });
1012
+ nt(i, a);
1013
+ const o = {
1014
+ info: e,
1015
+ pc: n,
1016
+ stream: r,
1017
+ makingOffer: !1,
1018
+ ignoreOffer: !1,
1019
+ pendingCandidates: []
1020
+ };
1021
+ return n.addEventListener("negotiationneeded", () => {
1022
+ (async () => {
1023
+ try {
1024
+ o.makingOffer = !0, await n.setLocalDescription(), n.localDescription && this.sendSignal(e.id, {
1025
+ kind: "sdp",
1026
+ description: {
1027
+ type: n.localDescription.type,
1028
+ sdp: n.localDescription.sdp
1029
+ }
1030
+ });
1031
+ } catch {
1032
+ } finally {
1033
+ o.makingOffer = !1;
1034
+ }
1035
+ })();
1036
+ }), n.addEventListener("icecandidate", (u) => {
1037
+ const c = u.candidate;
1038
+ c && this.sendSignal(e.id, {
1039
+ kind: "ice",
1040
+ candidate: {
1041
+ candidate: c.candidate,
1042
+ sdpMid: c.sdpMid,
1043
+ sdpMLineIndex: c.sdpMLineIndex,
1044
+ usernameFragment: c.usernameFragment
1045
+ }
1046
+ });
1047
+ }), n.addEventListener("track", (u) => {
1048
+ const c = u.track;
1049
+ o.stream.getTracks().includes(c) || o.stream.addTrack(c), c.addEventListener("ended", () => {
1050
+ o.stream.removeTrack(c);
1051
+ }), this.emit("remote-track", {
1052
+ peerId: e.id,
1053
+ peerName: e.name,
1054
+ stream: o.stream
1055
+ });
1056
+ }), n.addEventListener("connectionstatechange", () => {
1057
+ this.emit("connection-state", { peerId: e.id, state: n.connectionState }), (n.connectionState === "failed" || n.connectionState === "closed") && (this.remotes.delete(e.id), this.emit("peer-left", { peerId: e.id, peerName: e.name }));
1058
+ }), this.remotes.set(e.id, o), o;
1059
+ }
1060
+ /* ---- Helpers ------------------------------------------------------ */
1061
+ isPolite(e) {
1062
+ return this.selfId ? this.selfId > e : !1;
1063
+ }
1064
+ sendSignal(e, s) {
1065
+ this.send({ type: "signal", to: e, payload: s });
1066
+ }
1067
+ emit(e, s) {
1068
+ const n = this.listeners.get(e);
1069
+ if (n)
1070
+ for (const r of n) r(s);
1071
+ }
1072
+ }
1073
+ function nt(t, e) {
1074
+ const s = globalThis.RTCRtpReceiver?.getCapabilities;
1075
+ typeof s == "function" && (q(e, s("video"), (n) => {
1076
+ const r = n.toLowerCase();
1077
+ return r === "video/h264" || r === "video/rtx";
1078
+ }), q(t, s("audio"), (n) => {
1079
+ const r = n.toLowerCase();
1080
+ return r === "audio/opus" || r === "audio/telephone-event";
1081
+ }));
1082
+ }
1083
+ function q(t, e, s) {
1084
+ const n = t?.setCodecPreferences;
1085
+ if (typeof n != "function" || !e) return;
1086
+ const r = e.codecs.filter((i) => s(i.mimeType));
1087
+ if (r.length !== 0)
1088
+ try {
1089
+ n.call(t, r);
1090
+ } catch {
1091
+ }
1092
+ }
1093
+ function Q() {
1094
+ const t = /* @__PURE__ */ new Map(), e = /* @__PURE__ */ new Map();
1095
+ function s(n) {
1096
+ const r = e.get(n);
1097
+ if (r === void 0) return;
1098
+ const i = t.get(n) ?? null;
1099
+ for (const a of r) a(i);
1100
+ }
1101
+ return {
1102
+ resolve(n) {
1103
+ return t.get(n) ?? null;
1104
+ },
1105
+ subscribe(n, r) {
1106
+ let i = e.get(n);
1107
+ return i === void 0 && (i = /* @__PURE__ */ new Set(), e.set(n, i)), i.add(r), r(t.get(n) ?? null), () => {
1108
+ const a = e.get(n);
1109
+ a !== void 0 && (a.delete(r), a.size === 0 && e.delete(n));
1110
+ };
1111
+ },
1112
+ set(n, r) {
1113
+ t.get(n) !== r && (t.set(n, r), s(n));
1114
+ },
1115
+ remove(n) {
1116
+ t.has(n) && (t.delete(n), s(n));
1117
+ },
1118
+ clear() {
1119
+ const n = [...t.keys()];
1120
+ t.clear();
1121
+ for (const r of n) s(r);
1122
+ }
1123
+ };
1124
+ }
1125
+ function v(t) {
1126
+ return t.toLowerCase().replace(/[^a-z0-9_-]+/g, "_").replace(/^[_-]+|[_-]+$/g, "");
1127
+ }
1128
+ function ee(t, e, s) {
1129
+ t.on("remote-track", (n) => {
1130
+ const r = v(n.peerName);
1131
+ s.acquire(r, t) && e.set(r, n.stream);
1132
+ }), t.on("peer-left", (n) => {
1133
+ const r = v(n.peerName);
1134
+ s.acquire(r, t) && (e.remove(r), s.release(r, t));
1135
+ });
1136
+ }
1137
+ const rt = {
1138
+ acquire: () => !0,
1139
+ release: () => {
1140
+ }
1141
+ };
1142
+ function Tt(t) {
1143
+ const e = Q(), s = new Z(t);
1144
+ return ee(s, e, rt), {
1145
+ join: () => s.join(),
1146
+ leave: () => {
1147
+ s.leave(), e.clear();
1148
+ },
1149
+ resolvePeerStream: (n) => e.resolve(v(n)),
1150
+ subscribePeerStream: (n, r) => e.subscribe(v(n), r),
1151
+ registry: e,
1152
+ viewer: s
1153
+ };
1154
+ }
1155
+ function z(t) {
1156
+ const e = Q(), s = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map(), r = {
1157
+ acquire: (o, u) => {
1158
+ const c = n.get(o);
1159
+ return c === void 0 ? (n.set(o, u), !0) : c === u;
1160
+ },
1161
+ release: (o, u) => {
1162
+ n.get(o) === u && n.delete(o);
1163
+ }
1164
+ };
1165
+ function i(o) {
1166
+ if (s.has(o.roomId)) return;
1167
+ const u = new Z({
1168
+ name: o.name ?? "solar-viewer",
1169
+ ...o,
1170
+ ...t.deps !== void 0 && o.deps === void 0 ? { deps: t.deps } : {}
1171
+ });
1172
+ ee(u, e, r), s.set(o.roomId, { viewer: u });
1173
+ }
1174
+ function a(o) {
1175
+ const u = s.get(o);
1176
+ if (u !== void 0) {
1177
+ for (const [c, d] of [...n.entries()])
1178
+ d === u.viewer && (e.remove(c), n.delete(c));
1179
+ u.viewer.leave(), s.delete(o);
1180
+ }
1181
+ }
1182
+ for (const o of t.rooms) i(o);
1183
+ return {
1184
+ join: async () => {
1185
+ await Promise.all([...s.values()].map((o) => o.viewer.join()));
1186
+ },
1187
+ leave: () => {
1188
+ for (const o of [...s.keys()]) a(o);
1189
+ e.clear();
1190
+ },
1191
+ setRooms: async (o) => {
1192
+ const u = new Set(o.map((d) => d.roomId));
1193
+ for (const d of [...s.keys()])
1194
+ u.has(d) || a(d);
1195
+ const c = [];
1196
+ for (const d of o)
1197
+ if (!s.has(d.roomId)) {
1198
+ i(d);
1199
+ const m = s.get(d.roomId);
1200
+ m && c.push(m.viewer);
1201
+ }
1202
+ await Promise.all(c.map((d) => d.join()));
1203
+ },
1204
+ resolvePeerStream: (o) => e.resolve(v(o)),
1205
+ subscribePeerStream: (o, u) => e.subscribe(v(o), u),
1206
+ registry: e
1207
+ };
1208
+ }
1209
+ function Et(t) {
1210
+ if ("rooms" in t && Array.isArray(t.rooms))
1211
+ return z(t);
1212
+ const { name: e, deps: s, ...n } = t;
1213
+ return z({
1214
+ rooms: [{ ...n, ...e !== void 0 ? { name: e } : {} }],
1215
+ ...s !== void 0 ? { deps: s } : {}
1216
+ });
1217
+ }
1218
+ const it = { width: 1920, height: 1080 }, ot = () => {
1219
+ };
1220
+ function _t(t) {
1221
+ const e = t.stage ?? it, s = t.target;
1222
+ s.style.position ||= "relative", s.style.width = `${e.width}px`, s.style.height = `${e.height}px`, s.style.overflow = "hidden";
1223
+ const n = t.onDiagnostic ? V(t.onDiagnostic) : void 0, r = Y();
1224
+ r.reset(t.defaults ?? {});
1225
+ const i = H(s);
1226
+ return {
1227
+ ready: new Promise((o) => {
1228
+ import("./broadcast-DtHoU_fS.js").then(({ BroadcastMode: u }) => {
1229
+ i.render(
1230
+ /* @__PURE__ */ f(le, { children: /* @__PURE__ */ f(
1231
+ X,
1232
+ {
1233
+ value: {
1234
+ mode: "broadcast",
1235
+ store: r,
1236
+ bundle: t.bundle,
1237
+ status: "live",
1238
+ sendInput: ot
1239
+ },
1240
+ children: /* @__PURE__ */ f(u, {})
1241
+ }
1242
+ ) })
1243
+ );
1244
+ const c = new Promise((m) => {
1245
+ requestAnimationFrame(() => requestAnimationFrame(() => m()));
1246
+ }), d = typeof document < "u" && document.fonts ? document.fonts.ready.then(() => {
1247
+ }) : Promise.resolve();
1248
+ Promise.all([c, d]).then(() => o());
1249
+ });
1250
+ }),
1251
+ unmount() {
1252
+ n?.(), i.unmount();
1253
+ }
1254
+ };
1255
+ }
1256
+ function te(t, e) {
1257
+ if (typeof t != "string") return t;
1258
+ if (e[t]) return e[t];
1259
+ const s = /^assets\/([A-Za-z0-9]+)\.[A-Za-z0-9]+$/.exec(t);
1260
+ return s && s[1] !== void 0 && e[s[1]] ? e[s[1]] : t;
1261
+ }
1262
+ function B(t, e) {
1263
+ if (t === null || typeof t != "object") return;
1264
+ if (Array.isArray(t)) {
1265
+ for (const n of t) B(n, e);
1266
+ return;
1267
+ }
1268
+ const s = t;
1269
+ "src" in s && (s.src = te(s.src, e));
1270
+ for (const n of Object.values(s))
1271
+ n && typeof n == "object" && B(n, e);
1272
+ }
1273
+ function It(t, e) {
1274
+ const s = { ...t };
1275
+ for (const [n, r] of Object.entries(s))
1276
+ n.startsWith("__lit.image.") && (s[n] = te(r, e));
1277
+ return s;
1278
+ }
1279
+ async function Rt(t) {
1280
+ const e = [];
1281
+ for (const s of t)
1282
+ try {
1283
+ const n = new FontFace(s.family, s.src, {
1284
+ weight: String(s.weight),
1285
+ style: s.style ?? "normal"
1286
+ });
1287
+ await n.load(), document.fonts.add(n), e.push(s.family);
1288
+ } catch {
1289
+ }
1290
+ return e;
1291
+ }
1292
+ export {
1293
+ Te as A,
1294
+ U as B,
1295
+ bt as C,
1296
+ gt as F,
1297
+ Z as M,
1298
+ tt as P,
1299
+ qe as S,
1300
+ pt as a,
1301
+ kt as b,
1302
+ N as c,
1303
+ E as d,
1304
+ K as e,
1305
+ J as f,
1306
+ V as g,
1307
+ z as h,
1308
+ Q as i,
1309
+ Tt as j,
1310
+ Et as k,
1311
+ Rt as l,
1312
+ yt as m,
1313
+ He as n,
1314
+ x as o,
1315
+ St as p,
1316
+ _t as q,
1317
+ wt as r,
1318
+ Oe as s,
1319
+ vt as t,
1320
+ mt as u,
1321
+ te as v,
1322
+ Pe as w,
1323
+ It as x,
1324
+ B as y,
1325
+ $ as z
1326
+ };
1327
+ //# sourceMappingURL=index-Dz27r92m.js.map