- Add ts-ebml ^3.0.2 (parse half) and webm-muxer ^5.1.4 (write half) per
CONTEXT.md amendment D-14-remux; both MIT, both verified SW-compatible
in the d13 debug-session library survey.
- tests/background/webm-remux-deps.test.ts pins two contracts:
(a) named exports surface (Muxer + ArrayBufferTarget + Decoder).
(b) both libraries import cleanly when window/document are absent on
globalThis — guards the published dist against accidentally
acquiring DOM globals on the hot path that would crash the
Chrome service-worker runtime.
- Note: webm-muxer 5.1.4 upstream-deprecated in favor of Mediabunny; the
pinned version still meets the d13 architectural requirement
(single-EBML output via addVideoChunkRaw). Migration to Mediabunny is
out of scope for Plan 01-08 and would require a new ADR.
- Baseline 53 GREEN + 2 new GREEN; tsc clean; 2 webm-playback duration
RED still pending (drive to GREEN in Tasks 3-5).
138 lines
4.9 KiB
TypeScript
138 lines
4.9 KiB
TypeScript
// tests/background/webm-remux-deps.test.ts
|
|
//
|
|
// Plan 01-08 Task 1: SW-compatibility + presence contract for the two new
|
|
// runtime dependencies that the WebM remux pipeline rests on. Pins the
|
|
// architectural commitment that landed in CONTEXT.md amendment D-14-remux:
|
|
// - `ts-ebml` ^3.0.2 (MIT, parses each VideoSegment's EBML structure)
|
|
// - `webm-muxer` ^5.1.4 (MIT, writes the single-EBML-headered output)
|
|
//
|
|
// Both libraries were surveyed in `.planning/debug/d13-multi-ebml-concat-
|
|
// unplayable.md` (Evidence/library-survey, lines 380-410). They are pure
|
|
// JS, pure ESM/CJS, and were grep-verified at survey time to contain no
|
|
// hard DOM-global references on the hot path. Chrome's service-worker
|
|
// runtime (where `remuxSegments()` will execute) does not provide
|
|
// `window` or `document`; this test pins that compat at the
|
|
// devDependency-import surface so a future bump that accidentally adds
|
|
// a DOM global is caught at test time rather than at runtime in a
|
|
// production SW.
|
|
//
|
|
// Test 1 asserts named-export presence (RED until `npm install` lands).
|
|
// Test 2 asserts both libraries load under default Node globals without
|
|
// referencing `window` or `document` synchronously at import time.
|
|
//
|
|
// Skip discipline: none — these are pure import-shape tests, no external
|
|
// binaries, no fixtures. Vitest's default Node environment is sufficient.
|
|
|
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
|
|
interface GlobalSnapshot {
|
|
window: unknown;
|
|
document: unknown;
|
|
hadWindow: boolean;
|
|
hadDocument: boolean;
|
|
}
|
|
|
|
/**
|
|
* Capture the current values of `window` and `document` on `globalThis`
|
|
* so they can be restored after a test that deletes them. Vitest's
|
|
* `vi.stubGlobal` is not used here because we want to assert the
|
|
* absence of the globals at import time, not just stub them.
|
|
*
|
|
* @returns Snapshot the caller passes back to {@link restoreGlobals}.
|
|
*/
|
|
function snapshotGlobals(): GlobalSnapshot {
|
|
const g = globalThis as unknown as Record<string, unknown>;
|
|
return {
|
|
window: g.window,
|
|
document: g.document,
|
|
hadWindow: 'window' in g,
|
|
hadDocument: 'document' in g,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Restore the globals captured by {@link snapshotGlobals}. Idempotent.
|
|
*
|
|
* @param snap - Snapshot returned by {@link snapshotGlobals}.
|
|
*/
|
|
function restoreGlobals(snap: GlobalSnapshot): void {
|
|
const g = globalThis as unknown as Record<string, unknown>;
|
|
if (snap.hadWindow) {
|
|
g.window = snap.window;
|
|
} else {
|
|
delete g.window;
|
|
}
|
|
if (snap.hadDocument) {
|
|
g.document = snap.document;
|
|
} else {
|
|
delete g.document;
|
|
}
|
|
}
|
|
|
|
describe('webm-remux dependencies (Plan 01-08 Task 1)', () => {
|
|
it('exports Muxer + ArrayBufferTarget + Decoder', async () => {
|
|
// Dynamic import so a missing package surfaces as a precise test
|
|
// failure ("Cannot find module 'webm-muxer'") rather than a Vitest
|
|
// collection error that hides which dependency is the cause.
|
|
const webmMuxer = await import('webm-muxer');
|
|
expect(webmMuxer.Muxer).toBeDefined();
|
|
expect(webmMuxer.ArrayBufferTarget).toBeDefined();
|
|
|
|
const tsEbml = await import('ts-ebml');
|
|
expect(tsEbml.Decoder).toBeDefined();
|
|
});
|
|
|
|
describe('loads under default Node globals without DOM-global ReferenceErrors', () => {
|
|
let snap: GlobalSnapshot;
|
|
|
|
beforeEach(() => {
|
|
snap = snapshotGlobals();
|
|
const g = globalThis as unknown as Record<string, unknown>;
|
|
delete g.window;
|
|
delete g.document;
|
|
});
|
|
|
|
afterEach(() => {
|
|
restoreGlobals(snap);
|
|
});
|
|
|
|
it('webm-muxer + ts-ebml do not throw on import when window/document are absent', async () => {
|
|
// The Chrome service-worker runtime provides neither `window` nor
|
|
// `document`. If either library's published dist references one
|
|
// of these synchronously at module evaluation time (e.g. a UMD
|
|
// wrapper falling through to `window`), the import below would
|
|
// throw a ReferenceError and this test would fail with a clear
|
|
// signal.
|
|
//
|
|
// ts-ebml's UMD wrapper does contain a `typeof window` check
|
|
// with a `self`/`global` fallback per the d13 library survey
|
|
// — `typeof` does NOT throw on undeclared identifiers per
|
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof
|
|
// so the fallback path resolves cleanly.
|
|
//
|
|
// webm-muxer is documented zero-DOM-ref.
|
|
let webmMuxerError: unknown = null;
|
|
try {
|
|
await import('webm-muxer');
|
|
} catch (e) {
|
|
webmMuxerError = e;
|
|
}
|
|
expect(
|
|
webmMuxerError,
|
|
`webm-muxer threw at import time without window/document: ${String(webmMuxerError)}`,
|
|
).toBeNull();
|
|
|
|
let tsEbmlError: unknown = null;
|
|
try {
|
|
await import('ts-ebml');
|
|
} catch (e) {
|
|
tsEbmlError = e;
|
|
}
|
|
expect(
|
|
tsEbmlError,
|
|
`ts-ebml threw at import time without window/document: ${String(tsEbmlError)}`,
|
|
).toBeNull();
|
|
});
|
|
});
|
|
});
|