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
37 changes: 35 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ permissions:
contents: read

jobs:
package:
runs-on: ubuntu-latest
package-platform:
name: package (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- name: Checkout repository
uses: actions/checkout@v7
Expand All @@ -24,9 +29,30 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Format check
run: npm run fmt -- --check

- name: Run package gate
run: npm run check

- name: Upload implementation coverage
if: always()
uses: actions/upload-artifact@v7
with:
name: orm-coverage-${{ matrix.os }}
path: coverage
if-no-files-found: warn

package:
needs: package-platform
if: always()
runs-on: ubuntu-latest
steps:
- name: Require every platform package gate
env:
PACKAGE_RESULT: ${{ needs.package-platform.result }}
run: test "$PACKAGE_RESULT" = success

postgres-contract:
name: PostgreSQL ${{ matrix.postgres }}
runs-on: ubuntu-latest
Expand All @@ -49,6 +75,7 @@ jobs:
--health-retries 10
env:
ASKR_ORM_TEST_DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:5432/askr_orm_test
ASKR_ORM_TEST_SHADOW_URL: postgresql://postgres:postgres@127.0.0.1:5432/askr_orm_test_shadow
steps:
- name: Checkout repository
uses: actions/checkout@v7
Expand All @@ -61,5 +88,11 @@ jobs:
- name: Install dependencies
run: npm ci

- name: Create isolated scratch database
run: node tests/prepare-postgres.mjs

- name: Run PostgreSQL integration tests
run: npm run test:integration

- name: Qualify installed PostgreSQL generation with minimum peers
run: npm run test:packed
9 changes: 1 addition & 8 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,13 @@ on:
workflow_dispatch:

jobs:
release-branch:
runs-on: ubuntu-latest
permissions: {}
steps:
- name: Require main release branch
run: test "$GITHUB_REF" = 'refs/heads/main'

ci:
uses: ./.github/workflows/ci.yml
permissions:
contents: read

publish:
needs: [release-branch, ci]
needs: [ci]
uses: askrjs/actions/.github/workflows/publish-package.yml@main
permissions:
contents: write
Expand Down
11 changes: 11 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ Run `npm run check` before declaring a change ready; it includes coverage,
types, packaging, and benchmarks. Run real PostgreSQL and SQLite integration
lanes for adapter-sensitive changes.

## Changelog

Any change to the `version` field in `package.json`, whether a release,
prerelease, or patch bump, must include a matching `## <version>` section in
`CHANGELOG.md` in the same commit or pull request. Date the section and list
breaking changes (with migration notes), deprecations, additions, and fixes.
Move entries from `Unreleased` into the new version section rather than leaving
them there. If the repository has no `CHANGELOG.md` yet, create one (Keep a
Changelog style) at the next version bump. Do not publish or tag a version whose
changelog section is missing.

## Optimization Gate

A benchmark number is only half of an optimization's success criterion. The
Expand Down
63 changes: 63 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Changelog

## Unreleased

## 0.5.0 - 2026-10-12

### Breaking

- Curate the root to 68 names and the tooling subpath to its single runDatabaseCli
bridge. Remove 54 named exports and sql.key. Replace direct client construction
with defineDatabase/open; use sql.identifier/literal/unsafe and defineQuery for
their canonical contracts. Every removed name has migration guidance in
docs/0.5.0-api.md. Retain all five export-map keys and optional PostgreSQL peers.

### Added

- Optional DatabaseAdapter.executeScript for complete migration scripts; custom
drivers without it keep the existing execute fallback. PostgreSQL and SQLite
implementations execute driver-owned scripts without splitting semicolons.
- Exact packed runtime/declaration checks, removed-import and private-path checks,
optional-peer isolation/minimum installs, and real-adapter value parity probes.

### Fixed

- Generate SQLite registered-query fields as native number/string/blob/null
unions instead of PostgreSQL numeric strings and booleans. SQLite affinities
permit mixed storage classes; callers must narrow generated fields before use.
- SQLite migration execution no longer drops all statements after the first.
- PostgreSQL migration scripts no longer fail when the driver returns multiple
result sets.
- Failed SQLite rollback closes and quarantines the adapter, rolls back remaining
writes and releases shared-file ownership before a new connection recovers.
- PostgreSQL failed rollback/advisory unlock discards poisoned pooled connections
while preserving the primary operation error; failed statement deallocation
still releases and discards the owned client.
- Update compatible development tooling to audited versions and add TypeScript 6 compiler-API audits alongside the TypeScript 7 gate and pin
the matching coverage provider.

- Load CLI database entries through file URLs so Windows drive paths and reserved
URL characters in project directories remain filesystem paths.

- Transaction-owned migration plan/apply/resolve handles now reject use after
their transaction ends, before any SQL can run on a released connection.

- Complete PostgreSQL scratch reset with an actual database separation check,
session-owned workflow lock, normalized catalog introspection and protocol-only
query description. Unequal URL strings alone no longer authorize reset.
- Normalize the desired PostgreSQL schema inside rollback-only scratch DDL,
preserving native type/default/expression semantics without comparing codecs
or application property names to database columns.
- Quote enum type/schema names, emit one composite primary key, and defer initial
foreign keys until their referenced tables exist.
- Generate conservative nullable query results and standard PostgreSQL parser
types (int8/numeric strings, timestamp/date Date, unknown unsupported types).
- Gate real and normally installed generation/validation/no-op workflows against
isolated PostgreSQL 16, 17 and 18 scratch databases.

### Candidate verification

- The exact promotion candidate on PR #58 passes the Linux, macOS and Windows
package gates, PostgreSQL 16–18 integration and installed-generation checks,
and CodeQL. Tag creation and npm publication run through the main-branch
package workflow.
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ their scalar value. Writes return status by default. Ordinary promises are the
non-atomic coordination mechanism; use `db.transaction(...)` when operations
must be atomic. Nested transactions use savepoints, and a transaction client
throws after its callback completes. If rollback cleanup itself fails, the
original callback error remains the error surfaced to the caller.
original callback error remains the error surfaced to the caller. Failed
PostgreSQL cleanup discards the pooled connection; failed SQLite rollback
quarantines the adapter and closes its database. If physical close fails, explicit
close retries cleanup. Open a new SQLite adapter before retrying application work.

`escapeLikePattern()` escapes `\\`, `%`, and `_` for literal-text searches.
`like()` and `ilike()` bind the pattern and emit the matching `ESCAPE '\\'`
Expand All @@ -95,6 +98,14 @@ askr database migration plan
askr database migration apply --yes
```

PostgreSQL generated-artifact tooling uses a disposable scratch database and
proves actual separation from the target before reset. It normalizes supported
catalog shapes and describes query results without executing application queries.
See [PostgreSQL tooling](docs/postgres-tooling.md) for reset scope, supported
objects, conservative result types and connection requirements. Native and
installed PostgreSQL 16–18 CI lanes qualify the workflow. Complete coordinated
0.5.0 candidate qualification and maintainer review are still required.

Generation replays checksummed, forward-only SQL against the shadow database
before accepting it. It writes migration SQL plus one committed
`database/generated.ts` artifact containing schema identity, migration
Expand All @@ -109,3 +120,10 @@ SQLite uses Node's synchronous `node:sqlite` API behind a re-entrant async
connection queue. Transactions and streams hold the connection; cancellation
is checked between streamed rows, but a synchronous statement already running
cannot be interrupted.

## 0.5.0 review

The [API decisions](docs/0.5.0-api.md) record every retained and removed name and
its migration. The [hardening report](docs/0.5.0-hardening.md) distinguishes
regression fixes, executed characterization and the remaining coordinated release gates.
The package version remains 0.4.0 until the coordinated candidate is prepared.
13 changes: 11 additions & 2 deletions benchmarks/runtime.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { performance } from "node:perf_hooks";
import { createDatabaseClient, eq, table, text, uuid } from "../dist/index.js";
import { defineDatabase, eq, table, text, uuid } from "../dist/index.js";

const gate = process.argv.includes("--gate");
const iterations = 20_000;
Expand All @@ -23,7 +23,16 @@ const users = table("users", {
email: text().notNull(),
});
const adapter = new BenchmarkAdapter();
const db = createDatabaseClient({ users }, adapter);
const db = await defineDatabase({
tables: { users },
driver: {
dialect: "postgres",
open: async () => adapter,
shadow: async () => {
throw new Error("Benchmark does not use tooling.");
},
},
}).open();

async function measure(callback) {
const started = performance.now();
Expand Down
Loading
Loading