@sdeverywhere/runtime-async 0.2.9 → 0.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -3
- package/dist/index.js +3 -91
- package/dist/node-runtime-CQ0b2ThB.js +159 -0
- package/dist/node-runtime-CQ0b2ThB.js.map +1 -0
- package/dist/runner-DWs69cGp.js +165 -0
- package/dist/runner-DWs69cGp.js.map +1 -0
- package/dist/{runner.d.cts → runner-DcPcKf0C.d.ts} +20 -9
- package/dist/runner-DcPcKf0C.d.ts.map +1 -0
- package/dist/runner.d.ts +2 -41
- package/dist/runner.js +2 -54
- package/dist/worker-CVxcTQod.js +97 -0
- package/dist/worker-CVxcTQod.js.map +1 -0
- package/dist/worker.d.ts +5 -5
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +2 -39
- package/package.json +12 -11
- package/dist/index.cjs +0 -118
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -3
- package/dist/index.js.map +0 -1
- package/dist/runner.cjs +0 -78
- package/dist/runner.cjs.map +0 -1
- package/dist/runner.js.map +0 -1
- package/dist/worker.cjs +0 -63
- package/dist/worker.cjs.map +0 -1
- package/dist/worker.d.cts +0 -14
- package/dist/worker.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { n as WorkerSpec, t as spawnAsyncModelRunner } from "./runner-DcPcKf0C.js";
|
|
2
|
+
import { exposeModelWorker } from "./worker.js";
|
|
3
|
+
export { type WorkerSpec, exposeModelWorker, spawnAsyncModelRunner };
|
package/dist/index.js
CHANGED
|
@@ -1,91 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
async function spawnAsyncModelRunner(workerSpec) {
|
|
5
|
-
if (workerSpec["path"]) {
|
|
6
|
-
return spawnAsyncModelRunnerWithWorker(new Worker(workerSpec["path"]));
|
|
7
|
-
} else {
|
|
8
|
-
return spawnAsyncModelRunnerWithWorker(BlobWorker.fromText(workerSpec["source"]));
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
async function spawnAsyncModelRunnerWithWorker(worker) {
|
|
12
|
-
const modelWorker2 = await spawn(worker);
|
|
13
|
-
const initResult = await modelWorker2.initModel();
|
|
14
|
-
const modelListing = initResult.modelListing ? new ModelListing(initResult.modelListing) : void 0;
|
|
15
|
-
const params2 = new BufferedRunModelParams(modelListing);
|
|
16
|
-
let running = false;
|
|
17
|
-
let terminated = false;
|
|
18
|
-
return {
|
|
19
|
-
createOutputs: () => {
|
|
20
|
-
return new Outputs(initResult.outputVarIds, initResult.startTime, initResult.endTime, initResult.saveFreq);
|
|
21
|
-
},
|
|
22
|
-
runModel: async (inputs, outputs, options) => {
|
|
23
|
-
if (terminated) {
|
|
24
|
-
throw new Error("Async model runner has already been terminated");
|
|
25
|
-
} else if (running) {
|
|
26
|
-
throw new Error("Async model runner only supports one `runModel` call at a time");
|
|
27
|
-
} else {
|
|
28
|
-
running = true;
|
|
29
|
-
}
|
|
30
|
-
params2.updateFromParams(inputs, outputs, options);
|
|
31
|
-
let ioBuffer;
|
|
32
|
-
try {
|
|
33
|
-
ioBuffer = await modelWorker2.runModel(Transfer(params2.getEncodedBuffer()));
|
|
34
|
-
} finally {
|
|
35
|
-
running = false;
|
|
36
|
-
}
|
|
37
|
-
params2.updateFromEncodedBuffer(ioBuffer);
|
|
38
|
-
params2.finalizeOutputs(outputs);
|
|
39
|
-
return outputs;
|
|
40
|
-
},
|
|
41
|
-
terminate: () => {
|
|
42
|
-
if (terminated) {
|
|
43
|
-
return Promise.resolve();
|
|
44
|
-
} else {
|
|
45
|
-
terminated = true;
|
|
46
|
-
return Thread.terminate(modelWorker2);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// src/worker.ts
|
|
53
|
-
import { expose, Transfer as Transfer2 } from "threads/worker";
|
|
54
|
-
import { BufferedRunModelParams as BufferedRunModelParams2, createRunnableModel } from "@sdeverywhere/runtime";
|
|
55
|
-
var initGeneratedModel;
|
|
56
|
-
var runnableModel;
|
|
57
|
-
var params = new BufferedRunModelParams2();
|
|
58
|
-
var modelWorker = {
|
|
59
|
-
async initModel() {
|
|
60
|
-
if (runnableModel) {
|
|
61
|
-
throw new Error("RunnableModel was already initialized");
|
|
62
|
-
}
|
|
63
|
-
const generatedModel = await initGeneratedModel();
|
|
64
|
-
runnableModel = createRunnableModel(generatedModel);
|
|
65
|
-
return {
|
|
66
|
-
outputVarIds: runnableModel.outputVarIds,
|
|
67
|
-
modelListing: runnableModel.modelListing,
|
|
68
|
-
startTime: runnableModel.startTime,
|
|
69
|
-
endTime: runnableModel.endTime,
|
|
70
|
-
saveFreq: runnableModel.saveFreq,
|
|
71
|
-
outputRowLength: runnableModel.numSavePoints
|
|
72
|
-
};
|
|
73
|
-
},
|
|
74
|
-
runModel(ioBuffer) {
|
|
75
|
-
if (!runnableModel) {
|
|
76
|
-
throw new Error("RunnableModel must be initialized before running the model in worker");
|
|
77
|
-
}
|
|
78
|
-
params.updateFromEncodedBuffer(ioBuffer);
|
|
79
|
-
runnableModel.runModel(params);
|
|
80
|
-
return Transfer2(ioBuffer);
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
function exposeModelWorker(init) {
|
|
84
|
-
initGeneratedModel = init;
|
|
85
|
-
expose(modelWorker);
|
|
86
|
-
}
|
|
87
|
-
export {
|
|
88
|
-
exposeModelWorker,
|
|
89
|
-
spawnAsyncModelRunner
|
|
90
|
-
};
|
|
91
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
import { t as spawnAsyncModelRunner } from "./runner-DWs69cGp.js";
|
|
2
|
+
import { t as exposeModelWorker } from "./worker-CVxcTQod.js";
|
|
3
|
+
export { exposeModelWorker, spawnAsyncModelRunner };
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
//#region src/worker-rpc/model-worker-rpc.ts
|
|
2
|
+
/** Return whether the message is a model worker request. */
|
|
3
|
+
function isModelWorkerRequest(message) {
|
|
4
|
+
const kind = message?.kind;
|
|
5
|
+
return kind === "init" || kind === "run";
|
|
6
|
+
}
|
|
7
|
+
/** Return whether the message is a model worker response. */
|
|
8
|
+
function isModelWorkerResponse(message) {
|
|
9
|
+
const kind = message?.kind;
|
|
10
|
+
return kind === "initialized" || kind === "ran" || kind === "error";
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Create a client for the model worker connected to the given handle.
|
|
14
|
+
*
|
|
15
|
+
* @param port The handle for the spawned model worker.
|
|
16
|
+
* @returns The model worker client.
|
|
17
|
+
*/
|
|
18
|
+
function createModelWorkerClient(port) {
|
|
19
|
+
let pending;
|
|
20
|
+
let terminalError;
|
|
21
|
+
/** Reject the pending request, without making the client terminal. */
|
|
22
|
+
function rejectPending(error) {
|
|
23
|
+
const request = pending;
|
|
24
|
+
pending = void 0;
|
|
25
|
+
request?.reject(error);
|
|
26
|
+
}
|
|
27
|
+
/** Make the client terminal and reject its pending request. */
|
|
28
|
+
function fail(error) {
|
|
29
|
+
if (terminalError === void 0) {
|
|
30
|
+
terminalError = error;
|
|
31
|
+
rejectPending(error);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** Send one request to the worker and wait for its response. */
|
|
35
|
+
function send(request, transferables) {
|
|
36
|
+
if (terminalError !== void 0) return Promise.reject(terminalError);
|
|
37
|
+
if (pending !== void 0) return Promise.reject(/* @__PURE__ */ new Error("Model worker only supports one request at a time"));
|
|
38
|
+
const response = new Promise((resolve, reject) => {
|
|
39
|
+
pending = {
|
|
40
|
+
resolve,
|
|
41
|
+
reject
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
try {
|
|
45
|
+
port.postMessage(request, transferables);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
rejectPending(error instanceof Error ? error : new Error(String(error)));
|
|
48
|
+
}
|
|
49
|
+
return response;
|
|
50
|
+
}
|
|
51
|
+
port.onMessage((message) => {
|
|
52
|
+
if (!isModelWorkerResponse(message)) return;
|
|
53
|
+
const request = pending;
|
|
54
|
+
if (request === void 0) return;
|
|
55
|
+
pending = void 0;
|
|
56
|
+
if (message.kind === "error") request.reject(new Error(message.message));
|
|
57
|
+
else request.resolve(message);
|
|
58
|
+
});
|
|
59
|
+
port.onError((error) => fail(error));
|
|
60
|
+
port.onClose((error) => fail(error));
|
|
61
|
+
/** Make the client terminal with an unexpected-response error and return the error. */
|
|
62
|
+
function unexpectedResponse(kind, requestName) {
|
|
63
|
+
const error = /* @__PURE__ */ new Error(`Unexpected '${kind}' response to ${requestName} request`);
|
|
64
|
+
fail(error);
|
|
65
|
+
return error;
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
async initModel() {
|
|
69
|
+
const response = await send({ kind: "init" });
|
|
70
|
+
if (response.kind !== "initialized") throw unexpectedResponse(response.kind, "model initialization");
|
|
71
|
+
return response.result;
|
|
72
|
+
},
|
|
73
|
+
async runModel(buffer) {
|
|
74
|
+
const response = await send({
|
|
75
|
+
kind: "run",
|
|
76
|
+
buffer
|
|
77
|
+
}, [buffer]);
|
|
78
|
+
if (response.kind !== "ran") throw unexpectedResponse(response.kind, "model run");
|
|
79
|
+
return response.buffer;
|
|
80
|
+
},
|
|
81
|
+
dispose(error) {
|
|
82
|
+
fail(error);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Serve model worker requests received on the given port.
|
|
88
|
+
*
|
|
89
|
+
* @param port The port connected to the runner.
|
|
90
|
+
* @param methods The model worker operations.
|
|
91
|
+
*/
|
|
92
|
+
function serveModelWorker(port, methods) {
|
|
93
|
+
port.onMessage((message) => {
|
|
94
|
+
if (!isModelWorkerRequest(message)) return;
|
|
95
|
+
const request = message;
|
|
96
|
+
/** Handle the received request and post its response. */
|
|
97
|
+
async function handleRequest() {
|
|
98
|
+
try {
|
|
99
|
+
if (request.kind === "init") {
|
|
100
|
+
const result = await methods.initModel();
|
|
101
|
+
port.postMessage({
|
|
102
|
+
kind: "initialized",
|
|
103
|
+
result
|
|
104
|
+
});
|
|
105
|
+
} else {
|
|
106
|
+
const buffer = await methods.runModel(request.buffer);
|
|
107
|
+
port.postMessage({
|
|
108
|
+
kind: "ran",
|
|
109
|
+
buffer
|
|
110
|
+
}, [buffer]);
|
|
111
|
+
}
|
|
112
|
+
} catch (error) {
|
|
113
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
114
|
+
try {
|
|
115
|
+
port.postMessage({
|
|
116
|
+
kind: "error",
|
|
117
|
+
message: errorMessage
|
|
118
|
+
});
|
|
119
|
+
} catch {}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
handleRequest();
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
//#endregion
|
|
126
|
+
//#region src/worker-rpc/node-runtime.ts
|
|
127
|
+
/**
|
|
128
|
+
* Return the Node.js `process` object, or undefined if not running in Node.js.
|
|
129
|
+
* @hidden
|
|
130
|
+
*/
|
|
131
|
+
function nodeProcess() {
|
|
132
|
+
const process = globalThis.process;
|
|
133
|
+
return process?.versions?.node !== void 0 ? process : void 0;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Return true if the current context is running in a Node.js environment (as
|
|
137
|
+
* opposed to a browser environment).
|
|
138
|
+
* @hidden
|
|
139
|
+
*/
|
|
140
|
+
function isNodeEnvironment() {
|
|
141
|
+
return nodeProcess() !== void 0;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Return the `node:worker_threads` module.
|
|
145
|
+
*
|
|
146
|
+
* @returns The `node:worker_threads` module.
|
|
147
|
+
* @throws An error if the module cannot be accessed (for example, when running
|
|
148
|
+
* in a version of Node.js that does not support `process.getBuiltinModule`).
|
|
149
|
+
* @hidden
|
|
150
|
+
*/
|
|
151
|
+
function nodeWorkerThreads() {
|
|
152
|
+
const getBuiltinModule = nodeProcess()?.getBuiltinModule;
|
|
153
|
+
if (getBuiltinModule === void 0) throw new Error("Failed to access the `node:worker_threads` module; @sdeverywhere/runtime-async requires Node.js 22.3 or later");
|
|
154
|
+
return getBuiltinModule("node:worker_threads");
|
|
155
|
+
}
|
|
156
|
+
//#endregion
|
|
157
|
+
export { serveModelWorker as i, nodeWorkerThreads as n, createModelWorkerClient as r, isNodeEnvironment as t };
|
|
158
|
+
|
|
159
|
+
//# sourceMappingURL=node-runtime-CQ0b2ThB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-runtime-CQ0b2ThB.js","names":[],"sources":["../src/worker-rpc/model-worker-rpc.ts","../src/worker-rpc/node-runtime.ts"],"sourcesContent":["// Copyright (c) 2026 Climate Interactive / New Venture Fund\n\nimport type { WorkerHandle, WorkerPort } from './worker-port'\n\n/** The model metadata returned after the worker initializes the model. */\nexport interface InitResult {\n /** The IDs of the output variables, in output-buffer order. */\n outputVarIds: string[]\n /** The model listing, if it was included in the generated model. */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n modelListing?: /*ModelListingSpecs*/ any\n /** The start time of the model. */\n startTime: number\n /** The end time of the model. */\n endTime: number\n /** The frequency at which output values are saved. */\n saveFreq: number\n}\n\n/** A request sent from the runner to the model worker. */\ntype ModelWorkerRequest = { kind: 'init' } | { kind: 'run'; buffer: ArrayBuffer }\n\n/** A response sent from the model worker to the runner. */\ntype ModelWorkerResponse =\n | { kind: 'initialized'; result: InitResult }\n | { kind: 'ran'; buffer: ArrayBuffer }\n | { kind: 'error'; message: string }\n\n/** The operations that can be performed by the model worker. */\nexport interface ModelWorkerMethods {\n /** Initialize the model and return its metadata. */\n initModel(): InitResult | Promise<InitResult>\n /** Run the model using the parameters encoded in the given buffer. */\n runModel(buffer: ArrayBuffer): ArrayBuffer | Promise<ArrayBuffer>\n}\n\n/** A client for the operations exposed by the model worker. */\nexport interface ModelWorkerClient {\n /** Initialize the model and return its metadata. */\n initModel(): Promise<InitResult>\n /** Run the model using the parameters encoded in the given buffer. */\n runModel(buffer: ArrayBuffer): Promise<ArrayBuffer>\n /** Make the client terminal and reject its pending request. */\n dispose(error: Error): void\n}\n\n/** The callbacks for the single request that is currently pending. */\ninterface PendingRequest {\n /** Resolve the request with a worker response. */\n resolve(response: ModelWorkerResponse): void\n /** Reject the request with an error. */\n reject(error: Error): void\n}\n\n/** Return whether the message is a model worker request. */\nfunction isModelWorkerRequest(message: unknown): message is ModelWorkerRequest {\n const kind = (message as Partial<ModelWorkerRequest>)?.kind\n return kind === 'init' || kind === 'run'\n}\n\n/** Return whether the message is a model worker response. */\nfunction isModelWorkerResponse(message: unknown): message is ModelWorkerResponse {\n const kind = (message as Partial<ModelWorkerResponse>)?.kind\n return kind === 'initialized' || kind === 'ran' || kind === 'error'\n}\n\n/**\n * Create a client for the model worker connected to the given handle.\n *\n * @param port The handle for the spawned model worker.\n * @returns The model worker client.\n */\nexport function createModelWorkerClient(port: WorkerHandle): ModelWorkerClient {\n let pending: PendingRequest | undefined\n let terminalError: Error | undefined\n\n /** Reject the pending request, without making the client terminal. */\n function rejectPending(error: Error): void {\n const request = pending\n pending = undefined\n request?.reject(error)\n }\n\n /** Make the client terminal and reject its pending request. */\n function fail(error: Error): void {\n if (terminalError === undefined) {\n terminalError = error\n rejectPending(error)\n }\n }\n\n /** Send one request to the worker and wait for its response. */\n function send(request: ModelWorkerRequest, transferables?: Transferable[]): Promise<ModelWorkerResponse> {\n if (terminalError !== undefined) {\n return Promise.reject(terminalError)\n }\n if (pending !== undefined) {\n return Promise.reject(new Error('Model worker only supports one request at a time'))\n }\n\n const response = new Promise<ModelWorkerResponse>((resolve, reject) => {\n pending = { resolve, reject }\n })\n try {\n port.postMessage(request, transferables)\n } catch (error) {\n rejectPending(error instanceof Error ? error : new Error(String(error)))\n }\n return response\n }\n\n port.onMessage(message => {\n if (!isModelWorkerResponse(message)) {\n return\n }\n\n const request = pending\n if (request === undefined) {\n return\n }\n pending = undefined\n\n if (message.kind === 'error') {\n request.reject(new Error(message.message))\n } else {\n request.resolve(message)\n }\n })\n port.onError(error => fail(error))\n port.onClose(error => fail(error))\n\n /** Make the client terminal with an unexpected-response error and return the error. */\n function unexpectedResponse(kind: string, requestName: string): Error {\n // A response that does not match its request means the protocol state is\n // corrupt, so make the client terminal rather than allow further requests\n const error = new Error(`Unexpected '${kind}' response to ${requestName} request`)\n fail(error)\n return error\n }\n\n return {\n async initModel(): Promise<InitResult> {\n const response = await send({ kind: 'init' })\n if (response.kind !== 'initialized') {\n throw unexpectedResponse(response.kind, 'model initialization')\n }\n return response.result\n },\n\n async runModel(buffer: ArrayBuffer): Promise<ArrayBuffer> {\n const response = await send({ kind: 'run', buffer }, [buffer])\n if (response.kind !== 'ran') {\n throw unexpectedResponse(response.kind, 'model run')\n }\n return response.buffer\n },\n\n dispose(error: Error): void {\n fail(error)\n }\n }\n}\n\n/**\n * Serve model worker requests received on the given port.\n *\n * @param port The port connected to the runner.\n * @param methods The model worker operations.\n */\nexport function serveModelWorker(port: WorkerPort, methods: ModelWorkerMethods): void {\n port.onMessage(message => {\n if (!isModelWorkerRequest(message)) {\n return\n }\n const request = message\n\n /** Handle the received request and post its response. */\n async function handleRequest(): Promise<void> {\n try {\n if (request.kind === 'init') {\n const result = await methods.initModel()\n port.postMessage({ kind: 'initialized', result } satisfies ModelWorkerResponse)\n } else {\n const buffer = await methods.runModel(request.buffer)\n port.postMessage({ kind: 'ran', buffer } satisfies ModelWorkerResponse, [buffer])\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error)\n try {\n port.postMessage({ kind: 'error', message: errorMessage } satisfies ModelWorkerResponse)\n } catch {\n // Nothing more can be done if the error response also cannot be posted\n // (for example, when the port is already closed); swallow the failure\n // rather than crash the worker with an unhandled rejection. The runner\n // side will observe the problem through worker error/exit events.\n }\n }\n }\n\n void handleRequest()\n })\n}\n","// Copyright (c) 2026 Climate Interactive / New Venture Fund\n\n//\n// This module provides access to the Node.js APIs that are needed to run the model\n// in a worker thread. Note that we deliberately avoid a static (or dynamic) import\n// of `node:worker_threads` here, and instead use `process.getBuiltinModule`, which\n// is a plain property access that bundlers do not need to resolve. This allows a\n// single bundle to work in both browser and Node.js environments without any\n// bundler-specific configuration or polyfills.\n//\n// Note also that we declare only the small structural subset of the Node.js API\n// that we actually use, so that this package does not depend on `@types/node`.\n//\n\n/**\n * The subset of the Node.js `MessagePort` API that is used by this package.\n * @hidden\n */\nexport interface NodeMessagePort {\n postMessage(message: unknown, transferList?: Transferable[]): void\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n on(event: string, listener: (arg: any) => void): void\n}\n\n/**\n * The subset of the Node.js `Worker` API that is used by this package.\n * @hidden\n */\nexport interface NodeWorker extends NodeMessagePort {\n terminate(): Promise<number>\n}\n\n/**\n * The subset of the `node:worker_threads` module that is used by this package.\n * @hidden\n */\nexport interface NodeWorkerThreads {\n Worker: new (source: string | URL, options?: { eval?: boolean }) => NodeWorker\n parentPort?: NodeMessagePort\n}\n\n/**\n * The subset of the Node.js `process` object that is used by this package.\n * @hidden\n */\ninterface NodeProcess {\n versions?: { node?: string }\n getBuiltinModule?(id: string): unknown\n}\n\n/**\n * Return the Node.js `process` object, or undefined if not running in Node.js.\n * @hidden\n */\nfunction nodeProcess(): NodeProcess | undefined {\n const process = (globalThis as { process?: NodeProcess }).process\n return process?.versions?.node !== undefined ? process : undefined\n}\n\n/**\n * Return true if the current context is running in a Node.js environment (as\n * opposed to a browser environment).\n * @hidden\n */\nexport function isNodeEnvironment(): boolean {\n return nodeProcess() !== undefined\n}\n\n/**\n * Return the `node:worker_threads` module.\n *\n * @returns The `node:worker_threads` module.\n * @throws An error if the module cannot be accessed (for example, when running\n * in a version of Node.js that does not support `process.getBuiltinModule`).\n * @hidden\n */\nexport function nodeWorkerThreads(): NodeWorkerThreads {\n const getBuiltinModule = nodeProcess()?.getBuiltinModule\n if (getBuiltinModule === undefined) {\n throw new Error(\n 'Failed to access the `node:worker_threads` module; @sdeverywhere/runtime-async requires Node.js 22.3 or later'\n )\n }\n return getBuiltinModule('node:worker_threads') as NodeWorkerThreads\n}\n"],"mappings":";;AAuDA,SAAS,qBAAqB,SAAiD;CAC7E,MAAM,OAAQ,SAAyC;CACvD,OAAO,SAAS,UAAU,SAAS;AACrC;;AAGA,SAAS,sBAAsB,SAAkD;CAC/E,MAAM,OAAQ,SAA0C;CACxD,OAAO,SAAS,iBAAiB,SAAS,SAAS,SAAS;AAC9D;;;;;;;AAQA,SAAgB,wBAAwB,MAAuC;CAC7E,IAAI;CACJ,IAAI;;CAGJ,SAAS,cAAc,OAAoB;EACzC,MAAM,UAAU;EAChB,UAAU,KAAA;EACV,SAAS,OAAO,KAAK;CACvB;;CAGA,SAAS,KAAK,OAAoB;EAChC,IAAI,kBAAkB,KAAA,GAAW;GAC/B,gBAAgB;GAChB,cAAc,KAAK;EACrB;CACF;;CAGA,SAAS,KAAK,SAA6B,eAA8D;EACvG,IAAI,kBAAkB,KAAA,GACpB,OAAO,QAAQ,OAAO,aAAa;EAErC,IAAI,YAAY,KAAA,GACd,OAAO,QAAQ,uBAAO,IAAI,MAAM,kDAAkD,CAAC;EAGrF,MAAM,WAAW,IAAI,SAA8B,SAAS,WAAW;GACrE,UAAU;IAAE;IAAS;GAAO;EAC9B,CAAC;EACD,IAAI;GACF,KAAK,YAAY,SAAS,aAAa;EACzC,SAAS,OAAO;GACd,cAAc,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC;EACzE;EACA,OAAO;CACT;CAEA,KAAK,WAAU,YAAW;EACxB,IAAI,CAAC,sBAAsB,OAAO,GAChC;EAGF,MAAM,UAAU;EAChB,IAAI,YAAY,KAAA,GACd;EAEF,UAAU,KAAA;EAEV,IAAI,QAAQ,SAAS,SACnB,QAAQ,OAAO,IAAI,MAAM,QAAQ,OAAO,CAAC;OAEzC,QAAQ,QAAQ,OAAO;CAE3B,CAAC;CACD,KAAK,SAAQ,UAAS,KAAK,KAAK,CAAC;CACjC,KAAK,SAAQ,UAAS,KAAK,KAAK,CAAC;;CAGjC,SAAS,mBAAmB,MAAc,aAA4B;EAGpE,MAAM,wBAAQ,IAAI,MAAM,eAAe,KAAK,gBAAgB,YAAY,SAAS;EACjF,KAAK,KAAK;EACV,OAAO;CACT;CAEA,OAAO;EACL,MAAM,YAAiC;GACrC,MAAM,WAAW,MAAM,KAAK,EAAE,MAAM,OAAO,CAAC;GAC5C,IAAI,SAAS,SAAS,eACpB,MAAM,mBAAmB,SAAS,MAAM,sBAAsB;GAEhE,OAAO,SAAS;EAClB;EAEA,MAAM,SAAS,QAA2C;GACxD,MAAM,WAAW,MAAM,KAAK;IAAE,MAAM;IAAO;GAAO,GAAG,CAAC,MAAM,CAAC;GAC7D,IAAI,SAAS,SAAS,OACpB,MAAM,mBAAmB,SAAS,MAAM,WAAW;GAErD,OAAO,SAAS;EAClB;EAEA,QAAQ,OAAoB;GAC1B,KAAK,KAAK;EACZ;CACF;AACF;;;;;;;AAQA,SAAgB,iBAAiB,MAAkB,SAAmC;CACpF,KAAK,WAAU,YAAW;EACxB,IAAI,CAAC,qBAAqB,OAAO,GAC/B;EAEF,MAAM,UAAU;;EAGhB,eAAe,gBAA+B;GAC5C,IAAI;IACF,IAAI,QAAQ,SAAS,QAAQ;KAC3B,MAAM,SAAS,MAAM,QAAQ,UAAU;KACvC,KAAK,YAAY;MAAE,MAAM;MAAe;KAAO,CAA+B;IAChF,OAAO;KACL,MAAM,SAAS,MAAM,QAAQ,SAAS,QAAQ,MAAM;KACpD,KAAK,YAAY;MAAE,MAAM;MAAO;KAAO,GAAiC,CAAC,MAAM,CAAC;IAClF;GACF,SAAS,OAAO;IACd,MAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;IAC1E,IAAI;KACF,KAAK,YAAY;MAAE,MAAM;MAAS,SAAS;KAAa,CAA+B;IACzF,QAAQ,CAKR;GACF;EACF;EAEA,cAAmB;CACrB,CAAC;AACH;;;;;;;ACnJA,SAAS,cAAuC;CAC9C,MAAM,UAAW,WAAyC;CAC1D,OAAO,SAAS,UAAU,SAAS,KAAA,IAAY,UAAU,KAAA;AAC3D;;;;;;AAOA,SAAgB,oBAA6B;CAC3C,OAAO,YAAY,MAAM,KAAA;AAC3B;;;;;;;;;AAUA,SAAgB,oBAAuC;CACrD,MAAM,mBAAmB,YAAY,CAAC,EAAE;CACxC,IAAI,qBAAqB,KAAA,GACvB,MAAM,IAAI,MACR,+GACF;CAEF,OAAO,iBAAiB,qBAAqB;AAC/C"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { n as nodeWorkerThreads, r as createModelWorkerClient, t as isNodeEnvironment } from "./node-runtime-CQ0b2ThB.js";
|
|
2
|
+
import { BufferedRunModelParams, ModelListing, Outputs } from "@sdeverywhere/runtime";
|
|
3
|
+
//#region src/worker-rpc/spawn-worker.ts
|
|
4
|
+
/**
|
|
5
|
+
* Adapt the given Web Worker to the `WorkerHandle` interface.
|
|
6
|
+
*
|
|
7
|
+
* @param worker The Web Worker instance.
|
|
8
|
+
* @param onTerminate An optional function that is called after the worker is terminated.
|
|
9
|
+
* @returns The handle for the given worker.
|
|
10
|
+
* @hidden For internal use only.
|
|
11
|
+
*/
|
|
12
|
+
function portForWebWorker(worker, onTerminate) {
|
|
13
|
+
return {
|
|
14
|
+
postMessage: (message, transferables) => worker.postMessage(message, transferables ?? []),
|
|
15
|
+
onMessage: (handler) => worker.addEventListener("message", (event) => handler(event.data)),
|
|
16
|
+
onError: (handler) => {
|
|
17
|
+
worker.addEventListener("error", (event) => handler(event.error ?? new Error(event.message)));
|
|
18
|
+
worker.addEventListener("messageerror", () => {
|
|
19
|
+
handler(/* @__PURE__ */ new Error("Failed to deserialize message received from worker"));
|
|
20
|
+
});
|
|
21
|
+
},
|
|
22
|
+
onClose: () => void 0,
|
|
23
|
+
terminate: () => {
|
|
24
|
+
worker.terminate();
|
|
25
|
+
onTerminate?.();
|
|
26
|
+
return Promise.resolve();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Adapt the given Node.js worker to the `WorkerHandle` interface.
|
|
32
|
+
*
|
|
33
|
+
* @param worker The Node.js worker instance.
|
|
34
|
+
* @returns The handle for the given worker.
|
|
35
|
+
* @hidden For internal use only.
|
|
36
|
+
*/
|
|
37
|
+
function portForNodeWorker(worker) {
|
|
38
|
+
return {
|
|
39
|
+
postMessage: (message, transferables) => worker.postMessage(message, transferables ?? []),
|
|
40
|
+
onMessage: (handler) => worker.on("message", handler),
|
|
41
|
+
onError: (handler) => {
|
|
42
|
+
worker.on("error", handler);
|
|
43
|
+
worker.on("messageerror", handler);
|
|
44
|
+
},
|
|
45
|
+
onClose: (handler) => {
|
|
46
|
+
worker.on("exit", (exitCode) => handler(/* @__PURE__ */ new Error(`Worker exited with code ${exitCode}`)));
|
|
47
|
+
},
|
|
48
|
+
terminate: async () => {
|
|
49
|
+
await worker.terminate();
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Spawn a worker for the given spec. This uses a Web Worker when running in a
|
|
55
|
+
* browser environment, or a `worker_threads` worker when running in a Node.js
|
|
56
|
+
* environment.
|
|
57
|
+
*
|
|
58
|
+
* @param spec Either a `path` to the worker JavaScript file, or the `source`
|
|
59
|
+
* containing the full JavaScript source of the worker.
|
|
60
|
+
* @returns The handle for the spawned worker.
|
|
61
|
+
* @hidden For internal use only.
|
|
62
|
+
*/
|
|
63
|
+
function spawnWorker(spec) {
|
|
64
|
+
if (isNodeEnvironment()) {
|
|
65
|
+
const { Worker } = nodeWorkerThreads();
|
|
66
|
+
if ("source" in spec) return portForNodeWorker(new Worker(spec.source, { eval: true }));
|
|
67
|
+
else return portForNodeWorker(new Worker(spec.path));
|
|
68
|
+
} else if ("source" in spec) {
|
|
69
|
+
const blob = new Blob([spec.source], { type: "text/javascript" });
|
|
70
|
+
const url = URL.createObjectURL(blob);
|
|
71
|
+
return portForWebWorker(new Worker(url), () => URL.revokeObjectURL(url));
|
|
72
|
+
} else return portForWebWorker(new Worker(spec.path));
|
|
73
|
+
}
|
|
74
|
+
//#endregion
|
|
75
|
+
//#region src/runner.ts
|
|
76
|
+
/**
|
|
77
|
+
* Initialize a `ModelRunner` that runs the model asynchronously in a worker
|
|
78
|
+
* (a Web Worker when running in a browser environment, or a worker thread
|
|
79
|
+
* when running in a Node.js environment).
|
|
80
|
+
*
|
|
81
|
+
* In your app project, define a JavaScript file, called `worker.js` for example,
|
|
82
|
+
* that initializes the generated model in the context of a worker thread:
|
|
83
|
+
*
|
|
84
|
+
* ```js
|
|
85
|
+
* import { exposeModelWorker } from '@sdeverywhere/runtime-async/worker'
|
|
86
|
+
* import loadGeneratedModel from './sde-prep/generated-model.js'
|
|
87
|
+
*
|
|
88
|
+
* exposeModelWorker(loadGeneratedModel)
|
|
89
|
+
* ```
|
|
90
|
+
*
|
|
91
|
+
* Then, in your web app, call the `spawnAsyncModelRunner` function, which
|
|
92
|
+
* will spawn the worker thread and initialize the `ModelRunner` that communicates
|
|
93
|
+
* with the worker:
|
|
94
|
+
*
|
|
95
|
+
* ```js
|
|
96
|
+
* import { spawnAsyncModelRunner } from '@sdeverywhere/runtime-async/runner'
|
|
97
|
+
*
|
|
98
|
+
* async function initApp() {
|
|
99
|
+
* // ...
|
|
100
|
+
* const runner = await spawnAsyncModelRunner({ path: './worker.js' })
|
|
101
|
+
* // ...
|
|
102
|
+
* }
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* The returned promise rejects if the worker fails to initialize the model or
|
|
106
|
+
* exits unexpectedly.
|
|
107
|
+
*
|
|
108
|
+
* @param workerSpec Either a `path` to the worker JavaScript file, or the `source`
|
|
109
|
+
* containing the full JavaScript source of the worker.
|
|
110
|
+
*/
|
|
111
|
+
async function spawnAsyncModelRunner(workerSpec) {
|
|
112
|
+
return spawnAsyncModelRunnerWithWorker(spawnWorker(workerSpec));
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* @hidden For internal use only
|
|
116
|
+
*/
|
|
117
|
+
async function spawnAsyncModelRunnerWithWorker(worker) {
|
|
118
|
+
const client = createModelWorkerClient(worker);
|
|
119
|
+
let initResult;
|
|
120
|
+
try {
|
|
121
|
+
initResult = await client.initModel();
|
|
122
|
+
} catch (error) {
|
|
123
|
+
try {
|
|
124
|
+
await worker.terminate();
|
|
125
|
+
} catch {}
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
const modelListing = initResult.modelListing ? new ModelListing(initResult.modelListing) : void 0;
|
|
129
|
+
const params = new BufferedRunModelParams(modelListing);
|
|
130
|
+
let running = false;
|
|
131
|
+
let terminated = false;
|
|
132
|
+
return {
|
|
133
|
+
createOutputs: () => {
|
|
134
|
+
return new Outputs(initResult.outputVarIds, initResult.startTime, initResult.endTime, initResult.saveFreq);
|
|
135
|
+
},
|
|
136
|
+
runModel: async (inputs, outputs, options) => {
|
|
137
|
+
if (terminated) throw new Error("Async model runner has already been terminated");
|
|
138
|
+
else if (running) throw new Error("Async model runner only supports one `runModel` call at a time");
|
|
139
|
+
else running = true;
|
|
140
|
+
params.updateFromParams(inputs, outputs, options);
|
|
141
|
+
let ioBuffer;
|
|
142
|
+
try {
|
|
143
|
+
const buffer = params.getEncodedBuffer();
|
|
144
|
+
ioBuffer = await client.runModel(buffer);
|
|
145
|
+
} finally {
|
|
146
|
+
running = false;
|
|
147
|
+
}
|
|
148
|
+
params.updateFromEncodedBuffer(ioBuffer);
|
|
149
|
+
params.finalizeOutputs(outputs);
|
|
150
|
+
return outputs;
|
|
151
|
+
},
|
|
152
|
+
terminate: () => {
|
|
153
|
+
if (terminated) return Promise.resolve();
|
|
154
|
+
else {
|
|
155
|
+
terminated = true;
|
|
156
|
+
client.dispose(/* @__PURE__ */ new Error("Async model runner has already been terminated"));
|
|
157
|
+
return worker.terminate();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
//#endregion
|
|
163
|
+
export { spawnAsyncModelRunner as t };
|
|
164
|
+
|
|
165
|
+
//# sourceMappingURL=runner-DWs69cGp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner-DWs69cGp.js","names":[],"sources":["../src/worker-rpc/spawn-worker.ts","../src/runner.ts"],"sourcesContent":["// Copyright (c) 2026 Climate Interactive / New Venture Fund\n\nimport type { NodeWorker } from './node-runtime'\nimport { isNodeEnvironment, nodeWorkerThreads } from './node-runtime'\nimport type { WorkerHandle } from './worker-port'\n\n/**\n * Specifies the worker to be spawned; either a `path` to the worker JavaScript\n * file, or the `source` containing the full JavaScript source of the worker.\n */\nexport type WorkerSpec = { path: string } | { source: string }\n\n/**\n * The subset of the browser `Worker` API that is used by this package.\n * @hidden\n */\ninterface WebWorker {\n postMessage(message: unknown, transferables?: Transferable[]): void\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n addEventListener(type: string, listener: (event: any) => void): void\n terminate(): void\n}\n\n/**\n * Adapt the given Web Worker to the `WorkerHandle` interface.\n *\n * @param worker The Web Worker instance.\n * @param onTerminate An optional function that is called after the worker is terminated.\n * @returns The handle for the given worker.\n * @hidden For internal use only.\n */\nexport function portForWebWorker(worker: WebWorker, onTerminate?: () => void): WorkerHandle {\n return {\n postMessage: (message, transferables) => worker.postMessage(message, transferables ?? []),\n\n onMessage: handler => worker.addEventListener('message', event => handler(event.data)),\n\n onError: handler => {\n worker.addEventListener('error', event => handler(event.error ?? new Error(event.message)))\n // A `messageerror` event fires when a message received from the worker fails\n // deserialization; report it as an error so that a pending request settles\n // instead of hanging\n worker.addEventListener('messageerror', () => {\n handler(new Error('Failed to deserialize message received from worker'))\n })\n },\n\n // Web Workers do not expose an event when they close themselves (for example,\n // by calling `self.close()` or by running to completion without installing a\n // message listener), so unlike the Node adapter, this cannot report an\n // unexpected close and a pending request would remain unsettled in that case.\n // This is acceptable because worker scripts are expected to call\n // `exposeModelWorker`, which installs a message listener that keeps the\n // worker alive (the scripts generated by `plugin-worker` always do).\n onClose: () => undefined,\n\n terminate: () => {\n worker.terminate()\n onTerminate?.()\n return Promise.resolve()\n }\n }\n}\n\n/**\n * Adapt the given Node.js worker to the `WorkerHandle` interface.\n *\n * @param worker The Node.js worker instance.\n * @returns The handle for the given worker.\n * @hidden For internal use only.\n */\nexport function portForNodeWorker(worker: NodeWorker): WorkerHandle {\n return {\n postMessage: (message, transferables) => worker.postMessage(message, transferables ?? []),\n\n onMessage: handler => worker.on('message', handler),\n\n onError: handler => {\n worker.on('error', handler)\n // A `messageerror` event fires when a message received from the worker fails\n // deserialization; report it as an error so that a pending request settles\n // instead of hanging\n worker.on('messageerror', handler)\n },\n\n onClose: handler => {\n worker.on('exit', exitCode => handler(new Error(`Worker exited with code ${exitCode}`)))\n },\n\n terminate: async () => {\n await worker.terminate()\n }\n }\n}\n\n/**\n * Spawn a worker for the given spec. This uses a Web Worker when running in a\n * browser environment, or a `worker_threads` worker when running in a Node.js\n * environment.\n *\n * @param spec Either a `path` to the worker JavaScript file, or the `source`\n * containing the full JavaScript source of the worker.\n * @returns The handle for the spawned worker.\n * @hidden For internal use only.\n */\nexport function spawnWorker(spec: WorkerSpec): WorkerHandle {\n if (isNodeEnvironment()) {\n const { Worker } = nodeWorkerThreads()\n if ('source' in spec) {\n // Note that `eval` mode runs the source as CommonJS, which is compatible with\n // the `iife` format bundles produced by `plugin-worker`\n return portForNodeWorker(new Worker(spec.source, { eval: true }))\n } else {\n return portForNodeWorker(new Worker(spec.path))\n }\n } else {\n if ('source' in spec) {\n // Wrap the source in a blob so that it can be loaded as a worker. Note that we\n // hold on to the URL and revoke it only after the worker is terminated, since\n // the worker is not guaranteed to have fetched the source by the time the\n // `Worker` constructor returns.\n const blob = new Blob([spec.source], { type: 'text/javascript' })\n const url = URL.createObjectURL(blob)\n return portForWebWorker(new Worker(url), () => URL.revokeObjectURL(url))\n } else {\n return portForWebWorker(new Worker(spec.path))\n }\n }\n}\n","// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport type { ModelRunner } from '@sdeverywhere/runtime'\nimport { BufferedRunModelParams, ModelListing, Outputs } from '@sdeverywhere/runtime'\n\nimport { createModelWorkerClient } from './worker-rpc/model-worker-rpc'\nimport type { InitResult } from './worker-rpc/model-worker-rpc'\nimport type { WorkerSpec } from './worker-rpc/spawn-worker'\nimport { spawnWorker } from './worker-rpc/spawn-worker'\nimport type { WorkerHandle } from './worker-rpc/worker-port'\n\nexport type { WorkerSpec }\n\n/**\n * Initialize a `ModelRunner` that runs the model asynchronously in a worker\n * (a Web Worker when running in a browser environment, or a worker thread\n * when running in a Node.js environment).\n *\n * In your app project, define a JavaScript file, called `worker.js` for example,\n * that initializes the generated model in the context of a worker thread:\n *\n * ```js\n * import { exposeModelWorker } from '@sdeverywhere/runtime-async/worker'\n * import loadGeneratedModel from './sde-prep/generated-model.js'\n *\n * exposeModelWorker(loadGeneratedModel)\n * ```\n *\n * Then, in your web app, call the `spawnAsyncModelRunner` function, which\n * will spawn the worker thread and initialize the `ModelRunner` that communicates\n * with the worker:\n *\n * ```js\n * import { spawnAsyncModelRunner } from '@sdeverywhere/runtime-async/runner'\n *\n * async function initApp() {\n * // ...\n * const runner = await spawnAsyncModelRunner({ path: './worker.js' })\n * // ...\n * }\n * ```\n *\n * The returned promise rejects if the worker fails to initialize the model or\n * exits unexpectedly.\n *\n * @param workerSpec Either a `path` to the worker JavaScript file, or the `source`\n * containing the full JavaScript source of the worker.\n */\nexport async function spawnAsyncModelRunner(workerSpec: WorkerSpec): Promise<ModelRunner> {\n return spawnAsyncModelRunnerWithWorker(spawnWorker(workerSpec))\n}\n\n/**\n * @hidden For internal use only\n */\nasync function spawnAsyncModelRunnerWithWorker(worker: WorkerHandle): Promise<ModelRunner> {\n // Create the client that communicates with the `ModelWorker` running in the worker\n const client = createModelWorkerClient(worker)\n\n // Wait for the worker to initialize the model (in the worker thread). If\n // initialization fails, make sure the worker and any associated resources are\n // released before propagating the original error.\n let initResult: InitResult\n try {\n initResult = await client.initModel()\n } catch (error) {\n try {\n await worker.terminate()\n } catch {\n // Preserve the model initialization error if worker termination also fails\n }\n throw error\n }\n\n // Create a `ModelListing` instance if the listing was defined in the generated model\n const modelListing = initResult.modelListing ? new ModelListing(initResult.modelListing) : undefined\n\n // Maintain a `BufferedRunModelParams` instance that holds the I/O parameters\n const params = new BufferedRunModelParams(modelListing)\n\n // Use a flag to ensure that only one request is made at a time\n let running = false\n\n // Disallow `runModel` after the runner has been terminated\n let terminated = false\n\n return {\n createOutputs: () => {\n return new Outputs(initResult.outputVarIds, initResult.startTime, initResult.endTime, initResult.saveFreq)\n },\n\n runModel: async (inputs, outputs, options) => {\n if (terminated) {\n throw new Error('Async model runner has already been terminated')\n } else if (running) {\n throw new Error('Async model runner only supports one `runModel` call at a time')\n } else {\n running = true\n }\n\n // Update the I/O parameters\n params.updateFromParams(inputs, outputs, options)\n\n // Run the model in the worker. We transfer the underlying `ArrayBuffer`\n // instance to the worker to make it no-copy transferable, and then the\n // worker will transfer it back to us.\n let ioBuffer: ArrayBuffer\n try {\n const buffer = params.getEncodedBuffer()\n ioBuffer = await client.runModel(buffer)\n } finally {\n running = false\n }\n\n // Once the buffer is transferred to the worker, the buffer in the\n // `BufferedRunModelParams` becomes \"detached\" and is no longer usable.\n // After the buffer is transferred back from the worker, we need to\n // restore the state of the object to use the new buffer.\n params.updateFromEncodedBuffer(ioBuffer)\n\n // Copy the output values and elapsed time from the buffer to the\n // `Outputs` instance\n params.finalizeOutputs(outputs)\n\n return outputs\n },\n\n terminate: () => {\n if (terminated) {\n return Promise.resolve()\n } else {\n terminated = true\n client.dispose(new Error('Async model runner has already been terminated'))\n return worker.terminate()\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;;AA+BA,SAAgB,iBAAiB,QAAmB,aAAwC;CAC1F,OAAO;EACL,cAAc,SAAS,kBAAkB,OAAO,YAAY,SAAS,iBAAiB,CAAC,CAAC;EAExF,YAAW,YAAW,OAAO,iBAAiB,YAAW,UAAS,QAAQ,MAAM,IAAI,CAAC;EAErF,UAAS,YAAW;GAClB,OAAO,iBAAiB,UAAS,UAAS,QAAQ,MAAM,SAAS,IAAI,MAAM,MAAM,OAAO,CAAC,CAAC;GAI1F,OAAO,iBAAiB,sBAAsB;IAC5C,wBAAQ,IAAI,MAAM,oDAAoD,CAAC;GACzE,CAAC;EACH;EASA,eAAe,KAAA;EAEf,iBAAiB;GACf,OAAO,UAAU;GACjB,cAAc;GACd,OAAO,QAAQ,QAAQ;EACzB;CACF;AACF;;;;;;;;AASA,SAAgB,kBAAkB,QAAkC;CAClE,OAAO;EACL,cAAc,SAAS,kBAAkB,OAAO,YAAY,SAAS,iBAAiB,CAAC,CAAC;EAExF,YAAW,YAAW,OAAO,GAAG,WAAW,OAAO;EAElD,UAAS,YAAW;GAClB,OAAO,GAAG,SAAS,OAAO;GAI1B,OAAO,GAAG,gBAAgB,OAAO;EACnC;EAEA,UAAS,YAAW;GAClB,OAAO,GAAG,SAAQ,aAAY,wBAAQ,IAAI,MAAM,2BAA2B,UAAU,CAAC,CAAC;EACzF;EAEA,WAAW,YAAY;GACrB,MAAM,OAAO,UAAU;EACzB;CACF;AACF;;;;;;;;;;;AAYA,SAAgB,YAAY,MAAgC;CAC1D,IAAI,kBAAkB,GAAG;EACvB,MAAM,EAAE,WAAW,kBAAkB;EACrC,IAAI,YAAY,MAGd,OAAO,kBAAkB,IAAI,OAAO,KAAK,QAAQ,EAAE,MAAM,KAAK,CAAC,CAAC;OAEhE,OAAO,kBAAkB,IAAI,OAAO,KAAK,IAAI,CAAC;CAElD,OACE,IAAI,YAAY,MAAM;EAKpB,MAAM,OAAO,IAAI,KAAK,CAAC,KAAK,MAAM,GAAG,EAAE,MAAM,kBAAkB,CAAC;EAChE,MAAM,MAAM,IAAI,gBAAgB,IAAI;EACpC,OAAO,iBAAiB,IAAI,OAAO,GAAG,SAAS,IAAI,gBAAgB,GAAG,CAAC;CACzE,OACE,OAAO,iBAAiB,IAAI,OAAO,KAAK,IAAI,CAAC;AAGnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AChFA,eAAsB,sBAAsB,YAA8C;CACxF,OAAO,gCAAgC,YAAY,UAAU,CAAC;AAChE;;;;AAKA,eAAe,gCAAgC,QAA4C;CAEzF,MAAM,SAAS,wBAAwB,MAAM;CAK7C,IAAI;CACJ,IAAI;EACF,aAAa,MAAM,OAAO,UAAU;CACtC,SAAS,OAAO;EACd,IAAI;GACF,MAAM,OAAO,UAAU;EACzB,QAAQ,CAER;EACA,MAAM;CACR;CAGA,MAAM,eAAe,WAAW,eAAe,IAAI,aAAa,WAAW,YAAY,IAAI,KAAA;CAG3F,MAAM,SAAS,IAAI,uBAAuB,YAAY;CAGtD,IAAI,UAAU;CAGd,IAAI,aAAa;CAEjB,OAAO;EACL,qBAAqB;GACnB,OAAO,IAAI,QAAQ,WAAW,cAAc,WAAW,WAAW,WAAW,SAAS,WAAW,QAAQ;EAC3G;EAEA,UAAU,OAAO,QAAQ,SAAS,YAAY;GAC5C,IAAI,YACF,MAAM,IAAI,MAAM,gDAAgD;QAC3D,IAAI,SACT,MAAM,IAAI,MAAM,gEAAgE;QAEhF,UAAU;GAIZ,OAAO,iBAAiB,QAAQ,SAAS,OAAO;GAKhD,IAAI;GACJ,IAAI;IACF,MAAM,SAAS,OAAO,iBAAiB;IACvC,WAAW,MAAM,OAAO,SAAS,MAAM;GACzC,UAAU;IACR,UAAU;GACZ;GAMA,OAAO,wBAAwB,QAAQ;GAIvC,OAAO,gBAAgB,OAAO;GAE9B,OAAO;EACT;EAEA,iBAAiB;GACf,IAAI,YACF,OAAO,QAAQ,QAAQ;QAClB;IACL,aAAa;IACb,OAAO,wBAAQ,IAAI,MAAM,gDAAgD,CAAC;IAC1E,OAAO,OAAO,UAAU;GAC1B;EACF;CACF;AACF"}
|
|
@@ -1,5 +1,16 @@
|
|
|
1
|
-
import { ModelRunner } from
|
|
2
|
-
|
|
1
|
+
import { ModelRunner } from "@sdeverywhere/runtime";
|
|
2
|
+
//#region src/worker-rpc/spawn-worker.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Specifies the worker to be spawned; either a `path` to the worker JavaScript
|
|
5
|
+
* file, or the `source` containing the full JavaScript source of the worker.
|
|
6
|
+
*/
|
|
7
|
+
type WorkerSpec = {
|
|
8
|
+
path: string;
|
|
9
|
+
} | {
|
|
10
|
+
source: string;
|
|
11
|
+
};
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/runner.d.ts
|
|
3
14
|
/**
|
|
4
15
|
* Initialize a `ModelRunner` that runs the model asynchronously in a worker
|
|
5
16
|
* (a Web Worker when running in a browser environment, or a worker thread
|
|
@@ -29,13 +40,13 @@ import { ModelRunner } from '@sdeverywhere/runtime';
|
|
|
29
40
|
* }
|
|
30
41
|
* ```
|
|
31
42
|
*
|
|
43
|
+
* The returned promise rejects if the worker fails to initialize the model or
|
|
44
|
+
* exits unexpectedly.
|
|
45
|
+
*
|
|
32
46
|
* @param workerSpec Either a `path` to the worker JavaScript file, or the `source`
|
|
33
47
|
* containing the full JavaScript source of the worker.
|
|
34
48
|
*/
|
|
35
|
-
declare function spawnAsyncModelRunner(workerSpec:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}): Promise<ModelRunner>;
|
|
40
|
-
|
|
41
|
-
export { spawnAsyncModelRunner };
|
|
49
|
+
declare function spawnAsyncModelRunner(workerSpec: WorkerSpec): Promise<ModelRunner>;
|
|
50
|
+
//#endregion
|
|
51
|
+
export { WorkerSpec as n, spawnAsyncModelRunner as t };
|
|
52
|
+
//# sourceMappingURL=runner-DcPcKf0C.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner-DcPcKf0C.d.ts","names":[],"sources":["../src/worker-rpc/spawn-worker.d.ts","../src/runner.d.ts"],"mappings":";;;;;;KAMY;EACR;;EAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBC6BoB,sBAAsB,YAAY,aAAa,QAAQ"}
|
package/dist/runner.d.ts
CHANGED
|
@@ -1,41 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Initialize a `ModelRunner` that runs the model asynchronously in a worker
|
|
5
|
-
* (a Web Worker when running in a browser environment, or a worker thread
|
|
6
|
-
* when running in a Node.js environment).
|
|
7
|
-
*
|
|
8
|
-
* In your app project, define a JavaScript file, called `worker.js` for example,
|
|
9
|
-
* that initializes the generated model in the context of a worker thread:
|
|
10
|
-
*
|
|
11
|
-
* ```js
|
|
12
|
-
* import { exposeModelWorker } from '@sdeverywhere/runtime-async/worker'
|
|
13
|
-
* import loadGeneratedModel from './sde-prep/generated-model.js'
|
|
14
|
-
*
|
|
15
|
-
* exposeModelWorker(loadGeneratedModel)
|
|
16
|
-
* ```
|
|
17
|
-
*
|
|
18
|
-
* Then, in your web app, call the `spawnAsyncModelRunner` function, which
|
|
19
|
-
* will spawn the worker thread and initialize the `ModelRunner` that communicates
|
|
20
|
-
* with the worker:
|
|
21
|
-
*
|
|
22
|
-
* ```js
|
|
23
|
-
* import { spawnAsyncModelRunner } from '@sdeverywhere/runtime-async/runner'
|
|
24
|
-
*
|
|
25
|
-
* async function initApp() {
|
|
26
|
-
* // ...
|
|
27
|
-
* const runner = await spawnAsyncModelRunner({ path: './worker.js' })
|
|
28
|
-
* // ...
|
|
29
|
-
* }
|
|
30
|
-
* ```
|
|
31
|
-
*
|
|
32
|
-
* @param workerSpec Either a `path` to the worker JavaScript file, or the `source`
|
|
33
|
-
* containing the full JavaScript source of the worker.
|
|
34
|
-
*/
|
|
35
|
-
declare function spawnAsyncModelRunner(workerSpec: {
|
|
36
|
-
path: string;
|
|
37
|
-
} | {
|
|
38
|
-
source: string;
|
|
39
|
-
}): Promise<ModelRunner>;
|
|
40
|
-
|
|
41
|
-
export { spawnAsyncModelRunner };
|
|
1
|
+
import { n as WorkerSpec, t as spawnAsyncModelRunner } from "./runner-DcPcKf0C.js";
|
|
2
|
+
export { type WorkerSpec, spawnAsyncModelRunner };
|
package/dist/runner.js
CHANGED
|
@@ -1,54 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { BufferedRunModelParams, ModelListing, Outputs } from "@sdeverywhere/runtime";
|
|
4
|
-
async function spawnAsyncModelRunner(workerSpec) {
|
|
5
|
-
if (workerSpec["path"]) {
|
|
6
|
-
return spawnAsyncModelRunnerWithWorker(new Worker(workerSpec["path"]));
|
|
7
|
-
} else {
|
|
8
|
-
return spawnAsyncModelRunnerWithWorker(BlobWorker.fromText(workerSpec["source"]));
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
async function spawnAsyncModelRunnerWithWorker(worker) {
|
|
12
|
-
const modelWorker = await spawn(worker);
|
|
13
|
-
const initResult = await modelWorker.initModel();
|
|
14
|
-
const modelListing = initResult.modelListing ? new ModelListing(initResult.modelListing) : void 0;
|
|
15
|
-
const params = new BufferedRunModelParams(modelListing);
|
|
16
|
-
let running = false;
|
|
17
|
-
let terminated = false;
|
|
18
|
-
return {
|
|
19
|
-
createOutputs: () => {
|
|
20
|
-
return new Outputs(initResult.outputVarIds, initResult.startTime, initResult.endTime, initResult.saveFreq);
|
|
21
|
-
},
|
|
22
|
-
runModel: async (inputs, outputs, options) => {
|
|
23
|
-
if (terminated) {
|
|
24
|
-
throw new Error("Async model runner has already been terminated");
|
|
25
|
-
} else if (running) {
|
|
26
|
-
throw new Error("Async model runner only supports one `runModel` call at a time");
|
|
27
|
-
} else {
|
|
28
|
-
running = true;
|
|
29
|
-
}
|
|
30
|
-
params.updateFromParams(inputs, outputs, options);
|
|
31
|
-
let ioBuffer;
|
|
32
|
-
try {
|
|
33
|
-
ioBuffer = await modelWorker.runModel(Transfer(params.getEncodedBuffer()));
|
|
34
|
-
} finally {
|
|
35
|
-
running = false;
|
|
36
|
-
}
|
|
37
|
-
params.updateFromEncodedBuffer(ioBuffer);
|
|
38
|
-
params.finalizeOutputs(outputs);
|
|
39
|
-
return outputs;
|
|
40
|
-
},
|
|
41
|
-
terminate: () => {
|
|
42
|
-
if (terminated) {
|
|
43
|
-
return Promise.resolve();
|
|
44
|
-
} else {
|
|
45
|
-
terminated = true;
|
|
46
|
-
return Thread.terminate(modelWorker);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
export {
|
|
52
|
-
spawnAsyncModelRunner
|
|
53
|
-
};
|
|
54
|
-
//# sourceMappingURL=runner.js.map
|
|
1
|
+
import { t as spawnAsyncModelRunner } from "./runner-DWs69cGp.js";
|
|
2
|
+
export { spawnAsyncModelRunner };
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { i as serveModelWorker, n as nodeWorkerThreads, t as isNodeEnvironment } from "./node-runtime-CQ0b2ThB.js";
|
|
2
|
+
import { BufferedRunModelParams, createRunnableModel } from "@sdeverywhere/runtime";
|
|
3
|
+
//#region src/worker-rpc/parent-port.ts
|
|
4
|
+
/**
|
|
5
|
+
* Adapt the `parentPort` from `node:worker_threads` to the `WorkerPort` interface.
|
|
6
|
+
*
|
|
7
|
+
* @param parentPort The port that is connected to the parent (runner) thread.
|
|
8
|
+
* @returns The port for communicating with the runner.
|
|
9
|
+
* @hidden For internal use only.
|
|
10
|
+
*/
|
|
11
|
+
function portForNodeParentPort(parentPort) {
|
|
12
|
+
return {
|
|
13
|
+
postMessage: (message, transferables) => parentPort.postMessage(message, transferables ?? []),
|
|
14
|
+
onMessage: (handler) => parentPort.on("message", handler)
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Adapt the global scope of a Web Worker to the `WorkerPort` interface.
|
|
19
|
+
*
|
|
20
|
+
* @param scope The global scope of the Web Worker.
|
|
21
|
+
* @returns The port for communicating with the runner.
|
|
22
|
+
* @hidden For internal use only.
|
|
23
|
+
*/
|
|
24
|
+
function portForWorkerGlobalScope(scope) {
|
|
25
|
+
return {
|
|
26
|
+
postMessage: (message, transferables) => scope.postMessage(message, transferables ?? []),
|
|
27
|
+
onMessage: (handler) => scope.addEventListener("message", (event) => handler(event.data))
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Return the port that connects this worker to the runner running in the main thread.
|
|
32
|
+
*
|
|
33
|
+
* @returns The port for communicating with the runner.
|
|
34
|
+
* @throws An error if the current context is not a worker.
|
|
35
|
+
* @hidden For internal use only.
|
|
36
|
+
*/
|
|
37
|
+
function parentWorkerPort() {
|
|
38
|
+
if (isNodeEnvironment()) {
|
|
39
|
+
const { parentPort } = nodeWorkerThreads();
|
|
40
|
+
if (parentPort === void 0) throw new Error("The `exposeModelWorker` function must be called from a worker thread");
|
|
41
|
+
return portForNodeParentPort(parentPort);
|
|
42
|
+
} else {
|
|
43
|
+
const scope = globalThis;
|
|
44
|
+
if ("document" in globalThis || typeof scope.postMessage !== "function") throw new Error("The `exposeModelWorker` function must be called from a worker thread");
|
|
45
|
+
return portForWorkerGlobalScope(scope);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/worker.ts
|
|
50
|
+
/** @hidden */
|
|
51
|
+
let initGeneratedModel;
|
|
52
|
+
/** @hidden */
|
|
53
|
+
let runnableModel;
|
|
54
|
+
/**
|
|
55
|
+
* Maintain a `BufferedRunModelParams` instance that wraps the transferable buffer
|
|
56
|
+
* containing the I/O parameters.
|
|
57
|
+
* @hidden
|
|
58
|
+
*/
|
|
59
|
+
const params = new BufferedRunModelParams();
|
|
60
|
+
/** @hidden */
|
|
61
|
+
const modelWorker = {
|
|
62
|
+
async initModel() {
|
|
63
|
+
if (runnableModel) throw new Error("RunnableModel was already initialized");
|
|
64
|
+
const generatedModel = await initGeneratedModel();
|
|
65
|
+
runnableModel = createRunnableModel(generatedModel);
|
|
66
|
+
return {
|
|
67
|
+
outputVarIds: runnableModel.outputVarIds,
|
|
68
|
+
modelListing: runnableModel.modelListing,
|
|
69
|
+
startTime: runnableModel.startTime,
|
|
70
|
+
endTime: runnableModel.endTime,
|
|
71
|
+
saveFreq: runnableModel.saveFreq
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
runModel(ioBuffer) {
|
|
75
|
+
if (!runnableModel) throw new Error("RunnableModel must be initialized before running the model in worker");
|
|
76
|
+
params.updateFromEncodedBuffer(ioBuffer);
|
|
77
|
+
runnableModel.runModel(params);
|
|
78
|
+
return ioBuffer;
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Expose an object in the current worker thread that communicates with the
|
|
83
|
+
* `ModelRunner` instance running in the main thread. The exposed worker
|
|
84
|
+
* object will take care of running the model on the worker thread and
|
|
85
|
+
* sending the outputs back to the main thread.
|
|
86
|
+
*
|
|
87
|
+
* @param init The function that initializes the generated model instance that
|
|
88
|
+
* is used in the worker thread.
|
|
89
|
+
*/
|
|
90
|
+
function exposeModelWorker(init) {
|
|
91
|
+
initGeneratedModel = init;
|
|
92
|
+
serveModelWorker(parentWorkerPort(), modelWorker);
|
|
93
|
+
}
|
|
94
|
+
//#endregion
|
|
95
|
+
export { exposeModelWorker as t };
|
|
96
|
+
|
|
97
|
+
//# sourceMappingURL=worker-CVxcTQod.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-CVxcTQod.js","names":[],"sources":["../src/worker-rpc/parent-port.ts","../src/worker.ts"],"sourcesContent":["// Copyright (c) 2026 Climate Interactive / New Venture Fund\n\nimport type { NodeMessagePort } from './node-runtime'\nimport { isNodeEnvironment, nodeWorkerThreads } from './node-runtime'\nimport type { WorkerPort } from './worker-port'\n\n/**\n * The subset of the Web Worker global scope API that is used by this package.\n * @hidden\n */\ninterface WorkerGlobalScope {\n postMessage(message: unknown, transferables?: Transferable[]): void\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n addEventListener(type: string, listener: (event: any) => void): void\n}\n\n/**\n * Adapt the `parentPort` from `node:worker_threads` to the `WorkerPort` interface.\n *\n * @param parentPort The port that is connected to the parent (runner) thread.\n * @returns The port for communicating with the runner.\n * @hidden For internal use only.\n */\nexport function portForNodeParentPort(parentPort: NodeMessagePort): WorkerPort {\n return {\n postMessage: (message, transferables) => parentPort.postMessage(message, transferables ?? []),\n\n onMessage: handler => parentPort.on('message', handler)\n }\n}\n\n/**\n * Adapt the global scope of a Web Worker to the `WorkerPort` interface.\n *\n * @param scope The global scope of the Web Worker.\n * @returns The port for communicating with the runner.\n * @hidden For internal use only.\n */\nexport function portForWorkerGlobalScope(scope: WorkerGlobalScope): WorkerPort {\n return {\n postMessage: (message, transferables) => scope.postMessage(message, transferables ?? []),\n\n onMessage: handler => scope.addEventListener('message', event => handler(event.data))\n }\n}\n\n/**\n * Return the port that connects this worker to the runner running in the main thread.\n *\n * @returns The port for communicating with the runner.\n * @throws An error if the current context is not a worker.\n * @hidden For internal use only.\n */\nexport function parentWorkerPort(): WorkerPort {\n if (isNodeEnvironment()) {\n const { parentPort } = nodeWorkerThreads()\n if (parentPort === undefined) {\n throw new Error('The `exposeModelWorker` function must be called from a worker thread')\n }\n return portForNodeParentPort(parentPort)\n } else {\n const scope = globalThis as unknown as WorkerGlobalScope\n if ('document' in globalThis || typeof scope.postMessage !== 'function') {\n throw new Error('The `exposeModelWorker` function must be called from a worker thread')\n }\n return portForWorkerGlobalScope(scope)\n }\n}\n","// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport type { GeneratedModel, RunnableModel } from '@sdeverywhere/runtime'\nimport { BufferedRunModelParams, createRunnableModel } from '@sdeverywhere/runtime'\n\nimport type { InitResult, ModelWorkerMethods } from './worker-rpc/model-worker-rpc'\nimport { serveModelWorker } from './worker-rpc/model-worker-rpc'\nimport { parentWorkerPort } from './worker-rpc/parent-port'\n\n/** @hidden */\nlet initGeneratedModel: () => Promise<GeneratedModel>\n\n/** @hidden */\nlet runnableModel: RunnableModel\n\n/**\n * Maintain a `BufferedRunModelParams` instance that wraps the transferable buffer\n * containing the I/O parameters.\n * @hidden\n */\nconst params = new BufferedRunModelParams()\n\n/** @hidden */\nconst modelWorker: ModelWorkerMethods = {\n async initModel(): Promise<InitResult> {\n if (runnableModel) {\n throw new Error('RunnableModel was already initialized')\n }\n\n // Initialize the runnable model\n const generatedModel = await initGeneratedModel()\n runnableModel = createRunnableModel(generatedModel)\n\n // Transfer the model metadata to the runner\n return {\n outputVarIds: runnableModel.outputVarIds,\n modelListing: runnableModel.modelListing,\n startTime: runnableModel.startTime,\n endTime: runnableModel.endTime,\n saveFreq: runnableModel.saveFreq\n }\n },\n\n runModel(ioBuffer: ArrayBuffer): ArrayBuffer {\n if (!runnableModel) {\n throw new Error('RunnableModel must be initialized before running the model in worker')\n }\n\n // Update the `BufferedRunModelParams` to use the values in the buffer that was transferred\n // from the runner to the worker\n params.updateFromEncodedBuffer(ioBuffer)\n\n // Run the model synchronously on the worker thread using those I/O parameters\n runnableModel.runModel(params)\n\n // Transfer the buffer back to the runner\n return ioBuffer\n }\n}\n\n/**\n * Expose an object in the current worker thread that communicates with the\n * `ModelRunner` instance running in the main thread. The exposed worker\n * object will take care of running the model on the worker thread and\n * sending the outputs back to the main thread.\n *\n * @param init The function that initializes the generated model instance that\n * is used in the worker thread.\n */\nexport function exposeModelWorker(init: () => Promise<GeneratedModel>): void {\n // Save the initializer, which will be used when the runner calls `initModel`\n // on the worker\n initGeneratedModel = init\n\n // Handle the requests that arrive from the runner in the main thread\n serveModelWorker(parentWorkerPort(), modelWorker)\n}\n"],"mappings":";;;;;;;;;;AAuBA,SAAgB,sBAAsB,YAAyC;CAC7E,OAAO;EACL,cAAc,SAAS,kBAAkB,WAAW,YAAY,SAAS,iBAAiB,CAAC,CAAC;EAE5F,YAAW,YAAW,WAAW,GAAG,WAAW,OAAO;CACxD;AACF;;;;;;;;AASA,SAAgB,yBAAyB,OAAsC;CAC7E,OAAO;EACL,cAAc,SAAS,kBAAkB,MAAM,YAAY,SAAS,iBAAiB,CAAC,CAAC;EAEvF,YAAW,YAAW,MAAM,iBAAiB,YAAW,UAAS,QAAQ,MAAM,IAAI,CAAC;CACtF;AACF;;;;;;;;AASA,SAAgB,mBAA+B;CAC7C,IAAI,kBAAkB,GAAG;EACvB,MAAM,EAAE,eAAe,kBAAkB;EACzC,IAAI,eAAe,KAAA,GACjB,MAAM,IAAI,MAAM,sEAAsE;EAExF,OAAO,sBAAsB,UAAU;CACzC,OAAO;EACL,MAAM,QAAQ;EACd,IAAI,cAAc,cAAc,OAAO,MAAM,gBAAgB,YAC3D,MAAM,IAAI,MAAM,sEAAsE;EAExF,OAAO,yBAAyB,KAAK;CACvC;AACF;;;;ACzDA,IAAI;;AAGJ,IAAI;;;;;;AAOJ,MAAM,SAAS,IAAI,uBAAuB;;AAG1C,MAAM,cAAkC;CACtC,MAAM,YAAiC;EACrC,IAAI,eACF,MAAM,IAAI,MAAM,uCAAuC;EAIzD,MAAM,iBAAiB,MAAM,mBAAmB;EAChD,gBAAgB,oBAAoB,cAAc;EAGlD,OAAO;GACL,cAAc,cAAc;GAC5B,cAAc,cAAc;GAC5B,WAAW,cAAc;GACzB,SAAS,cAAc;GACvB,UAAU,cAAc;EAC1B;CACF;CAEA,SAAS,UAAoC;EAC3C,IAAI,CAAC,eACH,MAAM,IAAI,MAAM,sEAAsE;EAKxF,OAAO,wBAAwB,QAAQ;EAGvC,cAAc,SAAS,MAAM;EAG7B,OAAO;CACT;AACF;;;;;;;;;;AAWA,SAAgB,kBAAkB,MAA2C;CAG3E,qBAAqB;CAGrB,iBAAiB,iBAAiB,GAAG,WAAW;AAClD"}
|
package/dist/worker.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { GeneratedModel } from
|
|
2
|
-
|
|
1
|
+
import { GeneratedModel } from "@sdeverywhere/runtime";
|
|
2
|
+
//#region src/worker.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* Expose an object in the current worker thread that communicates with the
|
|
5
5
|
* `ModelRunner` instance running in the main thread. The exposed worker
|
|
@@ -9,6 +9,6 @@ import { GeneratedModel } from '@sdeverywhere/runtime';
|
|
|
9
9
|
* @param init The function that initializes the generated model instance that
|
|
10
10
|
* is used in the worker thread.
|
|
11
11
|
*/
|
|
12
|
-
declare function exposeModelWorker(init: () => Promise<GeneratedModel>): void;
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
export declare function exposeModelWorker(init: () => Promise<GeneratedModel>): void;
|
|
13
|
+
//#endregion
|
|
14
|
+
//# sourceMappingURL=worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.d.ts","names":[],"sources":["../src/worker.d.ts"],"mappings":";;;;;;;;;;;wBAUwB,kBAAkB,YAAY,QAAQ"}
|
package/dist/worker.js
CHANGED
|
@@ -1,39 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { BufferedRunModelParams, createRunnableModel } from "@sdeverywhere/runtime";
|
|
4
|
-
var initGeneratedModel;
|
|
5
|
-
var runnableModel;
|
|
6
|
-
var params = new BufferedRunModelParams();
|
|
7
|
-
var modelWorker = {
|
|
8
|
-
async initModel() {
|
|
9
|
-
if (runnableModel) {
|
|
10
|
-
throw new Error("RunnableModel was already initialized");
|
|
11
|
-
}
|
|
12
|
-
const generatedModel = await initGeneratedModel();
|
|
13
|
-
runnableModel = createRunnableModel(generatedModel);
|
|
14
|
-
return {
|
|
15
|
-
outputVarIds: runnableModel.outputVarIds,
|
|
16
|
-
modelListing: runnableModel.modelListing,
|
|
17
|
-
startTime: runnableModel.startTime,
|
|
18
|
-
endTime: runnableModel.endTime,
|
|
19
|
-
saveFreq: runnableModel.saveFreq,
|
|
20
|
-
outputRowLength: runnableModel.numSavePoints
|
|
21
|
-
};
|
|
22
|
-
},
|
|
23
|
-
runModel(ioBuffer) {
|
|
24
|
-
if (!runnableModel) {
|
|
25
|
-
throw new Error("RunnableModel must be initialized before running the model in worker");
|
|
26
|
-
}
|
|
27
|
-
params.updateFromEncodedBuffer(ioBuffer);
|
|
28
|
-
runnableModel.runModel(params);
|
|
29
|
-
return Transfer(ioBuffer);
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
function exposeModelWorker(init) {
|
|
33
|
-
initGeneratedModel = init;
|
|
34
|
-
expose(modelWorker);
|
|
35
|
-
}
|
|
36
|
-
export {
|
|
37
|
-
exposeModelWorker
|
|
38
|
-
};
|
|
39
|
-
//# sourceMappingURL=worker.js.map
|
|
1
|
+
import { t as exposeModelWorker } from "./worker-CVxcTQod.js";
|
|
2
|
+
export { exposeModelWorker };
|
package/package.json
CHANGED
|
@@ -1,33 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sdeverywhere/runtime-async",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.11",
|
|
4
4
|
"files": [
|
|
5
5
|
"dist/**"
|
|
6
6
|
],
|
|
7
7
|
"type": "module",
|
|
8
|
-
"main": "./dist/index.cjs",
|
|
9
8
|
"module": "./dist/index.js",
|
|
10
9
|
"types": "./dist/index.d.ts",
|
|
11
10
|
"exports": {
|
|
12
11
|
".": {
|
|
13
12
|
"types": "./dist/index.d.ts",
|
|
14
|
-
"import": "./dist/index.js"
|
|
15
|
-
"require": "./dist/index.cjs"
|
|
13
|
+
"import": "./dist/index.js"
|
|
16
14
|
},
|
|
17
15
|
"./runner": {
|
|
18
16
|
"types": "./dist/runner.d.ts",
|
|
19
|
-
"import": "./dist/runner.js"
|
|
20
|
-
"require": "./dist/runner.cjs"
|
|
17
|
+
"import": "./dist/runner.js"
|
|
21
18
|
},
|
|
22
19
|
"./worker": {
|
|
23
20
|
"types": "./dist/worker.d.ts",
|
|
24
|
-
"import": "./dist/worker.js"
|
|
25
|
-
"require": "./dist/worker.cjs"
|
|
21
|
+
"import": "./dist/worker.js"
|
|
26
22
|
}
|
|
27
23
|
},
|
|
28
24
|
"dependencies": {
|
|
29
|
-
"@sdeverywhere/runtime": "^0.2.
|
|
30
|
-
|
|
25
|
+
"@sdeverywhere/runtime": "^0.2.11"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^20.14.8"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=22.3"
|
|
31
32
|
},
|
|
32
33
|
"author": "Climate Interactive",
|
|
33
34
|
"license": "MIT",
|
|
@@ -50,7 +51,7 @@
|
|
|
50
51
|
"test:watch": "vitest",
|
|
51
52
|
"test:ci": "vitest run",
|
|
52
53
|
"type-check": "tsc --noEmit -p tsconfig-test.json",
|
|
53
|
-
"build": "
|
|
54
|
+
"build": "tsdown",
|
|
54
55
|
"docs": "../../scripts/gen-docs.js",
|
|
55
56
|
"ci:build": "run-s clean lint prettier:check type-check build test:ci docs"
|
|
56
57
|
}
|
package/dist/index.cjs
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
-
var __export = (target, all) => {
|
|
6
|
-
for (var name in all)
|
|
7
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
-
};
|
|
9
|
-
var __copyProps = (to, from, except, desc) => {
|
|
10
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
-
for (let key of __getOwnPropNames(from))
|
|
12
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
-
}
|
|
15
|
-
return to;
|
|
16
|
-
};
|
|
17
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
-
|
|
19
|
-
// src/index.ts
|
|
20
|
-
var index_exports = {};
|
|
21
|
-
__export(index_exports, {
|
|
22
|
-
exposeModelWorker: () => exposeModelWorker,
|
|
23
|
-
spawnAsyncModelRunner: () => spawnAsyncModelRunner
|
|
24
|
-
});
|
|
25
|
-
module.exports = __toCommonJS(index_exports);
|
|
26
|
-
|
|
27
|
-
// src/runner.ts
|
|
28
|
-
var import_threads = require("threads");
|
|
29
|
-
var import_runtime = require("@sdeverywhere/runtime");
|
|
30
|
-
async function spawnAsyncModelRunner(workerSpec) {
|
|
31
|
-
if (workerSpec["path"]) {
|
|
32
|
-
return spawnAsyncModelRunnerWithWorker(new import_threads.Worker(workerSpec["path"]));
|
|
33
|
-
} else {
|
|
34
|
-
return spawnAsyncModelRunnerWithWorker(import_threads.BlobWorker.fromText(workerSpec["source"]));
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
async function spawnAsyncModelRunnerWithWorker(worker) {
|
|
38
|
-
const modelWorker2 = await (0, import_threads.spawn)(worker);
|
|
39
|
-
const initResult = await modelWorker2.initModel();
|
|
40
|
-
const modelListing = initResult.modelListing ? new import_runtime.ModelListing(initResult.modelListing) : void 0;
|
|
41
|
-
const params2 = new import_runtime.BufferedRunModelParams(modelListing);
|
|
42
|
-
let running = false;
|
|
43
|
-
let terminated = false;
|
|
44
|
-
return {
|
|
45
|
-
createOutputs: () => {
|
|
46
|
-
return new import_runtime.Outputs(initResult.outputVarIds, initResult.startTime, initResult.endTime, initResult.saveFreq);
|
|
47
|
-
},
|
|
48
|
-
runModel: async (inputs, outputs, options) => {
|
|
49
|
-
if (terminated) {
|
|
50
|
-
throw new Error("Async model runner has already been terminated");
|
|
51
|
-
} else if (running) {
|
|
52
|
-
throw new Error("Async model runner only supports one `runModel` call at a time");
|
|
53
|
-
} else {
|
|
54
|
-
running = true;
|
|
55
|
-
}
|
|
56
|
-
params2.updateFromParams(inputs, outputs, options);
|
|
57
|
-
let ioBuffer;
|
|
58
|
-
try {
|
|
59
|
-
ioBuffer = await modelWorker2.runModel((0, import_threads.Transfer)(params2.getEncodedBuffer()));
|
|
60
|
-
} finally {
|
|
61
|
-
running = false;
|
|
62
|
-
}
|
|
63
|
-
params2.updateFromEncodedBuffer(ioBuffer);
|
|
64
|
-
params2.finalizeOutputs(outputs);
|
|
65
|
-
return outputs;
|
|
66
|
-
},
|
|
67
|
-
terminate: () => {
|
|
68
|
-
if (terminated) {
|
|
69
|
-
return Promise.resolve();
|
|
70
|
-
} else {
|
|
71
|
-
terminated = true;
|
|
72
|
-
return import_threads.Thread.terminate(modelWorker2);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// src/worker.ts
|
|
79
|
-
var import_worker = require("threads/worker");
|
|
80
|
-
var import_runtime2 = require("@sdeverywhere/runtime");
|
|
81
|
-
var initGeneratedModel;
|
|
82
|
-
var runnableModel;
|
|
83
|
-
var params = new import_runtime2.BufferedRunModelParams();
|
|
84
|
-
var modelWorker = {
|
|
85
|
-
async initModel() {
|
|
86
|
-
if (runnableModel) {
|
|
87
|
-
throw new Error("RunnableModel was already initialized");
|
|
88
|
-
}
|
|
89
|
-
const generatedModel = await initGeneratedModel();
|
|
90
|
-
runnableModel = (0, import_runtime2.createRunnableModel)(generatedModel);
|
|
91
|
-
return {
|
|
92
|
-
outputVarIds: runnableModel.outputVarIds,
|
|
93
|
-
modelListing: runnableModel.modelListing,
|
|
94
|
-
startTime: runnableModel.startTime,
|
|
95
|
-
endTime: runnableModel.endTime,
|
|
96
|
-
saveFreq: runnableModel.saveFreq,
|
|
97
|
-
outputRowLength: runnableModel.numSavePoints
|
|
98
|
-
};
|
|
99
|
-
},
|
|
100
|
-
runModel(ioBuffer) {
|
|
101
|
-
if (!runnableModel) {
|
|
102
|
-
throw new Error("RunnableModel must be initialized before running the model in worker");
|
|
103
|
-
}
|
|
104
|
-
params.updateFromEncodedBuffer(ioBuffer);
|
|
105
|
-
runnableModel.runModel(params);
|
|
106
|
-
return (0, import_worker.Transfer)(ioBuffer);
|
|
107
|
-
}
|
|
108
|
-
};
|
|
109
|
-
function exposeModelWorker(init) {
|
|
110
|
-
initGeneratedModel = init;
|
|
111
|
-
(0, import_worker.expose)(modelWorker);
|
|
112
|
-
}
|
|
113
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
114
|
-
0 && (module.exports = {
|
|
115
|
-
exposeModelWorker,
|
|
116
|
-
spawnAsyncModelRunner
|
|
117
|
-
});
|
|
118
|
-
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/runner.ts","../src/worker.ts"],"sourcesContent":["// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nexport { spawnAsyncModelRunner } from './runner'\n\nexport { exposeModelWorker } from './worker'\n","// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport { BlobWorker, spawn, Thread, Transfer, Worker } from 'threads'\n\nimport type { ModelRunner } from '@sdeverywhere/runtime'\nimport { BufferedRunModelParams, ModelListing, Outputs } from '@sdeverywhere/runtime'\n\n/**\n * Initialize a `ModelRunner` that runs the model asynchronously in a worker\n * (a Web Worker when running in a browser environment, or a worker thread\n * when running in a Node.js environment).\n *\n * In your app project, define a JavaScript file, called `worker.js` for example,\n * that initializes the generated model in the context of a worker thread:\n *\n * ```js\n * import { exposeModelWorker } from '@sdeverywhere/runtime-async/worker'\n * import loadGeneratedModel from './sde-prep/generated-model.js'\n *\n * exposeModelWorker(loadGeneratedModel)\n * ```\n *\n * Then, in your web app, call the `spawnAsyncModelRunner` function, which\n * will spawn the worker thread and initialize the `ModelRunner` that communicates\n * with the worker:\n *\n * ```js\n * import { spawnAsyncModelRunner } from '@sdeverywhere/runtime-async/runner'\n *\n * async function initApp() {\n * // ...\n * const runner = await spawnAsyncModelRunner({ path: './worker.js' })\n * // ...\n * }\n * ```\n *\n * @param workerSpec Either a `path` to the worker JavaScript file, or the `source`\n * containing the full JavaScript source of the worker.\n */\nexport async function spawnAsyncModelRunner(workerSpec: { path: string } | { source: string }): Promise<ModelRunner> {\n if (workerSpec['path']) {\n return spawnAsyncModelRunnerWithWorker(new Worker(workerSpec['path']))\n } else {\n return spawnAsyncModelRunnerWithWorker(BlobWorker.fromText(workerSpec['source']))\n }\n}\n\n/**\n * @hidden For internal use only\n */\nasync function spawnAsyncModelRunnerWithWorker(worker: Worker): Promise<ModelRunner> {\n // Spawn the given Worker that contains the `ModelWorker`\n const modelWorker = await spawn(worker)\n\n // Wait for the worker to initialize the wasm model (in the worker thread)\n const initResult = await modelWorker.initModel()\n\n // Create a `ModelListing` instance if the listing was defined in the generated model\n const modelListing = initResult.modelListing ? new ModelListing(initResult.modelListing) : undefined\n\n // Maintain a `BufferedRunModelParams` instance that holds the I/O parameters\n const params = new BufferedRunModelParams(modelListing)\n\n // Use a flag to ensure that only one request is made at a time\n let running = false\n\n // Disallow `runModel` after the runner has been terminated\n let terminated = false\n\n return {\n createOutputs: () => {\n return new Outputs(initResult.outputVarIds, initResult.startTime, initResult.endTime, initResult.saveFreq)\n },\n\n runModel: async (inputs, outputs, options) => {\n if (terminated) {\n throw new Error('Async model runner has already been terminated')\n } else if (running) {\n throw new Error('Async model runner only supports one `runModel` call at a time')\n } else {\n running = true\n }\n\n // Update the I/O parameters\n params.updateFromParams(inputs, outputs, options)\n\n // Run the model in the worker. We pass the underlying `ArrayBuffer`\n // instance back to the worker wrapped in a `Transfer` to make it\n // no-copy transferable, and then the worker will return it back\n // to us.\n let ioBuffer: ArrayBuffer\n try {\n ioBuffer = await modelWorker.runModel(Transfer(params.getEncodedBuffer()))\n } finally {\n running = false\n }\n\n // Once the buffer is transferred to the worker, the buffer in the\n // `BufferedRunModelParams` becomes \"detached\" and is no longer usable.\n // After the buffer is transferred back from the worker, we need to\n // restore the state of the object to use the new buffer.\n params.updateFromEncodedBuffer(ioBuffer)\n\n // Copy the output values and elapsed time from the buffer to the\n // `Outputs` instance\n params.finalizeOutputs(outputs)\n\n return outputs\n },\n\n terminate: () => {\n if (terminated) {\n return Promise.resolve()\n } else {\n terminated = true\n return Thread.terminate(modelWorker)\n }\n }\n }\n}\n","// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport type { TransferDescriptor } from 'threads'\nimport { expose, Transfer } from 'threads/worker'\n\nimport type { GeneratedModel, RunnableModel } from '@sdeverywhere/runtime'\nimport { BufferedRunModelParams, createRunnableModel } from '@sdeverywhere/runtime'\n\n/** @hidden */\nlet initGeneratedModel: () => Promise<GeneratedModel>\n\n/** @hidden */\nlet runnableModel: RunnableModel\n\n/**\n * Maintain a `BufferedRunModelParams` instance that wraps the transferable buffer\n * containing the I/O parameters.\n * @hidden\n */\nconst params = new BufferedRunModelParams()\n\ninterface InitResult {\n outputVarIds: string[]\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n modelListing?: /*ModelListingSpecs*/ any\n startTime: number\n endTime: number\n saveFreq: number\n outputRowLength: number\n}\n\n/** @hidden */\nconst modelWorker = {\n async initModel(): Promise<InitResult> {\n if (runnableModel) {\n throw new Error('RunnableModel was already initialized')\n }\n\n // Initialize the runnable model\n const generatedModel = await initGeneratedModel()\n runnableModel = createRunnableModel(generatedModel)\n\n // Transfer the model metadata to the runner\n return {\n outputVarIds: runnableModel.outputVarIds,\n modelListing: runnableModel.modelListing,\n startTime: runnableModel.startTime,\n endTime: runnableModel.endTime,\n saveFreq: runnableModel.saveFreq,\n outputRowLength: runnableModel.numSavePoints\n }\n },\n\n runModel(ioBuffer: ArrayBuffer): TransferDescriptor<ArrayBuffer> {\n if (!runnableModel) {\n throw new Error('RunnableModel must be initialized before running the model in worker')\n }\n\n // Update the `BufferedRunModelParams` to use the values in the buffer that was transferred\n // from the runner to the worker\n params.updateFromEncodedBuffer(ioBuffer)\n\n // Run the model synchronously on the worker thread using those I/O parameters\n runnableModel.runModel(params)\n\n // Transfer the buffer back to the runner\n return Transfer(ioBuffer)\n }\n}\n\n/**\n * Expose an object in the current worker thread that communicates with the\n * `ModelRunner` instance running in the main thread. The exposed worker\n * object will take care of running the model on the worker thread and\n * sending the outputs back to the main thread.\n *\n * @param init The function that initializes the generated model instance that\n * is used in the worker thread.\n */\nexport function exposeModelWorker(init: () => Promise<GeneratedModel>): void {\n // Save the initializer, which will be used when the runner calls `initModel`\n // on the worker\n initGeneratedModel = init\n\n // Expose the worker implementation to `threads.js`\n expose(modelWorker)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACEA,qBAA4D;AAG5D,qBAA8D;AAkC9D,eAAsB,sBAAsB,YAAyE;AACnH,MAAI,WAAW,MAAM,GAAG;AACtB,WAAO,gCAAgC,IAAI,sBAAO,WAAW,MAAM,CAAC,CAAC;AAAA,EACvE,OAAO;AACL,WAAO,gCAAgC,0BAAW,SAAS,WAAW,QAAQ,CAAC,CAAC;AAAA,EAClF;AACF;AAKA,eAAe,gCAAgC,QAAsC;AAEnF,QAAMA,eAAc,UAAM,sBAAM,MAAM;AAGtC,QAAM,aAAa,MAAMA,aAAY,UAAU;AAG/C,QAAM,eAAe,WAAW,eAAe,IAAI,4BAAa,WAAW,YAAY,IAAI;AAG3F,QAAMC,UAAS,IAAI,sCAAuB,YAAY;AAGtD,MAAI,UAAU;AAGd,MAAI,aAAa;AAEjB,SAAO;AAAA,IACL,eAAe,MAAM;AACnB,aAAO,IAAI,uBAAQ,WAAW,cAAc,WAAW,WAAW,WAAW,SAAS,WAAW,QAAQ;AAAA,IAC3G;AAAA,IAEA,UAAU,OAAO,QAAQ,SAAS,YAAY;AAC5C,UAAI,YAAY;AACd,cAAM,IAAI,MAAM,gDAAgD;AAAA,MAClE,WAAW,SAAS;AAClB,cAAM,IAAI,MAAM,gEAAgE;AAAA,MAClF,OAAO;AACL,kBAAU;AAAA,MACZ;AAGA,MAAAA,QAAO,iBAAiB,QAAQ,SAAS,OAAO;AAMhD,UAAI;AACJ,UAAI;AACF,mBAAW,MAAMD,aAAY,aAAS,yBAASC,QAAO,iBAAiB,CAAC,CAAC;AAAA,MAC3E,UAAE;AACA,kBAAU;AAAA,MACZ;AAMA,MAAAA,QAAO,wBAAwB,QAAQ;AAIvC,MAAAA,QAAO,gBAAgB,OAAO;AAE9B,aAAO;AAAA,IACT;AAAA,IAEA,WAAW,MAAM;AACf,UAAI,YAAY;AACd,eAAO,QAAQ,QAAQ;AAAA,MACzB,OAAO;AACL,qBAAa;AACb,eAAO,sBAAO,UAAUD,YAAW;AAAA,MACrC;AAAA,IACF;AAAA,EACF;AACF;;;ACpHA,oBAAiC;AAGjC,IAAAE,kBAA4D;AAG5D,IAAI;AAGJ,IAAI;AAOJ,IAAM,SAAS,IAAI,uCAAuB;AAa1C,IAAM,cAAc;AAAA,EAClB,MAAM,YAAiC;AACrC,QAAI,eAAe;AACjB,YAAM,IAAI,MAAM,uCAAuC;AAAA,IACzD;AAGA,UAAM,iBAAiB,MAAM,mBAAmB;AAChD,wBAAgB,qCAAoB,cAAc;AAGlD,WAAO;AAAA,MACL,cAAc,cAAc;AAAA,MAC5B,cAAc,cAAc;AAAA,MAC5B,WAAW,cAAc;AAAA,MACzB,SAAS,cAAc;AAAA,MACvB,UAAU,cAAc;AAAA,MACxB,iBAAiB,cAAc;AAAA,IACjC;AAAA,EACF;AAAA,EAEA,SAAS,UAAwD;AAC/D,QAAI,CAAC,eAAe;AAClB,YAAM,IAAI,MAAM,sEAAsE;AAAA,IACxF;AAIA,WAAO,wBAAwB,QAAQ;AAGvC,kBAAc,SAAS,MAAM;AAG7B,eAAO,wBAAS,QAAQ;AAAA,EAC1B;AACF;AAWO,SAAS,kBAAkB,MAA2C;AAG3E,uBAAqB;AAGrB,4BAAO,WAAW;AACpB;","names":["modelWorker","params","import_runtime"]}
|
package/dist/index.d.cts
DELETED
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runner.ts","../src/worker.ts"],"sourcesContent":["// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport { BlobWorker, spawn, Thread, Transfer, Worker } from 'threads'\n\nimport type { ModelRunner } from '@sdeverywhere/runtime'\nimport { BufferedRunModelParams, ModelListing, Outputs } from '@sdeverywhere/runtime'\n\n/**\n * Initialize a `ModelRunner` that runs the model asynchronously in a worker\n * (a Web Worker when running in a browser environment, or a worker thread\n * when running in a Node.js environment).\n *\n * In your app project, define a JavaScript file, called `worker.js` for example,\n * that initializes the generated model in the context of a worker thread:\n *\n * ```js\n * import { exposeModelWorker } from '@sdeverywhere/runtime-async/worker'\n * import loadGeneratedModel from './sde-prep/generated-model.js'\n *\n * exposeModelWorker(loadGeneratedModel)\n * ```\n *\n * Then, in your web app, call the `spawnAsyncModelRunner` function, which\n * will spawn the worker thread and initialize the `ModelRunner` that communicates\n * with the worker:\n *\n * ```js\n * import { spawnAsyncModelRunner } from '@sdeverywhere/runtime-async/runner'\n *\n * async function initApp() {\n * // ...\n * const runner = await spawnAsyncModelRunner({ path: './worker.js' })\n * // ...\n * }\n * ```\n *\n * @param workerSpec Either a `path` to the worker JavaScript file, or the `source`\n * containing the full JavaScript source of the worker.\n */\nexport async function spawnAsyncModelRunner(workerSpec: { path: string } | { source: string }): Promise<ModelRunner> {\n if (workerSpec['path']) {\n return spawnAsyncModelRunnerWithWorker(new Worker(workerSpec['path']))\n } else {\n return spawnAsyncModelRunnerWithWorker(BlobWorker.fromText(workerSpec['source']))\n }\n}\n\n/**\n * @hidden For internal use only\n */\nasync function spawnAsyncModelRunnerWithWorker(worker: Worker): Promise<ModelRunner> {\n // Spawn the given Worker that contains the `ModelWorker`\n const modelWorker = await spawn(worker)\n\n // Wait for the worker to initialize the wasm model (in the worker thread)\n const initResult = await modelWorker.initModel()\n\n // Create a `ModelListing` instance if the listing was defined in the generated model\n const modelListing = initResult.modelListing ? new ModelListing(initResult.modelListing) : undefined\n\n // Maintain a `BufferedRunModelParams` instance that holds the I/O parameters\n const params = new BufferedRunModelParams(modelListing)\n\n // Use a flag to ensure that only one request is made at a time\n let running = false\n\n // Disallow `runModel` after the runner has been terminated\n let terminated = false\n\n return {\n createOutputs: () => {\n return new Outputs(initResult.outputVarIds, initResult.startTime, initResult.endTime, initResult.saveFreq)\n },\n\n runModel: async (inputs, outputs, options) => {\n if (terminated) {\n throw new Error('Async model runner has already been terminated')\n } else if (running) {\n throw new Error('Async model runner only supports one `runModel` call at a time')\n } else {\n running = true\n }\n\n // Update the I/O parameters\n params.updateFromParams(inputs, outputs, options)\n\n // Run the model in the worker. We pass the underlying `ArrayBuffer`\n // instance back to the worker wrapped in a `Transfer` to make it\n // no-copy transferable, and then the worker will return it back\n // to us.\n let ioBuffer: ArrayBuffer\n try {\n ioBuffer = await modelWorker.runModel(Transfer(params.getEncodedBuffer()))\n } finally {\n running = false\n }\n\n // Once the buffer is transferred to the worker, the buffer in the\n // `BufferedRunModelParams` becomes \"detached\" and is no longer usable.\n // After the buffer is transferred back from the worker, we need to\n // restore the state of the object to use the new buffer.\n params.updateFromEncodedBuffer(ioBuffer)\n\n // Copy the output values and elapsed time from the buffer to the\n // `Outputs` instance\n params.finalizeOutputs(outputs)\n\n return outputs\n },\n\n terminate: () => {\n if (terminated) {\n return Promise.resolve()\n } else {\n terminated = true\n return Thread.terminate(modelWorker)\n }\n }\n }\n}\n","// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport type { TransferDescriptor } from 'threads'\nimport { expose, Transfer } from 'threads/worker'\n\nimport type { GeneratedModel, RunnableModel } from '@sdeverywhere/runtime'\nimport { BufferedRunModelParams, createRunnableModel } from '@sdeverywhere/runtime'\n\n/** @hidden */\nlet initGeneratedModel: () => Promise<GeneratedModel>\n\n/** @hidden */\nlet runnableModel: RunnableModel\n\n/**\n * Maintain a `BufferedRunModelParams` instance that wraps the transferable buffer\n * containing the I/O parameters.\n * @hidden\n */\nconst params = new BufferedRunModelParams()\n\ninterface InitResult {\n outputVarIds: string[]\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n modelListing?: /*ModelListingSpecs*/ any\n startTime: number\n endTime: number\n saveFreq: number\n outputRowLength: number\n}\n\n/** @hidden */\nconst modelWorker = {\n async initModel(): Promise<InitResult> {\n if (runnableModel) {\n throw new Error('RunnableModel was already initialized')\n }\n\n // Initialize the runnable model\n const generatedModel = await initGeneratedModel()\n runnableModel = createRunnableModel(generatedModel)\n\n // Transfer the model metadata to the runner\n return {\n outputVarIds: runnableModel.outputVarIds,\n modelListing: runnableModel.modelListing,\n startTime: runnableModel.startTime,\n endTime: runnableModel.endTime,\n saveFreq: runnableModel.saveFreq,\n outputRowLength: runnableModel.numSavePoints\n }\n },\n\n runModel(ioBuffer: ArrayBuffer): TransferDescriptor<ArrayBuffer> {\n if (!runnableModel) {\n throw new Error('RunnableModel must be initialized before running the model in worker')\n }\n\n // Update the `BufferedRunModelParams` to use the values in the buffer that was transferred\n // from the runner to the worker\n params.updateFromEncodedBuffer(ioBuffer)\n\n // Run the model synchronously on the worker thread using those I/O parameters\n runnableModel.runModel(params)\n\n // Transfer the buffer back to the runner\n return Transfer(ioBuffer)\n }\n}\n\n/**\n * Expose an object in the current worker thread that communicates with the\n * `ModelRunner` instance running in the main thread. The exposed worker\n * object will take care of running the model on the worker thread and\n * sending the outputs back to the main thread.\n *\n * @param init The function that initializes the generated model instance that\n * is used in the worker thread.\n */\nexport function exposeModelWorker(init: () => Promise<GeneratedModel>): void {\n // Save the initializer, which will be used when the runner calls `initModel`\n // on the worker\n initGeneratedModel = init\n\n // Expose the worker implementation to `threads.js`\n expose(modelWorker)\n}\n"],"mappings":";AAEA,SAAS,YAAY,OAAO,QAAQ,UAAU,cAAc;AAG5D,SAAS,wBAAwB,cAAc,eAAe;AAkC9D,eAAsB,sBAAsB,YAAyE;AACnH,MAAI,WAAW,MAAM,GAAG;AACtB,WAAO,gCAAgC,IAAI,OAAO,WAAW,MAAM,CAAC,CAAC;AAAA,EACvE,OAAO;AACL,WAAO,gCAAgC,WAAW,SAAS,WAAW,QAAQ,CAAC,CAAC;AAAA,EAClF;AACF;AAKA,eAAe,gCAAgC,QAAsC;AAEnF,QAAMA,eAAc,MAAM,MAAM,MAAM;AAGtC,QAAM,aAAa,MAAMA,aAAY,UAAU;AAG/C,QAAM,eAAe,WAAW,eAAe,IAAI,aAAa,WAAW,YAAY,IAAI;AAG3F,QAAMC,UAAS,IAAI,uBAAuB,YAAY;AAGtD,MAAI,UAAU;AAGd,MAAI,aAAa;AAEjB,SAAO;AAAA,IACL,eAAe,MAAM;AACnB,aAAO,IAAI,QAAQ,WAAW,cAAc,WAAW,WAAW,WAAW,SAAS,WAAW,QAAQ;AAAA,IAC3G;AAAA,IAEA,UAAU,OAAO,QAAQ,SAAS,YAAY;AAC5C,UAAI,YAAY;AACd,cAAM,IAAI,MAAM,gDAAgD;AAAA,MAClE,WAAW,SAAS;AAClB,cAAM,IAAI,MAAM,gEAAgE;AAAA,MAClF,OAAO;AACL,kBAAU;AAAA,MACZ;AAGA,MAAAA,QAAO,iBAAiB,QAAQ,SAAS,OAAO;AAMhD,UAAI;AACJ,UAAI;AACF,mBAAW,MAAMD,aAAY,SAAS,SAASC,QAAO,iBAAiB,CAAC,CAAC;AAAA,MAC3E,UAAE;AACA,kBAAU;AAAA,MACZ;AAMA,MAAAA,QAAO,wBAAwB,QAAQ;AAIvC,MAAAA,QAAO,gBAAgB,OAAO;AAE9B,aAAO;AAAA,IACT;AAAA,IAEA,WAAW,MAAM;AACf,UAAI,YAAY;AACd,eAAO,QAAQ,QAAQ;AAAA,MACzB,OAAO;AACL,qBAAa;AACb,eAAO,OAAO,UAAUD,YAAW;AAAA,MACrC;AAAA,IACF;AAAA,EACF;AACF;;;ACpHA,SAAS,QAAQ,YAAAE,iBAAgB;AAGjC,SAAS,0BAAAC,yBAAwB,2BAA2B;AAG5D,IAAI;AAGJ,IAAI;AAOJ,IAAM,SAAS,IAAIA,wBAAuB;AAa1C,IAAM,cAAc;AAAA,EAClB,MAAM,YAAiC;AACrC,QAAI,eAAe;AACjB,YAAM,IAAI,MAAM,uCAAuC;AAAA,IACzD;AAGA,UAAM,iBAAiB,MAAM,mBAAmB;AAChD,oBAAgB,oBAAoB,cAAc;AAGlD,WAAO;AAAA,MACL,cAAc,cAAc;AAAA,MAC5B,cAAc,cAAc;AAAA,MAC5B,WAAW,cAAc;AAAA,MACzB,SAAS,cAAc;AAAA,MACvB,UAAU,cAAc;AAAA,MACxB,iBAAiB,cAAc;AAAA,IACjC;AAAA,EACF;AAAA,EAEA,SAAS,UAAwD;AAC/D,QAAI,CAAC,eAAe;AAClB,YAAM,IAAI,MAAM,sEAAsE;AAAA,IACxF;AAIA,WAAO,wBAAwB,QAAQ;AAGvC,kBAAc,SAAS,MAAM;AAG7B,WAAOD,UAAS,QAAQ;AAAA,EAC1B;AACF;AAWO,SAAS,kBAAkB,MAA2C;AAG3E,uBAAqB;AAGrB,SAAO,WAAW;AACpB;","names":["modelWorker","params","Transfer","BufferedRunModelParams"]}
|
package/dist/runner.cjs
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
-
var __export = (target, all) => {
|
|
6
|
-
for (var name in all)
|
|
7
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
-
};
|
|
9
|
-
var __copyProps = (to, from, except, desc) => {
|
|
10
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
-
for (let key of __getOwnPropNames(from))
|
|
12
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
-
}
|
|
15
|
-
return to;
|
|
16
|
-
};
|
|
17
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
-
|
|
19
|
-
// src/runner.ts
|
|
20
|
-
var runner_exports = {};
|
|
21
|
-
__export(runner_exports, {
|
|
22
|
-
spawnAsyncModelRunner: () => spawnAsyncModelRunner
|
|
23
|
-
});
|
|
24
|
-
module.exports = __toCommonJS(runner_exports);
|
|
25
|
-
var import_threads = require("threads");
|
|
26
|
-
var import_runtime = require("@sdeverywhere/runtime");
|
|
27
|
-
async function spawnAsyncModelRunner(workerSpec) {
|
|
28
|
-
if (workerSpec["path"]) {
|
|
29
|
-
return spawnAsyncModelRunnerWithWorker(new import_threads.Worker(workerSpec["path"]));
|
|
30
|
-
} else {
|
|
31
|
-
return spawnAsyncModelRunnerWithWorker(import_threads.BlobWorker.fromText(workerSpec["source"]));
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
async function spawnAsyncModelRunnerWithWorker(worker) {
|
|
35
|
-
const modelWorker = await (0, import_threads.spawn)(worker);
|
|
36
|
-
const initResult = await modelWorker.initModel();
|
|
37
|
-
const modelListing = initResult.modelListing ? new import_runtime.ModelListing(initResult.modelListing) : void 0;
|
|
38
|
-
const params = new import_runtime.BufferedRunModelParams(modelListing);
|
|
39
|
-
let running = false;
|
|
40
|
-
let terminated = false;
|
|
41
|
-
return {
|
|
42
|
-
createOutputs: () => {
|
|
43
|
-
return new import_runtime.Outputs(initResult.outputVarIds, initResult.startTime, initResult.endTime, initResult.saveFreq);
|
|
44
|
-
},
|
|
45
|
-
runModel: async (inputs, outputs, options) => {
|
|
46
|
-
if (terminated) {
|
|
47
|
-
throw new Error("Async model runner has already been terminated");
|
|
48
|
-
} else if (running) {
|
|
49
|
-
throw new Error("Async model runner only supports one `runModel` call at a time");
|
|
50
|
-
} else {
|
|
51
|
-
running = true;
|
|
52
|
-
}
|
|
53
|
-
params.updateFromParams(inputs, outputs, options);
|
|
54
|
-
let ioBuffer;
|
|
55
|
-
try {
|
|
56
|
-
ioBuffer = await modelWorker.runModel((0, import_threads.Transfer)(params.getEncodedBuffer()));
|
|
57
|
-
} finally {
|
|
58
|
-
running = false;
|
|
59
|
-
}
|
|
60
|
-
params.updateFromEncodedBuffer(ioBuffer);
|
|
61
|
-
params.finalizeOutputs(outputs);
|
|
62
|
-
return outputs;
|
|
63
|
-
},
|
|
64
|
-
terminate: () => {
|
|
65
|
-
if (terminated) {
|
|
66
|
-
return Promise.resolve();
|
|
67
|
-
} else {
|
|
68
|
-
terminated = true;
|
|
69
|
-
return import_threads.Thread.terminate(modelWorker);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
75
|
-
0 && (module.exports = {
|
|
76
|
-
spawnAsyncModelRunner
|
|
77
|
-
});
|
|
78
|
-
//# sourceMappingURL=runner.cjs.map
|
package/dist/runner.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runner.ts"],"sourcesContent":["// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport { BlobWorker, spawn, Thread, Transfer, Worker } from 'threads'\n\nimport type { ModelRunner } from '@sdeverywhere/runtime'\nimport { BufferedRunModelParams, ModelListing, Outputs } from '@sdeverywhere/runtime'\n\n/**\n * Initialize a `ModelRunner` that runs the model asynchronously in a worker\n * (a Web Worker when running in a browser environment, or a worker thread\n * when running in a Node.js environment).\n *\n * In your app project, define a JavaScript file, called `worker.js` for example,\n * that initializes the generated model in the context of a worker thread:\n *\n * ```js\n * import { exposeModelWorker } from '@sdeverywhere/runtime-async/worker'\n * import loadGeneratedModel from './sde-prep/generated-model.js'\n *\n * exposeModelWorker(loadGeneratedModel)\n * ```\n *\n * Then, in your web app, call the `spawnAsyncModelRunner` function, which\n * will spawn the worker thread and initialize the `ModelRunner` that communicates\n * with the worker:\n *\n * ```js\n * import { spawnAsyncModelRunner } from '@sdeverywhere/runtime-async/runner'\n *\n * async function initApp() {\n * // ...\n * const runner = await spawnAsyncModelRunner({ path: './worker.js' })\n * // ...\n * }\n * ```\n *\n * @param workerSpec Either a `path` to the worker JavaScript file, or the `source`\n * containing the full JavaScript source of the worker.\n */\nexport async function spawnAsyncModelRunner(workerSpec: { path: string } | { source: string }): Promise<ModelRunner> {\n if (workerSpec['path']) {\n return spawnAsyncModelRunnerWithWorker(new Worker(workerSpec['path']))\n } else {\n return spawnAsyncModelRunnerWithWorker(BlobWorker.fromText(workerSpec['source']))\n }\n}\n\n/**\n * @hidden For internal use only\n */\nasync function spawnAsyncModelRunnerWithWorker(worker: Worker): Promise<ModelRunner> {\n // Spawn the given Worker that contains the `ModelWorker`\n const modelWorker = await spawn(worker)\n\n // Wait for the worker to initialize the wasm model (in the worker thread)\n const initResult = await modelWorker.initModel()\n\n // Create a `ModelListing` instance if the listing was defined in the generated model\n const modelListing = initResult.modelListing ? new ModelListing(initResult.modelListing) : undefined\n\n // Maintain a `BufferedRunModelParams` instance that holds the I/O parameters\n const params = new BufferedRunModelParams(modelListing)\n\n // Use a flag to ensure that only one request is made at a time\n let running = false\n\n // Disallow `runModel` after the runner has been terminated\n let terminated = false\n\n return {\n createOutputs: () => {\n return new Outputs(initResult.outputVarIds, initResult.startTime, initResult.endTime, initResult.saveFreq)\n },\n\n runModel: async (inputs, outputs, options) => {\n if (terminated) {\n throw new Error('Async model runner has already been terminated')\n } else if (running) {\n throw new Error('Async model runner only supports one `runModel` call at a time')\n } else {\n running = true\n }\n\n // Update the I/O parameters\n params.updateFromParams(inputs, outputs, options)\n\n // Run the model in the worker. We pass the underlying `ArrayBuffer`\n // instance back to the worker wrapped in a `Transfer` to make it\n // no-copy transferable, and then the worker will return it back\n // to us.\n let ioBuffer: ArrayBuffer\n try {\n ioBuffer = await modelWorker.runModel(Transfer(params.getEncodedBuffer()))\n } finally {\n running = false\n }\n\n // Once the buffer is transferred to the worker, the buffer in the\n // `BufferedRunModelParams` becomes \"detached\" and is no longer usable.\n // After the buffer is transferred back from the worker, we need to\n // restore the state of the object to use the new buffer.\n params.updateFromEncodedBuffer(ioBuffer)\n\n // Copy the output values and elapsed time from the buffer to the\n // `Outputs` instance\n params.finalizeOutputs(outputs)\n\n return outputs\n },\n\n terminate: () => {\n if (terminated) {\n return Promise.resolve()\n } else {\n terminated = true\n return Thread.terminate(modelWorker)\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,qBAA4D;AAG5D,qBAA8D;AAkC9D,eAAsB,sBAAsB,YAAyE;AACnH,MAAI,WAAW,MAAM,GAAG;AACtB,WAAO,gCAAgC,IAAI,sBAAO,WAAW,MAAM,CAAC,CAAC;AAAA,EACvE,OAAO;AACL,WAAO,gCAAgC,0BAAW,SAAS,WAAW,QAAQ,CAAC,CAAC;AAAA,EAClF;AACF;AAKA,eAAe,gCAAgC,QAAsC;AAEnF,QAAM,cAAc,UAAM,sBAAM,MAAM;AAGtC,QAAM,aAAa,MAAM,YAAY,UAAU;AAG/C,QAAM,eAAe,WAAW,eAAe,IAAI,4BAAa,WAAW,YAAY,IAAI;AAG3F,QAAM,SAAS,IAAI,sCAAuB,YAAY;AAGtD,MAAI,UAAU;AAGd,MAAI,aAAa;AAEjB,SAAO;AAAA,IACL,eAAe,MAAM;AACnB,aAAO,IAAI,uBAAQ,WAAW,cAAc,WAAW,WAAW,WAAW,SAAS,WAAW,QAAQ;AAAA,IAC3G;AAAA,IAEA,UAAU,OAAO,QAAQ,SAAS,YAAY;AAC5C,UAAI,YAAY;AACd,cAAM,IAAI,MAAM,gDAAgD;AAAA,MAClE,WAAW,SAAS;AAClB,cAAM,IAAI,MAAM,gEAAgE;AAAA,MAClF,OAAO;AACL,kBAAU;AAAA,MACZ;AAGA,aAAO,iBAAiB,QAAQ,SAAS,OAAO;AAMhD,UAAI;AACJ,UAAI;AACF,mBAAW,MAAM,YAAY,aAAS,yBAAS,OAAO,iBAAiB,CAAC,CAAC;AAAA,MAC3E,UAAE;AACA,kBAAU;AAAA,MACZ;AAMA,aAAO,wBAAwB,QAAQ;AAIvC,aAAO,gBAAgB,OAAO;AAE9B,aAAO;AAAA,IACT;AAAA,IAEA,WAAW,MAAM;AACf,UAAI,YAAY;AACd,eAAO,QAAQ,QAAQ;AAAA,MACzB,OAAO;AACL,qBAAa;AACb,eAAO,sBAAO,UAAU,WAAW;AAAA,MACrC;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
package/dist/runner.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runner.ts"],"sourcesContent":["// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport { BlobWorker, spawn, Thread, Transfer, Worker } from 'threads'\n\nimport type { ModelRunner } from '@sdeverywhere/runtime'\nimport { BufferedRunModelParams, ModelListing, Outputs } from '@sdeverywhere/runtime'\n\n/**\n * Initialize a `ModelRunner` that runs the model asynchronously in a worker\n * (a Web Worker when running in a browser environment, or a worker thread\n * when running in a Node.js environment).\n *\n * In your app project, define a JavaScript file, called `worker.js` for example,\n * that initializes the generated model in the context of a worker thread:\n *\n * ```js\n * import { exposeModelWorker } from '@sdeverywhere/runtime-async/worker'\n * import loadGeneratedModel from './sde-prep/generated-model.js'\n *\n * exposeModelWorker(loadGeneratedModel)\n * ```\n *\n * Then, in your web app, call the `spawnAsyncModelRunner` function, which\n * will spawn the worker thread and initialize the `ModelRunner` that communicates\n * with the worker:\n *\n * ```js\n * import { spawnAsyncModelRunner } from '@sdeverywhere/runtime-async/runner'\n *\n * async function initApp() {\n * // ...\n * const runner = await spawnAsyncModelRunner({ path: './worker.js' })\n * // ...\n * }\n * ```\n *\n * @param workerSpec Either a `path` to the worker JavaScript file, or the `source`\n * containing the full JavaScript source of the worker.\n */\nexport async function spawnAsyncModelRunner(workerSpec: { path: string } | { source: string }): Promise<ModelRunner> {\n if (workerSpec['path']) {\n return spawnAsyncModelRunnerWithWorker(new Worker(workerSpec['path']))\n } else {\n return spawnAsyncModelRunnerWithWorker(BlobWorker.fromText(workerSpec['source']))\n }\n}\n\n/**\n * @hidden For internal use only\n */\nasync function spawnAsyncModelRunnerWithWorker(worker: Worker): Promise<ModelRunner> {\n // Spawn the given Worker that contains the `ModelWorker`\n const modelWorker = await spawn(worker)\n\n // Wait for the worker to initialize the wasm model (in the worker thread)\n const initResult = await modelWorker.initModel()\n\n // Create a `ModelListing` instance if the listing was defined in the generated model\n const modelListing = initResult.modelListing ? new ModelListing(initResult.modelListing) : undefined\n\n // Maintain a `BufferedRunModelParams` instance that holds the I/O parameters\n const params = new BufferedRunModelParams(modelListing)\n\n // Use a flag to ensure that only one request is made at a time\n let running = false\n\n // Disallow `runModel` after the runner has been terminated\n let terminated = false\n\n return {\n createOutputs: () => {\n return new Outputs(initResult.outputVarIds, initResult.startTime, initResult.endTime, initResult.saveFreq)\n },\n\n runModel: async (inputs, outputs, options) => {\n if (terminated) {\n throw new Error('Async model runner has already been terminated')\n } else if (running) {\n throw new Error('Async model runner only supports one `runModel` call at a time')\n } else {\n running = true\n }\n\n // Update the I/O parameters\n params.updateFromParams(inputs, outputs, options)\n\n // Run the model in the worker. We pass the underlying `ArrayBuffer`\n // instance back to the worker wrapped in a `Transfer` to make it\n // no-copy transferable, and then the worker will return it back\n // to us.\n let ioBuffer: ArrayBuffer\n try {\n ioBuffer = await modelWorker.runModel(Transfer(params.getEncodedBuffer()))\n } finally {\n running = false\n }\n\n // Once the buffer is transferred to the worker, the buffer in the\n // `BufferedRunModelParams` becomes \"detached\" and is no longer usable.\n // After the buffer is transferred back from the worker, we need to\n // restore the state of the object to use the new buffer.\n params.updateFromEncodedBuffer(ioBuffer)\n\n // Copy the output values and elapsed time from the buffer to the\n // `Outputs` instance\n params.finalizeOutputs(outputs)\n\n return outputs\n },\n\n terminate: () => {\n if (terminated) {\n return Promise.resolve()\n } else {\n terminated = true\n return Thread.terminate(modelWorker)\n }\n }\n }\n}\n"],"mappings":";AAEA,SAAS,YAAY,OAAO,QAAQ,UAAU,cAAc;AAG5D,SAAS,wBAAwB,cAAc,eAAe;AAkC9D,eAAsB,sBAAsB,YAAyE;AACnH,MAAI,WAAW,MAAM,GAAG;AACtB,WAAO,gCAAgC,IAAI,OAAO,WAAW,MAAM,CAAC,CAAC;AAAA,EACvE,OAAO;AACL,WAAO,gCAAgC,WAAW,SAAS,WAAW,QAAQ,CAAC,CAAC;AAAA,EAClF;AACF;AAKA,eAAe,gCAAgC,QAAsC;AAEnF,QAAM,cAAc,MAAM,MAAM,MAAM;AAGtC,QAAM,aAAa,MAAM,YAAY,UAAU;AAG/C,QAAM,eAAe,WAAW,eAAe,IAAI,aAAa,WAAW,YAAY,IAAI;AAG3F,QAAM,SAAS,IAAI,uBAAuB,YAAY;AAGtD,MAAI,UAAU;AAGd,MAAI,aAAa;AAEjB,SAAO;AAAA,IACL,eAAe,MAAM;AACnB,aAAO,IAAI,QAAQ,WAAW,cAAc,WAAW,WAAW,WAAW,SAAS,WAAW,QAAQ;AAAA,IAC3G;AAAA,IAEA,UAAU,OAAO,QAAQ,SAAS,YAAY;AAC5C,UAAI,YAAY;AACd,cAAM,IAAI,MAAM,gDAAgD;AAAA,MAClE,WAAW,SAAS;AAClB,cAAM,IAAI,MAAM,gEAAgE;AAAA,MAClF,OAAO;AACL,kBAAU;AAAA,MACZ;AAGA,aAAO,iBAAiB,QAAQ,SAAS,OAAO;AAMhD,UAAI;AACJ,UAAI;AACF,mBAAW,MAAM,YAAY,SAAS,SAAS,OAAO,iBAAiB,CAAC,CAAC;AAAA,MAC3E,UAAE;AACA,kBAAU;AAAA,MACZ;AAMA,aAAO,wBAAwB,QAAQ;AAIvC,aAAO,gBAAgB,OAAO;AAE9B,aAAO;AAAA,IACT;AAAA,IAEA,WAAW,MAAM;AACf,UAAI,YAAY;AACd,eAAO,QAAQ,QAAQ;AAAA,MACzB,OAAO;AACL,qBAAa;AACb,eAAO,OAAO,UAAU,WAAW;AAAA,MACrC;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
package/dist/worker.cjs
DELETED
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
-
var __export = (target, all) => {
|
|
6
|
-
for (var name in all)
|
|
7
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
-
};
|
|
9
|
-
var __copyProps = (to, from, except, desc) => {
|
|
10
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
-
for (let key of __getOwnPropNames(from))
|
|
12
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
-
}
|
|
15
|
-
return to;
|
|
16
|
-
};
|
|
17
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
-
|
|
19
|
-
// src/worker.ts
|
|
20
|
-
var worker_exports = {};
|
|
21
|
-
__export(worker_exports, {
|
|
22
|
-
exposeModelWorker: () => exposeModelWorker
|
|
23
|
-
});
|
|
24
|
-
module.exports = __toCommonJS(worker_exports);
|
|
25
|
-
var import_worker = require("threads/worker");
|
|
26
|
-
var import_runtime = require("@sdeverywhere/runtime");
|
|
27
|
-
var initGeneratedModel;
|
|
28
|
-
var runnableModel;
|
|
29
|
-
var params = new import_runtime.BufferedRunModelParams();
|
|
30
|
-
var modelWorker = {
|
|
31
|
-
async initModel() {
|
|
32
|
-
if (runnableModel) {
|
|
33
|
-
throw new Error("RunnableModel was already initialized");
|
|
34
|
-
}
|
|
35
|
-
const generatedModel = await initGeneratedModel();
|
|
36
|
-
runnableModel = (0, import_runtime.createRunnableModel)(generatedModel);
|
|
37
|
-
return {
|
|
38
|
-
outputVarIds: runnableModel.outputVarIds,
|
|
39
|
-
modelListing: runnableModel.modelListing,
|
|
40
|
-
startTime: runnableModel.startTime,
|
|
41
|
-
endTime: runnableModel.endTime,
|
|
42
|
-
saveFreq: runnableModel.saveFreq,
|
|
43
|
-
outputRowLength: runnableModel.numSavePoints
|
|
44
|
-
};
|
|
45
|
-
},
|
|
46
|
-
runModel(ioBuffer) {
|
|
47
|
-
if (!runnableModel) {
|
|
48
|
-
throw new Error("RunnableModel must be initialized before running the model in worker");
|
|
49
|
-
}
|
|
50
|
-
params.updateFromEncodedBuffer(ioBuffer);
|
|
51
|
-
runnableModel.runModel(params);
|
|
52
|
-
return (0, import_worker.Transfer)(ioBuffer);
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
function exposeModelWorker(init) {
|
|
56
|
-
initGeneratedModel = init;
|
|
57
|
-
(0, import_worker.expose)(modelWorker);
|
|
58
|
-
}
|
|
59
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
60
|
-
0 && (module.exports = {
|
|
61
|
-
exposeModelWorker
|
|
62
|
-
});
|
|
63
|
-
//# sourceMappingURL=worker.cjs.map
|
package/dist/worker.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/worker.ts"],"sourcesContent":["// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport type { TransferDescriptor } from 'threads'\nimport { expose, Transfer } from 'threads/worker'\n\nimport type { GeneratedModel, RunnableModel } from '@sdeverywhere/runtime'\nimport { BufferedRunModelParams, createRunnableModel } from '@sdeverywhere/runtime'\n\n/** @hidden */\nlet initGeneratedModel: () => Promise<GeneratedModel>\n\n/** @hidden */\nlet runnableModel: RunnableModel\n\n/**\n * Maintain a `BufferedRunModelParams` instance that wraps the transferable buffer\n * containing the I/O parameters.\n * @hidden\n */\nconst params = new BufferedRunModelParams()\n\ninterface InitResult {\n outputVarIds: string[]\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n modelListing?: /*ModelListingSpecs*/ any\n startTime: number\n endTime: number\n saveFreq: number\n outputRowLength: number\n}\n\n/** @hidden */\nconst modelWorker = {\n async initModel(): Promise<InitResult> {\n if (runnableModel) {\n throw new Error('RunnableModel was already initialized')\n }\n\n // Initialize the runnable model\n const generatedModel = await initGeneratedModel()\n runnableModel = createRunnableModel(generatedModel)\n\n // Transfer the model metadata to the runner\n return {\n outputVarIds: runnableModel.outputVarIds,\n modelListing: runnableModel.modelListing,\n startTime: runnableModel.startTime,\n endTime: runnableModel.endTime,\n saveFreq: runnableModel.saveFreq,\n outputRowLength: runnableModel.numSavePoints\n }\n },\n\n runModel(ioBuffer: ArrayBuffer): TransferDescriptor<ArrayBuffer> {\n if (!runnableModel) {\n throw new Error('RunnableModel must be initialized before running the model in worker')\n }\n\n // Update the `BufferedRunModelParams` to use the values in the buffer that was transferred\n // from the runner to the worker\n params.updateFromEncodedBuffer(ioBuffer)\n\n // Run the model synchronously on the worker thread using those I/O parameters\n runnableModel.runModel(params)\n\n // Transfer the buffer back to the runner\n return Transfer(ioBuffer)\n }\n}\n\n/**\n * Expose an object in the current worker thread that communicates with the\n * `ModelRunner` instance running in the main thread. The exposed worker\n * object will take care of running the model on the worker thread and\n * sending the outputs back to the main thread.\n *\n * @param init The function that initializes the generated model instance that\n * is used in the worker thread.\n */\nexport function exposeModelWorker(init: () => Promise<GeneratedModel>): void {\n // Save the initializer, which will be used when the runner calls `initModel`\n // on the worker\n initGeneratedModel = init\n\n // Expose the worker implementation to `threads.js`\n expose(modelWorker)\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAGA,oBAAiC;AAGjC,qBAA4D;AAG5D,IAAI;AAGJ,IAAI;AAOJ,IAAM,SAAS,IAAI,sCAAuB;AAa1C,IAAM,cAAc;AAAA,EAClB,MAAM,YAAiC;AACrC,QAAI,eAAe;AACjB,YAAM,IAAI,MAAM,uCAAuC;AAAA,IACzD;AAGA,UAAM,iBAAiB,MAAM,mBAAmB;AAChD,wBAAgB,oCAAoB,cAAc;AAGlD,WAAO;AAAA,MACL,cAAc,cAAc;AAAA,MAC5B,cAAc,cAAc;AAAA,MAC5B,WAAW,cAAc;AAAA,MACzB,SAAS,cAAc;AAAA,MACvB,UAAU,cAAc;AAAA,MACxB,iBAAiB,cAAc;AAAA,IACjC;AAAA,EACF;AAAA,EAEA,SAAS,UAAwD;AAC/D,QAAI,CAAC,eAAe;AAClB,YAAM,IAAI,MAAM,sEAAsE;AAAA,IACxF;AAIA,WAAO,wBAAwB,QAAQ;AAGvC,kBAAc,SAAS,MAAM;AAG7B,eAAO,wBAAS,QAAQ;AAAA,EAC1B;AACF;AAWO,SAAS,kBAAkB,MAA2C;AAG3E,uBAAqB;AAGrB,4BAAO,WAAW;AACpB;","names":[]}
|
package/dist/worker.d.cts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { GeneratedModel } from '@sdeverywhere/runtime';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Expose an object in the current worker thread that communicates with the
|
|
5
|
-
* `ModelRunner` instance running in the main thread. The exposed worker
|
|
6
|
-
* object will take care of running the model on the worker thread and
|
|
7
|
-
* sending the outputs back to the main thread.
|
|
8
|
-
*
|
|
9
|
-
* @param init The function that initializes the generated model instance that
|
|
10
|
-
* is used in the worker thread.
|
|
11
|
-
*/
|
|
12
|
-
declare function exposeModelWorker(init: () => Promise<GeneratedModel>): void;
|
|
13
|
-
|
|
14
|
-
export { exposeModelWorker };
|
package/dist/worker.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/worker.ts"],"sourcesContent":["// Copyright (c) 2020-2022 Climate Interactive / New Venture Fund\n\nimport type { TransferDescriptor } from 'threads'\nimport { expose, Transfer } from 'threads/worker'\n\nimport type { GeneratedModel, RunnableModel } from '@sdeverywhere/runtime'\nimport { BufferedRunModelParams, createRunnableModel } from '@sdeverywhere/runtime'\n\n/** @hidden */\nlet initGeneratedModel: () => Promise<GeneratedModel>\n\n/** @hidden */\nlet runnableModel: RunnableModel\n\n/**\n * Maintain a `BufferedRunModelParams` instance that wraps the transferable buffer\n * containing the I/O parameters.\n * @hidden\n */\nconst params = new BufferedRunModelParams()\n\ninterface InitResult {\n outputVarIds: string[]\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n modelListing?: /*ModelListingSpecs*/ any\n startTime: number\n endTime: number\n saveFreq: number\n outputRowLength: number\n}\n\n/** @hidden */\nconst modelWorker = {\n async initModel(): Promise<InitResult> {\n if (runnableModel) {\n throw new Error('RunnableModel was already initialized')\n }\n\n // Initialize the runnable model\n const generatedModel = await initGeneratedModel()\n runnableModel = createRunnableModel(generatedModel)\n\n // Transfer the model metadata to the runner\n return {\n outputVarIds: runnableModel.outputVarIds,\n modelListing: runnableModel.modelListing,\n startTime: runnableModel.startTime,\n endTime: runnableModel.endTime,\n saveFreq: runnableModel.saveFreq,\n outputRowLength: runnableModel.numSavePoints\n }\n },\n\n runModel(ioBuffer: ArrayBuffer): TransferDescriptor<ArrayBuffer> {\n if (!runnableModel) {\n throw new Error('RunnableModel must be initialized before running the model in worker')\n }\n\n // Update the `BufferedRunModelParams` to use the values in the buffer that was transferred\n // from the runner to the worker\n params.updateFromEncodedBuffer(ioBuffer)\n\n // Run the model synchronously on the worker thread using those I/O parameters\n runnableModel.runModel(params)\n\n // Transfer the buffer back to the runner\n return Transfer(ioBuffer)\n }\n}\n\n/**\n * Expose an object in the current worker thread that communicates with the\n * `ModelRunner` instance running in the main thread. The exposed worker\n * object will take care of running the model on the worker thread and\n * sending the outputs back to the main thread.\n *\n * @param init The function that initializes the generated model instance that\n * is used in the worker thread.\n */\nexport function exposeModelWorker(init: () => Promise<GeneratedModel>): void {\n // Save the initializer, which will be used when the runner calls `initModel`\n // on the worker\n initGeneratedModel = init\n\n // Expose the worker implementation to `threads.js`\n expose(modelWorker)\n}\n"],"mappings":";AAGA,SAAS,QAAQ,gBAAgB;AAGjC,SAAS,wBAAwB,2BAA2B;AAG5D,IAAI;AAGJ,IAAI;AAOJ,IAAM,SAAS,IAAI,uBAAuB;AAa1C,IAAM,cAAc;AAAA,EAClB,MAAM,YAAiC;AACrC,QAAI,eAAe;AACjB,YAAM,IAAI,MAAM,uCAAuC;AAAA,IACzD;AAGA,UAAM,iBAAiB,MAAM,mBAAmB;AAChD,oBAAgB,oBAAoB,cAAc;AAGlD,WAAO;AAAA,MACL,cAAc,cAAc;AAAA,MAC5B,cAAc,cAAc;AAAA,MAC5B,WAAW,cAAc;AAAA,MACzB,SAAS,cAAc;AAAA,MACvB,UAAU,cAAc;AAAA,MACxB,iBAAiB,cAAc;AAAA,IACjC;AAAA,EACF;AAAA,EAEA,SAAS,UAAwD;AAC/D,QAAI,CAAC,eAAe;AAClB,YAAM,IAAI,MAAM,sEAAsE;AAAA,IACxF;AAIA,WAAO,wBAAwB,QAAQ;AAGvC,kBAAc,SAAS,MAAM;AAG7B,WAAO,SAAS,QAAQ;AAAA,EAC1B;AACF;AAWO,SAAS,kBAAkB,MAA2C;AAG3E,uBAAqB;AAGrB,SAAO,WAAW;AACpB;","names":[]}
|