Checkpoint web parity through Chromium input tasks
Some checks are pending
M6 deployable RC / quick (push) Waiting to run
M6 deployable RC / chromium (push) Blocked by required conditions
M6 deployable RC / release (push) Blocked by required conditions

This commit is contained in:
mes123456
2026-08-19 10:39:03 -04:00
parent 5a11045ca5
commit 380cbed4ff
634 changed files with 41862 additions and 212 deletions

View File

@@ -0,0 +1,168 @@
import type { ErrorCode } from "./error";
export const LIBRARY_NEGATIVE_CASE_SCHEMA = 1 as const;
export interface LibraryNegativeLibraryIR {
libraryId: string;
dependencyIds: string[];
}
export interface LibraryNegativeDataBlockIR {
dataBlockId: string;
sourceLibraryId: string;
}
export interface LibraryNegativeCrossReferenceIR {
fromLibraryId: string;
toLibraryId: string;
}
export interface LibraryNegativeReloadIR {
sourceLibraryId: string;
generation: number;
}
export interface LibraryNegativeInputIR {
schemaVersion: typeof LIBRARY_NEGATIVE_CASE_SCHEMA;
libraries: LibraryNegativeLibraryIR[];
dataBlocks: LibraryNegativeDataBlockIR[];
crossReferences: LibraryNegativeCrossReferenceIR[];
reloads: LibraryNegativeReloadIR[];
}
export interface LibraryNegativeValidationIR {
status: "VALID";
}
export class LibraryNegativeValidationError extends Error {
readonly code: ErrorCode;
readonly path?: string;
constructor(code: ErrorCode, message: string, path?: string) {
super(`${code}: ${message}`);
this.name = "LibraryNegativeValidationError";
this.code = code;
this.path = path;
}
}
const LIBRARY_ID = /^library:[a-f0-9]{64}$/;
const DATA_BLOCK_ID = /^[A-Za-z0-9][A-Za-z0-9:._/ -]{0,255}$/;
const MAX_ENTRIES = 10_000;
function record(value: unknown, path: string): Record<string, unknown> {
if (typeof value !== "object" || value === null || Array.isArray(value)) throw new LibraryNegativeValidationError("TASK_VALIDATION_FAILED", `${path} must be an object`, path);
return value as Record<string, unknown>;
}
function exactKeys(value: Record<string, unknown>, expected: readonly string[], path: string): void {
const actual = Object.keys(value).sort();
const allowed = [...expected].sort();
if (actual.length !== allowed.length || actual.some((key, index) => key !== allowed[index])) throw new LibraryNegativeValidationError("TASK_VALIDATION_FAILED", `${path} contains undeclared fields`, path);
}
function libraryId(value: unknown, path: string): string {
if (typeof value !== "string" || !LIBRARY_ID.test(value)) throw new LibraryNegativeValidationError("TASK_VALIDATION_FAILED", `${path} must be a library identity`, path);
return value;
}
function dataBlockId(value: unknown, path: string): string {
if (typeof value !== "string" || !DATA_BLOCK_ID.test(value)) throw new LibraryNegativeValidationError("TASK_VALIDATION_FAILED", `${path} is invalid`, path);
return value;
}
function integer(value: unknown, path: string): number {
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) throw new LibraryNegativeValidationError("TASK_VALIDATION_FAILED", `${path} must be a safe integer >= 0`, path);
return value;
}
function parseLibrary(value: unknown, path: string): LibraryNegativeLibraryIR {
const item = record(value, path);
exactKeys(item, ["libraryId", "dependencyIds"], path);
if (!Array.isArray(item.dependencyIds) || item.dependencyIds.length > MAX_ENTRIES) throw new LibraryNegativeValidationError("TASK_VALIDATION_FAILED", `${path}.dependencyIds exceeds its bound`, path);
return { libraryId: libraryId(item.libraryId, `${path}.libraryId`), dependencyIds: item.dependencyIds.map((dependency, index) => libraryId(dependency, `${path}.dependencyIds[${index}]`)) };
}
function parseDataBlock(value: unknown, path: string): LibraryNegativeDataBlockIR {
const item = record(value, path);
exactKeys(item, ["dataBlockId", "sourceLibraryId"], path);
return { dataBlockId: dataBlockId(item.dataBlockId, `${path}.dataBlockId`), sourceLibraryId: libraryId(item.sourceLibraryId, `${path}.sourceLibraryId`) };
}
function parseCrossReference(value: unknown, path: string): LibraryNegativeCrossReferenceIR {
const item = record(value, path);
exactKeys(item, ["fromLibraryId", "toLibraryId"], path);
return { fromLibraryId: libraryId(item.fromLibraryId, `${path}.fromLibraryId`), toLibraryId: libraryId(item.toLibraryId, `${path}.toLibraryId`) };
}
function parseReload(value: unknown, path: string): LibraryNegativeReloadIR {
const item = record(value, path);
exactKeys(item, ["sourceLibraryId", "generation"], path);
return { sourceLibraryId: libraryId(item.sourceLibraryId, `${path}.sourceLibraryId`), generation: integer(item.generation, `${path}.generation`) };
}
export function parseLibraryNegativeInput(value: unknown): LibraryNegativeInputIR {
const input = record(value, "input");
exactKeys(input, ["schemaVersion", "libraries", "dataBlocks", "crossReferences", "reloads"], "input");
if (input.schemaVersion !== LIBRARY_NEGATIVE_CASE_SCHEMA) throw new LibraryNegativeValidationError("PROTOCOL_MISMATCH", "Unsupported library negative-case schema", "schemaVersion");
const libraries = input.libraries;
const dataBlocks = input.dataBlocks;
const crossReferences = input.crossReferences;
const reloads = input.reloads;
if (!Array.isArray(libraries) || libraries.length > MAX_ENTRIES) throw new LibraryNegativeValidationError("TASK_VALIDATION_FAILED", "input.libraries exceeds its bound", "input.libraries");
if (!Array.isArray(dataBlocks) || dataBlocks.length > MAX_ENTRIES) throw new LibraryNegativeValidationError("TASK_VALIDATION_FAILED", "input.dataBlocks exceeds its bound", "input.dataBlocks");
if (!Array.isArray(crossReferences) || crossReferences.length > MAX_ENTRIES) throw new LibraryNegativeValidationError("TASK_VALIDATION_FAILED", "input.crossReferences exceeds its bound", "input.crossReferences");
if (!Array.isArray(reloads) || reloads.length > MAX_ENTRIES) throw new LibraryNegativeValidationError("TASK_VALIDATION_FAILED", "input.reloads exceeds its bound", "input.reloads");
return {
schemaVersion: LIBRARY_NEGATIVE_CASE_SCHEMA,
libraries: libraries.map((item, index) => parseLibrary(item, `libraries[${index}]`)),
dataBlocks: dataBlocks.map((item, index) => parseDataBlock(item, `dataBlocks[${index}]`)),
crossReferences: crossReferences.map((item, index) => parseCrossReference(item, `crossReferences[${index}]`)),
reloads: reloads.map((item, index) => parseReload(item, `reloads[${index}]`)),
};
}
function assertNoCycle(nodes: Set<string>, edges: Map<string, string[]>, label: string): void {
const active = new Set<string>();
const complete = new Set<string>();
const visit = (node: string): void => {
if (active.has(node)) throw new LibraryNegativeValidationError("LIBRARY_DEPENDENCY_CYCLE", `${label} contains a cycle at ${node}`, label);
if (complete.has(node)) return;
active.add(node);
for (const dependency of edges.get(node) ?? []) {
if (!nodes.has(dependency)) throw new LibraryNegativeValidationError("ASSET_MANIFEST_INVALID", `${label} references a missing library ${dependency}`, label);
visit(dependency);
}
active.delete(node);
complete.add(node);
};
for (const node of nodes) visit(node);
}
export function validateLibraryNegativeInput(value: unknown): LibraryNegativeValidationIR {
const input = parseLibraryNegativeInput(value);
const libraries = new Set(input.libraries.map((item) => item.libraryId));
if (libraries.size !== input.libraries.length) throw new LibraryNegativeValidationError("TASK_VALIDATION_FAILED", "duplicate library ID", "libraries");
const libraryEdges = new Map(input.libraries.map((item) => [item.libraryId, item.dependencyIds]));
assertNoCycle(libraries, libraryEdges, "library dependencies");
const crossEdges = new Map<string, string[]>();
for (const item of input.crossReferences) {
if (!libraries.has(item.fromLibraryId) || !libraries.has(item.toLibraryId)) throw new LibraryNegativeValidationError("ASSET_MANIFEST_INVALID", "cross-library reference names a missing library", "crossReferences");
crossEdges.set(item.fromLibraryId, [...(crossEdges.get(item.fromLibraryId) ?? []), item.toLibraryId]);
}
assertNoCycle(libraries, crossEdges, "cross-library references");
const dataBlocks = new Set<string>();
for (const item of input.dataBlocks) {
if (!libraries.has(item.sourceLibraryId)) throw new LibraryNegativeValidationError("ASSET_SOURCE_HASH_MISMATCH", "data-block source library is missing", "dataBlocks");
if (dataBlocks.has(item.dataBlockId)) throw new LibraryNegativeValidationError("TASK_VALIDATION_FAILED", `duplicate data-block ID ${item.dataBlockId}`, "dataBlocks");
dataBlocks.add(item.dataBlockId);
}
const reloads = new Set<string>();
for (const item of input.reloads) {
if (!libraries.has(item.sourceLibraryId)) throw new LibraryNegativeValidationError("ASSET_SOURCE_HASH_MISMATCH", "reload source library is missing", "reloads");
const identity = `${item.sourceLibraryId}:${item.generation}`;
if (reloads.has(identity)) throw new LibraryNegativeValidationError("REVISION_CONFLICT", `duplicate reload ${identity}`, "reloads");
reloads.add(identity);
}
return { status: "VALID" };
}