Vue — advanced usage
Escape hatches past the binding layer: connectWorker tuning, per-call abort/timeout, shared observables, and pool lifecycle — same APIs under every framework's idiom.
One observable, many components
Each useSharedValue builds its own observe(). Hoist it to module scope and wrap with useObservable — all subscribers share one field watch; onScopeDispose still unsubscribes per scope.
import { observe, shallowEqual } from '@atolljs/core';
import { useObservable } from '@atolljs/vue';
import { incidentsMemory } from '@atolljs/incidents';
export const pressure = observe(
incidentsMemory,
'state.metrics',
(m) => ({ open: m.open, critical: m.critical }),
{ equals: shallowEqual },
);
// in <script setup>: const p = useObservable(pressure); // shared watchImperative watch — outside components
watch() on a field connector fires on every write, local or remote — for logging, analytics, or bridging to non-reactive code. Dispose the returned function yourself.
import { watch } from '@atolljs/core';
import { incidentsMemory } from '@atolljs/incidents';
const stop = watch(
incidentsMemory.connector('state.metrics'),
(m) => console.log('open incidents:', m?.open),
);
stop(); // when the feature unloadsAbort & timeout per call
with() attaches RunOptions to the same client surface. A queued call drops outright; an in-flight call is orphaned — the worker finishes it and the reply is discarded.
import { TaskAbortedError, TaskTimeoutError } from '@atolljs/core';
const ac = new AbortController();
const request = incidents
.with({ signal: ac.signal, timeout: 2_000 })
.queryIncidents(query);
ac.abort(); // rejects with TaskAbortedErrorPool lifecycle under HMR
terminate() kills the pool — the next method call re-spawns it lazily. Without a dispose hook, Vite hot reloads would leak workers.
if (import.meta.hot) {
import.meta.hot.dispose(() => incidents.terminate());
}
incidents.pool?.stats(); // { workers, idle, inFlight, queued, … }