Skip to content

@praxisjs/core

1.2.0

Minor Changes

  • 6c353ba: Add untrack utility and isolate component mounting from outer reactive contexts

    @praxisjs/core exports two new functions from the public API:

    • peek(signal) — reads a signal once without subscribing to it (was already in /internal, now public)
    • untrack(fn) — runs a function with no active effect, suppressing all signal tracking inside it
    ts
    import { peek, untrack } from "@praxisjs/core";
    
    // read a signal without creating a dependency
    if (peek(this.max) > peek(this.count)) {
      this.count++;
    }
    
    // suppress tracking for a block of reads
    const snapshot = untrack(() => this.totalCost);

    @praxisjs/runtimemountComponent now runs entirely inside untrack. This fixes a bug where components mounted inside a reactive context (e.g. the router) would accidentally subscribe the outer effect to any signal read during construction or render. The symptoms were:

    • Eager reads like description={this.count} in JSX causing the router to re-mount the component on every state change, resetting state to its initial value
    • @Debug() (and any decorator that reads a signal in its addInitializer) triggering the same re-mount loop

    Reactive subscriptions set up via {() => signal} in JSX are unaffected — each arrow function creates its own isolated effect.

1.1.0

Minor Changes

  • 029ef04: debounced() now returns a signal with a .stop() method to cancel the pending timer and its effect. Synchronous throws inside a resource fetcher are now caught and set the resource to error state instead of propagating uncaught.

Patch Changes

  • 029ef04: Fix nested batch() calls overwriting the outer queue, and preserve dirty flag in computed signals when the compute function throws.
  • 029ef04: Isolate subscriber errors during signal updates — all subscribers now run even when one throws, and the last error is re-thrown after all have executed.

1.0.0

Major Changes

  • 3372878: Migrate all packages from functional APIs to a decorator-first design.

    @praxisjs/core

    • Added Composable abstract base class for building class-based composables
    • Removed resource, createResource, Resource, ResourceStatus, ResourceOptions from public exports — use @Resource from @praxisjs/decorators instead

    @praxisjs/motion

    • Replaced useMotion, tween, spring, createTransition, Animate, easings, resolveEasing with @Tween and @Spring decorators

    @praxisjs/di

    • Replaced useService and createScope with a @Scope decorator
    • Renamed exported type Scope to ScopeType to free the name for the new decorator

    @praxisjs/fsm

    • Removed createMachine — use the @StateMachine and @Transition decorators directly

    @praxisjs/router

    • Removed createRouter, lazy, useRouter, useParams, useQuery, useLocation
    • Added @RouterConfig, @Lazy, @InjectRouter, @Params, @Query, @Location decorators

    @praxisjs/store

    • Removed createStore — use the @Store and @UseStore decorators directly

    @praxisjs/composables

    • Replaced all use* composable functions with class-based composables extending Composable: WindowSize, ScrollPosition, ElementSize, Intersection, Focus, MediaQuery, ColorScheme, Mouse, KeyCombo, Idle, Clipboard, Geolocation, TimeAgo, Pagination

    @praxisjs/concurrent

    • Removed task, queue, pool and their instance types — use @Task, @Queue, @Pool decorators instead

0.4.2

Patch Changes

  • d11a10a: Fix effect() stop function not preventing re-runs

    The function returned by effect() was only calling the cleanup callback but leaving the effect subscribed to all tracked signals, so it would keep re-running after being stopped. The stop function now sets a stopped flag that makes future invocations of the wrapped effect a no-op, and nullifies the cleanup reference to ensure idempotent behaviour.

    This also fixes $subscribe unsubscription in @praxisjs/store, which relied on this mechanism to detach listeners.

0.4.1

Patch Changes

  • fe39901: fix(core): fix effect subscription leak in when() when source is immediately truthy

    When source() returned a truthy value on the first synchronous effect run, stop was still undefined at that point. The stop?.() call inside the callback was a no-op, leaving the effect subscribed forever — any future change to the source kept triggering the effect (which returned early via disposed, but still maintained its reactive tracking).

    The implementation was refactored to use a ref object ({ cancel }) to hold the cancellation function. This allows stop to be declared as const, removes the optional chain on a non-nullable type, and ensures the effect is properly cancelled via if (disposed) stop() after the first synchronous run.

0.4.0

Minor Changes

  • f52354d: Add @Computed() decorator to @praxisjs/decorators for declaring read-only reactive getters backed by a cached computed() signal. The getter recomputes automatically when any @State or @Prop dependency changes, and the result is cached until a dependency is invalidated — unlike a plain getter which recalculates on every read.

    @Debug() in @praxisjs/devtools now supports @Computed() getters (ClassGetterDecoratorContext) in addition to fields and methods, allowing computed values to be tracked and historized in the devtools panel.

    Also fixes a bug in the computed() primitive where an erroneous track(recompute) call caused premature dependency tracking on signal creation.

0.3.0

Minor Changes

  • bb0d4f8: Refactor decorator system and component architecture across PraxisJS packages

    • Replaced legacy decorator signatures (constructor, target, propertyKey, method descriptor) with the standard TC39 decorator context API (ClassDecoratorContext, ClassFieldDecoratorContext, ClassMethodDecoratorContext) across @praxisjs/decorators, @praxisjs/store, @praxisjs/concurrent, @praxisjs/router, @praxisjs/motion, @praxisjs/di, and @praxisjs/fsm.
    • Introduced StatefulComponent and StatelessComponent as the new base classes, replacing the deprecated BaseComponent/Function Component pattern, across @praxisjs/core, @praxisjs/runtime, @praxisjs/devtools, and templates.
    • Implemented core rendering functionality in @praxisjs/runtime (mountChildren, mountComponent, reactive scope management) and removed the deprecated renderer.ts.
    • Refactored @praxisjs/jsx to delegate rendering to @praxisjs/runtime and improved type safety with flattenChildren and isComponent utilities.
    • Updated internal module structure with new internal exports in package.json files for shared utilities and types.
    • Removed experimentalDecorators/emitDecoratorMetadata from tsconfig.json in favor of native decorator support.

Patch Changes

  • Updated dependencies [bb0d4f8]
    • @praxisjs/shared@0.2.0

0.2.0

Minor Changes

  • f48dbc4: Introduce WithHistory<T, K> utility type for better TypeScript inference when using the @History decorator, and fix performance issues in the history() primitive.

    Changes:

    @praxisjs/decorators: Added WithHistory<T, K> type that maps a property key to its corresponding *History accessor type, enabling proper type-checking on decorated classes. @praxisjs/decorators: Simplified @History decorator internals — replaced verbose getOwnPropertyDescriptor lookups with direct property access (this[propertyKey]), reducing complexity. @praxisjs/core: Fixed history() to use peek() when reading _past and _current inside the tracking effect, preventing unnecessary re-runs caused by reactive reads during history recording. @praxisjs/core: Added an _initialized guard so the first value is captured without pushing an empty entry into the past stack.

0.1.0

Minor Changes

  • aaf7dab: Initial beta release

Patch Changes

  • Updated dependencies [aaf7dab]
    • @praxisjs/jsx@0.1.0
    • @praxisjs/shared@0.1.0

Released under the MIT License.