Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 53 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,31 @@ jobs:
- name: Smoke test (SQLite, no DB needed)
run: pnpm run smoke

# Biome lint + format gate. no DB, no build => the fastest signal on a PR. `biome ci` is the
# non-writing CI mode: it fails on any lint error or unformatted file, so a reflow that never ran
# locally can't slip in. mirrors `pnpm run check` (prepublishOnly) so local & CI agree
quality:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v7

- name: Install pnpm
uses: pnpm/action-setup@v6

- name: Setup Node
uses: actions/setup-node@v7
with:
node-version: 22
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Lint + format check (Biome)
run: pnpm exec biome ci .

# TSDoc has to stay warning clean (typedoc.json treatWarningsAsErrors) => catch a broken
# {@link} or an undocumented public symbol here, on the PR, not after it merges & the Pages
# deploy (docs.yml) is the first thing to notice
Expand Down Expand Up @@ -105,9 +130,35 @@ jobs:
- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Integration tests (Vitest postgres files + engine swap, real database)
# coverage is generated here, inside the DATABASE_URL job => this is the only run that
# exercises the pg runtime paths & the down swap, so its numbers are the truest picture. the
# text-summary reporter drops the table straight into the job log. on a fork PR with no
# secret the pg tests self-skip, coverage still writes for the SQLite + logic pass
- name: Integration tests + coverage (Vitest incl. postgres files, real database)
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: |
pnpm run test
pnpm run test:coverage
pnpm run swap-test

# surface the totals on the run summary page too, not just buried in the log. reads the
# json-summary reporter's output => no extra action, no artifact upload (keeps the workflow
# off the deprecated Node 20 upload-artifact). a skipped coverage run just prints a note
- name: Coverage summary to run page
if: always()
run: |
node -e '
const fs = require("fs");
const p = "coverage/coverage-summary.json";
if (!fs.existsSync(p)) { console.log("no coverage summary (run skipped?)"); process.exit(0); }
const t = JSON.parse(fs.readFileSync(p, "utf8")).total;
const row = (k) => `| ${k} | ${t[k].pct}% | ${t[k].covered}/${t[k].total} |`;
const md = [
"## Coverage (src/)",
"",
"| metric | % | covered/total |",
"| --- | --- | --- |",
row("lines"), row("statements"), row("functions"), row("branches"),
].join("\n");
fs.appendFileSync(process.env.GITHUB_STEP_SUMMARY, md + "\n");
'
69 changes: 69 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Security

on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
# weekly, monday 04:17 UTC (odd minute => not on the top-of-hour stampede). the point of the
# cron is a CVE disclosed against an already-pinned dep, which a PR/push run would never re-check
- cron: '17 4 * * 1'

# only keep the newest run per ref alive, cancel stale ones
concurrency:
group: security-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
# dependency advisory scan. fails the run only on high/critical => moderate & low are reported in
# the log but don't gate, so a low-severity transitive advisory can't wedge every PR. audits the
# whole tree (not --prod) on purpose: this repo already treated a dev-only esbuild advisory as
# worth an override, so build tooling counts here too
audit:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v7

- name: Install pnpm
uses: pnpm/action-setup@v6

- name: Setup Node
uses: actions/setup-node@v7
with:
node-version: 22
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: pnpm audit (fail on high or critical)
run: pnpm audit --audit-level high

# CodeQL static analysis over the TS source. no build step needed => the javascript-typescript
# extractor reads source directly, and this is a library (no runnable entrypoint to trace anyway).
# results land in the repo's Security tab / code-scanning alerts
codeql:
runs-on: ubuntu-latest

permissions:
security-events: write
actions: read
contents: read

steps:
- name: Checkout
uses: actions/checkout@v7

- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: javascript-typescript

- name: Analyze
uses: github/codeql-action/analyze@v3
24 changes: 22 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,26 @@ While it's pre-1.0, minor versions may carry breaking changes.

## [Unreleased]

## [0.2.0] - 2026-08-21
## [1.0.0] - 2026-08-21

First stable release. The public API (the fluent chain, `createDAL`/`engineSwap`,
the error classes and config types) is now considered stable under SemVer. No
behavior or API changes from 0.2.0 — this release hardens the quality gates and
marks the surface as settled.

### Added

- Biome as the lint + format gate (`pnpm check` / `check:fix`, `lint`, `format`),
wired into CI as a `quality` job and into `prepublishOnly`.
- v8 code coverage (`pnpm test:coverage`, `@vitest/coverage-v8`) generated in the
Postgres CI job so the cross-engine and down-swap paths are measured.
- Security workflow: `pnpm audit --audit-level high` plus CodeQL on every push/PR
and a weekly cron.

### Changed

- Node engine floor is `>=22` and the CI matrix runs Node 22/24 (18/20 are past
EOL and vitest 4 pulls `styleText` from `node:util`, 20.12+ only).

Correctness, scalability and packaging hardening pass.

Expand Down Expand Up @@ -46,6 +65,7 @@ Initial pre-release of the universal SQLite/PostgreSQL DAL.
crashing, then recovers.
- Bidirectional engine swap => migrate data SQLite files <=> PostgreSQL schemas.

[Unreleased]: https://github.com/creative-softworks/sql-switch/compare/v0.2.0...HEAD
[Unreleased]: https://github.com/creative-softworks/sql-switch/compare/v1.0.0...HEAD
[1.0.0]: https://github.com/creative-softworks/sql-switch/compare/v0.2.0...v1.0.0
[0.2.0]: https://github.com/creative-softworks/sql-switch/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/creative-softworks/sql-switch/releases/tag/v0.1.0
45 changes: 45 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"$schema": "https://biomejs.dev/schemas/2.5.9/schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"files": {
"includes": ["src/**/*.ts", "test/**/*.ts", "scripts/**/*.ts", "*.config.ts"]
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100,
"lineEnding": "lf"
},
"assist": {
"enabled": true,
"actions": {
"source": {
"organizeImports": "off"
}
}
},
"linter": {
"enabled": true,
"rules": {
"preset": "recommended",
"style": {
"noNonNullAssertion": "off"
}
}
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"jsxQuoteStyle": "double",
"semicolons": "always",
"trailingCommas": "all",
"arrowParentheses": "always",
"bracketSpacing": true
}
}
}
15 changes: 12 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sql-switch",
"version": "0.2.0",
"version": "1.0.0",
"description": "Universal hot-swappable DAL — SQLite in dev, PostgreSQL in prod, same fluent API",
"type": "module",
"packageManager": "pnpm@10.26.1",
Expand Down Expand Up @@ -33,9 +33,16 @@
"swap-test": "tsx scripts/swap-test.ts",
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
"typecheck": "tsc --noEmit && tsc -p tsconfig.scripts.json",
"lint": "biome lint",
"lint:fix": "biome lint --write",
"format": "biome format --write",
"format:check": "biome format",
"check": "biome check",
"check:fix": "biome check --write",
"dev": "tsx watch src/database/index.ts",
"prepublishOnly": "pnpm run typecheck && pnpm run test && pnpm run smoke && pnpm run build"
"prepublishOnly": "pnpm run check && pnpm run typecheck && pnpm run test && pnpm run smoke && pnpm run build"
},
"keywords": [
"database",
Expand Down Expand Up @@ -80,16 +87,18 @@
}
},
"devDependencies": {
"@biomejs/biome": "^2.0.0",
"@types/better-sqlite3": "^9.6.0",
"@types/node": "^26.2.0",
"@types/pg": "^8.23.1",
"@vitest/coverage-v8": "^4.1.11",
"better-sqlite3": "^13.0.3",
"drizzle-kit": "^0.31.10",
"pg": "^8.23.0",
"tsup": "^8.5.1",
"tsx": "^4.23.12",
"typedoc": "^0.28.0",
"typescript": "^6.0.3",
"vitest": "^4.1.10"
"vitest": "^4.1.11"
}
}
Loading
Loading