Skip to content

Releases: ZtaMDev/Pulse-js

v0.3.0

Choose a tag to compare

@ZtaMDev ZtaMDev released this 08 Feb 20:50

v0.3.0 Universal Reactivity & DevTools 2.0

Release Date: February 8, 2026

v0.3.0 is the most significant update to Pulse.js since its inception. We've completely rebuilt the core engine to support Universal Proxy Reactivity, implemented a HMR-Stable Registry, and launched a brand new Agent-Client DevTools architecture.


Major Features

Proxy-Based Reactivity (pulse())

Pulse v3 moves away from explicit .set() and .get() calls for objects. The new pulse() function creates a deep reactive proxy that tracks property access automatically.

import { pulse } from "@pulse-js/core";

// Create a reactive object
const state = pulse({
  count: 0,
  user: { name: "Alice" },
  increment() {
    this.count++;
  },
});

// Mutate directly - it's reactive!
state.count++;
state.user.name = "Bob";
  • Deep Guard Inspection: See full dependency trees, execution status, and detailed failure reasons directly in the UI.

Normalized GuardReason

Evaluation failures now guarantee a structured GuardReason object. Simple strings or thrown errors are automatically wrapped, ensuring you can always safely access .code and .message in your UI.

const { reason } = usePulse(myGuard);
console.log(reason.code); // Always available
console.log(reason.message); // Always available

New Integrations

Astro Integration (@pulse-js/astro)

Pulse now native-supports Astro. Use Pulse state on the server during SSR and have it seamlessly hydrate on the client.

TanStack Query Bridge (@pulse-js/tanstack)

Easily bridge asynchronous remote state from TanStack Query into Pulse's reactive ecosystem using the new guardFromQuery selector.


📦 Package Versions

  • @pulse-js/core: 0.3.0
  • @pulse-js/tools: 0.3.0
  • @pulse-js/react: 0.3.0
  • @pulse-js/vue: 0.3.0
  • @pulse-js/svelte: 0.3.0
  • @pulse-js/astro: 0.3.0
  • @pulse-js/tanstack: 0.3.0

v0.2.2

Choose a tag to compare

@ZtaMDev ZtaMDev released this 20 Jan 01:53

v0.2.2 - Async GuardReason Integrity Fix

Release Date: January 19, 2026

This release addresses a critical data integrity issue where structured reasons passed to guardFail() were being lost in asynchronous guards, and normalizes all failure reasons to objects for a better developer experience.


Critical Fixes & DX Improvements

Async GuardReason Integrity & Normalization

In previous versions, reason could be a string or a GuardReason object, leading to complex TypeScript checks like typeof reason === 'object'.

v0.2.2 fixes the async loss bug and guarantees that reason is always a GuardReason object. Even simple string failures are now automatically wrapped as { code: 'ERROR', message: '...' }.

// ✅ Guaranteed object structure in v0.2.2
const { reason } = usePulse(isAdmin);

// No more "property does not exist on type string" errors!
console.log(reason?.code);
console.log(reason?.message);

Critical Fixes

Async GuardReason Integrity

In previous versions, if a guard was asynchronous (returning a Promise), any structured object passed to guardFail() (e.g., { code: 'AUTH', message: '...' }) would be converted to a plain string in the Promise's catch handler.

v0.2.2 fixes this by ensuring the internal _pulseFail signal is respected in both synchronous and asynchronous evaluation paths.

// ✅ Now works perfectly in v0.2.2
const isAdmin = guard("admin-check", async () => {
  const user = await fetchUser();
  if (!user) {
    return guardFail({
      code: "AUTH",
      message: "Authentication required",
    });
  }
  return true;
});

// In your UI component:
const { reason } = usePulse(isAdmin);
console.log(reason?.code); // "AUTH" (previously would be undefined)

Improvements

Cross-Package Version Synchronization

To ensure total consistency across the monorepo, all official adapters and tools have been bumped to align with Core v0.2.2.

  • React (@pulse-js/react): Now at v0.2.0, utilizing the latest Core version for stable async guard tracking.
  • Tools (@pulse-js/tools): Now at v0.2.0, improved stability for structured reason inspection.
  • Documentation: New examples for structured error handling in guides.

📦 Package Versions

  • @pulse-js/core: 0.2.2
  • @pulse-js/tools: 0.2.0
  • @pulse-js/react: 0.2.0
  • @pulse-js/vue: 0.1.4
  • @pulse-js/svelte: 0.1.6

v0.2.1

Choose a tag to compare

@ZtaMDev ZtaMDev released this 19 Jan 23:56

v0.2.1 - Advanced Guard Features

Release Date: January 19, 2026

This release introduces powerful new guard composition utilities and enhanced debugging for complex dependency trees.


New Features

guard.map() Composition Helper

A new way to transform sources directly into guards with full semantic tracking. This replaces the legacy compute for most use cases.

const todos = source([{ done: false }, { done: true }]);

// Reactive guard derived from source
const doneCount = guard.map(todos, (list) => list.filter((t) => t.done).length);

// doneCount works as a full Guard<number>
console.log(doneCount.state()); // { status: "ok", value: 1 }

InferGuardType<T> Type Helper

Extracting types from guards is now seamless with the new InferGuardType utility. Perfect for creating robust TypeScript interfaces.

import { guard, type InferGuardType } from "@pulse-js/core";

const authGuard = guard("auth", async () => fetchUser());
type AuthUser = InferGuardType<typeof authGuard>; // User | undefined

Enhanced Guard Debugging

The .explain() tree now includes the specific failure reasons for every sub-dependency, eliminating the guesswork in complex guard chains.

/* New explain() output for a failed guard: */
{
  "name": "can-place-order",
  "status": "fail",
  "dependencies": [
    { "name": "has-items", "status": "ok" },
    {
      "name": "sufficient-balance",
      "status": "fail",
      "reason": "Balance is too low" // ✅ Detail now included
    }
  ]
}

Core (@pulse-js/core v0.2.1)

  • guard.map: Native implementation for source transformations
  • Improved explain(): Deep dependency trees now propagate reasons

Documentation


📦 Package Versions

  • @pulse-js/core: 0.2.1
  • @pulse-js/tools: 0.1.9
  • @pulse-js/react: 0.1.9
  • @pulse-js/vue: 0.1.3
  • @pulse-js/svelte: 0.1.5

v0.2.0

Choose a tag to compare

@ZtaMDev ZtaMDev released this 19 Jan 04:48

v0.2.0 - HMR Stability & Smart Cleanup

Release Date: January 19, 2026

This major release focuses on Hot Module Replacement (HMR) stability, introducing a professional mark-and-sweep cleanup system and enhanced DevTools with customizable shortcuts.


New Features

Smart HMR Cleanup System

Pulse now uses a mark-and-sweep garbage collection approach for HMR:

  • Mark Phase: When HMR is detected, the generation counter increments and all units that re-register are marked as "alive"
  • Sweep Phase: After 100ms, units from previous generations that weren't re-registered are automatically removed
  • Result: Only truly deleted units are cleaned up; units that remain in your code are preserved

Example:

// Initial load (generation 0)
const count = source(0, { name: "count" });
const user = source(null, { name: "user" });

// After HMR (generation 1)
// - Both units re-register and are marked with generation 1
// - No cleanup needed, both units preserved

// If you delete 'user' from code and save
// - Only 'count' re-registers with generation 2
// - 'user' (still at generation 1) is swept after 100ms

Named Units Requirement for DevTools

To ensure HMR stability, only units with explicit names are now visible in DevTools:

// ✅ Visible in DevTools
const count = source(0, { name: "count" });
const isEven = guard("is-even", () => count() % 2 === 0);

// ❌ Not visible in DevTools (but works perfectly)
const temp = source(0);
const check = guard(() => temp() > 5);

Why? Unnamed units create new object instances on every HMR reload, making stable tracking impossible. This is a fundamental JavaScript/HMR limitation shared by all state management libraries (Redux, Zustand, Jotai, etc.).

Customizable DevTools Shortcuts

The keyboard shortcut for toggling DevTools is now fully customizable:

<!-- Default -->
<pulse-inspector></pulse-inspector>
<!-- Shortcut: Ctrl+M -->

<!-- Custom -->
<pulse-inspector shortcut="Ctrl+P"></pulse-inspector>
<pulse-inspector shortcut="Ctrl+Shift+D"></pulse-inspector>
<pulse-inspector shortcut="Alt+D"></pulse-inspector>

React:

import { PulseDevTools } from "@pulse-js/react/devtools";

<PulseDevTools shortcut="Ctrl+Shift+P" />;

DevTools Refresh Button

Added a refresh button (🔄) to the DevTools header for manual registry refresh.


Improvements

Core (@pulse-js/core v0.2.0)

  • Mark-and-Sweep Cleanup: Intelligent HMR cleanup that only removes truly deleted units
  • Generation Tracking: Each unit is marked with a generation number for precise lifecycle management
  • Improved Registry: Simplified registration logic with better HMR detection
  • Performance: Eliminated unnecessary stack trace parsing overhead

Tools (@pulse-js/tools v0.1.8)

  • Customizable Shortcuts: Support for any key combination via shortcut attribute
  • Refresh Button: Manual refresh capability for DevTools
  • Improved Tooltips: Dynamic tooltip showing current shortcut
  • Named Units Only: Cleaner, more stable unit tracking

Framework Integrations

React (@pulse-js/react v0.1.8)

  • Updated to use core 0.2.0 and tools 0.1.8
  • Shortcut prop support in PulseDevTools component

Vue (@pulse-js/vue v0.1.3)

  • Updated to use core 0.2.0 and tools 0.1.8

Svelte (@pulse-js/svelte v0.1.3)

  • Updated to use core 0.2.0

Documentation

  • Updated DevTools Guide with named units requirement
  • Added examples of customizable shortcuts
  • Removed non-existent theme prop from documentation
  • Clarified HMR behavior and limitations

Migration Guide

From v0.1.x to v0.2.0

Breaking Change: Named Units for DevTools Visibility

Unnamed sources and guards will no longer appear in DevTools. To see them:

// Before (won't appear in DevTools)
const count = source(0);
const isValid = guard(() => count() > 0);

// After (visible in DevTools)
const count = source(0, { name: "count" });
const isValid = guard("is-valid", () => count() > 0);

Note: This only affects DevTools visibility. Unnamed units continue to work perfectly in your application.

Shortcut Changes

The default shortcut has changed from Ctrl+Shift+P to Ctrl+M. If you prefer the old shortcut:

<pulse-inspector shortcut="Ctrl+Shift+P"></pulse-inspector>

Testing

  • All 33 tests passing
  • Verified HMR stability with mark-and-sweep cleanup
  • Tested customizable shortcuts across different key combinations
  • Validated DevTools refresh functionality

📦 Package Versions

  • @pulse-js/core: 0.2.0
  • @pulse-js/tools: 0.1.8
  • @pulse-js/react: 0.1.8
  • @pulse-js/vue: 0.1.3
  • @pulse-js/svelte: 0.1.3

v0.1.9

Choose a tag to compare

@ZtaMDev ZtaMDev released this 18 Jan 00:17

v0.1.9 HMR Stability & Svelte 5 Runes Integration

Release Date: January 17, 2026

This release brings major improvements to Hot Module Replacement (HMR) stability and completes the Svelte 5 Runes integration, making Pulse more robust for modern development workflows.


New Features

HMR-Safe Auto-Naming System

Pulse now automatically assigns stable, location-based names to unnamed sources and guards, preventing duplicates during HMR reloads.

How it works:

  • Unnamed units receive auto-generated names based on their source location (e.g., source@react-example:7)
  • Names remain stable across HMR reloads, even when code changes
  • Automatic cleanup of stale units after HMR using generation-based tracking

Example:

// Before: Would duplicate on HMR
const count = source(0);

// Now: Automatically named as "source@MyComponent:5"
// Stable across HMR reloads

Generation-Based Cleanup

The registry now tracks unit "generations" and automatically removes orphaned units after HMR:

  • Each HMR reload increments the generation counter
  • Units from previous generations are cleaned up after 100ms
  • Console logs show cleanup activity: [Pulse] Cleaned up X stale units after HMR

Performance Optimizations

  • Stack Trace Caching: Auto-naming now uses a cache to avoid repeated stack trace parsing
  • Lazy Evaluation: Names are only generated when needed, reducing overhead
  • 10k sources test: Maintains performance (~2.3s) with minimal production overhead

Svelte 5 Runes Integration

Complete Runes Support

@pulse-js/svelte now provides first-class support for Svelte 5's Runes system:

Native Reactivity:

<script lang="ts">
  import { source } from "@pulse-js/core";
  import { usePulse } from "@pulse-js/svelte";

  const countSource = source(0);
  const count = usePulse(countSource);
</script>

<button onclick={() => countSource.update(n => n + 1)}>
  Count: {count.value}
</button>

Guard Integration:

<script lang="ts">
  import { useGuard } from "@pulse-js/svelte";
  import { authGuard } from "./logic";

  const state = useGuard(authGuard);
</script>

{#if state.status === "ok"}
  <p>Welcome, {state.value.name}</p>
{:else if state.status === "fail"}
  <p style="color: red">Error: {state.reason}</p>
{/if}

Improved Type Inference

  • Reordered usePulse overloads to prioritize Guard<T> over Source<T>
  • Fixed TypeScript errors like Property 'status' does not exist
  • Adopted Svelte 5 prop patterns to eliminate state_referenced_locally warnings

Package Resolution Fixes

  • Updated package.json exports to correctly map Svelte 5 Runes entry points
  • Added optimizeDeps.exclude guidance for Vite configuration
  • Fixed .svelte.js output alignment with tsup build process

Improvements

Core (@pulse-js/core v0.1.9)

  • Global Singleton Registry: Uses globalThis to persist across HMR reloads
  • Auto-Naming Cache: Reduces stack trace parsing overhead
  • SSR Hydration: Restored _hydrate method for proper server-side rendering support

Tools (@pulse-js/tools v0.1.7)

  • Improved Display: Auto-generated names now show in DevTools with clean format
  • Better Tracking: Uses stable IDs for editing unnamed sources
  • HMR Awareness: DevTools correctly updates when units are replaced during HMR

Framework Integrations

React (@pulse-js/react v0.1.7)

  • Updated dependencies to use latest core and tools

Vue (@pulse-js/vue v0.1.2)

  • Updated dependencies to use latest core and tools

Svelte (@pulse-js/svelte v0.1.2)

  • Complete Svelte 5 Runes integration
  • Backward compatibility with Svelte 4 via usePulseStore
  • Proxy-based Guards for stable object identity
  • Updated documentation with Runes examples

Bug Fixes

  • Fixed SSR hydration test failure by restoring _hydrate method
  • Resolved package resolution errors in Svelte 5 projects
  • Fixed TypeScript inference issues in usePulse overloads
  • Fixed HMR duplication when adding/removing names from sources

Documentation

  • Updated Svelte Integration guide with Runes examples
  • Added Vite configuration instructions for Svelte 5
  • Documented auto-naming behavior and HMR cleanup
  • Added examples for both Runes and Store APIs

Migration Guide

For Svelte users:

If you're using Svelte 5, update your vite.config.ts:

import { defineConfig } from "vite";

export default defineConfig({
  resolve: {
    conditions: ["browser", "module", "jsnext:main", "jsnext"],
  },
  optimizeDeps: {
    exclude: ["@pulse-js/svelte"],
  },
});

Unnamed sources and guards will now automatically receive location-based names. This is transparent and requires no code changes, but you'll see cleaner names in DevTools (e.g., source@MyComponent:7 instead of random IDs).

v0.1.8

Choose a tag to compare

@ZtaMDev ZtaMDev released this 16 Jan 20:46

v0.1.8 (React Compatibility & Cyclic Fixes)

This release significantly improves React ergonomics and fixes critical bugs in the cyclic dependency detection logic.

Core Improvements (@pulse-js/core 0.1.8)

  • Fixed Cyclic Dependency Detection: The detection logic has been completely overhauled. It now correctly identifies true cycles (A -> A) without false positives for nested guard compositions (e.g., guard.all reading children).
  • GuardReason Interface: The GuardReason interface now includes a toString() signature. This ensures that reason objects can be safely converted to strings in all environments.

React Integration (@pulse-js/react 0.1.5)

  • formatReason Helper: A new utility function to easily render guard failure reasons in JSX.
    import { formatReason } from "@pulse-js/react";
    // ...
    <p>{formatReason(reason)}</p>;
  • Improved Types: Internal improvements to how types are exported to prevent circular dependencies during builds.

DevTools 2.0 (@pulse-js/tools 0.1.5)

  • Tabbed Interface: New split view with "Inspector" (list) and "Pulse Tree" (dependency graph).
  • Pulse Tree Visualization: Recursive tree view showing the entire dependency graph of your guards.
    • Expand/Collapse nodes to trace logic flows.
    • View status and failure reasons at every level of the tree.
  • Editable Sources: Click on any Source value in the inspector to edit it directly. Supports JSON syntax for objects/arrays.
  • Enhanced UI: Improved "Pill" design, better colors, and drag-and-drop mechanics.

Pulse v0.1.7

Choose a tag to compare

@ZtaMDev ZtaMDev released this 15 Jan 02:40
d69ba7d

v0.1.7 (Enterprise Reactivity & Cycle Detection)

This release focuses on hardening the core engine for large-scale applications with improved failure tracking, cyclic dependency detection, and enterprise-grade metadata.

Core Improvements (@pulse-js/core 0.1.7)

  • Cyclic Dependency Detection: Added a robust stack-based detection mechanism that catches infinite reactive loops (A -> B -> A) and reports them as descriptive failure reasons instead of causing stack overflows.
  • Persistent Dependency Insights: The .explain() graph now persists even after a failure. Previously, failing early would clear downstream dependencies; now it remembers the last known graph for better debugging.
  • Structured Reason Metadata: Introduced the GuardReason interface. Evaluators can now throw objects with code, message, and meta to provide rich error context to the UI.
  • Semantic State Preservation: When a failed Guard re-evaluates, its lastReason is preserved while pending, preventing UI flickering and allowing users to see the previous error while the next attempt is in progress.
  • Flexible Pending State: Returning undefined from a Guard evaluator now explicitly marks it as pending, simplifying data fetching and "not ready" patterns.

DevTools Improvements (@pulse-js/tools 0.1.4)

  • Metadata Rendering: The inspector now renders structured failure reasons, including error codes and JSON metadata snippets.
  • Evaluation State Tracking: Visual indicators for lastReason when a guard is in a pending state after a failure.
  • Dependency Status: Dependency lists now show the status of each dependent guard for faster troubleshooting.

React Integration (@pulse-js/react 0.1.4)

  • Updated Dependencies: Bumped to use @pulse-js/core@0.1.7 and @pulse-js/tools@0.1.4.

Pulse v0.1.1

Choose a tag to compare

@ZtaMDev ZtaMDev released this 13 Jan 00:41

v0.1.1 (Initial Base Release)

We are excited to announce the first public release of Pulse, a semantic reactivity system for modern applications. This release establishes the core primitives and ecosystem under the new @pulse-js namespace.

📦 Packages

  • @pulse-js/core: The reactivity engine.
  • @pulse-js/react: React 18+ integration hooks.
  • @pulse-js/tools: Visual debugging suite (formerly devtools).

Features

Core Reactivity

  • Sources: Primitive containers for state values.
  • Guards: First-class "Conditions" that track status (ok, fail, pending) and failure reasons.
  • Composition: Logic helpers guard.all, guard.any, guard.not, and guard.compute.
  • Async Support: Native Promise handling for async guards.
  • SSR: Isomorphic evaluate (server) and hydrate (client) methods.

React Integration

  • usePulse Hook: Universal hook for both Sources and Guards.
  • Concurrent Mode: Built on useSyncExternalStore for safe concurrent updates.
  • Smart Re-renders: Components only update when semantic state changes.

Developer Experience

  • Pulse Tools: A zero-config, draggable overlay for inspecting the reactive graph.
  • Type Safety: Full TypeScript support with inferred types.

Getting Started

npm install @pulse-js/core @pulse-js/react @pulse-js/tools