Decisions
Decisions are recorded here when they are not obvious from the diff. Dates are 2026-10-07 unless stated. A decision is revisited by adding a new entry, not by rewriting an old one.
D1. The package is needed because upstream 2.8.0 breaks on a fresh install
Decision. Publish a package: upstream jeep-sqlite 2.8.0 with its Stencil loader and
WebAssembly rebuilt together on current sql.js.
Evidence.
Upstream's latest npm release is
2.8.0, published 2024-08-16. The upstream GitHubmastertip (3f3c8f2) is that release; there is no newer release, tag or commit.Its
dependenciesdeclare"sql.js": "^1.11.0", which a fresh install resolves to the latest 1.x. Its prebuiltdist/embeds the sql.js glue from the 1.11 era (about half of the minified 1.11.0 glue blocks appear verbatim indist/esm/jeep-sqlite.entry.js; about a tenth of the 1.14.2 blocks do).Upstream's readme tells consumers to copy
sql-wasm.wasmfromnode_modules/sql.js/dist. That file is now a 1.14 binary. Pairing the 1.11 glue with it fails:failed to asynchronously prepare wasm: LinkError: WebAssembly.instantiate(): Import #34 "a" "I": function import requires a callableReproduced in Node by copying
sql-wasm.jsfromsql.js@1.11.0besidesql-wasm.wasmfromsql.js@1.14.2. Glue and binary from the same release (1.11.0 with 1.11.0) work.Upstream's Stencil guide still asks applications to add
rollup-plugin-node-polyfills. Itsdist/has no barebufferorprocessimports, so polyfills are not what is broken; the mismatched glue and binary are.
Conclusion. Upstream does not cover this. When it ships a release built on current sql.js, this package retires, and the change here can be offered to it as a pull request.
D2. The name is jeep-sqlite-current-sqljs, unscoped
Decision. jeep-sqlite-current-sqljs. The bare name jeep-sqlite is upstream's, and every
package here publishes unscoped.
Why. The name says what this package does with jeep-sqlite: it is jeep-sqlite on current sql.js. It does not claim to maintain or replace the project. Names that describe how the package is built (a "browser" or "bundled" variant) were set aside: they say nothing about the reason it exists.
D3. The first version is 2.9.0
Decision. 2.9.0.
Why. A prerelease such as 2.8.0-x.1 under a fresh name signals "not stable", and npm would
need --tag to publish it, so npm install jeep-sqlite-current-sqljs would not resolve it. The
component's API is unchanged, which keeps upstream's major (2); the minor marks what is new: a
loader built on a different sql.js, an exports map, and the WebAssembly files as exports. A patch
(2.8.1) would have undersold the new entry points. Release Please continues from 2.9.0.
D4. History descends from upstream, byte for byte
Decision. Preserve upstream's history byte for byte, with this package's commits on top.
The intended GitHub repository is a fork of jepiqueau/jeep-sqlite; creating it and verifying
the fork relationship remain publication steps. The local checkout has no remote yet.
Why. Attribution, and so that a pull request upstream is a normal fork pull request. The earlier
private build had to be sanitized for publication (hostnames and local paths rewritten in commit
text). Rewriting the whole history also strips the GPG signatures from upstream's merge commits,
which changes their hashes and breaks the ancestry. Upstream's history contains none of the
sanitized strings (checked with a case-insensitive scan of git log -p), so only this package's
own commits were rewritten and then rebased onto the untouched upstream commits. Upstream's
3f3c8f2 is an ancestor of main.
D5. sql.js is pinned to exactly 1.14.1
Decision. "sql.js": "1.14.1", no range.
Why. The failure in D1 is a range that let the binary drift away from the glue. An exact pin
makes the loader built here and the binary installed beside it the same release for every consumer,
and a sql.js upgrade becomes a deliberate rebuild and release (Dependabot's pull request rebuilds
in CI, and the packed-tarball check fails if the shipped binary does not match the installed
sql.js). 1.14.1 is chosen because it is the release @capacitor-community/sqlite consumers already
carry; the 1.14.2 binary differs from it, so the choice matters.
D6. The WebAssembly binaries ship in the package
Decision. wasm/sql-wasm-browser.wasm and wasm/sql-wasm.wasm are packed and exported as
jeep-sqlite-current-sqljs/sql-wasm-browser.wasm and jeep-sqlite-current-sqljs/sql-wasm.wasm.
Why. It makes the pairing in D5 a property of the package, not an instruction for the reader. The
embedded glue is sql.js's browser build, which asks for sql-wasm-browser.wasm; the default glue
asks for sql-wasm.wasm. The two files are byte-identical in each sql.js release, so shipping both
costs size, not correctness, and covers whichever build a toolchain picks. In Chrome, with the
packed tarball installed from npmjs into an empty project and the element served from its script
build, the only binary requested was sql-wasm-browser.wasm, and a create, insert, query and close
round trip reported SQLite 3.49.1 (the engine inside sql.js 1.14.1). They are copied from the
pinned dependency at build time (scripts/copy-wasm.mjs) and are not committed.
D7. An exports map, with /loader as a first-class entry
Decision. exports covers ., ./loader, ./dist/components/*, ./dist/jeep-sqlite/*, the
two WebAssembly files and ./package.json.
Why. Consumers import .../loader, which resolved only through directory resolution before.
The two dist wildcards keep the documented deep imports (dist/components/jeep-sqlite.js, and the
script build served from a CDN) working; the rest of dist/ (the Stencil collection, the CommonJS
internals) is deliberately not importable, because it was never documented and a wildcard over it
made publint warn about every file. The package is not "type": "module" (Stencil's *.cjs.js
outputs would become ES modules). The packed-tarball check loads CommonJS and ES modules in Node
and bundles every ESM entry for the browser with esbuild, which is how these entries are consumed.
Stencil emits its ES modules as .js. scripts/mark-esm.mjs writes {"type": "module"} into the
three directories that hold only ES modules (dist/esm, dist/components, dist/jeep-sqlite), and
the import conditions point into them, so the entries also import cleanly in Node (a server-rendering
framework importing the loader, for example). Remaining tool findings are accepted and understood:
Are The Types Wrong's false-cjs rule is ignored because Stencil emits one .d.ts for both module
kinds (a pure type declaration with no runtime), and publint notes the same ambiguity as a warning.
Resolution under bundler and node16 from CommonJS is clean.
D8. Toolchain: Node 26, pnpm 12, TypeScript 7 for the type check only
Decision. Node 26 (.nvmrc, mise.toml), pnpm 12, Vitest 4, Biome. TypeScript 7 runs the
typecheck gate; Stencil compiles with the TypeScript it bundles.
Why. Stencil 4 compiles components with its own bundled TypeScript and does not take the
repository's. TypeScript 7 still works as an independent check, with three settings written down in
tsconfig.json: moduleResolution: bundler (node10 is removed in 7); strict: false (TypeScript
7 turns strictness on by default, and the upstream source was never written for it); and
skipLibCheck: true (declaration conflicts inside @stencil/vitest's own typings). TypeScript 7
also found one real defect, fixed in uint2blob: it passed uint.buffer to Blob, which includes
bytes outside a sub-view; it now passes the view itself. For a whole buffer, as sql.js returns
from export(), the result is identical. Vitest stays on 4 because @stencil/vitest supports
vitest up to 4.
D9. Biome covers what this package owns, not upstream's source
Decision. Biome lints and formats scripts, configuration, tests and src/index.ts. The
component and utility sources under src/ are outside it.
Why. Run over them, Biome reports about a thousand findings (noExplicitAny, ==, let for
constants, formatting) in code that is upstream's. Fixing them would touch most lines, break
the line-for-line correspondence with upstream that lets a fix travel in either direction, and risks
behavior changes in a database layer (== to === among them). The source is checked by the
TypeScript gate, the Stencil build and the tests instead.
D10. Tests run the real engine; coverage is a floor
Decision. Two Vitest projects: spec (the element in Stencil's DOM) and unit (the Database
class against the real sql.js in Node with an in-memory store, no mocks of SQLite). Coverage
thresholds are a floor set at the current level.
Why. The behavior that matters (statements, transactions and rollback, persistence across
close and reopen, upgrades, JSON import and export) lives in the Database and utils-* modules and
can run in Node, where sql.js reads its binary from disk. The rest of the element is exercised by
upstream's manual browser pages (src/index_*.html), which are not automated. A test also asserts
the exact sql.js pin against the installed copy. The threshold only moves up.
D11. Documentation is upstream's readme, reorganized
Decision. Upstream's readme became docs/API.md (reference) and its release notes became the
tail of CHANGELOG.md; README.md is new. The empty upstream IonicAngular_App.md was removed.
Why. The method, event and usage reference is the useful part of upstream's readme and is kept intact. An empty guide in a documentation navigation is a dead link.
D12. CI covers every maintained Node line on Linux
Decision. Node.js 22, 24 and 26 on ubuntu-24.04; no Windows job. Local development
defaults to Node 26, but contributors may use any supported line without an exact-version guard.
Why. engines.node is >=22: the full verification chain, including tests and packed-consumer
imports, passes on Node 22 and 26. CI uses major versions to follow supported patch updates.
The package builds a browser artifact and touches no
OS-specific path or process behavior, so a Windows job would test the runner, not the package.
D13. The first publish is local; later releases publish by OIDC
Decision. 2.9.0 is published once from a clean checkout of the green main commit with
--provenance=false, using a token kept outside the repository. Later releases publish from the
publish job of .github/workflows/cd.yml with npm provenance, through trusted publishing.
Why. Trusted publishing is configured per package on npmjs, and the package must exist first. No token is stored in the repository or its secrets.
D14. Release automation resumes after the approved first publication
Decision. The first-release hold is fulfilled by the exact baseline v2.9.0 tag at
b036dce0039b0d18f10664a24e6bbc82789f5721, its GitHub release, and its npm publication. Restore
Release, CD and Automerge as guarded jobs. Every job also requires the repository variable
RELEASE_AUTOMATION_ENABLED to equal true; source changes alone do not activate automation.
Why. The initial if: false guards prevented imported history from creating releases or
publishing before the first package existed. The owner subsequently approved trusted publishing and
required npm 2FA for this package with the other public packages. That approval supersedes the
temporary first-stage source holds, while the repository variable keeps activation a deliberate
post-merge step. Release and CD retain their original trusted-event and successful-conclusion
conditions, and Automerge retains its same-repository, branch and generated-actor restrictions.
This repository's Release Please action uses CI_GITHUB_TOKEN, so its generated pull requests are
authored by jbdevprimary, not github-actions[bot]. The Release Please predicate admits only
that identity with this package's exact generated branch and expected release title, plus the
autorelease: pending label and Release Please body marker. It also listens for labeled, so the
predicate is evaluated after Release Please applies the pending label. Those facts are based on the
completed game-session #5, persistence-drizzle #7 and seeded-maze #4 release pull requests.