chore: initialize from frontend template 4dc033c
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* §16. Generic CPU Web Worker contract.
|
||||
*
|
||||
* The capability is `NOT_SELECTED` by default (§16.2). These types exist so a
|
||||
* selection can be expressed and validated, but no production worker entry is
|
||||
* created until a measured, CPU-bound task with an owner is contributed. This
|
||||
* runtime is separate from the OPFS dedicated worker and the Service Worker.
|
||||
*/
|
||||
|
||||
export const WEB_WORKER_PROTOCOL = "CA_WEB_WORKER_V1" as const;
|
||||
|
||||
export const WEB_WORKER_BOUNDS = Object.freeze({
|
||||
taskGroups: 16,
|
||||
tasksPerGroup: 32,
|
||||
activeGroups: 4,
|
||||
queuedPerGroup: 32,
|
||||
queuedBytesPerGroup: 16 * 1024 * 1024,
|
||||
defaultInputBytes: 1024 * 1024,
|
||||
hardInputBytes: 8 * 1024 * 1024,
|
||||
defaultOutputBytes: 1024 * 1024,
|
||||
hardOutputBytes: 8 * 1024 * 1024,
|
||||
defaultDeadlineMs: 5_000,
|
||||
hardDeadlineMs: 30_000,
|
||||
cancelGraceMs: 250,
|
||||
idleTerminateMs: 60_000,
|
||||
restartsPerWindow: 3,
|
||||
restartWindowMs: 300_000,
|
||||
transferables: 16,
|
||||
mainThreadChunkBudgetMs: 8,
|
||||
mainThreadFallbackInputBytes: 1024 * 1024,
|
||||
});
|
||||
|
||||
export interface InstalledWorkerTask {
|
||||
readonly taskId: string;
|
||||
readonly taskVersion: 1;
|
||||
readonly maximumInputBytes: number;
|
||||
readonly maximumOutputBytes: number;
|
||||
readonly deadlineMs: number;
|
||||
}
|
||||
|
||||
export interface InstalledWebWorkerContribution {
|
||||
readonly taskGroupId: string;
|
||||
readonly tasks: readonly InstalledWorkerTask[];
|
||||
readonly fallback: "MAIN_THREAD_CHUNKED" | "UNSUPPORTED";
|
||||
}
|
||||
|
||||
export type WorkerRequestMessage = Readonly<{
|
||||
protocol: typeof WEB_WORKER_PROTOCOL;
|
||||
kind: "EXECUTE";
|
||||
taskId: string;
|
||||
taskVersion: 1;
|
||||
requestId: string;
|
||||
workerGeneration: number;
|
||||
deadlineEpochMs: number;
|
||||
payload: unknown;
|
||||
}>;
|
||||
|
||||
export type WorkerCancelMessage = Readonly<{
|
||||
protocol: typeof WEB_WORKER_PROTOCOL;
|
||||
kind: "CANCEL";
|
||||
requestId: string;
|
||||
workerGeneration: number;
|
||||
}>;
|
||||
|
||||
export type WorkerFailureCode =
|
||||
| "TASK_UNKNOWN"
|
||||
| "VERSION_UNSUPPORTED"
|
||||
| "INPUT_INVALID"
|
||||
| "INPUT_TOO_LARGE"
|
||||
| "OUTPUT_INVALID"
|
||||
| "OUTPUT_TOO_LARGE"
|
||||
| "QUEUE_FULL"
|
||||
| "DEADLINE_EXCEEDED"
|
||||
| "CANCELLED"
|
||||
| "CRASHED"
|
||||
| "TRANSFER_FAILED"
|
||||
| "STALE_RESULT"
|
||||
| "RUNTIME_PROTOCOL_FAILURE";
|
||||
|
||||
export type WorkerResponseMessage =
|
||||
| Readonly<{
|
||||
protocol: typeof WEB_WORKER_PROTOCOL;
|
||||
kind: "SUCCESS";
|
||||
requestId: string;
|
||||
workerGeneration: number;
|
||||
payload: unknown;
|
||||
}>
|
||||
| Readonly<{
|
||||
protocol: typeof WEB_WORKER_PROTOCOL;
|
||||
kind: "FAILURE";
|
||||
requestId: string;
|
||||
workerGeneration: number;
|
||||
code: WorkerFailureCode;
|
||||
}>;
|
||||
|
||||
export function validateWebWorkerContributions(
|
||||
contributions: readonly InstalledWebWorkerContribution[],
|
||||
): readonly InstalledWebWorkerContribution[] {
|
||||
const bounds = WEB_WORKER_BOUNDS;
|
||||
if (contributions.length > bounds.taskGroups) {
|
||||
throw new TypeError("Web Worker task groups exceed their bound.");
|
||||
}
|
||||
const groupIds = new Set<string>();
|
||||
const taskIds = new Set<string>();
|
||||
for (const contribution of contributions) {
|
||||
if (!contribution.taskGroupId || groupIds.has(contribution.taskGroupId)) {
|
||||
throw new TypeError("Web Worker task group identity is invalid.");
|
||||
}
|
||||
groupIds.add(contribution.taskGroupId);
|
||||
if (
|
||||
contribution.tasks.length === 0 ||
|
||||
contribution.tasks.length > bounds.tasksPerGroup
|
||||
) {
|
||||
throw new TypeError("Web Worker task count is out of range.");
|
||||
}
|
||||
for (const task of contribution.tasks) {
|
||||
const qualified = `${contribution.taskGroupId}/${task.taskId}`;
|
||||
if (!task.taskId || taskIds.has(qualified)) {
|
||||
throw new TypeError("Duplicate Web Worker task.");
|
||||
}
|
||||
taskIds.add(qualified);
|
||||
if (
|
||||
task.taskVersion !== 1 ||
|
||||
!Number.isSafeInteger(task.maximumInputBytes) ||
|
||||
task.maximumInputBytes < 1 ||
|
||||
task.maximumInputBytes > bounds.hardInputBytes ||
|
||||
!Number.isSafeInteger(task.maximumOutputBytes) ||
|
||||
task.maximumOutputBytes < 1 ||
|
||||
task.maximumOutputBytes > bounds.hardOutputBytes ||
|
||||
!Number.isSafeInteger(task.deadlineMs) ||
|
||||
task.deadlineMs < 1 ||
|
||||
task.deadlineMs > bounds.hardDeadlineMs
|
||||
) {
|
||||
throw new TypeError(`Web Worker task bounds invalid: ${qualified}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Object.freeze([...contributions]);
|
||||
}
|
||||
Reference in New Issue
Block a user