Repository navigation
✨ Select structured data with Query and render YAML (#934) - #962
Conversation
The lockfile is frozen repository-wide, so taking a dependency is a separate act from using one: resolve it, commit the lock, then run setup. This is that act, and nothing in it reads the package yet. `yq.rs` is pinned exactly, in the three places the repository resolves from — core's own import map, core's npm manifest for the isolated builder, and the root map the builder reads. No range, no fallback version. The lock carries what resolution produced and nothing else: the specifier, the package with its registry integrity, and the three membership entries that place it under core. `pnpm-lock.yaml` is untouched, because canonical setup owns the Node layout and resolves it there.
…934) A document that reads a file, globs a list or computes data has the whole of it and no way to say which part it wants. Writing an eval block to pick a field is doing by hand what the producer already produced. ```xmd <File path="package.json" as="packageText" /> <Query value={packageText} yq="from_json | .scripts" as="scripts" /> <Yaml value={scripts} /> <Query value={scripts} yq=".test" /> ``` `value` and `yq` are required and ordinary; `as` is optional and ordinary. With `as` the selected value is bound as data and nothing is written. Without it the selected value is written as JSON — every type, strings included, quoted and escaped the way `<Json>` writes them. One spelling, one reading: what a reader sees never depends on which type the expression happened to select. Text stays text until `from_json` or `from_yaml` decodes it, so a document that meant to search a file's contents says so and one that meant to match the text itself still can. Exactly one JSON value is an answer: `null`, `false` and `[]` are answers, while selecting nothing and selecting several both refuse and say to collect with `[ … ]`. A refused selection binds nothing and substitutes no input, so a name it would have written keeps the value it had. It queries the value it was handed. The producer has already finished and been bound, so several queries over one value cost one read, and a producer's own next-page marker is ordinary data this never follows. `<Yaml>` is `<Json>`'s sibling for the other spelling of the same value: the captured operand, serialized once, rendered by the query engine's identity evaluation and returned exactly as it came back, terminating newline included. The engine is the published `yq.rs` pin, loaded by the invocation that needs it through one literal dynamic import observed with `until`. It reaches the environment, the filesystem and the clock only through options, and both call sites pass empty maps and omit the clock — so an environment read is an empty string, a file load and a clock refuse, and no process is ever started. The distribution is proven rather than assumed: one document runs under source Deno, source Node and source Bun, from one compiled host binary with nothing on PATH, and from the built npm closure packed and installed into a directory of its own under both Node and Bun — with the emitted core manifest declaring the exact pin. `pnpm-lock.yaml` carries canonical setup's own resolution of that pin.
… Q7 claim (#934) Both reviews asked for the same four finite corrections. None changes what a document observes: the interface, the uniform JSON output, the optional-as binding and the production boundary are exactly what they were. The engine's loaded module was asserted into a hand-written shape. The pinned package already publishes `yq`, `Yq`, `EvalOptions` and `Format`, so the import is destructured and inferred instead, and the duplicated local interfaces are gone. The fixture's empty array is declared rather than asserted, and the comments that narrated the declaration beneath them are gone too — the surprising ones stay: why the literal import is what a distribution carries, why `matched` decides nothing, why the whole answer is parsed at once, and why the renderer serializes once. Selection answers with Effection's `Result<string>` rather than a local ok/json/refusal union: the JSON text is the value, and the cardinality refusal is an Error carrying the message an author already read. Query re-raises it with that error as the cause, so what the document says is unchanged and the vendor failure still travels through its own wrapper. Q3 was one document asserting one aggregate message, and `@sh` is an encoder rather than the disabled process operation. It is now four independent observations with canaries this test owns and removes: a real host environment value that `strenv` answers as empty, an actual existing file that `load` refuses, a clock that refuses, and the package's own `system` refusing with the marker it would have created absent. Each asserts its own cause and that the target name was never bound. Q7 ran real artifacts but kept no identity: the binary's digest was computed and discarded, and the packed closure's were never taken. The one compile and one closure build now record the binary's own SHA-256 and byte length, every packed tarball's, each resolved consumption cwd, argv, runtime version and child exit, and the emitted core manifest's exact dependency beside the literal root import that survived the build — printed before the owned scratch is released, and the binary re-read afterwards to prove the hash names what ran.
…934) `query-component.test.ts` belongs to the corpus every runtime runs, and its environment canary reached for `Deno.env` directly. The Node adapter installs no Deno global, and the file carries no exclusion — so the shared suite named a global only one of its three runtimes has. The canary stays a real one. It is written and put back through `node:process`, which is the surface this repository's own `API.Env` reads the environment by, and the host readback now goes through that exported `env()` operation — so what the assertion compares is the value a document's own environment read would see. Restoration is registered before the value is set, so a scope ending between the two still puts back what this process had. All four observations stay as they were: the environment answer, the refused load of a file that exists, the refused clock, the refused process operation with its marker absent, each with its own cause and its own unbound target. Node and Bun evidence for this file remains unresolved: the one Node check taken was refused by tsx's IPC listener before any test registered, and no retry, alternate launch path or substitute runtime was used here.
Discovery finds every `scripts/tests/*.test.ts` and hands it to all three runtimes unless the manifest says otherwise, so the new packaging harness was being scheduled under Node and Bun — where `Deno.execPath()`, the canonical host compile and the dnt closure build have no meaning. Classifying it is the act that file was missing. Nothing it proves is dropped. The Node and Bun consumers are the ones this harness launches: source Node through the repository's `tsx` loader, source Bun, and the packed closure under both. Excluding the driver from those runtimes leaves every one of those launches exactly where it was, and what the engine does inside a document stays proven on every runtime by `query-component.test.ts`. One entry, in the list the adjacent Deno build and npm tooling already use, under the issue the manifest keeps that decision in. Discovery, the runners, the harness, its assertions and `exclusions.deno` are untouched.
PR #962: ✨ Select structured data with Query and render YAML (#934)17 files, +1823 / -4 Scope🔴 PR has 1827 lines changed. Split into focused PRs. 🟡 1827 lines changed. PRs under 400 receive more thorough review. 🟡 Changes span 6 directories. 🟡 PR mixes config and source changes. Structural✅ No structural bloat detected. Slop✅ Slop indicators look low. Static AnalysisOxlint: 2 diagnostics across 1 file (2 rules) no-shadow (1): packages/core/src/components/Query.ts CorrectnessFILE: packages/core/src/components/query-engine.ts, PATTERN: signal cluster, CONCERN: Multiple Oxlint violations in the same file, QUESTION: Are these violations indicative of unreviewed code or structural issues? FILE: packages/core/src/components/query-engine.ts, PATTERN: speculative abstraction, CONCERN: Functions |
Why
Documents need to select structured data without embedding a JavaScript transform, then choose explicitly how to render it.
What changes
Queryaccepts a value and ayqexpression. Withas, it binds the selected structured value silently. Withoutas, it prints every selected type as JSON, including quoted strings.Yamlrenders a supplied value as YAML through the ordinary component and capture paths.Selection uses the private published
yq.rs@0.1.4dependency. It accepts one complete JSON result, retains evaluation/refusal causes, and receives no host environment, file, clock or process capability. Existing binding, output and failure owners remain in place.Review guide
Start with
packages/core/src/components/Query.ts,Yaml.tsand their privatequery-engine.tshelper, then ordinary registry/docs/dependency wiring. The Deno-owned distribution harness still launches real source and packed Node/Bun consumers; its exclusion entry keeps only the unsupported driver invocation out of those runtime corpora.How to verify it
Both original reviewers independently PASS identical signed commit
fca8e1c27f5956e84734b3279d0492f1e21dbe8a, tree4a294b044821d23cdc839522948b0090201d3143. Their complete reports and canonical completed results are retained in the local delivery archive.9db42a14.9db42a14: 1 pass, 0 skipped. The original pre-test IPC refusal remains preserved; no further retry is authorized.642a9e59; real source, host-compiled and packed distribution evidence remains attributed to4f666e0c. No historical execution is relabeled as a final-head run.deno task verify:cleanran once on exact signedfca8e1c2and exited 0. Offline builds, release compile, resolution and concurrent ownership/non-interference proof passed.New dependencies
yq.rs@0.1.4supplies the ready expression evaluator shared privately by Query and Yaml. Dependency manifests and both lockfiles are committed; no loader or build workaround is introduced.Risks and limitations
This remains a draft. Runner-owned test-weight measurement is required after the applicability change and is pending. Actual required CI and original Planner title/body APPLY followed by independent VERIFY remain delivery gates. If measurement incorporation produces a signed child, both original reviewers and committed verification must cover that exact final child before its push. No merge-readiness claim is made for this draft.
Scope confirmation
Closes #934.