Skip to content

Releases

NEXUS v0.18.0

Choose a tag to compare

@github-actions github-actions released this 06 Nov 11:17
66ce65a

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. effect is 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.runFork returns Fiber.Fiber<A, E> (was Fiber.RuntimeFiber).
    • Service<Shape> and Capability.Environment are Context.Service<Shape, Shape> (was Context.Tag).
    • Schemas in Command, Event, State and Application are Schema.Codec<A, I> (was Schema.Schema<A, I>).
    • CommandValidationError.cause and a validated Event.subscribe fail with Schema.SchemaError (was ParseResult.ParseError).
    • EventBusShape.subscribe yields a PubSub.Subscription (was Queue.Dequeue).
    • ShutdownOptions.grace is a Duration.Input (was Duration.DurationInput).
  • Validation issue strings change. CommandValidationError.issues and StateValidationError.issues are now Effect 4's String(SchemaError) format, for example SchemaError(Expected string\n at ["n"]). Code that matches the old strings must be updated.
  • Runtime.run rejects with the squashed Cause (Effect 4's runPromise): a refusal rejects with the Refusal itself, so Runtime.isRefusal(rejection) now holds.

Changed

  • Runtime.refusalOf no longer unwraps a FiberFailure (Effect 4 has none); it still finds the refusal in a Cause or as the rejection itself.
  • Internal: the registry keeps a built Context instead of an Effect Runtime. isolated (the caller-isolation helper) is removed from Runtime.make's layer build, where Effect 4 restores the fiber's Context after a build and has no FiberRef; it stays for the scope close at termination and in settleWork. The architecture explanation changed to match.
  • The event bus closes each subscription with its own scope, and State ends its streams by shutting down its ref's PubSub, so closing ends streams normally, as before.
  • Event.subscribe subscription 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

Choose a tag to compare

@github-actions github-actions released this 31 Oct 06:41
c9543ce

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 to Layer.empty, so an application with no services yet writes Runtime.make(). (T1.1)
  • Application.define({ name }) needs no runtime. The input may omit it. The stored ApplicationDefinition still has runtime (filled with Layer.empty), so code that reads app.definition.runtime is unchanged. (T1.2)
  • Transforming schemas for Command and Event. Command.define and Event.define accept Schema.Schema<A, I> (for example Schema.NumberFromString). Command and EventDef gain a defaulted last type parameter (Encoded = A), so existing references such as EventDef<Tag, P> compile unchanged. Command.invoke takes the encoded input and the handler receives the decoded value. State is 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; publish takes the decoded payload and never decodes), and a failing payload ends the stream with a ParseError. The default is unchanged.
  • CommandValidationError.cause. The structured ParseError now sits beside issues, which is unchanged.
  • examples/hello: the smallest runnable app, with no Service, Layer or Resource. The README's first app drops its Layer.empty argument.
  • Docs: the shutdown statements in ARCHITECTURE.md, primitives/capability.md and primitives/runtime.md that said termination does not wait for effects started with run or runFork are 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 said instance. Mesh.UnitOccurrence and Mesh.CommandIntent gain an optional occurrence (MESH 0.15 to 0.17 do not give it: read occurrence ?? instance to support both); their instance stays, 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 named occurrence (a positional rename: no call changes). The adapter passes both through and renames nothing itself. A host that matched the diagnostic runtime-unknown-instance must match runtime-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 context and read only by a component that lists it in uses. 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?) (without context, MESH keeps the previous render's) and Mesh.host({ ..., context }), an Effect read each time the host renders. Like units, a change of context alone does not render: the host renders when its scope commits. New type Mesh.ContextValues. A program whose manifest has no context needs 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.ts reads occurrences/occurrence and checks the deprecated alias passes through. The mesh-slice intent 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 first publish or subscribe; a second, distinct EventDef with the same tag dies with a Runtime.Refusal (code: "duplicate-event-tag", a new RefusalCode). This closes the documented _tag cross-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 each EventDef once 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, and X on Mesh.bind), the optional subscribe options argument, the new CommandValidationError.cause field, and the optional context argument and option on Mesh.render, Mesh.update and Mesh.host are newly accepted. occurrence is added beside the deprecated instance on Mesh.UnitOccurrence and Mesh.CommandIntent.
  • MESH: needs MESH 0.18.0 for occurrence and context; the peer range still admits ^0.14.0 to ^0.17.0, where everything else works unchanged (without context, no context argument is passed).
  • RefusalCode gains a member. The Stable Runtime.RefusalCode union gains "duplicate-event-tag", so an exhaustive switch over it (one with a never default) 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

Choose a tag to compare

@github-actions github-actions released this 24 Oct 16:53
bd8c464

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

Choose a tag to compare

@github-actions github-actions released this 24 Oct 08:06
b773943

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

Choose a tag to compare

@github-actions github-actions released this 23 Oct 10:51
55b28d6

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), and units on Mesh.update(previous, snapshot, units?). units is 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).
  • units on Mesh.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 (undefined when 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 with intent.instance (the occurrence it is for) and intent.controlled.
  • The @valancex/mesh-runtime peer 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

Choose a tag to compare

@github-actions github-actions released this 20 Oct 06:49
21abe99

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-runtime peer 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/valance and 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-compiler and @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.0 when 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

Choose a tag to compare

@github-actions github-actions released this 14 Oct 01:13
b49b740

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-runtime peer 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 the mesh-if and mesh-each elements) 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.ts writes its repeat as {#each ids as item (item.id)}, and the development dependencies are MESH 0.11 (@valancex/mesh-compiler and @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-each and mesh-switch elements (retired-element): templates passed to Mesh.host that 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

Choose a tag to compare

@github-actions github-actions released this 10 Oct 12:05
e09790c

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-compiler and @valancex/port-web already 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 in src changes a global or the environment on import (Symbol.for for the refusal marker is a registry lookup), and tests/package-json.test.ts now checks both the declaration and that claim.

NEXUS v0.12.1

Choose a tag to compare

@github-actions github-actions released this 10 Oct 05:36
5e3ec35

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.json is exported. Every other package in the set (mesh-runtime, mesh-compiler, port-web, valance) lets a tool read its manifest through require.resolve("<name>/package.json"); NEXUS's exports map did not, so a version or peer-range check failed on it with ERR_PACKAGE_PATH_NOT_EXPORTED. Found by validating the released set against the API design principles. A test pins it.

NEXUS v0.12.0

Choose a tag to compare

@github-actions github-actions released this 10 Oct 04:40
5069c44

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 the Runtime.Refusal in value, or undefined. value may be a Refusal, a Cause, or the FiberFailure that the Promise Runtime.run returns rejects with, so a caller who raced a shutdown can write Runtime.refusalOf(error)?.code === "terminating" instead of unwrapping the failure by hand. It recognizes a refusal by its shape, as isRefusal does, so it works across two installed copies of the package. Anything else, a bug included, gives undefined.
  • Selector.combine(...).changes emits 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's update through the adapter: it returns { render, patches }, the new render and the render-patch-v1 operations a renderer applies in place, or fails with MeshDiagnostics (and previous is untouched and still dispatches). Like render, it is the runtime's operation and nothing more: the adapter keeps no render (M2), a rejection from the runtime is a defect, and previous must be a render of the same program, which only the caller knows. New types: Mesh.Updated and Mesh.RenderPatches. VALANCE uses it to draw only what changed.
  • @valancex/mesh-runtime peer range is ^0.10.0 (was ^0.8.0 || ^0.9.0). Mesh.update needs 0.10's update, 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 run or runFork is 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 } on Runtime.make and Application.start, or Application.shutdown(running, { grace }) for one call. It defaults to 0: interrupt at once, after one turn of the event loop in which a subscription that ended with the bus ends its consumer. Duration.infinity waits for everything.
  • Streams end first. The changes and values of application-owned State (and a Selector over it) now end when termination begins to wind the runtime down, so a consumer started with run ends 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 run rejects with Effect's interruption (Cause.isInterruptedOnly; Runtime.refusalOf finds nothing), an interrupted runFork fiber exits interrupted, and work started during the grace is refused with Refusal code terminating.
  • 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(...).changes change and the shutdown change above; nothing else.
  • MESH, PORT, VALANCE: VALANCE can use refusalOf once it depends on this version.
  • API stability: Runtime.refusalOf and the combine behavior are Stable from 0.12.0.