From 8a8b7fce72a6928d9f2411325cb9f7ac4c7d313a Mon Sep 17 00:00:00 2001 From: Ryan Bliss Date: Thu, 8 Oct 2026 16:02:53 -0700 Subject: [PATCH] chore(release): prepare Retree 0.11.2 --- .changeset/builtin-leaves.md | 7 --- .../keep-observed-across-resubscribe.md | 7 --- .changeset/memo-untracked-cold-key.md | 5 -- .changeset/observe-covered-reactive-nodes.md | 8 ---- .changeset/query-baseline-ignore.md | 5 -- .changeset/query-detach-shared-results.md | 10 ---- .../unsubscribe-after-clear-listeners.md | 5 -- .changeset/use-select-tree-version.md | 8 ---- package-lock.json | 46 +++++++++---------- .../retree-babel-plugin-compiler/CHANGELOG.md | 2 + .../retree-babel-plugin-compiler/package.json | 4 +- packages/retree-benchmark-cli/package.json | 4 +- packages/retree-convex/CHANGELOG.md | 10 ++++ packages/retree-convex/package.json | 10 ++-- packages/retree-core/CHANGELOG.md | 18 ++++++++ packages/retree-core/package.json | 2 +- packages/retree-create/CHANGELOG.md | 2 + packages/retree-create/package.json | 2 +- packages/retree-devtools/CHANGELOG.md | 2 + packages/retree-devtools/package.json | 6 +-- packages/retree-query/CHANGELOG.md | 11 +++++ packages/retree-query/package.json | 6 +-- packages/retree-react-convex/CHANGELOG.md | 2 + packages/retree-react-convex/package.json | 6 +-- packages/retree-react/CHANGELOG.md | 16 +++++++ packages/retree-react/package.json | 6 +-- 26 files changed, 109 insertions(+), 101 deletions(-) delete mode 100644 .changeset/builtin-leaves.md delete mode 100644 .changeset/keep-observed-across-resubscribe.md delete mode 100644 .changeset/memo-untracked-cold-key.md delete mode 100644 .changeset/observe-covered-reactive-nodes.md delete mode 100644 .changeset/query-baseline-ignore.md delete mode 100644 .changeset/query-detach-shared-results.md delete mode 100644 .changeset/unsubscribe-after-clear-listeners.md delete mode 100644 .changeset/use-select-tree-version.md diff --git a/.changeset/builtin-leaves.md b/.changeset/builtin-leaves.md deleted file mode 100644 index 7e95873..0000000 --- a/.changeset/builtin-leaves.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@retreejs/core": patch ---- - -Built-ins that keep their state in internal slots are now leaves, so reading them from a tree works. - -A `DOMException` stored in a tree threw on `error.message` ("Illegal invocation" in browsers, `ERR_INVALID_THIS` in Node) because Retree proxied it and its accessors reject a proxy as `this`. The same broke `URL`, `Blob`, `RegExp`, typed arrays, `AbortController`, `Promise`, and other platform objects. Retree now stores these as-is, like frozen objects: reads return the object itself, and assigning a new one notifies. `Retree.root` rejects them because a root must be a node. diff --git a/.changeset/keep-observed-across-resubscribe.md b/.changeset/keep-observed-across-resubscribe.md deleted file mode 100644 index 889b57d..0000000 --- a/.changeset/keep-observed-across-resubscribe.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -"@retreejs/react": patch ---- - -`useSelect` no longer re-runs `onUnobserved` and `onObserved` on a `ReactiveNode` it keeps reading when a prop moves the selector's other dependencies. Query nodes such as `ConvexQueryNode` no longer close and reopen their backend subscription on those re-renders. - -React hooks now release a `ReactiveNode` one microtask after its last listener unsubscribes, so `onUnobserved` runs just after unmount rather than during it. Tests that fake `queueMicrotask`, as Jest's fake timers do by default, need to flush microtasks (`jest.runAllTicks()`) before asserting on it. diff --git a/.changeset/memo-untracked-cold-key.md b/.changeset/memo-untracked-cold-key.md deleted file mode 100644 index 4a4b656..0000000 --- a/.changeset/memo-untracked-cold-key.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@retreejs/core": patch ---- - -A `@memo` or `@fnMemo` read inside `Retree.untracked` no longer subscribes the surrounding selector or effect to its key's reads. Before, a memo whose key had to re-run (on its first read, or after a key input changed) leaked those reads, so whether a selector stayed asleep depended on what had warmed the memo. diff --git a/.changeset/observe-covered-reactive-nodes.md b/.changeset/observe-covered-reactive-nodes.md deleted file mode 100644 index c2693f0..0000000 --- a/.changeset/observe-covered-reactive-nodes.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@retreejs/core": patch -"@retreejs/react": patch ---- - -Selector-only `Retree.select(selector, callback)`, `Retree.effect`, and `useSelect` now observe every `ReactiveNode` they read, so `onObserved` and `onUnobserved` run for it. - -Before, a node reached through a parent the selector also read was never observed, whether it existed before the run or was created during it. A query node created lazily inside a selector, such as `vm.snapshotsFor(id).state`, never subscribed and stayed loading forever. A selector also lost a dependency when `onObserved` wrote state while it subscribed, and `useSelect` re-ran a kept node's `onUnobserved` and `onObserved` when its other dependencies moved. diff --git a/.changeset/query-baseline-ignore.md b/.changeset/query-baseline-ignore.md deleted file mode 100644 index e7428f5..0000000 --- a/.changeset/query-baseline-ignore.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@retreejs/query": patch ---- - -Fixed an "already has a structural parent" error when a query reused a backend-cached result after an overlapping optimistic update rolled back. diff --git a/.changeset/query-detach-shared-results.md b/.changeset/query-detach-shared-results.md deleted file mode 100644 index 5208dfe..0000000 --- a/.changeset/query-detach-shared-results.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"@retreejs/query": patch -"@retreejs/convex": patch ---- - -Queries no longer adopt the objects a source emits, so two queries can share one cached result. - -Two queries that received the same cached result threw "Retree cannot assign this node because it already has a structural parent". Rows added during reconciliation were also shared between queries, so editing one query changed the other. Query state now holds its own copies, including of `initialState`. Frozen objects and opaque built-ins are still shared, and reconciliation copies only the rows and fields it writes. - -Custom reconcilers should not write objects from `next` into `current` directly. Use `reconcileArray`, or return a replacement value; it is copied, except for rows reused from `current`. diff --git a/.changeset/unsubscribe-after-clear-listeners.md b/.changeset/unsubscribe-after-clear-listeners.md deleted file mode 100644 index c707162..0000000 --- a/.changeset/unsubscribe-after-clear-listeners.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@retreejs/core": patch ---- - -Calling a `Retree.on` unsubscribe after `Retree.clearListeners` already removed that listener no longer runs the node's `onUnobserved` a second time. diff --git a/.changeset/use-select-tree-version.md b/.changeset/use-select-tree-version.md deleted file mode 100644 index e78caf4..0000000 --- a/.changeset/use-select-tree-version.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -"@retreejs/core": patch -"@retreejs/react": patch ---- - -A selector that reads `Retree.treeVersion(node)` now re-runs on every write under `node`. - -Before, a selector-only `useSelect` dropped writes to descendants the selector never read directly, unlike `Retree.select`. And `useSelect`, `Retree.select`, `Retree.effect` and `@select` getters all skipped a write to a descendant's field when the selector also read a different field of that descendant. diff --git a/package-lock.json b/package-lock.json index 56ea3ed..38a1957 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17531,7 +17531,7 @@ }, "packages/retree-babel-plugin-compiler": { "name": "@retreejs/babel-plugin-compiler", - "version": "0.11.1", + "version": "0.11.2", "license": "MIT", "dependencies": { "@babel/parser": "^7.27.0", @@ -17548,7 +17548,7 @@ }, "peerDependencies": { "@babel/core": "^7.0.0", - "@retreejs/core": "0.11.1" + "@retreejs/core": "0.11.2" } }, "packages/retree-babel-plugin-compiler/node_modules/@types/node": { @@ -17567,8 +17567,8 @@ "license": "MIT", "dependencies": { "@inquirer/prompts": "^7.0.0", - "@retreejs/core": "0.11.1", - "@retreejs/react": "0.11.1", + "@retreejs/core": "0.11.2", + "@retreejs/react": "0.11.2", "jsdom": "^26.1.0", "react": "^19.2.5", "react-dom": "^19.2.5" @@ -17585,24 +17585,24 @@ }, "packages/retree-convex": { "name": "@retreejs/convex", - "version": "0.11.1", + "version": "0.11.2", "license": "MIT", "devDependencies": { - "@retreejs/core": "0.11.1", - "@retreejs/query": "0.11.1", + "@retreejs/core": "0.11.2", + "@retreejs/query": "0.11.2", "convex": "^1.39.1", "dotenv-cli": "^11.0.0", "typescript": "^6.0.2" }, "peerDependencies": { - "@retreejs/core": "0.11.1", - "@retreejs/query": "0.11.1", + "@retreejs/core": "0.11.2", + "@retreejs/query": "0.11.2", "convex": "^1.39.1" } }, "packages/retree-core": { "name": "@retreejs/core", - "version": "0.11.1", + "version": "0.11.2", "license": "MIT", "devDependencies": { "@types/assert": "^1.5.6", @@ -17615,7 +17615,7 @@ }, "packages/retree-create": { "name": "@retreejs/create", - "version": "0.11.1", + "version": "0.11.2", "license": "MIT", "dependencies": { "@inquirer/prompts": "^7.0.0" @@ -17631,33 +17631,33 @@ }, "packages/retree-devtools": { "name": "@retreejs/devtools", - "version": "0.11.1", + "version": "0.11.2", "license": "MIT", "devDependencies": { - "@retreejs/core": "0.11.1", + "@retreejs/core": "0.11.2", "dotenv-cli": "^11.0.0", "typescript": "^6.0.2" }, "peerDependencies": { - "@retreejs/core": "0.11.1" + "@retreejs/core": "0.11.2" } }, "packages/retree-query": { "name": "@retreejs/query", - "version": "0.11.1", + "version": "0.11.2", "license": "MIT", "devDependencies": { - "@retreejs/core": "0.11.1", + "@retreejs/core": "0.11.2", "dotenv-cli": "^11.0.0", "typescript": "^6.0.2" }, "peerDependencies": { - "@retreejs/core": "0.11.1" + "@retreejs/core": "0.11.2" } }, "packages/retree-react": { "name": "@retreejs/react", - "version": "0.11.1", + "version": "0.11.2", "license": "MIT", "dependencies": { "use-sync-external-store": "^1.6.0" @@ -17666,7 +17666,7 @@ "@babel/core": "^7.29.0", "@babel/plugin-transform-modules-commonjs": "^7.28.0", "@babel/preset-react": "^7.27.0", - "@retreejs/core": "0.11.1", + "@retreejs/core": "0.11.2", "@types/react": "^19.2.3", "@types/react-dom": "^19.2.3", "@types/use-sync-external-store": "^1.5.0", @@ -17675,23 +17675,23 @@ "typescript": "^6.0.2" }, "peerDependencies": { - "@retreejs/core": "0.11.1", + "@retreejs/core": "0.11.2", "react": "^16.8.0 || ^17 || ^18 || ^19", "react-dom": "^16.8.0 || ^17 || ^18 || ^19" } }, "packages/retree-react-convex": { "name": "@retreejs/react-convex", - "version": "0.11.1", + "version": "0.11.2", "license": "MIT", "devDependencies": { - "@retreejs/convex": "0.11.1", + "@retreejs/convex": "0.11.2", "convex": "^1.39.1", "dotenv-cli": "^11.0.0", "typescript": "^6.0.2" }, "peerDependencies": { - "@retreejs/convex": "0.11.1", + "@retreejs/convex": "0.11.2", "convex": "^1.39.1" } }, diff --git a/packages/retree-babel-plugin-compiler/CHANGELOG.md b/packages/retree-babel-plugin-compiler/CHANGELOG.md index d6021ee..c60cb8c 100644 --- a/packages/retree-babel-plugin-compiler/CHANGELOG.md +++ b/packages/retree-babel-plugin-compiler/CHANGELOG.md @@ -1,5 +1,7 @@ # @retreejs/babel-plugin-compiler +## 0.11.2 + ## 0.11.1 ## 0.11.0 diff --git a/packages/retree-babel-plugin-compiler/package.json b/packages/retree-babel-plugin-compiler/package.json index 3ca66e0..fcde809 100644 --- a/packages/retree-babel-plugin-compiler/package.json +++ b/packages/retree-babel-plugin-compiler/package.json @@ -1,6 +1,6 @@ { "name": "@retreejs/babel-plugin-compiler", - "version": "0.11.1", + "version": "0.11.2", "description": "Babel plugin that compiles ReactiveNode classes into proxy-free managed classes.", "author": "Ryan Bliss", "private": false, @@ -28,7 +28,7 @@ }, "peerDependencies": { "@babel/core": "^7.0.0", - "@retreejs/core": "0.11.1" + "@retreejs/core": "0.11.2" }, "repository": { "directory": "packages/retree-babel-plugin-compiler", diff --git a/packages/retree-benchmark-cli/package.json b/packages/retree-benchmark-cli/package.json index b252671..0178b77 100644 --- a/packages/retree-benchmark-cli/package.json +++ b/packages/retree-benchmark-cli/package.json @@ -19,8 +19,8 @@ }, "dependencies": { "@inquirer/prompts": "^7.0.0", - "@retreejs/core": "0.11.1", - "@retreejs/react": "0.11.1", + "@retreejs/core": "0.11.2", + "@retreejs/react": "0.11.2", "jsdom": "^26.1.0", "react": "^19.2.5", "react-dom": "^19.2.5" diff --git a/packages/retree-convex/CHANGELOG.md b/packages/retree-convex/CHANGELOG.md index a7a1bea..2a0738e 100644 --- a/packages/retree-convex/CHANGELOG.md +++ b/packages/retree-convex/CHANGELOG.md @@ -1,5 +1,15 @@ # @retreejs/convex +## 0.11.2 + +### Patch Changes + +- ec97249: Queries no longer adopt the objects a source emits, so two queries can share one cached result. + + Two queries that received the same cached result threw "Retree cannot assign this node because it already has a structural parent". Rows added during reconciliation were also shared between queries, so editing one query changed the other. Query state now holds its own copies, including of `initialState`. Frozen objects and opaque built-ins are still shared, and reconciliation copies only the rows and fields it writes. + + Custom reconcilers should not write objects from `next` into `current` directly. Use `reconcileArray`, or return a replacement value; it is copied, except for rows reused from `current`. + ## 0.11.1 ## 0.11.0 diff --git a/packages/retree-convex/package.json b/packages/retree-convex/package.json index 267da82..7446960 100644 --- a/packages/retree-convex/package.json +++ b/packages/retree-convex/package.json @@ -1,6 +1,6 @@ { "name": "@retreejs/convex", - "version": "0.11.1", + "version": "0.11.2", "description": "Sync Convex queries into Retree reactive nodes.", "author": "Ryan Bliss", "private": false, @@ -15,15 +15,15 @@ "publish:package": "dotenv -e .env -- npm publish --access public" }, "devDependencies": { - "@retreejs/core": "0.11.1", - "@retreejs/query": "0.11.1", + "@retreejs/core": "0.11.2", + "@retreejs/query": "0.11.2", "convex": "^1.39.1", "dotenv-cli": "^11.0.0", "typescript": "^6.0.2" }, "peerDependencies": { - "@retreejs/core": "0.11.1", - "@retreejs/query": "0.11.1", + "@retreejs/core": "0.11.2", + "@retreejs/query": "0.11.2", "convex": "^1.39.1" }, "repository": { diff --git a/packages/retree-core/CHANGELOG.md b/packages/retree-core/CHANGELOG.md index 911b51f..1ec4d93 100644 --- a/packages/retree-core/CHANGELOG.md +++ b/packages/retree-core/CHANGELOG.md @@ -1,5 +1,23 @@ # @retreejs/core +## 0.11.2 + +### Patch Changes + +- 8b59d34: Built-ins that keep their state in internal slots are now leaves, so reading them from a tree works. + + A `DOMException` stored in a tree threw on `error.message` ("Illegal invocation" in browsers, `ERR_INVALID_THIS` in Node) because Retree proxied it and its accessors reject a proxy as `this`. The same broke `URL`, `Blob`, `RegExp`, typed arrays, `AbortController`, `Promise`, and other platform objects. Retree now stores these as-is, like frozen objects: reads return the object itself, and assigning a new one notifies. `Retree.root` rejects them because a root must be a node. + +- 6eece3b: A `@memo` or `@fnMemo` read inside `Retree.untracked` no longer subscribes the surrounding selector or effect to its key's reads. Before, a memo whose key had to re-run (on its first read, or after a key input changed) leaked those reads, so whether a selector stayed asleep depended on what had warmed the memo. +- 347c69e: Selector-only `Retree.select(selector, callback)`, `Retree.effect`, and `useSelect` now observe every `ReactiveNode` they read, so `onObserved` and `onUnobserved` run for it. + + Before, a node reached through a parent the selector also read was never observed, whether it existed before the run or was created during it. A query node created lazily inside a selector, such as `vm.snapshotsFor(id).state`, never subscribed and stayed loading forever. A selector also lost a dependency when `onObserved` wrote state while it subscribed, and `useSelect` re-ran a kept node's `onUnobserved` and `onObserved` when its other dependencies moved. + +- 651f298: Calling a `Retree.on` unsubscribe after `Retree.clearListeners` already removed that listener no longer runs the node's `onUnobserved` a second time. +- 651f298: A selector that reads `Retree.treeVersion(node)` now re-runs on every write under `node`. + + Before, a selector-only `useSelect` dropped writes to descendants the selector never read directly, unlike `Retree.select`. And `useSelect`, `Retree.select`, `Retree.effect` and `@select` getters all skipped a write to a descendant's field when the selector also read a different field of that descendant. + ## 0.11.1 ### Patch Changes diff --git a/packages/retree-core/package.json b/packages/retree-core/package.json index 20d9bd1..138ceb9 100644 --- a/packages/retree-core/package.json +++ b/packages/retree-core/package.json @@ -1,6 +1,6 @@ { "name": "@retreejs/core", - "version": "0.11.1", + "version": "0.11.2", "description": "React to changes in your object trees.", "author": "Ryan Bliss", "private": false, diff --git a/packages/retree-create/CHANGELOG.md b/packages/retree-create/CHANGELOG.md index 252c057..afe4b6f 100644 --- a/packages/retree-create/CHANGELOG.md +++ b/packages/retree-create/CHANGELOG.md @@ -1,5 +1,7 @@ # @retreejs/create +## 0.11.2 + ## 0.11.1 ## 0.11.0 diff --git a/packages/retree-create/package.json b/packages/retree-create/package.json index d5a55d9..9ee0010 100644 --- a/packages/retree-create/package.json +++ b/packages/retree-create/package.json @@ -1,6 +1,6 @@ { "name": "@retreejs/create", - "version": "0.11.1", + "version": "0.11.2", "description": "Interactive installer that adds Retree packages to an existing project.", "author": "Ryan Bliss", "license": "MIT", diff --git a/packages/retree-devtools/CHANGELOG.md b/packages/retree-devtools/CHANGELOG.md index a42fc1f..d48c0cf 100644 --- a/packages/retree-devtools/CHANGELOG.md +++ b/packages/retree-devtools/CHANGELOG.md @@ -1,5 +1,7 @@ # @retreejs/devtools +## 0.11.2 + ## 0.11.1 ## 0.11.0 diff --git a/packages/retree-devtools/package.json b/packages/retree-devtools/package.json index 3fd445a..e52e336 100644 --- a/packages/retree-devtools/package.json +++ b/packages/retree-devtools/package.json @@ -1,6 +1,6 @@ { "name": "@retreejs/devtools", - "version": "0.11.1", + "version": "0.11.2", "description": "Redux DevTools bridge and structured change-log taps for Retree reactive state.", "author": "Ryan Bliss", "private": false, @@ -16,12 +16,12 @@ "publish:package": "dotenv -e .env -- npm publish --access public" }, "devDependencies": { - "@retreejs/core": "0.11.1", + "@retreejs/core": "0.11.2", "dotenv-cli": "^11.0.0", "typescript": "^6.0.2" }, "peerDependencies": { - "@retreejs/core": "0.11.1" + "@retreejs/core": "0.11.2" }, "repository": { "directory": "packages/retree-devtools", diff --git a/packages/retree-query/CHANGELOG.md b/packages/retree-query/CHANGELOG.md index 97d3f5a..e503a49 100644 --- a/packages/retree-query/CHANGELOG.md +++ b/packages/retree-query/CHANGELOG.md @@ -1,5 +1,16 @@ # @retreejs/query +## 0.11.2 + +### Patch Changes + +- 2b64407: Fixed an "already has a structural parent" error when a query reused a backend-cached result after an overlapping optimistic update rolled back. +- ec97249: Queries no longer adopt the objects a source emits, so two queries can share one cached result. + + Two queries that received the same cached result threw "Retree cannot assign this node because it already has a structural parent". Rows added during reconciliation were also shared between queries, so editing one query changed the other. Query state now holds its own copies, including of `initialState`. Frozen objects and opaque built-ins are still shared, and reconciliation copies only the rows and fields it writes. + + Custom reconcilers should not write objects from `next` into `current` directly. Use `reconcileArray`, or return a replacement value; it is copied, except for rows reused from `current`. + ## 0.11.1 ## 0.11.0 diff --git a/packages/retree-query/package.json b/packages/retree-query/package.json index 4938141..f65df46 100644 --- a/packages/retree-query/package.json +++ b/packages/retree-query/package.json @@ -1,6 +1,6 @@ { "name": "@retreejs/query", - "version": "0.11.1", + "version": "0.11.2", "description": "Backend-agnostic async query nodes for Retree reactive state.", "author": "Ryan Bliss", "private": false, @@ -16,12 +16,12 @@ "publish:package": "dotenv -e .env -- npm publish --access public" }, "devDependencies": { - "@retreejs/core": "0.11.1", + "@retreejs/core": "0.11.2", "dotenv-cli": "^11.0.0", "typescript": "^6.0.2" }, "peerDependencies": { - "@retreejs/core": "0.11.1" + "@retreejs/core": "0.11.2" }, "repository": { "directory": "packages/retree-query", diff --git a/packages/retree-react-convex/CHANGELOG.md b/packages/retree-react-convex/CHANGELOG.md index 84cbd15..7af9770 100644 --- a/packages/retree-react-convex/CHANGELOG.md +++ b/packages/retree-react-convex/CHANGELOG.md @@ -1,5 +1,7 @@ # @retreejs/react-convex +## 0.11.2 + ## 0.11.1 ## 0.11.0 diff --git a/packages/retree-react-convex/package.json b/packages/retree-react-convex/package.json index 584fcbd..664c636 100644 --- a/packages/retree-react-convex/package.json +++ b/packages/retree-react-convex/package.json @@ -1,6 +1,6 @@ { "name": "@retreejs/react-convex", - "version": "0.11.1", + "version": "0.11.2", "description": "Use ConvexReactClient with Retree Convex nodes.", "author": "Ryan Bliss", "license": "MIT", @@ -16,13 +16,13 @@ "publish:package": "dotenv -e .env -- npm publish --access public" }, "devDependencies": { - "@retreejs/convex": "0.11.1", + "@retreejs/convex": "0.11.2", "convex": "^1.39.1", "dotenv-cli": "^11.0.0", "typescript": "^6.0.2" }, "peerDependencies": { - "@retreejs/convex": "0.11.1", + "@retreejs/convex": "0.11.2", "convex": "^1.39.1" }, "repository": { diff --git a/packages/retree-react/CHANGELOG.md b/packages/retree-react/CHANGELOG.md index 674f6b8..8463314 100644 --- a/packages/retree-react/CHANGELOG.md +++ b/packages/retree-react/CHANGELOG.md @@ -1,5 +1,21 @@ # @retreejs/react +## 0.11.2 + +### Patch Changes + +- 2113570: `useSelect` no longer re-runs `onUnobserved` and `onObserved` on a `ReactiveNode` it keeps reading when a prop moves the selector's other dependencies. Query nodes such as `ConvexQueryNode` no longer close and reopen their backend subscription on those re-renders. + + React hooks now release a `ReactiveNode` one microtask after its last listener unsubscribes, so `onUnobserved` runs just after unmount rather than during it. Tests that fake `queueMicrotask`, as Jest's fake timers do by default, need to flush microtasks (`jest.runAllTicks()`) before asserting on it. + +- 347c69e: Selector-only `Retree.select(selector, callback)`, `Retree.effect`, and `useSelect` now observe every `ReactiveNode` they read, so `onObserved` and `onUnobserved` run for it. + + Before, a node reached through a parent the selector also read was never observed, whether it existed before the run or was created during it. A query node created lazily inside a selector, such as `vm.snapshotsFor(id).state`, never subscribed and stayed loading forever. A selector also lost a dependency when `onObserved` wrote state while it subscribed, and `useSelect` re-ran a kept node's `onUnobserved` and `onObserved` when its other dependencies moved. + +- 651f298: A selector that reads `Retree.treeVersion(node)` now re-runs on every write under `node`. + + Before, a selector-only `useSelect` dropped writes to descendants the selector never read directly, unlike `Retree.select`. And `useSelect`, `Retree.select`, `Retree.effect` and `@select` getters all skipped a write to a descendant's field when the selector also read a different field of that descendant. + ## 0.11.1 ### Patch Changes diff --git a/packages/retree-react/package.json b/packages/retree-react/package.json index 24a2fba..ab9de24 100644 --- a/packages/retree-react/package.json +++ b/packages/retree-react/package.json @@ -1,6 +1,6 @@ { "name": "@retreejs/react", - "version": "0.11.1", + "version": "0.11.2", "description": "Easily build stateful React applications using familiar JavaScript patterns.", "author": "Ryan Bliss", "license": "MIT", @@ -18,7 +18,7 @@ "@babel/core": "^7.29.0", "@babel/plugin-transform-modules-commonjs": "^7.28.0", "@babel/preset-react": "^7.27.0", - "@retreejs/core": "0.11.1", + "@retreejs/core": "0.11.2", "@types/react": "^19.2.3", "@types/react-dom": "^19.2.3", "@types/use-sync-external-store": "^1.5.0", @@ -27,7 +27,7 @@ "typescript": "^6.0.2" }, "peerDependencies": { - "@retreejs/core": "0.11.1", + "@retreejs/core": "0.11.2", "react": "^16.8.0 || ^17 || ^18 || ^19", "react-dom": "^16.8.0 || ^17 || ^18 || ^19" },