From 6fad330efceb5af8e49d558394ddb492fca259db Mon Sep 17 00:00:00 2001 From: Luke Parker <10430890+Hona@users.noreply.github.com> Date: Fri, 28 Aug 2026 13:03:08 +1000 Subject: [PATCH] chore(deps): upgrade Solid and Router (#45808) --- bun.lock | 21 +++-- package.json | 9 +- .../app/test-browser/session-router.test.ts | 85 ++++++++++++++++++ .../app/test-browser/solid-runtime.test.ts | 89 +++++++++++++++++++ packages/desktop/package.json | 2 +- ...-js@1.9.10.patch => solid-js@1.9.15.patch} | 50 +---------- 6 files changed, 197 insertions(+), 59 deletions(-) create mode 100644 packages/app/test-browser/session-router.test.ts create mode 100644 packages/app/test-browser/solid-runtime.test.ts rename patches/{solid-js@1.9.10.patch => solid-js@1.9.15.patch} (68%) diff --git a/bun.lock b/bun.lock index e43b4fd8a81..5bd3f07372f 100644 --- a/bun.lock +++ b/bun.lock @@ -426,7 +426,7 @@ "@sentry/vite-plugin": "catalog:", "@solid-primitives/storage": "catalog:", "@solidjs/meta": "catalog:", - "@solidjs/router": "0.15.4", + "@solidjs/router": "catalog:", "@types/bun": "catalog:", "@types/node": "catalog:", "@typescript/native-preview": "catalog:", @@ -1043,7 +1043,7 @@ "@ai-sdk/mistral@3.0.51": "patches/@ai-sdk%2Fmistral@3.0.51.patch", "@npmcli/agent@4.0.2": "patches/@npmcli%2Fagent@4.0.2.patch", "@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch", - "solid-js@1.9.10": "patches/solid-js@1.9.10.patch", + "solid-js@1.9.15": "patches/solid-js@1.9.15.patch", "@ff-labs/fff-bun@0.10.5": "patches/@ff-labs%2Ffff-bun@0.10.5.patch", "@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch", "@dnd-kit/dom@0.5.0": "patches/@dnd-kit%2Fdom@0.5.0.patch", @@ -1057,6 +1057,7 @@ "@types/bun": "catalog:", "@types/node": "catalog:", "effect": "catalog:", + "solid-js": "catalog:", }, "catalog": { "@cloudflare/workers-types": "4.20251008.0", @@ -1085,7 +1086,7 @@ "@solid-primitives/resize-observer": "2.2.0", "@solid-primitives/storage": "4.4.0", "@solidjs/meta": "0.29.4", - "@solidjs/router": "0.15.4", + "@solidjs/router": "1.0.0", "@solidjs/start": "https://pkg.pr.new/@solidjs/start@dfb2020", "@standard-schema/spec": "1.1.0", "@tailwindcss/vite": "4.1.11", @@ -1118,7 +1119,7 @@ "resolve.exports": "2.0.3", "semver": "7.7.4", "shiki": "4.4.3", - "solid-js": "1.9.10", + "solid-js": "1.9.15", "solid-list": "0.3.0", "solid-sonner": "0.3.1", "sst": "4.13.1", @@ -2893,7 +2894,7 @@ "@solidjs/meta": ["@solidjs/meta@0.29.4", "", { "peerDependencies": { "solid-js": ">=1.8.4" } }, "sha512-zdIWBGpR9zGx1p1bzIPqF5Gs+Ks/BH8R6fWhmUa/dcK1L2rUC8BAcZJzNRYBQv74kScf1TSOs0EY//Vd/I0V8g=="], - "@solidjs/router": ["@solidjs/router@0.15.4", "", { "peerDependencies": { "solid-js": "^1.8.6" } }, "sha512-WOpgg9a9T638cR+5FGbFi/IV4l2FpmBs1GpIMSPa0Ce9vyJN7Wts+X2PqMf9IYn0zUj2MlSJtm1gp7/HI/n5TQ=="], + "@solidjs/router": ["@solidjs/router@1.0.0", "", { "peerDependencies": { "solid-js": "^1.8.6" } }, "sha512-cCSk1hvgCowiMa9bzzYWHiLu1U4E22+DfJe6/rOwAyECKrxc3jrd5QnoW3sDDJtW+e077cz/M67bPl3DqOBw1Q=="], "@solidjs/start": ["@solidjs/start@https://pkg.pr.new/@solidjs/start@dfb2020", { "dependencies": { "@babel/core": "^7.28.3", "@babel/traverse": "^7.28.3", "@babel/types": "^7.28.5", "@solidjs/meta": "^0.29.4", "@tanstack/server-functions-plugin": "1.134.5", "@types/babel__traverse": "^7.28.0", "@types/micromatch": "^4.0.9", "cookie-es": "^2.0.0", "defu": "^6.1.4", "error-stack-parser": "^2.1.4", "es-module-lexer": "^1.7.0", "esbuild": "^0.25.3", "fast-glob": "^3.3.3", "h3": "npm:h3@2.0.1-rc.4", "html-to-image": "^1.11.13", "micromatch": "^4.0.8", "path-to-regexp": "^8.2.0", "pathe": "^2.0.3", "radix3": "^1.1.2", "seroval": "^1.3.2", "seroval-plugins": "^1.2.1", "shiki": "^1.26.1", "solid-js": "^1.9.9", "source-map-js": "^1.2.1", "srvx": "^0.9.1", "terracotta": "^1.0.6", "vite": "7.1.10", "vite-plugin-solid": "^2.11.9", "vitest": "^4.0.10" } }, "sha512-7JjjA49VGNOsMRI8QRUhVudZmv0CnJ18SliSgK1ojszs/c3ijftgVkzvXdkSLN4miDTzbkXewf65D6ZBo6W+GQ=="], @@ -5235,9 +5236,9 @@ "serialize-javascript": ["serialize-javascript@7.1.0", "", {}, "sha512-RNEqWOyhhUQYN9V1GfHwu9AR/g+NTciH6Z5u3/no6X3/w+04J2lVDL+svFQVXgXrEGBMG2puMVN3gq2SNGuTGw=="], - "seroval": ["seroval@1.3.2", "", {}, "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ=="], + "seroval": ["seroval@1.5.6", "", {}, "sha512-rVQVWjjSvlINzaQPZH5JFqsqEsIWdTxY3iJZCnTL/5gQbXIRooVZKI60tVCkOVfzcRPejboxO2t0P89dg5mQaA=="], - "seroval-plugins": ["seroval-plugins@1.3.3", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w=="], + "seroval-plugins": ["seroval-plugins@1.5.6", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-HXuLAX2pu/UByPpaeo/TaMfvMIi+1QqIoPJYCcAtU8QkVNwgR6MPlGuCQTErV1JwraaMbYaWVIBX7mppzGLATQ=="], "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], @@ -5299,7 +5300,7 @@ "socks-proxy-agent": ["socks-proxy-agent@8.0.5", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" } }, "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw=="], - "solid-js": ["solid-js@1.9.10", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.3.0", "seroval-plugins": "~1.3.0" } }, "sha512-Coz956cos/EPDlhs6+jsdTxKuJDPT7B5SVIWgABwROyxjY7Xbr8wkzD68Et+NxnV7DLJ3nJdAC2r9InuV/4Jew=="], + "solid-js": ["solid-js@1.9.15", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.5.4", "seroval-plugins": "~1.5.4" } }, "sha512-EeiY2xfpZJqPLjXspVEKjAII4yv8NyG//NxZ3IpOFHdUNnnTyL0uJOeS9LWGvA7cFCz5y94cjFwYlmw5Luncsg=="], "solid-list": ["solid-list@0.3.0", "", { "dependencies": { "@corvu/utils": "~0.4.0" }, "peerDependencies": { "solid-js": "^1.8" } }, "sha512-t4hx/F/l8Vmq+ib9HtZYl7Z9F1eKxq3eKJTXlvcm7P7yI4Z8O7QSOOEVHb/K6DD7M0RxzVRobK/BS5aSfLRwKg=="], @@ -6307,6 +6308,10 @@ "@solidjs/start/path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="], + "@solidjs/start/seroval": ["seroval@1.3.2", "", {}, "sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ=="], + + "@solidjs/start/seroval-plugins": ["seroval-plugins@1.3.3", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w=="], + "@solidjs/start/shiki": ["shiki@1.29.2", "", { "dependencies": { "@shikijs/core": "1.29.2", "@shikijs/engine-javascript": "1.29.2", "@shikijs/engine-oniguruma": "1.29.2", "@shikijs/langs": "1.29.2", "@shikijs/themes": "1.29.2", "@shikijs/types": "1.29.2", "@shikijs/vscode-textmate": "^10.0.1", "@types/hast": "^3.0.4" } }, "sha512-njXuliz/cP+67jU2hukkxCNuH1yUi4QfdZZY+sMr5PPrIyXSu5iTb/qYC4BiWWB0vZ+7TbdvYUCeL23zpwCfbg=="], "@solidjs/start/vite": ["vite@7.1.10", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA=="], diff --git a/package.json b/package.json index 5e69c93016e..807ac0ea548 100644 --- a/package.json +++ b/package.json @@ -101,11 +101,11 @@ "tailwindcss": "4.1.11", "vite": "7.3.6", "@solidjs/meta": "0.29.4", - "@solidjs/router": "0.15.4", + "@solidjs/router": "1.0.0", "@solidjs/start": "https://pkg.pr.new/@solidjs/start@dfb2020", "@sentry/solid": "10.71.0", "@sentry/vite-plugin": "5.4.0", - "solid-js": "1.9.10", + "solid-js": "1.9.15", "solid-sonner": "0.3.1", "vite-plugin-solid": "2.11.10", "@lydell/node-pty": "1.2.0-beta.12" @@ -161,7 +161,8 @@ "@effect/platform-node-shared": "catalog:", "@types/bun": "catalog:", "@types/node": "catalog:", - "effect": "catalog:" + "effect": "catalog:", + "solid-js": "catalog:" }, "patchedDependencies": { "@ai-sdk/openai-compatible@2.0.41": "patches/@ai-sdk%2Fopenai-compatible@2.0.41.patch", @@ -169,7 +170,7 @@ "@npmcli/agent@4.0.2": "patches/@npmcli%2Fagent@4.0.2.patch", "@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch", "@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch", - "solid-js@1.9.10": "patches/solid-js@1.9.10.patch", + "solid-js@1.9.15": "patches/solid-js@1.9.15.patch", "@ai-sdk/mistral@3.0.51": "patches/@ai-sdk%2Fmistral@3.0.51.patch", "gcp-metadata@8.1.2": "patches/gcp-metadata@8.1.2.patch", "pacote@21.5.0": "patches/pacote@21.5.0.patch", diff --git a/packages/app/test-browser/session-router.test.ts b/packages/app/test-browser/session-router.test.ts new file mode 100644 index 00000000000..615833815f0 --- /dev/null +++ b/packages/app/test-browser/session-router.test.ts @@ -0,0 +1,85 @@ +import { expect, test } from "bun:test" +import { MemoryRouter, Route, createMemoryHistory, useNavigate, useParams } from "@solidjs/router" +import { createEffect, onCleanup } from "solid-js" +import { createComponent, render } from "solid-js/web" +import { createSessionOwnership } from "@/session/session-ownership" + +test("session ownership follows navigation, back, forward, replacement, and route disposal", async () => { + const host = document.createElement("div") + const history = createMemoryHistory() + history.set({ value: "/session/A", replace: true, scroll: false }) + const captured: ReturnType["capture"]>[] = [] + const navigate: ReturnType[] = [] + const cleaned: string[] = [] + const settle = () => new Promise((resolve) => setTimeout(resolve, 0)) + + const Session = () => { + const params = useParams<{ id: string }>() + const ownership = createSessionOwnership(() => params.id) + const label = document.createElement("div") + navigate.push(useNavigate()) + createEffect(() => { + label.textContent = params.id + captured.push(ownership.capture()) + }) + onCleanup(() => cleaned.push("session")) + return label + } + + const dispose = render( + () => + createComponent(MemoryRouter, { + history, + get children() { + return [ + createComponent(Route, { path: "/", component: () => "Home" }), + createComponent(Route, { path: "/session/:id", component: Session }), + ] + }, + }), + host, + ) + + try { + expect(host.textContent).toBe("A") + expect(captured[0].current()).toBe(true) + + navigate[0]("/session/B") + await settle() + expect(host.textContent).toBe("B") + expect(captured[0].current()).toBe(false) + expect(captured[1].current()).toBe(true) + + history.back() + await settle() + expect(host.textContent).toBe("A") + expect(captured[0].current()).toBe(false) + expect(captured[1].current()).toBe(false) + expect(captured[2].current()).toBe(true) + + history.forward() + await settle() + expect(host.textContent).toBe("B") + expect(captured[2].current()).toBe(false) + expect(captured[3].current()).toBe(true) + expect(navigate).toHaveLength(1) + expect(cleaned).toEqual([]) + + navigate[0]("/", { replace: true }) + await settle() + expect(host.textContent).toBe("Home") + expect(captured.every((owner) => !owner.current())).toBe(true) + expect(cleaned).toEqual(["session"]) + + history.back() + await settle() + expect(host.textContent).toBe("A") + expect(captured[4].current()).toBe(true) + expect(navigate).toHaveLength(2) + } finally { + dispose() + } + + expect(captured.every((owner) => !owner.current())).toBe(true) + expect(cleaned).toEqual(["session", "session"]) +}) diff --git a/packages/app/test-browser/solid-runtime.test.ts b/packages/app/test-browser/solid-runtime.test.ts new file mode 100644 index 00000000000..170ec5cf407 --- /dev/null +++ b/packages/app/test-browser/solid-runtime.test.ts @@ -0,0 +1,89 @@ +import { expect, test } from "bun:test" +import { + createComputed, + createMemo, + createResource, + createRoot, + createSignal, + onCleanup, + startTransition, + Suspense, + type Accessor, + type JSX, +} from "solid-js" +import { createComponent, render } from "solid-js/web" + +// Solid 1.9.15 includes the former transition patch for solidjs/solid#2046. +test("a memo created during a paused transition has a committed value", async () => { + const host = document.createElement("div") + const loaded = Promise.withResolvers() + const started = Promise.withResolvers() + const memos: Accessor[] = [] + const [session, setSession] = createSignal(false) + const dispose = render( + () => + createComponent(Suspense, { + fallback: "Loading", + get children() { + return createMemo(() => { + if (!session()) return "Home" + memos.push(createMemo(() => "Session")) + const [data] = createResource(() => loaded.promise) + started.resolve() + return createMemo(() => data() ?? "") + }) as unknown as JSX.Element + }, + }), + host, + ) + + try { + expect(host.textContent).toBe("Home") + const pending = startTransition(() => setSession(true)) + await started.promise + expect(host.textContent).toBe("Home") + expect(memos).toHaveLength(1) + expect(memos[0]()).toBe("Session") + + loaded.resolve("Session loaded") + await pending + expect(host.textContent).toBe("Session loaded") + expect(memos[0]()).toBe("Session") + } finally { + loaded.resolve("Session loaded") + dispose() + } +}) + +// Exercise each patched bundle independently; importing both dev formats emits Solid's duplicate-runtime warning. +test.each(["solid.js", "solid.cjs", "dev.js", "dev.cjs", "server.js", "server.cjs"])( + "%s reentrant disposal cleans owned computations and callbacks exactly once", + async (file) => { + const runtime: { + createRoot: typeof createRoot + createComputed: typeof createComputed + onCleanup: typeof onCleanup + } = await import(`solid-js/dist/${file}`) + const cleaned: string[] = [] + const dispose = runtime.createRoot((dispose) => { + runtime.onCleanup(() => cleaned.push("root")) + runtime.createComputed(() => { + runtime.onCleanup(() => cleaned.push("first")) + }) + runtime.createComputed(() => { + runtime.onCleanup(() => cleaned.push("second")) + runtime.onCleanup(() => { + cleaned.push("reentrant") + // Bound the regression so an unpatched runtime fails without overflowing the stack. + if (cleaned.filter((item) => item === "reentrant").length === 1) dispose() + }) + }) + return dispose + }) + + expect(dispose).not.toThrow() + expect(cleaned.toSorted()).toEqual(["first", "reentrant", "root", "second"]) + dispose() + expect(cleaned).toHaveLength(4) + }, +) diff --git a/packages/desktop/package.json b/packages/desktop/package.json index ecb8b11ed4e..8af742a1936 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -40,7 +40,7 @@ "@sentry/vite-plugin": "catalog:", "@solid-primitives/storage": "catalog:", "@solidjs/meta": "catalog:", - "@solidjs/router": "0.15.4", + "@solidjs/router": "catalog:", "@types/bun": "catalog:", "@types/node": "catalog:", "@typescript/native-preview": "catalog:", diff --git a/patches/solid-js@1.9.10.patch b/patches/solid-js@1.9.15.patch similarity index 68% rename from patches/solid-js@1.9.10.patch rename to patches/solid-js@1.9.15.patch index 0571d5c8c67..7bebffc025f 100644 --- a/patches/solid-js@1.9.10.patch +++ b/patches/solid-js@1.9.15.patch @@ -1,17 +1,7 @@ diff --git a/dist/dev.cjs b/dist/dev.cjs -index 7104749..dc3eac9 100644 --- a/dist/dev.cjs +++ b/dist/dev.cjs -@@ -764,6 +764,8 @@ function runComputation(node, value, time) { - if (node.updatedAt != null && "observers" in node) { - writeSignal(node, nextValue, true); - } else if (Transition && Transition.running && node.pure) { -+ // On first computation during transition, also set committed value #2046 -+ if (!Transition.sources.has(node)) node.value = nextValue; - Transition.sources.add(node); - node.tValue = nextValue; - } else node.value = nextValue; -@@ -987,18 +989,21 @@ function cleanNode(node) { +@@ -1009,18 +1009,21 @@ function cleanNode(node) { } } if (node.tOwned) { @@ -37,19 +27,9 @@ index 7104749..dc3eac9 100644 if (Transition && Transition.running) node.tState = 0;else node.state = 0; delete node.sourceMap; diff --git a/dist/dev.js b/dist/dev.js -index ea5e4bc..a2e2d59 100644 --- a/dist/dev.js +++ b/dist/dev.js -@@ -762,6 +762,8 @@ function runComputation(node, value, time) { - if (node.updatedAt != null && "observers" in node) { - writeSignal(node, nextValue, true); - } else if (Transition && Transition.running && node.pure) { -+ // On first computation during transition, also set committed value #2046 -+ if (!Transition.sources.has(node)) node.value = nextValue; - Transition.sources.add(node); - node.tValue = nextValue; - } else node.value = nextValue; -@@ -985,18 +987,21 @@ function cleanNode(node) { +@@ -1007,18 +1007,21 @@ function cleanNode(node) { } } if (node.tOwned) { @@ -75,7 +55,6 @@ index ea5e4bc..a2e2d59 100644 if (Transition && Transition.running) node.tState = 0;else node.state = 0; delete node.sourceMap; diff --git a/dist/server.cjs b/dist/server.cjs -index e715309..188ba81 100644 --- a/dist/server.cjs +++ b/dist/server.cjs @@ -127,12 +127,14 @@ function onCleanup(fn) { @@ -96,7 +75,6 @@ index e715309..188ba81 100644 } function catchError(fn, handler) { diff --git a/dist/server.js b/dist/server.js -index d5f8803..320d9af 100644 --- a/dist/server.js +++ b/dist/server.js @@ -125,12 +125,14 @@ function onCleanup(fn) { @@ -117,19 +95,9 @@ index d5f8803..320d9af 100644 } function catchError(fn, handler) { diff --git a/dist/solid.cjs b/dist/solid.cjs -index 7c133a2..5ef1501 100644 --- a/dist/solid.cjs +++ b/dist/solid.cjs -@@ -717,6 +717,8 @@ function runComputation(node, value, time) { - if (node.updatedAt != null && "observers" in node) { - writeSignal(node, nextValue, true); - } else if (Transition && Transition.running && node.pure) { -+ // On first computation during transition, also set committed value #2046 -+ if (!Transition.sources.has(node)) node.value = nextValue; - Transition.sources.add(node); - node.tValue = nextValue; - } else node.value = nextValue; -@@ -938,18 +940,21 @@ function cleanNode(node) { +@@ -964,18 +964,21 @@ function cleanNode(node) { } } if (node.tOwned) { @@ -155,19 +123,9 @@ index 7c133a2..5ef1501 100644 if (Transition && Transition.running) node.tState = 0;else node.state = 0; } diff --git a/dist/solid.js b/dist/solid.js -index 656fd26..6e0038c 100644 --- a/dist/solid.js +++ b/dist/solid.js -@@ -715,6 +715,8 @@ function runComputation(node, value, time) { - if (node.updatedAt != null && "observers" in node) { - writeSignal(node, nextValue, true); - } else if (Transition && Transition.running && node.pure) { -+ // On first computation during transition, also set committed value #2046 -+ if (!Transition.sources.has(node)) node.value = nextValue; - Transition.sources.add(node); - node.tValue = nextValue; - } else node.value = nextValue; -@@ -936,18 +938,21 @@ function cleanNode(node) { +@@ -962,18 +962,21 @@ function cleanNode(node) { } } if (node.tOwned) {