Skip to content

feat(runtime): standalone stack 分派 libsql://,经可选 Turso driver 包 (#5820) - #6272

Merged
qq9340100 merged 1 commit into
mainfrom
claude/issue-5820-standalone-stack-libsql
Aug 7, 2026
Merged

feat(runtime): standalone stack 分派 libsql://,经可选 Turso driver 包 (#5820)#6272
qq9340100 merged 1 commit into
mainfrom
claude/issue-5820-standalone-stack-libsql

Conversation

@qq9340100

Copy link
Copy Markdown
Collaborator

Fixes #5820

这条路径原来是什么样

packages/runtime/src/standalone-stack.tsdetectDriverFromUrl() 不认 libsql://,落到末尾的 Unsupported database URL scheme throw;同文件 resolveDatabaseUrl() 却把 TURSO_DATABASE_URL 列为 URL 来源之一 —— 读得进来、认不出去。#5602 / PR #5819 让 CLI 的 os serve / os start 真的能从 libsql:// 构造 TursoDriver 之后,同一个 OS_DATABASE_URL=libsql://… 就有了两种命运:os start 正常启动,os migrate(经 createStandaloneStack,以及 migrate-occupancy-gate.tsresolveStandaloneDatabase())硬失败。

前提重验(逐条实读,基线 origin/main@01faeb13a)

分诊评论的三条锚点在当前 main 上全部仍成立(行号相对 846ed1f 有漂移):

分诊记录 当前 main 实读 状态
:157 Unsupported database URL scheme throw standalone-stack.ts:156-159 ✅ 仍在
:218 ?? process.env.TURSO_DATABASE_URL?.trim() standalone-stack.ts:218(resolveDatabaseUrl 内) ✅ 仍在
:26-28 docstring「ships separately in the ObjectStack Cloud distribution」 standalone-stack.ts:26-29 ✅ 仍在

另外补两条实读,它们决定了形态:

没有发现任何 ADR 把 standalone stack 排除在 libSQL 之外(docs/adr/ 内 grep libsql / turso 无相关裁决),所以按派工的选项 1(口径收敛)实施。

实现:与 #5819 同形态(维护者 B-lite 裁决)

  • detectDriverFromUrl() 识别 libsql://http(s)://*.turso.* —— 与 CLI inferDriverTypeFromUrl() 逐字同款的两条,刻意保持一致:两个函数对同一个 OS_DATABASE_URL 回答同一个问题,分叉本身就是本单的病因;
  • driver 来自可选包 @objectstack/driver-turso,在新文件 packages/runtime/src/turso-driver-factory.ts 里惰性 import(),包成 DefaultDatasourcePlugin 的 host factory —— 那个 seam 的 docstring 本来就是为「宿主的 default 需要一个开源工厂造不出的 driver(cloud 的 turso)」写的。连接路径、bootCritical fail-fast 判决、OS_ALLOW_DRIVER_CONNECT_FAILURE 逃生口、Setup → Datasources 的 retained status 全部与其他 kind 共享,只有构造不同;
  • 缺包 ⇒ 响亮失败 MissingDriverPackageError,installCommand 作为数据字段(不只写在句子里),消息保留原始 import 错误;⛔ 无 SQLite 回退,理由写进消息本身(静默降级会让 os migrate 的 DDL 落到一个空的本地库,而远端 libSQL 数据原封不动 —— cli: OS_DATABASE_DRIVER=memory is advertised but serve.ts has no dispatch branch (falls through to SQLite) #3276 的账);
  • 加载发生在构造 plugin 之前,所以缺包时操作者拿到的是一条清晰消息,而不是 boot later 的 connect 报错;
  • Supported schemes: 列表随之更新;未知 scheme 仍然 throw(wat://nope 的 e2e 钉子 packages/cli/test/migrate-exit-code.e2e.test.ts:210 只匹配 Unsupported database URL scheme 子串,未受影响)。

TURSO_DATABASE_URL 的闭环

它一直是 resolveDatabaseUrl() 的第三顺位来源。本 PR 之后,「读得进」与「认得出」终于是同一件事:TURSO_DATABASE_URL=libsql://… 且未设 OS_DATABASE_URL 时,resolveStandaloneDatabase() 返回 { driver: 'turso' }(有钉子)。选项 2(删掉这个来源)未采纳,理由见 issue 分诊:它等于保留一个前提已不成立的排除。

authToken 的来源(实读确认后接上)

os serve 读的是 process.env.OS_DATABASE_AUTH_TOKEN || process.env.TURSO_AUTH_TOKEN(commands/serve.ts:1041),而 --database-auth-tokenstart.ts:242 / dev.ts:323 转发进 OS_DATABASE_AUTH_TOKEN。standalone stack 因此有完全对应的来源,已接上,优先级逐字对齐:cfg.databaseAuthTokenOS_DATABASE_AUTH_TOKENTURSO_AUTH_TOKEN(vendor 名,AGENTS.md 第 9 条明列的第三方例外)。空白值按「没有」处理,不会把 authToken: '' 递给 driver。

刻意没做的事

反向验证(先预测方向,再跑)

预测:识别臂是谓词不是计数,所以标准方向 —— 临时删掉 detectDriverFromUrl() 里那两行,应有 5 个用例转红(4 个走 URL 识别的钉子 + 1 个整栈钉子),而两个走 explicit driver 的用例(databaseDriver: 'turso' / OS_DATABASE_DRIVER=turso)绕过识别,应保持绿。

实际:5 红 / 26 绿,与预测逐条一致,且红的正是 issue 描述的那句原文:

× libsql:// resolves to turso, keeps the URL, and probes no sqlite file
× an https Turso endpoint resolves to turso — the exact spelling the CLI classifies
× TURSO_DATABASE_URL now dispatches as well as resolves (the read-in/refuse-out split is gone)
× offers NO silent SQLite fallback when the package is missing
× fails with the install command instead of "Unsupported database URL scheme"

Error: [StandaloneStack] Unsupported database URL scheme: libsql://my-db.turso.io. Supported
schemes: memory://, postgres://, pg://, mongodb://, mongodb+srv://, libsql:// (optional
@objectstack/driver-turso), file:

 Test Files  1 failed (1)
      Tests  5 failed | 26 passed (31)

识别臂已还原(git diff 内不含该临时改动)。

测试

新增 packages/runtime/src/standalone-stack.libsql.test.ts(31 例),三层:

  1. 识别 —— libsql / https-turso / TURSO_DATABASE_URL / explicit driver 四条正向;memory://postgres://postgresql://pg://mongodb://mongodb+srv://wasm-sqlite://file:、裸路径九条阳性对照不回归;wat://nope 仍抛 Unsupported;非 Turso 的 https://example.com/db 仍抛 Unsupported(新臂是窄的);
  2. 可选包两向 —— 替身 importer:在场 ⇒ 工厂只认 turso/libsql,create(){ url, authToken } 造出驱动、handle 的 connect/disconnect/checkHealth 可用、ownership 留默认;缺席 ⇒ MissingDriverPackageErrorinstallCommand 数据字段、消息含拒绝理由与原始 import 错误、失败路径里没有任何 sqlite 形状;另有「模块解析得到但没有 TursoDriver 导出」与 CJS default 形状两例。全程不连真 Turso;
  3. 整栈 —— createStandaloneStack({ databaseUrl: 'libsql://…' }) 现在不再以 Unsupported database URL scheme 收场(本工作区里走缺包臂,因为 driver-turso 刻意不是 runtime 的依赖);同一路径上 wat://nope 仍然照旧被拒。

命令与结果:

pnpm --filter @objectstack/runtime typecheck    →  tsc --noEmit,无输出
vitest run --maxWorkers=2 (packages/runtime)    →  Test Files 106 passed (106)
                                                   Tests 1537 passed (1537)
npx eslint --no-inline-config (4 个改动文件)     →  无输出,exit 0
node scripts/check-nul-bytes.mjs                →  OK (scanned 5949 tracked text files)
pnpm --filter @objectstack/runtime build        →  ESM/CJS/DTS 均 success;dist 里
                                                   import("@objectstack/driver-turso")
                                                   保持为运行期动态 import(external 生效)

另做了一次消费半径清扫:Supported schemes 字样全仓只此一处;detectDriverFromUrl / resolveStandaloneDatabase 的仓外消费者只有 cli/src/utils/migrate-occupancy-gate.ts(只读 sqliteFile,turso 恒为 null)与上面那条 e2e 钉子。

changeset

@objectstack/runtime minor —— 新增了 URL scheme 识别与一个 databaseDriver 枚举值(可授权面变大),不是纯修复,故 minor 而非 patch。

范围外发现(已查重后立单,均未认领)


Generated by Claude Code

…onal Turso driver (#5820)

`detectDriverFromUrl()` refused every libSQL URL as an unsupported scheme while
`resolveDatabaseUrl()` listed `TURSO_DATABASE_URL` among its URL sources — read
it in, cannot dispatch it out. Since #5602 wired `libsql://` for the CLI's
`os serve` / `os start`, the same `OS_DATABASE_URL=libsql://…` booted under
`os start` and hard-failed under `os migrate`, which boots through this stack.

- `libsql://` and `http(s)://*.turso.*` resolve to the `turso` kind — the same
  two spellings `inferDriverTypeFromUrl` classifies on the CLI side.
- The driver comes from the OPTIONAL `@objectstack/driver-turso` package,
  loaded lazily in `turso-driver-factory.ts` and injected through the host
  driver-factory seam `DefaultDatasourcePlugin` documents for exactly this
  case, so connect / bootCritical verdict / escape hatch stay shared.
- Package missing ⇒ loud `MissingDriverPackageError` carrying the install
  command as data; no SQLite fallback (#3276).
- `databaseAuthToken` is consumed now (OS_DATABASE_AUTH_TOKEN, then the
  vendor's TURSO_AUTH_TOKEN) instead of being declared and ignored.
- The file docstring's "ships separately in the ObjectStack Cloud
  distribution" claim expired with #4645; replaced with the facts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wbxm29qPKnLf44AbSxizqW
@vercel

vercel Bot commented Aug 7, 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 7, 2026 12:50pm

Request Review

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/runtime.

21 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/api/client-sdk.mdx (via packages/runtime)
  • content/docs/api/index.mdx (via @objectstack/runtime)
  • content/docs/api/wire-format.mdx (via @objectstack/runtime)
  • content/docs/automation/hook-bodies.mdx (via @objectstack/runtime)
  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/runtime)
  • content/docs/concepts/north-star.mdx (via packages/runtime)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/runtime)
  • content/docs/deployment/index.mdx (via @objectstack/runtime)
  • content/docs/deployment/production-readiness.mdx (via @objectstack/runtime)
  • content/docs/deployment/single-project-mode.mdx (via @objectstack/runtime)
  • content/docs/deployment/vercel.mdx (via @objectstack/runtime)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/runtime)
  • content/docs/kernel/cluster.mdx (via @objectstack/runtime)
  • content/docs/permissions/authentication.mdx (via @objectstack/runtime)
  • content/docs/permissions/authorization.mdx (via packages/runtime)
  • content/docs/plugins/packages.mdx (via @objectstack/runtime)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/runtime)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/runtime)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/runtime)
  • content/docs/releases/implementation-status.mdx (via @objectstack/runtime)
  • content/docs/releases/v17.mdx (via @objectstack/runtime)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling labels Aug 7, 2026
@qq9340100
qq9340100 marked this pull request as ready for review August 7, 2026 13:10
@qq9340100
qq9340100 added this pull request to the merge queue Aug 7, 2026
Merged via the queue into main with commit cca11e9 Aug 7, 2026
25 checks passed
@qq9340100
qq9340100 deleted the claude/issue-5820-standalone-stack-libsql branch August 7, 2026 13:26
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/l tests tooling

Projects

None yet

2 participants