XMachines 6.0.0: @xmachines/core, and a plain release
XMachines 6.0.0 adds @xmachines/core, the base of every other package. The release protocol moves into it, and it becomes opt-in: a subscription hands back a plain release, and asDisposable makes that release a Disposable. The release also moves the renderers to json-render 0.21.
The new base package: @xmachines/core
@xmachines/core holds code that more than one package uses, and that belongs to no layer of the Play Architecture. It has no root entry. It has three subpaths:
@xmachines/core/errors:BaseError, the base class of every typed error, andDisposeConflictError.@xmachines/core/utils: the release protocol, and small helpers for values and errors.@xmachines/core/cache: theKeyValueCachecontract, andcreateLruCache.
@xmachines/core is a peer of @xmachines/play, play-pattern, play-url, play-router, play-view, play-xstate, play-dom, play-react and play-tanstack-router. Install it with them:
pnpm add @xmachines/coreCaution: install one copy only. err instanceof BaseError answers false for an error of a second copy.
Every typed error extends BaseError
PlayError now extends BaseError, so err instanceof BaseError matches every typed error of the packages. Each error has two stable fields: scope, the class or module that threw it, and code, a machine-readable identifier.
import { BaseError } from "@xmachines/core/errors";
try { start();} catch (err) { if (err instanceof BaseError) { console.error(`[${err.scope}:${err.code}] ${err.message}`); } else { throw err; }}Branch on the class or on code, and never on message.
The release protocol becomes opt-in
From 3.0.0 to 5.0.0, a subscription handed back a Cleanup: a function that was also a Disposable. Each package had to build that value, and each test double had to write the dispose key.
In 6.0.0 these values are plain again. They hand back a release function, or an object with a release method, and no dispose key:
watchAtomof@xmachines/play-atom.BrowserHistory.subscribe,createBrowserHistory,createRouterandconnectRouterof@xmachines/play-dom-router.MountFnandcreateRendererof@xmachines/play-dom.PlayerActorof@xmachines/play-xstate.
A caller that wants a using scope publishes the release with asDisposable of @xmachines/core/utils. A caller that does not want one pays nothing.
import { asDisposable } from "@xmachines/core/utils";
// A release function: the function is its own release.{ using stop = asDisposable(watchAtom(count, render)); render(count.get());}
// An object: name its release.{ const plain = createBrowserHistory({ window }); using history = asDisposable(plain, () => plain.destroy()); history.push("/first");} // destroy() runs here
// An actor: publish its stop().{ const actor = definePlayer({ machine })(); using _stop = asDisposable(() => { actor.stop(); }); actor.start();} // stop() runs here, and after an exception tooasDisposable(target, release) throws DisposeConflictError when the target already has a dispose member that is not the release. A second call with the same release changes nothing.
@xmachines/play no longer exports Cleanup, asCleanup, DISPOSE or DisposeKey, and no other package re-exports the protocol. Import asDisposable, DISPOSE and DisposeKey from @xmachines/core/utils. A class that implements the protocol itself writes its release under DISPOSE, and not under a bare Symbol.dispose. A bundler that downlevels using reads the same key pair that DISPOSE names.
The cache moves to @xmachines/core/cache
PatternCache and createPatternCache are removed from @xmachines/play-pattern, @xmachines/play-url and @xmachines/play-router. Use KeyValueCache and createLruCache. The three packages re-export them.
// 5.0new RouteMap(mappings, { pathCache: createPatternCache(5000) });
// 6.0new RouteMap(mappings, { pathCache: createLruCache(5000) });Helpers in @xmachines/core/utils
shallowEqualExcept moves from @xmachines/play to @xmachines/core/utils. The subpath also holds new helpers that the packages share:
releaseQuietly(release, onFailure)runs one release, and gives a throw toonFailure. A teardown of several releases then runs each one.rethrowLater(error)throws an error again from a microtask of its own. A host escalates a failure this way from theonErrorhandler of a renderer.isError(value)tells if a value is anErrorof any realm, also from an iframe or fromnode:vm.safeString(value)anddescribeValue(value)put an unknown value in a message, and neither one throws.isThenable(value),createNullRecord()andcreateWarnOnce().
json-render 0.21
The renderers and @xmachines/play-view need the @xmachines/json-render-* packages ^0.21.0-xm.1 as peers. Install json-render 0.21 with them.
Upgrade
- Install
@xmachines/core, one copy. - Replace
CleanupandasCleanupwithasDisposableof@xmachines/core/utils. Wrap each value that ausingdeclaration holds. - Import
DISPOSE,DisposeKeyandshallowEqualExceptfrom@xmachines/core/utils. - Replace
createPatternCachewithcreateLruCache, andPatternCachewithKeyValueCache. - Move the
@xmachines/json-render-*packages to^0.21.0-xm.1.
The previous release is 5.0.0. The getting started guide of 6.0 lists the packages to install.
The changelog of 6.0.0 lists every change of the release.