Use from JavaScript#

The plen-wasm package also ships the same Project API as the JavaScript library, running on the WebAssembly core. This page shows how to load that API in the browser and in Node, and what's different from the JavaScript library.

Raw surface or glue#

The package has two layers:

  • Glue, the package entry. It gives you a Project class, schedule, the typed InputError and ProjectError, and optional Date conversion. It takes plain objects, not JSON text. Use it in browser and Node apps.
  • Raw exports, JsonProject and schedule from the generated pkg/ and pkg-node/ files. They take JSON strings and return a JSON envelope. Use them in custom runtimes, in servers that forward JSON as is, and in tools that work with the wire format. The rest of this branch documents them.

If you're building a browser or Node app, use the glue.

Install#

Build the package from source, then install the tarball:

cd crates/plen-wasm
npm pack
npm install ./plen-wasm-0.1.0.tgz

npm pack runs npm run build, which builds the browser target into pkg/ and the Node target into pkg-node/. You need wasm-pack for this step. From the workspace root, task build:wasm and task build:wasm:node build the same two targets.

Entries#

The package is ESM only ("type": "module"). Its exports map has two entries:

Import Resolves to Exports
plen-wasm in Node node.mjs Project, schedule, InputError, ProjectError, default init
plen-wasm elsewhere index.mjs the same
plen-wasm/glue glue.mjs createBindings

Type declarations for the main entry come from index.d.ts. The ./glue entry has no type declarations.

pkg/ and pkg-node/ ship in the package, but they aren't in the exports map. Don't import them for the glue API.

Initializing#

Browser#

  1. Import init and the API from the package entry.
  2. Call await init() once, before you create the first Project or call schedule. init() fetches and instantiates the WebAssembly module, plen_wasm_bg.wasm from the pkg/ directory. If you create a Project before init() resolves, the call fails.
  3. Use the API.
import init, { Project } from "plen-wasm";

await init();

const project = new Project({ dates: "string" });
const result = project.solve({
  tasks: [{ id: 1, start_date: "2026-01-01", duration: 2 }],
});
const preview = project.preview(
  { tasks: { update: [{ id: 1, duration: 3 }] } },
  { baselineId: result.baselineId },
);
project.dispose();

To load the .wasm from another place, pass it to init, as { module_or_path }. It accepts a URL, a Response, the bytes or a compiled WebAssembly.Module.

init() resolves to the WebAssembly module's exports, not to undefined.

Node#

Node resolves node.mjs. The module loads when you import it, so there's no init step:

import { schedule } from "plen-wasm";

const result = schedule({
  tasks: [{ id: 1, start_date: "2026-01-01", duration: 2 }],
});
console.log(result.updates);

init() exists in Node too, so the same code runs in both environments. It's async, does nothing, and resolves to the loaded module.

Use the API#

Once the module is loaded, the API is the same as the JavaScript library's: Project with solve, preview, analyze, apply, level, loadProfile, evaluate, slots, suggestAssignments, clone and dispose, plus schedule and the two error classes. Both packages use the same type declarations. For details, see the JavaScript library documentation.

Binding your own module#

If you instantiate the WebAssembly module yourself, for example from your own build or in a worker, bind the API to it with createBindings from plen-wasm/glue. It takes any object that has the raw exports (JsonProject and schedule) and returns { Project, InputError, ProjectError, schedule }:

import { createBindings } from "plen-wasm/glue";
import init, * as wasm from "./build/plen_wasm.js";

await init();
const { Project, InputError, ProjectError, schedule } = createBindings(wasm);

Each createBindings call creates its own InputError and ProjectError classes. Errors from these bindings fail instanceof checks against the classes the package entry exports. Check them against the classes the same call returned.

What differs from the js package#

Topic JavaScript library This package
init() Optional; resolves undefined Browser: required before the first Project; resolves to the WebAssembly module. Node: optional; resolves to the module
dispose() Marks the project unusable; frees nothing Frees the WebAssembly memory the project holds
Internal engine failure Error with name "Panic" WebAssembly.RuntimeError
createBindings Not available Exported from plen-wasm/glue

Dispose every project#

Call dispose() on every Project and every clone you no longer need. It frees the WebAssembly memory the project holds. Each clone has its own memory, so dispose it on its own.

dispose() is idempotent, as in the JavaScript library. Any later call on that project throws Error("Project has been disposed"). If you never call dispose(), a finalizer frees the memory after garbage collection. The JavaScript runtime doesn't guarantee that the finalizer runs, so don't rely on it, especially in long-lived editors.

schedule() creates and disposes its own temporary project, so there's nothing to free.

Engine failures#

An internal engine failure surfaces as a WebAssembly.RuntimeError. After one, create the project again (or reload the module) before you continue.

Raw API#

The raw exports under the glue are documented in this branch's API reference. Most JavaScript apps don't need them.