@norskvideo/moq-watch 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -2
- package/audio/buffer.d.ts +8 -0
- package/audio/buffer.d.ts.map +1 -1
- package/audio/decoder.d.ts +25 -15
- package/audio/decoder.d.ts.map +1 -1
- package/audio/emitter.d.ts +13 -10
- package/audio/emitter.d.ts.map +1 -1
- package/audio/handover.d.ts +16 -0
- package/audio/handover.d.ts.map +1 -0
- package/audio/index.d.ts +0 -2
- package/audio/index.d.ts.map +1 -1
- package/audio/render.d.ts +10 -1
- package/audio/render.d.ts.map +1 -1
- package/audio/ring-buffer.d.ts +7 -0
- package/audio/ring-buffer.d.ts.map +1 -1
- package/audio/shared-ring-buffer.d.ts +30 -2
- package/audio/shared-ring-buffer.d.ts.map +1 -1
- package/audio/source.d.ts +21 -17
- package/audio/source.d.ts.map +1 -1
- package/audio/subscription.d.ts +10 -0
- package/audio/subscription.d.ts.map +1 -0
- package/audio/terminal.d.ts +36 -0
- package/audio/terminal.d.ts.map +1 -0
- package/audio/unlock.d.ts +20 -0
- package/audio/unlock.d.ts.map +1 -0
- package/audio/warmup.d.ts +9 -0
- package/audio/warmup.d.ts.map +1 -0
- package/broadcast.d.ts +21 -61
- package/broadcast.d.ts.map +1 -1
- package/element.d.ts +45 -12
- package/element.d.ts.map +1 -1
- package/element.js +190 -95
- package/element.js.map +1 -1
- package/index.d.ts +9 -6
- package/index.d.ts.map +1 -1
- package/index.js +24 -5
- package/index.js.map +1 -0
- package/msf.d.ts.map +1 -1
- package/package.json +5 -6
- package/source-lWNGIbmd.js +1722 -0
- package/source-lWNGIbmd.js.map +1 -0
- package/support/element.d.ts.map +1 -1
- package/support/element.js.map +1 -1
- package/support/index.d.ts +3 -3
- package/support/index.d.ts.map +1 -1
- package/support/index.js +18 -17
- package/support/index.js.map +1 -1
- package/sync-DNQkgnlX.js +132 -0
- package/sync-DNQkgnlX.js.map +1 -0
- package/sync.d.ts +42 -22
- package/sync.d.ts.map +1 -1
- package/ui/components/buffer-control.d.ts +1 -1
- package/ui/components/buffer-control.d.ts.map +1 -1
- package/ui/components/fullscreen-button.d.ts +1 -2
- package/ui/components/fullscreen-button.d.ts.map +1 -1
- package/ui/components/play-pause.d.ts +1 -1
- package/ui/components/play-pause.d.ts.map +1 -1
- package/ui/element.d.ts.map +1 -1
- package/ui/element.js +269 -265
- package/ui/element.js.map +1 -1
- package/ui/fullscreen.d.ts +1 -2
- package/ui/fullscreen.d.ts.map +1 -1
- package/video/config.d.ts +17 -0
- package/video/config.d.ts.map +1 -0
- package/video/decoder.d.ts +42 -16
- package/video/decoder.d.ts.map +1 -1
- package/video/index.d.ts +0 -2
- package/video/index.d.ts.map +1 -1
- package/video/playhead.d.ts +31 -0
- package/video/playhead.d.ts.map +1 -0
- package/video/presentation.d.ts +16 -0
- package/video/presentation.d.ts.map +1 -0
- package/video/renderer.d.ts +14 -17
- package/video/renderer.d.ts.map +1 -1
- package/video/source.d.ts +23 -20
- package/video/source.d.ts.map +1 -1
- package/audio/backend.d.ts +0 -26
- package/audio/backend.d.ts.map +0 -1
- package/audio/mse.d.ts +0 -22
- package/audio/mse.d.ts.map +0 -1
- package/audio/ring-buffer.test.d.ts +0 -2
- package/audio/ring-buffer.test.d.ts.map +0 -1
- package/audio/shared-ring-buffer.test.d.ts +0 -2
- package/audio/shared-ring-buffer.test.d.ts.map +0 -1
- package/backend.d.ts +0 -65
- package/backend.d.ts.map +0 -1
- package/broadcast-BY0pAaOe.js +0 -1706
- package/broadcast-BY0pAaOe.js.map +0 -1
- package/buffered.d.ts +0 -19
- package/buffered.d.ts.map +0 -1
- package/mse.d.ts +0 -19
- package/mse.d.ts.map +0 -1
- package/sync-D67IxmxJ.js +0 -128
- package/sync-D67IxmxJ.js.map +0 -1
- package/sync.test.d.ts +0 -2
- package/sync.test.d.ts.map +0 -1
- package/video/backend.d.ts +0 -25
- package/video/backend.d.ts.map +0 -1
- package/video/mse.d.ts +0 -23
- package/video/mse.d.ts.map +0 -1
- package/video/source.test.d.ts +0 -2
- package/video/source.test.d.ts.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"source-lWNGIbmd.js","names":["#control","#state","#samples","#handoff","#anchored","#anchor","#position","#lastRead","#advance","#foldRead","#unwrapRead","#enabled","#headroom","#waiters","#timestamp","#stalled","#worklet","#backpressure","#ring","#signals","#open","#pending","#end","#discontinuity","#preSkip","#resetEpoch","#epoch","#preSkipRemaining","#remaining","#out","#signals","#runWorklet","#runEnabled","#runLatency","#runLatencyReanchor","#runDecoder","#decodedSampleRate","#ring","#trimDecodeBuffered","#prevFloor","#handover","#runCmafDecoder","#runLegacyDecoder","#terminal","#decodeBuffered","#emit","#onNext","#truncateDecodeBuffered","#addDecodeBuffered","#out","#signals","#gain","#out","#signals","#runCatalog","#runSupported","#runSelected","#select","#out","#announced","#wantAnnounced","#raw","#signals","#runAnnounced","#runBroadcast","#runCatalog","#runFiltered","#relativeTarget","#isPathAnnounced","#out","#identity","#signals","#runPending","#runActive","#runDisplay","#runBuffering","#active","#clearCurrentFrame","#latency","#run","#discontinuity","#park","#trimBuffered","#runCmaf","#runLegacy","#buffered","#onDiscontinuity","#addBuffered","#out","#signals","#ctx","#runVisible","#runRender","#runResize","#render","#out","#signals","#runCatalog","#runSupported","#runSelected","#supportCache","#select"],"sources":["../src/base64.ts","../src/audio/shared-ring-buffer.ts","../src/audio/buffer.ts","../src/audio/handover.ts","../src/audio/render-worklet.ts?worklet","../src/audio/subscription.ts","../src/audio/terminal.ts","../src/audio/unlock.ts","../src/audio/warmup.ts","../src/audio/decoder.ts","../src/audio/emitter.ts","../src/audio/source.ts","../src/msf.ts","../src/broadcast.ts","../src/video/config.ts","../src/video/playhead.ts","../src/video/presentation.ts","../src/video/decoder.ts","../src/video/renderer.ts","../src/video/source.ts"],"sourcesContent":["/** Decode a base64 string into bytes. Throws on invalid input. */\nexport function base64ToBytes(b64: string): Uint8Array {\n\tconst raw = atob(b64);\n\tconst bytes = new Uint8Array(raw.length);\n\tfor (let i = 0; i < raw.length; i++) bytes[i] = raw.charCodeAt(i);\n\treturn bytes;\n}\n","import { Time } from \"@moq/net\";\n\n// Control array slot indices. The playhead is not here: see `state`.\nconst WRITE = 0;\nconst LATENCY = 1;\nconst STALLED = 2;\nconst CONTROL_SLOTS = 3;\n\n/**\n * The playhead and the timeline it belongs to, packed into one 64-bit word: epoch in the high\n * half, read cursor in the low half.\n *\n * They have to move together. The reader samples the ring, copies, and only then publishes, and\n * the writer can rebase the timeline anywhere in between. Checking an epoch and then writing a\n * cursor leaves a window between the two no ordering closes, which is how a stale cursor ends up\n * on a fresh timeline: the playhead lands past WRITE, `read` returns nothing, and `insert` drops\n * everything as too old until WRITE catches up. Packed, the check *is* the write: the reader\n * compare-exchanges the whole word, so a rebase makes its publish fail rather than land.\n */\nconst CURSOR_MASK = 0xffffffffn;\n\nfunction pack(epoch: number, read: number): bigint {\n\treturn (BigInt(epoch >>> 0) << 32n) | BigInt(read >>> 0);\n}\n\n/** The timeline half. Bumped by the writer on every re-anchor. */\nfunction epochOf(state: bigint): number {\n\treturn Number(state >> 32n) | 0;\n}\n\n/** The playhead half, back as an i32 so the modular comparisons elsewhere still hold. */\nfunction readOf(state: bigint): number {\n\treturn Number(state & CURSOR_MASK) | 0;\n}\n\nexport interface SharedRingBufferInit {\n\tchannels: number;\n\tcapacity: number; // samples per channel, always a power of two\n\trate: number;\n\tsamples: SharedArrayBuffer; // channels * capacity * Float32Array.BYTES_PER_ELEMENT bytes\n\tcontrol: SharedArrayBuffer; // CONTROL_SLOTS * Int32Array.BYTES_PER_ELEMENT bytes\n\tstate: SharedArrayBuffer; // one BigInt64Array element: epoch and read cursor, packed\n\t// Buffered mode: never skip ahead on read, so we play through everything buffered.\n\tbuffered: boolean;\n}\n\n/** Rounds up to a power of two, which is what makes `slot` wrap-invariant. */\nfunction ceilPow2(n: number): number {\n\treturn n <= 1 ? 1 : 1 << (32 - Math.clz32(n - 1));\n}\n\n/**\n * Allocate the shared memory for a ring holding at least `capacity` samples per channel.\n *\n * The capacity is rounded up to a power of two so `slot` can mask instead of taking a\n * remainder; read `init.capacity` back rather than assuming the requested value.\n */\nexport function allocSharedRingBuffer(\n\tchannels: number,\n\tcapacity: number,\n\trate: number,\n\tbuffered = false,\n): SharedRingBufferInit {\n\tif (channels <= 0) throw new Error(\"invalid channels\");\n\tif (capacity <= 0 || capacity > 2 ** 30) throw new Error(\"invalid capacity\");\n\tif (rate <= 0) throw new Error(\"invalid sample rate\");\n\n\tcapacity = ceilPow2(capacity);\n\n\tconst samples = new SharedArrayBuffer(channels * capacity * Float32Array.BYTES_PER_ELEMENT);\n\tconst control = new SharedArrayBuffer(CONTROL_SLOTS * Int32Array.BYTES_PER_ELEMENT);\n\tconst state = new SharedArrayBuffer(BigInt64Array.BYTES_PER_ELEMENT);\n\n\t// Initialize STALLED to 1\n\tconst ctrl = new Int32Array(control);\n\tAtomics.store(ctrl, STALLED, 1);\n\n\treturn { channels, capacity, rate, samples, control, state, buffered };\n}\n\n/** Modular i32 max: returns a if a is ahead of b, else b. */\nfunction i32Max(a: number, b: number): number {\n\treturn ((a - b) | 0) > 0 ? a : b;\n}\n\n/**\n * Maps a sample index to a [0, capacity) array slot.\n *\n * `capacity` is a power of two, so masking is exact for negative indexes and, unlike a\n * remainder, survives the i32 wrap: consecutive indexes stay consecutive slots across\n * 0x7fffffff -> 0x80000000, which is what keeps unread samples from aliasing there.\n */\nfunction slot(idx: number, capacity: number): number {\n\treturn idx & (capacity - 1);\n}\n\nexport class SharedRingBuffer {\n\treadonly channels: number;\n\treadonly capacity: number;\n\treadonly rate: number;\n\treadonly buffered: boolean;\n\treadonly init: SharedRingBufferInit;\n\n\t#control: Int32Array;\n\t#state: BigInt64Array;\n\t#samples: Float32Array[];\n\n\t// Whether READ/WRITE have been anchored to the first inserted sample.\n\t#anchored = false;\n\n\t// Absolute sample index of that first sample. READ/WRITE are stored relative to it, so\n\t// `timestamp` adds it back to recover media time. Main-thread only: the worklet reads by\n\t// difference and never needs an absolute position.\n\t#anchor = 0;\n\n\t// Unwrapped READ, in anchor-relative samples, plus the raw i32 it was last derived from.\n\t// READ is Int32 and wraps every ~13.5h at 44.1kHz. That is harmless for the modular\n\t// comparisons the ring runs on, but reading the negative half as a magnitude would report\n\t// the playhead jumping back 2^32 samples, so accumulate deltas here instead. Main-thread\n\t// only, and only accurate while `timestamp` is observed more often than READ can advance\n\t// 2^31 samples, which the 50ms poll in `buffer.ts` satisfies by six orders of magnitude.\n\t#position = 0;\n\t#lastRead = 0;\n\n\t/**\n\t * Wrap the shared memory described by `init`.\n\t *\n\t * Pass `previous` in the worklet when this ring replaces one already being read. `resize`\n\t * snapshots the playhead on the main thread and hands the replacement over by message, so the\n\t * reader keeps draining the old ring in the meantime. Carrying its cursor across means those\n\t * samples are not played twice.\n\t */\n\tconstructor(init: SharedRingBufferInit, previous?: SharedRingBuffer) {\n\t\tthis.channels = init.channels;\n\t\tthis.capacity = init.capacity;\n\t\tthis.rate = init.rate;\n\t\tthis.buffered = init.buffered;\n\t\tthis.init = init;\n\n\t\tthis.#control = new Int32Array(init.control);\n\t\tthis.#state = new BigInt64Array(init.state);\n\t\tthis.#samples = [];\n\t\tfor (let i = 0; i < this.channels; i++) {\n\t\t\tthis.#samples.push(\n\t\t\t\tnew Float32Array(init.samples, i * this.capacity * Float32Array.BYTES_PER_ELEMENT, this.capacity),\n\t\t\t);\n\t\t}\n\n\t\tif (previous !== undefined) this.#handoff(previous);\n\t}\n\n\t/**\n\t * Carry `source`'s playhead across, if the replacement is still the timeline it belongs to.\n\t *\n\t * `resize` copies the epoch, so a destination that re-anchored since has a different one and\n\t * the cursor is dropped rather than applied to audio the reader has never played. The check\n\t * and the write are the same exchange, so a re-anchor landing mid-handoff makes it fail\n\t * instead of slipping through the gap that separate operations would leave.\n\t */\n\t#handoff(source: SharedRingBuffer): void {\n\t\tif (source.channels !== this.channels || source.rate !== this.rate) return;\n\n\t\tconst from = Atomics.load(source.#state, 0);\n\n\t\tfor (;;) {\n\t\t\tconst state = Atomics.load(this.#state, 0);\n\t\t\tif (epochOf(state) !== epochOf(from)) return;\n\t\t\tif (((readOf(from) - readOf(state)) | 0) <= 0) return;\n\n\t\t\tconst next = pack(epochOf(state), readOf(from));\n\t\t\tif (Atomics.compareExchange(this.#state, 0, state, next) === state) return;\n\t\t}\n\t}\n\n\t/**\n\t * Move the playhead forward to `candidate`, staying on whatever timeline is current.\n\t *\n\t * Retries while the word changes under it, and never steps backwards. Used by the writer's\n\t * overflow path; the reader publishes with its own exchange so it can tell a rebase apart\n\t * from losing a race.\n\t */\n\t#advance(candidate: number): void {\n\t\tfor (;;) {\n\t\t\tconst state = Atomics.load(this.#state, 0);\n\t\t\tif (((candidate - readOf(state)) | 0) <= 0) return;\n\n\t\t\tconst next = pack(epochOf(state), candidate);\n\t\t\tif (Atomics.compareExchange(this.#state, 0, state, next) === state) return;\n\t\t}\n\t}\n\n\t/**\n\t * Insert audio samples at the given timestamp.\n\t * Main thread only. Handles out-of-order writes, gap filling, and overflow.\n\t */\n\tinsert(timestamp: Time.Micro, data: Float32Array[]): void {\n\t\tif (data.length !== this.channels) throw new Error(\"wrong number of channels\");\n\n\t\tlet start = Math.round(Time.Second.fromMicro(timestamp) * this.rate);\n\t\tconst originalLength = data[0].length;\n\t\tlet offset = 0;\n\n\t\t// Anchor to the first sample so playback starts at its timestamp rather than gap-filling\n\t\t// from index 0.\n\t\tif (!this.#anchored) {\n\t\t\tthis.#anchor = start;\n\t\t\t// One store rebases both halves, so a reader's compare-exchange against the old word\n\t\t\t// cannot land afterwards.\n\t\t\tconst epoch = epochOf(Atomics.load(this.#state, 0));\n\t\t\tAtomics.store(this.#state, 0, pack((epoch + 1) | 0, 0));\n\t\t\tAtomics.store(this.#control, WRITE, 0);\n\t\t\tthis.#anchored = true;\n\t\t\tthis.#position = 0;\n\t\t\tthis.#lastRead = 0;\n\t\t}\n\n\t\t// Positions are relative to the anchor. READ/WRITE are Int32, so an absolute sample index\n\t\t// wraps once a stream has been broadcasting a while, and the wrapped value reads as far\n\t\t// ahead of READ, so every insert would be discarded as too old and the ring would go\n\t\t// silent for good. Relative positions start at 0 instead. They still wrap, after ~13.5h at\n\t\t// 44.1kHz, but nothing here reads them as magnitudes: the comparisons are modular, `slot`\n\t\t// masks a power-of-two capacity, and `timestamp` keeps its own unwrapped position.\n\t\tstart = (start - this.#anchor) | 0;\n\n\t\tconst end = (start + originalLength) | 0;\n\n\t\t// Trim old: discard samples before the read index\n\t\tconst read = readOf(Atomics.load(this.#state, 0));\n\t\tconst behind = (read - start) | 0;\n\t\tif (behind > 0) {\n\t\t\tif (behind >= originalLength) {\n\t\t\t\t// All samples are too old\n\t\t\t\treturn;\n\t\t\t}\n\t\t\toffset = behind;\n\t\t\tstart = (start + behind) | 0;\n\t\t}\n\n\t\tconst samples = originalLength - offset;\n\n\t\t// Overflow: if the write would exceed capacity from current READ, advance READ.\n\t\t// Use CAS so a concurrent reader advance isn't clobbered backward.\n\t\tif (((end - read) | 0) > this.capacity) {\n\t\t\tthis.#advance((end - this.capacity) | 0);\n\t\t}\n\n\t\t// Gap fill: zero-fill from current WRITE to start if there's a discontinuity\n\t\tconst write = Atomics.load(this.#control, WRITE);\n\t\tconst gap = (start - write) | 0;\n\t\tif (gap > 0) {\n\t\t\tconst gapSize = Math.min(gap, this.capacity);\n\t\t\tfor (let channel = 0; channel < this.channels; channel++) {\n\t\t\t\tconst dst = this.#samples[channel];\n\t\t\t\tfor (let i = 0; i < gapSize; i++) {\n\t\t\t\t\tdst[slot((write + i) | 0, this.capacity)] = 0;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Write sample data\n\t\tfor (let channel = 0; channel < this.channels; channel++) {\n\t\t\tconst src = data[channel];\n\t\t\tconst dst = this.#samples[channel];\n\t\t\tfor (let i = 0; i < samples; i++) {\n\t\t\t\tdst[slot((start + i) | 0, this.capacity)] = src[offset + i];\n\t\t\t}\n\t\t}\n\n\t\t// Advance WRITE (only forward)\n\t\tAtomics.store(this.#control, WRITE, i32Max(Atomics.load(this.#control, WRITE), end));\n\n\t\t// Un-stall: if buffered data >= LATENCY\n\t\tconst currentRead = readOf(Atomics.load(this.#state, 0));\n\t\tconst currentWrite = Atomics.load(this.#control, WRITE);\n\t\tconst latency = Atomics.load(this.#control, LATENCY);\n\t\tif (((currentWrite - currentRead) | 0) >= latency && latency > 0) {\n\t\t\tAtomics.store(this.#control, STALLED, 0);\n\t\t}\n\t}\n\n\t/**\n\t * Read audio samples into the output buffers.\n\t * AudioWorklet only. Returns the number of samples read.\n\t */\n\tread(output: Float32Array[]): number {\n\t\t// Sample the word before admission, not after. A re-anchor publishes the new state and\n\t\t// only then clears WRITE, so a reader admitted in between would pair a fresh epoch with\n\t\t// the previous timeline's WRITE and its exchange would succeed. Taken first, a reader\n\t\t// admitted before the rebase still holds the old word and its exchange fails, while one\n\t\t// arriving after sees the STALLED that `reset` raised and never starts.\n\t\tconst state = Atomics.load(this.#state, 0);\n\t\tif (Atomics.load(this.#control, STALLED) === 1) return 0;\n\n\t\tlet read = readOf(state);\n\t\tconst write = Atomics.load(this.#control, WRITE);\n\t\tconst latency = Atomics.load(this.#control, LATENCY);\n\n\t\t// Latency skip: if buffered data exceeds LATENCY, skip ahead.\n\t\t// CAS ensures we never step backward relative to a concurrent writer advance.\n\t\t// Disabled in buffered mode, where we deliberately play through the whole buffer.\n\t\tconst buffered = (write - read) | 0;\n\t\tif (!this.buffered && latency > 0 && buffered > latency) {\n\t\t\tconst skipTo = (write - latency) | 0;\n\t\t\tif (((skipTo - read) | 0) > 0) read = skipTo;\n\t\t}\n\n\t\tconst available = (write - read) | 0;\n\t\tconst count = Math.min(available, output[0].length);\n\t\tif (count <= 0) {\n\t\t\t// A latency skip still has to be published, and still only if nothing moved.\n\t\t\tif (((read - readOf(state)) | 0) > 0) {\n\t\t\t\tAtomics.compareExchange(this.#state, 0, state, pack(epochOf(state), read));\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\n\t\t// Copy samples\n\t\tfor (let channel = 0; channel < this.channels; channel++) {\n\t\t\tconst src = this.#samples[channel];\n\t\t\tconst dst = output[channel];\n\t\t\tfor (let i = 0; i < count; i++) {\n\t\t\t\tdst[i] = src[slot((read + i) | 0, this.capacity)];\n\t\t\t}\n\t\t}\n\n\t\t// Publish. The exchange fails if anything moved the word since the snapshot above, which\n\t\t// covers both a rebase and a concurrent overflow, so these samples are only ever counted\n\t\t// as played on the timeline they came from.\n\t\tconst next = pack(epochOf(state), (read + count) | 0);\n\t\tif (Atomics.compareExchange(this.#state, 0, state, next) !== state) {\n\t\t\t// The worklet takes the return value as an underflow count rather than clearing the\n\t\t\t// buffer it handed over, so silence the prefix or the discarded audio renders anyway.\n\t\t\tfor (let channel = 0; channel < this.channels; channel++) output[channel].fill(0, 0, count);\n\t\t\treturn 0;\n\t\t}\n\n\t\treturn count;\n\t}\n\n\t/** Update the target latency in samples. */\n\tsetLatency(samples: number): void {\n\t\tAtomics.store(this.#control, LATENCY, samples);\n\t}\n\n\t/**\n\t * Drop buffered samples at or after `timestamp`, keeping whatever is already due.\n\t * Main thread only.\n\t *\n\t * A successor track overwrites the slots its own samples land on, but anything the previous\n\t * track wrote beyond them would otherwise still play once the successor runs out.\n\t */\n\ttruncate(timestamp: Time.Micro): void {\n\t\tconst target = (Math.round(Time.Second.fromMicro(timestamp) * this.rate) - this.#anchor) | 0;\n\t\tfor (;;) {\n\t\t\tconst write = Atomics.load(this.#control, WRITE);\n\t\t\tif (((write - target) | 0) <= 0) return; // nothing buffered past the new timeline\n\t\t\t// Never retreat past the playhead: those samples are already due. The worklet can still\n\t\t\t// advance READ past the value read here, leaving READ ahead of WRITE. That reads as an\n\t\t\t// empty ring (read() returns nothing, insert() trims what the worklet already played) and\n\t\t\t// heals on the first successor sample past the playhead, so it costs a quantum of silence\n\t\t\t// rather than replaying anything.\n\t\t\tconst clamped = i32Max(target, readOf(Atomics.load(this.#state, 0)));\n\t\t\tif (((write - clamped) | 0) <= 0) return;\n\t\t\tif (Atomics.compareExchange(this.#control, WRITE, write, clamped) === write) return;\n\t\t}\n\t}\n\n\t/**\n\t * Flush buffered samples and re-stall, ready to anchor the next utterance (buffered mode).\n\t * Main thread only. The worklet reader sees STALLED and stops until the next insert.\n\t */\n\treset(): void {\n\t\tthis.#anchored = false;\n\t\tAtomics.store(this.#control, STALLED, 1);\n\t\tconst write = Atomics.load(this.#control, WRITE);\n\t\tconst state = Atomics.load(this.#state, 0);\n\t\tAtomics.store(this.#state, 0, pack(epochOf(state), write));\n\t}\n\n\t/**\n\t * Allocate a new ring with `newCapacity` samples and copy the unread window\n\t * [READ, WRITE) plus control state into it. Used when growing capacity so\n\t * we don't drop buffered audio. If `newCapacity` is smaller than the unread\n\t * span, the oldest samples are truncated.\n\t *\n\t * Main thread only. `resize()` reads from the source `SharedRingBuffer` and\n\t * writes into a freshly allocated buffer from `allocSharedRingBuffer`, so it\n\t * relies on the same invariant as `insert()`: no concurrent main-thread\n\t * writers. The AudioWorklet reader is tolerated via the CAS discipline used\n\t * by READ/WRITE elsewhere.\n\t */\n\tresize(newCapacity: number): SharedRingBuffer {\n\t\tconst init = allocSharedRingBuffer(this.channels, newCapacity, this.rate, this.buffered);\n\t\tconst dst = new SharedRingBuffer(init);\n\t\tdst.#anchored = this.#anchored;\n\t\tdst.#anchor = this.#anchor;\n\n\t\tconst state = Atomics.load(this.#state, 0);\n\t\tconst read = readOf(state);\n\t\tconst write = Atomics.load(this.#control, WRITE);\n\t\tconst latency = Atomics.load(this.#control, LATENCY);\n\t\tconst stalled = Atomics.load(this.#control, STALLED);\n\n\t\tconst available = (write - read) | 0;\n\t\tconst copyCount = Math.max(0, Math.min(available, dst.capacity));\n\t\tconst copyStart = (write - copyCount) | 0;\n\n\t\tfor (let channel = 0; channel < this.channels; channel++) {\n\t\t\tconst src = this.#samples[channel];\n\t\t\tconst out = dst.#samples[channel];\n\t\t\tfor (let i = 0; i < copyCount; i++) {\n\t\t\t\tconst idx = (copyStart + i) | 0;\n\t\t\t\tout[slot(idx, dst.capacity)] = src[slot(idx, this.capacity)];\n\t\t\t}\n\t\t}\n\n\t\tAtomics.store(dst.#state, 0, pack(epochOf(state), copyStart));\n\t\tAtomics.store(dst.#control, WRITE, write);\n\t\tAtomics.store(dst.#control, LATENCY, latency);\n\t\tAtomics.store(dst.#control, STALLED, stalled);\n\n\t\t// Carry the unwrapped playhead over, rebased onto dst's READ. Fold the same `read`\n\t\t// snapshot the copy used so both sides agree on one observation; `copyStart` is at or\n\t\t// ahead of it whenever the copy dropped the oldest samples.\n\t\tdst.#position = this.#foldRead(read) + ((copyStart - read) | 0);\n\t\tdst.#lastRead = copyStart;\n\n\t\treturn dst;\n\t}\n\n\t/**\n\t * Fold an observed READ into `#position`, so the returned offset keeps counting up across\n\t * the i32 wrap. Idempotent: folding the same value twice adds a zero delta.\n\t */\n\t#foldRead(read: number): number {\n\t\tthis.#position += (read - this.#lastRead) | 0;\n\t\tthis.#lastRead = read;\n\t\treturn this.#position;\n\t}\n\n\t/** `#foldRead` against the current READ. */\n\t#unwrapRead(): number {\n\t\treturn this.#foldRead(readOf(Atomics.load(this.#state, 0)));\n\t}\n\n\t/**\n\t * Current playback timestamp derived from READ position.\n\t *\n\t * Main thread only, and stateful: it advances the unwrapped read position, so it has to be\n\t * polled rather than sampled once. See `#position`.\n\t */\n\tget timestamp(): Time.Micro {\n\t\treturn Time.Micro.fromSecond(((this.#anchor + this.#unwrapRead()) / this.rate) as Time.Second);\n\t}\n\n\t/** Whether the buffer is stalled (waiting to fill). */\n\tget stalled(): boolean {\n\t\treturn Atomics.load(this.#control, STALLED) === 1;\n\t}\n\n\t/**\n\t * Number of buffered samples (WRITE - READ).\n\t *\n\t * Non-atomic: WRITE and READ are loaded separately, so a concurrent\n\t * writer/reader can make the two loads inconsistent. Intended for\n\t * tests and diagnostics, not control-flow decisions.\n\t */\n\tget length(): number {\n\t\treturn (Atomics.load(this.#control, WRITE) - readOf(Atomics.load(this.#state, 0))) | 0;\n\t}\n}\n","import { Time } from \"@moq/net\";\nimport { Effect, type Getter, Signal } from \"@moq/signals\";\nimport type { Data, InitPost, InitShared, Latency, Reset, State, Truncate } from \"./render\";\nimport { allocSharedRingBuffer, SharedRingBuffer } from \"./shared-ring-buffer\";\n\n/**\n * Timestamp-based backpressure for buffered playback. The decoded PCM ring only holds the latency\n * floor; everything above it (the buffered lookahead, up to the ceiling) stays upstream as encoded\n * Opus. `wait(timestamp)` stays pending until the playhead is within `headroom` (the floor) of\n * `timestamp`, so the decode loop holds a frame as Opus instead of decoding it too far ahead of the\n * floor-sized ring. Both transports share this; they differ only in how they observe the playhead\n * (Atomics poll vs worklet state messages). A no-op when not buffered (the ring bounds itself).\n */\nclass Backpressure {\n\treadonly #enabled: boolean;\n\t#headroom: Time.Micro;\n\t#waiters: Array<{ timestamp: Time.Micro; resolve: () => void }> = [];\n\n\tconstructor(enabled: boolean, headroom: Time.Micro) {\n\t\tthis.#enabled = enabled;\n\t\tthis.#headroom = headroom;\n\t}\n\n\t// Move the gate as the floor changes (e.g. \"real-time\" jitter tracking RTT).\n\tsetHeadroom(headroom: Time.Micro): void {\n\t\tthis.#headroom = headroom;\n\t}\n\n\twait(timestamp: Time.Micro, playhead: Time.Micro): Promise<void> {\n\t\tif (!this.#enabled) return Promise.resolve();\n\t\tif (playhead >= ((timestamp - this.#headroom) | 0)) return Promise.resolve();\n\t\treturn new Promise((resolve) => this.#waiters.push({ timestamp, resolve }));\n\t}\n\n\t// Resolve every waiter the playhead has reached. Thresholds are recomputed live so a changed\n\t// headroom takes effect on queued waiters too.\n\tadvance(playhead: Time.Micro): void {\n\t\tif (this.#waiters.length === 0) return;\n\t\tthis.#waiters = this.#waiters.filter(({ timestamp, resolve }) => {\n\t\t\tif (playhead < ((timestamp - this.#headroom) | 0)) return true;\n\t\t\tresolve();\n\t\t\treturn false;\n\t\t});\n\t}\n\n\t// Resolve everything unconditionally (reset/close): never strand a decode loop.\n\tflush(): void {\n\t\tfor (const { resolve } of this.#waiters) resolve();\n\t\tthis.#waiters = [];\n\t}\n}\n\n/** Convert a sample count to a Time.Micro duration at the given sample rate. */\nfunction samplesToMicro(samples: number, rate: number): Time.Micro {\n\treturn Time.Micro.fromSecond((samples / rate) as Time.Second);\n}\n\n/**\n * Unified interface for the audio buffer between the main thread and the AudioWorklet.\n *\n * Two implementations exist:\n * - `SharedAudioBuffer`: backed by SharedArrayBuffer, lock-free writes via Atomics.\n * - `PostAudioBuffer`: backed by postMessage transfer (the fallback when SAB is unavailable).\n *\n * Use `createAudioBuffer()` to pick the right implementation automatically.\n */\nexport interface AudioBuffer {\n\treadonly rate: number;\n\treadonly channels: number;\n\n\t/** Insert audio samples at the given timestamp. Handles out-of-order writes. */\n\tinsert(timestamp: Time.Micro, data: Float32Array[]): void;\n\n\t/** Update the target latency in samples. */\n\tsetLatency(samples: number): void;\n\n\t/** Flush buffered samples and re-stall, ready to anchor the next utterance (buffered mode). */\n\treset(): void;\n\n\t/**\n\t * Drop buffered samples at or after `timestamp`, keeping what is already due.\n\t *\n\t * Used when a new track takes over the timeline: its samples overwrite the slots they land on,\n\t * but the previous track's write-ahead tail would otherwise play once the new audio runs out.\n\t * Unlike `reset()` this keeps playing, so there's no gap at the handover.\n\t */\n\ttruncate(timestamp: Time.Micro): void;\n\n\t/**\n\t * Resolve once the playhead is near enough to decode a frame at `timestamp`. In buffered mode this\n\t * applies backpressure: it stays pending while decoding `timestamp` would run more than the latency\n\t * floor ahead of the playhead, so the caller holds the (encoded) frame instead of decoding it too\n\t * far ahead of the floor-sized ring. Resolves immediately when not buffered (the ring bounds itself).\n\t */\n\twait(timestamp: Time.Micro): Promise<void>;\n\n\t/** Current playback timestamp (derived from reader position). */\n\treadonly timestamp: Getter<Time.Micro>;\n\n\t/** Whether the buffer is stalled (waiting to fill). */\n\treadonly stalled: Getter<boolean>;\n\n\t/** Release any resources (event listeners, intervals, etc.). */\n\tclose(): void;\n}\n\n/** Returns true when SharedArrayBuffer is available and usable in the current context. */\nexport function supportsSharedArrayBuffer(): boolean {\n\tif (typeof SharedArrayBuffer === \"undefined\") return false;\n\t// In browsers, SharedArrayBuffer requires cross-origin isolation (COOP/COEP).\n\t// crossOriginIsolated is a browser global; in Node/Bun it's undefined.\n\tif (typeof crossOriginIsolated !== \"undefined\" && !crossOriginIsolated) return false;\n\treturn true;\n}\n\n/**\n * Create the best audio buffer implementation for the current environment.\n * Picks `SharedAudioBuffer` when possible, falling back to `PostAudioBuffer`.\n */\nexport function createAudioBuffer(\n\tworklet: AudioWorkletNode,\n\tchannels: number,\n\trate: number,\n\tlatencySamples: number,\n\tbuffered = false,\n): AudioBuffer {\n\tif (supportsSharedArrayBuffer()) {\n\t\tconsole.log(\"[audio] using SharedArrayBuffer audio buffer\");\n\t\treturn new SharedAudioBuffer(worklet, channels, rate, latencySamples, buffered);\n\t}\n\tconsole.warn(\n\t\t\"[audio] SharedArrayBuffer unavailable, falling back to the higher latency postMessage audio buffer. \" +\n\t\t\t\"Serve the page cross-origin isolated (Cross-Origin-Opener-Policy: same-origin, Cross-Origin-Embedder-Policy: require-corp) to avoid this.\",\n\t);\n\treturn new PostAudioBuffer(worklet, channels, rate, latencySamples, buffered);\n}\n\n/** SharedArrayBuffer-backed implementation. Writes go directly into shared memory. */\nclass SharedAudioBuffer implements AudioBuffer {\n\treadonly rate: number;\n\treadonly channels: number;\n\t#worklet: AudioWorkletNode;\n\t#ring: SharedRingBuffer;\n\n\treadonly #timestamp = new Signal<Time.Micro>(0 as Time.Micro);\n\treadonly timestamp: Getter<Time.Micro> = this.#timestamp;\n\n\treadonly #stalled = new Signal<boolean>(true);\n\treadonly stalled: Getter<boolean> = this.#stalled;\n\n\t#backpressure: Backpressure;\n\n\t#signals = new Effect();\n\n\tconstructor(worklet: AudioWorkletNode, channels: number, rate: number, latencySamples: number, buffered: boolean) {\n\t\tthis.#worklet = worklet;\n\t\tthis.channels = channels;\n\t\tthis.rate = rate;\n\n\t\t// The ring holds the latency floor as decoded PCM (headroom above it for overflow). In\n\t\t// buffered mode the lookahead above the floor stays encoded upstream, held back by `wait()`.\n\t\tconst capacity = Math.max(rate, latencySamples * 2);\n\t\tthis.#backpressure = new Backpressure(buffered, samplesToMicro(latencySamples, rate));\n\n\t\tconst init = allocSharedRingBuffer(channels, capacity, rate, buffered);\n\t\tthis.#ring = new SharedRingBuffer(init);\n\t\tthis.#ring.setLatency(latencySamples);\n\n\t\tconst msg: InitShared = { type: \"init-shared\", ...init };\n\t\tworklet.port.postMessage(msg);\n\n\t\t// Poll the shared control array and reflect it into signals.\n\t\tthis.#signals.interval(() => {\n\t\t\tconst stalled = this.#ring.stalled;\n\t\t\tthis.#timestamp.set(this.#ring.timestamp);\n\t\t\tthis.#stalled.set(stalled);\n\t\t\t// While stalled the playhead is parked, so release the decode loop to refill the floor;\n\t\t\t// once playing, hold it to ~the floor ahead.\n\t\t\tif (stalled) this.#backpressure.flush();\n\t\t\telse this.#backpressure.advance(this.#ring.timestamp);\n\t\t}, 50);\n\t}\n\n\tinsert(timestamp: Time.Micro, data: Float32Array[]): void {\n\t\tthis.#ring.insert(timestamp, data);\n\t}\n\n\tsetLatency(samples: number): void {\n\t\tthis.#backpressure.setHeadroom(samplesToMicro(samples, this.rate));\n\n\t\t// Grow the ring (preserving the unread window) if it's too small for the new latency.\n\t\tif (this.#ring.capacity < samples * 1.5) {\n\t\t\tconst newCapacity = Math.max(this.rate, samples * 2);\n\t\t\tthis.#ring = this.#ring.resize(newCapacity);\n\t\t\tthis.#ring.setLatency(samples);\n\n\t\t\tconst msg: InitShared = { type: \"init-shared\", ...this.#ring.init };\n\t\t\tthis.#worklet.port.postMessage(msg);\n\t\t} else {\n\t\t\tthis.#ring.setLatency(samples);\n\t\t}\n\t}\n\n\ttruncate(timestamp: Time.Micro): void {\n\t\tthis.#ring.truncate(timestamp);\n\t}\n\n\treset(): void {\n\t\tthis.#ring.reset();\n\t\tthis.#backpressure.flush(); // the old timeline is gone; let the decode loop re-anchor\n\t}\n\n\twait(timestamp: Time.Micro): Promise<void> {\n\t\t// Stalled = still filling the floor (bootstrap or underflow): let frames through to refill.\n\t\tif (this.#ring.stalled) return Promise.resolve();\n\t\treturn this.#backpressure.wait(timestamp, this.#ring.timestamp);\n\t}\n\n\tclose(): void {\n\t\tthis.#backpressure.flush(); // never leave a decode loop awaiting a closed buffer\n\t\tthis.#signals.close();\n\t}\n}\n\n/** postMessage-backed fallback implementation. Samples are transferred, not shared. */\nclass PostAudioBuffer implements AudioBuffer {\n\treadonly rate: number;\n\treadonly channels: number;\n\t#worklet: AudioWorkletNode;\n\n\treadonly #timestamp = new Signal<Time.Micro>(0 as Time.Micro);\n\treadonly timestamp: Getter<Time.Micro> = this.#timestamp;\n\n\treadonly #stalled = new Signal<boolean>(true);\n\treadonly stalled: Getter<boolean> = this.#stalled;\n\n\t// Backpressure runs off the playhead the worklet reports in its state messages.\n\t#backpressure: Backpressure;\n\n\t#signals = new Effect();\n\n\tconstructor(worklet: AudioWorkletNode, channels: number, rate: number, latencySamples: number, buffered: boolean) {\n\t\tthis.#worklet = worklet;\n\t\tthis.channels = channels;\n\t\tthis.rate = rate;\n\n\t\tthis.#backpressure = new Backpressure(buffered, samplesToMicro(latencySamples, rate));\n\n\t\tconst latency = Time.Milli.fromSecond((latencySamples / rate) as Time.Second);\n\t\tconst msg: InitPost = { type: \"init-post\", channels, rate, latency, buffered };\n\t\tworklet.port.postMessage(msg);\n\n\t\t// Listen for state updates from the worklet.\n\t\tthis.#signals.event(worklet.port, \"message\", (ev: Event) => {\n\t\t\tconst data = (ev as MessageEvent<State>).data;\n\t\t\tif (data?.type === \"state\") {\n\t\t\t\tthis.#timestamp.set(data.timestamp);\n\t\t\t\tthis.#stalled.set(data.stalled);\n\t\t\t\t// While stalled the playhead is parked, so release the decode loop to refill the floor;\n\t\t\t\t// once playing, hold it to ~the floor ahead.\n\t\t\t\tif (data.stalled) this.#backpressure.flush();\n\t\t\t\telse this.#backpressure.advance(data.timestamp);\n\t\t\t}\n\t\t});\n\t\t// addEventListener on a MessagePort requires start() to begin delivery.\n\t\tworklet.port.start();\n\t}\n\n\tinsert(timestamp: Time.Micro, data: Float32Array[]): void {\n\t\tconst msg: Data = { type: \"data\", data, timestamp };\n\t\t// Transfer the ArrayBuffers to avoid a copy. This is why samples can be dropped\n\t\t// under load: the main thread loses access until the worklet drains the message queue.\n\t\tthis.#worklet.port.postMessage(\n\t\t\tmsg,\n\t\t\tdata.map((d) => d.buffer),\n\t\t);\n\t}\n\n\tsetLatency(samples: number): void {\n\t\tthis.#backpressure.setHeadroom(samplesToMicro(samples, this.rate));\n\n\t\tconst latency = Time.Milli.fromSecond((samples / this.rate) as Time.Second);\n\t\tconst msg: Latency = { type: \"latency\", latency };\n\t\tthis.#worklet.port.postMessage(msg);\n\t}\n\n\ttruncate(timestamp: Time.Micro): void {\n\t\tconst msg: Truncate = { type: \"truncate\", timestamp };\n\t\tthis.#worklet.port.postMessage(msg);\n\t}\n\n\treset(): void {\n\t\tconst msg: Reset = { type: \"reset\" };\n\t\tthis.#worklet.port.postMessage(msg);\n\t\tthis.#backpressure.flush(); // the old timeline is gone; let the decode loop re-anchor\n\t}\n\n\twait(timestamp: Time.Micro): Promise<void> {\n\t\t// Stalled = still filling the floor (bootstrap or underflow): let frames through to refill.\n\t\tif (this.#stalled.peek()) return Promise.resolve();\n\t\t// Uses the worklet-reported playhead, which lags by a state-message interval; the floor's\n\t\t// headroom covers that. The worklet still drops the oldest if a frame slips through.\n\t\treturn this.#backpressure.wait(timestamp, this.#timestamp.peek());\n\t}\n\n\tclose(): void {\n\t\tthis.#backpressure.flush(); // never leave a decode loop awaiting a closed buffer\n\t\tthis.#signals.close();\n\t}\n}\n","/**\n * Tracks whether the audio ring still holds samples from a superseded subscription.\n *\n * The ring outlives the subscription that fills it, so reopening one (a rendition swap, a\n * republished broadcast, a reconnect) leaves the previous subscription's decoded audio buffered.\n * The replacement is the authority from its first frame onwards, so everything the ring holds past\n * that timestamp is stale and has to go.\n */\nexport class Handover {\n\t#open = false;\n\t#pending = false;\n\n\t/** Record a subscription opening on the current ring. */\n\topened(): void {\n\t\tif (this.#open) this.#pending = true;\n\t\tthis.#open = true;\n\t}\n\n\t/** Consume one decoded frame and report whether it is the first of a replacement subscription. */\n\ttakeover(): boolean {\n\t\tconst pending = this.#pending;\n\t\tthis.#pending = false;\n\t\treturn pending;\n\t}\n}\n","const code = \"var __defProp = Object.defineProperty;\\nvar __export = (target, all) => {\\n for (var name in all)\\n __defProp(target, name, { get: all[name], enumerable: true });\\n};\\n\\n// ../net/src/time.ts\\nvar time_exports = {};\\n__export(time_exports, {\\n Micro: () => Micro,\\n Milli: () => Milli,\\n Nano: () => Nano,\\n Second: () => Second,\\n Timescale: () => Timescale,\\n Timestamp: () => Timestamp\\n});\\nvar Nano = Object.assign((value) => value, {\\n zero: 0,\\n fromMicro: (us) => us * 1e3,\\n fromMilli: (ms) => ms * 1e6,\\n fromSecond: (s) => s * 1e9,\\n toMicro: (ns) => ns / 1e3,\\n toMilli: (ns) => ns / 1e6,\\n toSecond: (ns) => ns / 1e9,\\n now: () => performance.now() * 1e6,\\n add: (a, b) => a + b,\\n sub: (a, b) => a - b,\\n mul: (a, b) => a * b,\\n div: (a, b) => a / b,\\n max: (a, b) => Math.max(a, b),\\n min: (a, b) => Math.min(a, b)\\n});\\nvar Micro = Object.assign((value) => value, {\\n zero: 0,\\n fromNano: (ns) => ns / 1e3,\\n fromMilli: (ms) => ms * 1e3,\\n fromSecond: (s) => s * 1e6,\\n toNano: (us) => us * 1e3,\\n toMilli: (us) => us / 1e3,\\n toSecond: (us) => us / 1e6,\\n now: () => performance.now() * 1e3,\\n add: (a, b) => a + b,\\n sub: (a, b) => a - b,\\n mul: (a, b) => a * b,\\n div: (a, b) => a / b,\\n max: (a, b) => Math.max(a, b),\\n min: (a, b) => Math.min(a, b)\\n});\\nvar Milli = Object.assign((value) => value, {\\n zero: 0,\\n fromNano: (ns) => ns / 1e6,\\n fromMicro: (us) => us / 1e3,\\n fromSecond: (s) => s * 1e3,\\n toNano: (ms) => ms * 1e6,\\n toMicro: (ms) => ms * 1e3,\\n toSecond: (ms) => ms / 1e3,\\n now: () => performance.now(),\\n add: (a, b) => a + b,\\n sub: (a, b) => a - b,\\n mul: (a, b) => a * b,\\n div: (a, b) => a / b,\\n max: (a, b) => Math.max(a, b),\\n min: (a, b) => Math.min(a, b)\\n});\\nvar Timescale = Object.assign(\\n (unitsPerSecond) => {\\n if (!Number.isInteger(unitsPerSecond) || unitsPerSecond <= 0) {\\n throw new Error(`invalid timescale: ${unitsPerSecond}`);\\n }\\n return unitsPerSecond;\\n },\\n {\\n /** One unit per second. */\\n SECOND: 1,\\n /** 1,000 units per second. */\\n MILLI: 1e3,\\n /** 1,000,000 units per second. */\\n MICRO: 1e6,\\n /** 1,000,000,000 units per second. */\\n NANO: 1e9\\n }\\n);\\nvar Timestamp = class _Timestamp {\\n /** The raw value, in `scale` units. */\\n value;\\n /** Units per second the {@link value} is measured in. */\\n scale;\\n /** Build a timestamp of `value` units at `scale`. */\\n constructor(value, scale) {\\n if (!Number.isFinite(value) || value < 0) {\\n throw new Error(`invalid timestamp: ${value}`);\\n }\\n this.value = value;\\n this.scale = scale;\\n }\\n /** Monotonic now (`performance.now()`, milliseconds since page load), not wall-clock time. */\\n static now() {\\n return new _Timestamp(performance.now(), Timescale.MILLI);\\n }\\n /** A timestamp of `ms` milliseconds. */\\n static fromMillis(ms) {\\n return new _Timestamp(ms, Timescale.MILLI);\\n }\\n /** A timestamp of `us` microseconds. */\\n static fromMicros(us) {\\n return new _Timestamp(us, Timescale.MICRO);\\n }\\n /** This timestamp's value re-expressed at `scale` (a raw number, not a new Timestamp). */\\n as(scale) {\\n return scale === this.scale ? this.value : this.value * scale / this.scale;\\n }\\n /** The value in milliseconds. */\\n asMillis() {\\n return this.as(Timescale.MILLI);\\n }\\n /** The value in microseconds. */\\n asMicros() {\\n return this.as(Timescale.MICRO);\\n }\\n};\\nvar Second = Object.assign((value) => value, {\\n zero: 0,\\n fromNano: (ns) => ns / 1e9,\\n fromMicro: (us) => us / 1e6,\\n fromMilli: (ms) => ms / 1e3,\\n toNano: (s) => s * 1e9,\\n toMicro: (s) => s * 1e6,\\n toMilli: (s) => s * 1e3,\\n now: () => performance.now() / 1e3,\\n add: (a, b) => a + b,\\n sub: (a, b) => a - b,\\n mul: (a, b) => a * b,\\n div: (a, b) => a / b,\\n max: (a, b) => Math.max(a, b),\\n min: (a, b) => Math.min(a, b)\\n});\\n\\n// src/audio/ring-buffer.ts\\nvar AudioRingBuffer = class {\\n #buffer;\\n #writeIndex = 0;\\n #readIndex = 0;\\n rate;\\n channels;\\n #stalled = true;\\n // Buffered mode: play through everything buffered without skipping ahead.\\n #buffered;\\n // Un-stall threshold in samples (how much to buffer before playback starts).\\n #latencySamples;\\n // Whether the read/write indices have been anchored to the first inserted sample.\\n #anchored = false;\\n constructor(props) {\\n if (props.channels <= 0) throw new Error(\\\"invalid channels\\\");\\n if (props.rate <= 0) throw new Error(\\\"invalid sample rate\\\");\\n if (props.latency <= 0) throw new Error(\\\"invalid latency\\\");\\n this.#latencySamples = Math.ceil(props.rate * time_exports.Second.fromMilli(props.latency));\\n if (this.#latencySamples === 0) throw new Error(\\\"empty buffer\\\");\\n this.rate = props.rate;\\n this.channels = props.channels;\\n this.#buffered = props.buffered ?? false;\\n const capacity = this.#capacityFor(this.#latencySamples);\\n this.#buffer = [];\\n for (let i = 0; i < this.channels; i++) {\\n this.#buffer[i] = new Float32Array(capacity);\\n }\\n }\\n #capacityFor(latencySamples) {\\n return this.#buffered ? latencySamples * 2 : latencySamples;\\n }\\n get stalled() {\\n return this.#stalled;\\n }\\n get timestamp() {\\n return time_exports.Micro.fromSecond(this.#readIndex / this.rate);\\n }\\n get length() {\\n return this.#writeIndex - this.#readIndex;\\n }\\n get capacity() {\\n return this.#buffer[0]?.length;\\n }\\n resize(latency) {\\n this.#latencySamples = Math.ceil(this.rate * time_exports.Second.fromMilli(latency));\\n const newCapacity = this.#capacityFor(this.#latencySamples);\\n if (newCapacity === this.capacity) return;\\n if (newCapacity === 0) throw new Error(\\\"empty buffer\\\");\\n const newBuffer = [];\\n for (let i = 0; i < this.channels; i++) {\\n newBuffer[i] = new Float32Array(newCapacity);\\n }\\n const samplesToKeep = Math.min(this.length, newCapacity);\\n if (samplesToKeep > 0) {\\n const copyStart = this.#writeIndex - samplesToKeep;\\n for (let channel = 0; channel < this.channels; channel++) {\\n const src = this.#buffer[channel];\\n const dst = newBuffer[channel];\\n for (let i = 0; i < samplesToKeep; i++) {\\n const srcPos = (copyStart + i) % src.length;\\n const dstPos = (copyStart + i) % dst.length;\\n dst[dstPos] = src[srcPos];\\n }\\n }\\n }\\n this.#buffer = newBuffer;\\n this.#readIndex = this.#writeIndex - samplesToKeep;\\n if (samplesToKeep === 0) this.#stalled = true;\\n }\\n write(timestamp, data) {\\n if (data.length !== this.channels) throw new Error(\\\"wrong number of channels\\\");\\n let start = Math.round(time_exports.Second.fromMicro(timestamp) * this.rate);\\n let samples = data[0].length;\\n if (!this.#anchored) {\\n this.#readIndex = start;\\n this.#writeIndex = start;\\n this.#anchored = true;\\n }\\n let offset = this.#readIndex - start;\\n if (offset > samples) {\\n return;\\n } else if (offset > 0) {\\n samples -= offset;\\n start += offset;\\n } else {\\n offset = 0;\\n }\\n const end = start + samples;\\n const overflow = end - this.#readIndex - this.#buffer[0].length;\\n if (overflow >= 0) {\\n this.#stalled = false;\\n this.#readIndex += overflow;\\n }\\n if (start > this.#writeIndex) {\\n const gapSize = Math.min(start - this.#writeIndex, this.#buffer[0].length);\\n if (gapSize === 1) {\\n console.warn(\\\"floating point inaccuracy detected\\\");\\n }\\n for (let channel = 0; channel < this.channels; channel++) {\\n const dst = this.#buffer[channel];\\n for (let i = 0; i < gapSize; i++) {\\n const writePos = (this.#writeIndex + i) % dst.length;\\n dst[writePos] = 0;\\n }\\n }\\n }\\n for (let channel = 0; channel < this.channels; channel++) {\\n let src = data[channel];\\n src = src.subarray(src.length - samples);\\n const dst = this.#buffer[channel];\\n if (src.length !== samples) throw new Error(\\\"mismatching number of samples\\\");\\n for (let i = 0; i < samples; i++) {\\n const writePos = (start + i) % dst.length;\\n dst[writePos] = src[i];\\n }\\n }\\n if (end > this.#writeIndex) {\\n this.#writeIndex = end;\\n }\\n if (this.#buffered && this.length >= this.#latencySamples) {\\n this.#stalled = false;\\n }\\n }\\n /**\\n * Drop buffered samples at or after `timestamp`, keeping whatever is already due.\\n *\\n * A successor track overwrites the slots its own samples land on, but anything the previous\\n * track wrote beyond them would otherwise still play once the successor runs out.\\n */\\n truncate(timestamp) {\\n const target = Math.round(time_exports.Second.fromMicro(timestamp) * this.rate);\\n if (target >= this.#writeIndex) return;\\n this.#writeIndex = Math.max(target, this.#readIndex);\\n }\\n // Flush all buffered samples and re-stall, ready to anchor the next utterance.\\n reset() {\\n this.#readIndex = 0;\\n this.#writeIndex = 0;\\n this.#stalled = true;\\n this.#anchored = false;\\n }\\n read(output) {\\n if (output.length !== this.channels) throw new Error(\\\"wrong number of channels\\\");\\n if (this.#stalled) return 0;\\n const samples = Math.min(this.#writeIndex - this.#readIndex, output[0].length);\\n if (samples === 0) return 0;\\n for (let channel = 0; channel < this.channels; channel++) {\\n const dst = output[channel];\\n const src = this.#buffer[channel];\\n if (dst.length !== output[0].length) throw new Error(\\\"mismatching number of samples\\\");\\n for (let i = 0; i < samples; i++) {\\n const readPos = (this.#readIndex + i) % src.length;\\n dst[i] = src[readPos];\\n }\\n }\\n this.#readIndex += samples;\\n return samples;\\n }\\n};\\n\\n// src/audio/shared-ring-buffer.ts\\nvar WRITE = 0;\\nvar LATENCY = 1;\\nvar STALLED = 2;\\nvar CONTROL_SLOTS = 3;\\nvar CURSOR_MASK = 0xffffffffn;\\nfunction pack(epoch, read) {\\n return BigInt(epoch >>> 0) << 32n | BigInt(read >>> 0);\\n}\\nfunction epochOf(state) {\\n return Number(state >> 32n) | 0;\\n}\\nfunction readOf(state) {\\n return Number(state & CURSOR_MASK) | 0;\\n}\\nfunction ceilPow2(n) {\\n return n <= 1 ? 1 : 1 << 32 - Math.clz32(n - 1);\\n}\\nfunction allocSharedRingBuffer(channels, capacity, rate, buffered = false) {\\n if (channels <= 0) throw new Error(\\\"invalid channels\\\");\\n if (capacity <= 0 || capacity > 2 ** 30) throw new Error(\\\"invalid capacity\\\");\\n if (rate <= 0) throw new Error(\\\"invalid sample rate\\\");\\n capacity = ceilPow2(capacity);\\n const samples = new SharedArrayBuffer(channels * capacity * Float32Array.BYTES_PER_ELEMENT);\\n const control = new SharedArrayBuffer(CONTROL_SLOTS * Int32Array.BYTES_PER_ELEMENT);\\n const state = new SharedArrayBuffer(BigInt64Array.BYTES_PER_ELEMENT);\\n const ctrl = new Int32Array(control);\\n Atomics.store(ctrl, STALLED, 1);\\n return { channels, capacity, rate, samples, control, state, buffered };\\n}\\nfunction i32Max(a, b) {\\n return (a - b | 0) > 0 ? a : b;\\n}\\nfunction slot(idx, capacity) {\\n return idx & capacity - 1;\\n}\\nvar SharedRingBuffer = class _SharedRingBuffer {\\n channels;\\n capacity;\\n rate;\\n buffered;\\n init;\\n #control;\\n #state;\\n #samples;\\n // Whether READ/WRITE have been anchored to the first inserted sample.\\n #anchored = false;\\n // Absolute sample index of that first sample. READ/WRITE are stored relative to it, so\\n // `timestamp` adds it back to recover media time. Main-thread only: the worklet reads by\\n // difference and never needs an absolute position.\\n #anchor = 0;\\n // Unwrapped READ, in anchor-relative samples, plus the raw i32 it was last derived from.\\n // READ is Int32 and wraps every ~13.5h at 44.1kHz. That is harmless for the modular\\n // comparisons the ring runs on, but reading the negative half as a magnitude would report\\n // the playhead jumping back 2^32 samples, so accumulate deltas here instead. Main-thread\\n // only, and only accurate while `timestamp` is observed more often than READ can advance\\n // 2^31 samples, which the 50ms poll in `buffer.ts` satisfies by six orders of magnitude.\\n #position = 0;\\n #lastRead = 0;\\n /**\\n * Wrap the shared memory described by `init`.\\n *\\n * Pass `previous` in the worklet when this ring replaces one already being read. `resize`\\n * snapshots the playhead on the main thread and hands the replacement over by message, so the\\n * reader keeps draining the old ring in the meantime. Carrying its cursor across means those\\n * samples are not played twice.\\n */\\n constructor(init, previous) {\\n this.channels = init.channels;\\n this.capacity = init.capacity;\\n this.rate = init.rate;\\n this.buffered = init.buffered;\\n this.init = init;\\n this.#control = new Int32Array(init.control);\\n this.#state = new BigInt64Array(init.state);\\n this.#samples = [];\\n for (let i = 0; i < this.channels; i++) {\\n this.#samples.push(\\n new Float32Array(init.samples, i * this.capacity * Float32Array.BYTES_PER_ELEMENT, this.capacity)\\n );\\n }\\n if (previous !== void 0) this.#handoff(previous);\\n }\\n /**\\n * Carry `source`'s playhead across, if the replacement is still the timeline it belongs to.\\n *\\n * `resize` copies the epoch, so a destination that re-anchored since has a different one and\\n * the cursor is dropped rather than applied to audio the reader has never played. The check\\n * and the write are the same exchange, so a re-anchor landing mid-handoff makes it fail\\n * instead of slipping through the gap that separate operations would leave.\\n */\\n #handoff(source) {\\n if (source.channels !== this.channels || source.rate !== this.rate) return;\\n const from = Atomics.load(source.#state, 0);\\n for (; ; ) {\\n const state = Atomics.load(this.#state, 0);\\n if (epochOf(state) !== epochOf(from)) return;\\n if ((readOf(from) - readOf(state) | 0) <= 0) return;\\n const next = pack(epochOf(state), readOf(from));\\n if (Atomics.compareExchange(this.#state, 0, state, next) === state) return;\\n }\\n }\\n /**\\n * Move the playhead forward to `candidate`, staying on whatever timeline is current.\\n *\\n * Retries while the word changes under it, and never steps backwards. Used by the writer's\\n * overflow path; the reader publishes with its own exchange so it can tell a rebase apart\\n * from losing a race.\\n */\\n #advance(candidate) {\\n for (; ; ) {\\n const state = Atomics.load(this.#state, 0);\\n if ((candidate - readOf(state) | 0) <= 0) return;\\n const next = pack(epochOf(state), candidate);\\n if (Atomics.compareExchange(this.#state, 0, state, next) === state) return;\\n }\\n }\\n /**\\n * Insert audio samples at the given timestamp.\\n * Main thread only. Handles out-of-order writes, gap filling, and overflow.\\n */\\n insert(timestamp, data) {\\n if (data.length !== this.channels) throw new Error(\\\"wrong number of channels\\\");\\n let start = Math.round(time_exports.Second.fromMicro(timestamp) * this.rate);\\n const originalLength = data[0].length;\\n let offset = 0;\\n if (!this.#anchored) {\\n this.#anchor = start;\\n const epoch = epochOf(Atomics.load(this.#state, 0));\\n Atomics.store(this.#state, 0, pack(epoch + 1 | 0, 0));\\n Atomics.store(this.#control, WRITE, 0);\\n this.#anchored = true;\\n this.#position = 0;\\n this.#lastRead = 0;\\n }\\n start = start - this.#anchor | 0;\\n const end = start + originalLength | 0;\\n const read = readOf(Atomics.load(this.#state, 0));\\n const behind = read - start | 0;\\n if (behind > 0) {\\n if (behind >= originalLength) {\\n return;\\n }\\n offset = behind;\\n start = start + behind | 0;\\n }\\n const samples = originalLength - offset;\\n if ((end - read | 0) > this.capacity) {\\n this.#advance(end - this.capacity | 0);\\n }\\n const write = Atomics.load(this.#control, WRITE);\\n const gap = start - write | 0;\\n if (gap > 0) {\\n const gapSize = Math.min(gap, this.capacity);\\n for (let channel = 0; channel < this.channels; channel++) {\\n const dst = this.#samples[channel];\\n for (let i = 0; i < gapSize; i++) {\\n dst[slot(write + i | 0, this.capacity)] = 0;\\n }\\n }\\n }\\n for (let channel = 0; channel < this.channels; channel++) {\\n const src = data[channel];\\n const dst = this.#samples[channel];\\n for (let i = 0; i < samples; i++) {\\n dst[slot(start + i | 0, this.capacity)] = src[offset + i];\\n }\\n }\\n Atomics.store(this.#control, WRITE, i32Max(Atomics.load(this.#control, WRITE), end));\\n const currentRead = readOf(Atomics.load(this.#state, 0));\\n const currentWrite = Atomics.load(this.#control, WRITE);\\n const latency = Atomics.load(this.#control, LATENCY);\\n if ((currentWrite - currentRead | 0) >= latency && latency > 0) {\\n Atomics.store(this.#control, STALLED, 0);\\n }\\n }\\n /**\\n * Read audio samples into the output buffers.\\n * AudioWorklet only. Returns the number of samples read.\\n */\\n read(output) {\\n const state = Atomics.load(this.#state, 0);\\n if (Atomics.load(this.#control, STALLED) === 1) return 0;\\n let read = readOf(state);\\n const write = Atomics.load(this.#control, WRITE);\\n const latency = Atomics.load(this.#control, LATENCY);\\n const buffered = write - read | 0;\\n if (!this.buffered && latency > 0 && buffered > latency) {\\n const skipTo = write - latency | 0;\\n if ((skipTo - read | 0) > 0) read = skipTo;\\n }\\n const available = write - read | 0;\\n const count = Math.min(available, output[0].length);\\n if (count <= 0) {\\n if ((read - readOf(state) | 0) > 0) {\\n Atomics.compareExchange(this.#state, 0, state, pack(epochOf(state), read));\\n }\\n return 0;\\n }\\n for (let channel = 0; channel < this.channels; channel++) {\\n const src = this.#samples[channel];\\n const dst = output[channel];\\n for (let i = 0; i < count; i++) {\\n dst[i] = src[slot(read + i | 0, this.capacity)];\\n }\\n }\\n const next = pack(epochOf(state), read + count | 0);\\n if (Atomics.compareExchange(this.#state, 0, state, next) !== state) {\\n for (let channel = 0; channel < this.channels; channel++) output[channel].fill(0, 0, count);\\n return 0;\\n }\\n return count;\\n }\\n /** Update the target latency in samples. */\\n setLatency(samples) {\\n Atomics.store(this.#control, LATENCY, samples);\\n }\\n /**\\n * Drop buffered samples at or after `timestamp`, keeping whatever is already due.\\n * Main thread only.\\n *\\n * A successor track overwrites the slots its own samples land on, but anything the previous\\n * track wrote beyond them would otherwise still play once the successor runs out.\\n */\\n truncate(timestamp) {\\n const target = Math.round(time_exports.Second.fromMicro(timestamp) * this.rate) - this.#anchor | 0;\\n for (; ; ) {\\n const write = Atomics.load(this.#control, WRITE);\\n if ((write - target | 0) <= 0) return;\\n const clamped = i32Max(target, readOf(Atomics.load(this.#state, 0)));\\n if ((write - clamped | 0) <= 0) return;\\n if (Atomics.compareExchange(this.#control, WRITE, write, clamped) === write) return;\\n }\\n }\\n /**\\n * Flush buffered samples and re-stall, ready to anchor the next utterance (buffered mode).\\n * Main thread only. The worklet reader sees STALLED and stops until the next insert.\\n */\\n reset() {\\n this.#anchored = false;\\n Atomics.store(this.#control, STALLED, 1);\\n const write = Atomics.load(this.#control, WRITE);\\n const state = Atomics.load(this.#state, 0);\\n Atomics.store(this.#state, 0, pack(epochOf(state), write));\\n }\\n /**\\n * Allocate a new ring with `newCapacity` samples and copy the unread window\\n * [READ, WRITE) plus control state into it. Used when growing capacity so\\n * we don't drop buffered audio. If `newCapacity` is smaller than the unread\\n * span, the oldest samples are truncated.\\n *\\n * Main thread only. `resize()` reads from the source `SharedRingBuffer` and\\n * writes into a freshly allocated buffer from `allocSharedRingBuffer`, so it\\n * relies on the same invariant as `insert()`: no concurrent main-thread\\n * writers. The AudioWorklet reader is tolerated via the CAS discipline used\\n * by READ/WRITE elsewhere.\\n */\\n resize(newCapacity) {\\n const init = allocSharedRingBuffer(this.channels, newCapacity, this.rate, this.buffered);\\n const dst = new _SharedRingBuffer(init);\\n dst.#anchored = this.#anchored;\\n dst.#anchor = this.#anchor;\\n const state = Atomics.load(this.#state, 0);\\n const read = readOf(state);\\n const write = Atomics.load(this.#control, WRITE);\\n const latency = Atomics.load(this.#control, LATENCY);\\n const stalled = Atomics.load(this.#control, STALLED);\\n const available = write - read | 0;\\n const copyCount = Math.max(0, Math.min(available, dst.capacity));\\n const copyStart = write - copyCount | 0;\\n for (let channel = 0; channel < this.channels; channel++) {\\n const src = this.#samples[channel];\\n const out = dst.#samples[channel];\\n for (let i = 0; i < copyCount; i++) {\\n const idx = copyStart + i | 0;\\n out[slot(idx, dst.capacity)] = src[slot(idx, this.capacity)];\\n }\\n }\\n Atomics.store(dst.#state, 0, pack(epochOf(state), copyStart));\\n Atomics.store(dst.#control, WRITE, write);\\n Atomics.store(dst.#control, LATENCY, latency);\\n Atomics.store(dst.#control, STALLED, stalled);\\n dst.#position = this.#foldRead(read) + (copyStart - read | 0);\\n dst.#lastRead = copyStart;\\n return dst;\\n }\\n /**\\n * Fold an observed READ into `#position`, so the returned offset keeps counting up across\\n * the i32 wrap. Idempotent: folding the same value twice adds a zero delta.\\n */\\n #foldRead(read) {\\n this.#position += read - this.#lastRead | 0;\\n this.#lastRead = read;\\n return this.#position;\\n }\\n /** `#foldRead` against the current READ. */\\n #unwrapRead() {\\n return this.#foldRead(readOf(Atomics.load(this.#state, 0)));\\n }\\n /**\\n * Current playback timestamp derived from READ position.\\n *\\n * Main thread only, and stateful: it advances the unwrapped read position, so it has to be\\n * polled rather than sampled once. See `#position`.\\n */\\n get timestamp() {\\n return time_exports.Micro.fromSecond((this.#anchor + this.#unwrapRead()) / this.rate);\\n }\\n /** Whether the buffer is stalled (waiting to fill). */\\n get stalled() {\\n return Atomics.load(this.#control, STALLED) === 1;\\n }\\n /**\\n * Number of buffered samples (WRITE - READ).\\n *\\n * Non-atomic: WRITE and READ are loaded separately, so a concurrent\\n * writer/reader can make the two loads inconsistent. Intended for\\n * tests and diagnostics, not control-flow decisions.\\n */\\n get length() {\\n return Atomics.load(this.#control, WRITE) - readOf(Atomics.load(this.#state, 0)) | 0;\\n }\\n};\\n\\n// src/audio/render-worklet.ts\\nvar Render = class extends AudioWorkletProcessor {\\n // Set after init, depending on which path the main thread chose.\\n #backend;\\n #underflow = 0;\\n #stateCounter = 0;\\n constructor() {\\n super();\\n this.port.onmessage = (event) => {\\n const msg = event.data;\\n if (msg.type === \\\"init-shared\\\") {\\n console.log(\\\"[audio-worklet] init-shared: using SharedArrayBuffer path\\\");\\n const previous = this.#backend instanceof SharedRingBuffer ? this.#backend : void 0;\\n this.#backend = new SharedRingBuffer(msg, previous);\\n this.#underflow = 0;\\n } else if (msg.type === \\\"init-post\\\") {\\n console.log(\\\"[audio-worklet] init-post: using postMessage path\\\");\\n this.#backend = new AudioRingBuffer(msg);\\n this.#underflow = 0;\\n } else if (msg.type === \\\"data\\\") {\\n if (this.#backend instanceof AudioRingBuffer) this.#backend.write(msg.timestamp, msg.data);\\n } else if (msg.type === \\\"latency\\\") {\\n if (this.#backend instanceof AudioRingBuffer) this.#backend.resize(msg.latency);\\n } else if (msg.type === \\\"truncate\\\") {\\n if (this.#backend instanceof AudioRingBuffer) this.#backend.truncate(msg.timestamp);\\n } else if (msg.type === \\\"reset\\\") {\\n if (this.#backend instanceof AudioRingBuffer) this.#backend.reset();\\n }\\n };\\n }\\n process(_inputs, outputs, _parameters) {\\n const output = outputs[0];\\n const backend = this.#backend;\\n const samplesRead = backend?.read(output) ?? 0;\\n if (samplesRead < output[0].length) {\\n this.#underflow += output[0].length - samplesRead;\\n } else if (this.#underflow > 0 && backend) {\\n console.debug(`audio underflow: ${Math.round(1e3 * this.#underflow / backend.rate)}ms`);\\n this.#underflow = 0;\\n }\\n if (backend instanceof AudioRingBuffer) {\\n this.#stateCounter++;\\n if (this.#stateCounter >= 5) {\\n this.#stateCounter = 0;\\n const state = {\\n type: \\\"state\\\",\\n timestamp: backend.timestamp,\\n stalled: backend.stalled\\n };\\n this.port.postMessage(state);\\n }\\n }\\n return true;\\n }\\n};\\nregisterProcessor(\\\"render\\\", Render);\\n\";\nconst blob = new Blob([code], { type: \"application/javascript\" });\nexport default URL.createObjectURL(blob);","import * as Catalog from \"@moq/hang/catalog\";\nimport type * as Moq from \"@moq/net\";\nimport type { Time } from \"@moq/net\";\nimport type { Effect, Getter } from \"@moq/signals\";\n\n/** Opens a live-edge audio subscription and keeps its expiry budget aligned with playback. @internal */\nexport function subscribe(\n\teffect: Effect,\n\t{\n\t\tbroadcast,\n\t\ttrack,\n\t\tmaxLatency,\n\t}: {\n\t\tbroadcast: Moq.Broadcast.Consumer;\n\t\ttrack: string;\n\t\tmaxLatency: Getter<Time.Milli>;\n\t},\n): Moq.Track.Subscriber {\n\tconst priority = Catalog.PRIORITY.audio;\n\tlet latencyMax = Math.ceil(maxLatency.peek());\n\tconst subscriber = broadcast.track(track).subscribe({ priority, latencyMax });\n\teffect.cleanup(() => subscriber.close());\n\n\teffect.run((inner) => {\n\t\tconst next = Math.ceil(inner.get(maxLatency));\n\t\tif (next === latencyMax) return;\n\t\tlatencyMax = next;\n\t\tsubscriber.update({ priority, latencyMax });\n\t});\n\n\treturn subscriber;\n}\n","import type { Time } from \"@moq/net\";\n\ninterface SampleSpan {\n\treadonly timestamp: number;\n\treadonly sampleRate: number;\n\treadonly numberOfFrames: number;\n}\n\ninterface Update {\n\treadonly discontinuity: number;\n\treadonly end?: Time.Micro;\n\treadonly frame?: { readonly timestamp: Time.Micro };\n}\n\n/** The portion of decoded audio that belongs to the source timeline. */\nexport interface DecodedSpan {\n\t/** Source timestamp of the first retained frame. */\n\treadonly timestamp: Time.Micro;\n\t/** Decoded frames to skip before copying. */\n\treadonly frameOffset: number;\n\t/** Decoded frames to retain after the offset. */\n\treadonly frames: number;\n}\n\n/** Track codec epochs and map decoded output onto the source timeline. */\nexport class Terminal {\n\t#discontinuity = 0;\n\t#end?: Time.Micro;\n\t#epoch?: Time.Micro;\n\t#preSkip = 0;\n\t#preSkipRemaining?: number;\n\n\t/** The exclusive source endpoint most recently received. */\n\tget end(): Time.Micro | undefined {\n\t\treturn this.#end;\n\t}\n\n\t/** Reset state for a new container consumer with Opus pre-skip in 48 kHz frames. */\n\tclear(preSkip = 0): void {\n\t\tthis.#discontinuity = 0;\n\t\tthis.#end = undefined;\n\t\tthis.#preSkip = preSkip;\n\t\tthis.#resetEpoch();\n\t}\n\n\t/** Apply one ordered consumer result and report whether its codec epoch changed. */\n\tupdate(next: Update): boolean {\n\t\tconst reset = next.discontinuity !== this.#discontinuity;\n\t\tif (reset) {\n\t\t\tthis.#discontinuity = next.discontinuity;\n\t\t\tthis.#end = undefined;\n\t\t\tthis.#resetEpoch();\n\t\t}\n\t\tif (next.frame && this.#epoch === undefined) this.#epoch = next.frame.timestamp;\n\t\tif (next.end !== undefined) this.#end = next.end;\n\t\treturn reset;\n\t}\n\n\t/** Remove codec pre-skip and terminal padding from one decoded sample. */\n\tspan(sample: SampleSpan): DecodedSpan {\n\t\tconst epoch = this.#epoch ?? (sample.timestamp as Time.Micro);\n\t\tthis.#epoch = epoch;\n\n\t\tconst delayFrames = Math.floor((this.#preSkip * sample.sampleRate) / 48_000);\n\t\tconst remaining = this.#preSkipRemaining ?? delayFrames;\n\t\tconst frameOffset = Math.min(remaining, sample.numberOfFrames);\n\t\tthis.#preSkipRemaining = remaining - frameOffset;\n\n\t\tconst delay = Math.round((delayFrames * 1_000_000) / sample.sampleRate);\n\t\tconst timestamp = Math.max(epoch, sample.timestamp - delay) as Time.Micro;\n\t\tlet frames = sample.numberOfFrames - frameOffset;\n\t\tif (this.#end !== undefined) {\n\t\t\tif (this.#end <= timestamp) {\n\t\t\t\tframes = 0;\n\t\t\t} else {\n\t\t\t\tconst duration = this.#end - timestamp;\n\t\t\t\tconst terminalFrames = Math.round((duration * sample.sampleRate) / 1_000_000);\n\t\t\t\tframes = Math.min(frames, terminalFrames);\n\t\t\t}\n\t\t}\n\n\t\treturn { timestamp, frameOffset, frames };\n\t}\n\n\t#resetEpoch(): void {\n\t\tthis.#epoch = undefined;\n\t\tthis.#preSkipRemaining = undefined;\n\t}\n}\n","import { type Effect, Signal } from \"@moq/signals\";\n\n/**\n * Resume a suspended {@link AudioContext} from a real user gesture.\n *\n * The context is built at page load, before any user activation can exist, so browsers that\n * gate audio on a gesture reject a `resume()` made then. A single unconditional attempt would\n * fire once, be rejected, and never retry, leaving audio silent. This instead attempts\n * `resume()` immediately (for autoplay-permissive browsers like Chrome with prior engagement),\n * then retries on every `pointerdown`/`keydown` until the context is actually running, dropping\n * the gesture listeners once it is. `pointerdown` and `keydown` cover mouse, touch, pen, and\n * keyboard, and each carries a user activation.\n *\n * Safari also reports an \"interrupted\" state (a WebKit-only value outside the\n * suspended/running/closed set) and can leave it on its own; mirroring `statechange` into a\n * signal picks that up so the listeners are re-armed or dropped as the state moves.\n *\n * Scoped to `effect`: the listeners are removed when the effect reruns or closes.\n */\nexport function unlockOnGesture(effect: Effect, context: AudioContext): void {\n\tconst running = new Signal(context.state === \"running\");\n\teffect.event(context, \"statechange\", () => running.set(context.state === \"running\"));\n\n\teffect.run((inner) => {\n\t\tif (inner.get(running)) return;\n\n\t\tconst resume = () => {\n\t\t\tcontext.resume().catch(() => {});\n\t\t};\n\n\t\tresume();\n\t\tinner.event(document, \"pointerdown\", resume);\n\t\tinner.event(document, \"keydown\", resume);\n\t});\n}\n","/** Tracks decoder callbacks discarded during initial playback setup. */\nexport class Warmup {\n\t#remaining: number;\n\n\t/** Create a warm-up window containing `callbacks` decoder callbacks. */\n\tconstructor(callbacks: number) {\n\t\tthis.#remaining = callbacks;\n\t}\n\n\t/** Consume one callback and report whether it belongs to the warm-up window. */\n\tdrop(): boolean {\n\t\tif (this.#remaining === 0) return false;\n\t\tthis.#remaining--;\n\t\treturn true;\n\t}\n}\n","import * as Catalog from \"@moq/hang/catalog\";\nimport * as Container from \"@moq/hang/container\";\nimport * as Util from \"@moq/hang/util\";\nimport type * as Moq from \"@moq/net\";\nimport { Time } from \"@moq/net\";\nimport { Effect, type Getter, getter, type Inputs, type Readonlys, readonlys, Signal } from \"@moq/signals\";\nimport { base64ToBytes } from \"../base64\";\n\nimport { type Bound, latencyBounds, type Sync } from \"../sync\";\nimport { type AudioBuffer, createAudioBuffer } from \"./buffer\";\nimport { Handover } from \"./handover\";\n// Compiled and inlined as a blob URL via vite-plugin-worklet.\nimport RenderWorklet from \"./render-worklet.ts?worklet\";\nimport type { Source } from \"./source\";\nimport { subscribe } from \"./subscription\";\nimport { type DecodedSpan, Terminal } from \"./terminal\";\nimport { unlockOnGesture } from \"./unlock\";\nimport { Warmup } from \"./warmup\";\n\n// How long the latency target must hold steady before a floor increase re-anchors. Coalesces a\n// slider drag (many small steps) into a single re-anchor once the user settles on a value.\nconst LATENCY_REANCHOR_DEBOUNCE_MS = 150;\nconst LEGACY_WARMUP_CALLBACKS = 3;\n\nexport type DecoderInput = {\n\t// Enable to download the audio track.\n\tenabled: Getter<boolean>;\n};\n\ntype DecoderOutput = {\n\tcontext: Signal<AudioContext | undefined>;\n\n\t// The root of the audio graph, which can be used for custom visualizations.\n\t// Downcast to AudioNode so it matches Publish.Audio\n\troot: Signal<AudioNode | undefined>;\n\n\tsampleRate: Signal<number | undefined>;\n\tstats: Signal<Stats | undefined>;\n\n\t// Current playback timestamp from worklet\n\ttimestamp: Signal<Time.Milli | undefined>;\n\n\t// Whether the audio buffer is stalled (waiting to fill)\n\tstalled: Signal<boolean>;\n\n\t// Combined buffered ranges (network jitter + decode buffer)\n\tbuffered: Signal<Container.BufferedRanges>;\n};\n\n/** Cumulative audio statistics since the decoder started. */\nexport interface Stats {\n\t/** Number of encoded bytes received. */\n\tbytesReceived: number;\n}\n\n/**\n * Downloads audio from a track and emits it to an AudioContext.\n *\n * The user is responsible for hooking up audio to speakers, an analyzer, etc.\n */\nexport class Decoder {\n\treadonly in: Readonlys<DecoderInput>;\n\treadonly source: Source;\n\treadonly sync: Sync;\n\n\treadonly #out: DecoderOutput = {\n\t\tcontext: new Signal<AudioContext | undefined>(undefined),\n\t\troot: new Signal<AudioNode | undefined>(undefined),\n\t\tsampleRate: new Signal<number | undefined>(undefined),\n\t\tstats: new Signal<Stats | undefined>(undefined),\n\t\ttimestamp: new Signal<Time.Milli | undefined>(undefined),\n\t\tstalled: new Signal<boolean>(true),\n\t\tbuffered: new Signal<Container.BufferedRanges>([]),\n\t};\n\treadonly out = readonlys(this.#out);\n\n\t// Decode buffer: audio sent to worklet but not yet played\n\t#decodeBuffered = new Signal<Container.BufferedRanges>([]);\n\n\t// Audio ring bridging main thread and worklet (shared memory or postMessage transport).\n\t#ring: AudioBuffer | undefined;\n\n\t// The rate the decoder actually outputs, learned from the first decoded frame. This is the source\n\t// of truth for the graph: a decoder can output a different rate than it was configured with (e.g.\n\t// Opus decodes to 48kHz on Chrome/Firefox but to the configured rate on Safari). Until a frame\n\t// arrives we pre-build the graph from the catalog rate; if the real rate differs we rebuild it.\n\t#decodedSampleRate = new Signal<number | undefined>(undefined);\n\n\t// Ordered discontinuity and endpoint state from the container consumer.\n\t#terminal = new Terminal();\n\n\t// The latency floor as of the last settled change, to detect a floor *increase* (needs a deeper\n\t// cushion) versus a decrease or a real-time RTT wiggle. See #runLatencyReanchor.\n\t#prevFloor?: Bound;\n\n\t// Which subscription the ring's buffered samples came from. See #runDecoder.\n\t#handover = new Handover();\n\n\t#signals = new Effect();\n\n\tconstructor(source: Source, sync: Sync, props?: Inputs<DecoderInput>) {\n\t\tthis.in = {\n\t\t\tenabled: getter(props?.enabled ?? false),\n\t\t};\n\n\t\tthis.source = source;\n\t\tthis.sync = sync;\n\n\t\tthis.#signals.run(this.#runWorklet.bind(this));\n\t\tthis.#signals.run(this.#runEnabled.bind(this));\n\t\tthis.#signals.run(this.#runLatency.bind(this));\n\t\tthis.#signals.run(this.#runLatencyReanchor.bind(this));\n\t\tthis.#signals.run(this.#runDecoder.bind(this));\n\t}\n\n\t#runWorklet(effect: Effect): void {\n\t\t// It takes a second or so to initialize the AudioContext/AudioWorklet, so do it even if disabled.\n\t\t// This is less efficient for video-only playback but makes muting/unmuting instant.\n\n\t\t//const enabled = effect.get(this.enabled);\n\t\t//if (!enabled) return;\n\n\t\tconst config = effect.get(this.source.out.config);\n\t\tif (!config) return;\n\n\t\t// Pre-build the graph at the catalog rate so warm-up starts before the first frame arrives. The\n\t\t// decoder's actual output rate is the source of truth (see #emit); if it differs, #emit sets\n\t\t// #decodedSampleRate, which re-runs this effect and rebuilds the graph at the real rate.\n\t\tconst sampleRate = effect.get(this.#decodedSampleRate) ?? config.sampleRate;\n\t\tconst channelCount = config.numberOfChannels;\n\n\t\t// Expose the rate the graph actually runs at.\n\t\teffect.set(this.#out.sampleRate, sampleRate);\n\n\t\tconst context = new AudioContext({\n\t\t\tlatencyHint: \"interactive\", // We don't use real-time because of the buffer.\n\t\t\tsampleRate,\n\t\t});\n\t\teffect.set(this.#out.context, context);\n\n\t\teffect.cleanup(() => context.close());\n\n\t\teffect.spawn(async () => {\n\t\t\t// Register the AudioWorklet processor, racing the load against teardown. If teardown wins,\n\t\t\t// `loaded` is undefined and we bail before constructing the node: the module registration was\n\t\t\t// abandoned, so building against its name would throw. Gate on the race result, not\n\t\t\t// `context.state`, because `AudioContext.close()` only flips `.state` to \"closed\" synchronously\n\t\t\t// on Chrome (Firefox/Safari report \"suspended\").\n\t\t\tconst loaded = await Promise.race([\n\t\t\t\tcontext.audioWorklet.addModule(RenderWorklet).then(() => true),\n\t\t\t\teffect.cancel,\n\t\t\t]);\n\t\t\tif (!loaded) return;\n\n\t\t\t// Create the worklet node. outputChannelCount must be set explicitly\n\t\t\t// so the process() callback receives a matching channel layout.\n\t\t\t// Firefox defaults differently than Chrome otherwise.\n\t\t\tconst worklet = new AudioWorkletNode(context, \"render\", {\n\t\t\t\tchannelCount,\n\t\t\t\tchannelCountMode: \"explicit\",\n\t\t\t\toutputChannelCount: [channelCount],\n\t\t\t});\n\t\t\teffect.cleanup(() => worklet.disconnect());\n\n\t\t\t// Initial target latency in samples.\n\t\t\tconst latency = this.sync.out.buffer.peek();\n\t\t\tconst latencySamples = Math.ceil(sampleRate * Time.Second.fromMilli(latency));\n\t\t\tconst buffered = this.sync.out.buffered.peek();\n\n\t\t\t// Let the factory pick the best transport (SharedArrayBuffer or postMessage).\n\t\t\tconst ring = createAudioBuffer(worklet, channelCount, sampleRate, latencySamples, buffered);\n\t\t\tthis.#ring = ring;\n\t\t\teffect.cleanup(() => {\n\t\t\t\tring.close();\n\t\t\t\tthis.#ring = undefined;\n\t\t\t});\n\n\t\t\t// Mirror ring state (timestamp/stalled) onto our public signals.\n\t\t\teffect.run((inner) => {\n\t\t\t\tconst ts = Time.Milli.fromMicro(inner.get(ring.timestamp));\n\t\t\t\tthis.#out.timestamp.set(ts);\n\t\t\t\tthis.#trimDecodeBuffered(ts);\n\t\t\t});\n\t\t\teffect.run((inner) => {\n\t\t\t\tthis.#out.stalled.set(inner.get(ring.stalled));\n\t\t\t});\n\n\t\t\teffect.set(this.#out.root, worklet);\n\t\t});\n\t}\n\n\t#runEnabled(effect: Effect): void {\n\t\tconst enabled = effect.get(this.in.enabled);\n\t\tif (!enabled) return;\n\n\t\tconst context = effect.get(this.#out.context);\n\t\tif (!context) return;\n\n\t\t// The context is built at page load (see #runWorklet), before any user gesture, so it\n\t\t// must be started from a real interaction. See unlockOnGesture.\n\t\tunlockOnGesture(effect, context);\n\n\t\t// NOTE: You should disconnect/reconnect the worklet to save power when disabled.\n\t}\n\n\t#runLatency(effect: Effect): void {\n\t\t// Gate on the worklet signal so this effect re-runs once the ring is created.\n\t\tconst worklet = effect.get(this.#out.root);\n\t\tif (!worklet) return;\n\n\t\tconst ring = this.#ring;\n\t\tif (!ring) return;\n\n\t\tconst latency = effect.get(this.sync.out.buffer);\n\t\tconst latencySamples = Math.ceil(ring.rate * Time.Second.fromMilli(latency));\n\t\tring.setLatency(latencySamples);\n\t}\n\n\t// Re-anchor when the latency floor *increases*. A larger floor needs a deeper cushion: video\n\t// rebuilds it implicitly (its per-frame sync.wait() reads the live buffer, so it just holds\n\t// longer), but the audio ring keeps draining at its old depth -- resize() (via setLatency) only\n\t// re-stalls an *empty* ring, so a mid-playback ring never refills to the new floor and audio runs\n\t// ahead of video (the \"raise latency, only video re-buffers\" desync). reset() re-stalls the ring\n\t// so it refills to the new floor. Watch the latency *target* (not the derived buffer) so real-time\n\t// RTT jitter never triggers this, and debounce so a slider drag coalesces into one re-anchor.\n\t// Decreases are left to natural catch-up.\n\t#runLatencyReanchor(effect: Effect): void {\n\t\tconst floor = latencyBounds(effect.get(this.sync.in.latency)).min;\n\t\tif (this.#prevFloor === undefined) {\n\t\t\t// Startup: the initial fill already builds the cushion; just record the baseline.\n\t\t\tthis.#prevFloor = floor;\n\t\t\treturn;\n\t\t}\n\t\t// When the timer fires, the floor read above is still current: any change would have rerun\n\t\t// this effect (tearing down the timer), so compare it against the pre-change baseline directly.\n\t\tconst baseline = this.#prevFloor;\n\t\teffect.timer(() => {\n\t\t\tconst toMs = (b: Bound): number => (b === \"real-time\" ? 0 : b);\n\t\t\tif (toMs(floor) > toMs(baseline)) this.reset();\n\t\t\tthis.#prevFloor = floor;\n\t\t}, LATENCY_REANCHOR_DEBOUNCE_MS);\n\t}\n\n\t#runDecoder(effect: Effect): void {\n\t\tconst enabled = effect.get(this.in.enabled);\n\t\tif (!enabled) return;\n\n\t\tconst broadcast = effect.get(this.source.in.broadcast);\n\t\tif (!broadcast) return;\n\n\t\tconst track = effect.get(this.source.out.track);\n\t\tif (!track) return;\n\n\t\tconst config = effect.get(this.source.out.config);\n\t\tif (!config) return;\n\n\t\t// Honor a per-rendition `broadcast` override: subscribe on the resolved source\n\t\t// broadcast instead of the catalog's own broadcast.\n\t\tconst active = broadcast.relativeBroadcast(effect, config.broadcast);\n\t\tif (!active) return;\n\n\t\t// The ring outlives this effect (it's keyed on the sample rate and channel count), so a\n\t\t// replacement subscription (a rendition swap, a republished broadcast, a reconnect) inherits\n\t\t// whatever its predecessor decoded. Samples are timestamp indexed, so the replacement\n\t\t// overwrites the slots it lands on, but a publisher writing ahead of real-time leaves seconds\n\t\t// of tail beyond them. Drop that once the replacement's first frame says where it starts.\n\t\tthis.#handover.opened();\n\n\t\t// The Sync ceiling is the maximum age of a non-latest group before both the network and\n\t\t// container consumers skip it. Omitting startGroup keeps a new subscription at the live edge.\n\t\tconst sub = subscribe(effect, { broadcast: active, track, maxLatency: this.sync.out.maxBuffer });\n\n\t\tif (config.container.kind === \"cmaf\") {\n\t\t\tthis.#runCmafDecoder(effect, sub, config);\n\t\t} else {\n\t\t\tthis.#runLegacyDecoder(effect, sub, config);\n\t\t}\n\t}\n\n\t#runLegacyDecoder(effect: Effect, sub: Moq.Track.Subscriber, config: Catalog.AudioConfig): void {\n\t\tconst preSkip =\n\t\t\tconfig.codec === \"opus\" && config.description ? Util.Opus.preSkip(Util.Hex.toBytes(config.description)) : 0;\n\t\tthis.#terminal.clear(preSkip);\n\t\tconst format = config.container.kind === \"loc\" ? new Container.Loc.Format() : new Container.Legacy.Format();\n\t\t// Create consumer with slightly less latency than the render worklet to avoid underflowing.\n\t\t// TODO include JITTER_UNDERHEAD\n\t\tconst consumer = new Container.Consumer(sub, {\n\t\t\tformat,\n\t\t\tlatency: this.sync.out.maxBuffer,\n\t\t});\n\t\teffect.cleanup(() => consumer.close());\n\n\t\t// Combine network jitter buffer with decode buffer\n\t\teffect.run((inner) => {\n\t\t\tconst network = inner.get(consumer.buffered);\n\t\t\tconst decode = inner.get(this.#decodeBuffered);\n\t\t\tthis.#out.buffered.update(() => Container.mergeBufferedRanges(network, decode));\n\t\t});\n\n\t\teffect.spawn(async () => {\n\t\t\tconst loaded = await Util.Libav.polyfill();\n\t\t\tif (!loaded) return; // cancelled\n\n\t\t\tconst warmup = new Warmup(LEGACY_WARMUP_CALLBACKS);\n\n\t\t\tconst decoder = new AudioDecoder({\n\t\t\t\toutput: (data) => {\n\t\t\t\t\tconst decoded = this.#terminal.span(data);\n\t\t\t\t\tif (warmup.drop()) {\n\t\t\t\t\t\t// Drop initial callbacks to prime the decoder.\n\t\t\t\t\t\tdata.close();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tthis.#emit(data, decoded);\n\t\t\t\t},\n\t\t\t\terror: (error) => console.error(\"audio decoder error\", error),\n\t\t\t});\n\t\t\teffect.cleanup(() => {\n\t\t\t\tif (decoder.state !== \"closed\") decoder.close();\n\t\t\t});\n\n\t\t\t// Opus in CMAF uses raw packets; dOps is not a valid OGG Identification Header.\n\t\t\tconst description =\n\t\t\t\tconfig.codec === \"opus\"\n\t\t\t\t\t? undefined\n\t\t\t\t\t: config.description\n\t\t\t\t\t\t? Util.Hex.toBytes(config.description)\n\t\t\t\t\t\t: undefined;\n\t\t\tconst decoderConfig: AudioDecoderConfig = {\n\t\t\t\t...config,\n\t\t\t\tdescription,\n\t\t\t};\n\t\t\tdecoder.configure(decoderConfig);\n\n\t\t\tfor (;;) {\n\t\t\t\tconst next = await consumer.next();\n\t\t\t\tif (!next) break;\n\t\t\t\tif (this.#onNext(next)) {\n\t\t\t\t\tdecoder.reset();\n\t\t\t\t\tdecoder.configure(decoderConfig);\n\t\t\t\t}\n\t\t\t\tif (next.end !== undefined) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst { frame } = next;\n\t\t\t\tif (!frame) continue;\n\n\t\t\t\t// Mark that we received this frame right now.\n\t\t\t\tconst timestamp = Time.Milli.fromMicro(frame.timestamp as Time.Micro);\n\t\t\t\tthis.sync.received(timestamp, \"audio\");\n\n\t\t\t\tthis.#out.stats.update((stats) => ({\n\t\t\t\t\tbytesReceived: (stats?.bytesReceived ?? 0) + frame.payload.byteLength,\n\t\t\t\t}));\n\n\t\t\t\t// Backpressure: in buffered mode this holds the encoded frame until the playhead nears\n\t\t\t\t// it, keeping the lookahead above the floor as Opus instead of decoded PCM. No-op live.\n\t\t\t\tawait this.#ring?.wait(frame.timestamp as Time.Micro);\n\n\t\t\t\tconst chunk = new EncodedAudioChunk({\n\t\t\t\t\ttype: frame.keyframe ? \"key\" : \"delta\",\n\t\t\t\t\tdata: frame.payload,\n\t\t\t\t\ttimestamp: frame.timestamp,\n\t\t\t\t});\n\n\t\t\t\t// A fatal decode error closes the decoder, so decoding again throws InvalidStateError out\n\t\t\t\t// of this loop. Stop instead: the error callback already reported the real failure.\n\t\t\t\tif (decoder.state === \"closed\") break;\n\t\t\t\tdecoder.decode(chunk);\n\t\t\t}\n\t\t});\n\t}\n\n\t#runCmafDecoder(effect: Effect, sub: Moq.Track.Subscriber, config: Catalog.AudioConfig): void {\n\t\tif (config.container.kind !== \"cmaf\") return; // just to help typescript\n\n\t\tconst initSegment = base64ToBytes(config.container.init);\n\t\tconst init = Container.Cmaf.decodeInitSegment(initSegment);\n\t\tconst opusDescription = config.description ? Util.Hex.toBytes(config.description) : init.description;\n\t\tconst preSkip = config.codec === \"opus\" && opusDescription ? Util.Opus.preSkip(opusDescription) : 0;\n\t\tthis.#terminal.clear(preSkip);\n\t\t// Opus in CMAF uses raw packets (not OGG-wrapped), so description must be omitted.\n\t\t// The dOps box from the init segment is not a valid OGG Identification Header.\n\t\tconst description =\n\t\t\tconfig.codec === \"opus\"\n\t\t\t\t? undefined\n\t\t\t\t: config.description\n\t\t\t\t\t? Util.Hex.toBytes(config.description)\n\t\t\t\t\t: init.description;\n\n\t\tconst consumer = new Container.Consumer(sub, {\n\t\t\tformat: new Container.Cmaf.Format(init),\n\t\t\tlatency: this.sync.out.maxBuffer,\n\t\t});\n\t\teffect.cleanup(() => consumer.close());\n\n\t\t// Combine network jitter buffer with decode buffer\n\t\teffect.run((inner) => {\n\t\t\tconst network = inner.get(consumer.buffered);\n\t\t\tconst decode = inner.get(this.#decodeBuffered);\n\t\t\tthis.#out.buffered.update(() => Container.mergeBufferedRanges(network, decode));\n\t\t});\n\n\t\teffect.spawn(async () => {\n\t\t\tconst loaded = await Util.Libav.polyfill();\n\t\t\tif (!loaded) return; // cancelled\n\n\t\t\tconst decoder = new AudioDecoder({\n\t\t\t\toutput: (data) => this.#emit(data),\n\t\t\t\terror: (error) => console.error(\"audio decoder error\", error),\n\t\t\t});\n\t\t\teffect.cleanup(() => {\n\t\t\t\tif (decoder.state !== \"closed\") decoder.close();\n\t\t\t});\n\n\t\t\t// Configure decoder with description from catalog\n\t\t\tconst decoderConfig: AudioDecoderConfig = {\n\t\t\t\tcodec: config.codec,\n\t\t\t\tsampleRate: config.sampleRate,\n\t\t\t\tnumberOfChannels: config.numberOfChannels,\n\t\t\t\tdescription,\n\t\t\t};\n\t\t\tdecoder.configure(decoderConfig);\n\n\t\t\tfor (;;) {\n\t\t\t\tconst next = await consumer.next();\n\t\t\t\tif (!next) break;\n\n\t\t\t\t// Reset and re-anchor before decoding the first frame of a new codec epoch.\n\t\t\t\tif (this.#onNext(next)) {\n\t\t\t\t\tdecoder.reset();\n\t\t\t\t\tdecoder.configure(decoderConfig);\n\t\t\t\t}\n\n\t\t\t\tconst { frame } = next;\n\t\t\t\tif (!frame) continue;\n\n\t\t\t\tconst timestamp = Time.Milli.fromMicro(frame.timestamp);\n\t\t\t\tthis.sync.received(timestamp, \"audio\");\n\n\t\t\t\tthis.#out.stats.update((stats) => ({\n\t\t\t\t\tbytesReceived: (stats?.bytesReceived ?? 0) + frame.payload.byteLength,\n\t\t\t\t}));\n\n\t\t\t\t// Backpressure: in buffered mode this holds the encoded frame until the playhead nears\n\t\t\t\t// it, keeping the lookahead above the floor as Opus instead of decoded PCM. No-op live.\n\t\t\t\tawait this.#ring?.wait(frame.timestamp);\n\n\t\t\t\tif (decoder.state === \"closed\") break;\n\t\t\t\tdecoder.decode(\n\t\t\t\t\tnew EncodedAudioChunk({\n\t\t\t\t\t\ttype: frame.keyframe ? \"key\" : \"delta\",\n\t\t\t\t\t\tdata: frame.payload,\n\t\t\t\t\t\ttimestamp: frame.timestamp,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t}\n\n\t#emit(sample: AudioData, decoded: DecodedSpan = this.#terminal.span(sample)) {\n\t\tconst { timestamp, frameOffset, frames } = decoded;\n\t\tconst timestampMilli = Time.Milli.fromMicro(timestamp);\n\t\tif (frames === 0) {\n\t\t\tsample.close();\n\t\t\treturn;\n\t\t}\n\n\t\tconst ring = this.#ring;\n\t\tif (!ring) {\n\t\t\t// We're probably in the process of closing.\n\t\t\tsample.close();\n\t\t\treturn;\n\t\t}\n\n\t\t// sample.sampleRate is the source of truth, and it can differ from the rate we pre-built the\n\t\t// graph against (Opus decodes to 48kHz on Chrome/Firefox but to the configured rate on Safari).\n\t\t// If they disagree, rebuild the graph at the real rate and drop this frame; the ring being torn\n\t\t// down can't accept it, and the next frame lands in the correctly-rated ring.\n\t\tif (sample.sampleRate !== ring.rate) {\n\t\t\tthis.#decodedSampleRate.set(sample.sampleRate);\n\t\t\tsample.close();\n\t\t\treturn;\n\t\t}\n\n\t\t// Calculate end time from sample duration\n\t\tconst durationMicro = ((frames / sample.sampleRate) * 1_000_000) as Time.Micro;\n\t\tconst durationMilli = Time.Milli.fromMicro(durationMicro);\n\t\tconst end = Time.Milli.add(timestampMilli, durationMilli);\n\n\t\t// A new subscription has taken over the timeline: drop the previous one's write-ahead tail\n\t\t// rather than letting it play out after this frame. See #runDecoder.\n\t\tif (this.#handover.takeover()) {\n\t\t\tring.truncate(timestamp);\n\t\t\tthis.#truncateDecodeBuffered(timestampMilli);\n\t\t}\n\n\t\t// Add to decode buffer\n\t\tthis.#addDecodeBuffered(timestampMilli, end);\n\n\t\t// Firefox's Opus decoder sometimes outputs more channels than requested\n\t\t// (e.g. 6 for stereo). Clamp to the ring's channel count.\n\t\tconst channels = Math.min(sample.numberOfChannels, ring.channels);\n\t\tconst channelData: Float32Array[] = [];\n\t\tfor (let channel = 0; channel < channels; channel++) {\n\t\t\tconst data = new Float32Array(frames);\n\t\t\tsample.copyTo(data, { format: \"f32-planar\", planeIndex: channel, frameOffset, frameCount: frames });\n\t\t\tchannelData.push(data);\n\t\t}\n\n\t\t// Hand off to the ring. Shared transport writes directly; post transport\n\t\t// transfers the ArrayBuffers.\n\t\tring.insert(timestamp, channelData);\n\n\t\tsample.close();\n\t}\n\n\t#addDecodeBuffered(start: Time.Milli, end: Time.Milli): void {\n\t\tif (start > end) return;\n\n\t\tthis.#decodeBuffered.mutate((current) => {\n\t\t\tfor (const range of current) {\n\t\t\t\t// Extend range if new sample overlaps or is adjacent (1ms tolerance for float precision)\n\t\t\t\tif (start <= range.end + 1 && end >= range.start) {\n\t\t\t\t\trange.start = Time.Milli.min(range.start, start);\n\t\t\t\t\trange.end = Time.Milli.max(range.end, end);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcurrent.push({ start, end });\n\t\t\tcurrent.sort((a, b) => a.start - b.start);\n\t\t});\n\t}\n\n\t// Drop reported decode ranges at or after `timestamp`, mirroring a ring truncation.\n\t#truncateDecodeBuffered(timestamp: Time.Milli): void {\n\t\tthis.#decodeBuffered.mutate((current) => {\n\t\t\twhile (current.length > 0 && current[current.length - 1].start >= timestamp) current.pop();\n\t\t\tconst last = current[current.length - 1];\n\t\t\tif (last && last.end > timestamp) last.end = timestamp;\n\t\t});\n\t}\n\n\t#trimDecodeBuffered(timestamp: Time.Milli): void {\n\t\tthis.#decodeBuffered.mutate((current) => {\n\t\t\twhile (current.length > 0) {\n\t\t\t\tif (current[0].end >= timestamp) {\n\t\t\t\t\tcurrent[0].start = Time.Milli.max(current[0].start, timestamp);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcurrent.shift();\n\t\t\t}\n\t\t});\n\t}\n\n\t// Flush the audio buffer and re-stall, re-anchoring playback to the next frame.\n\t// Use in buffered mode at an utterance boundary (see Sync.reset).\n\treset(): void {\n\t\tthis.#ring?.reset();\n\t}\n\n\t// Apply ordered container metadata before handling the result. An endpoint that also\n\t// starts a new epoch must survive the reset so its following drain is trimmed.\n\t#onNext(next: { discontinuity: number; end?: Time.Micro; frame?: { timestamp: Time.Micro } }): boolean {\n\t\tif (!this.#terminal.update(next)) return false;\n\t\tthis.#ring?.reset();\n\t\tthis.sync.reset();\n\t\treturn true;\n\t}\n\n\tclose() {\n\t\tthis.#signals.close();\n\t}\n\n\t// Whether the WebCodecs audio decoder can play this config.\n\tstatic supported = supported;\n}\n\nasync function supported(config: Catalog.AudioConfig): Promise<boolean> {\n\tif (!Catalog.containerSupported(config.container)) {\n\t\t// `kind` is the literal \"unknown\" tag; the container the publisher actually named is in `raw`.\n\t\tconst kind = config.container.kind === \"unknown\" ? config.container.raw.kind : config.container.kind;\n\t\tconsole.warn(`audio: ignoring rendition with unknown container: ${kind}`);\n\t\treturn false;\n\t}\n\n\t// Opus only runs at its native rates, so a catalog advertising anything else is wrong and Safari\n\t// refuses to decode it. Warn rather than reject: Chrome and Firefox ignore the configured rate and\n\t// play these streams fine, so rejecting would silence them for a publisher they handle today.\n\tif (config.codec === \"opus\" && !Util.Opus.supportsRate(config.sampleRate)) {\n\t\tconsole.warn(`audio: opus advertised at ${config.sampleRate}Hz, which some browsers cannot decode`);\n\t}\n\n\t// Opus in CMAF uses raw packets; dOps is not a valid OGG Identification Header.\n\tlet description: Uint8Array | undefined;\n\tif (config.codec !== \"opus\") {\n\t\tif (config.description) {\n\t\t\tdescription = Util.Hex.toBytes(config.description);\n\t\t} else if (config.container.kind === \"cmaf\") {\n\t\t\ttry {\n\t\t\t\tdescription = Container.Cmaf.decodeInitSegment(base64ToBytes(config.container.init)).description;\n\t\t\t} catch (err) {\n\t\t\t\t// A malformed init segment means we can't extract the codec\n\t\t\t\t// description, so we can't probe support reliably. Reject the\n\t\t\t\t// track rather than letting isConfigSupported pass on a\n\t\t\t\t// description-less config and then having decode() fail later.\n\t\t\t\tconsole.warn(`audio: malformed CMAF init segment for codec ${config.codec}`, err);\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\tconst res = await AudioDecoder.isConfigSupported({\n\t\t...config,\n\t\tdescription,\n\t});\n\treturn res.supported ?? false;\n}\n","import { Effect, type Getter, getter, type Inputs, type Readonlys, readonlys, Signal } from \"@moq/signals\";\nimport type { Decoder } from \"./decoder\";\n\nconst MIN_GAIN = 0.001;\nconst FADE_TIME = 0.2;\n\nexport type EmitterInput = {\n\tvolume: Getter<number>;\n\n\t// Silences the audio and stops the download. Muted samples aren't worth the bandwidth,\n\t// and the decoder keeps the AudioContext warm so unmuting is still instant.\n\tmuted: Getter<boolean>;\n\n\t// Pauses playback, which also stops the download.\n\tpaused: Getter<boolean>;\n};\n\ntype EmitterOutput = {\n\t// Whether audio should be downloaded. Wired into the decoder's `enabled` input by the owner.\n\tenabled: Signal<boolean>;\n};\n\n// A helper that emits audio directly to the speakers.\nexport class Emitter {\n\treadonly source: Decoder;\n\n\treadonly in: Readonlys<EmitterInput>;\n\n\treadonly #out: EmitterOutput = {\n\t\tenabled: new Signal<boolean>(false),\n\t};\n\treadonly out = readonlys(this.#out);\n\n\t#signals = new Effect();\n\n\t// The gain node used to adjust the volume.\n\t#gain = new Signal<GainNode | undefined>(undefined);\n\n\tconstructor(source: Decoder, props?: Inputs<EmitterInput>) {\n\t\tthis.source = source;\n\t\tthis.in = {\n\t\t\tvolume: getter(props?.volume ?? 0.5),\n\t\t\tmuted: getter(props?.muted ?? false),\n\t\t\tpaused: getter(props?.paused ?? false),\n\t\t};\n\n\t\t// Only download while playing. `paused` gates whether we download/decode\n\t\t// audio at all; `muted` only silences the gain (see the field comments).\n\t\t// Folding `muted` in here would disable the source, freezing the audio\n\t\t// clock — and, once audio is the sync reference, stalling the video on a\n\t\t// spinner.\n\t\tthis.#signals.run((effect) => {\n\t\t\tconst enabled = !effect.get(this.in.paused);\n\t\t\tthis.#out.enabled.set(enabled);\n\t\t});\n\n\t\tthis.#signals.run((effect) => {\n\t\t\tconst root = effect.get(this.source.out.root);\n\t\t\tif (!root) return;\n\n\t\t\tconst gain = new GainNode(root.context, { gain: effect.get(this.in.volume) });\n\t\t\troot.connect(gain);\n\n\t\t\teffect.set(this.#gain, gain);\n\n\t\t\teffect.run((inner) => {\n\t\t\t\t// We only connect/disconnect when enabled to save power.\n\t\t\t\t// Otherwise the worklet keeps running in the background returning 0s.\n\t\t\t\tconst enabled = inner.get(this.#out.enabled);\n\t\t\t\tif (!enabled) return;\n\n\t\t\t\tgain.connect(root.context.destination); // speakers\n\t\t\t\tinner.cleanup(() => gain.disconnect());\n\t\t\t});\n\t\t});\n\n\t\t// Resume the AudioContext when unmuted. Browsers block autoplay until a\n\t\t// user gesture, and unmuting is that gesture. We keep the source enabled\n\t\t// through mute (so the sync clock never stalls the video), which means the\n\t\t// decoder's enable-driven resume no longer re-fires on unmute — so drive\n\t\t// the resume from the mute state here instead.\n\t\tthis.#signals.run((effect) => {\n\t\t\tconst root = effect.get(this.source.out.root);\n\t\t\tif (!root) return;\n\t\t\tif (effect.get(this.in.muted)) return;\n\t\t\t// root.context is typed BaseAudioContext; the decoder always creates a\n\t\t\t// full AudioContext, which is the one with resume().\n\t\t\tconst context = root.context as AudioContext;\n\t\t\tif (context.state === \"suspended\") void context.resume();\n\t\t});\n\n\t\tthis.#signals.run((effect) => {\n\t\t\tconst gain = effect.get(this.#gain);\n\t\t\tif (!gain) return;\n\n\t\t\t// Cancel any scheduled transitions on change.\n\t\t\teffect.cleanup(() => gain.gain.cancelScheduledValues(gain.context.currentTime));\n\n\t\t\tconst volume = effect.get(this.in.volume);\n\t\t\tif (volume < MIN_GAIN) {\n\t\t\t\tgain.gain.exponentialRampToValueAtTime(MIN_GAIN, gain.context.currentTime + FADE_TIME);\n\t\t\t\tgain.gain.setValueAtTime(0, gain.context.currentTime + FADE_TIME + 0.01);\n\t\t\t} else {\n\t\t\t\tgain.gain.exponentialRampToValueAtTime(volume, gain.context.currentTime + FADE_TIME);\n\t\t\t}\n\t\t});\n\t}\n\n\tclose() {\n\t\tthis.#signals.close();\n\t}\n}\n","import type * as Catalog from \"@moq/hang/catalog\";\nimport type * as Moq from \"@moq/net\";\nimport { Time } from \"@moq/net\";\nimport { Effect, type Getter, getter, type Inputs, type Readonlys, readonlys, Signal } from \"@moq/signals\";\nimport type { Broadcast } from \"../broadcast\";\n\n// AudioWorklet always renders in 128-sample quanta.\nconst WORKLET_QUANTUM = 128;\n\nexport type Target = {\n\t// Optional manual override for the selected rendition name.\n\tname?: string;\n};\n\n/**\n * A function that checks if an audio configuration can be played.\n *\n * `Decoder.supported` is the WebCodecs probe used by `<moq-watch>`.\n */\nexport type Supported = (config: Catalog.AudioConfig) => Promise<boolean>;\n\nexport type SourceInput = {\n\tbroadcast: Getter<Broadcast | undefined>;\n\n\t// The desired rendition/bitrate of the audio.\n\ttarget: Getter<Target | undefined>;\n\n\t// A function that checks if an audio configuration can be played. Renditions that fail the\n\t// probe are filtered out. Nothing is selected until one is provided.\n\tsupported: Getter<Supported | undefined>;\n};\n\ntype SourceOutput = {\n\tcatalog: Signal<Catalog.Audio | undefined>;\n\tavailable: Signal<Record<string, Catalog.AudioConfig>>;\n\ttrack: Signal<string | undefined>;\n\tconfig: Signal<Catalog.AudioConfig | undefined>;\n\n\t// The per-rendition jitter (ms) to add to the sync buffer. Wired into Sync by the parent.\n\tjitter: Signal<Moq.Time.Milli | undefined>;\n};\n\n/**\n * Source handles catalog extraction, support checking, and rendition selection\n * for audio playback. The Decoder consumes whichever rendition it picks.\n */\nexport class Source {\n\treadonly in: Readonlys<SourceInput>;\n\n\treadonly #out: SourceOutput = {\n\t\tcatalog: new Signal<Catalog.Audio | undefined>(undefined),\n\t\tavailable: new Signal<Record<string, Catalog.AudioConfig>>({}),\n\t\ttrack: new Signal<string | undefined>(undefined),\n\t\tconfig: new Signal<Catalog.AudioConfig | undefined>(undefined),\n\t\tjitter: new Signal<Moq.Time.Milli | undefined>(undefined),\n\t};\n\treadonly out = readonlys(this.#out);\n\n\t#signals = new Effect();\n\n\tconstructor(props?: Inputs<SourceInput>) {\n\t\tthis.in = {\n\t\t\tbroadcast: getter(props?.broadcast),\n\t\t\ttarget: getter(props?.target),\n\t\t\tsupported: getter(props?.supported),\n\t\t};\n\n\t\tthis.#signals.run(this.#runCatalog.bind(this));\n\t\tthis.#signals.run(this.#runSupported.bind(this));\n\t\tthis.#signals.run(this.#runSelected.bind(this));\n\t}\n\n\t#runCatalog(effect: Effect): void {\n\t\tconst broadcast = effect.get(this.in.broadcast);\n\t\tif (!broadcast) return;\n\n\t\tconst catalog = effect.get(broadcast.out.catalog)?.audio;\n\t\tif (!catalog) return;\n\n\t\teffect.set(this.#out.catalog, catalog);\n\t}\n\n\t#runSupported(effect: Effect): void {\n\t\tconst renditions = effect.get(this.#out.catalog)?.renditions ?? {};\n\t\tconst supported = effect.get(this.in.supported);\n\t\tif (!supported) return;\n\n\t\teffect.spawn(async () => {\n\t\t\tconst available: Record<string, Catalog.AudioConfig> = {};\n\n\t\t\t// `supported` comes from the consumer, so we cannot assume it ever settles. A rerun\n\t\t\t// waits for the tasks it spawned, so an unraced probe would hold the next run shut\n\t\t\t// for good. Captured here so it stays this run's promise once we start awaiting.\n\t\t\tconst cancelled = effect.cancel.then(() => undefined);\n\n\t\t\tfor (const [name, config] of Object.entries(renditions)) {\n\t\t\t\tconst isSupported = await Promise.race([supported(config), cancelled]);\n\n\t\t\t\t// Torn down: stop probing and publish nothing, since the rerun redoes this.\n\t\t\t\tif (effect.abort.aborted) return;\n\n\t\t\t\tif (isSupported) available[name] = config;\n\t\t\t}\n\n\t\t\tif (Object.keys(available).length === 0 && Object.keys(renditions).length > 0) {\n\t\t\t\tconsole.warn(\"no supported audio renditions found:\", renditions);\n\t\t\t}\n\n\t\t\tthis.#out.available.set(available);\n\t\t});\n\t}\n\n\t#runSelected(effect: Effect): void {\n\t\tconst available = effect.get(this.#out.available);\n\t\tif (Object.keys(available).length === 0) return;\n\n\t\tconst target = effect.get(this.in.target);\n\n\t\tlet selected: { track: string; config: Catalog.AudioConfig } | undefined;\n\n\t\t// Manual selection by name\n\t\tif (target?.name && target.name in available) {\n\t\t\tselected = { track: target.name, config: available[target.name] };\n\t\t} else {\n\t\t\t// Automatic selection\n\t\t\tselected = this.#select(available);\n\t\t\tif (!selected) return;\n\t\t}\n\n\t\teffect.set(this.#out.track, selected.track);\n\t\teffect.set(this.#out.config, selected.config);\n\n\t\t// Use catalog jitter if available, otherwise estimate from codec frame duration.\n\t\t// Add the worklet render quantum so the ring buffer has margin between frame arrivals.\n\t\tconst codecJitter = selected.config.jitter ?? defaultAudioJitter(selected.config) ?? 0;\n\t\tconst overhead = Math.ceil((WORKLET_QUANTUM / selected.config.sampleRate) * 1000);\n\t\tconst jitter = codecJitter + overhead;\n\t\teffect.set(this.#out.jitter, Time.Milli(jitter));\n\t}\n\n\t/**\n\t * Select rendition based on the configured strategy.\n\t */\n\t#select(\n\t\trenditions: Record<string, Catalog.AudioConfig>,\n\t): { track: string; config: Catalog.AudioConfig } | undefined {\n\t\tconst entries = Object.entries(renditions);\n\t\tif (entries.length === 0) return undefined;\n\n\t\tfor (const [track, config] of entries) {\n\t\t\tif (config.container.kind === \"legacy\") {\n\t\t\t\treturn { track, config };\n\t\t\t}\n\t\t}\n\n\t\tfor (const [track, config] of entries) {\n\t\t\tif (config.container.kind === \"loc\") {\n\t\t\t\treturn { track, config };\n\t\t\t}\n\t\t}\n\n\t\tfor (const [track, config] of entries) {\n\t\t\tif (config.container.kind === \"cmaf\") {\n\t\t\t\treturn { track, config };\n\t\t\t}\n\t\t}\n\n\t\treturn undefined;\n\t}\n\n\tclose(): void {\n\t\tthis.#signals.close();\n\t}\n}\n\n// Estimate the minimum jitter (frame duration) based on the audio codec.\n// TODO these are defaults; the actual frame duration depends on encoder config.\nfunction defaultAudioJitter(config: Catalog.AudioConfig): number | undefined {\n\tif (config.codec.startsWith(\"opus\")) {\n\t\t// Opus supports 2.5–60ms but 20ms is the real-time default.\n\t\treturn 20;\n\t}\n\n\tif (config.codec.startsWith(\"mp4a\")) {\n\t\t// 1024 samples for LC-AAC; HE-AAC/AAC-LD use different sizes.\n\t\treturn Math.ceil((1024 / config.sampleRate) * 1000);\n\t}\n\n\tif (config.codec === \"mp3\") {\n\t\t// 1152 samples per frame for MPEG-1 Layer III; MPEG-2/2.5 use 576.\n\t\tconst samples = config.sampleRate >= 32000 ? 1152 : 576;\n\t\treturn Math.ceil((samples / config.sampleRate) * 1000);\n\t}\n\n\treturn undefined;\n}\n","import type * as Catalog from \"@moq/hang/catalog\";\nimport { u53 } from \"@moq/hang/catalog\";\nimport type * as Msf from \"@moq/msf\";\nimport { base64ToBytes } from \"./base64\";\n\nconst DEFAULT_SAMPLE_RATE = 48000;\nconst DEFAULT_NUMBER_OF_CHANNELS = 2;\n\nfunction bytesToHex(bytes: Uint8Array): string {\n\tlet hex = \"\";\n\tfor (let i = 0; i < bytes.length; i++) hex += bytes[i].toString(16).padStart(2, \"0\");\n\treturn hex;\n}\n\ninterface ContainerInfo {\n\tcontainer: Catalog.Container;\n\tdescription?: string;\n}\n\n// `packaging` declares how the bytes are framed, not an optional capability, so every value maps to\n// its own container and an unrecognized one drops the rendition. Reading LOC's property block as\n// legacy's VarInt prefix is exactly what a permissive fallback caused.\nfunction toContainer(track: Msf.Track): ContainerInfo | undefined {\n\tlet initBytes: Uint8Array | undefined;\n\ttry {\n\t\tinitBytes = track.initData ? base64ToBytes(track.initData) : undefined;\n\t} catch {\n\t\tinitBytes = undefined;\n\t}\n\n\tconst description = initBytes ? bytesToHex(initBytes) : undefined;\n\n\tswitch (track.packaging) {\n\t\tcase \"cmaf\":\n\t\t\t// Unusable without its init segment, so a missing or malformed one drops the rendition rather\n\t\t\t// than falling back to a different framing. hang's CMAF path reads the codec metadata from\n\t\t\t// that segment, so there is no separate description to surface.\n\t\t\tif (!track.initData || !initBytes) return undefined;\n\t\t\treturn { container: { kind: \"cmaf\", init: track.initData }, description: undefined };\n\t\tcase \"loc\":\n\t\t\treturn { container: { kind: \"loc\" }, description };\n\t\tcase \"legacy\":\n\t\t\treturn { container: { kind: \"legacy\" }, description };\n\t\tdefault:\n\t\t\treturn undefined;\n\t}\n}\n\nfunction toVideoConfig(track: Msf.Track): Catalog.VideoConfig | undefined {\n\tif (!track.codec) return undefined;\n\n\tconst info = toContainer(track);\n\tif (!info) return undefined;\n\tconst { container, description } = info;\n\treturn {\n\t\tcodec: track.codec,\n\t\tcontainer,\n\t\tdescription,\n\t\tcodedWidth: track.width != null ? u53(track.width) : undefined,\n\t\tcodedHeight: track.height != null ? u53(track.height) : undefined,\n\t\tframerate: track.framerate,\n\t\tbitrate: track.bitrate != null ? u53(track.bitrate) : undefined,\n\t\tstalled: track.stalled,\n\t\tjitter: track.jitter != null ? u53(track.jitter) : undefined,\n\t};\n}\n\nfunction toAudioConfig(track: Msf.Track): Catalog.AudioConfig | undefined {\n\tif (!track.codec) return undefined;\n\n\tconst channels = (() => {\n\t\tif (!track.channelConfig) return DEFAULT_NUMBER_OF_CHANNELS;\n\t\tconst parsed = Number.parseInt(track.channelConfig, 10);\n\t\treturn Number.isFinite(parsed) ? parsed : DEFAULT_NUMBER_OF_CHANNELS;\n\t})();\n\n\tconst info = toContainer(track);\n\tif (!info) return undefined;\n\tconst { container, description } = info;\n\treturn {\n\t\tcodec: track.codec,\n\t\tcontainer,\n\t\tdescription,\n\t\tsampleRate: u53(track.samplerate ?? DEFAULT_SAMPLE_RATE),\n\t\tnumberOfChannels: u53(channels),\n\t\tbitrate: track.bitrate != null ? u53(track.bitrate) : undefined,\n\t\tjitter: track.jitter != null ? u53(track.jitter) : undefined,\n\t};\n}\n\n/** Convert an MSF catalog to a hang catalog Root. */\nexport function toHang(msf: Msf.Catalog): Catalog.Root {\n\tconst videoRenditions: Record<string, Catalog.VideoConfig> = {};\n\tconst audioRenditions: Record<string, Catalog.AudioConfig> = {};\n\n\tfor (const track of msf.tracks) {\n\t\tif (track.role === \"video\") {\n\t\t\tconst config = toVideoConfig(track);\n\t\t\tif (config) videoRenditions[track.name] = config;\n\t\t} else if (track.role === \"audio\") {\n\t\t\tconst config = toAudioConfig(track);\n\t\t\tif (config) audioRenditions[track.name] = config;\n\t\t}\n\t}\n\n\tconst root: Catalog.Root = {};\n\n\tif (Object.keys(videoRenditions).length > 0) {\n\t\troot.video = { renditions: videoRenditions };\n\t}\n\n\tif (Object.keys(audioRenditions).length > 0) {\n\t\troot.audio = { renditions: audioRenditions };\n\t}\n\n\treturn root;\n}\n","import * as Catalog from \"@moq/hang/catalog\";\nimport * as Json from \"@moq/json\";\nimport * as Msf from \"@moq/msf\";\nimport type * as Moq from \"@moq/net\";\nimport { Path } from \"@moq/net\";\nimport { Effect, type Getter, getter, type Inputs, type Readonlys, readonlys, Signal } from \"@moq/signals\";\n\nimport { toHang } from \"./msf\";\n\n// Connections already warned about missing broadcast-discovery support, so the\n// announcement check logs at most once per connection.\nconst warnedNoDiscovery = new WeakSet<Moq.Connection.Established>();\n\n// Whether to skip the announcement gate for a cross-broadcast reference: without discovery,\n// waiting on an announcement would hang forever, so subscribe immediately and warn once per\n// connection. The main broadcast doesn't need this; @moq/net's `announcedBroadcast` falls back\n// on its own.\nfunction skipDiscovery(conn: Moq.Connection.Established): boolean {\n\tif (conn.discovery) return false;\n\tif (!warnedNoDiscovery.has(conn)) {\n\t\twarnedNoDiscovery.add(conn);\n\t\tconsole.warn(\"relay does not support broadcast discovery; subscribing to siblings blind.\");\n\t}\n\treturn true;\n}\n\ntype ReferencedRendition = {\n\tbroadcast?: string;\n};\n\n// Either the catalog's own broadcast, or a sibling to consume by path.\ntype RelativeTarget = { local: true } | { local: false; path: Moq.Path.Valid };\n\nfunction filterRenditions<T extends ReferencedRendition>(\n\trenditions: Record<string, T>,\n\tusable: (rel: string | undefined) => boolean,\n): Record<string, T> {\n\treturn Object.fromEntries(Object.entries(renditions).filter(([, config]) => usable(config.broadcast)));\n}\n\nfunction filterCatalog(catalog: Catalog.Root, usable: (rel: string | undefined) => boolean): Catalog.Root {\n\treturn {\n\t\t...catalog,\n\t\tvideo: catalog.video\n\t\t\t? { ...catalog.video, renditions: filterRenditions(catalog.video.renditions, usable) }\n\t\t\t: undefined,\n\t\taudio: catalog.audio\n\t\t\t? { ...catalog.audio, renditions: filterRenditions(catalog.audio.renditions, usable) }\n\t\t\t: undefined,\n\t};\n}\n\n// Watch supports the on-the-wire catalog formats from @moq/hang, plus \"hangz\" (the\n// DEFLATE-compressed `catalog.json.z` track) and a \"manual\" mode where the user supplies the\n// catalog directly without fetching. \"hangz\" is opt-in only: it shares the `.hang` broadcast suffix\n// and is never auto-detected, so set it explicitly via `catalogFormat`.\nexport const CATALOG_FORMATS = [...Catalog.FORMATS, \"hangz\", \"manual\"] as const;\nexport type CatalogFormat = (typeof CATALOG_FORMATS)[number];\n\nexport function parseCatalogFormat(value: string | null): CatalogFormat | undefined {\n\tif (value === null) return undefined;\n\treturn CATALOG_FORMATS.find((f) => f === value);\n}\n\ntype Status = \"offline\" | \"loading\" | \"live\";\n\n// Signals the component reads. Whoever owns the backing Signal (the caller, or\n// another component whose output is wired in) does the writing.\nexport type BroadcastInput = {\n\tconnection: Getter<Moq.Connection.Established | undefined>;\n\n\t// Whether to start downloading the broadcast.\n\t// Defaults to false so you can make sure everything is ready before starting.\n\tenabled: Getter<boolean>;\n\n\t// The broadcast name.\n\tname: Getter<Moq.Path.Valid>;\n\n\t// Whether to reload the broadcast when it goes offline.\n\t// Defaults to true; pass false to subscribe immediately without waiting for an announcement.\n\treload: Getter<boolean>;\n\n\t// Which catalog format to use. When `undefined` (the default), the format is\n\t// auto-detected from the broadcast name extension (`.hang`, `.msf`), falling\n\t// back to `\"hang\"` if the name has no recognized extension. Set to a\n\t// specific value to override auto-detection. `\"hangz\"` (the compressed\n\t// `catalog.json.z` track) is opt-in only and never auto-detected.\n\tcatalogFormat: Getter<CatalogFormat | undefined>;\n\n\t// The manual-mode catalog source. Used directly when catalogFormat is \"manual\";\n\t// ignored otherwise. Read `output.catalog` for the effective catalog in any mode.\n\tcatalog: Getter<Catalog.Root | undefined>;\n};\n\ntype BroadcastOutput = {\n\tstatus: Signal<Status>;\n\tactive: Signal<Moq.Broadcast.Consumer | undefined>;\n\n\t// The effective catalog: the fetched one, or a copy of input.catalog in manual mode, minus\n\t// any rendition this consumer can't use (see `#runFiltered`). A rendition referencing another\n\t// broadcast appears once that broadcast is announced, so this can change without a new catalog.\n\tcatalog: Signal<Catalog.Root | undefined>;\n};\n\n// A catalog source that (optionally) reloads automatically when live/offline.\nexport class Broadcast {\n\treadonly in: Readonlys<BroadcastInput>;\n\n\treadonly #out: BroadcastOutput = {\n\t\tstatus: new Signal<Status>(\"offline\"),\n\t\tactive: new Signal<Moq.Broadcast.Consumer | undefined>(undefined),\n\t\tcatalog: new Signal<Catalog.Root | undefined>(undefined),\n\t};\n\treadonly out = readonlys(this.#out);\n\n\t// The set of announced paths on the connection, for cross-broadcast (`broadcast: ../`) references\n\t// so `relativeBroadcast` can gate on whether a sibling is announced. `undefined` until the stream\n\t// is open. Opened lazily; the main broadcast doesn't use it (`#runBroadcast` drives off its own\n\t// name-scoped stream).\n\treadonly #announced = new Signal<Set<Moq.Path.Valid> | undefined>(undefined);\n\n\t// Set true the first time a relative reference needs the announcement gate, so a broadcast with\n\t// no cross-broadcast renditions never opens the (broad) connection-scoped announcement stream.\n\treadonly #wantAnnounced = new Signal(false);\n\n\t// The catalog as published, before renditions this consumer cannot use are dropped. Kept\n\t// separate so the effective catalog can be re-derived when a reference becomes reachable.\n\t//\n\t// Writes notify unconditionally. A manual catalog can be mutated in place, and the rerun that\n\t// delivers it lands inside the same flush, where a value-compare coalesces the write away and\n\t// strands the filtered copy on the previous contents.\n\treadonly #raw = new Signal<Catalog.Root | undefined>(undefined);\n\n\t#signals = new Effect();\n\n\tconstructor(props?: Inputs<BroadcastInput>) {\n\t\tthis.in = {\n\t\t\tconnection: getter(props?.connection),\n\t\t\tname: getter(props?.name ?? Path.empty()),\n\t\t\tenabled: getter(props?.enabled ?? false),\n\t\t\treload: getter(props?.reload ?? true),\n\t\t\tcatalogFormat: getter<CatalogFormat | undefined>(props?.catalogFormat),\n\t\t\tcatalog: getter(props?.catalog),\n\t\t};\n\n\t\tthis.#signals.run(this.#runAnnounced.bind(this));\n\t\tthis.#signals.run(this.#runBroadcast.bind(this));\n\t\tthis.#signals.run(this.#runCatalog.bind(this));\n\t\tthis.#signals.run(this.#runFiltered.bind(this));\n\t}\n\n\t// Maintain the set of announced paths used by `relativeBroadcast`, by draining a connection-scoped\n\t// announcement stream. Only opened once a relative reference asks for it (see `#wantAnnounced`),\n\t// and reopened per connection.\n\t#runAnnounced(effect: Effect): void {\n\t\tthis.#announced.set(undefined);\n\n\t\tif (!effect.get(this.#wantAnnounced)) return;\n\t\tif (!effect.get(this.in.reload)) return;\n\n\t\tconst conn = effect.get(this.in.connection);\n\t\tif (!conn || skipDiscovery(conn)) return;\n\n\t\tconst announced = conn.announced(Path.empty());\n\t\teffect.cleanup(() => announced.close());\n\t\tthis.#announced.set(new Set());\n\n\t\teffect.spawn(async () => {\n\t\t\tfor (;;) {\n\t\t\t\tconst entry = await Promise.race([effect.cancel, announced.next()]);\n\t\t\t\tif (!entry) break;\n\t\t\t\tthis.#announced.mutate((active) => {\n\t\t\t\t\tif (!active) return;\n\t\t\t\t\tif (entry.active) active.add(entry.path);\n\t\t\t\t\telse active.delete(entry.path);\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\t}\n\n\t// Publish the catalog minus every rendition this consumer cannot use: one whose reference\n\t// escapes above the root, or names a broadcast that isn't announced to us. Selecting one of\n\t// those would render nothing, since `relativeBroadcast` resolves it to no broadcast at all.\n\t// Reruns as announcements arrive, so a rendition appears once its broadcast does.\n\t#runFiltered(effect: Effect): void {\n\t\tconst raw = effect.get(this.#raw);\n\t\tconst usable = (rel: string | undefined) => this.#relativeTarget(effect, rel) !== undefined;\n\t\teffect.set(this.#out.catalog, raw ? filterCatalog(raw, usable) : undefined);\n\t}\n\n\t// Whether `path` is currently announced, for `relativeBroadcast`'s cross-broadcast refs. Returns\n\t// true (subscribe immediately) when the gate can't apply: reload is off, or the relay doesn't\n\t// support discovery. Opens the announcement stream on first use.\n\t#isPathAnnounced(effect: Effect, path: Moq.Path.Valid): boolean {\n\t\tif (!effect.get(this.in.reload)) return true;\n\n\t\tconst conn = effect.get(this.in.connection);\n\t\t// Nothing to ask yet. `relativeBroadcast` still bails on the missing connection, and this\n\t\t// keeps a reconnect from briefly hiding every cross-broadcast rendition from selection.\n\t\tif (!conn) return true;\n\t\tif (skipDiscovery(conn)) return true;\n\n\t\tthis.#wantAnnounced.set(true);\n\n\t\tconst active = effect.get(this.#announced);\n\t\tif (!active) return false; // stream not open yet: wait rather than subscribe to a maybe-absent path\n\t\treturn active.has(path);\n\t}\n\n\t// Subscribe to the broadcast, waiting for its announcement so we never race a publisher that\n\t// comes online after us. @moq/net drives the re-consume on a same-name republish and the blind\n\t// fallback on a relay without discovery; mirror its handle into `active`.\n\t#runBroadcast(effect: Effect): void {\n\t\tconst enabled = effect.get(this.in.enabled);\n\t\tif (!enabled) return;\n\n\t\tconst conn = effect.get(this.in.connection);\n\t\tif (!conn) return;\n\n\t\tconst name = effect.get(this.in.name);\n\n\t\t// No announcement gate: subscribe immediately.\n\t\tif (!effect.get(this.in.reload)) {\n\t\t\tconst broadcast = conn.consume(name);\n\t\t\teffect.cleanup(() => broadcast.close());\n\t\t\teffect.set(this.#out.active, broadcast, undefined);\n\t\t\treturn;\n\t\t}\n\n\t\tconst announced = conn.announcedBroadcast(name);\n\t\teffect.cleanup(() => announced.close());\n\n\t\teffect.run((nested) => {\n\t\t\tnested.set(this.#out.active, nested.get(announced.active), undefined);\n\t\t});\n\t}\n\n\t#runCatalog(effect: Effect): void {\n\t\tconst enabled = effect.get(this.in.enabled);\n\t\tif (!enabled) return;\n\n\t\tconst catalogFormat = effect.get(this.in.catalogFormat);\n\t\tconst name = effect.get(this.in.name);\n\t\t// Explicit override beats name-derived auto-detection. When neither is\n\t\t// set we fall back to the default, keeping legacy names that have no\n\t\t// extension working.\n\t\tconst format: CatalogFormat = catalogFormat ?? Catalog.detectFormat(name) ?? Catalog.DEFAULT_FORMAT;\n\n\t\tif (format === \"manual\") {\n\t\t\t// Mirror the caller-supplied catalog into the effective output.\n\t\t\tconst catalog = effect.get(this.in.catalog);\n\t\t\tthis.#raw.set(catalog, true);\n\t\t\teffect.cleanup(() => this.#raw.set(undefined, true));\n\t\t\tthis.#out.status.set(catalog ? \"live\" : \"loading\");\n\t\t\treturn;\n\t\t}\n\n\t\tconst broadcast = effect.get(this.out.active);\n\t\tif (!broadcast) return;\n\n\t\tthis.#out.status.set(\"loading\");\n\n\t\tconst trackName = format === \"hang\" ? Catalog.TRACK : format === \"hangz\" ? Catalog.TRACK_COMPRESSED : \"catalog\";\n\t\tconst track = broadcast.track(trackName).subscribe({ priority: Catalog.PRIORITY.catalog });\n\t\teffect.cleanup(() => track.close());\n\n\t\t// The hang catalog is reconstructed from snapshots (and future deltas) via @moq/json, with\n\t\t// \"hangz\" decompressing the `.z` track; MSF stays on its own one-blob-per-group fetch.\n\t\tlet fetchNext: () => Promise<Catalog.Root | undefined>;\n\t\tif (format === \"hang\" || format === \"hangz\") {\n\t\t\tconst consumer = new Json.Snapshot.Consumer<Catalog.Root>(track, {\n\t\t\t\tschema: Catalog.RootSchema,\n\t\t\t\tcompression: format === \"hangz\",\n\t\t\t});\n\t\t\tfetchNext = () => consumer.next();\n\t\t} else {\n\t\t\tfetchNext = async () => {\n\t\t\t\tconst update = await Msf.fetch(track);\n\t\t\t\treturn update ? toHang(update) : undefined;\n\t\t\t};\n\t\t}\n\n\t\teffect.spawn(async () => {\n\t\t\ttry {\n\t\t\t\tfor (;;) {\n\t\t\t\t\tconst update = await Promise.race([effect.cancel, fetchNext()]);\n\t\t\t\t\tif (!update) break;\n\n\t\t\t\t\tconsole.debug(\"received catalog\", format, this.in.name.peek(), update);\n\n\t\t\t\t\tthis.#raw.set(update, true);\n\t\t\t\t\tthis.#out.status.set(\"live\");\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tconsole.warn(\"error fetching catalog\", this.in.name.peek(), err);\n\t\t\t} finally {\n\t\t\t\tthis.#raw.set(undefined);\n\t\t\t\tthis.#out.status.set(\"offline\");\n\t\t\t}\n\t\t});\n\t}\n\n\t/**\n\t * Resolve the `Moq.Broadcast.Consumer` that publishes a given track.\n\t *\n\t * If `rel` is set (a rendition's catalog `broadcast` field), treat it as a path\n\t * relative to this broadcast's name and consume the resolved broadcast on the same\n\t * connection. Otherwise return the catalog's own active broadcast.\n\t *\n\t * The consumer is scoped to the caller's `effect` (closed on its next run), so a\n\t * reference resolves lazily and reacts to `enabled` / connection / announcement\n\t * changes exactly like the catalog broadcast.\n\t */\n\t// Where a rendition's `broadcast` reference points once resolved and gated on the announcement\n\t// stream, or `undefined` when it names nothing consumable right now. Playback and rendition\n\t// selection both go through this so they cannot disagree about what is reachable.\n\t#relativeTarget(effect: Effect, rel: string | undefined): RelativeTarget | undefined {\n\t\tif (!rel) return { local: true };\n\n\t\tconst base = effect.get(this.in.name);\n\t\tconst resolved = Path.tryResolve(base, rel);\n\n\t\t// Ignore a rendition whose reference escapes above the root. A valid empty result\n\t\t// names the root broadcast, while a reference back to the catalog uses its active handle.\n\t\tif (resolved === undefined) return undefined;\n\t\tif (resolved === base) return { local: true };\n\n\t\tif (!this.#isPathAnnounced(effect, resolved)) return undefined;\n\n\t\treturn { local: false, path: resolved };\n\t}\n\n\t/** Resolve a rendition's `broadcast` reference to a consumer, or `undefined` if unreachable. */\n\trelativeBroadcast(effect: Effect, rel: string | undefined): Moq.Broadcast.Consumer | undefined {\n\t\tconst target = this.#relativeTarget(effect, rel);\n\t\tif (!target) return undefined;\n\t\tif (target.local) return effect.get(this.out.active);\n\n\t\tif (!effect.get(this.in.enabled)) return undefined;\n\n\t\tconst conn = effect.get(this.in.connection);\n\t\tif (!conn) return undefined;\n\n\t\tconst broadcast = conn.consume(target.path);\n\t\teffect.cleanup(() => broadcast.close());\n\t\treturn broadcast;\n\t}\n\n\tclose() {\n\t\tthis.#signals.close();\n\t}\n}\n","import type * as Catalog from \"@moq/hang/catalog\";\n\n/** Internal key used to describe which fields can change a support probe's result. */\nexport const supportCacheKey = Symbol(\"supportCacheKey\");\n\n/** The catalog fields that determine the demuxer and WebCodecs decoder instance. */\nexport type DecoderConfig = Pick<\n\tCatalog.VideoConfig,\n\t\"codec\" | \"container\" | \"description\" | \"displayAspectWidth\" | \"displayAspectHeight\"\n> & {\n\toptimizeForLatency: boolean;\n};\n\n/** The routing and decoder state whose changes require a new playback pipeline. */\nexport type PlaybackIdentity = {\n\tbroadcast: Catalog.VideoConfig[\"broadcast\"];\n\tdecoder: DecoderConfig;\n};\n\n/** Reduce a rendition config to the fields that require a new playback pipeline. */\nexport function playbackIdentity(config: Catalog.VideoConfig): PlaybackIdentity {\n\treturn {\n\t\tbroadcast: config.broadcast,\n\t\tdecoder: {\n\t\t\tcodec: config.codec,\n\t\t\tcontainer: config.container,\n\t\t\tdescription: config.description,\n\t\t\tdisplayAspectWidth: config.displayAspectWidth,\n\t\t\tdisplayAspectHeight: config.displayAspectHeight,\n\t\t\toptimizeForLatency: config.optimizeForLatency ?? true,\n\t\t},\n\t};\n}\n\n/** Return a stable cache key for the fields used by the WebCodecs support probe. */\nexport function decoderConfigKey(config: Catalog.VideoConfig): string {\n\treturn JSON.stringify(playbackIdentity(config).decoder);\n}\n","import type * as Catalog from \"@moq/hang/catalog\";\nimport { Time } from \"@moq/net\";\n\n// How far the incoming rendition may still trail the picture it replaces when we promote it,\n// absorbing scheduling noise so the switch doesn't hinge on landing inside a single frame\n// interval. This is also the largest step backwards a switch can make visible.\nconst SLACK = Time.Milli(100);\n\n/**\n * How far behind live a rendition's playhead can sit while still being at its own live edge.\n *\n * Frames arrive one group at a time, so this is the rendition's group cadence: the sync buffer\n * has to cover it or playback starves between groups. The catalog value wins. Otherwise assume\n * the publisher flushes each frame as it's encoded, so a frame interval is the longest we wait.\n * Undefined when the catalog declares neither.\n */\nexport function renditionJitter(config: Catalog.VideoConfig): Time.Milli | undefined {\n\tif (config.jitter !== undefined) return Time.Milli(config.jitter);\n\tif (config.framerate) return Time.Milli(Math.ceil(1000 / config.framerate));\n\treturn undefined;\n}\n\n/** The playheads involved in promoting a new rendition. */\nexport interface CaughtUp {\n\t/** The incoming rendition's playhead: the most recent timestamp it has rendered. */\n\tplayhead: Time.Milli;\n\n\t/** The outgoing rendition's playhead, or undefined when it has rendered nothing. */\n\tactive?: Time.Milli;\n}\n\n/**\n * Whether the incoming rendition has caught up enough to take over the picture.\n *\n * The bar is the outgoing playhead rather than the live edge, which is a moving target: the sync\n * buffer grows the moment a coarser rendition is selected, dropping live below the outgoing\n * playhead, and it runs ahead of both playheads whenever delivery is late, because the sync\n * reference only ever moves down. Barring on live would promote in the first case (replaying\n * everything between the two playheads) and stall the switch outright in the second. Waiting on\n * the outgoing playhead always terminates: live recovers at wall-clock rate, and the incoming\n * rendition fills its buffer meanwhile.\n */\nexport function caughtUp(props: CaughtUp): boolean {\n\t// Nothing is rendering from the outgoing rendition, so there's no picture to step back from.\n\tif (props.active === undefined) return true;\n\n\treturn Time.Milli.add(props.playhead, SLACK) >= props.active;\n}\n","import type * as Catalog from \"@moq/hang/catalog\";\n\ntype VideoRotation = 0 | 90 | 180 | 270;\n\ntype VideoDimensions = {\n\twidth: number;\n\theight: number;\n};\n\n/** Canvas transform and source dimensions for a video presentation. */\nexport type CanvasPresentationTransform = {\n\tmatrix: [number, number, number, number, number, number];\n\tsource: VideoDimensions;\n};\n\nfunction normalizeVideoRotation(rotation = 0): VideoRotation {\n\tconst normalized = ((rotation % 360) + 360) % 360;\n\treturn ((Math.round(normalized / 90) % 4) * 90) as VideoRotation;\n}\n\n/** Return dimensions after applying a clockwise quarter-turn. */\nexport function rotateVideoDimensions(dimensions: VideoDimensions, rotation = 0): VideoDimensions {\n\tconst normalized = normalizeVideoRotation(rotation);\n\treturn normalized === 90 || normalized === 270\n\t\t? { width: dimensions.height, height: dimensions.width }\n\t\t: { width: dimensions.width, height: dimensions.height };\n}\n\nfunction clean(value: number): number {\n\treturn Object.is(value, -0) ? 0 : value;\n}\n\nfunction flipMatrix(\n\tmatrix: [number, number, number, number, number, number],\n\twidth: number,\n): [number, number, number, number, number, number] {\n\tconst [a, b, c, d, e, f] = matrix;\n\treturn [clean(-a), clean(b), clean(-c), clean(d), clean(width - e), clean(f)];\n}\n\n/** Return the canvas transform needed to render a catalog video presentation. */\nexport function canvasPresentationTransform(\n\toutput: VideoDimensions,\n\tvideo?: Catalog.Video,\n): CanvasPresentationTransform {\n\tconst rotation = normalizeVideoRotation(video?.rotation);\n\tconst source = rotateVideoDimensions(output, rotation);\n\n\tlet matrix: [number, number, number, number, number, number];\n\tswitch (rotation) {\n\t\tcase 90:\n\t\t\tmatrix = [0, 1, -1, 0, output.width, 0];\n\t\t\tbreak;\n\t\tcase 180:\n\t\t\tmatrix = [-1, 0, 0, -1, output.width, output.height];\n\t\t\tbreak;\n\t\tcase 270:\n\t\t\tmatrix = [0, -1, 1, 0, 0, output.height];\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tmatrix = [1, 0, 0, 1, 0, 0];\n\t\t\tbreak;\n\t}\n\n\tif (video?.flip) matrix = flipMatrix(matrix, output.width);\n\treturn { matrix, source };\n}\n","import * as Catalog from \"@moq/hang/catalog\";\nimport * as Container from \"@moq/hang/container\";\nimport * as Util from \"@moq/hang/util\";\nimport type * as Moq from \"@moq/net\";\nimport { Time } from \"@moq/net\";\nimport {\n\ttype Computed,\n\ttype Dispose,\n\tEffect,\n\ttype Getter,\n\tgetter,\n\ttype Inputs,\n\ttype Readonlys,\n\treadonlys,\n\tSignal,\n} from \"@moq/signals\";\nimport { base64ToBytes } from \"../base64\";\n\nimport type { Sync } from \"../sync\";\nimport {\n\ttype DecoderConfig,\n\tdecoderConfigKey,\n\ttype PlaybackIdentity,\n\tplaybackIdentity,\n\tsupportCacheKey,\n} from \"./config\";\nimport { caughtUp } from \"./playhead\";\nimport { rotateVideoDimensions } from \"./presentation\";\nimport type { Source } from \"./source\";\n\n// The amount of time to wait before considering the video to be buffering.\nconst BUFFERING = Time.Milli(500);\n\nexport type DecoderInput = {\n\t/** Whether to download the video track. Wired from the renderer's output by the parent. */\n\tenabled: Getter<boolean>;\n\n\t/**\n\t * Whether to pace playback off the clock, holding each decoded frame until its presentation\n\t * time. Defaults to true.\n\t *\n\t * False paints on decode instead, trading smooth motion for the lowest latency the display can\n\t * offer. The clock stays wired either way and keeps tracking receipts and rewinds, so anything\n\t * else reading it (captions, the UI) still works and re-enabling this resumes cleanly.\n\t */\n\tpaced: Getter<boolean>;\n};\n\n/** Cumulative video statistics since the decoder started. */\nexport interface Stats {\n\t/** Number of decoded frames. */\n\tframeCount: number;\n\n\t/** Number of encoded bytes received. */\n\tbytesReceived: number;\n}\n\ntype DecoderOutput = {\n\t// The current frame to render.\n\tframe: Signal<VideoFrame | undefined>;\n\n\t// The timestamp of the current frame.\n\ttimestamp: Signal<Time.Milli | undefined>;\n\n\t// The display size of the video in pixels, ideally sourced from the catalog.\n\tdisplay: Signal<{ width: number; height: number } | undefined>;\n\n\tstalled: Signal<boolean>;\n\tstats: Signal<Stats | undefined>;\n\n\t// Combined buffered ranges (network jitter + decode buffer)\n\tbuffered: Signal<Container.BufferedRanges>;\n};\n\n/** Downloads video from a track and decodes it into {@link VideoFrame}s with WebCodecs. */\nexport class Decoder {\n\treadonly in: Readonlys<DecoderInput>;\n\treadonly source: Source;\n\treadonly sync: Sync;\n\n\treadonly #out: DecoderOutput = {\n\t\tframe: new Signal<VideoFrame | undefined>(undefined),\n\t\ttimestamp: new Signal<Time.Milli | undefined>(undefined),\n\t\tdisplay: new Signal<{ width: number; height: number } | undefined>(undefined),\n\t\tstalled: new Signal<boolean>(false),\n\t\tstats: new Signal<Stats | undefined>(undefined),\n\t\tbuffered: new Signal<Container.BufferedRanges>([]),\n\t};\n\treadonly out = readonlys(this.#out);\n\n\t// The current track running, held so we can cancel it when the new track is ready.\n\t#active = new Signal<DecoderTrack | undefined>(undefined);\n\treadonly #identity: Computed<PlaybackIdentity | undefined>;\n\n\t#signals = new Effect();\n\n\t#clearCurrentFrame(): void {\n\t\tthis.#out.frame.update((prev) => {\n\t\t\tprev?.close();\n\t\t\treturn undefined;\n\t\t});\n\t\tthis.#out.timestamp.set(undefined);\n\t}\n\n\tconstructor(source: Source, sync: Sync, props?: Inputs<DecoderInput>) {\n\t\tthis.in = {\n\t\t\tenabled: getter(props?.enabled ?? false),\n\t\t\tpaced: getter(props?.paced ?? true),\n\t\t};\n\n\t\tthis.source = source;\n\t\tthis.sync = sync;\n\t\tthis.#identity = this.#signals.computed((effect) => {\n\t\t\tconst config = effect.get(this.source.out.config);\n\t\t\treturn config ? playbackIdentity(config) : undefined;\n\t\t});\n\n\t\tthis.#signals.run(this.#runPending.bind(this));\n\t\tthis.#signals.run(this.#runActive.bind(this));\n\t\tthis.#signals.run(this.#runDisplay.bind(this));\n\t\tthis.#signals.run(this.#runBuffering.bind(this));\n\t}\n\n\t#runPending(effect: Effect): void {\n\t\tconst values = effect.getAll([\n\t\t\tthis.in.enabled,\n\t\t\tthis.source.in.broadcast,\n\t\t\tthis.source.out.track,\n\t\t\tthis.#identity,\n\t\t]);\n\t\tif (!values) {\n\t\t\t// Close the active track when disabled (e.g. paused or not visible).\n\t\t\t// The pending cleanup won't do this because it was already promoted to #active.\n\t\t\tthis.#active.set(undefined);\n\t\t\treturn;\n\t\t}\n\t\tconst [_, broadcast, track, identity] = values;\n\n\t\t// Honor a per-rendition `broadcast` override: subscribe on the resolved source\n\t\t// broadcast instead of the catalog's own broadcast.\n\t\tconst active: Moq.Broadcast.Consumer | undefined = broadcast.relativeBroadcast(effect, identity.broadcast);\n\t\tif (!active) {\n\t\t\t// Going offline should clear the last rendered frame.\n\t\t\tthis.#active.set(undefined);\n\t\t\tthis.#clearCurrentFrame();\n\t\t\tthis.#out.buffered.set([]);\n\t\t\treturn;\n\t\t}\n\n\t\t// Start a new pending effect.\n\t\tlet pending: DecoderTrack | undefined = new DecoderTrack({\n\t\t\tsync: this.sync,\n\t\t\tpaced: this.in.paced,\n\t\t\tbroadcast: active,\n\t\t\ttrack,\n\t\t\tconfig: identity.decoder,\n\t\t\tstats: this.#out.stats,\n\t\t});\n\n\t\teffect.cleanup(() => pending?.close());\n\n\t\teffect.run((effect) => {\n\t\t\tif (!pending) return;\n\n\t\t\tconst current = effect.get(this.#active);\n\t\t\tif (current) {\n\t\t\t\t// A zero timestamp is a rendered frame, not a missing one.\n\t\t\t\tconst pendingTimestamp = effect.get(pending.timestamp);\n\t\t\t\tif (pendingTimestamp === undefined) return;\n\n\t\t\t\t// Hold off until the new rendition has caught up to the picture it's replacing.\n\t\t\t\tif (!caughtUp({ playhead: pendingTimestamp, active: effect.get(current.timestamp) })) return;\n\t\t\t}\n\n\t\t\t// Upgrade the pending track to active.\n\t\t\t// #runActive will be in charge of it now.\n\t\t\tthis.#active.set(pending);\n\t\t\tpending = undefined;\n\n\t\t\t// This effect is done; close it to avoid a useless re-run.\n\t\t\teffect.close();\n\t\t});\n\t}\n\n\t#runActive(effect: Effect): void {\n\t\tconst active = effect.get(this.#active);\n\t\tif (!active) {\n\t\t\t// Clear stale data when disabled (e.g. paused or not visible).\n\t\t\tthis.#out.buffered.set([]);\n\t\t\treturn;\n\t\t}\n\n\t\teffect.cleanup(() => active.close());\n\n\t\t// Clone the frame so we own it independently of the DecoderTrack.\n\t\t// proxy() would share the same reference, allowing the source to close our frame.\n\t\teffect.run((inner) => {\n\t\t\tconst frame = inner.get(active.frame);\n\t\t\tthis.#out.frame.update((prev) => {\n\t\t\t\tprev?.close();\n\t\t\t\treturn frame?.clone();\n\t\t\t});\n\t\t});\n\t\teffect.proxy(this.#out.timestamp, active.timestamp);\n\t\teffect.proxy(this.#out.buffered, active.buffered);\n\t}\n\n\t#runDisplay(effect: Effect): void {\n\t\tconst catalog = effect.get(this.source.out.catalog);\n\t\tif (!catalog) return;\n\n\t\tconst display = catalog.display;\n\t\tif (display) {\n\t\t\teffect.set(this.#out.display, { width: display.width, height: display.height });\n\t\t\treturn;\n\t\t}\n\n\t\tconst frame = effect.get(this.#out.frame);\n\t\tif (!frame) return;\n\n\t\teffect.set(\n\t\t\tthis.#out.display,\n\t\t\trotateVideoDimensions({ width: frame.displayWidth, height: frame.displayHeight }, catalog.rotation),\n\t\t);\n\t}\n\n\t#runBuffering(effect: Effect): void {\n\t\tconst enabled = effect.get(this.in.enabled);\n\t\tif (!enabled) return;\n\n\t\tconst frame = effect.get(this.#out.frame);\n\t\tif (!frame) {\n\t\t\tthis.#out.stalled.set(true);\n\t\t\treturn;\n\t\t}\n\n\t\tthis.#out.stalled.set(false);\n\n\t\teffect.timer(() => {\n\t\t\tthis.#out.stalled.set(true);\n\t\t}, BUFFERING);\n\t}\n\n\tclose() {\n\t\tthis.#clearCurrentFrame();\n\n\t\tthis.#signals.close();\n\t}\n\n\t// Whether the WebCodecs video decoder can play this config.\n\tstatic supported = supported;\n}\n\ninterface DecoderTrackProps {\n\tsync: Sync;\n\tpaced: Getter<boolean>;\n\tbroadcast: Moq.Broadcast.Consumer;\n\ttrack: string;\n\tconfig: DecoderConfig;\n\n\tstats: Signal<Stats | undefined>;\n}\n\nclass DecoderTrack {\n\tsync: Sync;\n\tpaced: Getter<boolean>;\n\tbroadcast: Moq.Broadcast.Consumer;\n\ttrack: string;\n\tconfig: DecoderConfig;\n\tstats: Signal<Stats | undefined>;\n\n\ttimestamp = new Signal<Time.Milli | undefined>(undefined);\n\tframe = new Signal<VideoFrame | undefined>(undefined);\n\n\t// Network jitter + decode buffer.\n\tbuffered = new Signal<Container.BufferedRanges>([]);\n\n\t// Decoded frames waiting to be rendered.\n\t#buffered = new Signal<Container.BufferedRanges>([]);\n\n\t// The container's reorder/skip budget, mirroring the sync buffer. Zero when unpaced,\n\t// matching the consumer's own default: don't wait, skip to the live edge.\n\t#latency = new Signal<Time.Milli>(Time.Milli.zero);\n\n\t// The last discontinuity count seen from the container consumer; doubles as a generation\n\t// so in-flight decodes from before a rewind can be dropped on output.\n\t#discontinuity = 0;\n\n\t#signals = new Effect();\n\n\tconstructor(props: DecoderTrackProps) {\n\t\tthis.sync = props.sync;\n\t\tthis.paced = props.paced;\n\t\tthis.broadcast = props.broadcast;\n\t\tthis.track = props.track;\n\t\tthis.config = props.config;\n\t\tthis.stats = props.stats;\n\n\t\tthis.#signals.run((effect) => {\n\t\t\tconst paced = effect.get(this.paced);\n\t\t\tthis.#latency.set(paced ? effect.get(this.sync.out.buffer) : Time.Milli.zero);\n\t\t});\n\n\t\tthis.#signals.run(this.#run.bind(this));\n\t}\n\n\t#run(effect: Effect): void {\n\t\tconst sub = this.broadcast.track(this.track).subscribe({ priority: Catalog.PRIORITY.video });\n\t\teffect.cleanup(() => sub.close());\n\n\t\tconst decoder = new VideoDecoder({\n\t\t\toutput: async (frame: VideoFrame) => {\n\t\t\t\ttry {\n\t\t\t\t\t// The generation this frame was decoded in. If a rewind bumps it while we wait\n\t\t\t\t\t// below, this frame belongs to the reneged timeline and must be dropped.\n\t\t\t\t\tconst generation = this.#discontinuity;\n\n\t\t\t\t\tconst timestamp = Time.Milli.fromMicro(frame.timestamp as Time.Micro);\n\t\t\t\t\tif (timestamp < (this.timestamp.peek() ?? 0)) {\n\t\t\t\t\t\t// Late frame, don't render it.\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (this.paced.peek()) {\n\t\t\t\t\t\t// `received()` runs at submit, so a frame is anchored by the time it decodes.\n\t\t\t\t\t\t// Reaching here unanchored means a rewind reset the clock after this frame's\n\t\t\t\t\t\t// received(), so it predates the current timeline. Drop it: painting it would\n\t\t\t\t\t\t// set `timestamp` from the old timeline and late-reject the whole rewind.\n\t\t\t\t\t\tif (this.sync.out.reference.peek() === undefined) return;\n\n\t\t\t\t\t\tif (this.frame.peek() === undefined) {\n\t\t\t\t\t\t\t// Render something while we wait for the sync to catch up.\n\t\t\t\t\t\t\tthis.frame.set(frame.clone());\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!(await this.#park(timestamp, effect))) return;\n\t\t\t\t\t\tif (generation !== this.#discontinuity) return; // a rewind happened while waiting\n\n\t\t\t\t\t\tif (timestamp < (this.timestamp.peek() ?? 0)) {\n\t\t\t\t\t\t\t// Late frame, don't render it.\n\t\t\t\t\t\t\t// NOTE: This can happen when the ref is updated, such as on playback start.\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tthis.timestamp.set(timestamp);\n\n\t\t\t\t\t// Trim the decode buffer as frames are rendered\n\t\t\t\t\tthis.#trimBuffered(timestamp);\n\n\t\t\t\t\tthis.frame.update((prev) => {\n\t\t\t\t\t\tprev?.close();\n\t\t\t\t\t\treturn frame.clone(); // avoid closing the frame here\n\t\t\t\t\t});\n\t\t\t\t} finally {\n\t\t\t\t\tframe.close();\n\t\t\t\t}\n\t\t\t},\n\t\t\t// TODO bubble up error\n\t\t\terror: (error) => {\n\t\t\t\tconsole.error(error);\n\t\t\t\teffect.close();\n\t\t\t},\n\t\t});\n\t\teffect.cleanup(() => {\n\t\t\tif (decoder.state !== \"closed\") decoder.close();\n\t\t});\n\n\t\t// Input processing - depends on container type\n\t\tif (this.config.container.kind === \"cmaf\") {\n\t\t\tthis.#runCmaf(effect, sub, decoder);\n\t\t} else {\n\t\t\tthis.#runLegacy(effect, sub, decoder);\n\t\t}\n\t}\n\n\t#runLegacy(effect: Effect, sub: Moq.Track.Subscriber, decoder: VideoDecoder): void {\n\t\tconst format =\n\t\t\tthis.config.container.kind === \"loc\" ? new Container.Loc.Format() : new Container.Legacy.Format();\n\t\t// Create consumer that reorders groups/frames up to the provided latency.\n\t\tconst consumer = new Container.Consumer(sub, {\n\t\t\tformat,\n\t\t\tlatency: this.#latency,\n\t\t});\n\t\teffect.cleanup(() => consumer.close());\n\n\t\t// Combine network jitter buffer with decode buffer\n\t\teffect.run((inner) => {\n\t\t\tconst network = inner.get(consumer.buffered);\n\t\t\tconst decode = inner.get(this.#buffered);\n\t\t\tthis.buffered.update(() => Container.mergeBufferedRanges(network, decode));\n\t\t});\n\n\t\tdecoder.configure({\n\t\t\tcodec: this.config.codec,\n\t\t\tdescription: this.config.description ? Util.Hex.toBytes(this.config.description) : undefined,\n\t\t\tdisplayAspectWidth: this.config.displayAspectWidth,\n\t\t\tdisplayAspectHeight: this.config.displayAspectHeight,\n\t\t\toptimizeForLatency: this.config.optimizeForLatency,\n\t\t\t// @ts-expect-error Only supported by Chrome, so the renderer has to flip manually.\n\t\t\tflip: false,\n\t\t});\n\n\t\tlet previous: Time.Micro | undefined;\n\n\t\teffect.spawn(async () => {\n\t\t\tfor (;;) {\n\t\t\t\tconst next = await consumer.next();\n\t\t\t\tif (!next) break;\n\n\t\t\t\t// Publisher rewound: flush queued/in-flight video and re-anchor before decoding.\n\t\t\t\tif (this.#onDiscontinuity(next.discontinuity)) previous = undefined;\n\n\t\t\t\tconst { frame } = next;\n\t\t\t\tif (!frame) continue; // The group is done\n\n\t\t\t\t// Mark that we received this frame right now.\n\t\t\t\tconst timestamp = Time.Milli.fromMicro(frame.timestamp as Time.Micro);\n\t\t\t\tthis.sync.received(timestamp, \"video\");\n\n\t\t\t\tconst chunk = new EncodedVideoChunk({\n\t\t\t\t\ttype: frame.keyframe ? \"key\" : \"delta\",\n\t\t\t\t\tdata: frame.payload,\n\t\t\t\t\ttimestamp: frame.timestamp,\n\t\t\t\t});\n\n\t\t\t\t// Track both frame count and bytes received for stats in the UI\n\t\t\t\tthis.stats.update((current) => ({\n\t\t\t\t\tframeCount: (current?.frameCount ?? 0) + 1,\n\t\t\t\t\tbytesReceived: (current?.bytesReceived ?? 0) + frame.payload.byteLength,\n\t\t\t\t}));\n\n\t\t\t\t// Track decode buffer: frames sent to decoder but not yet rendered. Only bridge from\n\t\t\t\t// the previous frame when the consumer says nothing is missing in between. Group ids\n\t\t\t\t// can't answer that: they aren't required to be sequential (some encoders derive them\n\t\t\t\t// from DTS), so adjacency neither proves continuity nor rules out a gap the consumer\n\t\t\t\t// skipped, and reporting a skipped span as decoded overstates the buffer.\n\t\t\t\tif (previous !== undefined && next.continuous) {\n\t\t\t\t\tthis.#addBuffered(Time.Milli.fromMicro(previous), Time.Milli.fromMicro(frame.timestamp));\n\t\t\t\t}\n\n\t\t\t\tprevious = frame.timestamp;\n\n\t\t\t\tdecoder.decode(chunk);\n\t\t\t}\n\t\t});\n\t}\n\n\t#runCmaf(effect: Effect, sub: Moq.Track.Subscriber, decoder: VideoDecoder): void {\n\t\tconst container = this.config.container;\n\t\tif (container.kind !== \"cmaf\") return;\n\n\t\tconst initSegment = base64ToBytes(container.init);\n\t\tconst init = Container.Cmaf.decodeInitSegment(initSegment);\n\t\tconst description = this.config.description ? Util.Hex.toBytes(this.config.description) : init.description;\n\n\t\tconst consumer = new Container.Consumer(sub, {\n\t\t\tformat: new Container.Cmaf.Format(init),\n\t\t\tlatency: this.#latency,\n\t\t});\n\t\teffect.cleanup(() => consumer.close());\n\n\t\t// Combine network jitter buffer with decode buffer\n\t\teffect.run((inner) => {\n\t\t\tconst network = inner.get(consumer.buffered);\n\t\t\tconst decode = inner.get(this.#buffered);\n\t\t\tthis.buffered.update(() => Container.mergeBufferedRanges(network, decode));\n\t\t});\n\n\t\t// Configure decoder with description from catalog\n\t\tdecoder.configure({\n\t\t\tcodec: this.config.codec,\n\t\t\tdescription,\n\t\t\tdisplayAspectWidth: this.config.displayAspectWidth,\n\t\t\tdisplayAspectHeight: this.config.displayAspectHeight,\n\t\t\toptimizeForLatency: this.config.optimizeForLatency,\n\t\t\t// @ts-expect-error Only supported by Chrome, so the renderer has to flip manually.\n\t\t\tflip: false,\n\t\t});\n\n\t\tlet previous: Time.Micro | undefined;\n\n\t\teffect.spawn(async () => {\n\t\t\tfor (;;) {\n\t\t\t\tconst next = await consumer.next();\n\t\t\t\tif (!next) break;\n\n\t\t\t\t// Publisher rewound: flush queued/in-flight video and re-anchor before decoding.\n\t\t\t\tif (this.#onDiscontinuity(next.discontinuity)) previous = undefined;\n\n\t\t\t\tconst { frame } = next;\n\t\t\t\tif (!frame) continue;\n\n\t\t\t\t// Mark that we received this frame right now.\n\t\t\t\tconst timestamp = Time.Milli.fromMicro(frame.timestamp);\n\t\t\t\tthis.sync.received(timestamp, \"video\");\n\n\t\t\t\t// Track stats\n\t\t\t\tthis.stats.update((current) => ({\n\t\t\t\t\tframeCount: (current?.frameCount ?? 0) + 1,\n\t\t\t\t\tbytesReceived: (current?.bytesReceived ?? 0) + frame.payload.byteLength,\n\t\t\t\t}));\n\n\t\t\t\t// Track decode buffer (see #runLegacy: bridge on the consumer's continuity signal,\n\t\t\t\t// never on group adjacency, which proves nothing about the timeline).\n\t\t\t\tif (previous !== undefined && next.continuous) {\n\t\t\t\t\tthis.#addBuffered(Time.Milli.fromMicro(previous), Time.Milli.fromMicro(frame.timestamp));\n\t\t\t\t}\n\n\t\t\t\tprevious = frame.timestamp;\n\n\t\t\t\tif (decoder.state === \"closed\") break;\n\t\t\t\tdecoder.decode(\n\t\t\t\t\tnew EncodedVideoChunk({\n\t\t\t\t\t\ttype: frame.keyframe ? \"key\" : \"delta\",\n\t\t\t\t\t\tdata: frame.payload,\n\t\t\t\t\t\ttimestamp: frame.timestamp,\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\t}\n\n\t// React to the container consumer's discontinuity counter. On a change the publisher has\n\t// rewound the timeline, so drop what's queued downstream and re-anchor the shared clock\n\t// before the new utterance. Clearing `timestamp` is load-bearing: otherwise its stale high\n\t// value would late-reject the rewound (lower-timestamp) frames at the output guard. Bumping\n\t// the generation drops in-flight decodes on output. The held frame is left in place so the\n\t// last picture shows until the new keyframe renders, instead of flashing empty. Returns true\n\t// if a rewind was handled.\n\t#onDiscontinuity(count: number): boolean {\n\t\tif (count === this.#discontinuity) return false;\n\t\tthis.#discontinuity = count;\n\t\tthis.timestamp.set(undefined);\n\t\tthis.#buffered.set([]);\n\t\tthis.sync.reset();\n\t\treturn true;\n\t}\n\n\t// Sleep until the clock says to paint this frame, waking early if pacing is turned off or the\n\t// run is torn down. Returns false when the frame should be dropped.\n\t//\n\t// Turning pacing off has to release parked frames, otherwise a deep buffer holds them (and their\n\t// GPU memory) until deadlines that no longer apply. They paint rather than drop: a parked frame\n\t// is early, not stale, so it is the freshest picture available the moment pacing stops.\n\t//\n\t// Dispose the registration once the race settles: one that outlives the frame retains an entry\n\t// per decoded frame for as long as the track lives, which is unbounded during steady playback.\n\tasync #park(timestamp: Time.Milli, effect: Effect): Promise<boolean> {\n\t\tlet release: Dispose | undefined;\n\t\ttry {\n\t\t\tconst released = new Promise<boolean>((resolve) => {\n\t\t\t\trelease = this.paced.changed(() => resolve(!this.paced.peek()));\n\t\t\t});\n\t\t\tconst wait = this.sync.wait(timestamp).then(() => true);\n\t\t\treturn (await Promise.race([wait, released, effect.cancel])) ?? false;\n\t\t} finally {\n\t\t\trelease?.();\n\t\t}\n\t}\n\n\t// Add a range to the decode buffer (decoded, waiting to render)\n\t#addBuffered(start: Time.Milli, end: Time.Milli): void {\n\t\tif (start > end) return;\n\n\t\tthis.#buffered.mutate((current) => {\n\t\t\tfor (const range of current) {\n\t\t\t\t// Check if there's any overlap, then merge\n\t\t\t\tif (range.start <= end && range.end >= start) {\n\t\t\t\t\trange.start = Time.Milli.min(range.start, start);\n\t\t\t\t\trange.end = Time.Milli.max(range.end, end);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcurrent.push({ start, end });\n\t\t\tcurrent.sort((a, b) => a.start - b.start);\n\t\t});\n\t}\n\n\t// Trim the decode buffer up to the rendered timestamp\n\t#trimBuffered(timestamp: Time.Milli): void {\n\t\tthis.#buffered.mutate((current) => {\n\t\t\twhile (current.length > 0) {\n\t\t\t\tif (current[0].end >= timestamp) {\n\t\t\t\t\tcurrent[0].start = Time.Milli.max(current[0].start, timestamp);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tcurrent.shift();\n\t\t\t}\n\t\t});\n\t}\n\n\tclose(): void {\n\t\tthis.#signals.close();\n\n\t\tthis.frame.update((prev) => {\n\t\t\tprev?.close();\n\t\t\treturn undefined;\n\t\t});\n\t}\n}\n\nasync function supported(config: Catalog.VideoConfig): Promise<boolean> {\n\tif (!Catalog.containerSupported(config.container)) {\n\t\t// `kind` is the literal \"unknown\" tag; the container the publisher actually named is in `raw`.\n\t\tconst kind = config.container.kind === \"unknown\" ? config.container.raw.kind : config.container.kind;\n\t\tconsole.warn(`video: ignoring rendition with unknown container: ${kind}`);\n\t\treturn false;\n\t}\n\n\tlet description: Uint8Array | undefined;\n\tif (config.description) {\n\t\tdescription = Util.Hex.toBytes(config.description);\n\t} else if (config.container.kind === \"cmaf\") {\n\t\ttry {\n\t\t\tdescription = Container.Cmaf.decodeInitSegment(base64ToBytes(config.container.init)).description;\n\t\t} catch (err) {\n\t\t\t// A malformed init segment means we can't extract the codec\n\t\t\t// description, so we can't probe support reliably. Reject the\n\t\t\t// track rather than letting isConfigSupported pass on a\n\t\t\t// description-less config and then having runCmaf fail later.\n\t\t\tconsole.warn(`video: malformed CMAF init segment for codec ${config.codec}`, err);\n\t\t\treturn false;\n\t\t}\n\t}\n\tconst { supported } = await VideoDecoder.isConfigSupported({\n\t\tcodec: config.codec,\n\t\tdescription,\n\t\tdisplayAspectWidth: config.displayAspectWidth,\n\t\tdisplayAspectHeight: config.displayAspectHeight,\n\t\toptimizeForLatency: config.optimizeForLatency ?? true,\n\t});\n\n\tif (supported) return true;\n\n\t// Safari rejects `avc3.*` codec strings even though its H.264 decoder handles\n\t// inline SPS/PPS. Rewrite to `avc1.*` and retry; mutate config.codec so the\n\t// later `decoder.configure()` call uses the accepted string too.\n\tif (config.codec.startsWith(\"avc3.\")) {\n\t\tconst avc1 = `avc1.${config.codec.slice(\"avc3.\".length)}`;\n\t\tconst retry = await VideoDecoder.isConfigSupported({\n\t\t\tcodec: avc1,\n\t\t\tdescription,\n\t\t\tdisplayAspectWidth: config.displayAspectWidth,\n\t\t\tdisplayAspectHeight: config.displayAspectHeight,\n\t\t\toptimizeForLatency: config.optimizeForLatency ?? true,\n\t\t});\n\t\tif (retry.supported) {\n\t\t\tconfig.codec = avc1;\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n\nObject.assign(supported, { [supportCacheKey]: decoderConfigKey });\n","import type * as Catalog from \"@moq/hang/catalog\";\nimport { Time } from \"@moq/net\";\nimport { Effect, type Getter, getter, type Inputs, type Readonlys, readonlys, Signal } from \"@moq/signals\";\nimport type { Decoder } from \"./decoder\";\nimport { canvasPresentationTransform } from \"./presentation\";\n\n// Fraction of the canvas that must intersect the viewport before it counts as visible.\nconst INTERSECTION_THRESHOLD = 0.01;\n\n/**\n * Controls when video is downloaded relative to the canvas position.\n *\n * - `\"never\"`: never download video.\n * - `\"always\"`: always download video, regardless of the canvas position or tab visibility.\n * - a CSS length (`\"0px\"`, `\"200px\"`, `\"100%\"`, ...): download while the canvas is within\n * that distance of the viewport (used as the {@link IntersectionObserver} `rootMargin`) and\n * the tab is visible. `\"0px\"` means strictly on screen; larger values pre-warm the video\n * before it scrolls in.\n */\nexport type Visible = \"never\" | \"always\" | (string & {});\n\nexport type RendererInput = {\n\tcanvas: Getter<HTMLCanvasElement | undefined>;\n\n\t// When video is downloaded relative to the canvas position. See {@link Visible}. Defaults to \"20%\".\n\tvisible: Getter<Visible>;\n};\n\ntype RendererOutput = {\n\t// The most recently rendered frame, updated after each rAF paint.\n\tframe: Signal<VideoFrame | undefined>;\n\n\t// The media timestamp of the most recently rendered frame.\n\ttimestamp: Signal<Time.Milli | undefined>;\n\n\t// Whether the canvas should currently download per the configured distance and tab focus.\n\t// The owner combines this with `paused` to drive the decoder's `enabled` input.\n\tvisible: Signal<boolean>;\n};\n\n// An component to render a video to a canvas.\nexport class Renderer {\n\treadonly decoder: Decoder;\n\n\treadonly in: Readonlys<RendererInput>;\n\n\treadonly #out: RendererOutput = {\n\t\tframe: new Signal<VideoFrame | undefined>(undefined),\n\t\ttimestamp: new Signal<Time.Milli | undefined>(undefined),\n\t\tvisible: new Signal(false),\n\t};\n\treadonly out = readonlys(this.#out);\n\n\t#ctx = new Signal<CanvasRenderingContext2D | undefined>(undefined);\n\t#signals = new Effect();\n\n\tconstructor(decoder: Decoder, props?: Inputs<RendererInput>) {\n\t\tthis.decoder = decoder;\n\t\tthis.in = {\n\t\t\tcanvas: getter(props?.canvas),\n\t\t\tvisible: getter(props?.visible ?? \"20%\"),\n\t\t};\n\n\t\tthis.#signals.run((effect) => {\n\t\t\tconst canvas = effect.get(this.in.canvas);\n\t\t\tthis.#ctx.set(canvas?.getContext(\"2d\") ?? undefined);\n\t\t});\n\n\t\tthis.#signals.run(this.#runVisible.bind(this));\n\t\tthis.#signals.run(this.#runRender.bind(this));\n\t\tthis.#signals.run(this.#runResize.bind(this));\n\t}\n\n\t#runResize(effect: Effect) {\n\t\tconst values = effect.getAll([this.in.canvas, this.decoder.out.display]);\n\t\tif (!values) return; // Keep current canvas size until we have new dimensions\n\t\tconst [canvas, display] = values;\n\n\t\t// Only update if dimensions actually changed (setting canvas.width/height clears the canvas)\n\t\t// TODO I thought the signals library would prevent this, but I'm too lazy to investigate.\n\t\tif (canvas.width !== display.width || canvas.height !== display.height) {\n\t\t\tcanvas.width = display.width;\n\t\t\tcanvas.height = display.height;\n\t\t}\n\t}\n\n\t// Track whether the canvas should currently download per the configured distance and tab focus.\n\t#runVisible(effect: Effect): void {\n\t\tconst visible = effect.get(this.in.visible);\n\n\t\t// \"never\" forces the check off; \"always\" forces it on regardless of viewport or tab state.\n\t\tif (visible === \"never\") {\n\t\t\tthis.#out.visible.set(false);\n\t\t\treturn;\n\t\t}\n\n\t\tif (visible === \"always\") {\n\t\t\tthis.#out.visible.set(true);\n\t\t\teffect.cleanup(() => this.#out.visible.set(false));\n\t\t\treturn;\n\t\t}\n\n\t\t// A distance gates on the viewport (used as the rootMargin) and the tab being visible.\n\t\tconst canvas = effect.get(this.in.canvas);\n\t\tif (!canvas) {\n\t\t\tthis.#out.visible.set(false);\n\t\t\treturn;\n\t\t}\n\n\t\tlet intersecting = false;\n\t\tconst update = () => {\n\t\t\tthis.#out.visible.set(intersecting && !document.hidden);\n\t\t};\n\n\t\tconst callback = (entries: IntersectionObserverEntry[]) => {\n\t\t\tfor (const entry of entries) {\n\t\t\t\tintersecting = entry.isIntersecting;\n\t\t\t\tupdate();\n\t\t\t}\n\t\t};\n\n\t\t// `visible` is a CSS length, but the programmatic API accepts arbitrary strings. An\n\t\t// invalid rootMargin throws a SyntaxError, so fall back to the default margin.\n\t\tlet observer: IntersectionObserver;\n\t\ttry {\n\t\t\tobserver = new IntersectionObserver(callback, { threshold: INTERSECTION_THRESHOLD, rootMargin: visible });\n\t\t} catch {\n\t\t\tconsole.warn(`moq-watch: invalid visible margin \"${visible}\", using \"0px\"`);\n\t\t\tobserver = new IntersectionObserver(callback, { threshold: INTERSECTION_THRESHOLD });\n\t\t}\n\n\t\tupdate();\n\t\teffect.event(document, \"visibilitychange\", update);\n\t\tobserver.observe(canvas);\n\t\teffect.cleanup(() => observer.disconnect());\n\t\teffect.cleanup(() => this.#out.visible.set(false));\n\t}\n\n\t#runRender(effect: Effect) {\n\t\tconst ctx = effect.get(this.#ctx);\n\t\tif (!ctx) return;\n\n\t\tconst frame = effect.get(this.decoder.out.frame);\n\t\tconst video = effect.get(this.decoder.source.out.catalog);\n\n\t\t// Request a callback to render the frame based on the monitor's refresh rate.\n\t\t// Always render, even when paused (to show last frame).\n\t\tlet animate: number | undefined = requestAnimationFrame(() => {\n\t\t\tthis.#render(ctx, frame, video);\n\n\t\t\tif (frame) {\n\t\t\t\tthis.#out.frame.update((current) => {\n\t\t\t\t\tcurrent?.close();\n\t\t\t\t\treturn frame.clone();\n\t\t\t\t});\n\t\t\t\tthis.#out.timestamp.set(Time.Milli.fromMicro(frame.timestamp as Time.Micro));\n\t\t\t} else {\n\t\t\t\tthis.#out.frame.update((current) => {\n\t\t\t\t\tcurrent?.close();\n\t\t\t\t\treturn undefined;\n\t\t\t\t});\n\t\t\t\tthis.#out.timestamp.set(undefined);\n\t\t\t}\n\n\t\t\tanimate = undefined;\n\t\t});\n\n\t\t// Clean up any pending animation request.\n\t\teffect.cleanup(() => {\n\t\t\tif (animate !== undefined) cancelAnimationFrame(animate);\n\t\t});\n\t}\n\n\t#render(ctx: CanvasRenderingContext2D, frame?: VideoFrame, video?: Catalog.Video) {\n\t\tif (!frame) {\n\t\t\t// Clear canvas when no frame\n\t\t\tctx.fillStyle = \"#000\";\n\t\t\tctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);\n\t\t\treturn;\n\t\t}\n\n\t\t// Prepare background and transformations for this draw\n\t\tctx.save();\n\t\tctx.fillStyle = \"#000\";\n\t\tctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);\n\n\t\t// Apply rotation and horizontal flip from the video config.\n\t\tif (!video?.rotation) {\n\t\t\tif (video?.flip) {\n\t\t\t\tctx.scale(-1, 1);\n\t\t\t\tctx.translate(-ctx.canvas.width, 0);\n\t\t\t}\n\t\t\tctx.drawImage(frame, 0, 0, ctx.canvas.width, ctx.canvas.height);\n\t\t} else {\n\t\t\tconst transform = canvasPresentationTransform(ctx.canvas, video);\n\t\t\tctx.setTransform(...transform.matrix);\n\t\t\tctx.drawImage(frame, 0, 0, transform.source.width, transform.source.height);\n\t\t}\n\t\tctx.restore();\n\t}\n\n\t// Close the track and all associated resources.\n\tclose() {\n\t\tthis.#out.frame.update((current) => {\n\t\t\tcurrent?.close();\n\t\t\treturn undefined;\n\t\t});\n\t\tthis.#out.timestamp.set(undefined);\n\t\tthis.#signals.close();\n\t}\n}\n","import type * as Catalog from \"@moq/hang/catalog\";\nimport type * as Moq from \"@moq/net\";\nimport { Effect, type Getter, getter, type Inputs, type Readonlys, readonlys, Signal } from \"@moq/signals\";\nimport type { Broadcast } from \"../broadcast\";\nimport { supportCacheKey } from \"./config\";\nimport { renditionJitter } from \"./playhead\";\n\n/**\n * A function that checks if a video configuration can be played.\n *\n * `Decoder.supported` is the WebCodecs probe used by `<moq-watch>`.\n */\nexport type Supported = (config: Catalog.VideoConfig) => Promise<boolean>;\n\ntype CacheableSupported = Supported & {\n\t[supportCacheKey]?: (config: Catalog.VideoConfig) => string;\n};\n\n/** A video source error that prevents choosing a usable rendition. */\nexport type SourceError = \"unsupported\";\n\nexport type Target = {\n\t// Optional manual override for the selected rendition name.\n\tname?: string;\n\n\t// Maximum desired pixel area (codedWidth * codedHeight).\n\tpixels?: number;\n\n\t// Maximum desired coded width in pixels.\n\twidth?: number;\n\n\t// Maximum desired coded height in pixels.\n\theight?: number;\n\n\t// Maximum desired bitrate in bits per second.\n\tbitrate?: number;\n};\n\nexport type SourceInput = {\n\tbroadcast: Getter<Broadcast | undefined>;\n\ttarget: Getter<Target | undefined>;\n\n\t// A function that checks if a video configuration can be played. Renditions that fail the\n\t// probe are filtered out. Nothing is selected until one is provided.\n\tsupported: Getter<Supported | undefined>;\n};\n\ntype SourceOutput = {\n\tcatalog: Signal<Catalog.Video | undefined>;\n\tavailable: Signal<Record<string, Catalog.VideoConfig>>;\n\n\t// The current source error, or undefined while healthy or still probing.\n\terror: Signal<SourceError | undefined>;\n\n\t// The name of the active rendition.\n\ttrack: Signal<string | undefined>;\n\tconfig: Signal<Catalog.VideoConfig | undefined>;\n\n\t// The per-rendition jitter (ms) to add to the sync buffer. Wired into Sync by the parent.\n\tjitter: Signal<Moq.Time.Milli | undefined>;\n};\n\n/**\n * A filter that returns matching renditions sorted by preference (most preferred first).\n * Must return at least one rendition.\n */\ntype RenditionFilter = (entries: [string, Catalog.VideoConfig][]) => string[];\n\n/**\n * Filter and rank renditions by a maximum pixel count.\n * Returns renditions within budget (largest first for best quality).\n * Over-budget and unknown-resolution renditions are excluded.\n * If nothing is within budget, falls back to the single smallest rendition.\n */\nfunction byPixels(target: number): RenditionFilter {\n\treturn (entries) => {\n\t\tconst within: { name: string; size: number }[] = [];\n\t\tconst rest: { name: string; size: number }[] = [];\n\n\t\tfor (const [name, config] of entries) {\n\t\t\tif (config.codedWidth && config.codedHeight) {\n\t\t\t\tconst size = config.codedWidth * config.codedHeight;\n\t\t\t\tif (size <= target) {\n\t\t\t\t\twithin.push({ name, size });\n\t\t\t\t} else {\n\t\t\t\t\trest.push({ name, size });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Best quality within budget\n\t\twithin.sort((a, b) => b.size - a.size);\n\n\t\tif (within.length > 0) {\n\t\t\treturn within.map((e) => e.name);\n\t\t}\n\n\t\t// Degrade to smallest over-budget resolution.\n\t\tif (rest.length > 0) {\n\t\t\trest.sort((a, b) => a.size - b.size);\n\t\t\treturn [rest[0].name];\n\t\t}\n\n\t\t// No entries had resolution metadata, so return all names unranked.\n\t\treturn entries.map(([name]) => name);\n\t};\n}\n\n/**\n * Filter and rank renditions by maximum coded dimensions.\n * Returns renditions where codedWidth <= width AND codedHeight <= height\n * (each cap is optional). Within-budget renditions rank by area (largest first).\n * If nothing fits, falls back to the single smallest over-budget rendition.\n */\nfunction byDimensions(width?: number, height?: number): RenditionFilter {\n\treturn (entries) => {\n\t\tconst within: { name: string; size: number }[] = [];\n\t\tconst rest: { name: string; size: number }[] = [];\n\n\t\tfor (const [name, config] of entries) {\n\t\t\tif (!config.codedWidth || !config.codedHeight) continue;\n\t\t\tconst size = config.codedWidth * config.codedHeight;\n\t\t\tconst fitsWidth = width == null || config.codedWidth <= width;\n\t\t\tconst fitsHeight = height == null || config.codedHeight <= height;\n\t\t\tif (fitsWidth && fitsHeight) {\n\t\t\t\twithin.push({ name, size });\n\t\t\t} else {\n\t\t\t\trest.push({ name, size });\n\t\t\t}\n\t\t}\n\n\t\t// Best quality within budget\n\t\twithin.sort((a, b) => b.size - a.size);\n\n\t\tif (within.length > 0) {\n\t\t\treturn within.map((e) => e.name);\n\t\t}\n\n\t\t// Degrade to smallest over-budget rendition.\n\t\tif (rest.length > 0) {\n\t\t\trest.sort((a, b) => a.size - b.size);\n\t\t\treturn [rest[0].name];\n\t\t}\n\n\t\t// No entries had resolution metadata, so return all names unranked.\n\t\treturn entries.map(([name]) => name);\n\t};\n}\n\n/**\n * Filter and rank renditions by a maximum bitrate budget.\n * Returns renditions within budget (highest bitrate first for best quality).\n * Over-budget and unknown-bitrate renditions are excluded.\n * If nothing is within budget, falls back to the single lowest-bitrate rendition.\n */\nfunction byBitrate(target: number): RenditionFilter {\n\treturn (entries) => {\n\t\tconst within: { name: string; bitrate: number }[] = [];\n\t\tconst rest: { name: string; bitrate: number }[] = [];\n\n\t\tfor (const [name, config] of entries) {\n\t\t\tif (config.bitrate != null && config.bitrate <= target) {\n\t\t\t\twithin.push({ name, bitrate: config.bitrate });\n\t\t\t} else if (config.bitrate != null) {\n\t\t\t\trest.push({ name, bitrate: config.bitrate });\n\t\t\t}\n\t\t}\n\n\t\t// Best quality within budget\n\t\twithin.sort((a, b) => b.bitrate - a.bitrate);\n\n\t\tif (within.length > 0) {\n\t\t\treturn within.map((e) => e.name);\n\t\t}\n\n\t\t// Degrade to lowest over-budget bitrate.\n\t\tif (rest.length > 0) {\n\t\t\trest.sort((a, b) => a.bitrate - b.bitrate);\n\t\t\treturn [rest[0].name];\n\t\t}\n\n\t\t// No entries had bitrate metadata, so return all names unranked.\n\t\treturn entries.map(([name]) => name);\n\t};\n}\n\n/**\n * Pick the best rendition when no filters are active.\n * Prefers the largest resolution, falls back to highest bitrate,\n * then falls back to the first entry.\n */\nfunction bestRendition(entries: [string, Catalog.VideoConfig][]): string {\n\tlet best = entries[0];\n\n\tfor (const entry of entries) {\n\t\tconst [, config] = entry;\n\t\tconst [, bestConfig] = best;\n\n\t\tconst size = (config.codedWidth ?? 0) * (config.codedHeight ?? 0);\n\t\tconst bestSize = (bestConfig.codedWidth ?? 0) * (bestConfig.codedHeight ?? 0);\n\n\t\tif (size !== bestSize) {\n\t\t\tif (size > bestSize) best = entry;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif ((config.bitrate ?? 0) > (bestConfig.bitrate ?? 0)) {\n\t\t\tbest = entry;\n\t\t}\n\t}\n\n\treturn best[0];\n}\n\n/** Return unstalled renditions, or the lowest bitrate or resolution when every option is stalled. */\nfunction selectableRenditions(renditions: Record<string, Catalog.VideoConfig>): Record<string, Catalog.VideoConfig> {\n\tconst active = Object.entries(renditions).filter(([, config]) => !config.stalled);\n\tif (active.length > 0) return Object.fromEntries(active);\n\n\tconst entries = Object.entries(renditions);\n\tif (entries.length === 0) return {};\n\tconst byRate = byBitrate(0)(entries);\n\tconst lowest = byRate.length === 1 ? byRate[0] : byDimensions(0, 0)(entries)[0];\n\treturn { [lowest]: renditions[lowest] };\n}\n\n/**\n * Source handles catalog extraction, support checking, and rendition selection\n * for video playback. The Decoder consumes whichever rendition it picks.\n */\nexport class Source {\n\treadonly in: Readonlys<SourceInput>;\n\n\treadonly #out: SourceOutput = {\n\t\tcatalog: new Signal<Catalog.Video | undefined>(undefined),\n\t\tavailable: new Signal<Record<string, Catalog.VideoConfig>>({}),\n\t\terror: new Signal<SourceError | undefined>(undefined),\n\t\ttrack: new Signal<string | undefined>(undefined),\n\t\tconfig: new Signal<Catalog.VideoConfig | undefined>(undefined),\n\t\tjitter: new Signal<Moq.Time.Milli | undefined>(undefined),\n\t};\n\treadonly out = readonlys(this.#out);\n\n\t#signals = new Effect();\n\t#supportCache = new WeakMap<Supported, Map<string, { key: string; supported: boolean }>>();\n\n\tconstructor(props?: Inputs<SourceInput>) {\n\t\tthis.in = {\n\t\t\tbroadcast: getter(props?.broadcast),\n\t\t\ttarget: getter(props?.target),\n\t\t\tsupported: getter(props?.supported),\n\t\t};\n\n\t\tthis.#signals.run(this.#runCatalog.bind(this));\n\t\tthis.#signals.run(this.#runSupported.bind(this));\n\t\tthis.#signals.run(this.#runSelected.bind(this));\n\t}\n\n\t#runCatalog(effect: Effect): void {\n\t\tconst broadcast = effect.get(this.in.broadcast);\n\t\tif (!broadcast) return;\n\n\t\tconst catalog = effect.get(broadcast.out.catalog)?.video;\n\t\tif (!catalog) return;\n\n\t\teffect.set(this.#out.catalog, catalog);\n\t}\n\n\t#runSupported(effect: Effect): void {\n\t\tconst supported = effect.get(this.in.supported);\n\t\tif (!supported) {\n\t\t\tthis.#out.error.set(undefined);\n\t\t\treturn;\n\t\t}\n\n\t\tconst renditions = effect.get(this.#out.catalog)?.renditions ?? {};\n\t\tthis.#out.error.set(undefined);\n\t\tlet cache = this.#supportCache.get(supported);\n\t\tif (!cache) {\n\t\t\tcache = new Map();\n\t\t\tthis.#supportCache.set(supported, cache);\n\t\t}\n\t\tconst names = new Set(Object.keys(renditions));\n\t\tfor (const name of cache.keys()) {\n\t\t\tif (!names.has(name)) cache.delete(name);\n\t\t}\n\n\t\teffect.spawn(async () => {\n\t\t\tconst available: Record<string, Catalog.VideoConfig> = {};\n\n\t\t\t// `supported` comes from the consumer, so we cannot assume it ever settles. A rerun\n\t\t\t// waits for the tasks it spawned, so an unraced probe would hold the next run shut\n\t\t\t// for good. Captured here so it stays this run's promise once we start awaiting.\n\t\t\tconst cancelled = effect.cancel.then(() => undefined);\n\n\t\t\tfor (const [name, config] of Object.entries(renditions)) {\n\t\t\t\tconst cacheKey = (supported as CacheableSupported)[supportCacheKey];\n\t\t\t\tconst key = cacheKey ? cacheKey(config) : JSON.stringify(config);\n\t\t\t\tconst cached = cache.get(name);\n\t\t\t\tlet isSupported: boolean | undefined = false;\n\t\t\t\tif (cached?.key === key) {\n\t\t\t\t\tisSupported = cached.supported;\n\t\t\t\t} else {\n\t\t\t\t\tlet failed = false;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tisSupported = await Promise.race([supported(config), cancelled]);\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tfailed = true;\n\t\t\t\t\t\tconsole.warn(\n\t\t\t\t\t\t\t`[Source] video rendition ${name} (${config.codec}) support probe failed; treating as unsupported`,\n\t\t\t\t\t\t\terr,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (!failed && isSupported !== undefined) {\n\t\t\t\t\t\tcache.set(name, {\n\t\t\t\t\t\t\tkey: cacheKey ? cacheKey(config) : JSON.stringify(config),\n\t\t\t\t\t\t\tsupported: isSupported,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Torn down: stop probing and publish nothing, since the rerun redoes this.\n\t\t\t\tif (effect.abort.aborted) return;\n\n\t\t\t\tif (isSupported) available[name] = config;\n\t\t\t}\n\n\t\t\tconst error =\n\t\t\t\tObject.keys(available).length === 0 && Object.keys(renditions).length > 0 ? \"unsupported\" : undefined;\n\t\t\tif (error === \"unsupported\") {\n\t\t\t\tconsole.warn(\"[Source] No supported video renditions found:\", renditions);\n\t\t\t}\n\n\t\t\tthis.#out.error.set(error);\n\t\t\tthis.#out.available.set(available);\n\t\t});\n\t}\n\n\t#runSelected(effect: Effect): void {\n\t\tconst available = selectableRenditions(effect.get(this.#out.available));\n\t\tif (Object.keys(available).length === 0) return;\n\n\t\tconst target = effect.get(this.in.target);\n\n\t\t// Manual selection by name skips all ABR logic.\n\t\tif (target?.name && target.name in available) {\n\t\t\tconst config = available[target.name];\n\t\t\teffect.set(this.#out.track, target.name);\n\t\t\teffect.set(this.#out.config, config);\n\t\t\teffect.set(this.#out.jitter, renditionJitter(config));\n\t\t\treturn;\n\t\t}\n\n\t\t// Auto-select: use recv bandwidth if no explicit bitrate target.\n\t\tlet effectiveTarget = target;\n\t\tif (!target?.bitrate) {\n\t\t\tconst broadcast = effect.get(this.in.broadcast);\n\t\t\tconst connection = broadcast ? effect.get(broadcast.in.connection) : undefined;\n\t\t\tconst estimate = connection && effect.get(connection.probe).estimatedRecvRate;\n\t\t\tif (estimate != null) {\n\t\t\t\t// Apply a safety margin (80%) to avoid oscillation.\n\t\t\t\tconst safeBitrate = Math.round(estimate * 0.8);\n\t\t\t\teffectiveTarget = { ...target, bitrate: safeBitrate };\n\t\t\t}\n\t\t}\n\n\t\tconst selected = this.#select(available, effectiveTarget);\n\t\tif (!selected) return;\n\n\t\tconst config = available[selected];\n\n\t\teffect.set(this.#out.track, selected);\n\t\teffect.set(this.#out.config, config);\n\t\teffect.set(this.#out.jitter, renditionJitter(config));\n\t}\n\n\t/**\n\t * Select the best rendition using a generic filter system.\n\t *\n\t * Each enabled filter returns matching renditions sorted by preference.\n\t * The first rendition present in every filter's output is selected.\n\t * If no rendition satisfies all filters, a warning is logged.\n\t */\n\t#select(renditions: Record<string, Catalog.VideoConfig>, target?: Target): string | undefined {\n\t\tconst entries = Object.entries(renditions);\n\t\tif (entries.length === 0) return undefined;\n\t\tif (entries.length === 1) return entries[0][0];\n\n\t\t// Build enabled filters based on the target.\n\t\tconst filters: RenditionFilter[] = [];\n\n\t\tif (target?.pixels != null) {\n\t\t\tfilters.push(byPixels(target.pixels));\n\t\t}\n\t\tif (target?.width != null || target?.height != null) {\n\t\t\tfilters.push(byDimensions(target.width, target.height));\n\t\t}\n\t\tif (target?.bitrate != null) {\n\t\t\tfilters.push(byBitrate(target.bitrate));\n\t\t}\n\n\t\t// With no filters, pick the best rendition by quality.\n\t\tif (filters.length === 0) {\n\t\t\treturn bestRendition(entries);\n\t\t}\n\n\t\t// Run each filter to get ranked preference lists.\n\t\tconst rankings = filters.map((f) => f(entries));\n\n\t\t// Select the first rendition (in the first ranking's order) present in all rankings.\n\t\tconst sets = rankings.map((r) => new Set(r));\n\n\t\tfor (const name of rankings[0]) {\n\t\t\tif (sets.every((s) => s.has(name))) {\n\t\t\t\treturn name;\n\t\t\t}\n\t\t}\n\n\t\tconsole.warn(\"conflicting rendition filters, no rendition satisfies all criteria\");\n\t\treturn undefined;\n\t}\n\n\tclose(): void {\n\t\tthis.#signals.close();\n\t}\n}\n"],"mappings":";;;;;;;;;;AACA,SAAgB,EAAc,GAAyB;CACtD,IAAM,IAAM,KAAK,CAAG,GACd,IAAQ,IAAI,WAAW,EAAI,MAAM;CACvC,KAAK,IAAI,IAAI,GAAG,IAAI,EAAI,QAAQ,KAAK,EAAM,KAAK,EAAI,WAAW,CAAC;CAChE,OAAO;AACR;;;ACHA,IAAM,IAAQ,GACR,IAAU,GACV,IAAU,GACV,IAAgB,GAahB,IAAc;AAEpB,SAAS,EAAK,GAAe,GAAsB;CAClD,OAAQ,OAAO,MAAU,CAAC,KAAK,MAAO,OAAO,MAAS,CAAC;AACxD;AAGA,SAAS,EAAQ,GAAuB;CACvC,OAAO,OAAO,KAAS,GAAG,IAAI;AAC/B;AAGA,SAAS,EAAO,GAAuB;CACtC,OAAO,OAAO,IAAQ,CAAW,IAAI;AACtC;AAcA,SAAS,GAAS,GAAmB;CACpC,OAAO,KAAK,IAAI,IAAI,KAAM,KAAK,KAAK,MAAM,IAAI,CAAC;AAChD;AAQA,SAAgB,EACf,GACA,GACA,GACA,IAAW,IACY;CACvB,IAAI,KAAY,GAAG,MAAU,MAAM,kBAAkB;CACrD,IAAI,KAAY,KAAK,IAAW,KAAK,IAAI,MAAU,MAAM,kBAAkB;CAC3E,IAAI,KAAQ,GAAG,MAAU,MAAM,qBAAqB;CAEpD,IAAW,GAAS,CAAQ;CAE5B,IAAM,IAAU,IAAI,kBAAkB,IAAW,IAAW,aAAa,iBAAiB,GACpF,IAAU,IAAI,kBAAkB,IAAgB,WAAW,iBAAiB,GAC5E,IAAQ,IAAI,kBAAkB,cAAc,iBAAiB,GAG7D,IAAO,IAAI,WAAW,CAAO;CAGnC,OAFA,QAAQ,MAAM,GAAM,GAAS,CAAC,GAEvB;EAAE;EAAU;EAAU;EAAM;EAAS;EAAS;EAAO;CAAS;AACtE;AAGA,SAAS,EAAO,GAAW,GAAmB;CAC7C,QAAS,IAAI,IAAK,KAAK,IAAI,IAAI;AAChC;AASA,SAAS,EAAK,GAAa,GAA0B;CACpD,OAAO,IAAO,IAAW;AAC1B;AAEA,IAAa,KAAb,MAAa,EAAiB;CAC7B;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CAGA,KAAY;CAKZ,KAAU;CAQV,KAAY;CACZ,KAAY;CAUZ,YAAY,GAA4B,GAA6B;EASpE,AARA,KAAK,WAAW,EAAK,UACrB,KAAK,WAAW,EAAK,UACrB,KAAK,OAAO,EAAK,MACjB,KAAK,WAAW,EAAK,UACrB,KAAK,OAAO,GAEZ,KAAKA,KAAW,IAAI,WAAW,EAAK,OAAO,GAC3C,KAAKC,KAAS,IAAI,cAAc,EAAK,KAAK,GAC1C,KAAKC,KAAW,CAAC;EACjB,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,UAAU,KAClC,KAAKA,GAAS,KACb,IAAI,aAAa,EAAK,SAAS,IAAI,KAAK,WAAW,aAAa,mBAAmB,KAAK,QAAQ,CACjG;EAGD,AAAI,MAAa,KAAA,KAAW,KAAKC,GAAS,CAAQ;CACnD;CAUA,GAAS,GAAgC;EACxC,IAAI,EAAO,aAAa,KAAK,YAAY,EAAO,SAAS,KAAK,MAAM;EAEpE,IAAM,IAAO,QAAQ,KAAK,EAAOF,IAAQ,CAAC;EAE1C,SAAS;GACR,IAAM,IAAQ,QAAQ,KAAK,KAAKA,IAAQ,CAAC;GAEzC,IADI,EAAQ,CAAK,MAAM,EAAQ,CAAI,MAC7B,EAAO,CAAI,IAAI,EAAO,CAAK,IAAK,MAAM,GAAG;GAE/C,IAAM,IAAO,EAAK,EAAQ,CAAK,GAAG,EAAO,CAAI,CAAC;GAC9C,IAAI,QAAQ,gBAAgB,KAAKA,IAAQ,GAAG,GAAO,CAAI,MAAM,GAAO;EACrE;CACD;CASA,GAAS,GAAyB;EACjC,SAAS;GACR,IAAM,IAAQ,QAAQ,KAAK,KAAKA,IAAQ,CAAC;GACzC,KAAM,IAAY,EAAO,CAAK,IAAK,MAAM,GAAG;GAE5C,IAAM,IAAO,EAAK,EAAQ,CAAK,GAAG,CAAS;GAC3C,IAAI,QAAQ,gBAAgB,KAAKA,IAAQ,GAAG,GAAO,CAAI,MAAM,GAAO;EACrE;CACD;CAMA,OAAO,GAAuB,GAA4B;EACzD,IAAI,EAAK,WAAW,KAAK,UAAU,MAAU,MAAM,0BAA0B;EAE7E,IAAI,IAAQ,KAAK,MAAM,EAAK,OAAO,UAAU,CAAS,IAAI,KAAK,IAAI,GAC7D,IAAiB,EAAK,EAAE,CAAC,QAC3B,IAAS;EAIb,IAAI,CAAC,KAAKG,IAAW;GACpB,KAAKC,KAAU;GAGf,IAAM,IAAQ,EAAQ,QAAQ,KAAK,KAAKJ,IAAQ,CAAC,CAAC;GAKlD,AAJA,QAAQ,MAAM,KAAKA,IAAQ,GAAG,EAAM,IAAQ,IAAK,GAAG,CAAC,CAAC,GACtD,QAAQ,MAAM,KAAKD,IAAU,GAAO,CAAC,GACrC,KAAKI,KAAY,IACjB,KAAKE,KAAY,GACjB,KAAKC,KAAY;EAClB;EAQA,IAAS,IAAQ,KAAKF,KAAW;EAEjC,IAAM,IAAO,IAAQ,IAAkB,GAGjC,IAAO,EAAO,QAAQ,KAAK,KAAKJ,IAAQ,CAAC,CAAC,GAC1C,IAAU,IAAO,IAAS;EAChC,IAAI,IAAS,GAAG;GACf,IAAI,KAAU,GAEb;GAGD,AADA,IAAS,GACT,IAAS,IAAQ,IAAU;EAC5B;EAEA,IAAM,IAAU,IAAiB;EAIjC,CAAM,IAAM,IAAQ,KAAK,KAAK,YAC7B,KAAKO,GAAU,IAAM,KAAK,WAAY,CAAC;EAIxC,IAAM,IAAQ,QAAQ,KAAK,KAAKR,IAAU,CAAK,GACzC,IAAO,IAAQ,IAAS;EAC9B,IAAI,IAAM,GAAG;GACZ,IAAM,IAAU,KAAK,IAAI,GAAK,KAAK,QAAQ;GAC3C,KAAK,IAAI,IAAU,GAAG,IAAU,KAAK,UAAU,KAAW;IACzD,IAAM,IAAM,KAAKE,GAAS;IAC1B,KAAK,IAAI,IAAI,GAAG,IAAI,GAAS,KAC5B,EAAI,EAAM,IAAQ,IAAK,GAAG,KAAK,QAAQ,KAAK;GAE9C;EACD;EAGA,KAAK,IAAI,IAAU,GAAG,IAAU,KAAK,UAAU,KAAW;GACzD,IAAM,IAAM,EAAK,IACX,IAAM,KAAKA,GAAS;GAC1B,KAAK,IAAI,IAAI,GAAG,IAAI,GAAS,KAC5B,EAAI,EAAM,IAAQ,IAAK,GAAG,KAAK,QAAQ,KAAK,EAAI,IAAS;EAE3D;EAGA,QAAQ,MAAM,KAAKF,IAAU,GAAO,EAAO,QAAQ,KAAK,KAAKA,IAAU,CAAK,GAAG,CAAG,CAAC;EAGnF,IAAM,IAAc,EAAO,QAAQ,KAAK,KAAKC,IAAQ,CAAC,CAAC,GACjD,IAAe,QAAQ,KAAK,KAAKD,IAAU,CAAK,GAChD,IAAU,QAAQ,KAAK,KAAKA,IAAU,CAAO;EACnD,CAAM,IAAe,IAAe,MAAM,KAAW,IAAU,KAC9D,QAAQ,MAAM,KAAKA,IAAU,GAAS,CAAC;CAEzC;CAMA,KAAK,GAAgC;EAMpC,IAAM,IAAQ,QAAQ,KAAK,KAAKC,IAAQ,CAAC;EACzC,IAAI,QAAQ,KAAK,KAAKD,IAAU,CAAO,MAAM,GAAG,OAAO;EAEvD,IAAI,IAAO,EAAO,CAAK,GACjB,IAAQ,QAAQ,KAAK,KAAKA,IAAU,CAAK,GACzC,IAAU,QAAQ,KAAK,KAAKA,IAAU,CAAO,GAK7C,IAAY,IAAQ,IAAQ;EAClC,IAAI,CAAC,KAAK,YAAY,IAAU,KAAK,IAAW,GAAS;GACxD,IAAM,IAAU,IAAQ,IAAW;GACnC,CAAM,IAAS,IAAQ,KAAK,MAAG,IAAO;EACvC;EAEA,IAAM,IAAa,IAAQ,IAAQ,GAC7B,IAAQ,KAAK,IAAI,GAAW,EAAO,EAAE,CAAC,MAAM;EAClD,IAAI,KAAS,GAKZ,QAHM,IAAO,EAAO,CAAK,IAAK,KAAK,KAClC,QAAQ,gBAAgB,KAAKC,IAAQ,GAAG,GAAO,EAAK,EAAQ,CAAK,GAAG,CAAI,CAAC,GAEnE;EAIR,KAAK,IAAI,IAAU,GAAG,IAAU,KAAK,UAAU,KAAW;GACzD,IAAM,IAAM,KAAKC,GAAS,IACpB,IAAM,EAAO;GACnB,KAAK,IAAI,IAAI,GAAG,IAAI,GAAO,KAC1B,EAAI,KAAK,EAAI,EAAM,IAAO,IAAK,GAAG,KAAK,QAAQ;EAEjD;EAKA,IAAM,IAAO,EAAK,EAAQ,CAAK,GAAI,IAAO,IAAS,CAAC;EACpD,IAAI,QAAQ,gBAAgB,KAAKD,IAAQ,GAAG,GAAO,CAAI,MAAM,GAAO;GAGnE,KAAK,IAAI,IAAU,GAAG,IAAU,KAAK,UAAU,KAAW,EAAO,EAAQ,CAAC,KAAK,GAAG,GAAG,CAAK;GAC1F,OAAO;EACR;EAEA,OAAO;CACR;CAGA,WAAW,GAAuB;EACjC,QAAQ,MAAM,KAAKD,IAAU,GAAS,CAAO;CAC9C;CASA,SAAS,GAA6B;EACrC,IAAM,IAAU,KAAK,MAAM,EAAK,OAAO,UAAU,CAAS,IAAI,KAAK,IAAI,IAAI,KAAKK,KAAW;EAC3F,SAAS;GACR,IAAM,IAAQ,QAAQ,KAAK,KAAKL,IAAU,CAAK;GAC/C,KAAM,IAAQ,IAAU,MAAM,GAAG;GAMjC,IAAM,IAAU,EAAO,GAAQ,EAAO,QAAQ,KAAK,KAAKC,IAAQ,CAAC,CAAC,CAAC;GAEnE,KADM,IAAQ,IAAW,MAAM,KAC3B,QAAQ,gBAAgB,KAAKD,IAAU,GAAO,GAAO,CAAO,MAAM,GAAO;EAC9E;CACD;CAMA,QAAc;EAEb,AADA,KAAKI,KAAY,IACjB,QAAQ,MAAM,KAAKJ,IAAU,GAAS,CAAC;EACvC,IAAM,IAAQ,QAAQ,KAAK,KAAKA,IAAU,CAAK,GACzC,IAAQ,QAAQ,KAAK,KAAKC,IAAQ,CAAC;EACzC,QAAQ,MAAM,KAAKA,IAAQ,GAAG,EAAK,EAAQ,CAAK,GAAG,CAAK,CAAC;CAC1D;CAcA,OAAO,GAAuC;EAC7C,IAAM,IAAO,EAAsB,KAAK,UAAU,GAAa,KAAK,MAAM,KAAK,QAAQ,GACjF,IAAM,IAAI,EAAiB,CAAI;EAErC,AADA,EAAIG,KAAY,KAAKA,IACrB,EAAIC,KAAU,KAAKA;EAEnB,IAAM,IAAQ,QAAQ,KAAK,KAAKJ,IAAQ,CAAC,GACnC,IAAO,EAAO,CAAK,GACnB,IAAQ,QAAQ,KAAK,KAAKD,IAAU,CAAK,GACzC,IAAU,QAAQ,KAAK,KAAKA,IAAU,CAAO,GAC7C,IAAU,QAAQ,KAAK,KAAKA,IAAU,CAAO,GAE7C,IAAa,IAAQ,IAAQ,GAC7B,IAAY,KAAK,IAAI,GAAG,KAAK,IAAI,GAAW,EAAI,QAAQ,CAAC,GACzD,IAAa,IAAQ,IAAa;EAExC,KAAK,IAAI,IAAU,GAAG,IAAU,KAAK,UAAU,KAAW;GACzD,IAAM,IAAM,KAAKE,GAAS,IACpB,IAAM,EAAIA,GAAS;GACzB,KAAK,IAAI,IAAI,GAAG,IAAI,GAAW,KAAK;IACnC,IAAM,IAAO,IAAY,IAAK;IAC9B,EAAI,EAAK,GAAK,EAAI,QAAQ,KAAK,EAAI,EAAK,GAAK,KAAK,QAAQ;GAC3D;EACD;EAaA,OAXA,QAAQ,MAAM,EAAID,IAAQ,GAAG,EAAK,EAAQ,CAAK,GAAG,CAAS,CAAC,GAC5D,QAAQ,MAAM,EAAID,IAAU,GAAO,CAAK,GACxC,QAAQ,MAAM,EAAIA,IAAU,GAAS,CAAO,GAC5C,QAAQ,MAAM,EAAIA,IAAU,GAAS,CAAO,GAK5C,EAAIM,KAAY,KAAKG,GAAU,CAAI,KAAM,IAAY,IAAQ,IAC7D,EAAIF,KAAY,GAET;CACR;CAMA,GAAU,GAAsB;EAG/B,OAFA,KAAKD,MAAc,IAAO,KAAKC,KAAa,GAC5C,KAAKA,KAAY,GACV,KAAKD;CACb;CAGA,KAAsB;EACrB,OAAO,KAAKG,GAAU,EAAO,QAAQ,KAAK,KAAKR,IAAQ,CAAC,CAAC,CAAC;CAC3D;CAQA,IAAI,YAAwB;EAC3B,OAAO,EAAK,MAAM,YAAa,KAAKI,KAAU,KAAKK,GAAY,KAAK,KAAK,IAAoB;CAC9F;CAGA,IAAI,UAAmB;EACtB,OAAO,QAAQ,KAAK,KAAKV,IAAU,CAAO,MAAM;CACjD;CASA,IAAI,SAAiB;EACpB,OAAQ,QAAQ,KAAK,KAAKA,IAAU,CAAK,IAAI,EAAO,QAAQ,KAAK,KAAKC,IAAQ,CAAC,CAAC,IAAK;CACtF;AACD,GCzcM,IAAN,MAAmB;CAClB;CACA;CACA,KAAkE,CAAC;CAEnE,YAAY,GAAkB,GAAsB;EAEnD,AADA,KAAKU,KAAW,GAChB,KAAKC,KAAY;CAClB;CAGA,YAAY,GAA4B;EACvC,KAAKA,KAAY;CAClB;CAEA,KAAK,GAAuB,GAAqC;EAGhE,OAFI,CAAC,KAAKD,MACN,MAAc,IAAY,KAAKC,KAAa,KAAW,QAAQ,QAAQ,IACpE,IAAI,SAAS,MAAY,KAAKC,GAAS,KAAK;GAAE;GAAW;EAAQ,CAAC,CAAC;CAC3E;CAIA,QAAQ,GAA4B;EAC/B,KAAKA,GAAS,WAAW,MAC7B,KAAKA,KAAW,KAAKA,GAAS,QAAQ,EAAE,cAAW,iBAC9C,KAAa,IAAY,KAAKD,KAAa,OAC/C,EAAQ,GACD,GACP;CACF;CAGA,QAAc;EACb,KAAK,IAAM,EAAE,gBAAa,KAAKC,IAAU,EAAQ;EACjD,KAAKA,KAAW,CAAC;CAClB;AACD;AAGA,SAAS,EAAe,GAAiB,GAA0B;CAClE,OAAO,EAAK,MAAM,WAAY,IAAU,CAAoB;AAC7D;AAoDA,SAAgB,IAAqC;CAKpD,OADA,EAHI,OAAO,oBAAsB,OAG7B,OAAO,sBAAwB,OAAe,CAAC;AAEpD;AAMA,SAAgB,GACf,GACA,GACA,GACA,GACA,IAAW,IACG;CASd,OARI,EAA0B,KAC7B,QAAQ,IAAI,8CAA8C,GACnD,IAAI,GAAkB,GAAS,GAAU,GAAM,GAAgB,CAAQ,MAE/E,QAAQ,KACP,+OAED,GACO,IAAI,GAAgB,GAAS,GAAU,GAAM,GAAgB,CAAQ;AAC7E;AAGA,IAAM,KAAN,MAA+C;CAC9C;CACA;CACA;CACA;CAEA,KAAsB,IAAI,EAAmB,CAAe;CAC5D,YAAyC,KAAKC;CAE9C,KAAoB,IAAI,EAAgB,EAAI;CAC5C,UAAoC,KAAKC;CAEzC;CAEA,KAAW,IAAI,EAAO;CAEtB,YAAY,GAA2B,GAAkB,GAAc,GAAwB,GAAmB;EAGjH,AAFA,KAAKC,KAAW,GAChB,KAAK,WAAW,GAChB,KAAK,OAAO;EAIZ,IAAM,IAAW,KAAK,IAAI,GAAM,IAAiB,CAAC;EAClD,KAAKC,KAAgB,IAAI,EAAa,GAAU,EAAe,GAAgB,CAAI,CAAC;EAEpF,IAAM,IAAO,EAAsB,GAAU,GAAU,GAAM,CAAQ;EAErE,AADA,KAAKC,KAAQ,IAAI,GAAiB,CAAI,GACtC,KAAKA,GAAM,WAAW,CAAc;EAEpC,IAAM,IAAkB;GAAE,MAAM;GAAe,GAAG;EAAK;EAIvD,AAHA,EAAQ,KAAK,YAAY,CAAG,GAG5B,KAAKC,GAAS,eAAe;GAC5B,IAAM,IAAU,KAAKD,GAAM;GAK3B,AAJA,KAAKJ,GAAW,IAAI,KAAKI,GAAM,SAAS,GACxC,KAAKH,GAAS,IAAI,CAAO,GAGrB,IAAS,KAAKE,GAAc,MAAM,IACjC,KAAKA,GAAc,QAAQ,KAAKC,GAAM,SAAS;EACrD,GAAG,EAAE;CACN;CAEA,OAAO,GAAuB,GAA4B;EACzD,KAAKA,GAAM,OAAO,GAAW,CAAI;CAClC;CAEA,WAAW,GAAuB;EAIjC,IAHA,KAAKD,GAAc,YAAY,EAAe,GAAS,KAAK,IAAI,CAAC,GAG7D,KAAKC,GAAM,WAAW,IAAU,KAAK;GACxC,IAAM,IAAc,KAAK,IAAI,KAAK,MAAM,IAAU,CAAC;GAEnD,AADA,KAAKA,KAAQ,KAAKA,GAAM,OAAO,CAAW,GAC1C,KAAKA,GAAM,WAAW,CAAO;GAE7B,IAAM,IAAkB;IAAE,MAAM;IAAe,GAAG,KAAKA,GAAM;GAAK;GAClE,KAAKF,GAAS,KAAK,YAAY,CAAG;EACnC,OACC,KAAKE,GAAM,WAAW,CAAO;CAE/B;CAEA,SAAS,GAA6B;EACrC,KAAKA,GAAM,SAAS,CAAS;CAC9B;CAEA,QAAc;EAEb,AADA,KAAKA,GAAM,MAAM,GACjB,KAAKD,GAAc,MAAM;CAC1B;CAEA,KAAK,GAAsC;EAG1C,OADI,KAAKC,GAAM,UAAgB,QAAQ,QAAQ,IACxC,KAAKD,GAAc,KAAK,GAAW,KAAKC,GAAM,SAAS;CAC/D;CAEA,QAAc;EAEb,AADA,KAAKD,GAAc,MAAM,GACzB,KAAKE,GAAS,MAAM;CACrB;AACD,GAGM,KAAN,MAA6C;CAC5C;CACA;CACA;CAEA,KAAsB,IAAI,EAAmB,CAAe;CAC5D,YAAyC,KAAKL;CAE9C,KAAoB,IAAI,EAAgB,EAAI;CAC5C,UAAoC,KAAKC;CAGzC;CAEA,KAAW,IAAI,EAAO;CAEtB,YAAY,GAA2B,GAAkB,GAAc,GAAwB,GAAmB;EAKjH,AAJA,KAAKC,KAAW,GAChB,KAAK,WAAW,GAChB,KAAK,OAAO,GAEZ,KAAKC,KAAgB,IAAI,EAAa,GAAU,EAAe,GAAgB,CAAI,CAAC;EAGpF,IAAM,IAAgB;GAAE,MAAM;GAAa;GAAU;GAAM,SAD3C,EAAK,MAAM,WAAY,IAAiB,CACG;GAAS;EAAS;EAgB7E,AAfA,EAAQ,KAAK,YAAY,CAAG,GAG5B,KAAKE,GAAS,MAAM,EAAQ,MAAM,YAAY,MAAc;GAC3D,IAAM,IAAQ,EAA2B;GACzC,AAAI,GAAM,SAAS,YAClB,KAAKL,GAAW,IAAI,EAAK,SAAS,GAClC,KAAKC,GAAS,IAAI,EAAK,OAAO,GAG1B,EAAK,UAAS,KAAKE,GAAc,MAAM,IACtC,KAAKA,GAAc,QAAQ,EAAK,SAAS;EAEhD,CAAC,GAED,EAAQ,KAAK,MAAM;CACpB;CAEA,OAAO,GAAuB,GAA4B;EACzD,IAAM,IAAY;GAAE,MAAM;GAAQ;GAAM;EAAU;EAGlD,KAAKD,GAAS,KAAK,YAClB,GACA,EAAK,KAAK,MAAM,EAAE,MAAM,CACzB;CACD;CAEA,WAAW,GAAuB;EACjC,KAAKC,GAAc,YAAY,EAAe,GAAS,KAAK,IAAI,CAAC;EAGjE,IAAM,IAAe;GAAE,MAAM;GAAW,SADxB,EAAK,MAAM,WAAY,IAAU,KAAK,IACd;EAAQ;EAChD,KAAKD,GAAS,KAAK,YAAY,CAAG;CACnC;CAEA,SAAS,GAA6B;EACrC,IAAM,IAAgB;GAAE,MAAM;GAAY;EAAU;EACpD,KAAKA,GAAS,KAAK,YAAY,CAAG;CACnC;CAEA,QAAc;EAGb,AADA,KAAKA,GAAS,KAAK,YAAY,EADV,MAAM,QACI,CAAG,GAClC,KAAKC,GAAc,MAAM;CAC1B;CAEA,KAAK,GAAsC;EAK1C,OAHI,KAAKF,GAAS,KAAK,IAAU,QAAQ,QAAQ,IAG1C,KAAKE,GAAc,KAAK,GAAW,KAAKH,GAAW,KAAK,CAAC;CACjE;CAEA,QAAc;EAEb,AADA,KAAKG,GAAc,MAAM,GACzB,KAAKE,GAAS,MAAM;CACrB;AACD,GC7Sa,KAAb,MAAsB;CACrB,KAAQ;CACR,KAAW;CAGX,SAAe;EAEd,AADI,KAAKC,OAAO,KAAKC,KAAW,KAChC,KAAKD,KAAQ;CACd;CAGA,WAAoB;EACnB,IAAM,IAAU,KAAKC;EAErB,OADA,KAAKA,KAAW,IACT;CACR;AACD,GCvBM,KAAO,IAAI,KAAK,CAAC,u4xBAAI,GAAG,EAAE,MAAM,yBAAyB,CAAC,GAChE,KAAe,IAAI,gBAAgB,EAAI;;;ACIvC,SAAgB,GACf,GACA,EACC,cACA,UACA,iBAMsB;CACvB,IAAM,IAAW,EAAQ,SAAS,OAC9B,IAAa,KAAK,KAAK,EAAW,KAAK,CAAC,GACtC,IAAa,EAAU,MAAM,CAAK,CAAC,CAAC,UAAU;EAAE;EAAU;CAAW,CAAC;CAU5E,OATA,EAAO,cAAc,EAAW,MAAM,CAAC,GAEvC,EAAO,KAAK,MAAU;EACrB,IAAM,IAAO,KAAK,KAAK,EAAM,IAAI,CAAU,CAAC;EACxC,MAAS,MACb,IAAa,GACb,EAAW,OAAO;GAAE;GAAU;EAAW,CAAC;CAC3C,CAAC,GAEM;AACR;;;ACNA,IAAa,KAAb,MAAsB;CACrB,KAAiB;CACjB;CACA;CACA,KAAW;CACX;CAGA,IAAI,MAA8B;EACjC,OAAO,KAAKC;CACb;CAGA,MAAM,IAAU,GAAS;EAIxB,AAHA,KAAKC,KAAiB,GACtB,KAAKD,KAAO,KAAA,GACZ,KAAKE,KAAW,GAChB,KAAKC,GAAY;CAClB;CAGA,OAAO,GAAuB;EAC7B,IAAM,IAAQ,EAAK,kBAAkB,KAAKF;EAQ1C,OAPI,MACH,KAAKA,KAAiB,EAAK,eAC3B,KAAKD,KAAO,KAAA,GACZ,KAAKG,GAAY,IAEd,EAAK,SAAS,KAAKC,OAAW,KAAA,MAAW,KAAKA,KAAS,EAAK,MAAM,YAClE,EAAK,QAAQ,KAAA,MAAW,KAAKJ,KAAO,EAAK,MACtC;CACR;CAGA,KAAK,GAAiC;EACrC,IAAM,IAAQ,KAAKI,MAAW,EAAO;EACrC,KAAKA,KAAS;EAEd,IAAM,IAAc,KAAK,MAAO,KAAKF,KAAW,EAAO,aAAc,IAAM,GACrE,IAAY,KAAKG,MAAqB,GACtC,IAAc,KAAK,IAAI,GAAW,EAAO,cAAc;EAC7D,KAAKA,KAAoB,IAAY;EAErC,IAAM,IAAQ,KAAK,MAAO,IAAc,MAAa,EAAO,UAAU,GAChE,IAAY,KAAK,IAAI,GAAO,EAAO,YAAY,CAAK,GACtD,IAAS,EAAO,iBAAiB;EACrC,IAAI,KAAKL,OAAS,KAAA,GAAW;GAC5B,IAAI,KAAKA,MAAQ,GAChB,IAAS;QACH;IACN,IAAM,IAAW,KAAKA,KAAO,GACvB,IAAiB,KAAK,MAAO,IAAW,EAAO,aAAc,GAAS;IAC5E,IAAS,KAAK,IAAI,GAAQ,CAAc;GACzC;EACD;EAEA,OAAO;GAAE;GAAW;GAAa;EAAO;CACzC;CAEA,KAAoB;EAEnB,AADA,KAAKI,KAAS,KAAA,GACd,KAAKC,KAAoB,KAAA;CAC1B;AACD;;;ACrEA,SAAgB,GAAgB,GAAgB,GAA6B;CAC5E,IAAM,IAAU,IAAI,EAAO,EAAQ,UAAU,SAAS;CAGtD,AAFA,EAAO,MAAM,GAAS,qBAAqB,EAAQ,IAAI,EAAQ,UAAU,SAAS,CAAC,GAEnF,EAAO,KAAK,MAAU;EACrB,IAAI,EAAM,IAAI,CAAO,GAAG;EAExB,IAAM,UAAe;GACpB,EAAQ,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC;EAChC;EAIA,AAFA,EAAO,GACP,EAAM,MAAM,UAAU,eAAe,CAAM,GAC3C,EAAM,MAAM,UAAU,WAAW,CAAM;CACxC,CAAC;AACF;;;ACjCA,IAAa,KAAb,MAAoB;CACnB;CAGA,YAAY,GAAmB;EAC9B,KAAKC,KAAa;CACnB;CAGA,OAAgB;EAGf,OAFI,KAAKA,OAAe,MACxB,KAAKA,MACE;CACR;AACD,GCMM,KAA+B,KAC/B,KAA0B,GAsCnB,KAAb,MAAqB;CACpB;CACA;CACA;CAEA,KAA+B;EAC9B,SAAS,IAAI,EAAiC,KAAA,CAAS;EACvD,MAAM,IAAI,EAA8B,KAAA,CAAS;EACjD,YAAY,IAAI,EAA2B,KAAA,CAAS;EACpD,OAAO,IAAI,EAA0B,KAAA,CAAS;EAC9C,WAAW,IAAI,EAA+B,KAAA,CAAS;EACvD,SAAS,IAAI,EAAgB,EAAI;EACjC,UAAU,IAAI,EAAiC,CAAC,CAAC;CAClD;CACA,MAAe,EAAU,KAAKC,EAAI;CAGlC,KAAkB,IAAI,EAAiC,CAAC,CAAC;CAGzD;CAMA,KAAqB,IAAI,EAA2B,KAAA,CAAS;CAG7D,KAAY,IAAI,GAAS;CAIzB;CAGA,KAAY,IAAI,GAAS;CAEzB,KAAW,IAAI,EAAO;CAEtB,YAAY,GAAgB,GAAY,GAA8B;EAYrE,AAXA,KAAK,KAAK,EACT,SAAS,EAAO,GAAO,WAAW,EAAK,EACxC,GAEA,KAAK,SAAS,GACd,KAAK,OAAO,GAEZ,KAAKC,GAAS,IAAI,KAAKC,GAAY,KAAK,IAAI,CAAC,GAC7C,KAAKD,GAAS,IAAI,KAAKE,GAAY,KAAK,IAAI,CAAC,GAC7C,KAAKF,GAAS,IAAI,KAAKG,GAAY,KAAK,IAAI,CAAC,GAC7C,KAAKH,GAAS,IAAI,KAAKI,GAAoB,KAAK,IAAI,CAAC,GACrD,KAAKJ,GAAS,IAAI,KAAKK,GAAY,KAAK,IAAI,CAAC;CAC9C;CAEA,GAAY,GAAsB;EAOjC,IAAM,IAAS,EAAO,IAAI,KAAK,OAAO,IAAI,MAAM;EAChD,IAAI,CAAC,GAAQ;EAKb,IAAM,IAAa,EAAO,IAAI,KAAKC,EAAkB,KAAK,EAAO,YAC3D,IAAe,EAAO;EAG5B,EAAO,IAAI,KAAKP,GAAK,YAAY,CAAU;EAE3C,IAAM,IAAU,IAAI,aAAa;GAChC,aAAa;GACb;EACD,CAAC;EAKD,AAJA,EAAO,IAAI,KAAKA,GAAK,SAAS,CAAO,GAErC,EAAO,cAAc,EAAQ,MAAM,CAAC,GAEpC,EAAO,MAAM,YAAY;GAUxB,IAAI,CAAC,MAJgB,QAAQ,KAAK,CACjC,EAAQ,aAAa,UAAU,EAAa,CAAC,CAAC,WAAW,EAAI,GAC7D,EAAO,MACR,CAAC,GACY;GAKb,IAAM,IAAU,IAAI,iBAAiB,GAAS,UAAU;IACvD;IACA,kBAAkB;IAClB,oBAAoB,CAAC,CAAY;GAClC,CAAC;GACD,EAAO,cAAc,EAAQ,WAAW,CAAC;GAGzC,IAAM,IAAU,KAAK,KAAK,IAAI,OAAO,KAAK,GACpC,IAAiB,KAAK,KAAK,IAAa,EAAK,OAAO,UAAU,CAAO,CAAC,GACtE,IAAW,KAAK,KAAK,IAAI,SAAS,KAAK,GAGvC,IAAO,GAAkB,GAAS,GAAc,GAAY,GAAgB,CAAQ;GAiB1F,AAhBA,KAAKQ,KAAQ,GACb,EAAO,cAAc;IAEpB,AADA,EAAK,MAAM,GACX,KAAKA,KAAQ,KAAA;GACd,CAAC,GAGD,EAAO,KAAK,MAAU;IACrB,IAAM,IAAK,EAAK,MAAM,UAAU,EAAM,IAAI,EAAK,SAAS,CAAC;IAEzD,AADA,KAAKR,GAAK,UAAU,IAAI,CAAE,GAC1B,KAAKS,GAAoB,CAAE;GAC5B,CAAC,GACD,EAAO,KAAK,MAAU;IACrB,KAAKT,GAAK,QAAQ,IAAI,EAAM,IAAI,EAAK,OAAO,CAAC;GAC9C,CAAC,GAED,EAAO,IAAI,KAAKA,GAAK,MAAM,CAAO;EACnC,CAAC;CACF;CAEA,GAAY,GAAsB;EAEjC,IAAI,CADY,EAAO,IAAI,KAAK,GAAG,OAC9B,GAAS;EAEd,IAAM,IAAU,EAAO,IAAI,KAAKA,GAAK,OAAO;EACvC,KAIL,GAAgB,GAAQ,CAAO;CAGhC;CAEA,GAAY,GAAsB;EAGjC,IAAI,CADY,EAAO,IAAI,KAAKA,GAAK,IAChC,GAAS;EAEd,IAAM,IAAO,KAAKQ;EAClB,IAAI,CAAC,GAAM;EAEX,IAAM,IAAU,EAAO,IAAI,KAAK,KAAK,IAAI,MAAM,GACzC,IAAiB,KAAK,KAAK,EAAK,OAAO,EAAK,OAAO,UAAU,CAAO,CAAC;EAC3E,EAAK,WAAW,CAAc;CAC/B;CAUA,GAAoB,GAAsB;EACzC,IAAM,IAAQ,EAAc,EAAO,IAAI,KAAK,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC;EAC9D,IAAI,KAAKE,OAAe,KAAA,GAAW;GAElC,KAAKA,KAAa;GAClB;EACD;EAGA,IAAM,IAAW,KAAKA;EACtB,EAAO,YAAY;GAClB,IAAM,KAAQ,MAAsB,MAAM,cAAc,IAAI;GAE5D,AADI,EAAK,CAAK,IAAI,EAAK,CAAQ,KAAG,KAAK,MAAM,GAC7C,KAAKA,KAAa;EACnB,GAAG,EAA4B;CAChC;CAEA,GAAY,GAAsB;EAEjC,IAAI,CADY,EAAO,IAAI,KAAK,GAAG,OAC9B,GAAS;EAEd,IAAM,IAAY,EAAO,IAAI,KAAK,OAAO,GAAG,SAAS;EACrD,IAAI,CAAC,GAAW;EAEhB,IAAM,IAAQ,EAAO,IAAI,KAAK,OAAO,IAAI,KAAK;EAC9C,IAAI,CAAC,GAAO;EAEZ,IAAM,IAAS,EAAO,IAAI,KAAK,OAAO,IAAI,MAAM;EAChD,IAAI,CAAC,GAAQ;EAIb,IAAM,IAAS,EAAU,kBAAkB,GAAQ,EAAO,SAAS;EACnE,IAAI,CAAC,GAAQ;EAOb,KAAKC,GAAU,OAAO;EAItB,IAAM,IAAM,GAAU,GAAQ;GAAE,WAAW;GAAQ;GAAO,YAAY,KAAK,KAAK,IAAI;EAAU,CAAC;EAE/F,AAAI,EAAO,UAAU,SAAS,SAC7B,KAAKC,GAAgB,GAAQ,GAAK,CAAM,IAExC,KAAKC,GAAkB,GAAQ,GAAK,CAAM;CAE5C;CAEA,GAAkB,GAAgB,GAA2B,GAAmC;EAC/F,IAAM,IACL,EAAO,UAAU,UAAU,EAAO,cAAc,EAAK,KAAK,QAAQ,EAAK,IAAI,QAAQ,EAAO,WAAW,CAAC,IAAI;EAC3G,KAAKC,GAAU,MAAM,CAAO;EAC5B,IAAM,IAAS,EAAO,UAAU,SAAS,QAAQ,IAAI,EAAU,IAAI,OAAO,IAAI,IAAI,EAAU,OAAO,OAAO,GAGpG,IAAW,IAAI,EAAU,SAAS,GAAK;GAC5C;GACA,SAAS,KAAK,KAAK,IAAI;EACxB,CAAC;EAUD,AATA,EAAO,cAAc,EAAS,MAAM,CAAC,GAGrC,EAAO,KAAK,MAAU;GACrB,IAAM,IAAU,EAAM,IAAI,EAAS,QAAQ,GACrC,IAAS,EAAM,IAAI,KAAKC,EAAe;GAC7C,KAAKf,GAAK,SAAS,aAAa,EAAU,oBAAoB,GAAS,CAAM,CAAC;EAC/E,CAAC,GAED,EAAO,MAAM,YAAY;GAExB,IAAI,CAAC,MADgB,EAAK,MAAM,SAAS,GAC5B;GAEb,IAAM,IAAS,IAAI,GAAO,EAAuB,GAE3C,IAAU,IAAI,aAAa;IAChC,SAAS,MAAS;KACjB,IAAM,IAAU,KAAKc,GAAU,KAAK,CAAI;KACxC,IAAI,EAAO,KAAK,GAAG;MAElB,EAAK,MAAM;MACX;KACD;KACA,KAAKE,GAAM,GAAM,CAAO;IACzB;IACA,QAAQ,MAAU,QAAQ,MAAM,uBAAuB,CAAK;GAC7D,CAAC;GACD,EAAO,cAAc;IACpB,AAAI,EAAQ,UAAU,YAAU,EAAQ,MAAM;GAC/C,CAAC;GAGD,IAAM,IACL,EAAO,UAAU,SACd,KAAA,IACA,EAAO,cACN,EAAK,IAAI,QAAQ,EAAO,WAAW,IACnC,KAAA,GACC,IAAoC;IACzC,GAAG;IACH;GACD;GAGA,KAFA,EAAQ,UAAU,CAAa,KAEtB;IACR,IAAM,IAAO,MAAM,EAAS,KAAK;IACjC,IAAI,CAAC,GAAM;IAKX,IAJI,KAAKC,GAAQ,CAAI,MACpB,EAAQ,MAAM,GACd,EAAQ,UAAU,CAAa,IAE5B,EAAK,QAAQ,KAAA,GAChB;IAGD,IAAM,EAAE,aAAU;IAClB,IAAI,CAAC,GAAO;IAGZ,IAAM,IAAY,EAAK,MAAM,UAAU,EAAM,SAAuB;IASpE,AARA,KAAK,KAAK,SAAS,GAAW,OAAO,GAErC,KAAKjB,GAAK,MAAM,QAAQ,OAAW,EAClC,gBAAgB,GAAO,iBAAiB,KAAK,EAAM,QAAQ,WAC5D,EAAE,GAIF,MAAM,KAAKQ,IAAO,KAAK,EAAM,SAAuB;IAEpD,IAAM,IAAQ,IAAI,kBAAkB;KACnC,MAAM,EAAM,WAAW,QAAQ;KAC/B,MAAM,EAAM;KACZ,WAAW,EAAM;IAClB,CAAC;IAID,IAAI,EAAQ,UAAU,UAAU;IAChC,EAAQ,OAAO,CAAK;GACrB;EACD,CAAC;CACF;CAEA,GAAgB,GAAgB,GAA2B,GAAmC;EAC7F,IAAI,EAAO,UAAU,SAAS,QAAQ;EAEtC,IAAM,IAAc,EAAc,EAAO,UAAU,IAAI,GACjD,IAAO,EAAU,KAAK,kBAAkB,CAAW,GACnD,IAAkB,EAAO,cAAc,EAAK,IAAI,QAAQ,EAAO,WAAW,IAAI,EAAK,aACnF,IAAU,EAAO,UAAU,UAAU,IAAkB,EAAK,KAAK,QAAQ,CAAe,IAAI;EAClG,KAAKM,GAAU,MAAM,CAAO;EAG5B,IAAM,IACL,EAAO,UAAU,SACd,KAAA,IACA,EAAO,cACN,EAAK,IAAI,QAAQ,EAAO,WAAW,IACnC,EAAK,aAEJ,IAAW,IAAI,EAAU,SAAS,GAAK;GAC5C,QAAQ,IAAI,EAAU,KAAK,OAAO,CAAI;GACtC,SAAS,KAAK,KAAK,IAAI;EACxB,CAAC;EAUD,AATA,EAAO,cAAc,EAAS,MAAM,CAAC,GAGrC,EAAO,KAAK,MAAU;GACrB,IAAM,IAAU,EAAM,IAAI,EAAS,QAAQ,GACrC,IAAS,EAAM,IAAI,KAAKC,EAAe;GAC7C,KAAKf,GAAK,SAAS,aAAa,EAAU,oBAAoB,GAAS,CAAM,CAAC;EAC/E,CAAC,GAED,EAAO,MAAM,YAAY;GAExB,IAAI,CAAC,MADgB,EAAK,MAAM,SAAS,GAC5B;GAEb,IAAM,IAAU,IAAI,aAAa;IAChC,SAAS,MAAS,KAAKgB,GAAM,CAAI;IACjC,QAAQ,MAAU,QAAQ,MAAM,uBAAuB,CAAK;GAC7D,CAAC;GACD,EAAO,cAAc;IACpB,AAAI,EAAQ,UAAU,YAAU,EAAQ,MAAM;GAC/C,CAAC;GAGD,IAAM,IAAoC;IACzC,OAAO,EAAO;IACd,YAAY,EAAO;IACnB,kBAAkB,EAAO;IACzB;GACD;GAGA,KAFA,EAAQ,UAAU,CAAa,KAEtB;IACR,IAAM,IAAO,MAAM,EAAS,KAAK;IACjC,IAAI,CAAC,GAAM;IAGX,AAAI,KAAKC,GAAQ,CAAI,MACpB,EAAQ,MAAM,GACd,EAAQ,UAAU,CAAa;IAGhC,IAAM,EAAE,aAAU;IAClB,IAAI,CAAC,GAAO;IAEZ,IAAM,IAAY,EAAK,MAAM,UAAU,EAAM,SAAS;IAWtD,IAVA,KAAK,KAAK,SAAS,GAAW,OAAO,GAErC,KAAKjB,GAAK,MAAM,QAAQ,OAAW,EAClC,gBAAgB,GAAO,iBAAiB,KAAK,EAAM,QAAQ,WAC5D,EAAE,GAIF,MAAM,KAAKQ,IAAO,KAAK,EAAM,SAAS,GAElC,EAAQ,UAAU,UAAU;IAChC,EAAQ,OACP,IAAI,kBAAkB;KACrB,MAAM,EAAM,WAAW,QAAQ;KAC/B,MAAM,EAAM;KACZ,WAAW,EAAM;IAClB,CAAC,CACF;GACD;EACD,CAAC;CACF;CAEA,GAAM,GAAmB,IAAuB,KAAKM,GAAU,KAAK,CAAM,GAAG;EAC5E,IAAM,EAAE,cAAW,gBAAa,cAAW,GACrC,IAAiB,EAAK,MAAM,UAAU,CAAS;EACrD,IAAI,MAAW,GAAG;GACjB,EAAO,MAAM;GACb;EACD;EAEA,IAAM,IAAO,KAAKN;EAClB,IAAI,CAAC,GAAM;GAEV,EAAO,MAAM;GACb;EACD;EAMA,IAAI,EAAO,eAAe,EAAK,MAAM;GAEpC,AADA,KAAKD,GAAmB,IAAI,EAAO,UAAU,GAC7C,EAAO,MAAM;GACb;EACD;EAGA,IAAM,IAAkB,IAAS,EAAO,aAAc,KAChD,IAAgB,EAAK,MAAM,UAAU,CAAa,GAClD,IAAM,EAAK,MAAM,IAAI,GAAgB,CAAa;EAUxD,AANI,KAAKI,GAAU,SAAS,MAC3B,EAAK,SAAS,CAAS,GACvB,KAAKO,GAAwB,CAAc,IAI5C,KAAKC,GAAmB,GAAgB,CAAG;EAI3C,IAAM,IAAW,KAAK,IAAI,EAAO,kBAAkB,EAAK,QAAQ,GAC1D,IAA8B,CAAC;EACrC,KAAK,IAAI,IAAU,GAAG,IAAU,GAAU,KAAW;GACpD,IAAM,IAAO,IAAI,aAAa,CAAM;GAEpC,AADA,EAAO,OAAO,GAAM;IAAE,QAAQ;IAAc,YAAY;IAAS;IAAa,YAAY;GAAO,CAAC,GAClG,EAAY,KAAK,CAAI;EACtB;EAMA,AAFA,EAAK,OAAO,GAAW,CAAW,GAElC,EAAO,MAAM;CACd;CAEA,GAAmB,GAAmB,GAAuB;EACxD,IAAQ,KAEZ,KAAKJ,GAAgB,QAAQ,MAAY;GACxC,KAAK,IAAM,KAAS,GAEnB,IAAI,KAAS,EAAM,MAAM,KAAK,KAAO,EAAM,OAAO;IAEjD,AADA,EAAM,QAAQ,EAAK,MAAM,IAAI,EAAM,OAAO,CAAK,GAC/C,EAAM,MAAM,EAAK,MAAM,IAAI,EAAM,KAAK,CAAG;IACzC;GACD;GAID,AADA,EAAQ,KAAK;IAAE;IAAO;GAAI,CAAC,GAC3B,EAAQ,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;EACzC,CAAC;CACF;CAGA,GAAwB,GAA6B;EACpD,KAAKA,GAAgB,QAAQ,MAAY;GACxC,OAAO,EAAQ,SAAS,KAAK,EAAQ,EAAQ,SAAS,EAAE,CAAC,SAAS,IAAW,EAAQ,IAAI;GACzF,IAAM,IAAO,EAAQ,EAAQ,SAAS;GACtC,AAAI,KAAQ,EAAK,MAAM,MAAW,EAAK,MAAM;EAC9C,CAAC;CACF;CAEA,GAAoB,GAA6B;EAChD,KAAKA,GAAgB,QAAQ,MAAY;GACxC,OAAO,EAAQ,SAAS,IAAG;IAC1B,IAAI,EAAQ,EAAE,CAAC,OAAO,GAAW;KAChC,EAAQ,EAAE,CAAC,QAAQ,EAAK,MAAM,IAAI,EAAQ,EAAE,CAAC,OAAO,CAAS;KAC7D;IACD;IACA,EAAQ,MAAM;GACf;EACD,CAAC;CACF;CAIA,QAAc;EACb,KAAKP,IAAO,MAAM;CACnB;CAIA,GAAQ,GAA+F;EAItG,OAHK,KAAKM,GAAU,OAAO,CAAI,KAC/B,KAAKN,IAAO,MAAM,GAClB,KAAK,KAAK,MAAM,GACT,MAHkC;CAI1C;CAEA,QAAQ;EACP,KAAKP,GAAS,MAAM;CACrB;CAGA,OAAO,YAAY;AACpB;AAEA,eAAe,GAAU,GAA+C;CACvE,IAAI,CAAC,EAAQ,mBAAmB,EAAO,SAAS,GAAG;EAElD,IAAM,IAAO,EAAO,UAAU,SAAS,YAAY,EAAO,UAAU,IAAI,OAAO,EAAO,UAAU;EAEhG,OADA,QAAQ,KAAK,qDAAqD,GAAM,GACjE;CACR;CAKA,AAAI,EAAO,UAAU,UAAU,CAAC,EAAK,KAAK,aAAa,EAAO,UAAU,KACvE,QAAQ,KAAK,6BAA6B,EAAO,WAAW,sCAAsC;CAInG,IAAI;CACJ,IAAI,EAAO,UAAU,QAAQ;EAC5B,IAAI,EAAO,aACV,IAAc,EAAK,IAAI,QAAQ,EAAO,WAAW;OAC3C,IAAI,EAAO,UAAU,SAAS,QACpC,IAAI;GACH,IAAc,EAAU,KAAK,kBAAkB,EAAc,EAAO,UAAU,IAAI,CAAC,CAAC,CAAC;EACtF,SAAS,GAAK;GAMb,OADA,QAAQ,KAAK,gDAAgD,EAAO,SAAS,CAAG,GACzE;EACR;CAEF;CAKA,QAAO,MAJW,aAAa,kBAAkB;EAChD,GAAG;EACH;CACD,CAAC,EAAA,CACU,aAAa;AACzB;;;ACvmBA,IAAM,IAAW,MACX,IAAY,IAmBL,IAAb,MAAqB;CACpB;CAEA;CAEA,KAA+B,EAC9B,SAAS,IAAI,EAAgB,EAAK,EACnC;CACA,MAAe,EAAU,KAAKmB,EAAI;CAElC,KAAW,IAAI,EAAO;CAGtB,KAAQ,IAAI,EAA6B,KAAA,CAAS;CAElD,YAAY,GAAiB,GAA8B;EAqD1D,AApDA,KAAK,SAAS,GACd,KAAK,KAAK;GACT,QAAQ,EAAO,GAAO,UAAU,EAAG;GACnC,OAAO,EAAO,GAAO,SAAS,EAAK;GACnC,QAAQ,EAAO,GAAO,UAAU,EAAK;EACtC,GAOA,KAAKC,GAAS,KAAK,MAAW;GAC7B,IAAM,IAAU,CAAC,EAAO,IAAI,KAAK,GAAG,MAAM;GAC1C,KAAKD,GAAK,QAAQ,IAAI,CAAO;EAC9B,CAAC,GAED,KAAKC,GAAS,KAAK,MAAW;GAC7B,IAAM,IAAO,EAAO,IAAI,KAAK,OAAO,IAAI,IAAI;GAC5C,IAAI,CAAC,GAAM;GAEX,IAAM,IAAO,IAAI,SAAS,EAAK,SAAS,EAAE,MAAM,EAAO,IAAI,KAAK,GAAG,MAAM,EAAE,CAAC;GAK5E,AAJA,EAAK,QAAQ,CAAI,GAEjB,EAAO,IAAI,KAAKC,IAAO,CAAI,GAE3B,EAAO,KAAK,MAAU;IAGL,EAAM,IAAI,KAAKF,GAAK,OAC/B,MAEL,EAAK,QAAQ,EAAK,QAAQ,WAAW,GACrC,EAAM,cAAc,EAAK,WAAW,CAAC;GACtC,CAAC;EACF,CAAC,GAOD,KAAKC,GAAS,KAAK,MAAW;GAC7B,IAAM,IAAO,EAAO,IAAI,KAAK,OAAO,IAAI,IAAI;GAE5C,IADI,CAAC,KACD,EAAO,IAAI,KAAK,GAAG,KAAK,GAAG;GAG/B,IAAM,IAAU,EAAK;GACrB,AAAI,EAAQ,UAAU,eAAa,EAAa,OAAO;EACxD,CAAC,GAED,KAAKA,GAAS,KAAK,MAAW;GAC7B,IAAM,IAAO,EAAO,IAAI,KAAKC,EAAK;GAClC,IAAI,CAAC,GAAM;GAGX,EAAO,cAAc,EAAK,KAAK,sBAAsB,EAAK,QAAQ,WAAW,CAAC;GAE9E,IAAM,IAAS,EAAO,IAAI,KAAK,GAAG,MAAM;GACxC,AAAI,IAAS,KACZ,EAAK,KAAK,6BAA6B,GAAU,EAAK,QAAQ,cAAc,CAAS,GACrF,EAAK,KAAK,eAAe,GAAG,EAAK,QAAQ,cAAc,IAAY,GAAI,KAEvE,EAAK,KAAK,6BAA6B,GAAQ,EAAK,QAAQ,cAAc,CAAS;EAErF,CAAC;CACF;CAEA,QAAQ;EACP,KAAKD,GAAS,MAAM;CACrB;AACD,GCxGM,IAAkB,KAuCX,IAAb,MAAoB;CACnB;CAEA,KAA8B;EAC7B,SAAS,IAAI,EAAkC,KAAA,CAAS;EACxD,WAAW,IAAI,EAA4C,CAAC,CAAC;EAC7D,OAAO,IAAI,EAA2B,KAAA,CAAS;EAC/C,QAAQ,IAAI,EAAwC,KAAA,CAAS;EAC7D,QAAQ,IAAI,EAAmC,KAAA,CAAS;CACzD;CACA,MAAe,EAAU,KAAKE,EAAI;CAElC,KAAW,IAAI,EAAO;CAEtB,YAAY,GAA6B;EASxC,AARA,KAAK,KAAK;GACT,WAAW,EAAO,GAAO,SAAS;GAClC,QAAQ,EAAO,GAAO,MAAM;GAC5B,WAAW,EAAO,GAAO,SAAS;EACnC,GAEA,KAAKC,GAAS,IAAI,KAAKC,GAAY,KAAK,IAAI,CAAC,GAC7C,KAAKD,GAAS,IAAI,KAAKE,GAAc,KAAK,IAAI,CAAC,GAC/C,KAAKF,GAAS,IAAI,KAAKG,GAAa,KAAK,IAAI,CAAC;CAC/C;CAEA,GAAY,GAAsB;EACjC,IAAM,IAAY,EAAO,IAAI,KAAK,GAAG,SAAS;EAC9C,IAAI,CAAC,GAAW;EAEhB,IAAM,IAAU,EAAO,IAAI,EAAU,IAAI,OAAO,CAAC,EAAE;EAC9C,KAEL,EAAO,IAAI,KAAKJ,GAAK,SAAS,CAAO;CACtC;CAEA,GAAc,GAAsB;EACnC,IAAM,IAAa,EAAO,IAAI,KAAKA,GAAK,OAAO,CAAC,EAAE,cAAc,CAAC,GAC3D,IAAY,EAAO,IAAI,KAAK,GAAG,SAAS;EACzC,KAEL,EAAO,MAAM,YAAY;GACxB,IAAM,IAAiD,CAAC,GAKlD,IAAY,EAAO,OAAO,WAAW,KAAA,CAAS;GAEpD,KAAK,IAAM,CAAC,GAAM,MAAW,OAAO,QAAQ,CAAU,GAAG;IACxD,IAAM,IAAc,MAAM,QAAQ,KAAK,CAAC,EAAU,CAAM,GAAG,CAAS,CAAC;IAGrE,IAAI,EAAO,MAAM,SAAS;IAE1B,AAAI,MAAa,EAAU,KAAQ;GACpC;GAMA,AAJI,OAAO,KAAK,CAAS,CAAC,CAAC,WAAW,KAAK,OAAO,KAAK,CAAU,CAAC,CAAC,SAAS,KAC3E,QAAQ,KAAK,wCAAwC,CAAU,GAGhE,KAAKA,GAAK,UAAU,IAAI,CAAS;EAClC,CAAC;CACF;CAEA,GAAa,GAAsB;EAClC,IAAM,IAAY,EAAO,IAAI,KAAKA,GAAK,SAAS;EAChD,IAAI,OAAO,KAAK,CAAS,CAAC,CAAC,WAAW,GAAG;EAEzC,IAAM,IAAS,EAAO,IAAI,KAAK,GAAG,MAAM,GAEpC;EAGJ,IAAI,GAAQ,QAAQ,EAAO,QAAQ,GAClC,IAAW;GAAE,OAAO,EAAO;GAAM,QAAQ,EAAU,EAAO;EAAM;OAIhE,IADA,IAAW,KAAKK,GAAQ,CAAS,GAC7B,CAAC,GAAU;EAIhB,AADA,EAAO,IAAI,KAAKL,GAAK,OAAO,EAAS,KAAK,GAC1C,EAAO,IAAI,KAAKA,GAAK,QAAQ,EAAS,MAAM;EAM5C,IAAM,KAFc,EAAS,OAAO,UAAU,EAAmB,EAAS,MAAM,KAAK,KACpE,KAAK,KAAM,IAAkB,EAAS,OAAO,aAAc,GAC/C;EAC7B,EAAO,IAAI,KAAKA,GAAK,QAAQ,EAAK,MAAM,CAAM,CAAC;CAChD;CAKA,GACC,GAC6D;EAC7D,IAAM,IAAU,OAAO,QAAQ,CAAU;EACrC,MAAQ,WAAW,GAEvB;QAAK,IAAM,CAAC,GAAO,MAAW,GAC7B,IAAI,EAAO,UAAU,SAAS,UAC7B,OAAO;IAAE;IAAO;GAAO;GAIzB,KAAK,IAAM,CAAC,GAAO,MAAW,GAC7B,IAAI,EAAO,UAAU,SAAS,OAC7B,OAAO;IAAE;IAAO;GAAO;GAIzB,KAAK,IAAM,CAAC,GAAO,MAAW,GAC7B,IAAI,EAAO,UAAU,SAAS,QAC7B,OAAO;IAAE;IAAO;GAAO;EAZA;CAiB1B;CAEA,QAAc;EACb,KAAKC,GAAS,MAAM;CACrB;AACD;AAIA,SAAS,EAAmB,GAAiD;CAC5E,IAAI,EAAO,MAAM,WAAW,MAAM,GAEjC,OAAO;CAGR,IAAI,EAAO,MAAM,WAAW,MAAM,GAEjC,OAAO,KAAK,KAAM,OAAO,EAAO,aAAc,GAAI;CAGnD,IAAI,EAAO,UAAU,OAAO;EAE3B,IAAM,IAAU,EAAO,cAAc,OAAQ,OAAO;EACpD,OAAO,KAAK,KAAM,IAAU,EAAO,aAAc,GAAI;CACtD;AAGD;;;AC9LA,IAAM,IAAsB,MACtB,IAA6B;AAEnC,SAAS,EAAW,GAA2B;CAC9C,IAAI,IAAM;CACV,KAAK,IAAI,IAAI,GAAG,IAAI,EAAM,QAAQ,KAAK,KAAO,EAAM,EAAE,CAAC,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG;CACnF,OAAO;AACR;AAUA,SAAS,EAAY,GAA6C;CACjE,IAAI;CACJ,IAAI;EACH,IAAY,EAAM,WAAW,EAAc,EAAM,QAAQ,IAAI,KAAA;CAC9D,QAAQ;EACP,IAAY,KAAA;CACb;CAEA,IAAM,IAAc,IAAY,EAAW,CAAS,IAAI,KAAA;CAExD,QAAQ,EAAM,WAAd;EACC,KAAK,QAKJ,OADI,CAAC,EAAM,YAAY,CAAC,IAAW,SAC5B;GAAE,WAAW;IAAE,MAAM;IAAQ,MAAM,EAAM;GAAS;GAAG,aAAa,KAAA;EAAU;EACpF,KAAK,OACJ,OAAO;GAAE,WAAW,EAAE,MAAM,MAAM;GAAG;EAAY;EAClD,KAAK,UACJ,OAAO;GAAE,WAAW,EAAE,MAAM,SAAS;GAAG;EAAY;EACrD,SACC;CACF;AACD;AAEA,SAAS,EAAc,GAAmD;CACzE,IAAI,CAAC,EAAM,OAAO;CAElB,IAAM,IAAO,EAAY,CAAK;CAC9B,IAAI,CAAC,GAAM;CACX,IAAM,EAAE,cAAW,mBAAgB;CACnC,OAAO;EACN,OAAO,EAAM;EACb;EACA;EACA,YAAY,EAAM,SAAS,OAA0B,KAAA,IAAnB,EAAI,EAAM,KAAK;EACjD,aAAa,EAAM,UAAU,OAA2B,KAAA,IAApB,EAAI,EAAM,MAAM;EACpD,WAAW,EAAM;EACjB,SAAS,EAAM,WAAW,OAA4B,KAAA,IAArB,EAAI,EAAM,OAAO;EAClD,SAAS,EAAM;EACf,QAAQ,EAAM,UAAU,OAA2B,KAAA,IAApB,EAAI,EAAM,MAAM;CAChD;AACD;AAEA,SAAS,GAAc,GAAmD;CACzE,IAAI,CAAC,EAAM,OAAO;CAElB,IAAM,WAAkB;EACvB,IAAI,CAAC,EAAM,eAAe,OAAO;EACjC,IAAM,IAAS,OAAO,SAAS,EAAM,eAAe,EAAE;EACtD,OAAO,OAAO,SAAS,CAAM,IAAI,IAAS;CAC3C,EAAA,CAAG,GAEG,IAAO,EAAY,CAAK;CAC9B,IAAI,CAAC,GAAM;CACX,IAAM,EAAE,cAAW,mBAAgB;CACnC,OAAO;EACN,OAAO,EAAM;EACb;EACA;EACA,YAAY,EAAI,EAAM,cAAc,CAAmB;EACvD,kBAAkB,EAAI,CAAQ;EAC9B,SAAS,EAAM,WAAW,OAA4B,KAAA,IAArB,EAAI,EAAM,OAAO;EAClD,QAAQ,EAAM,UAAU,OAA2B,KAAA,IAApB,EAAI,EAAM,MAAM;CAChD;AACD;AAGA,SAAgB,GAAO,GAAgC;CACtD,IAAM,IAAuD,CAAC,GACxD,IAAuD,CAAC;CAE9D,KAAK,IAAM,KAAS,EAAI,QACvB,IAAI,EAAM,SAAS,SAAS;EAC3B,IAAM,IAAS,EAAc,CAAK;EAClC,AAAI,MAAQ,EAAgB,EAAM,QAAQ;CAC3C,OAAO,IAAI,EAAM,SAAS,SAAS;EAClC,IAAM,IAAS,GAAc,CAAK;EAClC,AAAI,MAAQ,EAAgB,EAAM,QAAQ;CAC3C;CAGD,IAAM,IAAqB,CAAC;CAU5B,OARI,OAAO,KAAK,CAAe,CAAC,CAAC,SAAS,MACzC,EAAK,QAAQ,EAAE,YAAY,EAAgB,IAGxC,OAAO,KAAK,CAAe,CAAC,CAAC,SAAS,MACzC,EAAK,QAAQ,EAAE,YAAY,EAAgB,IAGrC;AACR;;;ACzGA,IAAM,oBAAoB,IAAI,QAAoC;AAMlE,SAAS,EAAc,GAA2C;CAMjE,OALA,CAAI,EAAK,cACJ,EAAkB,IAAI,CAAI,MAC9B,EAAkB,IAAI,CAAI,GAC1B,QAAQ,KAAK,4EAA4E,IAEnF;AACR;AASA,SAAS,EACR,GACA,GACoB;CACpB,OAAO,OAAO,YAAY,OAAO,QAAQ,CAAU,CAAC,CAAC,QAAQ,GAAG,OAAY,EAAO,EAAO,SAAS,CAAC,CAAC;AACtG;AAEA,SAAS,GAAc,GAAuB,GAA4D;CACzG,OAAO;EACN,GAAG;EACH,OAAO,EAAQ,QACZ;GAAE,GAAG,EAAQ;GAAO,YAAY,EAAiB,EAAQ,MAAM,YAAY,CAAM;EAAE,IACnF,KAAA;EACH,OAAO,EAAQ,QACZ;GAAE,GAAG,EAAQ;GAAO,YAAY,EAAiB,EAAQ,MAAM,YAAY,CAAM;EAAE,IACnF,KAAA;CACJ;AACD;AAMA,IAAa,IAAkB;CAAC,GAAG,EAAQ;CAAS;CAAS;AAAQ;AAGrE,SAAgB,GAAmB,GAAiD;CAC/E,UAAU,MACd,OAAO,EAAgB,MAAM,MAAM,MAAM,CAAK;AAC/C;AA2CA,IAAa,KAAb,MAAuB;CACtB;CAEA,KAAiC;EAChC,QAAQ,IAAI,EAAe,SAAS;EACpC,QAAQ,IAAI,EAA2C,KAAA,CAAS;EAChE,SAAS,IAAI,EAAiC,KAAA,CAAS;CACxD;CACA,MAAe,EAAU,KAAKK,EAAI;CAMlC,KAAsB,IAAI,EAAwC,KAAA,CAAS;CAI3E,KAA0B,IAAI,EAAO,EAAK;CAQ1C,KAAgB,IAAI,EAAiC,KAAA,CAAS;CAE9D,KAAW,IAAI,EAAO;CAEtB,YAAY,GAAgC;EAa3C,AAZA,KAAK,KAAK;GACT,YAAY,EAAO,GAAO,UAAU;GACpC,MAAM,EAAO,GAAO,QAAQ,EAAK,MAAM,CAAC;GACxC,SAAS,EAAO,GAAO,WAAW,EAAK;GACvC,QAAQ,EAAO,GAAO,UAAU,EAAI;GACpC,eAAe,EAAkC,GAAO,aAAa;GACrE,SAAS,EAAO,GAAO,OAAO;EAC/B,GAEA,KAAKI,GAAS,IAAI,KAAKC,GAAc,KAAK,IAAI,CAAC,GAC/C,KAAKD,GAAS,IAAI,KAAKE,GAAc,KAAK,IAAI,CAAC,GAC/C,KAAKF,GAAS,IAAI,KAAKG,GAAY,KAAK,IAAI,CAAC,GAC7C,KAAKH,GAAS,IAAI,KAAKI,GAAa,KAAK,IAAI,CAAC;CAC/C;CAKA,GAAc,GAAsB;EAInC,IAHA,KAAKP,GAAW,IAAI,KAAA,CAAS,GAEzB,CAAC,EAAO,IAAI,KAAKC,EAAc,KAC/B,CAAC,EAAO,IAAI,KAAK,GAAG,MAAM,GAAG;EAEjC,IAAM,IAAO,EAAO,IAAI,KAAK,GAAG,UAAU;EAC1C,IAAI,CAAC,KAAQ,EAAc,CAAI,GAAG;EAElC,IAAM,IAAY,EAAK,UAAU,EAAK,MAAM,CAAC;EAI7C,AAHA,EAAO,cAAc,EAAU,MAAM,CAAC,GACtC,KAAKD,GAAW,oBAAI,IAAI,IAAI,CAAC,GAE7B,EAAO,MAAM,YAAY;GACxB,SAAS;IACR,IAAM,IAAQ,MAAM,QAAQ,KAAK,CAAC,EAAO,QAAQ,EAAU,KAAK,CAAC,CAAC;IAClE,IAAI,CAAC,GAAO;IACZ,KAAKA,GAAW,QAAQ,MAAW;KAC7B,MACD,EAAM,SAAQ,EAAO,IAAI,EAAM,IAAI,IAClC,EAAO,OAAO,EAAM,IAAI;IAC9B,CAAC;GACF;EACD,CAAC;CACF;CAMA,GAAa,GAAsB;EAClC,IAAM,IAAM,EAAO,IAAI,KAAKE,EAAI;EAEhC,EAAO,IAAI,KAAKH,GAAK,SAAS,IAAM,GAAc,IADlC,MAA4B,KAAKS,GAAgB,GAAQ,CAAG,MAAM,KAAA,CACrB,IAAI,KAAA,CAAS;CAC3E;CAKA,GAAiB,GAAgB,GAA+B;EAC/D,IAAI,CAAC,EAAO,IAAI,KAAK,GAAG,MAAM,GAAG,OAAO;EAExC,IAAM,IAAO,EAAO,IAAI,KAAK,GAAG,UAAU;EAI1C,IADI,CAAC,KACD,EAAc,CAAI,GAAG,OAAO;EAEhC,KAAKP,GAAe,IAAI,EAAI;EAE5B,IAAM,IAAS,EAAO,IAAI,KAAKD,EAAU;EAEzC,OADK,IACE,EAAO,IAAI,CAAI,IADF;CAErB;CAKA,GAAc,GAAsB;EAEnC,IAAI,CADY,EAAO,IAAI,KAAK,GAAG,OAC9B,GAAS;EAEd,IAAM,IAAO,EAAO,IAAI,KAAK,GAAG,UAAU;EAC1C,IAAI,CAAC,GAAM;EAEX,IAAM,IAAO,EAAO,IAAI,KAAK,GAAG,IAAI;EAGpC,IAAI,CAAC,EAAO,IAAI,KAAK,GAAG,MAAM,GAAG;GAChC,IAAM,IAAY,EAAK,QAAQ,CAAI;GAEnC,AADA,EAAO,cAAc,EAAU,MAAM,CAAC,GACtC,EAAO,IAAI,KAAKD,GAAK,QAAQ,GAAW,KAAA,CAAS;GACjD;EACD;EAEA,IAAM,IAAY,EAAK,mBAAmB,CAAI;EAG9C,AAFA,EAAO,cAAc,EAAU,MAAM,CAAC,GAEtC,EAAO,KAAK,MAAW;GACtB,EAAO,IAAI,KAAKA,GAAK,QAAQ,EAAO,IAAI,EAAU,MAAM,GAAG,KAAA,CAAS;EACrE,CAAC;CACF;CAEA,GAAY,GAAsB;EAEjC,IAAI,CADY,EAAO,IAAI,KAAK,GAAG,OAC9B,GAAS;EAEd,IAAM,IAAgB,EAAO,IAAI,KAAK,GAAG,aAAa,GAChD,IAAO,EAAO,IAAI,KAAK,GAAG,IAAI,GAI9B,IAAwB,KAAiB,EAAQ,aAAa,CAAI,KAAK,EAAQ;EAErF,IAAI,MAAW,UAAU;GAExB,IAAM,IAAU,EAAO,IAAI,KAAK,GAAG,OAAO;GAG1C,AAFA,KAAKG,GAAK,IAAI,GAAS,EAAI,GAC3B,EAAO,cAAc,KAAKA,GAAK,IAAI,KAAA,GAAW,EAAI,CAAC,GACnD,KAAKH,GAAK,OAAO,IAAI,IAAU,SAAS,SAAS;GACjD;EACD;EAEA,IAAM,IAAY,EAAO,IAAI,KAAK,IAAI,MAAM;EAC5C,IAAI,CAAC,GAAW;EAEhB,KAAKA,GAAK,OAAO,IAAI,SAAS;EAE9B,IAAM,IAAY,MAAW,SAAS,EAAQ,QAAQ,MAAW,UAAU,EAAQ,mBAAmB,WAChG,IAAQ,EAAU,MAAM,CAAS,CAAC,CAAC,UAAU,EAAE,UAAU,EAAQ,SAAS,QAAQ,CAAC;EACzF,EAAO,cAAc,EAAM,MAAM,CAAC;EAIlC,IAAI;EACJ,IAAI,MAAW,UAAU,MAAW,SAAS;GAC5C,IAAM,IAAW,IAAI,EAAK,SAAS,SAAuB,GAAO;IAChE,QAAQ,EAAQ;IAChB,aAAa,MAAW;GACzB,CAAC;GACD,UAAkB,EAAS,KAAK;EACjC,OACC,IAAY,YAAY;GACvB,IAAM,IAAS,MAAM,EAAI,MAAM,CAAK;GACpC,OAAO,IAAS,GAAO,CAAM,IAAI,KAAA;EAClC;EAGD,EAAO,MAAM,YAAY;GACxB,IAAI;IACH,SAAS;KACR,IAAM,IAAS,MAAM,QAAQ,KAAK,CAAC,EAAO,QAAQ,EAAU,CAAC,CAAC;KAC9D,IAAI,CAAC,GAAQ;KAKb,AAHA,QAAQ,MAAM,oBAAoB,GAAQ,KAAK,GAAG,KAAK,KAAK,GAAG,CAAM,GAErE,KAAKG,GAAK,IAAI,GAAQ,EAAI,GAC1B,KAAKH,GAAK,OAAO,IAAI,MAAM;IAC5B;GACD,SAAS,GAAK;IACb,QAAQ,KAAK,0BAA0B,KAAK,GAAG,KAAK,KAAK,GAAG,CAAG;GAChE,UAAU;IAET,AADA,KAAKG,GAAK,IAAI,KAAA,CAAS,GACvB,KAAKH,GAAK,OAAO,IAAI,SAAS;GAC/B;EACD,CAAC;CACF;CAgBA,GAAgB,GAAgB,GAAqD;EACpF,IAAI,CAAC,GAAK,OAAO,EAAE,OAAO,GAAK;EAE/B,IAAM,IAAO,EAAO,IAAI,KAAK,GAAG,IAAI,GAC9B,IAAW,EAAK,WAAW,GAAM,CAAG;EAItC,UAAa,KAAA,GACjB;OAAI,MAAa,GAAM,OAAO,EAAE,OAAO,GAAK;GAEvC,SAAKU,GAAiB,GAAQ,CAAQ,GAE3C,OAAO;IAAE,OAAO;IAAO,MAAM;GAAS;EAJM;CAK7C;CAGA,kBAAkB,GAAgB,GAA6D;EAC9F,IAAM,IAAS,KAAKD,GAAgB,GAAQ,CAAG;EAC/C,IAAI,CAAC,GAAQ;EACb,IAAI,EAAO,OAAO,OAAO,EAAO,IAAI,KAAK,IAAI,MAAM;EAEnD,IAAI,CAAC,EAAO,IAAI,KAAK,GAAG,OAAO,GAAG;EAElC,IAAM,IAAO,EAAO,IAAI,KAAK,GAAG,UAAU;EAC1C,IAAI,CAAC,GAAM;EAEX,IAAM,IAAY,EAAK,QAAQ,EAAO,IAAI;EAE1C,OADA,EAAO,cAAc,EAAU,MAAM,CAAC,GAC/B;CACR;CAEA,QAAQ;EACP,KAAKL,GAAS,MAAM;CACrB;AACD,GC5Va,IAAkB,OAAO,iBAAiB;AAiBvD,SAAgB,EAAiB,GAA+C;CAC/E,OAAO;EACN,WAAW,EAAO;EAClB,SAAS;GACR,OAAO,EAAO;GACd,WAAW,EAAO;GAClB,aAAa,EAAO;GACpB,oBAAoB,EAAO;GAC3B,qBAAqB,EAAO;GAC5B,oBAAoB,EAAO,sBAAsB;EAClD;CACD;AACD;AAGA,SAAgB,GAAiB,GAAqC;CACrE,OAAO,KAAK,UAAU,EAAiB,CAAM,CAAC,CAAC,OAAO;AACvD;;;AC/BA,IAAM,KAAQ,EAAK,MAAM,GAAG;AAU5B,SAAgB,EAAgB,GAAqD;CACpF,IAAI,EAAO,WAAW,KAAA,GAAW,OAAO,EAAK,MAAM,EAAO,MAAM;CAChE,IAAI,EAAO,WAAW,OAAO,EAAK,MAAM,KAAK,KAAK,MAAO,EAAO,SAAS,CAAC;AAE3E;AAsBA,SAAgB,GAAS,GAA0B;CAIlD,OAFI,EAAM,WAAW,KAAA,KAEd,EAAK,MAAM,IAAI,EAAM,UAAU,EAAK,KAAK,EAAM;AACvD;;;AChCA,SAAS,EAAuB,IAAW,GAAkB;CAC5D,IAAM,KAAe,IAAW,MAAO,OAAO;CAC9C,OAAS,KAAK,MAAM,IAAa,EAAE,IAAI,IAAK;AAC7C;AAGA,SAAgB,EAAsB,GAA6B,IAAW,GAAoB;CACjG,IAAM,IAAa,EAAuB,CAAQ;CAClD,OAAO,MAAe,MAAM,MAAe,MACxC;EAAE,OAAO,EAAW;EAAQ,QAAQ,EAAW;CAAM,IACrD;EAAE,OAAO,EAAW;EAAO,QAAQ,EAAW;CAAO;AACzD;AAEA,SAAS,EAAM,GAAuB;CACrC,OAAO,OAAO,GAAG,GAAO,EAAE,IAAI,IAAI;AACnC;AAEA,SAAS,GACR,GACA,GACmD;CACnD,IAAM,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,KAAK;CAC3B,OAAO;EAAC,EAAM,CAAC,CAAC;EAAG,EAAM,CAAC;EAAG,EAAM,CAAC,CAAC;EAAG,EAAM,CAAC;EAAG,EAAM,IAAQ,CAAC;EAAG,EAAM,CAAC;CAAC;AAC7E;AAGA,SAAgB,GACf,GACA,GAC8B;CAC9B,IAAM,IAAW,EAAuB,GAAO,QAAQ,GACjD,IAAS,EAAsB,GAAQ,CAAQ,GAEjD;CACJ,QAAQ,GAAR;EACC,KAAK;GACJ,IAAS;IAAC;IAAG;IAAG;IAAI;IAAG,EAAO;IAAO;GAAC;GACtC;EACD,KAAK;GACJ,IAAS;IAAC;IAAI;IAAG;IAAG;IAAI,EAAO;IAAO,EAAO;GAAM;GACnD;EACD,KAAK;GACJ,IAAS;IAAC;IAAG;IAAI;IAAG;IAAG;IAAG,EAAO;GAAM;GACvC;EACD,SACC,IAAS;GAAC;GAAG;GAAG;GAAG;GAAG;GAAG;EAAC;CAE5B;CAGA,OADI,GAAO,SAAM,IAAS,GAAW,GAAQ,EAAO,KAAK,IAClD;EAAE;EAAQ;CAAO;AACzB;;;ACnCA,IAAM,KAAY,EAAK,MAAM,GAAG,GA4CnB,KAAb,MAAqB;CACpB;CACA;CACA;CAEA,KAA+B;EAC9B,OAAO,IAAI,EAA+B,KAAA,CAAS;EACnD,WAAW,IAAI,EAA+B,KAAA,CAAS;EACvD,SAAS,IAAI,EAAsD,KAAA,CAAS;EAC5E,SAAS,IAAI,EAAgB,EAAK;EAClC,OAAO,IAAI,EAA0B,KAAA,CAAS;EAC9C,UAAU,IAAI,EAAiC,CAAC,CAAC;CAClD;CACA,MAAe,EAAU,KAAKO,EAAI;CAGlC,KAAU,IAAI,EAAiC,KAAA,CAAS;CACxD;CAEA,KAAW,IAAI,EAAO;CAEtB,KAA2B;EAK1B,AAJA,KAAKA,GAAK,MAAM,QAAQ,MAAS;GAChC,GAAM,MAAM;EAEb,CAAC,GACD,KAAKA,GAAK,UAAU,IAAI,KAAA,CAAS;CAClC;CAEA,YAAY,GAAgB,GAAY,GAA8B;EAgBrE,AAfA,KAAK,KAAK;GACT,SAAS,EAAO,GAAO,WAAW,EAAK;GACvC,OAAO,EAAO,GAAO,SAAS,EAAI;EACnC,GAEA,KAAK,SAAS,GACd,KAAK,OAAO,GACZ,KAAKC,KAAY,KAAKC,GAAS,UAAU,MAAW;GACnD,IAAM,IAAS,EAAO,IAAI,KAAK,OAAO,IAAI,MAAM;GAChD,OAAO,IAAS,EAAiB,CAAM,IAAI,KAAA;EAC5C,CAAC,GAED,KAAKA,GAAS,IAAI,KAAKC,GAAY,KAAK,IAAI,CAAC,GAC7C,KAAKD,GAAS,IAAI,KAAKE,GAAW,KAAK,IAAI,CAAC,GAC5C,KAAKF,GAAS,IAAI,KAAKG,GAAY,KAAK,IAAI,CAAC,GAC7C,KAAKH,GAAS,IAAI,KAAKI,GAAc,KAAK,IAAI,CAAC;CAChD;CAEA,GAAY,GAAsB;EACjC,IAAM,IAAS,EAAO,OAAO;GAC5B,KAAK,GAAG;GACR,KAAK,OAAO,GAAG;GACf,KAAK,OAAO,IAAI;GAChB,KAAKL;EACN,CAAC;EACD,IAAI,CAAC,GAAQ;GAGZ,KAAKM,GAAQ,IAAI,KAAA,CAAS;GAC1B;EACD;EACA,IAAM,CAAC,GAAG,GAAW,GAAO,KAAY,GAIlC,IAA6C,EAAU,kBAAkB,GAAQ,EAAS,SAAS;EACzG,IAAI,CAAC,GAAQ;GAIZ,AAFA,KAAKA,GAAQ,IAAI,KAAA,CAAS,GAC1B,KAAKC,GAAmB,GACxB,KAAKR,GAAK,SAAS,IAAI,CAAC,CAAC;GACzB;EACD;EAGA,IAAI,IAAoC,IAAI,EAAa;GACxD,MAAM,KAAK;GACX,OAAO,KAAK,GAAG;GACf,WAAW;GACX;GACA,QAAQ,EAAS;GACjB,OAAO,KAAKA,GAAK;EAClB,CAAC;EAID,AAFA,EAAO,cAAc,GAAS,MAAM,CAAC,GAErC,EAAO,KAAK,MAAW;GACtB,IAAI,CAAC,GAAS;GAEd,IAAM,IAAU,EAAO,IAAI,KAAKO,EAAO;GACvC,IAAI,GAAS;IAEZ,IAAM,IAAmB,EAAO,IAAI,EAAQ,SAAS;IAIrD,IAHI,MAAqB,KAAA,KAGrB,CAAC,GAAS;KAAE,UAAU;KAAkB,QAAQ,EAAO,IAAI,EAAQ,SAAS;IAAE,CAAC,GAAG;GACvF;GAQA,AAJA,KAAKA,GAAQ,IAAI,CAAO,GACxB,IAAU,KAAA,GAGV,EAAO,MAAM;EACd,CAAC;CACF;CAEA,GAAW,GAAsB;EAChC,IAAM,IAAS,EAAO,IAAI,KAAKA,EAAO;EACtC,IAAI,CAAC,GAAQ;GAEZ,KAAKP,GAAK,SAAS,IAAI,CAAC,CAAC;GACzB;EACD;EAcA,AAZA,EAAO,cAAc,EAAO,MAAM,CAAC,GAInC,EAAO,KAAK,MAAU;GACrB,IAAM,IAAQ,EAAM,IAAI,EAAO,KAAK;GACpC,KAAKA,GAAK,MAAM,QAAQ,OACvB,GAAM,MAAM,GACL,GAAO,MAAM,EACpB;EACF,CAAC,GACD,EAAO,MAAM,KAAKA,GAAK,WAAW,EAAO,SAAS,GAClD,EAAO,MAAM,KAAKA,GAAK,UAAU,EAAO,QAAQ;CACjD;CAEA,GAAY,GAAsB;EACjC,IAAM,IAAU,EAAO,IAAI,KAAK,OAAO,IAAI,OAAO;EAClD,IAAI,CAAC,GAAS;EAEd,IAAM,IAAU,EAAQ;EACxB,IAAI,GAAS;GACZ,EAAO,IAAI,KAAKA,GAAK,SAAS;IAAE,OAAO,EAAQ;IAAO,QAAQ,EAAQ;GAAO,CAAC;GAC9E;EACD;EAEA,IAAM,IAAQ,EAAO,IAAI,KAAKA,GAAK,KAAK;EACnC,KAEL,EAAO,IACN,KAAKA,GAAK,SACV,EAAsB;GAAE,OAAO,EAAM;GAAc,QAAQ,EAAM;EAAc,GAAG,EAAQ,QAAQ,CACnG;CACD;CAEA,GAAc,GAAsB;EACnB,MAAO,IAAI,KAAK,GAAG,OAC9B,GAGL;OAAI,CADU,EAAO,IAAI,KAAKA,GAAK,KAC9B,GAAO;IACX,KAAKA,GAAK,QAAQ,IAAI,EAAI;IAC1B;GACD;GAIA,AAFA,KAAKA,GAAK,QAAQ,IAAI,EAAK,GAE3B,EAAO,YAAY;IAClB,KAAKA,GAAK,QAAQ,IAAI,EAAI;GAC3B,GAAG,EAAS;EANZ;CAOD;CAEA,QAAQ;EAGP,AAFA,KAAKQ,GAAmB,GAExB,KAAKN,GAAS,MAAM;CACrB;CAGA,OAAO,YAAY;AACpB,GAYM,IAAN,MAAmB;CAClB;CACA;CACA;CACA;CACA;CACA;CAEA,YAAY,IAAI,EAA+B,KAAA,CAAS;CACxD,QAAQ,IAAI,EAA+B,KAAA,CAAS;CAGpD,WAAW,IAAI,EAAiC,CAAC,CAAC;CAGlD,KAAY,IAAI,EAAiC,CAAC,CAAC;CAInD,KAAW,IAAI,EAAmB,EAAK,MAAM,IAAI;CAIjD,KAAiB;CAEjB,KAAW,IAAI,EAAO;CAEtB,YAAY,GAA0B;EAarC,AAZA,KAAK,OAAO,EAAM,MAClB,KAAK,QAAQ,EAAM,OACnB,KAAK,YAAY,EAAM,WACvB,KAAK,QAAQ,EAAM,OACnB,KAAK,SAAS,EAAM,QACpB,KAAK,QAAQ,EAAM,OAEnB,KAAKA,GAAS,KAAK,MAAW;GAC7B,IAAM,IAAQ,EAAO,IAAI,KAAK,KAAK;GACnC,KAAKO,GAAS,IAAI,IAAQ,EAAO,IAAI,KAAK,KAAK,IAAI,MAAM,IAAI,EAAK,MAAM,IAAI;EAC7E,CAAC,GAED,KAAKP,GAAS,IAAI,KAAKQ,GAAK,KAAK,IAAI,CAAC;CACvC;CAEA,GAAK,GAAsB;EAC1B,IAAM,IAAM,KAAK,UAAU,MAAM,KAAK,KAAK,CAAC,CAAC,UAAU,EAAE,UAAU,EAAQ,SAAS,MAAM,CAAC;EAC3F,EAAO,cAAc,EAAI,MAAM,CAAC;EAEhC,IAAM,IAAU,IAAI,aAAa;GAChC,QAAQ,OAAO,MAAsB;IACpC,IAAI;KAGH,IAAM,IAAa,KAAKC,IAElB,IAAY,EAAK,MAAM,UAAU,EAAM,SAAuB;KAMpE,IALI,KAAa,KAAK,UAAU,KAAK,KAAK,MAKtC,KAAK,MAAM,KAAK,MAKf,KAAK,KAAK,IAAI,UAAU,KAAK,MAAM,KAAA,MAEnC,KAAK,MAAM,KAAK,MAAM,KAAA,KAEzB,KAAK,MAAM,IAAI,EAAM,MAAM,CAAC,GAGzB,CAAE,MAAM,KAAKC,GAAM,GAAW,CAAM,MACpC,MAAe,KAAKD,MAEpB,KAAa,KAAK,UAAU,KAAK,KAAK,KAGzC;KASF,AALA,KAAK,UAAU,IAAI,CAAS,GAG5B,KAAKE,GAAc,CAAS,GAE5B,KAAK,MAAM,QAAQ,OAClB,GAAM,MAAM,GACL,EAAM,MAAM,EACnB;IACF,UAAU;KACT,EAAM,MAAM;IACb;GACD;GAEA,QAAQ,MAAU;IAEjB,AADA,QAAQ,MAAM,CAAK,GACnB,EAAO,MAAM;GACd;EACD,CAAC;EAMD,AALA,EAAO,cAAc;GACpB,AAAI,EAAQ,UAAU,YAAU,EAAQ,MAAM;EAC/C,CAAC,GAGG,KAAK,OAAO,UAAU,SAAS,SAClC,KAAKC,GAAS,GAAQ,GAAK,CAAO,IAElC,KAAKC,GAAW,GAAQ,GAAK,CAAO;CAEtC;CAEA,GAAW,GAAgB,GAA2B,GAA6B;EAClF,IAAM,IACL,KAAK,OAAO,UAAU,SAAS,QAAQ,IAAI,EAAU,IAAI,OAAO,IAAI,IAAI,EAAU,OAAO,OAAO,GAE3F,IAAW,IAAI,EAAU,SAAS,GAAK;GAC5C;GACA,SAAS,KAAKN;EACf,CAAC;EAUD,AATA,EAAO,cAAc,EAAS,MAAM,CAAC,GAGrC,EAAO,KAAK,MAAU;GACrB,IAAM,IAAU,EAAM,IAAI,EAAS,QAAQ,GACrC,IAAS,EAAM,IAAI,KAAKO,EAAS;GACvC,KAAK,SAAS,aAAa,EAAU,oBAAoB,GAAS,CAAM,CAAC;EAC1E,CAAC,GAED,EAAQ,UAAU;GACjB,OAAO,KAAK,OAAO;GACnB,aAAa,KAAK,OAAO,cAAc,EAAK,IAAI,QAAQ,KAAK,OAAO,WAAW,IAAI,KAAA;GACnF,oBAAoB,KAAK,OAAO;GAChC,qBAAqB,KAAK,OAAO;GACjC,oBAAoB,KAAK,OAAO;GAEhC,MAAM;EACP,CAAC;EAED,IAAI;EAEJ,EAAO,MAAM,YAAY;GACxB,SAAS;IACR,IAAM,IAAO,MAAM,EAAS,KAAK;IACjC,IAAI,CAAC,GAAM;IAGX,AAAI,KAAKC,GAAiB,EAAK,aAAa,MAAG,IAAW,KAAA;IAE1D,IAAM,EAAE,aAAU;IAClB,IAAI,CAAC,GAAO;IAGZ,IAAM,IAAY,EAAK,MAAM,UAAU,EAAM,SAAuB;IACpE,KAAK,KAAK,SAAS,GAAW,OAAO;IAErC,IAAM,IAAQ,IAAI,kBAAkB;KACnC,MAAM,EAAM,WAAW,QAAQ;KAC/B,MAAM,EAAM;KACZ,WAAW,EAAM;IAClB,CAAC;IAmBD,AAhBA,KAAK,MAAM,QAAQ,OAAa;KAC/B,aAAa,GAAS,cAAc,KAAK;KACzC,gBAAgB,GAAS,iBAAiB,KAAK,EAAM,QAAQ;IAC9D,EAAE,GAOE,MAAa,KAAA,KAAa,EAAK,cAClC,KAAKC,GAAa,EAAK,MAAM,UAAU,CAAQ,GAAG,EAAK,MAAM,UAAU,EAAM,SAAS,CAAC,GAGxF,IAAW,EAAM,WAEjB,EAAQ,OAAO,CAAK;GACrB;EACD,CAAC;CACF;CAEA,GAAS,GAAgB,GAA2B,GAA6B;EAChF,IAAM,IAAY,KAAK,OAAO;EAC9B,IAAI,EAAU,SAAS,QAAQ;EAE/B,IAAM,IAAc,EAAc,EAAU,IAAI,GAC1C,IAAO,EAAU,KAAK,kBAAkB,CAAW,GACnD,IAAc,KAAK,OAAO,cAAc,EAAK,IAAI,QAAQ,KAAK,OAAO,WAAW,IAAI,EAAK,aAEzF,IAAW,IAAI,EAAU,SAAS,GAAK;GAC5C,QAAQ,IAAI,EAAU,KAAK,OAAO,CAAI;GACtC,SAAS,KAAKT;EACf,CAAC;EAWD,AAVA,EAAO,cAAc,EAAS,MAAM,CAAC,GAGrC,EAAO,KAAK,MAAU;GACrB,IAAM,IAAU,EAAM,IAAI,EAAS,QAAQ,GACrC,IAAS,EAAM,IAAI,KAAKO,EAAS;GACvC,KAAK,SAAS,aAAa,EAAU,oBAAoB,GAAS,CAAM,CAAC;EAC1E,CAAC,GAGD,EAAQ,UAAU;GACjB,OAAO,KAAK,OAAO;GACnB;GACA,oBAAoB,KAAK,OAAO;GAChC,qBAAqB,KAAK,OAAO;GACjC,oBAAoB,KAAK,OAAO;GAEhC,MAAM;EACP,CAAC;EAED,IAAI;EAEJ,EAAO,MAAM,YAAY;GACxB,SAAS;IACR,IAAM,IAAO,MAAM,EAAS,KAAK;IACjC,IAAI,CAAC,GAAM;IAGX,AAAI,KAAKC,GAAiB,EAAK,aAAa,MAAG,IAAW,KAAA;IAE1D,IAAM,EAAE,aAAU;IAClB,IAAI,CAAC,GAAO;IAGZ,IAAM,IAAY,EAAK,MAAM,UAAU,EAAM,SAAS;IAiBtD,IAhBA,KAAK,KAAK,SAAS,GAAW,OAAO,GAGrC,KAAK,MAAM,QAAQ,OAAa;KAC/B,aAAa,GAAS,cAAc,KAAK;KACzC,gBAAgB,GAAS,iBAAiB,KAAK,EAAM,QAAQ;IAC9D,EAAE,GAIE,MAAa,KAAA,KAAa,EAAK,cAClC,KAAKC,GAAa,EAAK,MAAM,UAAU,CAAQ,GAAG,EAAK,MAAM,UAAU,EAAM,SAAS,CAAC,GAGxF,IAAW,EAAM,WAEb,EAAQ,UAAU,UAAU;IAChC,EAAQ,OACP,IAAI,kBAAkB;KACrB,MAAM,EAAM,WAAW,QAAQ;KAC/B,MAAM,EAAM;KACZ,WAAW,EAAM;IAClB,CAAC,CACF;GACD;EACD,CAAC;CACF;CASA,GAAiB,GAAwB;EAMxC,OALI,MAAU,KAAKP,OACnB,KAAKA,KAAiB,GACtB,KAAK,UAAU,IAAI,KAAA,CAAS,GAC5B,KAAKK,GAAU,IAAI,CAAC,CAAC,GACrB,KAAK,KAAK,MAAM,GACT;CACR;CAWA,MAAMJ,GAAM,GAAuB,GAAkC;EACpE,IAAI;EACJ,IAAI;GACH,IAAM,IAAW,IAAI,SAAkB,MAAY;IAClD,IAAU,KAAK,MAAM,cAAc,EAAQ,CAAC,KAAK,MAAM,KAAK,CAAC,CAAC;GAC/D,CAAC,GACK,IAAO,KAAK,KAAK,KAAK,CAAS,CAAC,CAAC,WAAW,EAAI;GACtD,OAAQ,MAAM,QAAQ,KAAK;IAAC;IAAM;IAAU,EAAO;GAAM,CAAC,KAAM;EACjE,UAAU;GACT,IAAU;EACX;CACD;CAGA,GAAa,GAAmB,GAAuB;EAClD,IAAQ,KAEZ,KAAKI,GAAU,QAAQ,MAAY;GAClC,KAAK,IAAM,KAAS,GAEnB,IAAI,EAAM,SAAS,KAAO,EAAM,OAAO,GAAO;IAE7C,AADA,EAAM,QAAQ,EAAK,MAAM,IAAI,EAAM,OAAO,CAAK,GAC/C,EAAM,MAAM,EAAK,MAAM,IAAI,EAAM,KAAK,CAAG;IACzC;GACD;GAID,AADA,EAAQ,KAAK;IAAE;IAAO;GAAI,CAAC,GAC3B,EAAQ,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;EACzC,CAAC;CACF;CAGA,GAAc,GAA6B;EAC1C,KAAKA,GAAU,QAAQ,MAAY;GAClC,OAAO,EAAQ,SAAS,IAAG;IAC1B,IAAI,EAAQ,EAAE,CAAC,OAAO,GAAW;KAChC,EAAQ,EAAE,CAAC,QAAQ,EAAK,MAAM,IAAI,EAAQ,EAAE,CAAC,OAAO,CAAS;KAC7D;IACD;IACA,EAAQ,MAAM;GACf;EACD,CAAC;CACF;CAEA,QAAc;EAGb,AAFA,KAAKd,GAAS,MAAM,GAEpB,KAAK,MAAM,QAAQ,MAAS;GAC3B,GAAM,MAAM;EAEb,CAAC;CACF;AACD;AAEA,eAAe,EAAU,GAA+C;CACvE,IAAI,CAAC,EAAQ,mBAAmB,EAAO,SAAS,GAAG;EAElD,IAAM,IAAO,EAAO,UAAU,SAAS,YAAY,EAAO,UAAU,IAAI,OAAO,EAAO,UAAU;EAEhG,OADA,QAAQ,KAAK,qDAAqD,GAAM,GACjE;CACR;CAEA,IAAI;CACJ,IAAI,EAAO,aACV,IAAc,EAAK,IAAI,QAAQ,EAAO,WAAW;MAC3C,IAAI,EAAO,UAAU,SAAS,QACpC,IAAI;EACH,IAAc,EAAU,KAAK,kBAAkB,EAAc,EAAO,UAAU,IAAI,CAAC,CAAC,CAAC;CACtF,SAAS,GAAK;EAMb,OADA,QAAQ,KAAK,gDAAgD,EAAO,SAAS,CAAG,GACzE;CACR;CAED,IAAM,EAAE,iBAAc,MAAM,aAAa,kBAAkB;EAC1D,OAAO,EAAO;EACd;EACA,oBAAoB,EAAO;EAC3B,qBAAqB,EAAO;EAC5B,oBAAoB,EAAO,sBAAsB;CAClD,CAAC;CAED,IAAI,GAAW,OAAO;CAKtB,IAAI,EAAO,MAAM,WAAW,OAAO,GAAG;EACrC,IAAM,IAAO,QAAQ,EAAO,MAAM,MAAM,CAAc;EAQtD,KAAI,MAPgB,aAAa,kBAAkB;GAClD,OAAO;GACP;GACA,oBAAoB,EAAO;GAC3B,qBAAqB,EAAO;GAC5B,oBAAoB,EAAO,sBAAsB;EAClD,CAAC,EAAA,CACS,WAET,OADA,EAAO,QAAQ,GACR;CAET;CAEA,OAAO;AACR;AAEA,OAAO,OAAO,GAAW,GAAG,IAAkB,GAAiB,CAAC;;;AC1oBhE,IAAM,IAAyB,KAkClB,KAAb,MAAsB;CACrB;CAEA;CAEA,KAAgC;EAC/B,OAAO,IAAI,EAA+B,KAAA,CAAS;EACnD,WAAW,IAAI,EAA+B,KAAA,CAAS;EACvD,SAAS,IAAI,EAAO,EAAK;CAC1B;CACA,MAAe,EAAU,KAAKiB,EAAI;CAElC,KAAO,IAAI,EAA6C,KAAA,CAAS;CACjE,KAAW,IAAI,EAAO;CAEtB,YAAY,GAAkB,GAA+B;EAc5D,AAbA,KAAK,UAAU,GACf,KAAK,KAAK;GACT,QAAQ,EAAO,GAAO,MAAM;GAC5B,SAAS,EAAO,GAAO,WAAW,KAAK;EACxC,GAEA,KAAKC,GAAS,KAAK,MAAW;GAC7B,IAAM,IAAS,EAAO,IAAI,KAAK,GAAG,MAAM;GACxC,KAAKC,GAAK,IAAI,GAAQ,WAAW,IAAI,KAAK,KAAA,CAAS;EACpD,CAAC,GAED,KAAKD,GAAS,IAAI,KAAKE,GAAY,KAAK,IAAI,CAAC,GAC7C,KAAKF,GAAS,IAAI,KAAKG,GAAW,KAAK,IAAI,CAAC,GAC5C,KAAKH,GAAS,IAAI,KAAKI,GAAW,KAAK,IAAI,CAAC;CAC7C;CAEA,GAAW,GAAgB;EAC1B,IAAM,IAAS,EAAO,OAAO,CAAC,KAAK,GAAG,QAAQ,KAAK,QAAQ,IAAI,OAAO,CAAC;EACvE,IAAI,CAAC,GAAQ;EACb,IAAM,CAAC,GAAQ,KAAW;EAI1B,CAAI,EAAO,UAAU,EAAQ,SAAS,EAAO,WAAW,EAAQ,YAC/D,EAAO,QAAQ,EAAQ,OACvB,EAAO,SAAS,EAAQ;CAE1B;CAGA,GAAY,GAAsB;EACjC,IAAM,IAAU,EAAO,IAAI,KAAK,GAAG,OAAO;EAG1C,IAAI,MAAY,SAAS;GACxB,KAAKL,GAAK,QAAQ,IAAI,EAAK;GAC3B;EACD;EAEA,IAAI,MAAY,UAAU;GAEzB,AADA,KAAKA,GAAK,QAAQ,IAAI,EAAI,GAC1B,EAAO,cAAc,KAAKA,GAAK,QAAQ,IAAI,EAAK,CAAC;GACjD;EACD;EAGA,IAAM,IAAS,EAAO,IAAI,KAAK,GAAG,MAAM;EACxC,IAAI,CAAC,GAAQ;GACZ,KAAKA,GAAK,QAAQ,IAAI,EAAK;GAC3B;EACD;EAEA,IAAI,IAAe,IACb,UAAe;GACpB,KAAKA,GAAK,QAAQ,IAAI,KAAgB,CAAC,SAAS,MAAM;EACvD,GAEM,KAAY,MAAyC;GAC1D,KAAK,IAAM,KAAS,GAEnB,AADA,IAAe,EAAM,gBACrB,EAAO;EAET,GAII;EACJ,IAAI;GACH,IAAW,IAAI,qBAAqB,GAAU;IAAE,WAAW;IAAwB,YAAY;GAAQ,CAAC;EACzG,QAAQ;GAEP,AADA,QAAQ,KAAK,sCAAsC,EAAQ,eAAe,GAC1E,IAAW,IAAI,qBAAqB,GAAU,EAAE,WAAW,EAAuB,CAAC;EACpF;EAMA,AAJA,EAAO,GACP,EAAO,MAAM,UAAU,oBAAoB,CAAM,GACjD,EAAS,QAAQ,CAAM,GACvB,EAAO,cAAc,EAAS,WAAW,CAAC,GAC1C,EAAO,cAAc,KAAKA,GAAK,QAAQ,IAAI,EAAK,CAAC;CAClD;CAEA,GAAW,GAAgB;EAC1B,IAAM,IAAM,EAAO,IAAI,KAAKE,EAAI;EAChC,IAAI,CAAC,GAAK;EAEV,IAAM,IAAQ,EAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,GACzC,IAAQ,EAAO,IAAI,KAAK,QAAQ,OAAO,IAAI,OAAO,GAIpD,IAA8B,4BAA4B;GAiB7D,AAhBA,KAAKI,GAAQ,GAAK,GAAO,CAAK,GAE1B,KACH,KAAKN,GAAK,MAAM,QAAQ,OACvB,GAAS,MAAM,GACR,EAAM,MAAM,EACnB,GACD,KAAKA,GAAK,UAAU,IAAI,EAAK,MAAM,UAAU,EAAM,SAAuB,CAAC,MAE3E,KAAKA,GAAK,MAAM,QAAQ,MAAY;IACnC,GAAS,MAAM;GAEhB,CAAC,GACD,KAAKA,GAAK,UAAU,IAAI,KAAA,CAAS,IAGlC,IAAU,KAAA;EACX,CAAC;EAGD,EAAO,cAAc;GACpB,AAAI,MAAY,KAAA,KAAW,qBAAqB,CAAO;EACxD,CAAC;CACF;CAEA,GAAQ,GAA+B,GAAoB,GAAuB;EACjF,IAAI,CAAC,GAAO;GAGX,AADA,EAAI,YAAY,QAChB,EAAI,SAAS,GAAG,GAAG,EAAI,OAAO,OAAO,EAAI,OAAO,MAAM;GACtD;EACD;EAQA,IALA,EAAI,KAAK,GACT,EAAI,YAAY,QAChB,EAAI,SAAS,GAAG,GAAG,EAAI,OAAO,OAAO,EAAI,OAAO,MAAM,GAGlD,CAAC,GAAO,UAKX,AAJI,GAAO,SACV,EAAI,MAAM,IAAI,CAAC,GACf,EAAI,UAAU,CAAC,EAAI,OAAO,OAAO,CAAC,IAEnC,EAAI,UAAU,GAAO,GAAG,GAAG,EAAI,OAAO,OAAO,EAAI,OAAO,MAAM;OACxD;GACN,IAAM,IAAY,GAA4B,EAAI,QAAQ,CAAK;GAE/D,AADA,EAAI,aAAa,GAAG,EAAU,MAAM,GACpC,EAAI,UAAU,GAAO,GAAG,GAAG,EAAU,OAAO,OAAO,EAAU,OAAO,MAAM;EAC3E;EACA,EAAI,QAAQ;CACb;CAGA,QAAQ;EAMP,AALA,KAAKA,GAAK,MAAM,QAAQ,MAAY;GACnC,GAAS,MAAM;EAEhB,CAAC,GACD,KAAKA,GAAK,UAAU,IAAI,KAAA,CAAS,GACjC,KAAKC,GAAS,MAAM;CACrB;AACD;;;ACxIA,SAAS,GAAS,GAAiC;CAClD,QAAQ,MAAY;EACnB,IAAM,IAA2C,CAAC,GAC5C,IAAyC,CAAC;EAEhD,KAAK,IAAM,CAAC,GAAM,MAAW,GAC5B,IAAI,EAAO,cAAc,EAAO,aAAa;GAC5C,IAAM,IAAO,EAAO,aAAa,EAAO;GACxC,AAAI,KAAQ,IACX,EAAO,KAAK;IAAE;IAAM;GAAK,CAAC,IAE1B,EAAK,KAAK;IAAE;IAAM;GAAK,CAAC;EAE1B;EAiBD,OAbA,EAAO,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI,GAEjC,EAAO,SAAS,IACZ,EAAO,KAAK,MAAM,EAAE,IAAI,IAI5B,EAAK,SAAS,KACjB,EAAK,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI,GAC5B,CAAC,EAAK,EAAE,CAAC,IAAI,KAId,EAAQ,KAAK,CAAC,OAAU,CAAI;CACpC;AACD;AAQA,SAAS,EAAa,GAAgB,GAAkC;CACvE,QAAQ,MAAY;EACnB,IAAM,IAA2C,CAAC,GAC5C,IAAyC,CAAC;EAEhD,KAAK,IAAM,CAAC,GAAM,MAAW,GAAS;GACrC,IAAI,CAAC,EAAO,cAAc,CAAC,EAAO,aAAa;GAC/C,IAAM,IAAO,EAAO,aAAa,EAAO,aAClC,IAAY,KAAS,QAAQ,EAAO,cAAc,GAClD,IAAa,KAAU,QAAQ,EAAO,eAAe;GAC3D,AAAI,KAAa,IAChB,EAAO,KAAK;IAAE;IAAM;GAAK,CAAC,IAE1B,EAAK,KAAK;IAAE;IAAM;GAAK,CAAC;EAE1B;EAgBA,OAbA,EAAO,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI,GAEjC,EAAO,SAAS,IACZ,EAAO,KAAK,MAAM,EAAE,IAAI,IAI5B,EAAK,SAAS,KACjB,EAAK,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI,GAC5B,CAAC,EAAK,EAAE,CAAC,IAAI,KAId,EAAQ,KAAK,CAAC,OAAU,CAAI;CACpC;AACD;AAQA,SAAS,EAAU,GAAiC;CACnD,QAAQ,MAAY;EACnB,IAAM,IAA8C,CAAC,GAC/C,IAA4C,CAAC;EAEnD,KAAK,IAAM,CAAC,GAAM,MAAW,GAC5B,AAAI,EAAO,WAAW,QAAQ,EAAO,WAAW,IAC/C,EAAO,KAAK;GAAE;GAAM,SAAS,EAAO;EAAQ,CAAC,IACnC,EAAO,WAAW,QAC5B,EAAK,KAAK;GAAE;GAAM,SAAS,EAAO;EAAQ,CAAC;EAkB7C,OAbA,EAAO,MAAM,GAAG,MAAM,EAAE,UAAU,EAAE,OAAO,GAEvC,EAAO,SAAS,IACZ,EAAO,KAAK,MAAM,EAAE,IAAI,IAI5B,EAAK,SAAS,KACjB,EAAK,MAAM,GAAG,MAAM,EAAE,UAAU,EAAE,OAAO,GAClC,CAAC,EAAK,EAAE,CAAC,IAAI,KAId,EAAQ,KAAK,CAAC,OAAU,CAAI;CACpC;AACD;AAOA,SAAS,GAAc,GAAkD;CACxE,IAAI,IAAO,EAAQ;CAEnB,KAAK,IAAM,KAAS,GAAS;EAC5B,IAAM,GAAG,KAAU,GACb,GAAG,KAAc,GAEjB,KAAQ,EAAO,cAAc,MAAM,EAAO,eAAe,IACzD,KAAY,EAAW,cAAc,MAAM,EAAW,eAAe;EAE3E,IAAI,MAAS,GAAU;GACtB,AAAI,IAAO,MAAU,IAAO;GAC5B;EACD;EAEA,CAAK,EAAO,WAAW,MAAM,EAAW,WAAW,OAClD,IAAO;CAET;CAEA,OAAO,EAAK;AACb;AAGA,SAAS,GAAqB,GAAsF;CACnH,IAAM,IAAS,OAAO,QAAQ,CAAU,CAAC,CAAC,QAAQ,GAAG,OAAY,CAAC,EAAO,OAAO;CAChF,IAAI,EAAO,SAAS,GAAG,OAAO,OAAO,YAAY,CAAM;CAEvD,IAAM,IAAU,OAAO,QAAQ,CAAU;CACzC,IAAI,EAAQ,WAAW,GAAG,OAAO,CAAC;CAClC,IAAM,IAAS,EAAU,CAAC,CAAC,CAAC,CAAO,GAC7B,IAAS,EAAO,WAAW,IAAI,EAAO,KAAK,EAAa,GAAG,CAAC,CAAC,CAAC,CAAO,CAAC,CAAC;CAC7E,OAAO,GAAG,IAAS,EAAW,GAAQ;AACvC;AAMA,IAAa,KAAb,MAAoB;CACnB;CAEA,KAA8B;EAC7B,SAAS,IAAI,EAAkC,KAAA,CAAS;EACxD,WAAW,IAAI,EAA4C,CAAC,CAAC;EAC7D,OAAO,IAAI,EAAgC,KAAA,CAAS;EACpD,OAAO,IAAI,EAA2B,KAAA,CAAS;EAC/C,QAAQ,IAAI,EAAwC,KAAA,CAAS;EAC7D,QAAQ,IAAI,EAAmC,KAAA,CAAS;CACzD;CACA,MAAe,EAAU,KAAKM,EAAI;CAElC,KAAW,IAAI,EAAO;CACtB,qBAAgB,IAAI,QAAqE;CAEzF,YAAY,GAA6B;EASxC,AARA,KAAK,KAAK;GACT,WAAW,EAAO,GAAO,SAAS;GAClC,QAAQ,EAAO,GAAO,MAAM;GAC5B,WAAW,EAAO,GAAO,SAAS;EACnC,GAEA,KAAKC,GAAS,IAAI,KAAKC,GAAY,KAAK,IAAI,CAAC,GAC7C,KAAKD,GAAS,IAAI,KAAKE,GAAc,KAAK,IAAI,CAAC,GAC/C,KAAKF,GAAS,IAAI,KAAKG,GAAa,KAAK,IAAI,CAAC;CAC/C;CAEA,GAAY,GAAsB;EACjC,IAAM,IAAY,EAAO,IAAI,KAAK,GAAG,SAAS;EAC9C,IAAI,CAAC,GAAW;EAEhB,IAAM,IAAU,EAAO,IAAI,EAAU,IAAI,OAAO,CAAC,EAAE;EAC9C,KAEL,EAAO,IAAI,KAAKJ,GAAK,SAAS,CAAO;CACtC;CAEA,GAAc,GAAsB;EACnC,IAAM,IAAY,EAAO,IAAI,KAAK,GAAG,SAAS;EAC9C,IAAI,CAAC,GAAW;GACf,KAAKA,GAAK,MAAM,IAAI,KAAA,CAAS;GAC7B;EACD;EAEA,IAAM,IAAa,EAAO,IAAI,KAAKA,GAAK,OAAO,CAAC,EAAE,cAAc,CAAC;EACjE,KAAKA,GAAK,MAAM,IAAI,KAAA,CAAS;EAC7B,IAAI,IAAQ,KAAKK,GAAc,IAAI,CAAS;EAC5C,AAAK,MACJ,oBAAQ,IAAI,IAAI,GAChB,KAAKA,GAAc,IAAI,GAAW,CAAK;EAExC,IAAM,IAAQ,IAAI,IAAI,OAAO,KAAK,CAAU,CAAC;EAC7C,KAAK,IAAM,KAAQ,EAAM,KAAK,GAC7B,AAAK,EAAM,IAAI,CAAI,KAAG,EAAM,OAAO,CAAI;EAGxC,EAAO,MAAM,YAAY;GACxB,IAAM,IAAiD,CAAC,GAKlD,IAAY,EAAO,OAAO,WAAW,KAAA,CAAS;GAEpD,KAAK,IAAM,CAAC,GAAM,MAAW,OAAO,QAAQ,CAAU,GAAG;IACxD,IAAM,IAAY,EAAiC,IAC7C,IAAM,IAAW,EAAS,CAAM,IAAI,KAAK,UAAU,CAAM,GACzD,IAAS,EAAM,IAAI,CAAI,GACzB,IAAmC;IACvC,IAAI,GAAQ,QAAQ,GACnB,IAAc,EAAO;SACf;KACN,IAAI,IAAS;KACb,IAAI;MACH,IAAc,MAAM,QAAQ,KAAK,CAAC,EAAU,CAAM,GAAG,CAAS,CAAC;KAChE,SAAS,GAAK;MAEb,AADA,IAAS,IACT,QAAQ,KACP,4BAA4B,EAAK,IAAI,EAAO,MAAM,kDAClD,CACD;KACD;KACA,AAAI,CAAC,KAAU,MAAgB,KAAA,KAC9B,EAAM,IAAI,GAAM;MACf,KAAK,IAAW,EAAS,CAAM,IAAI,KAAK,UAAU,CAAM;MACxD,WAAW;KACZ,CAAC;IAEH;IAGA,IAAI,EAAO,MAAM,SAAS;IAE1B,AAAI,MAAa,EAAU,KAAQ;GACpC;GAEA,IAAM,IACL,OAAO,KAAK,CAAS,CAAC,CAAC,WAAW,KAAK,OAAO,KAAK,CAAU,CAAC,CAAC,SAAS,IAAI,gBAAgB,KAAA;GAM7F,AALI,MAAU,iBACb,QAAQ,KAAK,iDAAiD,CAAU,GAGzE,KAAKL,GAAK,MAAM,IAAI,CAAK,GACzB,KAAKA,GAAK,UAAU,IAAI,CAAS;EAClC,CAAC;CACF;CAEA,GAAa,GAAsB;EAClC,IAAM,IAAY,GAAqB,EAAO,IAAI,KAAKA,GAAK,SAAS,CAAC;EACtE,IAAI,OAAO,KAAK,CAAS,CAAC,CAAC,WAAW,GAAG;EAEzC,IAAM,IAAS,EAAO,IAAI,KAAK,GAAG,MAAM;EAGxC,IAAI,GAAQ,QAAQ,EAAO,QAAQ,GAAW;GAC7C,IAAM,IAAS,EAAU,EAAO;GAGhC,AAFA,EAAO,IAAI,KAAKA,GAAK,OAAO,EAAO,IAAI,GACvC,EAAO,IAAI,KAAKA,GAAK,QAAQ,CAAM,GACnC,EAAO,IAAI,KAAKA,GAAK,QAAQ,EAAgB,CAAM,CAAC;GACpD;EACD;EAGA,IAAI,IAAkB;EACtB,IAAI,CAAC,GAAQ,SAAS;GACrB,IAAM,IAAY,EAAO,IAAI,KAAK,GAAG,SAAS,GACxC,IAAa,IAAY,EAAO,IAAI,EAAU,GAAG,UAAU,IAAI,KAAA,GAC/D,IAAW,KAAc,EAAO,IAAI,EAAW,KAAK,CAAC,CAAC;GAC5D,IAAI,KAAY,MAAM;IAErB,IAAM,IAAc,KAAK,MAAM,IAAW,EAAG;IAC7C,IAAkB;KAAE,GAAG;KAAQ,SAAS;IAAY;GACrD;EACD;EAEA,IAAM,IAAW,KAAKM,GAAQ,GAAW,CAAe;EACxD,IAAI,CAAC,GAAU;EAEf,IAAM,IAAS,EAAU;EAIzB,AAFA,EAAO,IAAI,KAAKN,GAAK,OAAO,CAAQ,GACpC,EAAO,IAAI,KAAKA,GAAK,QAAQ,CAAM,GACnC,EAAO,IAAI,KAAKA,GAAK,QAAQ,EAAgB,CAAM,CAAC;CACrD;CASA,GAAQ,GAAiD,GAAqC;EAC7F,IAAM,IAAU,OAAO,QAAQ,CAAU;EACzC,IAAI,EAAQ,WAAW,GAAG;EAC1B,IAAI,EAAQ,WAAW,GAAG,OAAO,EAAQ,EAAE,CAAC;EAG5C,IAAM,IAA6B,CAAC;EAapC,IAXI,GAAQ,UAAU,QACrB,EAAQ,KAAK,GAAS,EAAO,MAAM,CAAC,IAEjC,GAAQ,SAAS,QAAQ,GAAQ,UAAU,SAC9C,EAAQ,KAAK,EAAa,EAAO,OAAO,EAAO,MAAM,CAAC,GAEnD,GAAQ,WAAW,QACtB,EAAQ,KAAK,EAAU,EAAO,OAAO,CAAC,GAInC,EAAQ,WAAW,GACtB,OAAO,GAAc,CAAO;EAI7B,IAAM,IAAW,EAAQ,KAAK,MAAM,EAAE,CAAO,CAAC,GAGxC,IAAO,EAAS,KAAK,MAAM,IAAI,IAAI,CAAC,CAAC;EAE3C,KAAK,IAAM,KAAQ,EAAS,IAC3B,IAAI,EAAK,OAAO,MAAM,EAAE,IAAI,CAAI,CAAC,GAChC,OAAO;EAIT,QAAQ,KAAK,oEAAoE;CAElF;CAEA,QAAc;EACb,KAAKC,GAAS,MAAM;CACrB;AACD"}
|
package/support/element.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"element.d.ts","sourceRoot":"","sources":["../../src/support/element.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"element.d.ts","sourceRoot":"","sources":["../../src/support/element.ts"],"names":[],"mappings":"AAcA,QAAA,MAAM,QAAQ,YAAI,MAAM,EAAE,SAAS,CAAU,CAAC;AAC9C,KAAK,QAAQ,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AAO1C,MAAM,MAAM,IAAI,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;AAE5D,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,WAAW;;IAQvD,MAAM,CAAC,kBAAkB,+BAAY;IAErC,cAMC;IAED,iBAAiB,SAGhB;IAED,oBAAoB,SAGnB;IAED,wBAAwB,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,QAczF;IAED,IAAI,IAAI,IAAI,IAAI,CAEf;IAED,IAAI,IAAI,CAAC,IAAI,EAAE,IAAI,EAElB;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,OAAO,CAAC,OAAO,EAAE,OAAO,EAE3B;CAgND;AAID,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,qBAAqB;QAC9B,mBAAmB,EAAE,eAAe,CAAC;KACrC;CACD"}
|