Skip to content

refactor!: remove record-adapter-sqljs and the FTS4 branch it required - #163

Merged
cuibonobo merged 4 commits into
mainfrom
claude/sqljs-adapter-strategy-81g3vf
Aug 14, 2026
Merged

refactor!: remove record-adapter-sqljs and the FTS4 branch it required#163
cuibonobo merged 4 commits into
mainfrom
claude/sqljs-adapter-strategy-81g3vf

Conversation

@cuibonobo

@cuibonobo cuibonobo commented Aug 13, 2026

Copy link
Copy Markdown
Member

Summary

Removes @haverstack/record-adapter-sqljs, the FTS4 code that existed only to serve it, and the browser-support claims it backed. Net −2288 lines.

sql.js was chosen for runtimes without full Node support — principally Cloudflare Workers — and never served that case:

  • initialize()/open() called initSqlJs() with no options. Workers forbid compiling WASM from bytes at runtime; the only route is injecting a pre-compiled WebAssembly.Module through Emscripten's instantiateWasm, and the adapter exposed no options pass-through at all. The same gap left a bundled browser app unable to set locateFile.
  • Even with the WASM injection fixed, persist() serialized the whole database after every write. In a per-request Worker isolate that has no single-writer guarantee, against docs/spec/adapters.md § Concurrency & storage ownership.
  • Its tests ran under environment: 'node', so the package only ever demonstrated sql.js working in the one runtime its own module comment said it was not for.

With the adapter gone, FTS4 has no consumer. Removed: FTS4_SCHEMA_SQL, fts.ts, the FtsStrategy seam (which existed solely because FTS4 and FTS5 differ on unindexing), and the FTS4→FTS5 migration in record-adapter-sqlite plus its test block. SharedSqlRecordLogic now reaches fts5Strategy directly instead of taking fts and sanitizeSearch as constructor deps, and buildWhereClause(query) calls sanitizeFts5Query rather than threading a sanitizer through.

Browser support is removed from the README package tables and repo tree, CONTRIBUTING's tree, and the adapters spec rather than caveated — the claim was what created the obligation reported in the issue.

Closes #144. Follow-ups filed: #160 (blob-adapter-s3), #161 (record-adapter-do-sqlite).

Spec

Observable behavior changes — an adapter is gone, and record-adapter-sqlite no longer opens FTS4 files. docs/spec/adapters.md is updated in the same change:

  • Adapter backendsrecord-adapter-sqljs row dropped from the table
  • Adapter backends (sqlite-shared paragraph) — rewritten for a single consumer, plus a new paragraph recording why SqlExecutor is synchronous and what that rules out (D1, libsql over HTTP). The File compatibility note now states the file carries an FTS5 index, so a reader needs FTS5 and not merely SQLite.
  • Adapter capabilities — sql.js removed from the contentFieldQuery local-adapter list, the per-adapter notes, and the maxAttachmentBytes: null list
  • Concurrency & storage ownership — the record-adapter-sqljs bullet (persist-callback delegation to the embedding host) is deleted

docs/spec/wire-format.md § Bounding query cost is also touched, as fallout from merging main — see below.

docs/spec/attachments.md is untouched: nothing here changes the blob contract or garbage collection.

Verification

All five, on the post-merge tree:

pnpm run format:check   # clean
pnpm run lint           # clean, 8 packages
pnpm test               # 1053 passed across 7 packages
pnpm run build          # clean
pnpm run typecheck      # clean

Checked by hand: grep -ri "sqljs|sql\.js|fts4" returns nothing outside pnpm-lock.yaml, and the surviving browser mentions are all unrelated (wire-format's HTTP-client note, id.ts/did.ts runtime-agnostic claims, the bookmark commons doc). pnpm install regenerated the lockfile.

Notes for reviewers

Merged main (#162) into this branch, which conflicted in three places:

  • record-adapter-sqljs/src/index.ts and sqlite-shared/src/fts.ts — modify/delete. Main added maxContentBytes to the sqljs adapter's capabilities and rewrote the FTS4 sanitizer's cost note; both files stay deleted and neither change has anywhere to land.
  • sqlite-shared/src/fts5.ts — both sides rewrote the module comment's query-timeout paragraph. Took main's version, which points at the new Bounding query cost spec section, minus its reference to the FTS4 sanitizer as a peer.

That new spec section also described both engines blocking in-process as the reason a timeout can't be set from inside the call. With sql.js gone only node:sqlite is left to describe, so this PR edits one sentence of wire-format.md that main just added — flagging it because it's outside the diff you'd expect from a package removal.

Slightly beyond the literal FTS4 deletion: buildWhereClause's sanitizeSearch parameter and its SanitizeSearch type are gone too. Once FTS4 went, it had exactly one caller passing one constant and no test stub — dead injection rather than a seam. Happy to restore it if you'd rather keep the shape.

SqlExecutor stays, even though sqlite-shared now has one consumer. Its synchronous shape is what would make #161 cheap, and the spec now says so explicitly instead of leaving it implicit.

Not in this PR: @haverstack/record-adapter-sqljs@0.6.0 is still live on npm. This drops the publish: script but can't touch what's published — deprecating the package with a pointer to record-adapter-sqlite is a manual step.

claude added 4 commits August 13, 2026 17:26
sql.js was chosen for runtimes without full Node support, principally
Cloudflare Workers, and never served that case. initialize()/open() call
initSqlJs() with no options, and Workers forbid compiling WASM from bytes
at runtime — the only route is injecting a pre-compiled WebAssembly.Module
via Emscripten's instantiateWasm, which the adapter exposed no hook for.
The same gap left a bundled browser app unable to set locateFile.

Nor would fixing the WASM injection have been enough. Serializing the
whole database after every write has no single-writer guarantee in a
per-request isolate, which the concurrency rule in docs/spec/adapters.md
requires. The package's tests ran under environment: 'node', so it only
ever demonstrated sql.js working in the one runtime its own module comment
said it was not for.

With the adapter gone, FTS4 has no consumer: FTS4_SCHEMA_SQL, fts.ts, the
FtsStrategy seam (which existed solely because FTS4 and FTS5 differ on
unindexing), and the FTS4->FTS5 migration in record-adapter-sqlite all go
with it. SharedSqlRecordLogic reaches FTS5 directly rather than taking a
strategy and a sanitizer as constructor deps, and buildWhereClause calls
sanitizeFts5Query instead of threading one through.

Browser support is removed from the README package tables, CONTRIBUTING's
tree, and the adapters spec rather than caveated — the claim was the thing
creating the obligation reported in the closed issue.

Closes #144. Follow-ups: #160 (blob-adapter-s3), #161 (Durable Objects).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015CWGuMhU9ygf9J4jaDDqdE
Resolves three conflicts from #162:

- record-adapter-sqljs/src/index.ts and sqlite-shared/src/fts.ts —
  modify/delete. Main added maxContentBytes to the sqljs adapter's
  capabilities and rewrote the FTS4 sanitizer's cost note; both files stay
  deleted, and neither change has anywhere to land.
- sqlite-shared/src/fts5.ts — both sides rewrote the module comment's
  query-timeout paragraph. Takes main's version, which points at the new
  wire-format Bounding query cost section, minus its reference to the FTS4
  sanitizer as a peer.

Also drops sql.js from that new spec section, which described both engines
blocking in-process as the reason a timeout can't be set from inside the
call. Only node:sqlite is left to describe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015CWGuMhU9ygf9J4jaDDqdE
SharedSqlRecordLogic and SharedTokenLogic each took an optional onWrite
callback, invoked after every mutating operation so an engine whose
storage sits outside the database handle could flush. Only sql.js needed
that — it exported the whole database and handed the bytes to the host.
record-adapter-sqlite constructs both with { exec } alone, so since that
adapter's removal the hooks have fired into nothing.

Removing them also settles what the record logic is: onWrite was the only
genuinely asynchronous step in it, so every await inside SharedSqlRecordLogic
was already awaiting undefined. The class is now synchronous throughout,
and its async method signatures exist solely to satisfy StackRecordAdapter,
which must stay Promise-based for the API adapter's sake.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015CWGuMhU9ygf9J4jaDDqdE
D4 already rules out awaiting a handler's returned promise, but it left two
things inferable rather than stated: whether an async handler is legal at
all (the `=> void` handler type reads as a prohibition to some, when it only
means the return value is ignored), and what the no-delay guarantee is
actually scoped to.

It is scoped to the emitter. Handlers run inline on the caller's thread,
which a node:sqlite-backed adapter has just run its write on synchronously,
so a handler's synchronous body still extends that window and defers the
mutating method's promise. Deferring work with an async handler is the fix,
and is what the rule was always steering toward.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015CWGuMhU9ygf9J4jaDDqdE
@cuibonobo
cuibonobo merged commit cf2bb4b into main Aug 14, 2026
5 checks passed
@cuibonobo
cuibonobo deleted the claude/sqljs-adapter-strategy-81g3vf branch August 14, 2026 13:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Browser blob adapter — the browser story is currently records-only

2 participants