fix(service-datasource): the open-core libSQL arm tells you how to install the driver it is missing (#7314) - #7384
Merged
Conversation
…stall the driver it is missing (#7314) `@objectstack/driver-turso` is an OPTIONAL install, so both loaders that can build a libSQL datasource have to answer "the package is not here". They answered it very differently. The HOST loader (`@objectstack/runtime`'s `loadTursoDriverFactory`, single owner since #6268) raises `MissingDriverPackageError` carrying the install command as data, plus a message naming the command, the consequence, and why the boot refuses instead of quietly opening a SQLite file. The shared open-core factory's `turso` arm — the one serving every OTHER door: a datasource added in Setup, `testConnection`, a declared non-default datasource — said only turso driver requested but @objectstack/driver-turso is not installed (…). the fault and nothing else. Same missing package; whether you were told how to fix it depended on whether your datasource happened to be named `default`. That arm now answers with the same quality of remedy, in `missingTursoDriverMessage()`, pinned by CONTENT rather than by `toThrow()` — the defect is what the message OMITS, and a throw-only assertion stayed green throughout the years it omitted it. Two deliberate differences from the host loader's wording, because this arm serves different doors. It NAMES THE DATASOURCE (here there may be several and only one is libSQL). And it names no `OS_DATABASE_URL` / `--database` / `OS_ALLOW_DRIVER_CONNECT_FAILURE`: those select or bypass the HOST's `default` and can do nothing for the datasource that actually failed — pointing a stuck reader at a knob that cannot affect their problem is the failure `connect-failure-remedy.ts` was written to end (#5794). One fix, stated once. The original import error is still interpolated in full, which is load-bearing: this re-throw drops the error's `code`, so `isUnbuiltWorkspaceFailure` can only recognise a half-built checkout from the `Cannot find package` TEXT the message carries. A test pins that classification, not just the wording. Deliberately NOT done here (they are `domain:cli` territory, #7314 points 2/3): the typed `MissingDriverPackageError` is not mirrored — that class lives in `@objectstack/runtime`, which DEPENDS on this package, so importing it inverts the dependency and declaring a second same-named class is precisely the identity hazard #6268 closed. And the host loader's narrow `url`/`authToken`-only config read is untouched; this arm already reads the full `TursoConfigSchema` surface. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015fkdTyGmMD5s8ZtEifvuGy
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckNo hand-written docs reference the 1 changed package(s). ✅ |
os-help
marked this pull request as ready for review
August 10, 2026 08:39
This was referenced Aug 10, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #7314 — point 1 only. Points 2 and 3 are deliberately left; see "What this PR does not do".
The defect
@objectstack/driver-tursois an OPTIONAL install (it drags@libsql/clientand its native bindings), so both loaders that can build a libSQL datasource must answer "the package is not here". They answered it very differently.The HOST loader —
@objectstack/runtime'sloadTursoDriverFactory, the single owner since #6268 — raisesMissingDriverPackageErrorcarrying the install command as data, plus a message naming the command, the consequence, and why the boot refuses instead of quietly opening a SQLite file.The shared open-core factory's
tursoarm — the one serving every OTHER door: a datasource added in Setup,testConnection, a declared non-default datasource — said only:The fault and nothing else. Same missing package, and whether you were told how to fix it depended on whether your datasource happened to be named
default.The change
packages/services/service-datasource/src/default-datasource-driver-factory.ts— thetursoarm now raisesmissingTursoDriverMessage():Two deliberate differences from the host loader's wording, because this arm serves different doors:
OS_DATABASE_URL/--database/OS_ALLOW_DRIVER_CONNECT_FAILURE. Those select or bypass the HOST'sdefaultdatasource and can do nothing for the datasource that actually failed. Pointing a stuck reader at a knob that cannot affect their problem is the failureconnect-failure-remedy.tswas written to end (datasource fail-fast 不认识「工作区未构建」这个成因 —— 对 ERR_MODULE_NOT_FOUND 仍建议改配置或设 OS_ALLOW_DRIVER_CONNECT_FAILURE=1(两条都是有害建议) #5794). One fix, stated once, and no escape hatch named.TURSO_DRIVER_PACKAGE,TURSO_DRIVER_INSTALL_COMMANDandmissingTursoDriverMessageare exported, so a host that renders the remedy itself reads one declaration instead of re-typing a sentence — and so a later runtime-lane change can converge onto them in the legal import direction (runtime already depends on this package).Behaviour is otherwise unchanged: same failure at the same moment, still a refusal, never a fallback to a different engine. Only the message differs.
What this PR does not do (#7314 points 2 and 3 —
domain:clilane)MissingDriverPackageErrorlives in@objectstack/runtime, which depends on@objectstack/service-datasource— importing it would invert the dependency, and declaring a second same-named class is precisely the class-identity hazard observation: 可选 Turso driver 的 loader 现在有两份(cli/utils/storage-driver.ts 与 runtime/turso-driver-factory.ts)—— 建议收敛到单一 owner #6268 closed (serve.tsdecides fatality withinstanceof). This arm therefore still raises a plainError, now with the full remedy in its message. Nothing routes it toserve.tstoday.origin/main(with observation: 可选 Turso driver 的 loader 现在有两份(cli/utils/storage-driver.ts 与 runtime/turso-driver-factory.ts)—— 建议收敛到单一 owner #6268 / PR refactor(runtime,cli): give the optional Turso/libSQL loader one owner (#6268) #7313 merged at 68f5ecc): this arm already readsurl,authToken,encryptionKey,concurrency,syncUrl,sync,timeout,modeandschemaMode, while the host loader readsconst config = (spec.config ?? {}) as { url?: unknown; authToken?: unknown; }—urlandauthTokenonly. The narrow read is the host loader's, so nothing about point 3 is fixable inside this file surface.pool还有两处静默丢弃:turso 整块、mongodb 的两个 timeout 键(#5714/#5931 拒绝集合覆盖不到的剩余面) #7243 (datasource.pooldropped for turso in this same factory) is a separate card and is untouched; this diff does not go near the pool guard.Tests
packages/services/service-datasource/src/__tests__/default-datasource-driver-factory.test.ts, seven cases. Pinned by content, not bytoThrow()— the defect is what the message OMITS, and a throw-only assertion stayed green throughout the years it omitted it:defaultfallbackisUnbuiltWorkspaceFailure(...) === true. Load-bearing rather than cosmetic: this re-throw drops the error'scode, so the unbuilt-workspace classifier can only recognise a half-built checkout from theCannot find packageTEXT the message carries. Drop the interpolation and a half-built worktree silently goes back to being told "Fix the datasource configuration" (datasource fail-fast 不认识「工作区未构建」这个成因 —— 对 ERR_MODULE_NOT_FOUND 仍建议改配置或设 OS_ALLOW_DRIVER_CONNECT_FAILURE=1(两条都是有害建议) #5794).@objectstack/driver-tursois genuinely unresolvable from this package (that is what "optional" means), so no stub is needed; if it ever becomes resolvable the case fails with a message saying the fixture premise changed, rather than silently passing.Reverse verification — direction RED, as expected. Reverting only the arm's
throwto the pre-#7314 wording (viagit checkout, nevergit stash) turns the wiring case red:Reported honestly: only 1 of the 7 goes red on that particular revert, because the other 6 pin the message builder, which that revert does not remove. Reverting the builder as well makes the test file fail to compile — a red, but a compile red, so the wiring case is the meaningful behavioural one.
Green, restored (byte-identical to the pre-revert diff):
pnpm --filter @objectstack/service-datasource typecheck→tsc --noEmit, clean.node scripts/check-nul-bytes.mjs→ OK (6741 files). ESLint on the three changed files, clean.Consumer direction: the API change is purely additive, so the sweep was scoped to the one consumer that could actually collide —
@objectstack/runtimedeclares its own same-namedTURSO_DRIVER_PACKAGE/TURSO_DRIVER_INSTALL_COMMAND. Nothingexport *s this package, andpnpm --filter @objectstack/runtime typecheckis clean against the rebuilt.d.ts. That the rebuilt declaration is genuinely being read was proved in reverse, from insidepackages/runtime, with a throwaway probe (removed):Changeset
patchfor@objectstack/service-datasource— the text an operator reads on a real failure path changes, which is user-visible, while no signature, verdict or timing does.Generated by Claude Code