UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

167 lines • 6.19 kB
import { z } from 'zod'; import { compiledModule } from "../rs/stream.js"; import { abortable, makeAbortError } from "../utils/abort.js"; const Reply = z.object({ ok: z.boolean(), value: z.unknown() }); async function createPort(done, failed) { const node = globalThis.process?.versions ?.node; if (node !== undefined) { const { Worker: NodeWorker } = await import('node:worker_threads'); const url = new URL(import.meta.url.endsWith('.ts') ? './worker_entry.ts' : './worker_entry.js', import.meta.url); const worker = new NodeWorker(url, { execArgv: url.pathname.endsWith('.ts') ? ['--experimental-transform-types', '--disable-warning=ExperimentalWarning'] : [], }); worker.on('message', (raw) => { const reply = Reply.safeParse(raw); if (reply.success) done(reply.data); else failed(new Error('invalid computation worker reply', { cause: reply.error })); }); worker.on('messageerror', failed); worker.on('error', failed); worker.on('exit', (code) => { failed(new Error(`computation worker exited (${String(code)})`)); }); return { post: (data) => { worker.postMessage(data); }, close: async () => { await worker.terminate(); }, }; } // The worker starts from the bundle core embeds, not from worker_entry.js: // a bundler that moves this module (Vite's dependency pre-bundling) leaves // no file where a relative URL would look. const { default: source } = await import("../generated/computation_worker.js"); const wasm = await compiledModule(); const url = URL.createObjectURL(new Blob([source], { type: 'text/javascript' })); const { Worker } = globalThis; const worker = new Worker(url, { type: 'module' }); worker.onmessage = (event) => { const reply = Reply.safeParse(event.data); if (reply.success) done(reply.data); else failed(new Error('invalid computation worker reply', { cause: reply.error })); }; worker.onmessageerror = () => { failed(new Error('cannot decode computation worker reply')); }; worker.onerror = (event) => { // A script the page refuses to start carries no message. failed(new Error(event.message || 'computation worker did not start')); }; return { post: (data) => { worker.postMessage({ ...data, wasm }); }, close: () => { worker.terminate(); URL.revokeObjectURL(url); return Promise.resolve(); }, }; } /** An invocation-owned worker for computations that cannot yield inside their engine. */ export class Worker { port; closing; closeSettled = false; closeFailed = false; terminal; waiting = []; async call(name, args, signal) { try { signal?.throwIfAborted(); if (this.terminal !== undefined) throw this.terminal; if (this.closeSettled && !this.closeFailed && this.port === undefined) { this.closing = undefined; this.closeSettled = false; } if (this.closing !== undefined) throw new Error('computation worker is closing'); const port = (this.port ??= this.open()); const ready = await abortable(port, signal); this.checkReady(port, signal); const reply = new Promise((resolve, reject) => { this.waiting.push({ resolve, reject }); try { ready.post({ name, args }); } catch (error) { reject(error instanceof Error ? error : new Error('cannot post computation', { cause: error })); } }); const { ok, value } = await abortable(reply, signal); if (!ok) throw value instanceof Error ? value : new Error('Computation failed', { cause: value }); return value; } catch (error) { try { await this.close(); } catch (cleanup) { throw new AggregateError([error, cleanup], 'computation worker failed to stop'); } throw error; } } close(retry = false) { if (retry && this.closeSettled && this.closeFailed) { this.closing = undefined; this.closeSettled = false; this.closeFailed = false; } if (this.closing !== undefined) return this.closing; for (const waiter of this.waiting.splice(0)) waiter.reject(makeAbortError()); const port = this.port; const stopping = Promise.resolve().then(async () => { if (port !== undefined) { let ready; try { ready = await port; } catch { this.port = undefined; return; } await ready.close(); if (this.port === port) this.port = undefined; } }); this.closing = stopping; void stopping.then(() => { this.closeSettled = true; }, () => { this.closeSettled = true; this.closeFailed = true; }); return stopping; } checkReady(port, signal) { if (port !== this.port || this.closing !== undefined) throw makeAbortError(signal); if (this.terminal !== undefined) throw this.terminal; } open() { return createPort((reply) => this.waiting.shift()?.resolve(reply), (error) => { if (this.closing !== undefined) return; this.terminal = error; for (const waiter of this.waiting.splice(0)) waiter.reject(error); }); } } //# sourceMappingURL=worker.js.map