causl/causl-core-rs#370. Calling this releases the engine IMMEDIATELY, in
the turn that calls it. A loop that constructs and disposes 20 000 graphs
without yielding retains 2.1 MB, about 0.11 KB per graph (Node v26.5.1,
arm64, heapUsed after two forced full collections). Before #370 the same
loop retained 299.4 MB, about 15.33 KB per graph, because
registerWasmGraphTeardown aimed a WeakRef at the backend and the
WeakRef constructor's AddToKeptObjects pin (ECMA-262 §26.1.1) is released
by the host at the next event-loop turn and by nothing else.
DROPPING a graph without disposing it is different, and it is still bounded
by a turn. An undisposed graph's engine is held by the sidecar's
module-level per-engineId handler maps, and the only thing that removes
those entries for a graph nobody disposed is the FinalizationRegistry
backstop, whose callback is a turn boundary by definition. Measured at
about 16.2 KB per graph inside the turn, returning to baseline across one.
So a synchronous loop over tens of thousands of graphs must either call
this, or yield (await new Promise((r) => setImmediate(r))) periodically.
Calling this is the cheaper of the two and is the only one available inside
a single turn.
#111 — public per-graph teardown. Releases the wasm engine behind a
Graphconstructed by this module: removes its per-engineIdhandler registrations from the sidecar (the module-level maps that otherwise pin the whole engine — its unbounded value cache, fn-table, and observers), frees its Rust slot (when the artefact carries the per-engine free extern), and drops it from the live registry. Idempotent; a no-op for a graph this module did not construct. Equivalent tograph.dispose()/using graph = createCauslWasmSync(...).