create-volt 0.26.0 → 0.27.0

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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,17 @@ All notable changes to `create-volt` are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/), and this project adheres to
5
5
  [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [0.27.0] - 2026-06-29
8
+
9
+ ### Fixed
10
+ - **Reactive crash on conditional re-render** (`volt.js`) — a signal write
11
+ notified a *snapshot* of subscribers, so a parent reactive block that disposed
12
+ a nested one mid-update would still run the stale nested effect on detached DOM
13
+ ("Cannot read properties of null (reading 'insertBefore')"). Disposed effects
14
+ now skip their queued run, and `renderRange` guards against a detached range.
15
+ Fixes the **setup wizard** crashing when toggling add-ons or changing the DB
16
+ driver. Regression-tested in the headless-browser suite.
17
+
7
18
  ## [0.26.0] - 2026-06-29
8
19
 
9
20
  ### Added
@@ -370,6 +381,7 @@ All notable changes to `create-volt` are documented here. The format follows
370
381
  watching and full-page hot reload. Supports `--skip-install` and `--force`,
371
382
  and auto-detects npm / pnpm / yarn / bun for the install step.
372
383
 
384
+ [0.27.0]: https://github.com/MIR-2025/volt/releases/tag/v0.27.0
373
385
  [0.26.0]: https://github.com/MIR-2025/volt/releases/tag/v0.26.0
374
386
  [0.25.0]: https://github.com/MIR-2025/volt/releases/tag/v0.25.0
375
387
  [0.24.0]: https://github.com/MIR-2025/volt/releases/tag/v0.24.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-volt",
3
- "version": "0.26.0",
3
+ "version": "0.27.0",
4
4
  "description": "Scaffold a new Volt app — no-build, signals-based UI with Socket.io hot reload.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -47,7 +47,11 @@ export function effect(fn) {
47
47
  deps: new Set(),
48
48
  children: new Set(),
49
49
  parent: activeEffect,
50
+ disposed: false,
50
51
  run() {
52
+ // A signal write notifies a *snapshot* of subscribers; a parent re-render
53
+ // can dispose this effect before its turn in that snapshot — so skip if so.
54
+ if (eff.disposed) return;
51
55
  disposeChildren(eff);
52
56
  cleanupDeps(eff);
53
57
  const prev = activeEffect;
@@ -59,6 +63,7 @@ export function effect(fn) {
59
63
  }
60
64
  },
61
65
  dispose() {
66
+ eff.disposed = true;
62
67
  disposeChildren(eff);
63
68
  cleanupDeps(eff);
64
69
  if (eff.parent) eff.parent.children.delete(eff);
@@ -152,6 +157,7 @@ function appendChild(parent, child) {
152
157
 
153
158
  // Replace everything between the start/end anchors with `value`'s nodes.
154
159
  function renderRange(start, end, value) {
160
+ if (!end.parentNode) return; // range detached (parent re-rendered) — nothing to do
155
161
  let n = start.nextSibling;
156
162
  while (n && n !== end) {
157
163
  const t = n.nextSibling;
@@ -47,7 +47,11 @@ export function effect(fn) {
47
47
  deps: new Set(),
48
48
  children: new Set(),
49
49
  parent: activeEffect,
50
+ disposed: false,
50
51
  run() {
52
+ // A signal write notifies a *snapshot* of subscribers; a parent re-render
53
+ // can dispose this effect before its turn in that snapshot — so skip if so.
54
+ if (eff.disposed) return;
51
55
  disposeChildren(eff);
52
56
  cleanupDeps(eff);
53
57
  const prev = activeEffect;
@@ -59,6 +63,7 @@ export function effect(fn) {
59
63
  }
60
64
  },
61
65
  dispose() {
66
+ eff.disposed = true;
62
67
  disposeChildren(eff);
63
68
  cleanupDeps(eff);
64
69
  if (eff.parent) eff.parent.children.delete(eff);
@@ -152,6 +157,7 @@ function appendChild(parent, child) {
152
157
 
153
158
  // Replace everything between the start/end anchors with `value`'s nodes.
154
159
  function renderRange(start, end, value) {
160
+ if (!end.parentNode) return; // range detached (parent re-rendered) — nothing to do
155
161
  let n = start.nextSibling;
156
162
  while (n && n !== end) {
157
163
  const t = n.nextSibling;
@@ -47,7 +47,11 @@ export function effect(fn) {
47
47
  deps: new Set(),
48
48
  children: new Set(),
49
49
  parent: activeEffect,
50
+ disposed: false,
50
51
  run() {
52
+ // A signal write notifies a *snapshot* of subscribers; a parent re-render
53
+ // can dispose this effect before its turn in that snapshot — so skip if so.
54
+ if (eff.disposed) return;
51
55
  disposeChildren(eff);
52
56
  cleanupDeps(eff);
53
57
  const prev = activeEffect;
@@ -59,6 +63,7 @@ export function effect(fn) {
59
63
  }
60
64
  },
61
65
  dispose() {
66
+ eff.disposed = true;
62
67
  disposeChildren(eff);
63
68
  cleanupDeps(eff);
64
69
  if (eff.parent) eff.parent.children.delete(eff);
@@ -152,6 +157,7 @@ function appendChild(parent, child) {
152
157
 
153
158
  // Replace everything between the start/end anchors with `value`'s nodes.
154
159
  function renderRange(start, end, value) {
160
+ if (!end.parentNode) return; // range detached (parent re-rendered) — nothing to do
155
161
  let n = start.nextSibling;
156
162
  while (n && n !== end) {
157
163
  const t = n.nextSibling;