Skip to content

build!: bundle sqlite-shared, bump versions, complete the publish surface - #170

Merged
cuibonobo merged 2 commits into
mainfrom
claude/haverstack-server-sync-3kit7n
Aug 15, 2026
Merged

build!: bundle sqlite-shared, bump versions, complete the publish surface#170
cuibonobo merged 2 commits into
mainfrom
claude/haverstack-server-sync-3kit7n

Conversation

@cuibonobo

Copy link
Copy Markdown
Member

Summary

Everything needed to publish the coordinated release in #167. Three things, all packaging.

1. sqlite-shared stays unpublished, and is now genuinely internal.

docs/spec/adapters.md described it as "an internal, non-public package", but record-adapter-sqlite declared it in dependencies and tsc emitted the import verbatim:

dist/index.js:29        import { RECORD_SCHEMA_SQL, acquireLock, SharedSqlRecordLogic,}
                          from '@haverstack/sqlite-shared';
dist/executor.d.ts:1    import type { SqlExecutor } from '@haverstack/sqlite-shared';

So publishing record-adapter-sqlite would have meant publishing sqlite-shared too — or shipping a package that cannot resolve at install time. This makes the documented intent true instead:

  • record-adapter-sqlite builds with tsup, inlining sqlite-shared and keeping @haverstack/core external. Inlining core would give the package a private copy of the error classes and break instanceof against the caller's copy — the same class-identity hazard already visible in haverstack/server's current node_modules.
  • sqlite-shared moves to devDependencies and is marked private.
  • SqlExecutor disappears from the emitted .d.ts. It was only ever reachable through an internal file that tsc happened to emit; a single-entry bundle drops it. The public surface is now exactly NativeSQLiteRecordAdapter, NativeRecordInitializeOptions, NativeRecordOpenOptions, NativeTokenStore, NativeTokenStoreOptions, defaultTokenStorePath.

node:sqlite is unaffected — src/node-sqlite.ts reaches it through process.getBuiltinModule() rather than a static import, which survives bundling intact.

2. Versions. For packages already on npm: core 0.8.0 → 0.9.0, and wire-types / adapter-local / blob-adapter-disk / adapter-api 0.6.0 → 0.7.0. record-adapter-sqlite, conformance-fixtures and commons stay at 0.1.0 as first publishes. Every version currently in the repo is identical to what is already on the registry, so npm publish would reject all of them as-is.

3. The publish surface is complete. Adds the two missing scripts (publish:record-adapter-sqlite, publish:conformance-fixtures) and publish:all, which runs all eight in dependency order so nothing is published before something it depends on.

Closes the packaging half of #167. Follows #168, which reorganized the export surface.

Spec

docs/spec/adapters.md § Package naming convention — the sqlite-shared paragraph gains a statement that it is bundled rather than published, and that SqlExecutor is therefore an internal collaborator rather than an extension point. That is the observable change: a third-party SQLite adapter can no longer be built by installing sqlite-shared, and the doc now says so rather than implying the opposite.

Flagged as a real decision rather than a packaging tweak, because it forecloses something #161 (record-adapter-do-sqlite) might have wanted — see Notes.

CONTRIBUTING.md and AGENTS.md updated to match, including a note that sqlite-shared belongs in devDependencies and that adding an export to it does not widen any package's public API. Both also drop stale references to "the two SQLite adapters", left over from the record-adapter-sqljs removal.

Verification

pnpm run format:check   All matched files use Prettier code style
pnpm run lint           9 packages, clean
pnpm test               1065 tests passed
pnpm run build          9 packages built
pnpm run typecheck      9 packages, clean
pnpm run verify:pack    all entry points resolve from packed tarballs

verify:pack is new (scripts/verify-pack.mjs) and is the one worth looking at. It packs every publishable package, installs the tarballs into a throwaway project outside the workspace, and imports each entry point. It covers two failure modes a green build cannot:

Both are silent until published, and unpublishing is not a clean remedy. Current output:

  skip (private)  @haverstack/sqlite-shared
  packed  @haverstack/core@0.9.0 … (8 packages)
  absent (correct)  @haverstack/sqlite-shared
  ok  @haverstack/core            ok  @haverstack/core/did
  ok  @haverstack/core/wire       ok  @haverstack/core/adapter
  ok  @haverstack/core/testing    ok  @haverstack/wire-types
  ok  @haverstack/blob-adapter-disk
  ok  @haverstack/record-adapter-sqlite
  ok  @haverstack/adapter-local   ok  @haverstack/adapter-api
  ok  @haverstack/commons         ok  @haverstack/conformance-fixtures

Checked by hand: the bundle's only external imports are @haverstack/core, fs and crypto; node:sqlite still reached via process.getBuiltinModule; the rolled .d.ts imports only from @haverstack/core, /adapter and /wire.

CI needs no changes — tsup is a devDependency, and the existing install → build → typecheck ordering already covers it.

Notes for reviewers

This closes a door, deliberately. docs/spec/adapters.md said sqlite-shared exists "so a second SQLite engine inherits the behavior rather than reimplementing it" while also calling it non-public — which only coexist if that second engine always lives in this repository. Bundling picks that reading. If #161's record-adapter-do-sqlite is meant to be buildable by a third party rather than merged here, this is the change to reconsider, and the remedy is small: publish sqlite-shared and drop noExternal from the tsup config. The spec now states the trade-off explicitly so it is a decision rather than an accident.

record-adapter-sqlite is the only package that changed build tooling. Everything else stays on plain tsc. Worth a look at whether one bundler in an otherwise uniform repo is an acceptable cost — the alternative was publishing sqlite-shared.

Version numbering is a judgement call. 0.9.0 for core follows #167's suggestion, holding 1.0.0 for when haverstack/server passes the conformance suite — the point at which the wire protocol has been demonstrated by a second implementation. The 0.6.0 → 0.7.0 group keeps the existing pattern of adapters versioning independently of core.

Not in scope: git tags and release notes, both still open on #167. verify:pack is not wired into CI — it packs and installs, so it is slower than the other jobs, and it is most useful as a pre-publish gate. Adding it as a release-workflow step would be reasonable follow-up.

🤖 Generated with Claude Code

https://claude.ai/code/session_01D5LtyME9WJky9jaB51tpjp


Generated by Claude Code

claude added 2 commits August 15, 2026 18:21
…face

Everything needed to publish the coordinated release in #167.

sqlite-shared stays unpublished. docs/spec/adapters.md called it "an
internal, non-public package", but record-adapter-sqlite declared it as a
dependency and tsc emitted the import verbatim, so publishing that adapter
would have required publishing sqlite-shared too — or shipped a package
that cannot resolve. Bundling makes the documented intent true:

- record-adapter-sqlite builds with tsup, inlining sqlite-shared and
  keeping @haverstack/core external. Inlining core would give the package
  its own copy of the error classes and break instanceof against the
  caller's copy.
- sqlite-shared moves to devDependencies and is marked private.
- SqlExecutor no longer appears in the emitted .d.ts. It was only ever
  reachable through an internal file that tsc happened to emit; the
  single-entry bundle drops it, so the public surface is now exactly the
  six intended symbols.

Versions, for the packages already on npm: core 0.8.0 -> 0.9.0, and
wire-types, adapter-local, blob-adapter-disk, adapter-api 0.6.0 -> 0.7.0.
record-adapter-sqlite, conformance-fixtures and commons stay at 0.1.0 as
first publishes.

Adds the two missing publish scripts (record-adapter-sqlite,
conformance-fixtures) and publish:all, which runs them in dependency
order so no package is published before something it depends on.

Adds scripts/verify-pack.mjs, run as verify:pack. It packs every
publishable package, installs the tarballs into a throwaway project
outside the workspace, and imports every entry point. This covers two
things a green build cannot: that the subpath exports added in #168
resolve from a real tarball, and that sqlite-shared is absent from an
installed tree. Both are silent-until-published failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D5LtyME9WJky9jaB51tpjp
pnpm rewrites the workspace protocol at publish time, and workspace:*
becomes an exact pin — the tarball for adapter-local@0.7.0 declared
"@haverstack/core": "0.9.0", not a range.

That bakes in the duplicate-copy hazard #167 documents. Publishing a
core 0.9.1 would leave adapter-local demanding exactly 0.9.0, so an app
depending on both gets two copies of core in its tree — and errors thrown
inside the adapter carry a different class identity than the ones the app
tests with instanceof. It is the same failure already visible in
haverstack/server's node_modules, and with workspace:* it recurs on every
patch release rather than being one-time drift.

workspace:^ rewrites to ^0.9.0 instead. In the 0.x range that means
>=0.9.0 <0.10.0, so patch releases dedupe to a single copy while a minor
bump — which is this project's breaking-change signal — still requires a
coordinated release. devDependencies keep workspace:*; they are never
published.

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

Copy link
Copy Markdown
Member Author

Pushed a second commit (288005d) after a review question about whether anything pins a specific core version.

Nothing hardcodes a version — but workspace:* was producing an exact pin, which is worse. pnpm rewrites the workspace protocol at publish time, and workspace:* becomes the concrete version with no range. Packing adapter-local before the fix:

"dependencies": {
  "@haverstack/core": "0.9.0",
  "@haverstack/blob-adapter-disk": "0.7.0",
  "@haverstack/record-adapter-sqlite": "0.1.0"
}

That bakes the duplicate-copy hazard this PR's parent issue documents straight into the release. Publish a core 0.9.1 and adapter-local@0.7.0 still demands exactly 0.9.0, so an app depending on both ends up with two copies of core in its tree — and errors thrown inside the adapter carry a different class identity than the ones the app tests with instanceof.

It is the same failure already sitting in haverstack/server's node_modules today (core@0.6.0 and core@0.8.0 side by side). What I had assumed was one-time drift from an old release is actually structural: with workspace:* it would recur on every patch release.

Fix: workspace:^ on the @haverstack/* entries in dependencies, which rewrites to a range:

"dependencies": {
  "@haverstack/core": "^0.9.0",
  "@haverstack/blob-adapter-disk": "^0.7.0",
  "@haverstack/record-adapter-sqlite": "^0.1.0"
}

In the 0.x range ^0.9.0 means >=0.9.0 <0.10.0, so patch releases dedupe to a single copy while a minor bump — this project's breaking-change signal, per the no-backward-compatibility policy — still forces a coordinated release. That is the intended semantics rather than an accident of the caret.

devDependencies keep workspace:*; they are never published, and sqlite-shared is only reachable that way now.

Re-verified after the change: 9 packages built, 9 typechecked, 1065 tests, format clean, and verify:pack green with the ranges confirmed in freshly packed tarballs.


Generated by Claude Code

@cuibonobo
cuibonobo merged commit 72868c4 into main Aug 15, 2026
5 checks passed
@cuibonobo
cuibonobo deleted the claude/haverstack-server-sync-3kit7n branch August 15, 2026 22:01
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.

2 participants