Describe the bug
After any write to a store subtree, snapshot() of it and copying it into another store both drop symbol-keyed properties. Before the first write they're preserved (identity return path), so the loss appears only once the object has an override.
Symbol keys are the standard way a data layer attaches metadata that cannot collide with API fields (cache etags, dirty flags). Dropping them breaks save/undo flows that snapshot edited store state before sending it to a worker, cache, or another store — and because the failure only appears after a normal field edit, initial tests with untouched records pass. The internal store-to-store materialization path uses the same key enumeration, so copying a previously written subtree into another store drops its symbol keys too.
Your Example Website or App
Live reproduction — opens the standalone browser repro in StackBlitz.
The repro keeps an invoice record with a symbol-keyed cache tag, performs one ordinary field edit, and checks the tag on the resulting snapshot.
import { createSignal, createStore, flush, snapshot } from "solid-js";
const cacheMeta = Symbol("cacheMeta");
export default function App() {
const [invoice, setInvoice] = createStore({ total: 100, [cacheMeta]: "etag-v1" } as any);
const [result, setResult] = createSignal("Click the button to run the repro.");
function editAndSnapshot() {
setInvoice((draft: any) => {
draft.total = 125;
});
flush();
const copy = snapshot(invoice) as any;
setResult(
copy[cacheMeta] === "etag-v1"
? "PASS - snapshot kept the symbol key"
: `FAIL - snapshot[cacheMeta] is ${String(copy[cacheMeta])}`
);
}
return (
<main style={{ "font-family": "system-ui", padding: "16px" }}>
<h2>snapshot() drops symbol keys after a write</h2>
<p>total: {invoice.total}</p>
<button onClick={editAndSnapshot}>edit total and snapshot</button>
<pre>{result()}</pre>
</main>
);
}
Steps to Reproduce the Bug or Issue
- Click edit total and snapshot. The handler writes
total = 125, flushes, and snapshots the record.
- The edited total is present, but the symbol-keyed cache tag is missing.
On 2.0.0-beta.18 the page shows:
FAIL - snapshot[cacheMeta] is undefined
Expected behavior
Symbol keys survive snapshot() and internal store-to-store materialization after writes, as in Solid 1.x's equivalent unwrap() behavior:
PASS - snapshot kept the symbol key
Screenshots or Videos
No response
Platform
- OS: macOS
- Browser: Chrome
- Version: 2.0.0-beta.18 (re-verified at
next @ 1dc0b45e)
Additional context
Root cause: getKeys() (packages/solid-signals/src/store/store.ts:511) seeds base keys from Object.keys(source) (strings only), and drives both snapshotImpl (utils.ts:23) and unwrapStoreValue (store.ts:176). Residual gap of #2769, which added ownEnumerableKeys only to the setter/merge/omit/set-trap sites — not these internal materialization paths.
Because unwrapStoreValue shares the same enumeration, writing a written store subtree into another store also drops its symbol-keyed props (third case in the repro test).
Repro test: packages/solid-signals/tests/store/hunt2-snapshot-symbol-keys.test.ts (2 failing + control). 1.x check: hunt-1x-checks/checks/w2-store-unwrap-symbol-keys.test.ts.
Does this exist in Solid 1.x?
Regression. Verified against 1.9.14: unwrap after a write keeps symbol keys, and copying a written subtree into another store keeps them.
Describe the bug
After any write to a store subtree,
snapshot()of it and copying it into another store both drop symbol-keyed properties. Before the first write they're preserved (identity return path), so the loss appears only once the object has an override.Symbol keys are the standard way a data layer attaches metadata that cannot collide with API fields (cache etags, dirty flags). Dropping them breaks save/undo flows that snapshot edited store state before sending it to a worker, cache, or another store — and because the failure only appears after a normal field edit, initial tests with untouched records pass. The internal store-to-store materialization path uses the same key enumeration, so copying a previously written subtree into another store drops its symbol keys too.
Your Example Website or App
Live reproduction — opens the standalone browser repro in StackBlitz.
The repro keeps an invoice record with a symbol-keyed cache tag, performs one ordinary field edit, and checks the tag on the resulting snapshot.
Steps to Reproduce the Bug or Issue
total = 125, flushes, and snapshots the record.On 2.0.0-beta.18 the page shows:
Expected behavior
Symbol keys survive
snapshot()and internal store-to-store materialization after writes, as in Solid 1.x's equivalentunwrap()behavior:Screenshots or Videos
No response
Platform
next@1dc0b45e)Additional context
Root cause:
getKeys()(packages/solid-signals/src/store/store.ts:511) seeds base keys fromObject.keys(source)(strings only), and drives bothsnapshotImpl(utils.ts:23) andunwrapStoreValue(store.ts:176). Residual gap of #2769, which addedownEnumerableKeysonly to the setter/merge/omit/set-trap sites — not these internal materialization paths.Because
unwrapStoreValueshares the same enumeration, writing a written store subtree into another store also drops its symbol-keyed props (third case in the repro test).Repro test:
packages/solid-signals/tests/store/hunt2-snapshot-symbol-keys.test.ts(2 failing + control). 1.x check:hunt-1x-checks/checks/w2-store-unwrap-symbol-keys.test.ts.Does this exist in Solid 1.x?
Regression. Verified against 1.9.14:
unwrapafter a write keeps symbol keys, and copying a written subtree into another store keeps them.