refactor!: remove record-adapter-sqljs and the FTS4 branch it required - #163
Merged
Conversation
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
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.
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()calledinitSqlJs()with no options. Workers forbid compiling WASM from bytes at runtime; the only route is injecting a pre-compiledWebAssembly.Modulethrough Emscripten'sinstantiateWasm, and the adapter exposed no options pass-through at all. The same gap left a bundled browser app unable to setlocateFile.persist()serialized the whole database after every write. In a per-request Worker isolate that has no single-writer guarantee, againstdocs/spec/adapters.md § Concurrency & storage ownership.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, theFtsStrategyseam (which existed solely because FTS4 and FTS5 differ on unindexing), and the FTS4→FTS5 migration inrecord-adapter-sqliteplus its test block.SharedSqlRecordLogicnow reachesfts5Strategydirectly instead of takingftsandsanitizeSearchas constructor deps, andbuildWhereClause(query)callssanitizeFts5Queryrather 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-sqliteno longer opens FTS4 files.docs/spec/adapters.mdis updated in the same change:record-adapter-sqljsrow dropped from the tablesqlite-sharedparagraph) — rewritten for a single consumer, plus a new paragraph recording whySqlExecutoris 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.contentFieldQuerylocal-adapter list, the per-adapter notes, and themaxAttachmentBytes: nulllistrecord-adapter-sqljsbullet (persist-callback delegation to the embedding host) is deleteddocs/spec/wire-format.md § Bounding query costis also touched, as fallout from merging main — see below.docs/spec/attachments.mdis untouched: nothing here changes the blob contract or garbage collection.Verification
All five, on the post-merge tree:
Checked by hand:
grep -ri "sqljs|sql\.js|fts4"returns nothing outsidepnpm-lock.yaml, and the survivingbrowsermentions are all unrelated (wire-format's HTTP-client note,id.ts/did.tsruntime-agnostic claims, the bookmark commons doc).pnpm installregenerated the lockfile.Notes for reviewers
Merged main (#162) into this branch, which conflicted in three places:
record-adapter-sqljs/src/index.tsandsqlite-shared/src/fts.ts— modify/delete. Main addedmaxContentBytesto 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:sqliteis left to describe, so this PR edits one sentence ofwire-format.mdthat 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'ssanitizeSearchparameter and itsSanitizeSearchtype 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.SqlExecutorstays, even thoughsqlite-sharednow 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.0is still live on npm. This drops thepublish:script but can't touch what's published — deprecating the package with a pointer torecord-adapter-sqliteis a manual step.