@varianz/node (install); it works from TypeScript or plain JavaScript. Test-side integrations (@varianz/vitest, @varianz/playwright) are covered under Testing.
Requires Node.js 22+ and, for the decorator API, TC39 (stage-3) decorators — TypeScript ≥ 5.0 with experimentalDecorators: false.
Two registration styles
Both produce the same runtime handles and stage API.
Decorators
context.addInitializer(...), so registration happens when the class is instantiated, not at import time. Construct the service before calling getVPointHandle(...).
createVPoints
opts.patch):
'wrap'(default) — returns a proxy; the original object is untouched. Calls on the original object are not intercepted — use the returnedinstrumented, or:'mutate'— replaces methods on the original object in place (logs a one-time warning).'off'— registers without intercepting; dispatch manually viacallVPoint().
Other registration paths
registerVPoint(vpdBytes, api)— from an offline-built descriptor;encodeVpd(spec)builds one from aDynamicSpec.discover('src/services/**/*.service.ts')— loads matching files and instantiates exported classes so@vpointinitializers fire. Runs in-process, so.tsglobs need a TS loader (tsx,ts-node, or--experimental-strip-types).
Schemas
Schemas come from four sources, in fixed precedence: explicit registration options → the@varianz/scanner build-time manifest → first successful invocation → auto-derived Unknown.
Run the scanner and your TS types become the schema source — decorators like @struct/@field are then overrides for special cases (canonical names, Kind.Int32 instead of the Float64 default for number, field access modes). Values typed Unknown still support CEL field access at runtime; only CEL struct-literal construction needs a typed schema.
Kinds cover the numeric range (Int8–Int128, UInt8–UInt128, Float32/64), Bool, Utf8, Bytes, Struct, and Unknown. Named struct kinds link a parameter to a @struct declaration:
Local stages
Uniquely, the Node SDK can attach stages in-process without the registry — JS functions, CEL, or debug logging — at any anchor:using stage = attachStage(...)); on Node 22 call stage.remove() explicitly. attachCelStage and attachDebugStage also accept a VPoint name instead of a handle.
Sessions
The SDK holds the session inAsyncLocalStorage, exposed as VarianzContext:
varianzId('sessionId') in its params, and the SDK enters the scope automatically. Framework-specific wiring (Fastify, Koa, Hono, NestJS, gRPC): Propagate sessions.
Registry connection
initRegistry is process-global and throws on a second call — call it exactly once at startup (the test fixtures guard this for you). The environment tags determine which environment-scoped stages reach this instance.
For diagnostics: initTracing('info') or initTracing('debug', { json: true }).
Troubleshooting
- Decorators don’t register — decoration runs on instantiation. Construct the service before
getVPointHandle(...). initRegistrythrows “already initialised” — it’s process-global. In Vitest, setfileParallelism: false.- Calls aren’t intercepted — you’re calling the original object with the default
wrappatch mode. Call throughinstrumentedor usepatch: 'mutate'. varianz-scanfinds no VPoints — the scanner pre-filters files containingvpoint/createVPointscall sites; importing types alone registers nothing.
