@@ -10,7 +10,7 @@ The package `solid-querybuilder` is a **Solid 2.0** port of
1010document order, ` data-testid ` , ` data-path ` , and byte-identical ` class ` attributes must match
1111React Query Builder's output for all conformance cases.
1212
13- Blueprints: ` svelte-querybuilder@0.1.1 ` (Phase 1) and ` @react-querybuilder/vue@0.2.0 ` (Phase 2) .
13+ Blueprints: ` svelte-querybuilder@0.1.1 ` and ` @react-querybuilder/vue@0.2.0 ` .
1414Deviate only where Solid idiom demands.
1515
1616```
@@ -21,7 +21,7 @@ solid-querybuilder/
2121│ └── scripts/ # build/check/ssr-smoke helpers
2222└── examples/
2323 ├── demo/ # Vite + Solid 2, aliased to the library's src
24- └── ssr/ # hand-rolled Vite SSR consumer; the phase SSR gate
24+ └── ssr/ # hand-rolled Vite SSR consumer; the SSR gate
2525```
2626
2727** Target is Solid 2 only.** Peers are ` solid-js@^2.0.0-beta.32 ` ** and
@@ -32,8 +32,7 @@ shims or `solid-js@1` code paths here.
3232
3333Wiring strategy is ** hybrid** : ` QueryManager ` owns every write (history, guards, ` reconfigure ` );
3434an internal store mirror (` reconcile ` d by ` id ` ) is the read path. See
35- ` ~/git/SOLID_QB_PLAN.md ` for the full rationale and step-by-step plan; this file only records the
36- standing rules that apply to every step.
35+ ` ~/git/SOLID_QB_PLAN.md ` for the full rationale; this file only records the standing rules.
3736
3837## Commands
3938
@@ -45,11 +44,10 @@ standing rules that apply to every step.
4544- ` bun run check:versions ` — asserts the resolved prerelease toolchain has not drifted; runs
4645 first in CI
4746- ` bun run test:ssr ` — ** two halves, in sequence.** First ` packages/solid-querybuilder ` 's
48- ` scripts/ssr-smoke.ts ` (the step-1 script: export-condition order in isolation, plus a markup
49- assertion). Then ` examples/ssr ` 's ` ssr-smoke-test.ts ` (step 8: builds the example against the
50- published ` dist ` , serves it, asserts status + markup, then hydrates in jsdom). Step 8
51- ** supersedes but does not replace** the first — keep both; only the first checks the export
52- condition in isolation.
47+ ` scripts/ssr-smoke.ts ` (export-condition order in isolation, plus a markup assertion). Then
48+ ` examples/ssr ` 's ` ssr-smoke-test.ts ` (builds the example against the published ` dist ` , serves
49+ it, asserts status + markup, then hydrates in jsdom). The second ** supersedes but does not
50+ replace** the first — keep both; only the first checks the export condition in isolation.
5351- ` bun run check ` — ` tsc --noEmit ` , then ` check:examples ` , which fans the same out to
5452 ` @solid-querybuilder/example-* ` so an example type error breaks CI
5553- ` bun run lint ` , ` bun run fmt ` , ` bun run fmt:check `
@@ -72,10 +70,10 @@ package that renders fine in the browser and silently breaks SSR/hydration.
7270A key _ lookup_ (` exports['.'].solid ` ) is order-blind and does not gate anything — do not
7371regress it back to that.
7472
75- Keep the script even after step 8 supersedes it with the SolidStart gate; it is the only thing
76- that checks the export condition in isolation. (There is no Solid 2 SolidStart yet — step 8's
77- gate is a plain Vite SSR example — but the export condition is what that gate rests on either
78- way.)
73+ Keep the script even though the SolidStart gate would supersede it ; it is the only thing
74+ that checks the export condition in isolation. (There is no Solid 2 SolidStart yet — the
75+ example's gate is a plain Vite SSR example — but the export condition is what that gate rests on
76+ either way.)
7977
8078### The SSR smoke test runs one Solid instance
8179
@@ -85,7 +83,7 @@ own copy of `solid-js`, so importing `renderToString` in the host process instea
8583with a _ different instance_ than the component was compiled against. Solid keeps
8684owner/` sharedConfig ` state at module scope, so the copies do not share it — a trivial component
8785survives this, but anything using ` createContext ` /` createStore ` /` createEffect ` (i.e.
88- ` QueryBuilder ` , from step 4 ) does not.
86+ ` QueryBuilder ` ) does not.
8987
9088** Conditions are the plugin's job now, not the config's.** ` vite-plugin-solid@3 ` gives the ssr
9189environment ` ['solid', 'development', 'module', 'node', 'development|production'] ` on its own, so
@@ -111,7 +109,7 @@ example is the only thing in the repo that exercises the publishable artifact en
111109 dom-compiled bundle and render nothing server-side.
112110- ** The SSR server is started programmatically on an ephemeral port, never by spawning a CLI.** A
113111 spawned preview leaves an orphan holding the port and serving a stale build, silently poisoning
114- the next run — caught in Phase 1, hit again in Phase 2 .
112+ the next run.
115113- ** The hydration half runs both scripts in-process, not in jsdom.** ` runScripts: 'dangerously' ` is
116114 a dead end: jsdom's vm global trips Bun with "Proxy is not allowed in the global prototype
117115 chain", and it cannot execute the ` type="module" ` client bundle anyway. The inline
@@ -126,8 +124,7 @@ example is the only thing in the repo that exercises the publishable artifact en
126124
127125` src/index.tsx ` does ` export * from '@react-querybuilder/core' ` , not just ` export type * ` . A
128126consumer calls ` formatQuery ` from ` solid-querybuilder ` and never depends on core directly, exactly
129- as React Query Builder's own barrel works. Step 3's plan called for this; ` examples/ssr ` is what
130- proved it missing at step 8.
127+ as React Query Builder's own barrel works. ` examples/ssr ` is what proved a gap here in review.
131128
132129### Relative import specifiers
133130
@@ -153,7 +150,7 @@ type-only module erased by the bundler), and allows `./foo.jsx` under `dist/sour
153150- ** Split effects, not ` on() ` .** ` on() ` is gone; ` createEffect(compute, apply) ` makes the compute
154151 phase the dependency declaration, so the old "always use ` on ` " rule is now enforced by the API
155152 shape. Deps in compute, writes in apply. ` { defer: true } ` survives as an option.
156- - ** Apply-phase writes are legal — no ` ownedWrite ` needed** (proven at step 1.5). The owned-write
153+ - ** Apply-phase writes are legal — no ` ownedWrite ` needed. ** The owned-write
157154 rule rejects writes made while an owner is on the stack, and the apply phase is unowned. Note
158155 ` ownedWrite ` is a ** signal** option, not an effect option; ` createEffect ` has no such option.
159156- ⚠️ ** The body of ` createRoot(fn) ` IS an owned scope.** A write there throws
@@ -187,8 +184,8 @@ setX(v)` returns the setter's return value and throws "invalid cleanup value". U
187184 shape and throws.
188185- ` createProjection(fn, seed, options?) ` is a derived, ** read-only** store with the same ` 'id' `
189186 default key. It can be driven from a non-reactive external source (the manager's subscribe
190- callback) by bumping a version signal from that callback and reading the signal in ` fn ` — proven
191- at step 1.5, and it is what ` createQueryBuilderState ` uses (step 3) .
187+ callback) by bumping a version signal from that callback and reading the signal in ` fn ` ; this is
188+ what ` createQueryBuilderState ` uses.
192189- ` createStore ` 's setter takes a ** draft callback** (` setStore(draft => { draft.x = … }) ` ). There is
193190 no 1.x ` setStore('key', value) ` path-argument form; it throws ` fn is not a function ` .
194191
@@ -218,7 +215,7 @@ it passes whether or not the `snapshot()` is there:
218215- A subquery renders ** bare ` <div> ` s** for its group header/body, not a ` rule-group ` element
219216 (React's ` RuleWithSubQueryGroupComponentsWrapper ` ), and it is not customizable.
220217
221- ### The conformance harness (step 6)
218+ ### The conformance harness
222219
223220` vitest.conformance.config.ts ` runs ** two projects** , because the two fixture layers demand
224221opposite render modes. This is structural, not cosmetic — one plugin instance cannot serve both.
@@ -268,32 +265,32 @@ opposite render modes. This is structural, not cosmetic — one plugin instance
268265 ` RuleTypeOf<RG> ` helper, and no re-widening cast inside components (all of which Vue needed).
269266- ` src/types/types.test-d.ts ` is compiled by ` tsc ` (` bun run check ` ), ** not** run by Vitest. It is
270267 a ** two-sided** gate: a failed assertion errors, and an ` @ts-expect-error ` that stops erroring
271- (member quietly re-added) errors as ` TS2578 ` . Both directions proven at step 2 .
268+ (member quietly re-added) errors as ` TS2578 ` . Both directions are proven .
272269- ** TypeScript is pinned to ` ^5.9 ` .** Neither ` vite-plugin-solid ` 's babel preset nor the
273270 declaration pipeline is validated against TypeScript 7.
274271
275272## Gates
276273
277- ** Standing rule: every gate must be proven to fail.** When a step adds a gate , deliberately break
274+ ** Standing rule: every gate must be proven to fail.** When a gate is added , deliberately break
278275it, record that it went red, then revert. A gate that cannot fail is worse than none.
279276
280- Current gates (steps 6 + 7 + 8) : ` check:versions ` , ` fmt:check ` , ` build ` , ` check ` , ` check:exports ` ,
281- ` lint ` , ` test:coverage ` (global 80% lines, plus a per-directory 90% lines on ` packages/*/src/** ` —
282- widened at step 5 from the step-3 ` packages/*/src/reactive/** ` , which it subsumes ; both
277+ Current gates: ` check:versions ` , ` fmt:check ` , ` build ` , ` check ` , ` check:exports ` ,
278+ ` lint ` , ` test:coverage ` (global 80% lines, plus a per-directory 90% lines on ` packages/*/src/** ` ,
279+ which subsumes the narrower ` packages/*/src/reactive/** ` key ; both
283280non-vacuous, both proved red with no injected dead code), ** ` conformance ` ** (237 assertions: 50
284281static classnames, 50 accessible descriptions, 50 post-flush classnames, 58 action sequences, 19
285282port-side action sequences, plus alignment/drift/format), ` test:ssr ` (** both halves** ), and
286283` check ` including the examples.
287284
288- The step-7 a11y gate was proved red and reverted: deleting the ` title ` binding from
285+ The a11y gate was proved red and reverted: deleting the ` title ` binding from
289286` ValueSelector.tsx ` turned ** all nine** axe cases red on the ** WCAG** assertion (` select-name ` , a
290287level-A violation, not merely a best-practice one) while all three keyboard tests stayed green.
291288Note the best-practice assertion is an ** equality** check against ` ['label-title-only'] ` , not a
292289suppression — RQB labels selectors and text editors with ` title ` alone and DOM parity is locked, so
293290that one rule is accepted (recorded under "Known limitations" in ` CHANGELOG.md ` ) while any _ other_
294291best-practice regression still fails.
295292
296- The four step-6 conformance gates were each proved red and reverted:
293+ The four conformance gates were each proved red and reverted:
297294
2982951 . ** DOM parity** — ` conformance-gate-probe ` appended to ` ActionElement.tsx ` 's class turned
299296 exactly 100 cases red (50 static + 50 post-flush), which is the split the two projects promise.
@@ -303,28 +300,28 @@ The four step-6 conformance gates were each proved red and reverted:
303300 three case-alignment tests) red while all 50 rendered cases stayed green.
3043014 . ** Value-editor reset** — an early ` return ` in ` createValueEditorReset ` 's apply phase left
305302 conformance at 237/237 green (as upstream predicts: every case is ` differsFromStatic: false ` )
306- while turning 5 of the 9 post-mount unit assertions red. That asymmetry is exactly why the
307- plan forbids proving this one through the post-flush fixture.
303+ while turning 5 of the 9 post-mount unit assertions red. That asymmetry is exactly why this one
304+ cannot be proved through the post-flush fixture alone .
308305
309306Separately confirmed: with ` test/fixtures/ ` removed, ` bun run test ` still passes 284/284 and
310307` conformance:test ` fails with the actionable "run ` bun run conformance:fetch ` " message rather than
311308an opaque parse error.
312309
313- The step-8 example gate was proved red twice, independently, and reverted both times:
310+ The example gate was proved red twice, independently, and reverted both times:
314311` document.title ` injected into ` QueryBuilder.tsx ` turned the served response into a 500 and took
31531219 assertions with it; a one-attribute divergence in ` examples/ssr/src/entry-client.tsx ` turned the
316313hydration surface comparison red while every markup assertion stayed green. Those two failure
317314modes share no code, which is the point of having both.
318315
319- All five were proven red at step 1 and reverted: coverage (threshold to 99 + an injected
316+ All five were proven red once and reverted: coverage (threshold to 99 + an injected
320317uncovered function), export-condition ** order** (` import ` moved first), export-condition
321318** target** (` solid ` repointed at ` dist/index.js ` ), the SSR ** markup** assertion (component's label
322319dropped), and ` check-dist-specifiers ` (a directory import appended to ` dist/index.d.ts ` ).
323320
324- ** Four of the five were re-proved red on the Solid 2 toolchain at step 1.5 ** — a gate proved red
321+ ** Four of the five were re-proved red on the Solid 2 toolchain** — a gate proved red
325322under Solid 1 is not evidence about Solid 2, since the plugin, the resolver behavior and the SSR
326323renderer all changed. Coverage, condition ** order** (both layers fired), condition ** target** , and
327- SSR ** markup** under the new synchronous ` renderToString ` . ` check-dist-specifiers ` is unaffected by
324+ SSR ** markup** under the synchronous ` renderToString ` . ` check-dist-specifiers ` is unaffected by
328325the runtime swap; it was re-run against the rebuilt ` dist ` instead.
329326
330327⚠️ Two assertion shapes that look like gates but are not, both found and removed in review — do
@@ -337,8 +334,8 @@ not reintroduce them:
337334 Build distinctness is now covered properly by the two Node resolutions in ` test:ssr ` .
338335
339336⚠️ Coverage-gate proof caveat: with ` src/index.ts ` a pure ` export * ` , v8 reports ` 0/0 ` and the
340- threshold passes vacuously. The step-1 proof must also inject an uncovered multi-line function
341- body to demonstrate the gate is live; non-vacuity is re-confirmed for real at step 3 .
337+ threshold passes vacuously. The proof must also inject an uncovered multi-line function
338+ body to demonstrate the gate is live; non-vacuity is re-confirmed against real reactive-layer code .
342339
343340## Coverage
344341
@@ -348,8 +345,8 @@ is how CI runs it.
348345
349346## Generated / fetched files
350347
351- - ` packages/solid-querybuilder/test/fixtures/ ` — downloaded by ` scripts/fetch-fixtures.ts ` (added
352- at step 6), gitignored. A fresh clone must pass ` bun run test ` without them.
348+ - ` packages/solid-querybuilder/test/fixtures/ ` — downloaded by ` scripts/fetch-fixtures.ts ` ,
349+ gitignored. A fresh clone must pass ` bun run test ` without them.
353350
354351## Repo status
355352
0 commit comments