Milestone v1 (v2.0.0): Mokosh — Session Capture #1

Merged
strategy155 merged 297 commits from gsd/phase-04-harden-clean-up-optional into main 2026-05-31 15:34:17 +00:00
3 changed files with 256 additions and 3 deletions
Showing only changes of commit 503531485c - Show all commits

116
package-lock.json generated
View File

@@ -9,7 +9,9 @@
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"jszip": "^3.10.1", "jszip": "^3.10.1",
"rrweb": "^2.0.0-alpha.4" "rrweb": "^2.0.0-alpha.4",
"ts-ebml": "^3.0.2",
"webm-muxer": "^5.1.4"
}, },
"devDependencies": { "devDependencies": {
"@crxjs/vite-plugin": "^2.0.0-beta.25", "@crxjs/vite-plugin": "^2.0.0-beta.25",
@@ -1233,6 +1235,12 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/dom-webcodecs": {
"version": "0.1.18",
"resolved": "https://registry.npmjs.org/@types/dom-webcodecs/-/dom-webcodecs-0.1.18.tgz",
"integrity": "sha512-vAvE8C9DGWR+tkb19xyjk1TSUlJ7RUzzp4a9Anu7mwBT+fpyePWK1UxmH14tMO5zHmrnrRIMg5NutnnDztLxgg==",
"license": "MIT"
},
"node_modules/@types/estree": { "node_modules/@types/estree": {
"version": "1.0.8", "version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
@@ -1264,6 +1272,12 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/wicg-file-system-access": {
"version": "2020.9.8",
"resolved": "https://registry.npmjs.org/@types/wicg-file-system-access/-/wicg-file-system-access-2020.9.8.tgz",
"integrity": "sha512-ggMz8nOygG7d/stpH40WVaNvBwuyYLnrg5Mbyf6bmsj/8+gb6Ei4ZZ9/4PNpcPNTT8th9Q8sM8wYmWGjMWLX/A==",
"license": "MIT"
},
"node_modules/@vitest/expect": { "node_modules/@vitest/expect": {
"version": "4.1.6", "version": "4.1.6",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.6.tgz", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.6.tgz",
@@ -1435,6 +1449,14 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/buffers": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz",
"integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==",
"engines": {
"node": ">=0.2.0"
}
},
"node_modules/chai": { "node_modules/chai": {
"version": "6.2.2", "version": "6.2.2",
"resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
@@ -1445,6 +1467,15 @@
"node": ">=18" "node": ">=18"
} }
}, },
"node_modules/commander": {
"version": "12.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
"integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/convert-source-map": { "node_modules/convert-source-map": {
"version": "1.9.0", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
@@ -1575,6 +1606,40 @@
"url": "https://github.com/fb55/domutils?sponsor=1" "url": "https://github.com/fb55/domutils?sponsor=1"
} }
}, },
"node_modules/ebml": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ebml/-/ebml-3.0.0.tgz",
"integrity": "sha512-Q6C1u4/TX1nYipT9HNIopp95YyyyI0zs1GXdNRKO7XL7k+oo+ZtDc1CaJjpCdmlLxWsnlKBOXJCXkYU0K/Anlg==",
"license": "MIT",
"dependencies": {
"buffers": "^0.1.1",
"debug": "~3.1.0"
},
"engines": {
"node": ">= 6.4"
}
},
"node_modules/ebml-block": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/ebml-block/-/ebml-block-1.1.2.tgz",
"integrity": "sha512-HgNlIsRFP6D9VKU5atCeHRJY7XkJP8bOe8yEhd8NB7B3b4++VWTyauz6g650iiPmLfPLGlVpoJmGSgMfXDYusg==",
"license": "MIT"
},
"node_modules/ebml/node_modules/debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
}
},
"node_modules/ebml/node_modules/ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
"license": "MIT"
},
"node_modules/entities": { "node_modules/entities": {
"version": "4.5.0", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
@@ -1641,6 +1706,15 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/events": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
"license": "MIT",
"engines": {
"node": ">=0.8.x"
}
},
"node_modules/expect-type": { "node_modules/expect-type": {
"version": "1.3.0", "version": "1.3.0",
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
@@ -1769,6 +1843,12 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/int64-buffer": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/int64-buffer/-/int64-buffer-1.1.0.tgz",
"integrity": "sha512-94smTCQOvigN4d/2R/YDjz8YVG0Sufvv2aAh8P5m42gwhCsDAJqnbNOrxJsrADuAFAA69Q/ptGzxvNcNuIJcvw==",
"license": "MIT"
},
"node_modules/is-extglob": { "node_modules/is-extglob": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -2126,6 +2206,12 @@
"@jridgewell/sourcemap-codec": "^1.5.5" "@jridgewell/sourcemap-codec": "^1.5.5"
} }
}, },
"node_modules/matroska-schema": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/matroska-schema/-/matroska-schema-2.1.0.tgz",
"integrity": "sha512-6c1oFmDxf4Vc5J5lA+9wO7TKcw5M1w85HfzFhAFT4OuEUuqp/s/jqqC3OKlaWe1YwN5wTThJyTC7iwhyW7kQdg==",
"license": "MIT"
},
"node_modules/merge2": { "node_modules/merge2": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -2597,6 +2683,23 @@
"node": ">=8.0" "node": ">=8.0"
} }
}, },
"node_modules/ts-ebml": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/ts-ebml/-/ts-ebml-3.0.2.tgz",
"integrity": "sha512-Br6DA/YbdpsiSQjc0KaF3ASQtkk3MCiA4q5kIA7ptv6adZa/MdYa2TXAXF2bAzRZIMWfyFEC9Gicr3nb51MgDA==",
"license": "MIT",
"dependencies": {
"commander": "^12.0.0",
"ebml": "^3.0.0",
"ebml-block": "^1.1.2",
"events": "^3.3.0",
"int64-buffer": "^1.0.1",
"matroska-schema": "^2.1.0"
},
"bin": {
"ts-ebml": "lib/cli.js"
}
},
"node_modules/tslib": { "node_modules/tslib": {
"version": "2.8.1", "version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@@ -2964,6 +3067,17 @@
} }
} }
}, },
"node_modules/webm-muxer": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/webm-muxer/-/webm-muxer-5.1.4.tgz",
"integrity": "sha512-ditzgFVFbfqPaugkIr4mGhAdob5K9HY6Rzlh7TRsA368yA1sp/m5O7nQCcMLdgFDeNGtFPg8B+MeXLtpzKWX6Q==",
"deprecated": "This library is superseded by Mediabunny. Please migrate to it.",
"license": "MIT",
"dependencies": {
"@types/dom-webcodecs": "^0.1.4",
"@types/wicg-file-system-access": "^2020.9.5"
}
},
"node_modules/why-is-node-running": { "node_modules/why-is-node-running": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",

View File

@@ -10,8 +10,10 @@
"test": "vitest run" "test": "vitest run"
}, },
"dependencies": { "dependencies": {
"jszip": "^3.10.1",
"rrweb": "^2.0.0-alpha.4", "rrweb": "^2.0.0-alpha.4",
"jszip": "^3.10.1" "ts-ebml": "^3.0.2",
"webm-muxer": "^5.1.4"
}, },
"devDependencies": { "devDependencies": {
"@crxjs/vite-plugin": "^2.0.0-beta.25", "@crxjs/vite-plugin": "^2.0.0-beta.25",

View File

@@ -0,0 +1,137 @@
// 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();
});
});
});