diff --git a/translation-demo/package.json b/translation-demo/package.json index ce1859b..be6f86d 100644 --- a/translation-demo/package.json +++ b/translation-demo/package.json @@ -12,9 +12,9 @@ }, "dependencies": { "@fishjam-cloud/react-client": "0.28.3", - "@moq/publish": "0.2.15", - "@moq/signals": "0.1.9", - "@moq/watch": "0.2.17", + "@moq/publish": "0.4.4", + "@moq/signals": "0.2.2", + "@moq/watch": "0.5.0", "@radix-ui/react-icons": "^1.3.2", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-popover": "^1.1.17", @@ -44,5 +44,8 @@ "typescript": "^5.7.3", "vite": "^6.0.11" }, + "resolutions": { + "@moq/net": "0.3.1" + }, "packageManager": "yarn@4.6.0" } diff --git a/translation-demo/service/uv.lock b/translation-demo/service/uv.lock index bf92860..3a4ff7e 100644 --- a/translation-demo/service/uv.lock +++ b/translation-demo/service/uv.lock @@ -352,15 +352,15 @@ wheels = [ [[package]] name = "moq-ffi" -version = "0.2.21" +version = "0.2.33" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/18/b5/03952368282fdc8492ffb740201671e1cfd44f40d9777855386a5e399841/moq_ffi-0.2.21.tar.gz", hash = "sha256:d815772232f210e794616ed55fbb34704fabb09fabef56d627ee2f1743c1dc05", size = 1613999, upload-time = "2026-06-16T07:15:28.009Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/49/f8a28010b26ce6397d8a27d77182452c36f5946ab6c9ea704e23b9950bb7/moq_ffi-0.2.33.tar.gz", hash = "sha256:f05ca1ac72f24e041bdce7bf5e671850a670986e8f85dcc8b624cbad6a107de3", size = 1815208, upload-time = "2026-07-18T14:08:37.475Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/33/4b/b8db087a86dc010637e7bc4626c4c1dab0865859f2eb9b76e1b29c319e3d/moq_ffi-0.2.21-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:51273f13459f9fcb4a40c5a768dec640fbd42b51810ac0791ffe6d2bb1f309ec", size = 6915731, upload-time = "2026-06-16T07:15:17.059Z" }, - { url = "https://files.pythonhosted.org/packages/8d/f0/2ee3dd4720f8db2c123d0bc8081084ff0f86c8b349902d59cde1fc52080f/moq_ffi-0.2.21-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b9a797acabd4dfa2221c70fd838271abf0fe88de2f49fd07e6e7fffbb577facc", size = 6552728, upload-time = "2026-06-16T07:15:19.142Z" }, - { url = "https://files.pythonhosted.org/packages/de/7b/7e605b76e2bb6ddffcbea1faeb71d7cd44f1ac36c6250ed73863a9be0ae1/moq_ffi-0.2.21-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f7488e3b93b94e5f216cca4e09a0c6581c969e5c40560664256a3e5bcb61feb", size = 7347515, upload-time = "2026-06-16T07:15:21.224Z" }, - { url = "https://files.pythonhosted.org/packages/b8/bf/6842f3b9db44d25b4bab4969e68fea78f587e30e93f65c4c4aca368b700c/moq_ffi-0.2.21-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:d12a88543cf3944219c0bbfb6665d5546ad5b14d228003983e42782a6927c70f", size = 7414829, upload-time = "2026-06-16T07:15:23.638Z" }, - { url = "https://files.pythonhosted.org/packages/ac/0d/a586635923d22dc31c3ff4f0517edcb9e0e767879510f608f7e090f10567/moq_ffi-0.2.21-py3-none-win_amd64.whl", hash = "sha256:85b4198f968e34ba907ae4e5d24021167a0bd291016f1fe97c8daa09b140a215", size = 5646579, upload-time = "2026-06-16T07:15:25.797Z" }, + { url = "https://files.pythonhosted.org/packages/cc/74/478759b0cb3f5f1b05304182ca786f072e35333c21c771bbfa5514ac81a5/moq_ffi-0.2.33-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e8611aa05578c5167bec5a8f6bc4a2cdc093ef00d3abf0ea36b9e1d3e31f2678", size = 6654077, upload-time = "2026-07-18T14:08:29.694Z" }, + { url = "https://files.pythonhosted.org/packages/0b/4b/801796478a5e29d6a45fd13c333d548a2061578907ccb3a1e5d151b35741/moq_ffi-0.2.33-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d882d799cb8af8598cb70063ade6dd5f8dadad79d197992217c6f70a7a00cc2d", size = 6248557, upload-time = "2026-07-18T14:08:31.351Z" }, + { url = "https://files.pythonhosted.org/packages/ac/c5/4b3c7c19f443464e164cc44af423a9db212b0552ef75f8990042441fe45c/moq_ffi-0.2.33-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f341f31c9bf3299b1b2de8a4cc15d248cc5494c628d74e510fd250afdbda2582", size = 7145816, upload-time = "2026-07-18T14:08:32.911Z" }, + { url = "https://files.pythonhosted.org/packages/da/e4/83aad890088bc4fc10275e714b60b8fce66b09ce532a321a966b0a58acfb/moq_ffi-0.2.33-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:b734ced0cf0c8ae786b19d12f911635bfe8a10279d5528e46f9f9d71ed3f4f2d", size = 7451710, upload-time = "2026-07-18T14:08:34.413Z" }, + { url = "https://files.pythonhosted.org/packages/78/53/03aa89be4a08b3befe58977d6c1deab91616088d540ba02dea9cce9ebfe4/moq_ffi-0.2.33-py3-none-win_amd64.whl", hash = "sha256:fafb24c57319b3fa15621cdb320fe0c4b7212129172fb033a3287aa8414a9e29", size = 5486230, upload-time = "2026-07-18T14:08:35.971Z" }, ] [[package]] diff --git a/translation-demo/src/hooks/useMoqConnection.ts b/translation-demo/src/hooks/useMoqConnection.ts index 66ed595..1858f6a 100644 --- a/translation-demo/src/hooks/useMoqConnection.ts +++ b/translation-demo/src/hooks/useMoqConnection.ts @@ -46,10 +46,9 @@ export const useMoqConnection = () => { const [connectionSignal] = useState( () => - new Signal(undefined), + new Signal(undefined), ); - const reloadRef = useRef(null); const sessionCleanupRef = useRef<(() => void) | null>(null); const streamRef = useRef(null); const translationProvidersRef = useRef( @@ -75,7 +74,9 @@ export const useMoqConnection = () => { } const activeLanguages = new Set( - Object.keys(provider.broadcast.catalog.peek()?.audio?.renditions ?? {}), + Object.keys( + provider.broadcast.out.catalog.peek()?.audio?.renditions ?? {}, + ), ); for (const language of activeLanguages) { @@ -107,7 +108,7 @@ export const useMoqConnection = () => { } } - const catalog = current.broadcast.catalog.peek(); + const catalog = current.broadcast.out.catalog.peek(); const hasVideo = !!catalog?.video && Object.keys(catalog.video.renditions ?? {}).length > 0; @@ -154,15 +155,15 @@ export const useMoqConnection = () => { const broadcast = new Watch.Broadcast({ connection: connectionSignal, enabled: true, - name: Publish.Lite.Path.from(path), + name: Publish.Net.Path.from(path), }); const sync = () => { refreshStream(); }; - const disposeCatalog = broadcast.catalog.subscribe(sync); - const disposeStatus = broadcast.status.subscribe(sync); + const disposeCatalog = broadcast.out.catalog.subscribe(sync); + const disposeStatus = broadcast.out.status.subscribe(sync); streamRef.current = { path, @@ -187,9 +188,8 @@ export const useMoqConnection = () => { const broadcast = new Watch.Broadcast({ connection: connectionSignal, - announced: reloadRef.current?.announced, enabled: true, - name: Publish.Lite.Path.from(path), + name: Publish.Net.Path.from(path), reload: true, }); @@ -197,8 +197,8 @@ export const useMoqConnection = () => { refreshStream(); }; - const disposeCatalog = broadcast.catalog.subscribe(sync); - const disposeStatus = broadcast.status.subscribe(sync); + const disposeCatalog = broadcast.out.catalog.subscribe(sync); + const disposeStatus = broadcast.out.status.subscribe(sync); translationProvidersRef.current.set(path, { path, @@ -220,7 +220,6 @@ export const useMoqConnection = () => { const disconnect = useCallback(() => { sessionCleanupRef.current?.(); sessionCleanupRef.current = null; - reloadRef.current = null; connectionSignal.set(undefined); setConnectionStatus("disconnected"); setHasSession(false); @@ -234,13 +233,11 @@ export const useMoqConnection = () => { setHasSession(true); setConnectionStatus("connecting"); - const reload = new Publish.Lite.Connection.Reload({ + const reload = new Publish.Net.Connection.Reload({ enabled: true, url: connectionUrl, }); - reloadRef.current = reload; - const disposeStatus = reload.status.subscribe((value) => { setConnectionStatus(value); }); @@ -249,7 +246,7 @@ export const useMoqConnection = () => { connectionSignal.set(connection); }); - const syncStream = (announced: Set) => { + const syncStream = (announced: Iterable) => { let streamPath: string | undefined; const providerPaths = new Map< string, @@ -295,14 +292,38 @@ export const useMoqConnection = () => { refreshStream(); }; - const disposeDiscovery = reload.announced.subscribe((announced) => { - syncStream(announced); - }); + const announcements = reload.announced(); + const announced = new Map(); + let discoveryCancelled = false; + + void (async () => { + try { + for (;;) { + const event = await announcements.next(); + if (!event || discoveryCancelled) { + break; + } + + const path = event.path.toString(); + if (event.active) { + announced.set(path, event.path); + } else { + announced.delete(path); + } + syncStream(announced.values()); + } + } catch (error) { + if (!discoveryCancelled) { + console.error("MoQ broadcast discovery failed", error); + } + } + })(); - syncStream(reload.announced.peek()); + syncStream([]); sessionCleanupRef.current = () => { - disposeDiscovery(); + discoveryCancelled = true; + announcements.close(); disposeEstablished(); disposeStatus(); reload.close(); diff --git a/translation-demo/src/hooks/usePublisher.ts b/translation-demo/src/hooks/usePublisher.ts index 8f441db..b49a78e 100644 --- a/translation-demo/src/hooks/usePublisher.ts +++ b/translation-demo/src/hooks/usePublisher.ts @@ -32,7 +32,7 @@ export const usePublisher = () => { ); const [connectionSignal] = useState( () => - new Signal(undefined), + new Signal(undefined), ); const [streamName, setStreamName] = useState(null); @@ -44,12 +44,13 @@ export const usePublisher = () => { // Holds the teardown for the active session; its presence also means "already publishing". const sessionCleanupRef = useRef<(() => void) | null>(null); - const cameraDevices = useSignalValue(camera.device.available) ?? []; - const microphoneDevices = useSignalValue(microphone.device.available) ?? []; - const selectedCameraId = useSignalValue(camera.device.active); - const selectedMicrophoneId = useSignalValue(microphone.device.active); + const cameraDevices = useSignalValue(camera.device.out.available) ?? []; + const microphoneDevices = + useSignalValue(microphone.device.out.available) ?? []; + const selectedCameraId = useSignalValue(camera.device.out.active); + const selectedMicrophoneId = useSignalValue(microphone.device.out.active); - const cameraTrack = useSignalValue(camera.source); + const cameraTrack = useSignalValue(camera.out.source); const previewStream = useMemo( () => (cameraTrack ? new MediaStream([cameraTrack]) : null), [cameraTrack], @@ -109,7 +110,7 @@ export const usePublisher = () => { return; } - const reload = new Publish.Lite.Connection.Reload({ + const reload = new Publish.Net.Connection.Reload({ enabled: true, url: connectionUrl, }); @@ -120,35 +121,42 @@ export const usePublisher = () => { }); // Single-segment broadcast leaf; the `translations` namespace is in the connection URL. - // The hd/sd video and audio encoders must be explicitly enabled (with a config), + // The video and audio encoders must be explicitly enabled (with a config), // otherwise the catalog publishes with no active tracks and the relay aborts it. + const capture = new Publish.Video.Capture({ source: camera.out.source }); const broadcast = new Publish.Broadcast({ connection: connectionSignal, enabled: true, - name: Publish.Lite.Path.from(name), - audio: { - enabled: microphone.enabled, - source: microphone.source, + name: Publish.Net.Path.from(name), + display: capture.out.display, + }); + const video = new Publish.Video.Encoder("video/hd", { + broadcast, + capture, + enabled: true, + config: { + maxPixels: 1280 * 720, + maxBitrate: 1_000_000, + frameRate: 30, }, - video: { - source: camera.source, - // Single 720p rendition; the lower-quality sd encoder stays off. - hd: { - enabled: camera.enabled, - config: { - maxPixels: 1280 * 720, - maxBitrate: 1_000_000, - frameRate: 30, - }, - }, - sd: { enabled: false }, + }); + const audio = new Publish.Audio.Encoder("audio", { + broadcast, + codec: { + mime: "opus", + usedtx: false, }, + enabled: true, + source: microphone.out.source, }); sessionCleanupRef.current = () => { disposeEstablished(); disposeStatus(); + audio.close(); + video.close(); broadcast.close(); + capture.close(); reload.close(); }; }, [ diff --git a/translation-demo/src/hooks/useSyncedStreamPlayer.ts b/translation-demo/src/hooks/useSyncedStreamPlayer.ts index 374a229..bd744d2 100644 --- a/translation-demo/src/hooks/useSyncedStreamPlayer.ts +++ b/translation-demo/src/hooks/useSyncedStreamPlayer.ts @@ -48,14 +48,9 @@ export const ORIGINAL_AUDIO_KEY = "original"; // (STREAM_DELAY_MS − providerDelay) — that lands it on the exact same play-head as the video. type AudioVoice = { key: string; - // The original stream broadcast or a shared translation provider broadcast — both owned by the - // connection, so a voice never closes its broadcast. - broadcast: Watch.Broadcast; - // Language rendition to select for a translation; undefined uses the default (original) track. - trackName?: string; sync: Watch.Sync; - // True for a translation's dedicated clock (which we created and must close). - ownsSync: boolean; + // Present only for a translation's dedicated clock, which we created and must close. + latency?: Watch.Signals.Signal; source: Watch.Audio.Source; decoder: Watch.Audio.Decoder; signals: Watch.Signals.Effect; @@ -72,6 +67,7 @@ type VoiceConfig = { trackName?: string; desiredGain: number; useOwnSync: boolean; + source?: Watch.Audio.Source; }; /** @@ -87,7 +83,7 @@ type VoiceConfig = { export class SyncedStreamPlayer { readonly sync: Watch.Sync; // Media timestamp of the most recent decoded video frame; undefined until the first frame. - readonly videoTimestamp: Watch.Video.Decoder["timestamp"]; + readonly videoTimestamp: Watch.Video.Decoder["out"]["timestamp"]; // The track currently heard, and the one warming up (if any) — exposed for the UI. readonly audibleKey = new Watch.Signals.Signal(ORIGINAL_AUDIO_KEY); readonly pendingKey = new Watch.Signals.Signal(undefined); @@ -96,6 +92,9 @@ export class SyncedStreamPlayer { readonly #videoSource: Watch.Video.Source; readonly #videoDecoder: Watch.Video.Decoder; readonly #renderer: Watch.Video.Renderer; + readonly #canvas = new Watch.Signals.Signal< + HTMLCanvasElement | undefined + >(undefined); readonly #voices = new Map(); #warmupTimer: number | undefined; @@ -106,20 +105,32 @@ export class SyncedStreamPlayer { ) { this.#connection = connection; + this.#videoSource = new Watch.Video.Source({ + broadcast: originalBroadcast, + supported: Watch.Video.Decoder.supported, + }); + const originalAudioSource = new Watch.Audio.Source({ + broadcast: originalBroadcast, + supported: Watch.Audio.Decoder.supported, + }); + this.sync = new Watch.Sync({ latency: STREAM_DELAY_MS as Watch.Net.Time.Milli, connection, + video: this.#videoSource.out.jitter, + audio: originalAudioSource.out.jitter, }); - this.#videoSource = new Watch.Video.Source(this.sync, { - broadcast: originalBroadcast, - }); - this.#videoDecoder = new Watch.Video.Decoder(this.#videoSource); + this.#videoDecoder = new Watch.Video.Decoder( + this.#videoSource, + this.sync, + { enabled: true }, + ); this.#renderer = new Watch.Video.Renderer(this.#videoDecoder, { - paused: false, + canvas: this.#canvas, visible: MEDIA_VISIBLE, }); - this.videoTimestamp = this.#videoDecoder.timestamp; + this.videoTimestamp = this.#videoDecoder.out.timestamp; // The original audio is always present, kept warm, and audible by default. It shares the // player clock (it plays at the same delay as the video). @@ -130,12 +141,13 @@ export class SyncedStreamPlayer { broadcast: originalBroadcast, desiredGain: 1, useOwnSync: false, + source: originalAudioSource, }), ); } attachCanvas(canvas: HTMLCanvasElement | null): void { - this.#renderer.canvas.set(canvas ?? undefined); + this.#canvas.set(canvas ?? undefined); } /** @@ -206,29 +218,43 @@ export class SyncedStreamPlayer { trackName, desiredGain, useOwnSync, + source: providedSource, }: VoiceConfig): AudioVoice { + const target = new Watch.Signals.Signal( + undefined, + ); + const source = + providedSource ?? + new Watch.Audio.Source({ + broadcast, + target, + supported: Watch.Audio.Decoder.supported, + }); + const latency = useOwnSync + ? new Watch.Signals.Signal( + STREAM_DELAY_MS as Watch.Net.Time.Milli, + ) + : undefined; + // A translation gets its own clock so its jitter buffer (and thus playback delay) can be // shortened to compensate for the provider delay; the original shares the player clock. - const sync = useOwnSync + const sync = latency ? new Watch.Sync({ - latency: STREAM_DELAY_MS as Watch.Net.Time.Milli, + latency, connection: this.#connection, + audio: source.out.jitter, }) : this.sync; - const source = new Watch.Audio.Source(sync, { broadcast }); - const decoder = new Watch.Audio.Decoder(source); // Download & decode immediately so the track warms up (and stays ready) while silent. - decoder.enabled.set(true); + const decoder = new Watch.Audio.Decoder(source, sync, { enabled: true }); const gain = new Watch.Signals.Signal(undefined); const signals = new Watch.Signals.Effect(); const voice: AudioVoice = { key, - broadcast, - trackName, sync, - ownsSync: useOwnSync, + latency, source, decoder, signals, @@ -243,20 +269,22 @@ export class SyncedStreamPlayer { // rendition so the decoder subscribes (never two subscriptions to the track at once). if (trackName) { signals.run((effect) => { - const active = effect.get(broadcast.active); + const active = effect.get(broadcast.out.active); if (!active) { return; } const renditions = - effect.get(broadcast.catalog)?.audio?.renditions ?? {}; + effect.get(broadcast.out.catalog)?.audio?.renditions ?? {}; if (renditions[trackName]) { - source.target.set({ name: trackName }); + target.set({ name: trackName }); return; } - source.target.set(undefined); - const trigger = active.subscribe(trackName, AUDIO_PRIORITY); + target.set(undefined); + const trigger = active.subscribe(trackName, { + priority: AUDIO_PRIORITY, + }); effect.cleanup(() => trigger.close()); }); } @@ -265,8 +293,8 @@ export class SyncedStreamPlayer { // gain 0 the node stays in the graph, so the worklet keeps draining in sync — crossfading is // then just a gain ramp on an already time-aligned track. signals.run((effect) => { - const context = effect.get(decoder.context); - const root = effect.get(decoder.root); + const context = effect.get(decoder.out.context); + const root = effect.get(decoder.out.root); if (!context || !root) { return; } @@ -322,12 +350,12 @@ export class SyncedStreamPlayer { // same media position as the video instead of `providerDelay` behind it. Done once, while the // ring is still filling. providerDelay = (latest original ts) − (latest translation ts). #compensateProviderDelay(voice: AudioVoice): void { - if (!voice.ownsSync || voice.latencyAdjusted) { + if (!voice.latency || voice.latencyAdjusted) { return; } - const originalTs = this.sync.timestamp.peek(); - const translationTs = voice.sync.timestamp.peek(); + const originalTs = this.sync.out.timestamp.peek(); + const translationTs = voice.sync.out.timestamp.peek(); if (originalTs === undefined || translationTs === undefined) { return; } @@ -337,7 +365,7 @@ export class SyncedStreamPlayer { MIN_TRANSLATION_LATENCY_MS, STREAM_DELAY_MS - providerDelay, ); - voice.sync.latency.set(latency as Watch.Net.Time.Milli); + voice.latency.set(latency as Watch.Net.Time.Milli); voice.latencyAdjusted = true; } @@ -353,7 +381,7 @@ export class SyncedStreamPlayer { // The ring un-stalls when the play-head reaches buffered audio, so this is the moment the new // track can take over without a silent gap. #isEmitting(voice: AudioVoice): boolean { - return voice.decoder.stalled.peek() === false; + return voice.decoder.out.stalled.peek() === false; } #crossfadeTo(key: string): void { @@ -410,7 +438,7 @@ export class SyncedStreamPlayer { voice.signals.close(); voice.decoder.close(); voice.source.close(); - if (voice.ownsSync) { + if (voice.latency) { voice.sync.close(); } } diff --git a/translation-demo/src/hooks/useTranslationTranscription.ts b/translation-demo/src/hooks/useTranslationTranscription.ts index 6a5afb6..cf888ad 100644 --- a/translation-demo/src/hooks/useTranslationTranscription.ts +++ b/translation-demo/src/hooks/useTranslationTranscription.ts @@ -42,8 +42,10 @@ const trimCaption = (text: string): string => { return `…${wordBoundary === -1 ? tail : tail.slice(wordBoundary + 1)}`; }; -// `Watch.Broadcast.active` exposes the underlying network broadcast. -type MoqBroadcast = NonNullable>; +// `Watch.Broadcast.out.active` exposes the underlying network broadcast. +type MoqBroadcast = NonNullable< + ReturnType +>; // The translation audio playback clock (ms), used to reveal each caption segment only // once the heard audio reaches its timestamp. @@ -221,7 +223,7 @@ export const useTranslationTranscription = ( const track = activeBroadcast.subscribe( getTranscriptionTrackName(trackName), - TRANSCRIPTION_PRIORITY, + { priority: TRANSCRIPTION_PRIORITY }, ); let cancelled = false; entry.closeTrack = () => { @@ -264,8 +266,8 @@ export const useTranslationTranscription = ( }; entryRef.current = entry; - watchActive(broadcast.active.peek()); - entry.dispose = broadcast.active.changed(watchActive); + watchActive(broadcast.out.active.peek()); + entry.dispose = broadcast.out.active.changed(watchActive); recompute(); return () => { diff --git a/translation-demo/src/utils/types.ts b/translation-demo/src/utils/types.ts index 1ff6149..5a5dcfb 100644 --- a/translation-demo/src/utils/types.ts +++ b/translation-demo/src/utils/types.ts @@ -3,7 +3,7 @@ import type { Signal } from "@moq/signals"; import type * as Watch from "@moq/watch"; export type MoqConnectionSignal = Signal< - Publish.Lite.Connection.Established | undefined + Publish.Net.Connection.Established | undefined >; type TranslationStatus = "active" | "requestable"; diff --git a/translation-demo/yarn.lock b/translation-demo/yarn.lock index d33b316..b9369f3 100644 --- a/translation-demo/yarn.lock +++ b/translation-demo/yarn.lock @@ -347,98 +347,128 @@ __metadata: languageName: node linkType: hard -"@libav.js/variant-opus-af@npm:^6.8.8": - version: 6.8.8 - resolution: "@libav.js/variant-opus-af@npm:6.8.8" - checksum: 10c0/733e6c4c0b31d288235bf543fdcf8f85505f0d2d4e90d153418737d6dd8b312f392d4c2532296a5db0c9eff1415c6b39a475c0353c312d267e342e72583a1342 +"@libav.js/variant-opus-af@npm:^6.9.8": + version: 6.10.9 + resolution: "@libav.js/variant-opus-af@npm:6.10.9" + checksum: 10c0/419b47fcd0d65c47d021076b83833407bb2522a8f1aa5a286dda1023e8c8093b08a21cf115375af82d30c9847d6caa90d4f34040e0991ffddeda82162eb3a8eb languageName: node linkType: hard -"@moq/hang@npm:^0.2.10": - version: 0.2.10 - resolution: "@moq/hang@npm:0.2.10" +"@moq/flate@npm:^0.1.1": + version: 0.1.1 + resolution: "@moq/flate@npm:0.1.1" + dependencies: + pako: "npm:^3.0.0" + checksum: 10c0/e19ac515be759886d87f31b2494f18c1d131cf79b8e95636f5ae5b0a0e3b7f1a03926df995240cbe7eb9c17453bac99cb2bba7658edf424cf8605fb98ebd6101 + languageName: node + linkType: hard + +"@moq/hang@npm:^0.3.5": + version: 0.3.5 + resolution: "@moq/hang@npm:0.3.5" dependencies: "@kixelated/libavjs-webcodecs-polyfill": "npm:^0.5.5" - "@libav.js/variant-opus-af": "npm:^6.8.8" - "@moq/loc": "npm:^0.1.1" - "@moq/net": "npm:^0.1.5" - "@moq/signals": "npm:^0.1.9" - "@svta/cml-iso-bmff": "npm:^1.0.2" + "@libav.js/variant-opus-af": "npm:^6.9.8" + "@moq/json": "npm:^0.3.1" + "@moq/loc": "npm:^0.2.1" + "@moq/net": "npm:^0.2.8" + "@moq/signals": "npm:^0.2.2" + "@svta/cml-iso-bmff": "npm:^1.0.4" + "@svta/cml-utils": "npm:1.6.0" zod: "npm:^4.4.3" - checksum: 10c0/37a0e0d39a18abb5d3290c1f7b01f6cd2461e2a6571138fbc7e4542f4b58edd073c847f235f0692a149888347fa7ccf617049d165000028e1eaf7fb5175fcc38 + checksum: 10c0/1065da5fc7f7f79b88f83277b4bfbc5fa3a6e19d2eebfbe684aeb9ea1d881dd97b826038ff6ce52594d78934131278ff38d766aff681c53d5696a9377bd2b19b languageName: node linkType: hard -"@moq/json@npm:^0.0.3": - version: 0.0.3 - resolution: "@moq/json@npm:0.0.3" +"@moq/hang@npm:^0.4.0": + version: 0.4.0 + resolution: "@moq/hang@npm:0.4.0" dependencies: - "@moq/net": "npm:^0.1.5" - "@moq/signals": "npm:^0.1.9" + "@kixelated/libavjs-webcodecs-polyfill": "npm:^0.5.5" + "@libav.js/variant-opus-af": "npm:^6.9.8" + "@moq/json": "npm:^0.3.1" + "@moq/loc": "npm:^0.2.1" + "@moq/net": "npm:^0.3.0" + "@moq/signals": "npm:^0.2.2" + "@svta/cml-iso-bmff": "npm:^1.0.4" + "@svta/cml-utils": "npm:1.6.0" + zod: "npm:^4.4.3" + checksum: 10c0/80123f2a4968fcd895566b4b721bdd5a68fe7cecefa5022ece6feaa848e8f7c9bf0988039c66e59f6a5219c8b60fcbe7dfb9ced36af4bee1a8e470dd593858e2 + languageName: node + linkType: hard + +"@moq/json@npm:^0.3.1": + version: 0.3.1 + resolution: "@moq/json@npm:0.3.1" + dependencies: + "@moq/flate": "npm:^0.1.1" + "@moq/net": "npm:^0.2.3" + "@moq/signals": "npm:^0.2.1" peerDependencies: zod: ^4.0.0 - checksum: 10c0/5fc4eb88869d3a374a31e9813acf43290cc72b15507cb04e7f28af6cdacfd05c82815d31b7d5680c0297357801a52ef5fe48069a362d192fe930b3cc0746fd0d + checksum: 10c0/ede0974f86e6a4c3c972b852be740b1edb3aa4c91f4b9709a2634046958956a92278aac6ff8f7a96db5e416fc54e5cacdde3516469b4b2bb690eb71c40edcf8b languageName: node linkType: hard -"@moq/loc@npm:^0.1.1": - version: 0.1.1 - resolution: "@moq/loc@npm:0.1.1" +"@moq/loc@npm:^0.2.1": + version: 0.2.1 + resolution: "@moq/loc@npm:0.2.1" dependencies: - "@moq/net": "npm:^0.1.5" - checksum: 10c0/3c85212ad6d9cad4869bba7c4cba448fd2ef18e35608c62d11d694b20fc54b882063f10fe992421ef9c9e374abb99b68fb1dc19ed99c2652e51b8a7ca75d1ce0 + "@moq/net": "npm:^0.2.3" + checksum: 10c0/0305d62f7f650734e8bb756d7dfa13ded61abcf280e001733ccc4ca760d23da10a7f3a956353ebf54422e1cd34c5542d9bbaafa963464961119b8848def4f872 languageName: node linkType: hard -"@moq/msf@npm:^0.1.2": - version: 0.1.2 - resolution: "@moq/msf@npm:0.1.2" +"@moq/msf@npm:^0.2.0": + version: 0.2.0 + resolution: "@moq/msf@npm:0.2.0" dependencies: - "@moq/net": "npm:^0.1.5" + "@moq/net": "npm:^0.2.0" zod: "npm:^4.4.3" - checksum: 10c0/26500bc6a5974c4ecc4374d2071b0e8d236939a7b9a5e18698d8892642643671f935d5cc83a673bcf65e4941f3385b0f186d34930fb43287ecc3c8eeee8e4995 + checksum: 10c0/e6d6682d4661ad4b9aaa253fb1124e1031b29007c1b5928353ca903c3e58a2dea1b6f1cb7333fff4da7b7ce2fb7a4c4eadaa6224f63820a0c625ded55bbb08c0 languageName: node linkType: hard -"@moq/net@npm:^0.1.5": - version: 0.1.5 - resolution: "@moq/net@npm:0.1.5" +"@moq/net@npm:0.3.1": + version: 0.3.1 + resolution: "@moq/net@npm:0.3.1" dependencies: - "@moq/qmux": "npm:^0.0.6" - "@moq/signals": "npm:^0.1.9" + "@moq/qmux": "npm:^0.3.3" + "@moq/signals": "npm:^0.2.2" async-mutex: "npm:^0.5.0" peerDependencies: zod: ^4.0.0 - checksum: 10c0/40673f8d0b6bf4ff56fefa657223a50eed8b5885375167c9ae319ff681a3d5cf179eae70a1f29578f93149534c5017e18cb03b13d562a33902dbbce7f8a9cc8f + checksum: 10c0/60eb19e522a858b8ff09419b29ed37f3059a30bdc4e1c8f02e8c52dc403a7c1a5de6673f36dd2f72de52f44ea0f4c987257a828593f1e37b9d6acee628519999 languageName: node linkType: hard -"@moq/publish@npm:0.2.15": - version: 0.2.15 - resolution: "@moq/publish@npm:0.2.15" +"@moq/publish@npm:0.4.4": + version: 0.4.4 + resolution: "@moq/publish@npm:0.4.4" dependencies: - "@moq/hang": "npm:^0.2.10" - "@moq/json": "npm:^0.0.3" - "@moq/net": "npm:^0.1.5" - "@moq/signals": "npm:^0.1.9" - checksum: 10c0/f162e2b70ae101ff3286df44fc2a5a52db7f2de7a9a1b8cc0942c620143765b4ea61f3d30e31938e10afc65d5857655089c88196cf4a2f399ac90f5ec8c4efe1 + "@moq/hang": "npm:^0.3.5" + "@moq/net": "npm:^0.2.8" + "@moq/signals": "npm:^0.2.2" + checksum: 10c0/17252eb1b94491953df9e2cd9971f8fe442feb145f99a804cde2efc62af5713509c55462139b07deb13a632be5b3b0f554051a8a6f2408f922131ac8392a7dde languageName: node linkType: hard -"@moq/qmux@npm:^0.0.6": - version: 0.0.6 - resolution: "@moq/qmux@npm:0.0.6" - checksum: 10c0/f37838fc6f519ff5d2d3243b9e11a13009ab64e7f1d70d42245633a6d4eca6b6a0265cfc352fa2b944fbc21b19abe752e0ffa24f9518975e5f7122d674538fcd +"@moq/qmux@npm:^0.3.3": + version: 0.3.3 + resolution: "@moq/qmux@npm:0.3.3" + dependencies: + "@moq/web-socket-stream": "npm:^0.1.1" + checksum: 10c0/bd9b8d0ed54ca3bb7e53324352d1f4e17f427a9de0c32268eaeea28ad8ec35d327de1014a0d9f7927c92b76a2dc5a193985b31b786c1104579365b0a1996c729 languageName: node linkType: hard -"@moq/signals@npm:0.1.9, @moq/signals@npm:^0.1.9": - version: 0.1.9 - resolution: "@moq/signals@npm:0.1.9" +"@moq/signals@npm:0.2.2, @moq/signals@npm:^0.2.1, @moq/signals@npm:^0.2.2": + version: 0.2.2 + resolution: "@moq/signals@npm:0.2.2" peerDependencies: - "@types/react": ^19.2.15 - react: ^19.0.0 - solid-js: ^1.9.13 + "@types/react": ^19.2.17 + react: ^19.2.8 + solid-js: ^1.9.14 peerDependenciesMeta: "@types/react": optional: true @@ -446,7 +476,7 @@ __metadata: optional: true solid-js: optional: true - checksum: 10c0/919ea3437500ec4fddc288547c1b9c08ba2a0734b954b99368830bfd9bef62a87be4dbb967c2e67ae95ae0a4fff072f54678d0ab9fd1d0a967a1fcbb357e0bc3 + checksum: 10c0/339ae549abe5cf5faab7f9f58a4670fac514f67c6abebc97ffd9ab4bb0e28db73cf7f55857677612a1f927499c438222f7b7efdf9e43ce7df04ab95fe7d49e3f languageName: node linkType: hard @@ -455,9 +485,9 @@ __metadata: resolution: "@moq/translation-demo@workspace:." dependencies: "@fishjam-cloud/react-client": "npm:0.28.3" - "@moq/publish": "npm:0.2.15" - "@moq/signals": "npm:0.1.9" - "@moq/watch": "npm:0.2.17" + "@moq/publish": "npm:0.4.4" + "@moq/signals": "npm:0.2.2" + "@moq/watch": "npm:0.5.0" "@radix-ui/react-icons": "npm:^1.3.2" "@radix-ui/react-label": "npm:^2.1.0" "@radix-ui/react-popover": "npm:^1.1.17" @@ -487,16 +517,22 @@ __metadata: languageName: unknown linkType: soft -"@moq/watch@npm:0.2.17": - version: 0.2.17 - resolution: "@moq/watch@npm:0.2.17" +"@moq/watch@npm:0.5.0": + version: 0.5.0 + resolution: "@moq/watch@npm:0.5.0" dependencies: - "@moq/hang": "npm:^0.2.10" - "@moq/json": "npm:^0.0.3" - "@moq/msf": "npm:^0.1.2" - "@moq/net": "npm:^0.1.5" - "@moq/signals": "npm:^0.1.9" - checksum: 10c0/e25b84f4789d780eb70d913d95ede4198318c655ea9995afcf308e00b5cba5189ed45689e4c3d3e59d0efa522aeeb5e8594dc6db5f357287483779a300d022c4 + "@moq/hang": "npm:^0.4.0" + "@moq/msf": "npm:^0.2.0" + "@moq/net": "npm:^0.3.0" + "@moq/signals": "npm:^0.2.2" + checksum: 10c0/5ae41dbae9cd519388a857ad8fa88fa9d64cf1d45b0814a799312e85523f028ee3357ab29a6f59adc75ea1079cedd65377e416e5a6f18afbf27cab9ef89eb4c8 + languageName: node + linkType: hard + +"@moq/web-socket-stream@npm:^0.1.1": + version: 0.1.1 + resolution: "@moq/web-socket-stream@npm:0.1.1" + checksum: 10c0/96cc69f0eed4009f7c29961dec0e41674b115dac3ec676d7fa7ad370734e477db8b2a9edee264d1d14df9fd320093f5dd30a3472ec8347b6da9f31dcbe343df3 languageName: node linkType: hard @@ -1496,12 +1532,12 @@ __metadata: languageName: node linkType: hard -"@svta/cml-iso-bmff@npm:^1.0.2": - version: 1.0.2 - resolution: "@svta/cml-iso-bmff@npm:1.0.2" +"@svta/cml-iso-bmff@npm:^1.0.4": + version: 1.0.5 + resolution: "@svta/cml-iso-bmff@npm:1.0.5" peerDependencies: - "@svta/cml-utils": 1.5.0 - checksum: 10c0/a1633699df929687d99fb88967f7f796b0835252631f8963261d4615d5a8f877d095ecc4c5b571ef9a3d59c104d4b158bde9829193e6ec296e6afcef4f2fcd8b + "@svta/cml-utils": 1.6.0 + checksum: 10c0/415d9fb6a3145400447a390e4960bccfc907288262ebade60f571526799513d29095374b114b69d71ecaac8ae54dc9c8dd9b236a7c0ffa7a1ebe8eabd9336a34 languageName: node linkType: hard @@ -1512,6 +1548,13 @@ __metadata: languageName: node linkType: hard +"@svta/cml-utils@npm:1.6.0": + version: 1.6.0 + resolution: "@svta/cml-utils@npm:1.6.0" + checksum: 10c0/8937660dd552ba0ff29756220f43db1897e03a646c696a390cd21ff73a6c5fcba10f2523daf260ec3ca6535d97b3d511b98a7c8643b6c6ed0ef85386a70e8d6a + languageName: node + linkType: hard + "@swc/core-darwin-arm64@npm:1.11.10": version: 1.11.10 resolution: "@swc/core-darwin-arm64@npm:1.11.10" @@ -2743,6 +2786,13 @@ __metadata: languageName: node linkType: hard +"pako@npm:^3.0.0": + version: 3.0.1 + resolution: "pako@npm:3.0.1" + checksum: 10c0/1292b2ffcbdf42f95be5519ffc11c4cb8daf065f7681936a5b0fe2028753396fbaea0c22cb22e54a4f12bb0c3217513f6b2261fa77e4dbf8c9bd76efe3eca2cc + languageName: node + linkType: hard + "path-key@npm:^3.1.0": version: 3.1.1 resolution: "path-key@npm:3.1.1"