@onreza/sqlx-js 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -24,12 +24,13 @@ const rows = await sql(
24
24
  - **Precise nullability inference** through `libpg-query`: `JOIN` direction (LEFT/RIGHT/FULL), inner `JOIN ... ON` predicates, DML `RETURNING`, `COALESCE`, `CASE`, `COUNT`, expression propagation. Parameters become `T | null` when wrapped in `COALESCE`/`NULLIF`/`IS [NOT] NULL`/`IS [NOT] DISTINCT FROM`, or when bound to a nullable column in `INSERT`/`UPDATE`.
25
25
  - **WHERE narrowing**: `IS NOT NULL`, equality chains, `IN`, `LIKE`, `BETWEEN` make columns non-null. Tracks `AND`/`OR` semantics.
26
26
  - **PostgreSQL enums** generated as TypeScript literal unions (read + write side).
27
- - **Schema-aware `jsonb`** via a `SqlxJsJson` global namespace and a config-driven column → type mapping. Works for both result columns and `INSERT`/`UPDATE`/`WHERE` parameters. Unmapped `json`/`jsonb` falls back to `JsonValue` for rows and `JsonInput` for parameters instead of `unknown`.
27
+ - **Schema-aware `jsonb`** via a `SqlxJsJson` global namespace and a config-driven column → type mapping. Works for both result columns and `INSERT`/`UPDATE`/`WHERE` parameters. Unmapped `json`/`jsonb` falls back to `JsonValue` for rows and an explicitly wrapped, structurally checked JSON parameter.
28
28
  - **Extension types out of the box**: `pgvector` (`vector`, `halfvec`, `sparsevec`), `hstore`, `citext`, `ltree`/`lquery`/`ltxtquery`. Add your own through `customTypes` config.
29
29
  - **Domains** resolve to their base TypeScript type (`CREATE DOMAIN email AS text` → `string`), including domains over extension types or other domains.
30
30
  - **Wide built-in type coverage**: numeric, text, date/time, UUID, json/jsonb, network (inet/cidr/macaddr/macaddr8), bit strings, ranges/multiranges, geometric, money, tsvector/tsquery, xml — and the matching array variants.
31
31
  - **External SQL files** via `sql.file("queries/foo.sql", ...)` — prepared and typed through `KnownFileQueries`. Watch mode re-prepares on `.sql` edits too.
32
32
  - **One-row helpers**: `sql.one(...)`, `sql.optional(...)`, `sql.file.one(...)`, `sql.file.optional(...)`, and the same chain on the `tx` callback — friendly with `noUncheckedIndexedAccess: true`. The scanner walks all of them.
33
+ - **Reusable query definitions** via `defineQuery`: declare one typed SQL contract and execute it through either a root client or transaction-scoped executor. `QueryParams`, `QueryRow`, and `QueryResult` expose its generated types without indexing a registry by SQL text.
33
34
  - **Unambiguous JSON and PostgreSQL array params** through `sql.json(...)` and `sql.array(...)`. Primitive JSON arrays cannot be silently encoded as PostgreSQL array literals.
34
35
  - **Typed transactions** via `sql.transaction(async tx => …)` — the `tx` callback parameter is recognized by the scanner, so queries inside the block keep full type checking.
35
36
  - **Sourcemap-accurate error reporting**: every prepare failure points to `file:line:column` of the originating `sql(...)` call site, with PG error code, position, and hint.
@@ -39,7 +40,7 @@ const rows = await sql(
39
40
  - **Optional pgschema workflow** via `init --schema-provider pgschema` and `sqlx-js db install|check|plan|apply` for PostgreSQL schema-as-code projects.
40
41
  - **Versioned offline cache** committed to your repo. `prepare --check` validates fingerprints, generator revision, and type-affecting config without a database; `prepare --verify` compares fresh live/shadow artifacts without writing.
41
42
  - **Schema snapshot + LLM manifest** via `schema dump` / `schema check`: tables, columns, constraints, indexes, types, and function/procedure metadata are introspected from PostgreSQL.
42
- - **Generated function catalog** via `KnownFunctions`: `prepare` records user-schema PostgreSQL functions/procedures from `pg_proc` with approximate parameter and return TypeScript types.
43
+ - **Generated function catalog** via `KnownFunctions`: `prepare` records application-owned PostgreSQL functions/procedures from `pg_proc` with approximate parameter and return TypeScript types while excluding extension-owned internals by default.
43
44
  - **Shadow database validation** via `migrate dev` / `migrate verify`: auto-create a disposable shadow DB, apply migrations, validate SQL, and drop it afterwards.
44
45
  - **Safe identifier quoting** via `sql.id(...)`, backed by the committed schema snapshot whitelist.
45
46
  - **Single runtime adapter**: Postgres.js backs the runtime on Node/Bun-compatible environments — no Bun.SQL-specific adapter to choose.
@@ -48,16 +49,19 @@ const rows = await sql(
48
49
  - **Environment doctor** checks runtime versions, config loading, `.env`, database connectivity/permissions, cache metadata, tsconfig inclusion, and pgschema availability.
49
50
  - **Strict inference gate** promotes degraded nullability analysis and generated `unknown` query types to CI errors.
50
51
  - **GitHub/editor diagnostics adapter** converts versioned prepare JSON into workflow annotations or Unix problem-matcher output.
52
+ - **Versioned query inventory** via `queries --json`, including stable query IDs, definition names, cardinality, call sites, SQL files, and cache state. The same command can emit a deterministic embedded-SQL module for bundled applications.
51
53
 
52
54
  ## Install
53
55
 
54
56
  ```bash
55
57
  npm install @onreza/sqlx-js
58
+ npm install --save-dev typescript
56
59
  # or
57
60
  bun add @onreza/sqlx-js
61
+ bun add --dev typescript
58
62
  ```
59
63
 
60
- Node.js 24 or newer is required. Bun users need Bun 1.3 or newer.
64
+ Node.js 24 or newer is required. Bun users need Bun 1.3 or newer. TypeScript is an optional peer so production-only installs do not pull the compiler and its platform package into the application image; source scanning commands (`prepare`, `queries`, `doctor`, `ci`, and migration development/verification) require it in development dependencies.
61
65
 
62
66
  The package installs `sqlx-js` and `sqlx-js-diagnostics` binaries. The CLI examples below use `npx @onreza/sqlx-js`; `bunx @onreza/sqlx-js ...` works the same if your project uses Bun.
63
67
 
@@ -112,6 +116,22 @@ CREATE TABLE users (
112
116
  );
113
117
  ```
114
118
 
119
+ For queries with several values, named parameters keep the SQL and arguments aligned:
120
+
121
+ ```ts
122
+ const rows = await sql(
123
+ `SELECT id, name
124
+ FROM users
125
+ WHERE email = $email OR recovery_email = $email
126
+ LIMIT $limit`,
127
+ { email: "user@example.com", limit: 10 },
128
+ );
129
+ ```
130
+
131
+ Named parameters use ASCII identifier names (`$user_id`), are numbered by first appearance before PostgreSQL sees the query, and repeated names reuse the same positional parameter. The generated object contract rejects missing, extra, and incorrectly typed properties. Named `$name` and positional `$1` parameters cannot be mixed in one query. Quoted strings, comments, dollar-quoted bodies, and `$` inside PostgreSQL identifiers are left unchanged. Positional parameters remain supported and are still the shortest form for simple queries.
132
+
133
+ PostgreSQL describes an uncast `LIMIT $limit` or `OFFSET $offset` parameter as `int8`, so its TypeScript type is `bigint`. Use `LIMIT $limit::int` / `OFFSET $offset::int` when the application pagination API uses JavaScript `number` values.
134
+
115
135
  During local development, validate the migration and regenerate query artifacts against a disposable shadow database:
116
136
 
117
137
  ```bash
@@ -169,6 +189,39 @@ const rows = await sql(`SELECT id FROM users WHERE name = $1`, "alice");
169
189
 
170
190
  Unknown queries, wrong parameter types, and dynamic strings are compile errors. For genuinely dynamic SQL, use `unsafe`.
171
191
 
192
+ ### `defineQuery`
193
+
194
+ Define a query once without closing over a global client, then run the same generated contract through a root or transaction executor:
195
+
196
+ ```ts
197
+ import {
198
+ defineQuery,
199
+ sql,
200
+ type QueryParams,
201
+ type QueryResult,
202
+ type QueryRow,
203
+ type SqlExecutor,
204
+ } from "@onreza/sqlx-js";
205
+
206
+ export const findUser = defineQuery.optional(
207
+ "users.findById",
208
+ `SELECT id, email FROM users WHERE id = $id`,
209
+ );
210
+
211
+ type FindUserParams = QueryParams<typeof findUser>;
212
+ type FindUserRow = QueryRow<typeof findUser>;
213
+ type FindUserResult = QueryResult<typeof findUser>; // FindUserRow | null
214
+
215
+ await findUser.run(sql, { id: userId });
216
+ await sql.transaction((tx) => findUser.run(tx, { id: userId }));
217
+
218
+ async function loadUser(executor: SqlExecutor, params: FindUserParams) {
219
+ return findUser.run(executor, params);
220
+ }
221
+ ```
222
+
223
+ The optional definition name is included in query observer and inventory metadata. The stable `queryId` is derived from the same lexical SQL fingerprint used by prepare/cache. `defineQuery.one`, `.optional`, and `.execute` mirror the cardinality contracts of the corresponding `sql` helpers.
224
+
172
225
  ### `sql.file(path, ...params)`
173
226
 
174
227
  Load SQL from an external file. The path is root-relative everywhere: prepare resolves it against `--root`, codegen keeps the exact string literal as the `KnownFileQueries` key, and runtime resolves it against `fileRoot` (default: `process.cwd()`). Absolute paths and paths escaping the root are rejected.
@@ -186,6 +239,20 @@ const admins = await sql.file("queries/top_admins.sql", "admin", 5);
186
239
 
187
240
  File-backed queries are emitted into a separate `KnownFileQueries` interface. A call from any nested source directory still uses the same project-root-relative literal.
188
241
 
242
+ For a compiled or bundled application, emit a TypeScript asset module and pass it to the client:
243
+
244
+ ```bash
245
+ sqlx-js queries --embed src/sqlx-js-files.generated.ts
246
+ ```
247
+
248
+ ```ts
249
+ import { sqlxJsEmbeddedSql } from "./sqlx-js-files.generated";
250
+
251
+ const db = createSqlClient(databaseUrl, { sqlFiles: sqlxJsEmbeddedSql });
252
+ ```
253
+
254
+ Embedded entries take precedence over filesystem reads. The module contains only referenced external SQL files; inline SQL remains in application code.
255
+
189
256
  ### `sql.one(query, ...params)` and `sql.optional(query, ...params)`
190
257
 
191
258
  Convenience wrappers for single-row queries. `one` throws if the row count is not exactly 1; `optional` returns `null` for 0 rows and throws on more than 1. They keep working under `noUncheckedIndexedAccess: true` without `rows[0]!` patterns.
@@ -239,6 +306,8 @@ await sql(
239
306
 
240
307
  Generated parameter types require `PgArrayParameter<T>` or `JsonParameter<T>`, so mixing the two representations is a TypeScript error. A PostgreSQL `json[]` / `jsonb[]` composes both wrappers: the outer `sql.array(...)` selects the PostgreSQL array representation and each non-SQL-NULL element uses `sql.json(...)`. `sql.json(null)` represents JSON `null`; a bare `null` remains SQL `NULL` when the database parameter is nullable.
241
308
 
309
+ `sql.json()` accepts ordinary structurally JSON-compatible interfaces and preserves their concrete type in `JsonParameter<T>`. It rejects known non-JSON values such as `Date`, `bigint`, functions, and `undefined` array elements. TypeScript is structurally typed, so it cannot identify every user-defined class solely because it was constructed with `new`; runtime JSON semantics still belong to `JSON.stringify`.
310
+
242
311
  Both helpers also work with `unsafe(...)`. `encodePgArrayLiteral(arr)` remains exported for code that explicitly needs a PostgreSQL array literal string.
243
312
 
244
313
  ### Parameter nullability
@@ -341,6 +410,8 @@ await Promise.all([primary.close(), replica.close()]);
341
410
 
342
411
  Each generated `sqlx-js-env.d.ts` exports its own `SqlxJsGeneratedRegistry`. Passing it to `createSqlClient<...>()` keeps a scoped client on that project's query contract even when a monorepo TypeScript program includes declarations for several databases. The global `sql` export remains available for the single-client convenience path.
343
412
 
413
+ When a workspace package exports database source to other TypeScript programs, bind `SqlxJsGeneratedRegistry` at that package's client boundary. A consumer does not automatically include the database package's ambient `.d.ts`; exporting an unscoped client can therefore collapse its literal parameters to `never` outside the package.
414
+
344
415
  The scanner recognizes clients assigned directly from an imported `createSqlClient(...)` (including aliased and namespace imports), so `client.sql(...)`, its cardinality helpers, file queries, and transactions participate in `prepare` exactly like the global `sql` surface.
345
416
 
346
417
  `createClient(url, options)` accepts every Postgres.js option plus sqlx-js runtime options:
@@ -355,19 +426,22 @@ setClient(createClient(process.env.DATABASE_URL, {
355
426
  // Development-only: re-stat sql.file() files on every call. The default
356
427
  // immutable cache avoids synchronous filesystem work in the query hot path.
357
428
  reloadSqlFiles: true,
429
+ // Optional generated map from `sqlx-js queries --embed ...`. When present,
430
+ // sql.file() needs no runtime filesystem asset for those paths.
431
+ sqlFiles: sqlxJsEmbeddedSql,
358
432
  // Honored for every unsafe call. Set false for PgBouncer transaction mode
359
433
  // unless protocol-level prepared statements are configured there.
360
434
  prepare: false,
361
435
  // Fires after every query/transaction statement, success or failure.
362
- onQuery: ({ query, params, durationMs, rowCount, error }) => {
363
- if (error) logger.error({ query, error }); // database errors are PgError
364
- else if (durationMs > 200) logger.warn({ slow: query, durationMs, rowCount });
436
+ onQuery: ({ queryId, queryName, query, params, durationMs, rowCount, error }) => {
437
+ if (error) logger.error({ queryId, queryName, query, error });
438
+ else if (durationMs > 200) logger.warn({ queryId, queryName, durationMs, rowCount });
365
439
  },
366
440
  onQueryHookError: (error) => logger.error({ error }, "query observer failed"),
367
441
  }));
368
442
  ```
369
443
 
370
- The `onQuery` hook is the integration point for metrics, tracing, and slow-query logging — sqlx-js does not log queries itself. It is a non-blocking observer: synchronous throws and asynchronous rejections preserve the database result/error and are passed to `onQueryHookError` when configured. The event carries the raw `params`, which may contain personal or sensitive data — don't log them blindly; redact or omit `params` in shared sinks. Database errors are normalized to `PgError`; transport and non-database errors pass through unchanged.
444
+ The `onQuery` hook is the integration point for metrics, tracing, and slow-query logging — sqlx-js does not log queries itself. `queryId` is the stable prepare/cache fingerprint and is suitable for metric labels; `queryName` is present for named `defineQuery` calls. The hook is a non-blocking observer: synchronous throws and asynchronous rejections preserve the database result/error and are passed to `onQueryHookError` when configured. The event carries the raw `params`, which may contain personal or sensitive data — don't log them blindly; redact or omit `params` in shared sinks. Database errors are normalized to `PgError`; transport and non-database errors pass through unchanged.
371
445
 
372
446
  ### `clearSqlFileCache()`
373
447
 
@@ -376,19 +450,19 @@ Drops the in-memory cache used by `sql.file(...)`. Files are immutable after the
376
450
  ### Typed errors
377
451
 
378
452
  ```ts
379
- import { NoRowsError, TooManyRowsError, PgError } from "@onreza/sqlx-js";
453
+ import { NoRowsError, TooManyRowsError, SQLSTATE, isPgError } from "@onreza/sqlx-js";
380
454
 
381
455
  try {
382
456
  const u = await sql.one(`SELECT id FROM users WHERE id = $1`, 99);
383
457
  } catch (e) {
384
458
  if (e instanceof NoRowsError) return null;
385
459
  if (e instanceof TooManyRowsError) console.error("ambiguous query, got", e.actual);
386
- if (e instanceof PgError) console.error("pg code:", e.code, "position:", e.position);
460
+ if (isPgError(e, SQLSTATE.uniqueViolation)) console.error("duplicate:", e.constraint);
387
461
  throw e;
388
462
  }
389
463
  ```
390
464
 
391
- `sql.one` throws `NoRowsError` on 0 rows and `TooManyRowsError` (with `.actual`) on >1. Any database error raised by the default runtime is normalized into a `PgError`, so `e instanceof PgError` works the same in `prepare`, `migrate`, and ordinary `sql(...)` calls. `PgError` exposes `.code`, `.position`, `.hint`, `.detail`, `.severity`, `.schema`, `.table`, `.column`, `.constraint`, and the original driver error on `.cause`. Non-database failures (e.g. a dropped connection) are rethrown unchanged.
465
+ `sql.one` throws `NoRowsError` on 0 rows and `TooManyRowsError` (with `.actual`) on >1. Any database error raised by the default runtime is normalized into a `PgError`; `isPgError(error, code?)` is the concise type guard for SQLSTATE handling. `SQLSTATE` contains the common unique, foreign-key, not-null, check, serialization, and deadlock codes. `PgError` exposes `.code`, `.position`, `.hint`, `.detail`, `.severity`, `.schema`, `.table`, `.column`, `.constraint`, and the original driver error on `.cause`. A zero-row update/delete is not a PostgreSQL error: use `sql.execute(...).rowCount`, `sql.optional`, or `sql.one` for that invariant. Non-database failures are rethrown unchanged.
392
466
 
393
467
  ### Transactions with options
394
468
 
@@ -411,10 +485,12 @@ In addition to `import { sql } from "@onreza/sqlx-js"`, the scanner recognises `
411
485
  ```
412
486
  sqlx-js init [--root <dir>] [--schema-provider builtin|pgschema]
413
487
  sqlx-js doctor [--root <dir>] [--dts <path>] [--json]
488
+ sqlx-js ci [--root <dir>] [--dts <path>] [--schema <path>] [--json] [--shadow-url <url>] [--shadow-admin-url <url>]
414
489
  sqlx-js db install | check [--root <dir>]
415
490
  sqlx-js db plan | apply [--root <dir>] [-- <pgschema args>]
416
491
  sqlx-js prepare [--check | --offline | --verify | --watch] [--json | --jsonl] [--strict-inference] [--root <dir>] [--dts <path>] [--no-prune] [--shadow-url <url>]
417
- sqlx-js migrate dev [--shadow-admin-url <url> | --shadow-url <url>] [--lock-timeout <ms>] [--strict-inference] | verify [--shadow-admin-url <url> | --shadow-url <url>] [--lock-timeout <ms>] [--strict-inference] | run [--dry-run] [--json] [--lock-timeout <ms>] | info [--json] | check [--json] | revert [--dry-run] [--json] [--shadow-admin-url <url> | --shadow-url <url>] [--lock-timeout <ms>] | add <name> | squash <name> [--shadow-admin-url <url> | --shadow-url <url>] [--replace] [--pg-dump <path>] [--lock-timeout <ms>] | archive list | archive restore <name> [--force]
492
+ sqlx-js queries [--json] [--embed <path>] [--root <dir>]
493
+ sqlx-js migrate dev [--dts <path>] [--shadow-admin-url <url> | --shadow-url <url>] [--lock-timeout <ms>] [--strict-inference] | verify [--dts <path>] [--shadow-admin-url <url> | --shadow-url <url>] [--lock-timeout <ms>] [--strict-inference] | run [--dry-run] [--json] [--lock-timeout <ms>] | info [--json] | check [--json] | revert [--dry-run] [--json] [--shadow-admin-url <url> | --shadow-url <url>] [--lock-timeout <ms>] | add <name> | squash <name> [--shadow-admin-url <url> | --shadow-url <url>] [--replace] [--pg-dump <path>] [--lock-timeout <ms>] | archive list | archive restore <name> [--force]
418
494
  sqlx-js schema dump [--schema <path>] [--manifest <path>] [--no-manifest] [--shadow-url <url>]
419
495
  sqlx-js schema check [--schema <path>] [--shadow-url <url>]
420
496
  sqlx-js --version | --help
@@ -434,6 +510,7 @@ Regular `prepare` describes queries across a small connection pool (default 8, o
434
510
  | `--migrations <dir>` | Root-relative migrations directory (default: `<root>/migrations`). |
435
511
  | `--dry-run` | For `migrate run` / `migrate revert`: validate without applying to the target DB. |
436
512
  | `--json` | Machine-readable prepare diagnostics, doctor output, migration inspection and dry-runs. |
513
+ | `--embed <path>` | For `queries`: write a deterministic TypeScript map of referenced external SQL files. |
437
514
  | `--jsonl` | Versioned streaming events for `prepare --watch`. |
438
515
  | `--strict-inference` | Fail prepare/dev/verify when nullability degrades or a generated query type contains `unknown`. |
439
516
  | `--force` | For `migrate archive restore`: allow overwriting existing migration files. |
@@ -449,7 +526,9 @@ Regular `prepare` describes queries across a small connection pool (default 8, o
449
526
 
450
527
  Flags that take a value accept both `--flag value` and `--flag=value` forms.
451
528
 
452
- Prepare and doctor JSON use `formatVersion: 1`. Prepare diagnostics include a stable phase plus root-relative file, 1-based line/column, PostgreSQL code/position/hint when available, and the query text. Degraded inference and generated `unknown` types appear as warnings by default; `--strict-inference` promotes them to errors. This is intended for CI annotations and editor integrations; stdout contains one JSON document and human progress is suppressed. `prepare --watch --jsonl` emits one `start`, `diagnostic`, `prepared`, `error`, `watching`, or `stopping` event per line so an editor can consume diagnostics without waiting for the watch process to exit. Fatal `error` events include the same structured `diagnostic` object as CLI preflight failures, preserving the prepare phase and source location when available.
529
+ Prepare and doctor JSON use `formatVersion: 1`. Prepare diagnostics include a stable phase plus root-relative file, 1-based line/column, query ID/name, PostgreSQL code/position/hint when available, and the query text. Degraded inference and generated `unknown` types appear as warnings by default; `--strict-inference` promotes them to errors. This is intended for CI annotations and editor integrations; stdout contains one JSON document and human progress is suppressed. `prepare --watch --jsonl` emits one `start`, `diagnostic`, `prepared`, `error`, `watching`, or `stopping` event per line so an editor can consume diagnostics without waiting for the watch process to exit. Fatal `error` events include the same structured `diagnostic` object as CLI preflight failures, preserving the prepare phase and source location when available.
530
+
531
+ `queries --json` is database-free and read-only. It emits `formatVersion: 1` inventory entries with `queryId`, optional definition names, cardinalities, root-relative call sites, SQL file paths, and `current`/`stale`/`missing` cache status, plus orphaned cache IDs. Config, scan, cache, and embed failures use versioned structured diagnostics with source location when available. Adding `--embed` writes the external-SQL module only after a successful scan.
453
532
 
454
533
  `DATABASE_URL` must be set for any command that touches the application database or auto-creates a shadow database. `SHADOW_ADMIN_DATABASE_URL` can point at a maintenance/admin database when the application user cannot `CREATE DATABASE`; `SHADOW_DATABASE_URL` can point at a pre-created disposable shadow database. The internal wire client understands `sslmode`, `sslrootcert`, `sslcert`, `sslkey`, `application_name`, `options` (PostgreSQL startup options such as `-c search_path=app,public`), `connect_timeout` (seconds), and `statement_timeout` (milliseconds). Unqualified relations are resolved using the prepare session's real `search_path`; they are not assumed to live in `public`.
455
534
 
@@ -602,6 +681,14 @@ export default defineConfig({
602
681
  "posts.meta": "SqlxJsJson.PostMeta",
603
682
  "posts.attachments": "SqlxJsJson.Attachment",
604
683
  },
684
+ // Explicit application-owned assertions for direct scalar columns only.
685
+ columnTypes: {
686
+ "analytics_event.action": "AnalyticsAction",
687
+ },
688
+ functionCatalog: {
689
+ // Extension-owned functions are excluded by default.
690
+ includeExtensionOwned: false,
691
+ },
605
692
  });
606
693
  ```
607
694
 
@@ -627,7 +714,17 @@ declare global {
627
714
  export {};
628
715
  ```
629
716
 
630
- After re-running `prepare`, every `jsonb` column or parameter declared in `jsonbTypes` is checked against the corresponding TypeScript type. Columns without a custom mapping use `JsonValue` for result rows and `JsonInput` inside `JsonParameter` for parameters, both exported by `@onreza/sqlx-js`. Pass JSON parameters through `sql.json(value)`: non-JSON inputs such as `Date`, functions, and `bigint` are rejected by TypeScript while plain JSON objects, arrays, strings, numbers, booleans, and nested JSON `null` values are accepted. A bare top-level `null` remains SQL `NULL` and is allowed only when the mapped database parameter is nullable; use `sql.json(null)` for JSON `null`.
717
+ After re-running `prepare`, every `jsonb` column or parameter declared in `jsonbTypes` is checked against the corresponding TypeScript type. Columns without a custom mapping use `JsonValue` for result rows and `JsonParameter<unknown>` for parameters: the existential parameter type accepts any wrapper already proven JSON-safe by `sql.json(value)` without requiring domain interfaces to declare a string index signature. Non-JSON inputs such as `Date`, functions, and `bigint` are rejected by TypeScript while plain JSON objects, arrays, strings, numbers, booleans, and nested JSON `null` values are accepted. A bare top-level `null` remains SQL `NULL` and is allowed only when the mapped database parameter is nullable; use `sql.json(null)` for JSON `null`.
718
+
719
+ ### Direct scalar `columnTypes`
720
+
721
+ `columnTypes` is an explicit application-owned type assertion for a direct scalar table column. It affects result fields that PostgreSQL attributes to that exact column and parameters that sqlx-js maps back to it through `INSERT`, `UPDATE`, `WHERE`, or `JOIN` analysis. It never changes arbitrary expressions such as `upper(action)`, and it does not apply to PostgreSQL/JSON array columns. Use a schema-qualified key when table names can collide. Mapping the same logical column through both `jsonbTypes` and `columnTypes` is rejected.
722
+
723
+ This assertion does not validate stored values at runtime. Prefer a PostgreSQL enum/domain when the database truly owns a closed value set; use `columnTypes` when the database deliberately stores a broader scalar such as `text` and the application accepts responsibility for the narrower TypeScript contract.
724
+
725
+ ### Function catalog scope
726
+
727
+ Application-owned functions and procedures from non-system schemas are generated into `KnownFunctions`. Objects owned by installed extensions are excluded through `pg_depend`, preventing extension internals from dominating committed artifacts. Set `functionCatalog.includeExtensionOwned: true` only when those approximate signatures are needed, or set `functionCatalog: false` to disable catalog generation entirely.
631
728
 
632
729
  ### Extension types and `customTypes`
633
730
 
@@ -680,6 +777,14 @@ The runtime strips the `!`/`?` suffix from column keys so the row shape stays cl
680
777
 
681
778
  ## CI workflow
682
779
 
780
+ The shortest production gate is provider-aware:
781
+
782
+ ```bash
783
+ sqlx-js ci
784
+ ```
785
+
786
+ For the built-in migration provider it runs shadow migration verification with strict inference, followed by the read-only offline artifact check. For pgschema it checks the configured provider, fails when the desired schema produces an unapplied plan, performs live `prepare --verify --strict-inference`, and then verifies committed artifacts offline. If a committed schema snapshot exists, both flows also run `schema check`. `--json` returns a versioned per-step report suitable for CI systems.
787
+
683
788
  Commit the generated `sqlx-js-env.d.ts` and the `.sqlx-js/` cache directory to your repo. In CI:
684
789
 
685
790
  ```yaml
@@ -701,6 +806,8 @@ The `migrate verify` step needs `DATABASE_URL` credentials that can either creat
701
806
 
702
807
  The managed pgschema binary is installed under `node_modules/.cache/sqlx-js/pgschema/`, not `.sqlx-js/`, so it is not part of the committed offline cache.
703
808
 
809
+ Generated declarations and cache files should be excluded from formatters and linters. They remain included in `tsconfig.json` for type checking, but rules such as Biome's empty-interface or confusing-void checks are not meaningful for declaration-merging points and PostgreSQL procedure contracts.
810
+
704
811
  ## Contributing
705
812
 
706
813
  The project uses [conventional commits](https://www.conventionalcommits.org/), validated locally by `cocogitto` through `lefthook` hooks. Install both before contributing:
@@ -726,7 +833,7 @@ Releases are automated via `release-please`: pushes to `main` accumulate into a
726
833
  - Migrations run inside `BEGIN/COMMIT`. DDL that disallows transactions (`CREATE INDEX CONCURRENTLY`, `VACUUM`, `REINDEX CONCURRENTLY`, …) will fail; split such operations into separate migrations executed outside the runner.
727
834
  - The **internal** wire client (used by `migrate run`, `prepare`, and the runtime `migrate()` helper) reads `sslmode`, `sslrootcert`/`sslcert`/`sslkey`, `application_name`, `options`, `connect_timeout`, and `statement_timeout` from `DATABASE_URL`. The default runtime `sql()` path delegates connection handling to Postgres.js; configure TLS, pooling, and timeouts through the `DATABASE_URL` and `createClient(...)` options it understands (`statementTimeoutMs` is a convenience that maps to a per-connection `statement_timeout`).
728
835
  - `connect_timeout` bounds the entire internal-client connect, including the TLS handshake and SCRAM authentication.
729
- - Runtime `sql.file(path)` resolves against `fileRoot` while prepare resolves against `--root`. They are both root-relative, but applications started outside the project root must set `fileRoot` explicitly.
836
+ - Runtime `sql.file(path)` resolves against `fileRoot` while prepare resolves against `--root`. They are both root-relative, but applications started outside the project root must set `fileRoot` explicitly or provide the generated `sqlFiles` map.
730
837
 
731
838
  See [ROADMAP.md](./ROADMAP.md) for what's planned.
732
839
 
@@ -734,7 +841,7 @@ See [ROADMAP.md](./ROADMAP.md) for what's planned.
734
841
 
735
842
  ### Cache, codegen, and parameter contract changes (pre-1.0)
736
843
 
737
- Generated cache now includes `.sqlx-js/cache-manifest.json` with an explicit cache format, generator revision, and hash of `jsonbTypes` / `customTypes`. Cache without this manifest is rejected. Delete `.sqlx-js/` and re-run `sqlx-js prepare` against your database — there is no data loss because the cache is generated.
844
+ Generated cache includes `.sqlx-js/cache-manifest.json` with an explicit cache format, generator revision, and hash of type/function-catalog settings. Cache without this manifest is rejected. Delete `.sqlx-js/` and re-run `sqlx-js prepare` against your database — there is no data loss because the cache is generated.
738
845
 
739
846
  Generated JSON and PostgreSQL array parameters now require `sql.json(...)` and `sql.array(...)`. This removes the ambiguous runtime guess where a JavaScript array could mean either a PostgreSQL array or a JSON array. Replace raw array JSON params with `sql.json(value)` and PostgreSQL arrays with `sql.array(value)` before regenerating declarations.
740
847
 
@@ -742,6 +849,8 @@ CI (`prepare --check`) will also fail loudly until the cache is regenerated; thi
742
849
 
743
850
  Generator revision 4 changes the declaration layout so it exports `SqlxJsGeneratedRegistry` for scoped clients while continuing to augment the global `KnownQueries` convenience API. Re-run live `sqlx-js prepare` after upgrading. `prepare --check` is now strictly read-only; use `prepare --offline` when deliberate cache-to-declaration regeneration is required.
744
851
 
852
+ Generator revision 6 adds `columnTypes` and function-catalog scope to the generated contract. Extension-owned functions are no longer emitted by default. Re-run live `sqlx-js prepare`; set `functionCatalog.includeExtensionOwned: true` only if application code intentionally indexes those signatures.
853
+
745
854
  Runtime observers and SQL-file caching are also stricter production boundaries. An exception from `onQuery` no longer replaces a successful query result; handle it through `onQueryHookError`. `sql.file()` no longer performs an mtime check on every call—use `reloadSqlFiles: true` during development or call `clearSqlFileCache()` explicitly after changing a file.
746
855
 
747
856
  ## License
package/ROADMAP.md CHANGED
@@ -7,11 +7,12 @@ Items already shipped live in the [README](./README.md) feature list; this file
7
7
  | Feature | ROI | Notes |
8
8
  |---------|-----|-------|
9
9
  | pgschema integration hardening | 6 | Managed install/check/plan/apply now has a real pinned-binary PostgreSQL E2E in CI. Continue with schema snapshot handoff and migration guidance for projects that outgrow built-in migrations. |
10
+ | Separate runtime package | Deferred | The audited root import already excludes compile-time modules. Making TypeScript an optional peer reduced a clean production install from about 33 MB to 2.4 MB; a second public package and release boundary is not justified for the remaining analyzer dependency unless production consumers demonstrate measurable pressure. |
10
11
  | Built-in migration lifecycle maintenance | 5 | Keep `migrate run/dev/verify/revert/squash/archive` stable for simple projects and application startup, but avoid expanding it into a full PostgreSQL schema-as-code system. |
11
- | Prisma migration assistant | 7 | Import Prisma Migrate SQL history and Prisma TypedSQL/raw SQL into `sqlx-js`; classify Prisma Client CRUD/nested-write sites as assisted/manual instead of promising a fully automatic ORM rewrite. |
12
+ | Prisma migration assistant | 7 | Import Prisma Migrate SQL history and Prisma TypedSQL/raw SQL into `sqlx-js`; classify Prisma Client CRUD/nested-write sites as assisted/manual instead of promising a fully automatic ORM rewrite. The shipped `queries --json` inventory covers sqlx-js definitions/call sites after conversion, not Prisma reference-graph discovery. |
12
13
  | Self-join precision (unqualified ColumnRef) | 4 | `SELECT name FROM users u1 JOIN users u2 ON ...` with unqualified `name` can't be attributed to a specific alias. PG would reject ambiguous unqualified refs anyway, but explicit aliasing currently has no narrowing benefit in self-joins. |
13
14
  | Tagged-template literal API (`` sql`SELECT ${x}` ``) | 8 | Restoring sqlx's inline-SQL aesthetic requires either a TS compiler plugin (`ts-patch`) or a Bun preload-time AST rewriter. TS itself hardcodes the first tag argument as `TemplateStringsArray` and refuses to narrow to literal tuples. Significant effort, large UX win. |
14
- | LSP server | 5 | Realtime hover and schema autocomplete. Versioned batch JSON, incremental `prepare --watch --jsonl`, and `sqlx-js-diagnostics` already provide the transport for editor problems; a full server still needs separate VS Code / Neovim clients. |
15
+ | Editor integration / LSP | Deferred | Keep the versioned batch JSON, incremental `prepare --watch --jsonl`, and `sqlx-js-diagnostics` transport stable, but do not build or maintain a VS Code extension or full LSP until real consumer demand justifies the separate editor clients and release lifecycle. |
15
16
  | Schema-aware `jsonb` runtime validation | 5 | Optional opt-in: pass a Zod / Valibot / ArkType schema, validate rows on read. Currently we are compile-time-only by design. |
16
17
  | MySQL backend | 5 | Some runtime clients support it, but MySQL has no `Describe Statement` equivalent. Would need a real SQL parser pass + `INFORMATION_SCHEMA` introspection. |
17
18
  | SQLite backend | 4 | SQLite's column types are dynamic. Would require running `EXPLAIN` and a heuristic mapper, or schema-driven inference per-statement. |
@@ -21,6 +22,6 @@ Items already shipped live in the [README](./README.md) feature list; this file
21
22
 
22
23
  ## Long-term
23
24
 
24
- - Full LSP with schema-driven autocomplete.
25
+ - Editor clients or a full LSP only after repeated consumer demand demonstrates that the separate maintenance and release lifecycle will pay for itself.
25
26
  - Hooks for ORM-like helpers that build on top of the typed `sql()` primitive (joins, paginated queries, etc.) without becoming an ORM.
26
27
  - Optional binary protocol support in the underlying wire client for measurable perf gain on large result sets.
@@ -22,10 +22,12 @@ const HELP = {
22
22
  usage:
23
23
  sqlx-js init [--root <dir>] [--schema-provider builtin|pgschema]
24
24
  sqlx-js doctor [--root <dir>] [--dts <path>] [--json]
25
+ sqlx-js ci [--root <dir>] [--dts <path>] [--schema <path>] [--json] [--shadow-url <url>] [--shadow-admin-url <url>]
25
26
  sqlx-js db install | check [--root <dir>]
26
27
  sqlx-js db plan | apply [--root <dir>] [-- <pgschema args>]
27
28
  sqlx-js prepare [--check | --offline | --verify | --watch] [--json | --jsonl] [--strict-inference] [--root <dir>] [--dts <path>] [--no-prune] [--shadow-url <url>]
28
- sqlx-js migrate dev [--shadow-admin-url <url> | --shadow-url <url>] [--lock-timeout <ms>] [--strict-inference] | verify [--shadow-admin-url <url> | --shadow-url <url>] [--lock-timeout <ms>] [--strict-inference] | run [--dry-run] [--json] [--lock-timeout <ms>] | info [--json] | check [--json] | revert [--dry-run] [--json] [--shadow-admin-url <url> | --shadow-url <url>] [--lock-timeout <ms>] | add <name> | squash <name> [--shadow-admin-url <url> | --shadow-url <url>] [--replace] [--pg-dump <path>] [--lock-timeout <ms>] | archive list | archive restore <name> [--force]
29
+ sqlx-js queries [--json] [--embed <path>] [--root <dir>]
30
+ sqlx-js migrate dev [--dts <path>] [--shadow-admin-url <url> | --shadow-url <url>] [--lock-timeout <ms>] [--strict-inference] | verify [--dts <path>] [--shadow-admin-url <url> | --shadow-url <url>] [--lock-timeout <ms>] [--strict-inference] | run [--dry-run] [--json] [--lock-timeout <ms>] | info [--json] | check [--json] | revert [--dry-run] [--json] [--shadow-admin-url <url> | --shadow-url <url>] [--lock-timeout <ms>] | add <name> | squash <name> [--shadow-admin-url <url> | --shadow-url <url>] [--replace] [--pg-dump <path>] [--lock-timeout <ms>] | archive list | archive restore <name> [--force]
29
31
  sqlx-js schema dump [--schema <path>] [--manifest <path>] [--no-manifest] [--shadow-url <url>]
30
32
  sqlx-js schema check [--schema <path>] [--shadow-url <url>]
31
33
  sqlx-js --version
@@ -48,6 +50,7 @@ flags:
48
50
  --dry-run validate and print migrate run/revert plan without applying migrations
49
51
  --json machine-readable output for prepare and migration inspection/dry-run commands
50
52
  --jsonl streaming machine-readable output for prepare --watch
53
+ --embed <path> emit a deterministic TypeScript map of referenced SQL files
51
54
  --strict-inference fail when query inference degrades or emits unknown types
52
55
  --force allow archive restore to overwrite existing migration files
53
56
  --lock-timeout <ms> advisory-lock acquisition timeout for migrate run/revert/dev/verify/squash
@@ -62,9 +65,11 @@ flags:
62
65
  `,
63
66
  init: `usage: sqlx-js init [--root <dir>] [--schema-provider builtin|pgschema]`,
64
67
  doctor: `usage: sqlx-js doctor [--root <dir>] [--dts <path>] [--json]`,
68
+ ci: `usage: sqlx-js ci [--root <dir>] [--dts <path>] [--schema <path>] [--json] [--shadow-url <url>] [--shadow-admin-url <url>] [--migrations <dir>]`,
65
69
  db: `usage: sqlx-js db install | check [--root <dir>] | plan | apply [--root <dir>] [-- <pgschema args>]`,
66
70
  prepare: `usage: sqlx-js prepare [--check | --offline | --verify | --watch] [--json | --jsonl] [--strict-inference] [--root <dir>] [--dts <path>] [--no-prune] [--shadow-url <url>]`,
67
- migrate: `usage: sqlx-js migrate dev [--shadow-admin-url <url> | --shadow-url <url>] [--lock-timeout <ms>] [--strict-inference] | verify [--shadow-admin-url <url> | --shadow-url <url>] [--lock-timeout <ms>] [--strict-inference] | run [--dry-run] [--json] [--lock-timeout <ms>] | info [--json] | check [--json] | revert [--dry-run] [--json] [--shadow-admin-url <url> | --shadow-url <url>] [--lock-timeout <ms>] | add <name> | squash <name> [--shadow-admin-url <url> | --shadow-url <url>] [--replace] [--pg-dump <path>] [--lock-timeout <ms>] | archive list | archive restore <name> [--force]`,
71
+ queries: `usage: sqlx-js queries [--json] [--embed <path>] [--root <dir>]`,
72
+ migrate: `usage: sqlx-js migrate dev [--dts <path>] [--shadow-admin-url <url> | --shadow-url <url>] [--lock-timeout <ms>] [--strict-inference] | verify [--dts <path>] [--shadow-admin-url <url> | --shadow-url <url>] [--lock-timeout <ms>] [--strict-inference] | run [--dry-run] [--json] [--lock-timeout <ms>] | info [--json] | check [--json] | revert [--dry-run] [--json] [--shadow-admin-url <url> | --shadow-url <url>] [--lock-timeout <ms>] | add <name> | squash <name> [--shadow-admin-url <url> | --shadow-url <url>] [--replace] [--pg-dump <path>] [--lock-timeout <ms>] | archive list | archive restore <name> [--force]`,
68
73
  schema: `usage: sqlx-js schema dump [--schema <path>] [--manifest <path>] [--no-manifest] [--shadow-url <url>] | check [--schema <path>] [--shadow-url <url>]`,
69
74
  };
70
75
  function printHelp(scope, error = false) {
@@ -84,7 +89,7 @@ const passthroughIndex = rawArgv.indexOf("--");
84
89
  const cliArgv = passthroughIndex >= 0 ? rawArgv.slice(0, passthroughIndex) : rawArgv;
85
90
  const passthroughArgs = passthroughIndex >= 0 ? rawArgv.slice(passthroughIndex + 1) : [];
86
91
  const cmd = cliArgv[0];
87
- const scopes = new Set(["init", "doctor", "db", "prepare", "migrate", "schema"]);
92
+ const scopes = new Set(["init", "doctor", "ci", "db", "prepare", "queries", "migrate", "schema"]);
88
93
  if (cmd === "--version" || cmd === "-v") {
89
94
  console.log(VERSION);
90
95
  process.exit(0);
@@ -107,6 +112,16 @@ function optionsFor(command, subcommand) {
107
112
  return { ...ROOT_OPTIONS, "schema-provider": { type: "string" } };
108
113
  if (command === "doctor")
109
114
  return { ...ROOT_OPTIONS, dts: { type: "string" }, json: { type: "boolean" } };
115
+ if (command === "ci")
116
+ return {
117
+ ...ROOT_OPTIONS,
118
+ json: { type: "boolean" },
119
+ dts: { type: "string" },
120
+ schema: { type: "string" },
121
+ migrations: { type: "string" },
122
+ "shadow-url": { type: "string" },
123
+ "shadow-admin-url": { type: "string" },
124
+ };
110
125
  if (command === "db")
111
126
  return ROOT_OPTIONS;
112
127
  if (command === "prepare") {
@@ -125,6 +140,8 @@ function optionsFor(command, subcommand) {
125
140
  "strict-inference": { type: "boolean" },
126
141
  };
127
142
  }
143
+ if (command === "queries")
144
+ return { ...ROOT_OPTIONS, json: { type: "boolean" }, embed: { type: "string" } };
128
145
  if (command === "schema") {
129
146
  const common = {
130
147
  ...ROOT_OPTIONS,
@@ -145,6 +162,7 @@ function optionsFor(command, subcommand) {
145
162
  if (subcommand === "dev") {
146
163
  return {
147
164
  ...common,
165
+ dts: { type: "string" },
148
166
  "shadow-admin-url": { type: "string" },
149
167
  "shadow-url": { type: "string" },
150
168
  "lock-timeout": { type: "string" },
@@ -155,6 +173,7 @@ function optionsFor(command, subcommand) {
155
173
  if (subcommand === "verify") {
156
174
  return {
157
175
  ...common,
176
+ dts: { type: "string" },
158
177
  "shadow-admin-url": { type: "string" },
159
178
  "shadow-url": { type: "string" },
160
179
  "lock-timeout": { type: "string" },
@@ -214,7 +233,7 @@ function requirePositionals(min, max, label) {
214
233
  }
215
234
  }
216
235
  function validateInvocation() {
217
- if (cmd === "init" || cmd === "doctor" || cmd === "prepare") {
236
+ if (cmd === "init" || cmd === "doctor" || cmd === "ci" || cmd === "prepare" || cmd === "queries") {
218
237
  requirePositionals(0, 0, cmd);
219
238
  return;
220
239
  }
@@ -264,17 +283,56 @@ function validateInvocation() {
264
283
  }
265
284
  validateInvocation();
266
285
  const root = resolve(arg("--root", process.cwd()));
286
+ function failCiPreflight(message) {
287
+ if (cmd === "ci" && flag("--json")) {
288
+ console.log(JSON.stringify({
289
+ formatVersion: 1,
290
+ ok: false,
291
+ results: [{ name: "preflight", ok: false, durationMs: 0, exitCode: 2, stderr: message }],
292
+ }, null, 2));
293
+ }
294
+ else {
295
+ console.error(message);
296
+ }
297
+ process.exit(2);
298
+ }
267
299
  if (cmd !== "doctor") {
268
300
  try {
269
301
  assertSupportedRuntime();
270
302
  }
271
303
  catch (e) {
272
- console.error(e.message);
304
+ failCiPreflight(e.message);
305
+ }
306
+ }
307
+ const needsTypeScript = cmd === "doctor" ||
308
+ cmd === "ci" ||
309
+ cmd === "prepare" ||
310
+ cmd === "queries" ||
311
+ (cmd === "migrate" && (positionals[0] === "dev" || positionals[0] === "verify"));
312
+ if (needsTypeScript) {
313
+ try {
314
+ import.meta.resolve("typescript");
315
+ }
316
+ catch {
317
+ const message = "sqlx-js: TypeScript is required for source scanning. Install it with `npm install --save-dev typescript` or `bun add --dev typescript`.";
318
+ if (cmd === "doctor" && flag("--json")) {
319
+ console.log(JSON.stringify({
320
+ formatVersion: 1,
321
+ ok: false,
322
+ checks: [{ name: "typescript", status: "error", message }],
323
+ }, null, 2));
324
+ }
325
+ else {
326
+ if (cmd === "ci")
327
+ failCiPreflight(message);
328
+ console.error(message);
329
+ }
273
330
  process.exit(2);
274
331
  }
275
332
  }
276
333
  let envError;
277
334
  const needsEnvironment = cmd === "doctor" ||
335
+ cmd === "ci" ||
278
336
  cmd === "schema" ||
279
337
  (cmd === "db" && (positionals[0] === "plan" || positionals[0] === "apply")) ||
280
338
  (cmd === "prepare" && !flag("--check") && !flag("--offline")) ||
@@ -286,8 +344,7 @@ if (needsEnvironment) {
286
344
  catch (e) {
287
345
  envError = e.message;
288
346
  if (cmd !== "doctor") {
289
- console.error(envError);
290
- process.exit(2);
347
+ failCiPreflight(envError);
291
348
  }
292
349
  }
293
350
  }
@@ -316,6 +373,28 @@ else if (cmd === "doctor") {
316
373
  const { runDoctor } = await import("../src/commands/doctor.js");
317
374
  await runDoctor({ root, databaseUrl, cacheDir, dtsPath, json: flag("--json"), envError });
318
375
  }
376
+ else if (cmd === "ci") {
377
+ const { runCi } = await import("../src/commands/ci.js");
378
+ let config;
379
+ try {
380
+ config = await loadConfig(root);
381
+ }
382
+ catch (error) {
383
+ failCiPreflight(error.message);
384
+ }
385
+ runCi({
386
+ executable: process.execPath,
387
+ cliPath: fileURLToPath(import.meta.url),
388
+ root,
389
+ config,
390
+ schemaPath,
391
+ json: flag("--json"),
392
+ shadowUrl,
393
+ shadowAdminUrl,
394
+ migrationsDir: arg("--migrations"),
395
+ dtsPath: dtsArg ? dtsPath : undefined,
396
+ });
397
+ }
319
398
  else if (cmd === "db") {
320
399
  const { runPgschemaCommand, runPgschemaInstall } = await import("../src/commands/pgschema.js");
321
400
  const sub = positionals[0];
@@ -458,6 +537,37 @@ else if (cmd === "prepare") {
458
537
  }
459
538
  }
460
539
  }
540
+ else if (cmd === "queries") {
541
+ const { QueriesError, runQueries } = await import("../src/commands/queries.js");
542
+ const embed = arg("--embed");
543
+ try {
544
+ await runQueries({
545
+ root,
546
+ cacheDir,
547
+ json: flag("--json"),
548
+ embedPath: embed ? resolve(root, embed) : undefined,
549
+ });
550
+ }
551
+ catch (error) {
552
+ if (flag("--json")) {
553
+ const diagnostic = error instanceof QueriesError
554
+ ? {
555
+ severity: "error",
556
+ phase: error.phase,
557
+ message: error.message,
558
+ ...(error.file === undefined ? {} : { file: error.file }),
559
+ ...(error.line === undefined ? {} : { line: error.line }),
560
+ ...(error.column === undefined ? {} : { column: error.column }),
561
+ }
562
+ : { severity: "error", phase: "scan", message: error.message };
563
+ console.log(JSON.stringify({ formatVersion: 1, ok: false, diagnostics: [diagnostic] }, null, 2));
564
+ }
565
+ else {
566
+ console.error(error.message);
567
+ }
568
+ process.exit(2);
569
+ }
570
+ }
461
571
  else if (cmd === "schema") {
462
572
  const { runSchemaCheck, runSchemaDump } = await import("../src/commands/schema.js");
463
573
  const sub = positionals[0];
@@ -1,6 +1,9 @@
1
- export declare const CACHE_FORMAT_VERSION = 2;
2
- export declare const GENERATOR_REVISION = 4;
1
+ export declare const CACHE_FORMAT_VERSION = 3;
2
+ export declare const GENERATOR_REVISION = 6;
3
3
  export declare const CACHE_MANIFEST_FILE = "cache-manifest.json";
4
+ export declare class CacheManifestStaleError extends Error {
5
+ constructor(path: string);
6
+ }
4
7
  export type CacheManifest = {
5
8
  cacheFormat: typeof CACHE_FORMAT_VERSION;
6
9
  generatorRevision: typeof GENERATOR_REVISION;
@@ -19,6 +22,7 @@ export type CacheEntry = {
19
22
  paramOids: number[];
20
23
  paramTsTypes: string[];
21
24
  paramNullable?: boolean[];
25
+ paramNames?: string[];
22
26
  columns: CacheColumn[];
23
27
  hasResultSet: boolean;
24
28
  hasInline?: boolean;