feat: publish av-tools 0.1.0

This commit is contained in:
2026-07-24 14:58:03 +02:00
commit fcc537b024
254 changed files with 63270 additions and 0 deletions

View File

@@ -0,0 +1,298 @@
/// <reference types="node" />
import { createServer, type Server } from 'node:http';
import { execFileSync } from 'node:child_process';
import { mkdtempSync, readFileSync, rmSync, statSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { dirname, extname, join, resolve, sep } from 'node:path';
import { fileURLToPath } from 'node:url';
import type { ConsoleMessage, Page, Request, Response } from '@playwright/test';
export const REPOSITORY_ROOT = resolve(
dirname(fileURLToPath(import.meta.url)),
'../..'
);
export const DIST_ROOT = join(REPOSITORY_ROOT, 'dist');
export const GENERATED_FIXTURES = join(
REPOSITORY_ROOT,
'tests/fixtures/generated'
);
export const PRIMARY_FIXTURE = join(GENERATED_FIXTURES, 'pattern-av.mp4');
const NESTED_PREFIX = '/deep/nested/av/';
const SECURITY_HEADERS = {
'Cross-Origin-Embedder-Policy': 'require-corp',
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Resource-Policy': 'same-origin',
};
function contentType(filePath: string): string {
const types: Readonly<Record<string, string>> = {
'.css': 'text/css; charset=utf-8',
'.html': 'text/html; charset=utf-8',
'.js': 'text/javascript; charset=utf-8',
'.json': 'application/json; charset=utf-8',
'.svg': 'image/svg+xml',
'.wasm': 'application/wasm',
};
return types[extname(filePath).toLowerCase()] ?? 'application/octet-stream';
}
function readApplicationManifest(): Record<string, unknown> {
return JSON.parse(
readFileSync(join(DIST_ROOT, 'toolbox-app.json'), 'utf8')
) as Record<string, unknown>;
}
function writeJson(
response: import('node:http').ServerResponse,
status: number,
value: unknown
): void {
response.writeHead(status, {
...SECURITY_HEADERS,
'Cache-Control': 'no-store',
'Content-Type': 'application/json; charset=utf-8',
});
response.end(JSON.stringify(value));
}
function serveFile(
response: import('node:http').ServerResponse,
filePath: string
): void {
try {
const stats = statSync(filePath);
if (!stats.isFile()) {
response.writeHead(404, SECURITY_HEADERS);
response.end('Not found');
return;
}
response.writeHead(200, {
...SECURITY_HEADERS,
'Cache-Control': 'no-store',
'Content-Length': String(stats.size),
'Content-Type': contentType(filePath),
});
response.end(readFileSync(filePath));
} catch {
response.writeHead(404, SECURITY_HEADERS);
response.end('Not found');
}
}
function closeServer(server: Server): Promise<void> {
return new Promise((resolveClose, rejectClose) => {
server.close((error) => {
if (error) rejectClose(error);
else resolveClose();
});
});
}
export interface NestedStaticServer {
origin: string;
appUrl: string;
close: () => Promise<void>;
}
/**
* Serves the built artifact exactly as it will be assembled below a portal
* path. This catches root-absolute URLs that a root-only preview cannot.
*/
export async function startNestedStaticServer(): Promise<NestedStaticServer> {
const applicationManifest = readApplicationManifest();
const otherManifest = {
...applicationManifest,
id: 'de.add-ideas.browser-test-tool',
name: 'Browser Test Tool',
description: 'A same-origin catalog peer used by browser validation.',
entry: './',
actions: [],
};
const catalog = {
$schema:
'https://git.add-ideas.de/zemion/toolbox-sdk/raw/branch/main/schemas/toolbox-catalog.v1.schema.json',
schemaVersion: 1,
id: 'de.add-ideas.browser-test-toolbox',
name: 'Browser Test Toolbox',
home: './',
theme: { mode: 'system', brand: 'Browser test' },
apps: [
{ manifest: './av/toolbox-app.json', enabled: true },
{ manifest: './other/toolbox-app.json', enabled: true },
],
};
const distWithSeparator = `${resolve(DIST_ROOT)}${sep}`;
const server = createServer((request, response) => {
const requestUrl = new URL(request.url ?? '/', 'http://127.0.0.1');
const { pathname } = requestUrl;
if (pathname === '/deep/nested/toolbox.catalog.json') {
writeJson(response, 200, catalog);
return;
}
if (pathname === '/deep/nested/invalid-catalog.json') {
writeJson(response, 200, {
schemaVersion: 999,
id: 'invalid',
});
return;
}
if (pathname === '/deep/nested/other/toolbox-app.json') {
writeJson(response, 200, otherManifest);
return;
}
if (pathname === '/deep/nested/favicon.svg') {
serveFile(response, join(DIST_ROOT, 'favicon.svg'));
return;
}
if (!pathname.startsWith(NESTED_PREFIX)) {
response.writeHead(404, SECURITY_HEADERS);
response.end('Not found');
return;
}
const relativePath = decodeURIComponent(
pathname.slice(NESTED_PREFIX.length)
);
const requestedPath = resolve(
DIST_ROOT,
relativePath === '' ? 'index.html' : relativePath
);
if (
requestedPath !== resolve(DIST_ROOT) &&
!requestedPath.startsWith(distWithSeparator)
) {
response.writeHead(400, SECURITY_HEADERS);
response.end('Invalid path');
return;
}
serveFile(response, requestedPath);
});
await new Promise<void>((resolveListen, rejectListen) => {
server.once('error', rejectListen);
server.listen(0, '127.0.0.1', () => {
server.off('error', rejectListen);
resolveListen();
});
});
const address = server.address();
if (!address || typeof address === 'string') {
await closeServer(server);
throw new Error('Nested test server did not expose a TCP port');
}
const origin = `http://127.0.0.1:${address.port}`;
return {
origin,
appUrl: `${origin}${NESTED_PREFIX}`,
close: () => closeServer(server),
};
}
export interface RuntimeObservation {
requests: string[];
responses: Array<{
url: string;
status: number;
contentType: string | undefined;
}>;
failedRequests: string[];
pageErrors: string[];
consoleErrors: string[];
consoleWarnings: string[];
}
export function observeRuntime(page: Page): RuntimeObservation {
const observation: RuntimeObservation = {
requests: [],
responses: [],
failedRequests: [],
pageErrors: [],
consoleErrors: [],
consoleWarnings: [],
};
page.on('request', (request: Request) => {
observation.requests.push(request.url());
});
page.on('response', (response: Response) => {
observation.responses.push({
url: response.url(),
status: response.status(),
contentType: response.headers()['content-type'],
});
});
page.on('requestfailed', (request: Request) => {
observation.failedRequests.push(
`${request.url()}: ${request.failure()?.errorText ?? 'unknown failure'}`
);
});
page.on('pageerror', (error: Error) => {
observation.pageErrors.push(error.stack ?? error.message);
});
page.on('console', (message: ConsoleMessage) => {
if (message.type() === 'error') {
observation.consoleErrors.push(message.text());
}
if (message.type() === 'warning') {
observation.consoleWarnings.push(message.text());
}
});
return observation;
}
export interface TemporaryMediaFixture {
filePath: string;
dispose: () => void;
}
export function createCancellationFixture(): TemporaryMediaFixture {
const directory = mkdtempSync(join(tmpdir(), 'av-tools-browser-'));
const filePath = join(directory, 'long-cancellation-source.mp4');
try {
execFileSync(
'ffmpeg',
[
'-hide_banner',
'-loglevel',
'error',
'-y',
'-stream_loop',
'14',
'-i',
PRIMARY_FIXTURE,
'-t',
'30',
'-vf',
'scale=1280:720',
'-r',
'30',
'-c:v',
'libx264',
'-preset',
'ultrafast',
'-crf',
'32',
'-c:a',
'aac',
'-b:a',
'64k',
filePath,
],
{ stdio: 'pipe' }
);
} catch (error) {
rmSync(directory, { recursive: true, force: true });
throw error;
}
return {
filePath,
dispose: () => rmSync(directory, { recursive: true, force: true }),
};
}