@zakkster/lite-signal 1.0.3 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +32 -3
  2. package/llms.txt +2 -2
  3. package/package.json +7 -7
package/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  > Zero-GC reactive graph for hot paths. Object-pooled nodes, versioned push-pull propagation, 32-bit modular epochs. Built for 16ms render budgets and 1MB extension bundles.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@zakkster/lite-signal.svg?style=for-the-badge&color=latest)](https://www.npmjs.com/package/@zakkster/lite-signal)
6
+ ![Zero-GC](https://img.shields.io/badge/Zero--GC-Engine-00C853?style=for-the-badge&logo=leaf&logoColor=white)
6
7
  [![bundle size](https://img.shields.io/badge/min%2Bgz-~3.2KB-blue?style=flat-square)](https://bundlephobia.com/package/@zakkster/lite-signal)
7
8
  [![npm downloads](https://img.shields.io/npm/dm/@zakkster/lite-signal?style=for-the-badge&color=blue)](https://www.npmjs.com/package/@zakkster/lite-signal)
8
9
  [![npm total downloads](https://img.shields.io/npm/dt/@zakkster/lite-signal?style=for-the-badge&color=blue)](https://www.npmjs.com/package/@zakkster/lite-signal)
@@ -365,7 +366,7 @@ These are the questions you'd ask in a code review, with the answers:
365
366
 
366
367
  ## Benchmarks
367
368
 
368
- Honest numbers, against the same workload, with anti-DCE sinks and verified effect execution. All measurements: Node 22, **2016-era Intel MacBook Pro (4 cores, ~10 yr old hardware)**, 20K iterations × 5 inner runs × 12 outer invocations (median reported). Newer/faster machines shift all libs up proportionally; the relative ordering between libs is what matters.
369
+ Honest numbers, against the same workload, with anti-DCE sinks and verified effect execution. All measurements: Node 22, **2016-era Intel MacBook Pro (4 cores, ~10 yr old hardware)**, 20K iterations × 5 inner runs × 50+ outer invocations (median reported). Newer/faster machines shift all libs up proportionally; the relative ordering between libs is what matters.
369
370
 
370
371
  | Scenario | What it stresses | lite-signal | alien-signals | preact | solid-js |
371
372
  | ---------- | -------------------------------- | ----------- | ------------- | ---------- | --------- |
@@ -382,7 +383,7 @@ On allocation pressure, `lite-signal` is alone in the zero-Δheap band: ~15 KB o
382
383
 
383
384
  > Note on the +71 KB retained that lite-signal shows on KAIROS specifically: that's the pre-allocated pool sitting in memory holding the live graph (1002 nodes + ~2000 links). The pool *is* the working memory — see the [Case for object pooling](#case-for-object-pooling) section. On the other benches the graph is small enough that the same pool floats below baseline after GC.
384
385
 
385
- The benchmark harness is in [`bench/benchmark.mjs`](./bench/benchmark.mjs). It:
386
+ The benchmark harness is in [`bench/benchmark.mjs`](./bench/benchmark.mjs); a full methodology write-up — including the anti-DCE design, workload diagrams, variance discipline, reproducibility recipe, and a self-validation procedure for the harness itself — lives in [`bench/README.md`](./bench/README.md). It:
386
387
 
387
388
  1. Writes every effect's output to a shared `Float64Array(4096)` exposed on `globalThis` — V8 cannot prove these writes are dead.
388
389
  2. Uses the **client** Solid runtime (`solid-js/dist/solid.js`), not the SSR stub Node resolves to by default. The default Node resolution silently no-ops effects, which is how earlier benchmarks across the ecosystem have reported Solid at ~50 GHz throughput.
@@ -447,6 +448,34 @@ npm run verify # test + test:gc + a sanity bench
447
448
 
448
449
  ---
449
450
 
451
+ ## Performance Trade-offs & Topology Scaling
452
+
453
+ `lite-signal` was built with a strict mandate: **absolute zero garbage collection**. By packing the dependency graph into a flat, pre-allocated memory arena, we eliminate the Scavenger GC pauses that plague 120fps Canvas/WebGL loops.
454
+
455
+ However, flat arrays come with a mathematical trade-off. While memory allocation is $O(1)$, modifying a flat array during dynamic dependency churn requires $O(N)$ linear scans.
456
+
457
+ **Andrii Volynets** (author of the phenomenal [Alien Signals](https://github.com/stackblitz/alien-signals)) generously ran `lite-signal` through his advanced topology matrix. The results clearly highlight where the zero-GC flat-array architecture excels, and where pointer-based graphs (like Alien/Reflex) take the lead:
458
+
459
+ #### 1. Stable Topologies (Fan-in / Fan-out / Broadcast)
460
+ In stable environments (typical of game engines, particle systems, and visualizers), `lite-signal` is blisteringly fast and maintains a near-zero allocation profile, keeping frame times perfectly flat.
461
+
462
+ #### 2. Dynamic Topologies (Web Apps / Layered DAGs)
463
+ In highly chaotic graphs with branch switching, selective reads, and wide dense churn (typical of large DOM-based web frameworks like Vue or React), the $O(N)$ edge traversal cost of flat arrays becomes the dominant bottleneck.
464
+
465
+ *Andrii's benchmark results for dynamic topologies:*
466
+ | Scenario | alien-signals | reflex | lite-signal |
467
+ | :--- | :--- | :--- | :--- |
468
+ | **1000x12 (4 sources, dynamic)** | 184ms | 194ms | 2031ms |
469
+ | **1000x5 (25 sources, wide/dense)** | 304ms | 303ms | 1746ms |
470
+ | **64x6 (selective dynamic DAG)** | 181ms | 196ms | 559ms |
471
+
472
+ **The Takeaway:** "Zero-GC" and "topology scalability" are orthogonal dimensions. If you are building a DOM framework with massive dynamic `v-if` churn, use Alien Signals. If you are building a 120fps Canvas game with a stable scene graph where any GC pause is a dropped frame, use `lite-signal`.
473
+
474
+ ### Roadmap: v1.1
475
+ We are actively working on a v1.1 architectural update to address this topology degradation while maintaining the zero-GC contract. By moving to a version-stamped dependency reconciliation pass (`lastSeenInEval`) with a pre-allocated scratch buffer, we expect to drop dynamic read costs to $O(1)$ unconditionally.
476
+
477
+ ---
478
+
450
479
  ## What this is not
451
480
 
452
481
  - **A virtual DOM, JSX runtime, or rendering library.** It's the substrate. Plug it under whatever rendering layer you like.
@@ -584,4 +613,4 @@ MIT © Zahary Shinikchiev
584
613
 
585
614
  ---
586
615
 
587
- > Part of the **@zakkster** zero-GC stack: [`lite-ecs`](https://www.npmjs.com/package/@zakkster/lite-ecs) · [`lite-ease`](https://www.npmjs.com/package/@zakkster/lite-ease) · [`lite-pointer-tracker`](https://www.npmjs.com/package/@zakkster/lite-pointer-tracker) · [`lite-bmfont`](https://www.npmjs.com/package/@zakkster/lite-bmfont) · [`lite-color`](https://www.npmjs.com/package/@zakkster/lite-color)
616
+ > Part of the **@zakkster** zero-GC stack: [`lite-ecs`](https://www.npmjs.com/package/@zakkster/lite-ecs) · [`lite-ease`](https://www.npmjs.com/package/@zakkster/lite-ease) · [`lite-pointer-tracker`](https://www.npmjs.com/package/@zakkster/lite-pointer-tracker) · [`lite-bmfont`](https://www.npmjs.com/package/@zakkster/lite-bmfont) · [`lite-color`](https://www.npmjs.com/package/@zakkster/lite-color)
package/llms.txt CHANGED
@@ -117,7 +117,7 @@ class CapacityError extends Error {
117
117
  }
118
118
  ```
119
119
 
120
- ## Benchmark snapshot (Node 22, 2016-era Intel MacBook Pro, 20K iter × 5 runs × 12 invocations)
120
+ ## Benchmark snapshot (Node 22, 2016-era Intel MacBook Pro, 20K iter × 5 runs × 50+ invocations)
121
121
 
122
122
  | Scenario | lite-signal | alien-signals | preact | solid |
123
123
  | --------------------------------------- | ----------- | ------------- | -------- | -------- |
@@ -199,4 +199,4 @@ npm install @zakkster/lite-signal
199
199
 
200
200
  ## License
201
201
 
202
- MIT
202
+ MIT
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@zakkster/lite-signal",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Zero-GC reactive graph. Monomorphic object pool, versioned push-pull propagation, 32-bit modular versioning. Built for hot paths and long-running processes.",
5
5
  "author": "Zahary Shinikchiev <shinikchiev@yahoo.com>",
6
6
  "license": "MIT",
7
7
  "type": "module",
8
- "main": "Signal.js",
9
- "module": "Signal.js",
10
- "types": "Signal.d.ts",
8
+ "main": "./Signal.js",
9
+ "module": "./Signal.js",
10
+ "types": "./Signal.d.ts",
11
11
  "exports": {
12
12
  ".": {
13
- "types": "Signal.d.ts",
14
- "import": "Signal.js",
15
- "default": "Signal.js"
13
+ "types": "./Signal.d.ts",
14
+ "import": "./Signal.js",
15
+ "default": "./Signal.js"
16
16
  }
17
17
  },
18
18
  "files": [