tricu

A lazy functional language based on Tree Calculus
Log | Files | Refs | README | LICENSE

worker.js (4502B)


      1 // Test-only JS evaluator driver; excluded from the production site.
      2 import { sourceText } from '../compiler.js';
      3 import { compileExpression } from './expression.js';
      4 import { publishedBundle } from '../published-bundle.js';
      5 import { exportTree, treeFormat } from '../tree-export.js';
      6 import { BROWSER_BUDGET, collectionPolicy, PACKAGE_COLLECTION } from '../execution.js';
      7 import { Graph } from '../graph.js';
      8 import { bundleHash, loadBundle } from '../bundle.js';
      9 import { literals, makeLiteral, outputType, view } from '../codecs.js';
     10 const scope = globalThis;
     11 const send = (message) => scope.postMessage(message);
     12 const running = () => send({ type: 'state', state: 'running' });
     13 let phase = 'empty';
     14 let graph, exports;
     15 let stored;
     16 async function load(hash, localId) {
     17     phase = 'loading';
     18     running();
     19     const loaded = await publishedBundle(hash, localId);
     20     const { bundle, source } = loaded;
     21     stored = loaded;
     22     const budget = BROWSER_BUDGET;
     23     graph = new Graph(budget, collectionPolicy(budget, PACKAGE_COLLECTION.threshold));
     24     exports = loadBundle(graph, bundle);
     25     phase = 'ready';
     26     send({ type: 'loaded', hash, exports: bundle.exports, nodes: bundle.nodes.length, metadata: bundle.metadata, source });
     27 }
     28 async function run(message) {
     29     if (phase !== 'ready' || !graph || !exports)
     30         throw new Error('Load a bundle before running');
     31     phase = 'running';
     32     running();
     33     const fn = message.expression === undefined ? exports.get(message.export) : await compileExpression(graph, exports, message.expression);
     34     if (fn === undefined)
     35         throw new Error('Unknown export');
     36     if (message.mode !== 'pure' && message.mode !== 'io')
     37         throw new Error('Unsupported execution mode');
     38     const type = outputType(message.output), args = literals(message.arguments).map(value => makeLiteral(graph, value));
     39     let result = message.expression === undefined ? graph.apply(fn, args) : fn;
     40     graph.collect([result]); // Exclusive one-run session: only this application remains owned.
     41     if (message.mode === 'io')
     42         throw new Error('Reference graph IO execution is retired; use a native runtime');
     43     const displayed = view(graph, result, type);
     44     phase = 'done';
     45     send({ type: 'done', result: displayed, stats: graph.stats });
     46 }
     47 scope.onmessage = ({ data }) => {
     48     if (!data || typeof data !== 'object' || Array.isArray(data))
     49         return;
     50     const message = data;
     51     if (message['type'] === 'download' && phase === 'ready' && stored) {
     52         send({ type: 'downloaded', hash: stored.hash, bytes: stored.bytes });
     53         return;
     54     }
     55     if (message['type'] === 'export' && phase === 'ready' && stored) {
     56         try {
     57             if (typeof message['export'] !== 'string' || message['export'].length > 512)
     58                 throw new Error('Invalid export selection');
     59             const format = treeFormat(message['format']);
     60             const file = exportTree(stored.bundle, message['export'], format);
     61             send({ type: 'exported', hash: stored.hash, export: message['export'], format, file });
     62         }
     63         catch (error) {
     64             send({ type: 'export-error', message: error instanceof Error ? error.message : String(error) });
     65         }
     66         return;
     67     }
     68     let operation;
     69     try {
     70         if (message['type'] === 'load' && phase === 'empty') {
     71             const localId = message['localId'];
     72             if (localId !== undefined && (typeof localId !== 'string' || localId.length > 128))
     73                 throw new Error('Invalid catalogue ID');
     74             operation = load(bundleHash(message['hash']), localId);
     75         }
     76         else if (message['type'] === 'run' && phase === 'ready') {
     77             if (typeof message['export'] !== 'string' || message['export'].length > 512)
     78                 throw new Error('Invalid export selection');
     79             operation = run({ type: 'run', export: message['export'], arguments: message['arguments'],
     80                 ...(message['expression'] === undefined ? {} : { expression: sourceText(message['expression']) }),
     81                 output: outputType(message['output']), mode: message['mode'], step: message['step'] === true });
     82         }
     83         else
     84             return; // A Worker owns exactly one load and one run, no racing sessions.
     85     }
     86     catch (error) {
     87         operation = Promise.reject(error);
     88     }
     89     operation.catch((error) => { phase = 'done'; send({ type: 'error', message: error instanceof Error ? error.message : String(error) }); });
     90 };