Repository navigation
fix(runtime): the standalone host refuses an opaque-scheme OS_DATABASE_URL, and a .wasm.db ending no longer rescues an unrecognised scheme - #23861
Conversation
…RL, and a .wasm.db ending no longer rescues an unrecognised scheme detectDriverFromUrl read any value without `<scheme>://` as a SQLite file path, so `jdbc:postgresql://…` and `redis:h` booted "ready" on a file at a path made of the URL, and its `.wasm.db` test ran ahead of every scheme check, so `redis://h/x.wasm.db` opened a WASM SQLite file under `./redis:/h/`. Both broke the module header's own rule that unknown URL schemes throw. An opaque scheme (two or more characters and a `:`, no `//`) and an unrecognised scheme ending in `.wasm.db` now meet the existing unsupported-scheme refusal, unchanged, naming the scheme only. A single letter and a `:` stays a Windows drive path; `file:`, `sqlite:`, plain paths and `:memory:` resolve as before. The parity rows now assert the CLI and runtime hosts agree on these values. Claude-Session: https://claude.ai/code/session_018t6a5rvqPRvEDMCVVW9kYT Co-authored-by: Claude <noreply@anthropic.com>
… so it exercises the rescue The ablation of the .wasm.db position left this row green: its URL ended in a token query, which the old test never matched. The token rides in the path now. Claude-Session: https://claude.ai/code/session_018t6a5rvqPRvEDMCVVW9kYT Co-authored-by: Claude <noreply@anthropic.com>
…aque-scheme-refusal
📓 Docs Drift Check2 anchor(s) derived from 2 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 44 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 288cb3c14eda5916320495aa0c3431d9c5aeaf01 && git checkout 288cb3c14eda5916320495aa0c3431d9c5aeaf01
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin f7e836da1ed344aaa6936b9de8b173d0f326b9ec 5866f58f4f12e7c44ac2a06a907a7a9d416d879f && git checkout -B drift-repro f7e836da1ed344aaa6936b9de8b173d0f326b9ec && git merge --no-ff 5866f58f4f12e7c44ac2a06a907a7a9d416d879f
node scripts/docs-audit/affected-docs.mjs --json f7e836da1ed344aaa6936b9de8b173d0f326b9ec |
席位核对 — REWORK (one item)REWORK · #23765 · The one item. Two comments in
The PR that closes the gap corrects the sentences that describe it as open. The claim kept Everything else reviewed, no change asked:
After the push, the report's Generated by Claude Code |
…use it
Two comments in inferDriverTypeFromUrl described the runtime host's
opaque-scheme arm as an open gap ("the runtime host still opens as a
file", "an implementation gap on that host"). This PR closes it, so both
now say what holds: both hosts refuse an opaque scheme, naming the
scheme, and driver-vocabulary-parity.test.ts holds the two equal.
Comment-only; no other line of the file changes.
Claude-Session: https://claude.ai/code/session_018t6a5rvqPRvEDMCVVW9kYT
Co-authored-by: Claude <noreply@anthropic.com>
席位核对ACCEPT · #23765 ·
Landing: every check on this head is green or an expected skip, and Generated by Claude Code |
Fixes #23765
Clause-②: no
What
The runtime's standalone host (
detectDriverFromUrlinpackages/runtime/src/standalone-stack.ts) now refuses two URL shapes it used to open as a SQLite file at a path made of the URL. Both broke the module header's own rule (standalone-stack.ts:34): "Unknown URL schemes throw — we never silently fall back to sqlite".:with no//(jdbc:postgresql://…,redis:h). The scheme-less arm read "noSCHEME://" as "a file path"..wasm.dbending (redis://h/x.wasm.db). The.wasm.dbtest ran ahead of every scheme check. It now selects the WASM engine only for afile:URL or a value with no scheme.Both meet the existing unsupported-scheme refusal, unchanged: plain
Error, same words, naming the scheme only. A single letter and a:stays a Windows drive path.file:,sqlite:, plain paths,wasm-sqlite://and:memory:resolve as before.Files:
packages/runtime/src/standalone-stack.ts:detectDriverFromUrlonly (the.wasm.dbtest moves under thefile:and no-scheme returns; the scheme computation gains the opaque-scheme fallback).packages/runtime/src/standalone-stack-opaque-scheme-refusal.test.ts(new): pins beside the existing standalone database-URL tests.packages/cli/src/utils/driver-vocabulary-parity.test.ts: the four rows that named this card move into the "both hosts REFUSE" block. That block now also asserts the runtime names the same scheme as the CLI. Two drive-letter rows join the "both hosts ACCEPT" block as the control. The CLI-only block is deleted.packages/cli/src/utils/storage-driver.ts: comments only (commit5866f58f4). TheinferDriverTypeFromUrldocblock and its opaque-scheme arm comment described this card's gap as open; they now say both hosts refuse an opaque scheme, naming the scheme, anddriver-vocabulary-parity.test.tsholds the two equal. No other line of the file changes..changeset/23765-opaque-scheme-refusal.md:@objectstack/runtimepatch.The PM's mechanism assumptions, measured
One rule, two hosts. The CLI's rule after PR fix(cli): os serve refuses an OS_DATABASE_URL whose scheme no driver recognises, and reads pg:// and a scheme-less path as the runtime host does #23761 is written inline in
inferDriverTypeFromUrl(packages/cli/src/utils/storage-driver.ts): theSCHEME://regex, falling back to the opaque regex/^[a-z][a-z0-9+.-]+:/i. No package both hosts depend on has an opaque-scheme classifier.git grepof that regex overpackages/(tests excluded) has one hit, the CLI's own.@objectstack/specand@objectstack/typeshave no such classifier. The runtime cannot import from the CLI, which depends on it. A shared copy would need a new runtime export plus an edit tostorage-driver.ts, which the claim holds off. So this PR writes the runtime's arm with the same regex, character for character, and the parity rows hold the two hosts equal.Who reads the answer.
detectDriverFromUrlis module-private, with one call site:resolveStandaloneDatabase. That function feedscreateStandaloneStackand, in the CLI,migrate-occupancy-gate.ts,schema-migrate.tsandtelemetry-datasource.ts. In-repo producers, at3fed1eb00:git grep -n "jdbc:", excluding CHANGELOGs: 9 hits. All are CLI refusal pins, CLI comments or the sibling changeset.*.wasm.dbliterals: 15 hits. 2file:and 6sqlite:(positive control), and 7 others, all CLI refusal pins, comments or the changeset.packages examples apps content scripts: 2,703. Of these, 367 start withfile:orsqlite:(positive control). The opaque-scheme values that remain do not reach this host: driver-turso constructor tests (libsql:db.example.turso.io), a banner display string,data:/javascript:/pkg:literals, and grep noise.No producer, example or fixture that this refusal now breaks.
The refusal's shape. This PR reuses the existing throw and does not edit it. The pins compare against a reference refusal (
wat://nope) rather than pinning prose: same constructor (Error), same own property names, samecode(none; this refusal has no ADR-0112 code), and the same message once the scheme is masked. The first sentence names the scheme. A placeholder credential (PLACEHOLDER-USER/PLACEHOLDER-SECRET/PLACEHOLDER-TOKEN) appears in no own property of the error.Reproduction at the public door.
os serve --devover a plain config, each run in a fresh scratch directory outside the repo:OS_DATABASE_URL3fed1eb00jdbc:postgresql://127.0.0.1:5999/db✓ Server is ready,Driver: SqlDriver(better-sqlite3) → jdbc:postgresql://127.0.0.1:5999/db; created./jdbc:postgresql:/127.0.0.1:5999/db(+ telemetry siblings)Loading objectstack.config.ts...:✗ [StandaloneStack] Unsupported database URL scheme "jdbc:". Supported schemes: …; directory holds only the configredis://h/x.wasm.db✓ Server is ready; created./redis:/h/x.wasm.db… scheme "redis://"; nothing createdredis:h✓ Server is ready; created./redis:h(+ telemetry siblings)… scheme "redis:"; nothing createdCONTROL on this branch:
file:./x.db,sqlite:./x.dband./x.dbboot ready on./x.db,./x.wasm.dbboots ready on./x.wasm.db, and:memory:boots ready on:memory:.C:\data\x.dbandC:/data/x.dbare pinned as string fixtures (sqlite, same file).Round 2 (
5866f58f4, comment-only, after the seat's REWORK6212986188): the parity andstorage-driverunit files 164 passed, clitypecheckexit 0, gate union 64 run / 1 NOT-MEASURED (check:type-check-debt, CI's), CI all success. The sections below cite round 1 atc01539665for the runtime readings, whose files this round does not touch.Tests (final head
c01539665)pnpm --filter @objectstack/runtime exec vitest run --project local --maxWorkers=2:Test Files 400 passed (400),Tests 6026 passed | 19 skipped (6045),VERDICT command-exit 0.pnpm --filter @objectstack/runtime typecheck: exit 0 (check:test-typecheck: OK; the new test file adds no debt).pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2 src/utils/driver-vocabulary-parity.test.ts src/utils/storage-driver.test.ts:2 passed,164 passed, exit 0. The parity file resolves@objectstack/runtimethroughdist/, rebuilt from this head (the opaque regex is present indist/index.jsanddist/index.cjs).pnpm --filter @objectstack/cli typecheck: exit 0.pnpm exec eslint --no-inline-configon the three changed source files: exit 0, no output.integrationtier is left to CI; this diff touches no integration-layer file and no spawn entry. The runtime'srepoproject (test:repo) is left to CI (Test Core); this diff touches none of its three files.Ablation. The pin file imports
./standalone-stack.jsfrom source, so no rebuild was needed. Mutations went throughnode scripts/ablation-replace.mjs(wrap mode, restore proven by blob hash equal to HEAD and an emptygit diff HEAD):urlSchemeOf(dbUrl) ?? /^[a-z][a-z0-9+.-]+:/i.exec(dbUrl)?.[0]reverted tourlSchemeOf(dbUrl). Blob7fe20179cd6dto2d4dd5eb306e. Result:Tests 21 failed | 26 passed (47): all 18 opaque rows across the three doors, plus the 3jdbc:x/app.wasm.dbrows. CONTROL stayed green. Restored..wasm.dbposition (the early return put back ahead offile:):9 failed | 38 passed. The credentialedredis://…row stayed green: its URL ended in a?authToken=query, so it never exercised the rescue. Commit60575c3dfmoves the token into the path.12 failed | 35 passed (47): every rescue row across the three doors. Restored.Gates (final head
c01539665)node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(merge base1eac7ad75, 4 paths) derived 65 commands. 64 ran with exit 0.pnpm check:type-check-debtwas not run on this head: NOT MEASURED. On the first pass my 590 s foreground timeout killed it (exit 124). That kill interrupted its closure refresh mid-build and left@objectstack/organizationswith adist/missingindex.d.ts.check:dts-closureandcheck:dual-build-cjs-loadsread that state red on the second pass. I rebuilt that package, and both reran exit 0. CI runscheck:type-check-debtinType Check · debt ledger.--ranreconciliation:65 derived famil(ies) accounted for — 64 run, 1 NOT-MEASURED, exit 0.Lint & Repo Gatescheck-run.Acceptance notes
wasm-sqlite://nor "a path with no scheme"; the CLI's list names both. That is polish, so not filed.normalizeDatabaseUrl, so a non-sqlite declared datasource whoseurlissqlite:./x.dbis now refused as"sqlite:". By reading, not measured: before this PR it opened a file literally namedsqlite:./x.db, andsqlite://was already refused on that tier. No in-repo producer writes it.mainmoved after my merge. It moved 8 commits past1eac7ad75, among themf7a5ebed7andae4dfafa1(PR fix(driver-turso,cli): the v18 organization-ownership ceremony runs on a Turso remote database #23843), which edit other functions ofstandalone-stack.ts.git merge-treereports a clean merge with disjoint hunks. The incoming test (standalone-stack-uncreatable-db-directory.test.ts) uses onlyfile:andwasm-sqlite://URLs, which this change leaves alone. PR CI runs on the merge ref.OS_DATABASE_DRIVER=sqliteskipsdetectDriverFromUrland reachessqliteFilenameFromUrl, which recognises onlySCHEME://. Measured at the door on this branch:OS_DATABASE_DRIVER=sqlite OS_DATABASE_URL=jdbc:postgresql://127.0.0.1:5999/db os serve --devstill boots ready on./jdbc:postgresql:/127.0.0.1:5999/db, whileredis://h/x.dbis refused. That function is held off here (runtime(standalone host): an upper-caseFILE:database URL is classified as SQLite but its prefix is not stripped, soOS_DATABASE_URL=FILE:./app.dbcreates a directory literally namedFILE:.and a database inside it #23794 and cli(storage):OS_DATABASE_URL=file://NAMEopens//NAMEat the filesystem root on the CLI host, where the runtime host opens./NAME(thefile:twin of thesqlite://alias #23633 fixed) #23796 are the open cards on it), so this shape goes to the seat for that family.The body edits of round 2 (the
storage-driver.tsfile bullet, the round-2 line, and the removed stale Acceptance note) were written by thedomain:cliseat 2 PM (sessionsession_018t6a5rvqPRvEDMCVVW9kYT) from the dev's round-2 report.