Repository navigation
Releases
NEXUS v0.18.0
NEXUS v0.18 release notes
v0.18.0
Prepared 2026-10-31 (nothing tagged or published: v0.17.0 is the latest release).
v0.18.0 is a minor release that moves NEXUS to Effect 4. North-star tier: none (no NEXUS API is added; every change follows Effect 4's types). No scorecard audit moves.
Breaking
- Requires Effect 4.
effectis now a peer dependency (^4.1.0, developed against 4.1.0), so an application and NEXUS share one Effect. Node>=22.19, Effect 4's own floor. - Public signatures follow Effect 4's types.
Runtime.runForkreturnsFiber.Fiber<A, E>(wasFiber.RuntimeFiber).Service<Shape>andCapability.EnvironmentareContext.Service<Shape, Shape>(wasContext.Tag).- Schemas in
Command,Event,StateandApplicationareSchema.Codec<A, I>(wasSchema.Schema<A, I>). CommandValidationError.causeand a validatedEvent.subscribefail withSchema.SchemaError(wasParseResult.ParseError).EventBusShape.subscribeyields aPubSub.Subscription(wasQueue.Dequeue).ShutdownOptions.graceis aDuration.Input(wasDuration.DurationInput).
- Validation issue strings change.
CommandValidationError.issuesandStateValidationError.issuesare now Effect 4'sString(SchemaError)format, for exampleSchemaError(Expected string\n at ["n"]). Code that matches the old strings must be updated. Runtime.runrejects with the squashedCause(Effect 4'srunPromise): a refusal rejects with theRefusalitself, soRuntime.isRefusal(rejection)now holds.
Changed
Runtime.refusalOfno longer unwraps aFiberFailure(Effect 4 has none); it still finds the refusal in aCauseor as the rejection itself.- Internal: the registry keeps a built
Contextinstead of an EffectRuntime.isolated(the caller-isolation helper) is removed fromRuntime.make's layer build, where Effect 4 restores the fiber's Context after a build and has noFiberRef; it stays for the scope close at termination and insettleWork. The architecture explanation changed to match. - The event bus closes each subscription with its own scope, and
Stateends its streams by shutting down its ref'sPubSub, so closing ends streams normally, as before. Event.subscribesubscription setup is uninterruptible between making its scope and registering the finalizer, so an interruption cannot leave an unclosed subscription.
Upgrade
Move the application to effect@^4.1.0 on Node 22.19 or later. Replace Effect 3 names in code that touches NEXUS's types (Schema.Schema to Schema.Codec, ParseError to SchemaError, Context.Tag to Context.Service), and update any match on validation issue strings. Other behavior is unchanged.
Verification
pnpm typecheck, pnpm test (38 files, 543 tests) and pnpm build pass; pnpm pack produces valancex-nexus-0.18.0.tgz containing dist/index.js.
Release steps
Valance and Port consume the unreleased pack through local packs and overrides in commits marked DO NOT MERGE BEFORE THE RELEASES:. After NEXUS 0.18.0 is published, revert those, then tag v0.18.0.
NEXUS v0.17.0
NEXUS v0.17 release notes
v0.17.0
Prepared 2026-10-31 (nothing tagged or published: v0.16.0 is the latest release).
v0.17.0 is a minor release that shortens the first app (Tier 1; north-star audits T1.1 and T1.2) and widens the schema types of Command and Event (Tier 1 for the widening, Tier 2 for validate). All changes are additive: omitted arguments, defaulted type parameters, an optional options argument and a new cause field.
Runtime.make()needs no layer. The layer defaults toLayer.empty, so an application with no services yet writesRuntime.make(). (T1.1)Application.define({ name })needs noruntime. The input may omit it. The storedApplicationDefinitionstill hasruntime(filled withLayer.empty), so code that readsapp.definition.runtimeis unchanged. (T1.2)- Transforming schemas for
CommandandEvent.Command.defineandEvent.defineacceptSchema.Schema<A, I>(for exampleSchema.NumberFromString).CommandandEventDefgain a defaulted last type parameter (Encoded = A), so existing references such asEventDef<Tag, P>compile unchanged.Command.invoketakes the encoded input and the handler receives the decoded value.Stateis unchanged. (Tier 1; audit I2 ◐→P, with Valance B3) Event.subscribe(def, { validate: true })(Tier 2; audit I2). Opt-in: each payload is checked against the schema's decoded side (the bus carries decoded values;publishtakes the decoded payload and never decodes), and a failing payload ends the stream with aParseError. The default is unchanged.CommandValidationError.cause. The structuredParseErrornow sits besideissues, which is unchanged.examples/hello: the smallest runnable app, with no Service, Layer or Resource. The README's first app drops itsLayer.emptyargument.- Docs: the shutdown statements in
ARCHITECTURE.md,primitives/capability.mdandprimitives/runtime.mdthat said termination does not wait for effects started withrunorrunForkare corrected: it waits up to the grace, then interrupts them (as since v0.12).
MESH 0.18: the occurrence rename and the context channel
Lands together with MESH 0.18.0 (prepared, not yet published) and Valance Task 19. The peer range gains ^0.18.0; 0.14 to 0.17 stay in it.
- Occurrence, not instance (Tier 2; refinements C-4, Valance K30). MESH 0.18 names one occurrence of a component with a unit
occurrence(Render.occurrences,intent.occurrence) where 0.15 to 0.17 saidinstance.Mesh.UnitOccurrenceandMesh.CommandIntentgain an optionaloccurrence(MESH 0.15 to 0.17 do not give it: readoccurrence ?? instanceto support both); theirinstancestays, marked@deprecated, because it is the only name an older MESH gives and MESH 0.18 keeps it as a non-enumerable alias for one release.Mesh.raise's second parameter is namedoccurrence(a positional rename: no call changes). The adapter passes both through and renames nothing itself. A host that matched the diagnosticruntime-unknown-instancemust matchruntime-unknown-occurrence(MESH retired the old code). - The context channel (Tier 2; refinements C-1, Valance K27). A value many distant components need (a theme, a locale, the current user), declared once in the manifest's
contextand read only by a component that lists it inuses. The host holds the values and the adapter hands them to the runtime beside the snapshot, as it does units:Mesh.render(program, snapshot, units?, context?),Mesh.update(previous, snapshot, units?, context?)(withoutcontext, MESH keeps the previous render's) andMesh.host({ ..., context }), anEffectread each time the host renders. Likeunits, a change of context alone does not render: the host renders when its scope commits. New typeMesh.ContextValues. A program whose manifest has nocontextneeds none of it, and an absent input is not passed, so an older MESH is called exactly as before. - Scorecard: M2 (one term per concept) ◐ → ◐: the unit sense of "instance" is gone from the adapter's names, other collisions remain. C1 (a value reaches a deep component without threading) Fail → Pass and C2 (the channel is explicit and checkable) not applicable → Pass, with MESH 0.18 (the adapter is the pass-through; the check is MESH's). No audit regresses.
- Tests:
tests/mesh-context.test.ts(render, update and host with context; a missing required value is MESH's diagnostic);tests/mesh-units.test.tsreadsoccurrences/occurrenceand checks the deprecated alias passes through. Themesh-sliceintent fixture is re-copied from MESH 0.18, whose intent carries argument names.
Behavior
- Duplicate event tags are refused (audit I2). Each runtime registers an
EventDef's tag at its firstpublishorsubscribe; a second, distinctEventDefwith the same tag dies with aRuntime.Refusal(code: "duplicate-event-tag", a newRefusalCode). This closes the documented_tagcross-delivery hazard, so a subscription only receives its own def's payloads. It refuses only programs that already had that hazard; one def used repeatedly, and distinct tags, are unchanged. In a long-lived runtime, an event re-created on hot reload (a re-evaluated module) is refused: define eachEventDefonce in a module that is not re-evaluated, or restart the runtime on reload. Lands together with Valance Task 25.
Compatibility
- API and behavior: every existing call is unchanged, with one behavior change: a program that registers two distinct
EventDefs under one tag is now refused (see "Behavior"). Beyond that, only omitted arguments, new defaulted type parameters (Encoded, andXonMesh.bind), the optionalsubscribeoptionsargument, the newCommandValidationError.causefield, and the optionalcontextargument and option onMesh.render,Mesh.updateandMesh.hostare newly accepted.occurrenceis added beside the deprecatedinstanceonMesh.UnitOccurrenceandMesh.CommandIntent. - MESH: needs MESH 0.18.0 for
occurrenceandcontext; the peer range still admits^0.14.0to^0.17.0, where everything else works unchanged (withoutcontext, no context argument is passed). RefusalCodegains a member. The StableRuntime.RefusalCodeunion gains"duplicate-event-tag", so an exhaustiveswitchover it (one with aneverdefault) stops compiling until it handles the new code.
Release steps
After MESH 0.18.0 is on npm: revert 538d8e2 (the local packs and pnpm-workspace.yaml overrides), run pnpm install, then pnpm typecheck, pnpm test and pnpm build, then tag v0.17.0.
NEXUS v0.16.0
NEXUS v0.16 release notes
v0.16.0
Prepared 2026-10-24 (nothing tagged or published: v0.15.0 is the latest release).
v0.16.0 is a minor release with one change: the @valancex/mesh-runtime peer range is ^0.14.0 || ^0.15.0 || ^0.16.0 || ^0.17.0. MESH 0.17 adds checking a source while it arrives (a stream session in @valancex/mesh-compiler) and the effect and title of a command in the manifest; v0.15.0's range stops at 0.16, so the newest set would not install together. Nothing in the adapter changes: the runtime's render, update and dispatch are those of 0.16.
Compatibility
- API and behavior: unchanged. Install: wider (an application on MESH 0.14 to 0.17).
- Tier: not a public-API change; no audit of the north-star scorecard is touched.
Release order
MESH 0.17.0, NEXUS 0.16.0, PORT 0.14.0, then the VALANCE packages.
Verification
pnpm typecheck, pnpm test and pnpm build against MESH 0.16.0 (the development dependency) and again against the local pack of MESH 0.17.0 (a temporary override, not committed); installs under pnpm 10 and 11 with --frozen-lockfile; npm publish --dry-run of the packed tarball.
NEXUS v0.15.0
NEXUS v0.15 release notes
v0.15.0
Prepared 2026-10-24 (nothing tagged or published: v0.14.0 is the latest release).
v0.15.0 is a minor release with one change: the @valancex/mesh-runtime peer range is ^0.14.0 || ^0.15.0 || ^0.16.0. MESH 0.16 is published and v0.14.0's range stopped at 0.15, so the newest published set did not install together (the budgets' installability check, Bet 3: npm refused @valancex/mesh-runtime@0.16.0 beside @valancex/nexus@0.14.0). Nothing in the adapter changes: MESH 0.16 adds no runtime or compiler behavior (its release is the session-log vectors of @valancex/mesh-conformance).
Compatibility
- API and behavior: unchanged. Install: wider (an application on MESH 0.14, 0.15 or 0.16).
- Tier: not a public-API change; no audit of the north-star scorecard is touched.
Release order
NEXUS 0.15.0, PORT 0.13.0, then the VALANCE packages (whose ranges admit MESH 0.16).
Verification
pnpm typecheck, pnpm test (33 files, 517 tests, against MESH 0.16.0 as the development dependency) and pnpm build; installs under pnpm 10 and 11 with --frozen-lockfile (pnpm 11 needs pnpm-workspace.yaml, which exempts the two MESH 0.16.0 packages from the release age until a day has passed); npm publish --dry-run of the packed tarball.
NEXUS v0.14.0
NEXUS v0.14 release notes
v0.14.0
Prepared 2026-10-23 (nothing tagged or published: v0.13.0 is the latest release).
v0.14.0 is a minor release. The MESH adapter (Mesh) gains what a host needs to give a component state of its own for one occurrence of it (MESH 0.15's units; direction Bet 8). Nothing else changes.
Changes
Mesh.render(program, snapshot, units?), a plain Effect for rendering a snapshot (the host's scope selector is not needed for it), andunitsonMesh.update(previous, snapshot, units?).unitsis the state the host holds for the unit occurrences, by the occurrence's key; it is passed to the runtime as it is. A render lists the occurrences it met (render.instances).unitsonMesh.host, an optional Effect read each time the host renders. Without it, and with a program that has no units, nothing differs.Mesh.raise(render, instance, event, payload?)resolves an event a unit announces to the intent of the handler its caller bound (undefinedwhen it bound none).- A binding receives the intent as its second argument:
(args, intent) => Effect. A binding that takes one parameter is unchanged. A unit's command arrives withintent.instance(the occurrence it is for) andintent.controlled. - The
@valancex/mesh-runtimepeer range is^0.14.0 || ^0.15.0. Units need 0.15; the rest works on 0.14. The adapter keeps no state of the occurrences (invariant M2): the host holds it and decides when an occurrence's ends.
Compatibility
- API: additive. Behavior: unchanged for a program with no unit. Install: wider (an application on MESH 0.14 or 0.15).
- Tier: Tier 2 (opt-in; a default that is correct without it). The mechanism stays in the host (VALANCE), below the adapter.
Release order
MESH 0.15.0, NEXUS 0.14.0, then the VALANCE packages (which depend on both for units).
Verification
pnpm typecheck, pnpm test and pnpm build, twice. Against MESH 0.14 from the registry (this adapter builds and runs on it): 513 tests pass and the 4 in tests/mesh-units.test.ts are skipped, by name, because that runtime has no raise. Against MESH 0.15's build in its place: 517 tests (33 files), 516 pass; the one that does not is the check that the lockfile names the installed runtime, which it cannot until the lockfile is regenerated after MESH 0.15 is published. The development dependencies are still the registry's MESH 0.14 until 0.15 is published (a DO NOT MERGE commit raises them, reverted on the branch). npm publish --dry-run of the packed tarball passes.
NEXUS v0.13.0
NEXUS v0.13 release notes
v0.13.0
Prepared 2026-10-18 (nothing tagged or published: v0.12.3 is the latest release).
v0.13.0 is a minor release. It changes no API and no behavior of the runtime. It changes which MESH it is installed with.
Changes
- The
@valancex/mesh-runtimepeer range is^0.14.0. It was^0.10.0 || ^0.11.0 || ^0.12.0. A 0.x caret admits one minor only, so an application on MESH 0.14 (enums, presence tests and defaults, snippets) was told it had an unmet peer. The range is now the one range@valancex/common,@valancex/valanceand this package share (the Valance direction work's decision W20), so a plain install has one WebAssembly runtime, not two. The development dependencies are MESH 0.14 (@valancex/mesh-compilerand@valancex/mesh-runtime^0.14.0).
Compatibility
- API: unchanged. Behavior: unchanged.
- Install: narrower. An application on MESH 0.10, 0.11, 0.12 or 0.13 now has an unmet peer with this version; raise MESH to 0.14 with it (
pnpm add @valancex/mesh-runtime@^0.14.0), or stay on NEXUS 0.12.3. That is why this is a minor release and not a patch. - VALANCE, PORT: the VALANCE packages move to NEXUS
^0.13.0when it is released; PORT Web 0.10 accepts MESH 0.6 to 0.14.
Release order
MESH 0.14.0 (released), NEXUS 0.13.0, PORT Web 0.10.0, then the VALANCE packages.
Verification
pnpm typecheck, pnpm test (513 tests, 32 files) and pnpm build against MESH 0.14.0 from the registry. The range is pinned by tests/mesh-runtime-compat.test.ts (every alternative is ^0.minor.patch; the installed runtime's minor is admitted; one runtime in the lockfile). npm publish --dry-run of the packed tarball passes. Not verified: the VALANCE packages against NEXUS 0.13 (they are not released against it yet).
NEXUS v0.12.3
NEXUS v0.12.3 release notes
Prepared 2026-10-13 (nothing tagged or published: v0.12.2 is the latest release).
v0.12.3 is a patch release. It changes no API and no behavior of the runtime.
Changes
- The
@valancex/mesh-runtimepeer range accepts MESH 0.11 and 0.12. It was^0.10.0; it is now^0.10.0 || ^0.11.0 || ^0.12.0. A 0.x caret admits one minor only, so an application on MESH 0.11 (blocks:{#if}and{#each}replace themesh-ifandmesh-eachelements) or on MESH 0.12 (formatting in the language server) got a peer warning, and with VALANCE 0.9 or later, which requires the 0.11 line, two runtimes could be installed. With the new range there is exactly one, the one the application supplies and initializes. - The tests use blocks. The keyed-repeat test in
tests/mesh-runtime-compat.test.tswrites its repeat as{#each ids as item (item.id)}, and the development dependencies are MESH 0.11 (@valancex/mesh-compilerand@valancex/mesh-runtime^0.11.0).
Compatibility
- API: unchanged. Behavior: unchanged.
- Install: additive. An application on MESH 0.10 keeps working; MESH 0.11 and 0.12 are now accepted. Declare the runtime yourself (
pnpm add @valancex/mesh-runtime@^0.12.0). - MESH 0.11 and later reject the
mesh-if,mesh-eachandmesh-switchelements (retired-element): templates passed toMesh.hostthat use them must be rewritten as blocks, in the MESH upgrade, not in NEXUS. - VALANCE, PORT: VALANCE 0.10 and PORT Web 0.4.2 accept the same MESH lines.
Release order
MESH 0.12.0, then NEXUS 0.12.3, PORT Web 0.4.2, VALANCE 0.10.0 (see the VALANCE repository's docs/releases/release-set-2026-10-b.md).
Verification
pnpm typecheck, pnpm test (513 tests), against MESH 0.11 from the registry. The range itself is pinned by tests/mesh-runtime-compat.test.ts (every alternative is ^0.minor.patch; the installed runtime's minor is admitted; one runtime in the lockfile).
NEXUS v0.12.2
NEXUS v0.12.2 release notes
Prepared 2026-10-10 (nothing tagged or published).
v0.12.2 is a patch release. It changes no API and no behavior of the runtime.
Changes
"sideEffects": false.@valancex/mesh-runtime,@valancex/mesh-compilerand@valancex/port-webalready say that importing them does nothing; NEXUS did not, so a bundler had to keep, and evaluate, every NEXUS module that anything imported, even when the importing code used none of it. That put NEXUS into the bundle of a page that only needed a function from a package that happens to import it (VALANCE's entries are the case that found it). Nothing insrcchanges a global or the environment on import (Symbol.forfor the refusal marker is a registry lookup), andtests/package-json.test.tsnow checks both the declaration and that claim.
NEXUS v0.12.1
NEXUS v0.12.1 release notes
Prepared 2026-10-10.
v0.12.1 is a patch release. It changes no API and no behavior of the runtime.
Changes
@valancex/nexus/package.jsonis exported. Every other package in the set (mesh-runtime,mesh-compiler,port-web,valance) lets a tool read its manifest throughrequire.resolve("<name>/package.json"); NEXUS'sexportsmap did not, so a version or peer-range check failed on it withERR_PACKAGE_PATH_NOT_EXPORTED. Found by validating the released set against the API design principles. A test pins it.
NEXUS v0.12.0
NEXUS v0.12.0 release notes
Prepared 2026-10-10 (not yet released: v0.11.0 is the latest release).
v0.12.0 is a minor release. It adds Runtime.refusalOf, Mesh.update and a shutdown policy for the work a runtime started (shutdown.grace), fixes one behavior of Selector.combine(...).changes, and moves the peer range of @valancex/mesh-runtime to MESH 0.10.
Changes
Runtime.refusalOf(value)returns theRuntime.Refusalinvalue, orundefined.valuemay be aRefusal, aCause, or theFiberFailurethat the PromiseRuntime.runreturns rejects with, so a caller who raced a shutdown can writeRuntime.refusalOf(error)?.code === "terminating"instead of unwrapping the failure by hand. It recognizes a refusal by its shape, asisRefusaldoes, so it works across two installed copies of the package. Anything else, a bug included, givesundefined.Selector.combine(...).changesemits on the first commit of either input. It waited for both inputs to have committed before emitting, so a combined selector whose inputs commit at different times missed the first values of the earlier one. This is a bug fix and a behavior change: a consumer that counted emissions will see one more.Mesh.update(previous, snapshot)is MESH 0.10'supdatethrough the adapter: it returns{ render, patches }, the new render and therender-patch-v1operations a renderer applies in place, or fails withMeshDiagnostics(andpreviousis untouched and still dispatches). Likerender, it is the runtime's operation and nothing more: the adapter keeps no render (M2), a rejection from the runtime is a defect, andpreviousmust be a render of the same program, which only the caller knows. New types:Mesh.UpdatedandMesh.RenderPatches. VALANCE uses it to draw only what changed.@valancex/mesh-runtimepeer range is^0.10.0(was^0.8.0 || ^0.9.0).Mesh.updateneeds 0.10'supdate, and a package that names an export the installed runtime lacks does not load, so an application on MESH 0.8 or 0.9 stays on NEXUS 0.11.
Shutdown: the work a runtime started
Until now, ending a runtime released its resources while the effects started with Runtime.run and Runtime.runFork kept running, so an effect could be using a Resource that was released under it, and nothing waited for it. Termination now settles that work:
- Tracked. An effect started with
runorrunForkis the runtime's work from the moment it starts until it exits, however it exits. - Settled. After new work is refused and the bus is closed, termination waits up to the grace for the work to finish on its own, then interrupts what is left and waits for each effect to exit, so it has run its own finalizers before any resource of the runtime is released. The grace is
shutdown: { grace }onRuntime.makeandApplication.start, orApplication.shutdown(running, { grace })for one call. It defaults to0: interrupt at once, after one turn of the event loop in which a subscription that ended with the bus ends its consumer.Duration.infinitywaits for everything. - Streams end first. The
changesandvaluesof application-ownedState(and aSelectorover it) now end when termination begins to wind the runtime down, so a consumer started withrunends normally instead of being interrupted to make room for a release that waits for it. - A command may end its application. The effect that asks for the shutdown is not waited for or interrupted.
- What a caller sees. An interrupted
runrejects with Effect's interruption (Cause.isInterruptedOnly;Runtime.refusalOffinds nothing), an interruptedrunForkfiber exits interrupted, and work started during the grace is refused withRefusalcodeterminating. - Behavior change. An effect that used to outlive termination is now interrupted. Nothing else about the order of termination changed. Code that needs such an effect to finish passes a grace, or awaits it before ending the runtime.
Compatibility
- API: additive. No export is removed or changed.
- Install: breaking for an application on MESH 0.8 or 0.9, which must move to MESH 0.10 with NEXUS 0.12 (the peer range). Nothing else about installing changes.
- Behavior: the
Selector.combine(...).changeschange and the shutdown change above; nothing else. - MESH, PORT, VALANCE: VALANCE can use
refusalOfonce it depends on this version. - API stability:
Runtime.refusalOfand thecombinebehavior are Stable from 0.12.0.