You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(driver-turso)!: timeout bounds remote operations; localPath and wasm leave the published config schema (ADR-0049 enforce-or-remove) (#16376)
* wip(driver-turso): timeout bounds remote operations; localPath and wasm tombstoned (#16024)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
* docs(changeset): the spec ledger entry is an additive widening — minor, not patch (#16024)
The Check Changeset step's WHICH LEVEL rule (maintainer, 2026-09-04, batch #35):
a purely additive widening of a published package's public surface takes at
least minor, and the act sets the floor. The new D3 entry in
packages/spec/src/migrations/registry.ts is that act for @objectstack/spec.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ARYe3yQTQCUFm5qPYNgKaJ
* docs(driver-turso): the replica arm's sync is not cancelled, and the mirror does not declare `mode` (#16024)
Two prose repairs the contract review raised as non-blocking; no behaviour and
no schema changes.
- packages/drivers/driver-turso/README.md — the README named both arms and the
WebSocket gap but dropped the clause `turso-driver.ts`'s docblock and the
changeset both carry: on the replica arm the native binding's own sync is not
cancelled, only no longer awaited. Wording matched to the docblock.
- docs/design/driver-turso.md §10 — "Both declare exactly the keys the driver
reads" overstated: the package-published mirror does not declare `mode`,
which `TursoDriver.detectMode` reads. The sentence now claims only the
direction ADR-0049 governs (no declared key the driver does not read) and
names the gap. The gap itself is pre-existing and deliberately left alone.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg
---------
Co-authored-by: Claude <noreply@anthropic.com>
Three keys on this package's published Turso configuration were declared with a
11
+
describe promising behaviour that no code delivered — ADR-0049's
12
+
declared-but-unenforced shape, sitting beside `concurrency`, which was declared
13
+
the same way and IS forwarded. The maintainer ruled per key: forward `timeout`;
14
+
remove `localPath` and `wasm`. Not a rename for any of the three — an inert key
15
+
with a better name is what ADR-0049 exists to prevent.
16
+
17
+
**`TursoDriverConfig.timeout` now does what its docblock has always said.** It
18
+
never reached `@libsql/client`. It still does not reach that client's own
19
+
`Config.timeout`, and deliberately: measured against `@libsql/client@0.17.4`,
20
+
that option is the busy timeout for lock contention on local `file:` databases
21
+
("remote clients ignore it"), so forwarding to it would have left remote mode
22
+
exactly as inert as before. Instead:
23
+
24
+
-**Remote mode over HTTP** (`libsql://`, `https://`, `http://`): the driver
25
+
hands the client a `fetch` that aborts every request once the window elapses,
26
+
and the operation fails as `TIMEOUT` / 504 (the ADR-0112 envelope) instead of
27
+
hanging on a stalled endpoint. `wss://` / `ws://` URLs ride the WebSocket
28
+
transport, which exposes no such seam in this client version — they are not
29
+
bounded, and the docblock says so.
30
+
-**Replica mode**: `sync()` — the one remote operation on that arm — rejects
31
+
with the same envelope when it has not completed within the window. The native
32
+
binding's sync is not cancelled, only no longer awaited.
33
+
-`0` or unset means no bound, as the published schema already documented.
34
+
35
+
A datasource authors this as `config.timeoutMs`; the datasource seam maps it
36
+
onto the driver's `timeout`, so a `timeoutMs` that used to be silently dropped
37
+
now bounds the connection it describes.
38
+
39
+
**BREAKING** — `TursoConfigSchema` refuses `localPath` and `wasm`. Neither was
40
+
read by any code: the replica arm names its local file via `url` (forwarding
41
+
`localPath` would have created a second way to say the same thing), and nothing
42
+
selects a WASM build of libSQL (forwarding `wasm` would have meant building
43
+
one). The shape is a plain `z.object`, so a bare deletion would have stripped
44
+
both keys in silence; they stay declared as `z.never()` tombstones instead —
45
+
`tsc` refuses them on anything typed `TursoConfig`, and a value reaching the
46
+
parse raises the prescription below rather than a generic unrecognised-key
47
+
error. The same treatment this package's `timeout` → `timeoutMs` rename took.
48
+
49
+
## Migration
50
+
51
+
| Wrote | Write instead |
52
+
| --- | --- |
53
+
|`localPath: './replica.db'` beside `url: 'file:./replica.db'`| delete `localPath` — `url` names the replica's local file, `syncUrl` the remote primary; a path that differed from `url` belongs in `url`|
54
+
|`wasm: true`| delete `wasm` — no WASM build was ever selected; a runtime that cannot load native bindings uses the remote arm (`libsql://` / `https://`), which needs none |
55
+
56
+
`@objectstack/spec`'s own turso contract never declared either key, so no stack
57
+
source or stored datasource row that passed the spec door can carry them; the
58
+
ADR-0087 ledger records the removal as the D3 entry
59
+
`driver-turso-config-local-path-wasm-retired` (no D2 conversion — there is no
60
+
lossless rewrite for a value that never did anything), which is the
61
+
`@objectstack/spec``minor` here — the entry is a new member of the published migration
62
+
registry (`packages/spec/src/migrations/registry.ts`), an additive widening of that package's
|`sync.onConnect`|`boolean`|`true`| Sync immediately on connect |
525
-
|`timeout`|`number?`| — | Operation timeout in milliseconds |
526
-
|`wasm`|`boolean?`| — | Use WASM build for edge/browser environments |
531
+
|`timeoutMs`|`number?`| — | Operation timeout in milliseconds for remote operations (0 = no bound): remote mode over HTTP aborts each request at the window (`TIMEOUT` / 504); replica mode bounds `sync()`; WebSocket URLs are not bounded |
0 commit comments