From ffdbaa345bd7753ce56a1177b92e7f092cbe5d4e Mon Sep 17 00:00:00 2001 From: Rev Albrecht von Nullpointer <160512015+revxshafi@users.noreply.github.com> Date: Sat, 22 Aug 2026 08:37:13 +0000 Subject: [PATCH] docs: redesign README with SVG hero + architecture diagram MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restructure README example-first (quick start leads, Postgres switch as proof) and add a visual system: a pure-SVG hero (name, tagline, engine-swap motif) and a how-it-works architecture diagram, both self-backgrounded for light/dark GitHub themes. Add real npm/types/node/license badges. Content tables (reliability, migration guarantees, API, limits) preserved, repeated prose trimmed. ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) --- README.md | 119 ++++++++++++++++++++------------- assets/readme/architecture.svg | 75 +++++++++++++++++++++ assets/readme/hero.svg | 73 ++++++++++++++++++++ 3 files changed, 221 insertions(+), 46 deletions(-) create mode 100644 assets/readme/architecture.svg create mode 100644 assets/readme/hero.svg diff --git a/README.md b/README.md index 64a8064..7251fa9 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,27 @@ -# sql-switch +

+ sql-switch โ€” one fluent API for your data: SQLite in dev, PostgreSQL in prod, with a one-command engine swap between the two +

-Universal hot-swappable database abstraction layer for Node.js. Run SQLite locally, PostgreSQL in production โ€” the same fluent API covers both. Migrate your data between engines with one CLI command, or one function call. +

+ npm version + TypeScript types included + supported Node.js versions + MIT license +

-## Install +

+ One fluent API for your data. Run SQLite while you build, PostgreSQL in production โ€” +
the same call works on both โ€” then migrate between engines with a single command. +

-```bash -npm install sql-switch -``` - -Also published under the Creative-Softworks scope if you prefer the branded name โ€” it re-exports -this package unchanged, so pick whichever reads better to you: +--- -```bash -npm install @creative-softworks/sql-switch -``` - -The database drivers are **optional peer dependencies** โ€” install only the one for the engine you -run, so a SQLite-only app never pulls in `pg` and its build, and vice versa: +## Quick start ```bash -npm install better-sqlite3 # local mode -npm install pg # cloud mode +npm install sql-switch better-sqlite3 ``` -Each driver is loaded lazily the first time you `connect()` in that mode, so importing the package -never requires both engines to be present. - -## Quick start - ```ts import { createDAL } from 'sql-switch'; @@ -38,17 +32,21 @@ await db.connect({ collector: { enabled: true, time: 3000 }, }); -// read -const settings = await db.schema('antinuke').table('settings').key('guild_123').get(); - -// write (queued, flushed every 3s in bulk) +// write โ€” queued in RAM, flushed in bulk every 3s await db.schema('antinuke').table('settings').key('guild_123').set({ strict: true }); -// write immediately, bypassing the collector +// read โ€” a queued write is visible to the next get() on the same key +const settings = await db.schema('antinuke').table('settings').key('guild_123').get(); + +// need it on disk right now? bypass the collector await db.schema('antinuke').table('settings').key('guild_123').set({ strict: true }).force(); ``` -## Switch to PostgreSQL +That is the whole surface: `schema โ†’ table โ†’ key โ†’ operation`. The same chain drives every engine. + +## Same code, production engine + +Nothing above changes when you go to PostgreSQL โ€” only the `connect()` config does: ```ts await db.connect({ @@ -61,9 +59,38 @@ await db.connect({ }); ``` -`statementTimeout` (default 30s, `0` disables) is the ceiling on a single operation. Without one a -query that never answers holds a pool connection for the life of the process โ€” `max` of those and -every later read blocks with no error at all. +In local mode each schema is its own `.db` file (`./data/databases/antinuke.db`, WAL on by default); +in cloud mode each schema is a Postgres logical schema (`antinuke.settings`). Your code never sees +the difference. + +> `statementTimeout` (default 30s, `0` disables) caps a single operation. Without one, a query that +> never answers holds a pool connection for the life of the process โ€” `max` of those and every later +> read blocks with no error at all. + +The drivers are **optional peer dependencies**, loaded lazily the first time you `connect()` in that +mode โ€” a SQLite-only app never pulls in `pg`, and vice versa. Install just the one you run: + +```bash +npm install better-sqlite3 # local mode +npm install pg # cloud mode +``` + +Prefer the branded name? `@creative-softworks/sql-switch` re-exports this package unchanged. + +## How it works + +

+ Fluent-API calls pass through a write collector into a lazily loaded, mode-gated driver targeting either local SQLite files or PostgreSQL schemas; engineSwap migrates data between the two, chunked and resumable +

+ +- **Write collector** โ€” buffers writes in RAM and flushes them in bulk on an interval, collapsing + repeated writes to the same key inside the window. `.force()` bypasses it for an immediate write. +- **Circuit breaker** โ€” caps pending writes at 5000 keys and trips to read-only on a Postgres + outage instead of crashing, then heals itself once the database answers again. +- **Exit flush** โ€” `SIGINT`, `SIGTERM` and `beforeExit` all drain the buffer on the way out; the + library never calls `process.exit()` for you. +- **Engine swap** โ€” moves data both directions, a chunk at a time, journalled so an interrupted run + resumes deterministically. ## Enumerate, scan & convenience helpers @@ -130,16 +157,13 @@ await db.connect({ ## Engine swap -Move your data between engines either from the terminal or from code. +Move your data between engines from the terminal or from code. ### CLI ```bash -# local SQLite โ†’ production PostgreSQL -npm run db:engine-swap -- --up - -# production PostgreSQL โ†’ local SQLite -npm run db:engine-swap -- --down +npm run db:engine-swap -- --up # local SQLite โ†’ production PostgreSQL +npm run db:engine-swap -- --down # production PostgreSQL โ†’ local SQLite ``` | Flag | Description | @@ -152,9 +176,8 @@ npm run db:engine-swap -- --down ### From code -Same migration, no terminal. Anything you leave out is filled in โ€” `dataDir` defaults to -`./data/databases`, `connectionString` to `process.env.DATABASE_URL`, and missing schemas, -tables and directories are created on the target side. +Anything you leave out is filled in โ€” `dataDir` defaults to `./data/databases`, `connectionString` +to `process.env.DATABASE_URL`, and missing schemas, tables and directories are created on the target. ```ts import { engineSwap } from 'sql-switch'; @@ -168,9 +191,8 @@ const result = await engineSwap({ console.log(`${result.totalRows} rows across ${result.tables.length} tables`); ``` -Or swap a live DAL and keep using the same object. Pending writes are flushed and the open -handles closed first, then it reconnects on the target engine with your existing collector -settings: +Or swap a live DAL and keep using the same object โ€” pending writes are flushed and handles closed +first, then it reconnects on the target engine with your existing collector settings: ```ts await db.swapEngine({ direction: 'up', onConflict: 'overwrite' }); @@ -190,7 +212,7 @@ await engineSwap({ ### What the migration guarantees -| | | +| Guarantee | Detail | |---|---| | Memory | Rows stream a chunk at a time in both directions โ€” peak memory is one chunk, not one table. | | Atomicity | Each table moves in its own transaction going up; going down the file is built as `.tmp` and renamed into place. | @@ -232,8 +254,8 @@ if (result.skippedNames.length) console.warn('left alone:', result.skippedNames) ## Limits -The library is built so the only hard wall you hit is the database running out of storage. The few -non-storage constraints below are deliberate, so they're documented rather than left as surprises. +The only hard wall you hit is the database running out of storage. The few non-storage constraints +below are deliberate, so they're documented rather than left as surprises. | Limit | Detail | |-------|--------| @@ -258,3 +280,8 @@ npm run docs:serve # serve /docs on http://localhost:3000 ## Requirements - Node.js >= 22.0.0 (tested on the active LTS / current lines, 22 and 24) + +## License + +[MIT](./LICENSE) + diff --git a/assets/readme/architecture.svg b/assets/readme/architecture.svg new file mode 100644 index 0000000..08a188a --- /dev/null +++ b/assets/readme/architecture.svg @@ -0,0 +1,75 @@ + + How sql-switch works + Your fluent-API calls go through a write collector into a lazily loaded, mode-gated driver that targets either local SQLite files or PostgreSQL schemas. engineSwap migrates data between the two, chunked and resumable. + + + + + + + + + + + + + + + + + YOUR CODE + db.schema('antinuke') + .table('settings') + .key('guild_123') + .set({ strict: true }) + + + + + WRITE COLLECTOR + buffers writes in RAM + bulk-flush every 3000 ms + collapses repeats ยท .force() = now + circuit breaker trips at 5000 + + + + DRIVER + lazy ยท mode-gated + + + SQLiteโ†’ ./data/databases/*.db + + + PostgreSQLโ†’ logical schemas + + + + + + + + + + + + engineSwap()โ€” migrate data both directions + + + SQLite files + + + + + + + + + + PostgreSQL schemas + + chunked streaming ยท per-table transaction ยท journalled & resumable + + diff --git a/assets/readme/hero.svg b/assets/readme/hero.svg new file mode 100644 index 0000000..f10b1cd --- /dev/null +++ b/assets/readme/hero.svg @@ -0,0 +1,73 @@ + + sql-switch + One fluent API for your data โ€” SQLite in dev, PostgreSQL in prod, with a one-command engine swap between them. + + + + + + + + + + + + + + + + UNIVERSAL ยท HOT-SWAPPABLE ยท ZERO LOCK-IN + + sql-switch + + One fluent API. SQLite in dev, + PostgreSQL in prod, one-command swap. + + + npm 1.0.1 + ยท + MIT + ยท + node โ‰ฅ 22 + ยท + TypeScript + + + + + + + + .key(id).set(v) + + + + + + + + + SQLite + local .db file + mode: 'local' + + + + + PostgreSQL + cloud schema + mode: 'cloud' + + + + + + + + + swapEngine() + +