@praxisjs/core
Changelog for @praxisjs/core — signals, computed values, effects, peek, untrack, and batch.
@praxisjs/core
1.8.2
Internal: _setProps consolidated into RootComponent.
_setProps(props) was implemented identically in both StatefulComponent and StatelessComponent. It is now defined once on RootComponent and removed from the subclasses. No behavior change.
The framework-internal fields _rawProps, _mounted, _anchor, _defaults, and _stateDirty are now annotated with @internal JSDoc, hiding them from TypeDoc output and IDE autocomplete. No public API change.
1.8.1
Bug fix: dynamic reactive dependencies are cleaned up correctly.
Effects now unsubscribe from dependencies they no longer read when they rerun or stop. Computed values also drop stale branch subscriptions before recomputing, so conditional dependencies do not keep invalidating values after the active branch changes.
Signal notifications now iterate over a stable subscriber snapshot, ensuring that subscription changes during notification do not skip remaining listeners.
Bug fix: keyed resources keep cache and in-flight state consistent.
Calling mutate() on a resource with a key now writes the optimistic value into the shared cache, so future resources with the same key start from the mutated data. Settled keyed requests also clear their shared in-flight entry even if the local result was made stale by cancel().
1.8.0
onError can now return fallback DOM (error boundary).
onError(error: Error) now has return type Node | Node[] | null | void. When onError returns a node or array of nodes, the runtime mounts them in place of the failed component output. Return null or void to render nothing (previous behavior is unchanged).
onError(err: Error) {
return <p class="error-fallback">Failed to load: {err.message}</p>
}See Lifecycle Hooks — onError for full usage.
writableComputed(getter, setter) — new internal primitive.
Added writableComputed<T>(getter: () => T, setter: (value: T) => void): WritableComputed<T> to @praxisjs/core/internal. It wraps a computed() with set and update methods, returning a WritableComputed<T>. Used internally by @Computed({ set }) in @praxisjs/decorators.
Also added WritableComputed<T> type to @praxisjs/shared — extends Computed<T> with set(value: T): void and update(updater: (prev: T) => T): void.
1.7.0
resource() — cache, deduplication, stale-while-revalidate, refetch on focus.
New options on ResourceOptions:
key?: string— enables a global shared cache for this resource. All instances sharing the same key reuse in-flight requests (deduplication) and read from the same cache entry.staleTime?: number— how long (ms) a cached entry is considered fresh. While fresh, the initial fetch is skipped. Default0means always stale: cached data is shown immediately while a background fetch runs (stale-while-revalidate).refetchOnFocus?: boolean— whentrue, refetches wheneverdocument.visibilityStatebecomes"visible". No-op in non-browser environments.
New Resource<T> method:
destroy()— stops reactive effects, removes the focus listener, and unregisters from the cache registry. Called automatically by the@Resourcedecorator on component unmount.
New export from @praxisjs/core/internal:
invalidateResource(key)— clears the cache entry forkeyand triggers an immediate refetch on every registered resource under that key._clearCache()— resets all cache state (tests only).
Internal: removed a redundant clearTimeout call from debounced() — the effect cleanup already handles it on every re-run.
1.6.0
Reactivity engine rewrite — lower allocation and cleaner internals. No API changes.
signal()— subscribers stored asnull | Effect | Effect[], avoidingSetallocation for zero- and single-subscriber casescomputed()— plain closure, lazy recompute callback, separate tracking for downstream computeds vs leaf effectseffect()— simplified to a named closure withrunandstopbatch()— pre-allocated module-level array instead of a newSetper call
1.5.0
New: syncedSignal(channelName, initialValue) — signal that stays in sync across browser tabs via BroadcastChannel. Writes in any tab broadcast to all others.
1.4.1
@Watchmulti-prop coalescing — when multiple props change in the same tick, the callback fires once with the final values- Computed notification coalescing — leaf subscribers notified once per microtask boundary when multiple dependencies change simultaneously
- TypeScript fix —
createLifecycleMethodDecoratornow accepts methods with typed parameters
1.4.0
StatelessComponent default type parameter changed from object to Record<never, never>. Components that only receive children no longer need a type argument.
1.3.0
StatelessComponent now exposes an optional typed children prop. this.props.children works without declaring it in T.
1.2.0
New: peek(signal) and untrack(fn) — promoted to the public API.
Bug fix: mountComponent now runs inside untrack(). Static reads like description={this.count} inside a reactive context (e.g. the router) were accidentally subscribing the outer effect to those signals, causing the component to re-mount on every state change.
1.1.0
debounced()returns a.stop()method to cancel the pending timer- Nested
batch()calls no longer overwrite the outer queue - Subscriber errors are now isolated — all subscribers run even when one throws
1.0.0
Breaking
All functional APIs replaced by decorators across the entire ecosystem.
- Added
Composableabstract base class for class-based composables - Removed
resource,createResource,Resource,ResourceStatus,ResourceOptions— use@Resourcefrom@praxisjs/decoratorsinstead
0.4.2
Bug fix: effect() stop function now prevents future re-runs by setting a stopped flag. Previously it only ran cleanup but left the effect subscribed.
0.4.1
Bug fix: when() no longer leaks its reactive effect when the source is immediately truthy on the first run.
0.4.0
Added @Computed() decorator for cached reactive getters.
0.3.0
Migrated from legacy decorator signatures to the TC39 decorator context API. Introduced StatefulComponent and StatelessComponent.
0.1.0
Initial beta release.