1
0

feat: stream private office screens

This commit is contained in:
2026-08-11 21:31:15 -07:00
parent d841575315
commit 5ca214e4bb
28 changed files with 1754 additions and 35 deletions
+124
View File
@@ -0,0 +1,124 @@
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import {
fetchEphemeralIceConfiguration,
IceConfigurationUnavailableError,
} from "../media/iceClient.ts";
describe("ephemeral ICE configuration client", () => {
it("posts the exact authenticated request and returns an in-memory RTC configuration", async () => {
let input: RequestInfo | URL | undefined;
let init: RequestInit | undefined;
const result = await fetchEphemeralIceConfiguration({
authenticatedFetch: async (nextInput, nextInit) => {
input = nextInput;
init = nextInit;
return Response.json({
type: "ice-config-grant",
protocolVersion: 1,
requestId: "ice-test-1",
issuedAtMs: 100,
expiresAtMs: 600_000,
iceServers: [
{ urls: ["stun:relay.example.test:3478"] },
{
urls: ["turns:relay.example.test:5349"],
username: "temporary-user-01",
credential: "temporary-password-01",
credentialType: "password",
},
],
});
},
now: () => 200,
requestId: () => "ice-test-1",
});
assert.equal(input, "/api/v1/media/ice");
assert.equal(init?.method, "POST");
assert.equal(init?.credentials, "same-origin");
assert.equal(init?.cache, "no-store");
assert.deepEqual(JSON.parse(String(init?.body)), {
type: "ice-config-request",
protocolVersion: 1,
requestId: "ice-test-1",
});
assert.deepEqual(result.configuration.iceServers, [
{ urls: ["stun:relay.example.test:3478"] },
{
urls: ["turns:relay.example.test:5349"],
username: "temporary-user-01",
credential: "temporary-password-01",
credentialType: "password",
},
]);
assert.equal(result.expiresAtMs, 600_000);
});
it("rejects unavailable, mismatched, expired, and malformed responses", async () => {
const run = (body: unknown) => fetchEphemeralIceConfiguration({
authenticatedFetch: async () => Response.json(body),
now: () => 200,
requestId: () => "ice-test-1",
});
await assert.rejects(run({
type: "ice-config-unavailable",
protocolVersion: 1,
requestId: "ice-test-1",
retryAfterMs: 10_000,
}), /temporarily unavailable/);
await assert.rejects(run({
type: "ice-config-unavailable",
protocolVersion: 1,
requestId: "ice-test-2",
retryAfterMs: 10_000,
}), /did not match/);
await assert.rejects(run({
type: "ice-config-grant",
protocolVersion: 1,
requestId: "ice-test-1",
issuedAtMs: 10,
expiresAtMs: 100,
iceServers: [{
urls: ["turn:relay.example.test:3478"],
username: "temporary-user-01",
credential: "temporary-password-01",
credentialType: "password",
}],
}), /not active/);
await assert.rejects(run({ profile: "must-not-parse" }), /invalid response/);
});
it("exposes validated 429 and 503 retry timing without exposing an untrusted body", async () => {
for (const status of [429, 503]) {
await assert.rejects(fetchEphemeralIceConfiguration({
authenticatedFetch: async () => Response.json({
type: "ice-config-unavailable",
protocolVersion: 1,
requestId: "ice-backoff-1",
retryAfterMs: 12_345,
}, { status }),
requestId: () => "ice-backoff-1",
}), (error: unknown) => {
assert.ok(error instanceof IceConfigurationUnavailableError);
assert.equal(error.retryAfterMs, 12_345);
assert.equal(error.status, status);
return true;
});
}
await assert.rejects(fetchEphemeralIceConfiguration({
authenticatedFetch: async () => Response.json({
type: "ice-config-unavailable",
protocolVersion: 1,
requestId: "somebody-elses-request",
retryAfterMs: 12_345,
}, { status: 503 }),
requestId: () => "ice-backoff-1",
}), /did not match/);
await assert.rejects(fetchEphemeralIceConfiguration({
authenticatedFetch: async () => Response.json({ retryAfterMs: 1 }, { status: 503 }),
requestId: () => "ice-backoff-1",
}), /failed \(503\)/);
});
});
+67
View File
@@ -0,0 +1,67 @@
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import {
isIceConfigGrantActive,
isSafeIceUrl,
parseIceConfigRequest,
parseIceConfigResponse,
type IceConfigGrant,
} from "../media/index.ts";
const grant = (): IceConfigGrant => ({
type: "ice-config-grant",
protocolVersion: 1,
requestId: "ice-request-1",
issuedAtMs: 1_000,
expiresAtMs: 601_000,
iceServers: [
{ urls: ["stun:relay.example.test:3478"] },
{
urls: ["turn:relay.example.test:3478", "turns:relay.example.test:5349"],
username: "1700000600:opaque_nonce_value",
credential: "dGVzdF9jcmVkZW50aWFsX3ZhbHVl",
credentialType: "password",
},
],
});
describe("ICE configuration wire contract", () => {
it("accepts exact requests and bounded ephemeral grants", () => {
assert.equal(parseIceConfigRequest({
type: "ice-config-request", protocolVersion: 1, requestId: "ice-request-1",
}).ok, true);
assert.equal(parseIceConfigResponse(JSON.parse(JSON.stringify(grant()))).ok, true);
assert.equal(isIceConfigGrantActive(grant(), 300_000), true);
assert.equal(isIceConfigGrantActive(grant(), 601_000), false);
});
it("allows only credential-free ICE URL forms", () => {
for (const url of [
"stun:relay.example.test:3478", "stuns:relay.example.test:5349",
"turn:192.0.2.4:3478", "turn:relay.example.test:3478?transport=udp",
"turn:relay.example.test:3478?transport=tcp", "turns:[2001:db8::1]:5349?transport=tcp",
]) {
assert.equal(isSafeIceUrl(url), true, url);
}
for (const url of [
"https://relay.example.test", "turn:user:pass@relay.example.test:3478",
"turn:relay.example.test:3478?credential=secret", "turn://relay.example.test:3478",
"stun:relay.example.test:3478?transport=udp", "turn:relay.example.test:3478?transport=sctp",
"turn:relay.example.test:3478?transport=tcp&credential=secret",
"turn:relay.example.test:99999", "turn:relay example.test:3478",
]) assert.equal(isSafeIceUrl(url), false, url);
});
it("rejects extra keys, credential tricks, missing TURN, and excessive lifetime", () => {
assert.equal(parseIceConfigRequest({
type: "ice-config-request", protocolVersion: 1, requestId: "ice-request-1", subject: "stable-user",
}).ok, false);
assert.equal(parseIceConfigResponse({ ...grant(), identity: { profile: "karti" } }).ok, false);
assert.equal(parseIceConfigResponse({ ...grant(), iceServers: [{ urls: ["stun:relay.example.test:3478"] }] }).ok, false);
assert.equal(parseIceConfigResponse({ ...grant(), expiresAtMs: 3_601_001 }).ok, false);
const turn = grant().iceServers[1] as unknown as Record<string, unknown>;
assert.equal(parseIceConfigResponse({
...grant(), iceServers: [{ ...turn, urls: ["turn:user@relay.example.test:3478"] }],
}).ok, false);
});
});
+47 -1
View File
@@ -1,6 +1,7 @@
import assert from "node:assert/strict";
import { describe, it } from "node:test";
import { createOfficeScreenPanel, type MediaSurfaceDescriptor } from "../media/index.ts";
import { createOfficeScreenPanel } from "../media/officeScreenPanel.ts";
import type { MediaSurfaceDescriptor } from "../media/presentation.ts";
type Listener = (event: FakeEvent) => void;
class FakeEvent {
@@ -121,6 +122,18 @@ describe("office screen manager panel", () => {
assert.equal("mediaDevices" in panel, false);
});
it("keeps presenting separate from viewer opt-in", () => {
const { panel, shares, opts } = setup();
panel.open();
const root = panel.root as unknown as FakeElement;
const share = root.find("data-action", "share");
assert.equal(share.disabled, false);
share.dispatch("click");
assert.deepEqual(shares, ["lobby-monitor"]);
assert.deepEqual(opts, []);
assert.deepEqual(panel.state().optedInScreenIds, []);
});
it("updates defensively, removes stale consent, and represents an empty office", () => {
const { panel } = setup();
const root = panel.root as unknown as FakeElement;
@@ -136,6 +149,39 @@ describe("office screen manager panel", () => {
assert.match(root.text(), /No authored office screens/);
});
it("shows concise remote lifecycle states and forgets them with their screen", () => {
const { panel, stops, opts } = setup();
const root = panel.root as unknown as FakeElement;
root.find("data-action", "opt-in").dispatch("click");
panel.setRemoteStatus("lobby-monitor", "connecting");
assert.equal(panel.state().remoteStatusByScreen["lobby-monitor"], "connecting");
assert.match(root.text(), /Remote connecting/);
assert.equal(root.find("data-action", "stop").disabled, false);
root.find("data-action", "stop").dispatch("click");
assert.deepEqual(stops, ["lobby-monitor"]);
panel.setRemoteStatus("lobby-monitor", "unavailable");
assert.match(root.text(), /No remote share/);
assert.equal(root.find("data-action", "opt-in").textContent, "Retry remote");
root.find("data-action", "opt-in").dispatch("click");
assert.deepEqual(opts.at(-1), ["lobby-monitor", true]);
assert.deepEqual(panel.state().optedInScreenIds, ["lobby-monitor"]);
panel.update([SURFACES[1]!]);
assert.deepEqual(panel.state().remoteStatusByScreen, {});
assert.throws(() => panel.setRemoteStatus("commons-display", "invalid" as "live"), /invalid remote status/);
});
it("refreshes a remote surface from bound back to cleared", () => {
const { panel } = setup();
const root = panel.root as unknown as FakeElement;
panel.setRemoteStatus("lobby-monitor", "live");
panel.update([{ ...SURFACES[0]!, bound: true }, SURFACES[1]!]);
assert.match(root.text(), /Media active · Remote live/);
panel.update(SURFACES);
panel.setRemoteStatus("lobby-monitor", "off");
assert.match(root.text(), /Media off · Remote off/);
assert.equal(root.find("data-action", "stop").disabled, true);
});
it("traps tab focus, closes on Escape, restores focus, and disposes idempotently", () => {
const { document, container, panel } = setup();
const trigger = document.createElement("button");
+37 -1
View File
@@ -182,6 +182,7 @@ describe("remote office media presenter", () => {
/explicit opt-in/,
);
await media.startPresenter({ consent: { authorized: true, optedIn: true }, stream: stream as unknown as MediaStream, video: video as unknown as HTMLVideoElement });
assert.equal(media.state().status, "live", "an empty hosted room is ready for its first viewer");
await settle();
assert.equal(peers.length, 1);
assert.equal(peers[0]?.added[0], presenterTrack as unknown as MediaStreamTrack);
@@ -324,7 +325,7 @@ describe("remote office media viewer", () => {
});
const video = new FakeVideo();
await assert.rejects(media.startViewer({ decision: decision(false), video: video as unknown as HTMLVideoElement }), /ready live-stream/);
await media.startViewer({ decision: decision(), video: video as unknown as HTMLVideoElement });
await media.startViewer({ viewerOptIn: true, video: video as unknown as HTMLVideoElement });
await settle();
assert.equal(peers[0]?.transceivers[0], "video");
assert.deepEqual(peers[0]?.remote[0], { type: "offer", sdp: "presenter-offer" });
@@ -340,4 +341,39 @@ describe("remote office media viewer", () => {
assert.equal(video.srcObject, null);
assert.equal(media.state().status, "revoked");
});
it("forgets its grant when the server has already stopped the session", async () => {
const presenterPeer = { participantId: "presenter-opaque", role: "presenter" as const };
let emitEvent: (value: Uint8Array) => void = () => { throw new Error("event stream is not open"); };
let leaveCalls = 0;
const fetcher: typeof fetch = async (input, init = {}) => {
const body = JSON.parse(String(init.body)) as Record<string, unknown>;
if (String(input).endsWith("/join")) {
return Response.json(grant("screen-share-join-grant", String(body.requestId), "viewer", [presenterPeer]), { status: 201 });
}
if (String(input).endsWith("/events")) {
const stream = new ReadableStream<Uint8Array>({ start(controller) { emitEvent = (value) => controller.enqueue(value); } });
const resume = grant("screen-share-resume-grant", String(body.requestId), "viewer", [presenterPeer]);
emitEvent(new TextEncoder().encode(`data: ${JSON.stringify(resume)}\n\n`));
return new Response(stream, { status: 200, headers: { "Content-Type": "text/event-stream" } });
}
if (String(input).endsWith("/leave")) leaveCalls += 1;
return new Response(null, { status: 204 });
};
const media = createRemoteOfficeMedia({
role: "viewer", binding: BINDING, authenticatedFetch: fetcher, now: () => NOW,
peerConnectionFactory: () => new FakePeer() as unknown as RTCPeerConnection,
});
await media.startViewer({ viewerOptIn: true, video: new FakeVideo() as unknown as HTMLVideoElement });
await settle();
emitEvent(new TextEncoder().encode(`data: ${JSON.stringify({
type: "screen-share-stopped", protocolVersion: 1, sequence: 2, timestampMs: NOW + 1,
sessionId: "share-session", binding: BINDING, reason: "presenter-stopped",
})}\n\n`));
await settle();
assert.equal(media.state().status, "stopped");
assert.equal(media.state().sessionId, null);
await media.dispose();
assert.equal(leaveCalls, 0, "cleanup does not reuse a server-invalidated capability");
});
});