Skip to content

fix: Tier 1 ship-blockers — packaging, pg value integrity, eager delete (v1.0.1) - #15

Merged
revxshafi merged 1 commit into
mainfrom
fix/tier1-ship-blockers
Aug 22, 2026
Merged

fix: Tier 1 ship-blockers — packaging, pg value integrity, eager delete (v1.0.1)#15
revxshafi merged 1 commit into
mainfrom
fix/tier1-ship-blockers

Conversation

@revxshafi

Copy link
Copy Markdown
Collaborator

Summary

Tier 1 correctness fixes. No public API changes and no breaking changes — safe as a patch release (1.0.01.0.1).

  • NEW-13 — import 'sql-switch' no longer drags in both drivers. With tsup splitting: false, esbuild inlined each lazily-imported driver into the entry chunk and hoisted its top-level import pg / import better-sqlite3, so a single-engine install crashed on import. splitting: true keeps the drivers as their own chunks, loaded only once an engine is selected — restoring the optional-peer-dep invariant. Covered on both the ESM and CJS entrypoints.
  • NEW-2 — pg get() no longer corrupts JSON-looking strings. get() read through drizzle's jsonb column, running a second JSON.parse on a value pg had already parsed, so a snowflake id "123456789012345678" came back as a precision-lost number and disagreed with entries()/scans on the same row. get/set/delete now use the same raw parameterized $n::jsonb pool path as the bulk upsert and the scans, so every read/write path agrees byte-for-byte.
  • NEW-7 — pg set(null) is consistent across paths. drizzle mapped a JS null onto SQL NULL, which the NOT NULL value column rejected on the forced path. set now stores a jsonb null on every path; get() reads it back as null and has() still reports the row.
  • C1 — an un-awaited delete() now lands. delete() only did its work inside the WriteOperation callbacks, so a fire-and-forget delete() silently did nothing while a fire-and-forget set() committed. It now executes eagerly at call time (matching set()) and evicts any buffered set for the key so a later flush can't resurrect it; await/.force() only decide whether you wait.

drizzle-orm is still used for the SQLite driver and the schema builders, so it remains a dependency. package.json peer/optional deps are unchanged — no driver was promoted to a hard dependency.

Tests added

  • test/no-driver-hoist.test.ts — dist guard: asserts neither built entrypoint (ESM + CJS) has a module-scope import/require of pg/better-sqlite3. Proven to fail against a splitting: false build.
  • test/eager-delete.test.ts — engine-agnostic (KeyProxy over the in-memory fakedriver): un-awaited delete reaches the driver with the collector off and on, and evicts a buffered set. Proven to fail against a lazy delete.
  • test/pg-json-roundtrip.test.ts — pg-only (skipIf(!DATABASE_URL), own throwaway schema per test): snowflake string stays a string & get() agrees with the scan, nested JSON-looking payloads survive batchSet, and set(null)/batchSet(null) both store a jsonb null with exists() still true.

Verification

  • pnpm check and pnpm typecheck clean; pnpm docs at zero warnings.
  • Local run: 8 test files pass (57 tests) including the 3 new regression files, plus the pg-backed round-trip suite against a real DATABASE_URL.
  • The two subprocess/SQLite exit-flush tests (exit-flush, idle-exit) fail only in this sandbox from a known better-sqlite3 native-ABI segfault; they exercise untouched machinery and are authoritative on CI (Node 22/24 ubuntu).

Tier 2 (NEW-1 / NEW-5 cross-process safety) and Tier 3 are deliberately out of scope for this release.

🤖 Generated with Claude Code

…e) => v1.0.1

NEW-13: tsup splitting:true so import 'sql-switch' no longer hoists both
drivers into the entry chunk => a single-engine install stops crashing on
import. Drivers stay lazy, mode-gated chunks (optional-peer-dep invariant
restored). Guarded by test/no-driver-hoist.test.ts on both ESM & CJS output.

NEW-2: pg get() no longer double-parses jsonb (drizzle ran a second
JSON.parse on pg's already-parsed value) => a snowflake-looking string like
"123456789012345678" stops coming back as a precision-lost number. get/set/
delete now use the same raw $n::jsonb pool path as the scans & bulk upsert.

NEW-7: set(null) stores a jsonb null on every path instead of a SQL NULL that
the NOT NULL value column rejected on the forced path.

C1: an un-awaited delete() now executes eagerly at call time (matching set()),
and evicts any buffered set for the key so a later flush can't resurrect it.

No public API changes, no breaking changes => patch release. drizzle-orm is
still used for the SQLite driver & schema builders. Regression tests added:
eager-delete (engine-agnostic), pg-json-roundtrip (pg-only), no-driver-hoist
(dist guard). CHANGELOG updated; version bumped 1.0.0 => 1.0.1 in all 3 spots.
@revxshafi
revxshafi merged commit 15c25a0 into main Aug 22, 2026
8 checks passed
@revxshafi
revxshafi deleted the fix/tier1-ship-blockers branch August 22, 2026 07:14
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.

1 participant