@sdeverywhere/plugin-worker
v0.2.15
Published
This package provides a plugin that generates a `worker.js` file that can run a System Dynamics model (as generated by [SDEverywhere](https://github.com/climateinteractive/SDEverywhere)) asynchronously in a Web Worker or Node.js worker thread.
Readme
@sdeverywhere/plugin-worker
This package provides a plugin that generates a worker.js file that can run a System Dynamics model (as generated by SDEverywhere) asynchronously in a Web Worker or Node.js worker thread.
Quick Start
The best way to get started with SDEverywhere is to follow the Quick Start instructions.
If you follow those instructions, the @sdeverywhere/plugin-worker package will be added to your project automatically, in which case you can skip the next section and jump straight to the "Usage" section below.
Install
# npm
npm install --save-dev @sdeverywhere/plugin-worker
# pnpm
pnpm add -D @sdeverywhere/plugin-worker
# yarn
yarn add -D @sdeverywhere/plugin-workerUsage
Note: If you followed the "Quick Start" instructions above and/or are using one of the standard project templates provided by SDEverywhere, the sde.config.js file should already be set up to use plugin-worker.
Reading these instructions can still be helpful if you are setting up a project manually or want to understand how plugin-worker can be integrated into your project.
Why use this plugin?
Running an SDEverywhere-generated model can take long enough to make an application feel unresponsive if the model runs on the main (UI) thread.
This plugin bundles your generated model together with the @sdeverywhere/runtime-async glue code into a single, self-contained worker.js file.
When your application spawns that worker, model runs happen on a separate thread, which keeps sliders and other controls responsive.
The generated worker works in both web browser and Node.js environments without any changes on your part: it uses a Web Worker in a browser, and a worker thread in Node.js.
Steps
- Add
@sdeverywhere/plugin-workeras a project "dev" dependency:
cd your-model-project
npm install --save-dev @sdeverywhere/plugin-worker- Update your
sde.config.jsfile to useworkerPlugin. Make sure thatworkerPluginruns after any plugin that produces the generated model (for example,plugin-wasm):
import { dirname, join as joinPath } from 'path'
import { fileURLToPath } from 'url'
import { workerPlugin } from '@sdeverywhere/plugin-worker'
const __dirname = dirname(fileURLToPath(import.meta.url))
const corePath = (...parts) => joinPath(__dirname, 'packages', 'core', ...parts)
export async function config() {
return {
modelFiles: ['model/example.mdl'],
// ...
plugins: [
// Generate a `worker.js` file that runs the model asynchronously on a
// worker thread for improved responsiveness
workerPlugin({
// If `outputPaths` is undefined, `worker.js` is written to the `sde-prep`
// directory. More commonly, you will write it into your app or library
// source tree so that it can be imported directly.
outputPaths: [corePath('src', 'model', 'generated', 'worker.js')]
})
]
}
}- Run
sde bundle(orsde dev); the plugin writes aworker.jsfile to each configured output path.
Note: If you set genFormat to 'c' in your sde.config.js file, add wasmPlugin() before workerPlugin() so that the C code is compiled to a WebAssembly module first.
If genFormat is 'js' (the default), workerPlugin can pick up the generated JavaScript model directly and no additional plugin is needed.
Using the generated worker
The generated worker.js is a self-contained bundle, so the recommended approach is to import its source and pass it to spawnAsyncModelRunner from @sdeverywhere/runtime-async.
This avoids having to serve or resolve a separate worker file at runtime.
In a Vite-based project, you can use the ?raw suffix to import the file as a string:
import { spawnAsyncModelRunner } from '@sdeverywhere/runtime-async'
import workerJs from './generated/worker.js?raw'
export async function createModelRunner() {
return spawnAsyncModelRunner({ source: workerJs })
}Alternatively, if you serve worker.js as a static asset, you can spawn it by path instead:
const runner = await spawnAsyncModelRunner({ path: './worker.js' })The resulting ModelRunner can be used directly, or can be passed to a higher-level scheduler such as ModelScheduler.
See the @sdeverywhere/runtime and @sdeverywhere/runtime-async packages for more details.
Writing to multiple locations
The outputPaths option accepts more than one path, which is useful if the same worker is needed by more than one package in a monorepo:
workerPlugin({
outputPaths: [corePath('src', 'model', 'generated', 'worker.js'), appPath('src', 'model', 'generated', 'worker.js')]
})Documentation
API documentation (for plugin configuration options) is available in the docs directory.
License
SDEverywhere is distributed under the MIT license. See LICENSE for more details.
