Skip to content

fix(service-datasource): the open-core libSQL arm tells you how to install the driver it is missing (#7314) - #7384

Merged
os-help merged 1 commit into
mainfrom
claude/issue-7314-turso-open-core-loader
Aug 10, 2026
Merged

fix(service-datasource): the open-core libSQL arm tells you how to install the driver it is missing (#7314)#7384
os-help merged 1 commit into
mainfrom
claude/issue-7314-turso-open-core-loader

Conversation

@os-help

@os-help os-help commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Part of #7314 — point 1 only. Points 2 and 3 are deliberately left; see "What this PR does not do".

The defect

@objectstack/driver-turso is an OPTIONAL install (it drags @libsql/client and 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's loadTursoDriverFactory, the 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, 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 — the turso arm now raises missingTursoDriverMessage():

datasource 'warehouse': a libSQL/Turso datasource was requested, but the driver
package @objectstack/driver-turso is not installed. Install it next to the
server that opens this datasource:

    npm install @objectstack/driver-turso

(pnpm add … / yarn add ….) It is an OPTIONAL package, so a default install stays
free of @libsql/client and its native bindings. This refuses rather than falling
back to another engine: a silent fallback would open an empty local database
that accepts writes while your libSQL data stays untouched, and every write
would land in the wrong database. Import error: …

Two deliberate differences from the host loader's wording, because this arm serves different doors:

TURSO_DRIVER_PACKAGE, TURSO_DRIVER_INSTALL_COMMAND and missingTursoDriverMessage are 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:cli lane)

Tests

packages/services/service-datasource/src/__tests__/default-datasource-driver-factory.test.ts, seven cases. Pinned by content, not by toThrow() — the defect is what the message OMITS, and a throw-only assertion stayed green throughout the years it omitted it:

  • the exact install command, on its own copy-pasteable line
  • the package name
  • the consequence and the deliberate refusal
  • the datasource name, and the default fallback
  • the import error kept verbatim, asserted through isUnbuiltWorkspaceFailure(...) === true. Load-bearing rather than cosmetic: this re-throw drops the error's code, so the unbuilt-workspace classifier can only recognise a half-built checkout from the Cannot find package TEXT 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).
  • no escape hatch and no host-boot knob named, install command appearing exactly once
  • the wiring: the arm really raises this. @objectstack/driver-turso is 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 throw to the pre-#7314 wording (via git checkout, never git stash) turns the wiring case red:

AssertionError: expected 'turso driver requested but @objectsta…' to contain 'npm install @objectstack/driver-turso'
 Test Files  1 failed | 11 passed (12)
      Tests  1 failed | 273 passed (274)

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):

 Test Files  12 passed (12)
      Tests  274 passed (274)

pnpm --filter @objectstack/service-datasource typechecktsc --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/runtime declares its own same-named TURSO_DRIVER_PACKAGE / TURSO_DRIVER_INSTALL_COMMAND. Nothing export *s this package, and pnpm --filter @objectstack/runtime typecheck is clean against the rebuilt .d.ts. That the rebuilt declaration is genuinely being read was proved in reverse, from inside packages/runtime, with a throwaway probe (removed):

error TS2345: Argument of type '{}' is not assignable to parameter of type
  '{ datasource?: string | undefined; cause: unknown; }'
error TS2322: Type 'number' is not assignable to type 'string'

Changeset

patch for @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

…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
@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 10, 2026 8:28am

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

No hand-written docs reference the 1 changed package(s). ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants