@onreza/sqlx-js 0.8.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 +98 -14
- package/ROADMAP.md +1 -1
- package/dist/bin/sqlx-js.js +39 -2
- package/dist/src/cache.d.ts +4 -1
- package/dist/src/cache.js +12 -63
- package/dist/src/commands/init.js +8 -0
- package/dist/src/commands/prepare.d.ts +2 -0
- package/dist/src/commands/prepare.js +46 -35
- package/dist/src/commands/queries.d.ts +38 -0
- package/dist/src/commands/queries.js +143 -0
- package/dist/src/config.d.ts +5 -0
- package/dist/src/config.js +29 -0
- package/dist/src/index.d.ts +10 -2
- package/dist/src/index.js +3 -1
- package/dist/src/pg/functions.d.ts +3 -1
- package/dist/src/pg/functions.js +11 -3
- package/dist/src/postgres-runtime.d.ts +2 -0
- package/dist/src/postgres-runtime.js +9 -6
- package/dist/src/query-id.d.ts +1 -0
- package/dist/src/query-id.js +61 -0
- package/dist/src/query.d.ts +53 -0
- package/dist/src/query.js +38 -0
- package/dist/src/runtime.d.ts +27 -3
- package/dist/src/runtime.js +67 -21
- package/dist/src/scan/scanner.d.ts +2 -0
- package/dist/src/scan/scanner.js +102 -17
- package/dist/src/typed.d.ts +4 -2
- package/package.json +1 -1
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
|
|
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
|
|
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,6 +49,7 @@ 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
|
|
|
@@ -59,7 +61,7 @@ bun add @onreza/sqlx-js
|
|
|
59
61
|
bun add --dev typescript
|
|
60
62
|
```
|
|
61
63
|
|
|
62
|
-
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`, `doctor`, `ci`, and migration development/verification) require it in development dependencies.
|
|
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.
|
|
63
65
|
|
|
64
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.
|
|
65
67
|
|
|
@@ -128,6 +130,8 @@ const rows = await sql(
|
|
|
128
130
|
|
|
129
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.
|
|
130
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
|
+
|
|
131
135
|
During local development, validate the migration and regenerate query artifacts against a disposable shadow database:
|
|
132
136
|
|
|
133
137
|
```bash
|
|
@@ -185,6 +189,39 @@ const rows = await sql(`SELECT id FROM users WHERE name = $1`, "alice");
|
|
|
185
189
|
|
|
186
190
|
Unknown queries, wrong parameter types, and dynamic strings are compile errors. For genuinely dynamic SQL, use `unsafe`.
|
|
187
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
|
+
|
|
188
225
|
### `sql.file(path, ...params)`
|
|
189
226
|
|
|
190
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.
|
|
@@ -202,6 +239,20 @@ const admins = await sql.file("queries/top_admins.sql", "admin", 5);
|
|
|
202
239
|
|
|
203
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.
|
|
204
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
|
+
|
|
205
256
|
### `sql.one(query, ...params)` and `sql.optional(query, ...params)`
|
|
206
257
|
|
|
207
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.
|
|
@@ -255,6 +306,8 @@ await sql(
|
|
|
255
306
|
|
|
256
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.
|
|
257
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
|
+
|
|
258
311
|
Both helpers also work with `unsafe(...)`. `encodePgArrayLiteral(arr)` remains exported for code that explicitly needs a PostgreSQL array literal string.
|
|
259
312
|
|
|
260
313
|
### Parameter nullability
|
|
@@ -357,6 +410,8 @@ await Promise.all([primary.close(), replica.close()]);
|
|
|
357
410
|
|
|
358
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.
|
|
359
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
|
+
|
|
360
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.
|
|
361
416
|
|
|
362
417
|
`createClient(url, options)` accepts every Postgres.js option plus sqlx-js runtime options:
|
|
@@ -371,19 +426,22 @@ setClient(createClient(process.env.DATABASE_URL, {
|
|
|
371
426
|
// Development-only: re-stat sql.file() files on every call. The default
|
|
372
427
|
// immutable cache avoids synchronous filesystem work in the query hot path.
|
|
373
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,
|
|
374
432
|
// Honored for every unsafe call. Set false for PgBouncer transaction mode
|
|
375
433
|
// unless protocol-level prepared statements are configured there.
|
|
376
434
|
prepare: false,
|
|
377
435
|
// Fires after every query/transaction statement, success or failure.
|
|
378
|
-
onQuery: ({ query, params, durationMs, rowCount, error }) => {
|
|
379
|
-
if (error) logger.error({ query, error });
|
|
380
|
-
else if (durationMs > 200) logger.warn({
|
|
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 });
|
|
381
439
|
},
|
|
382
440
|
onQueryHookError: (error) => logger.error({ error }, "query observer failed"),
|
|
383
441
|
}));
|
|
384
442
|
```
|
|
385
443
|
|
|
386
|
-
The `onQuery` hook is the integration point for metrics, tracing, and slow-query logging — sqlx-js does not log queries itself.
|
|
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.
|
|
387
445
|
|
|
388
446
|
### `clearSqlFileCache()`
|
|
389
447
|
|
|
@@ -392,19 +450,19 @@ Drops the in-memory cache used by `sql.file(...)`. Files are immutable after the
|
|
|
392
450
|
### Typed errors
|
|
393
451
|
|
|
394
452
|
```ts
|
|
395
|
-
import { NoRowsError, TooManyRowsError,
|
|
453
|
+
import { NoRowsError, TooManyRowsError, SQLSTATE, isPgError } from "@onreza/sqlx-js";
|
|
396
454
|
|
|
397
455
|
try {
|
|
398
456
|
const u = await sql.one(`SELECT id FROM users WHERE id = $1`, 99);
|
|
399
457
|
} catch (e) {
|
|
400
458
|
if (e instanceof NoRowsError) return null;
|
|
401
459
|
if (e instanceof TooManyRowsError) console.error("ambiguous query, got", e.actual);
|
|
402
|
-
if (e
|
|
460
|
+
if (isPgError(e, SQLSTATE.uniqueViolation)) console.error("duplicate:", e.constraint);
|
|
403
461
|
throw e;
|
|
404
462
|
}
|
|
405
463
|
```
|
|
406
464
|
|
|
407
|
-
`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
|
|
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.
|
|
408
466
|
|
|
409
467
|
### Transactions with options
|
|
410
468
|
|
|
@@ -431,6 +489,7 @@ sqlx-js ci [--root <dir>] [--dts <path>] [--schema <path>] [--json] [--shadow-ur
|
|
|
431
489
|
sqlx-js db install | check [--root <dir>]
|
|
432
490
|
sqlx-js db plan | apply [--root <dir>] [-- <pgschema args>]
|
|
433
491
|
sqlx-js prepare [--check | --offline | --verify | --watch] [--json | --jsonl] [--strict-inference] [--root <dir>] [--dts <path>] [--no-prune] [--shadow-url <url>]
|
|
492
|
+
sqlx-js queries [--json] [--embed <path>] [--root <dir>]
|
|
434
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]
|
|
435
494
|
sqlx-js schema dump [--schema <path>] [--manifest <path>] [--no-manifest] [--shadow-url <url>]
|
|
436
495
|
sqlx-js schema check [--schema <path>] [--shadow-url <url>]
|
|
@@ -451,6 +510,7 @@ Regular `prepare` describes queries across a small connection pool (default 8, o
|
|
|
451
510
|
| `--migrations <dir>` | Root-relative migrations directory (default: `<root>/migrations`). |
|
|
452
511
|
| `--dry-run` | For `migrate run` / `migrate revert`: validate without applying to the target DB. |
|
|
453
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. |
|
|
454
514
|
| `--jsonl` | Versioned streaming events for `prepare --watch`. |
|
|
455
515
|
| `--strict-inference` | Fail prepare/dev/verify when nullability degrades or a generated query type contains `unknown`. |
|
|
456
516
|
| `--force` | For `migrate archive restore`: allow overwriting existing migration files. |
|
|
@@ -466,7 +526,9 @@ Regular `prepare` describes queries across a small connection pool (default 8, o
|
|
|
466
526
|
|
|
467
527
|
Flags that take a value accept both `--flag value` and `--flag=value` forms.
|
|
468
528
|
|
|
469
|
-
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.
|
|
470
532
|
|
|
471
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`.
|
|
472
534
|
|
|
@@ -619,6 +681,14 @@ export default defineConfig({
|
|
|
619
681
|
"posts.meta": "SqlxJsJson.PostMeta",
|
|
620
682
|
"posts.attachments": "SqlxJsJson.Attachment",
|
|
621
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
|
+
},
|
|
622
692
|
});
|
|
623
693
|
```
|
|
624
694
|
|
|
@@ -644,7 +714,17 @@ declare global {
|
|
|
644
714
|
export {};
|
|
645
715
|
```
|
|
646
716
|
|
|
647
|
-
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 `
|
|
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.
|
|
648
728
|
|
|
649
729
|
### Extension types and `customTypes`
|
|
650
730
|
|
|
@@ -726,6 +806,8 @@ The `migrate verify` step needs `DATABASE_URL` credentials that can either creat
|
|
|
726
806
|
|
|
727
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.
|
|
728
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
|
+
|
|
729
811
|
## Contributing
|
|
730
812
|
|
|
731
813
|
The project uses [conventional commits](https://www.conventionalcommits.org/), validated locally by `cocogitto` through `lefthook` hooks. Install both before contributing:
|
|
@@ -751,7 +833,7 @@ Releases are automated via `release-please`: pushes to `main` accumulate into a
|
|
|
751
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.
|
|
752
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`).
|
|
753
835
|
- `connect_timeout` bounds the entire internal-client connect, including the TLS handshake and SCRAM authentication.
|
|
754
|
-
- 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.
|
|
755
837
|
|
|
756
838
|
See [ROADMAP.md](./ROADMAP.md) for what's planned.
|
|
757
839
|
|
|
@@ -759,7 +841,7 @@ See [ROADMAP.md](./ROADMAP.md) for what's planned.
|
|
|
759
841
|
|
|
760
842
|
### Cache, codegen, and parameter contract changes (pre-1.0)
|
|
761
843
|
|
|
762
|
-
Generated cache
|
|
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.
|
|
763
845
|
|
|
764
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.
|
|
765
847
|
|
|
@@ -767,6 +849,8 @@ CI (`prepare --check`) will also fail loudly until the cache is regenerated; thi
|
|
|
767
849
|
|
|
768
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.
|
|
769
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
|
+
|
|
770
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.
|
|
771
855
|
|
|
772
856
|
## License
|
package/ROADMAP.md
CHANGED
|
@@ -9,7 +9,7 @@ Items already shipped live in the [README](./README.md) feature list; this file
|
|
|
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
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. |
|
|
11
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. |
|
|
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. |
|
|
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. |
|
|
13
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. |
|
|
14
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. |
|
|
15
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. |
|
package/dist/bin/sqlx-js.js
CHANGED
|
@@ -26,6 +26,7 @@ usage:
|
|
|
26
26
|
sqlx-js db install | check [--root <dir>]
|
|
27
27
|
sqlx-js db plan | apply [--root <dir>] [-- <pgschema args>]
|
|
28
28
|
sqlx-js prepare [--check | --offline | --verify | --watch] [--json | --jsonl] [--strict-inference] [--root <dir>] [--dts <path>] [--no-prune] [--shadow-url <url>]
|
|
29
|
+
sqlx-js queries [--json] [--embed <path>] [--root <dir>]
|
|
29
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]
|
|
30
31
|
sqlx-js schema dump [--schema <path>] [--manifest <path>] [--no-manifest] [--shadow-url <url>]
|
|
31
32
|
sqlx-js schema check [--schema <path>] [--shadow-url <url>]
|
|
@@ -49,6 +50,7 @@ flags:
|
|
|
49
50
|
--dry-run validate and print migrate run/revert plan without applying migrations
|
|
50
51
|
--json machine-readable output for prepare and migration inspection/dry-run commands
|
|
51
52
|
--jsonl streaming machine-readable output for prepare --watch
|
|
53
|
+
--embed <path> emit a deterministic TypeScript map of referenced SQL files
|
|
52
54
|
--strict-inference fail when query inference degrades or emits unknown types
|
|
53
55
|
--force allow archive restore to overwrite existing migration files
|
|
54
56
|
--lock-timeout <ms> advisory-lock acquisition timeout for migrate run/revert/dev/verify/squash
|
|
@@ -66,6 +68,7 @@ flags:
|
|
|
66
68
|
ci: `usage: sqlx-js ci [--root <dir>] [--dts <path>] [--schema <path>] [--json] [--shadow-url <url>] [--shadow-admin-url <url>] [--migrations <dir>]`,
|
|
67
69
|
db: `usage: sqlx-js db install | check [--root <dir>] | plan | apply [--root <dir>] [-- <pgschema args>]`,
|
|
68
70
|
prepare: `usage: sqlx-js prepare [--check | --offline | --verify | --watch] [--json | --jsonl] [--strict-inference] [--root <dir>] [--dts <path>] [--no-prune] [--shadow-url <url>]`,
|
|
71
|
+
queries: `usage: sqlx-js queries [--json] [--embed <path>] [--root <dir>]`,
|
|
69
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]`,
|
|
70
73
|
schema: `usage: sqlx-js schema dump [--schema <path>] [--manifest <path>] [--no-manifest] [--shadow-url <url>] | check [--schema <path>] [--shadow-url <url>]`,
|
|
71
74
|
};
|
|
@@ -86,7 +89,7 @@ const passthroughIndex = rawArgv.indexOf("--");
|
|
|
86
89
|
const cliArgv = passthroughIndex >= 0 ? rawArgv.slice(0, passthroughIndex) : rawArgv;
|
|
87
90
|
const passthroughArgs = passthroughIndex >= 0 ? rawArgv.slice(passthroughIndex + 1) : [];
|
|
88
91
|
const cmd = cliArgv[0];
|
|
89
|
-
const scopes = new Set(["init", "doctor", "ci", "db", "prepare", "migrate", "schema"]);
|
|
92
|
+
const scopes = new Set(["init", "doctor", "ci", "db", "prepare", "queries", "migrate", "schema"]);
|
|
90
93
|
if (cmd === "--version" || cmd === "-v") {
|
|
91
94
|
console.log(VERSION);
|
|
92
95
|
process.exit(0);
|
|
@@ -137,6 +140,8 @@ function optionsFor(command, subcommand) {
|
|
|
137
140
|
"strict-inference": { type: "boolean" },
|
|
138
141
|
};
|
|
139
142
|
}
|
|
143
|
+
if (command === "queries")
|
|
144
|
+
return { ...ROOT_OPTIONS, json: { type: "boolean" }, embed: { type: "string" } };
|
|
140
145
|
if (command === "schema") {
|
|
141
146
|
const common = {
|
|
142
147
|
...ROOT_OPTIONS,
|
|
@@ -228,7 +233,7 @@ function requirePositionals(min, max, label) {
|
|
|
228
233
|
}
|
|
229
234
|
}
|
|
230
235
|
function validateInvocation() {
|
|
231
|
-
if (cmd === "init" || cmd === "doctor" || cmd === "ci" || cmd === "prepare") {
|
|
236
|
+
if (cmd === "init" || cmd === "doctor" || cmd === "ci" || cmd === "prepare" || cmd === "queries") {
|
|
232
237
|
requirePositionals(0, 0, cmd);
|
|
233
238
|
return;
|
|
234
239
|
}
|
|
@@ -302,6 +307,7 @@ if (cmd !== "doctor") {
|
|
|
302
307
|
const needsTypeScript = cmd === "doctor" ||
|
|
303
308
|
cmd === "ci" ||
|
|
304
309
|
cmd === "prepare" ||
|
|
310
|
+
cmd === "queries" ||
|
|
305
311
|
(cmd === "migrate" && (positionals[0] === "dev" || positionals[0] === "verify"));
|
|
306
312
|
if (needsTypeScript) {
|
|
307
313
|
try {
|
|
@@ -531,6 +537,37 @@ else if (cmd === "prepare") {
|
|
|
531
537
|
}
|
|
532
538
|
}
|
|
533
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
|
+
}
|
|
534
571
|
else if (cmd === "schema") {
|
|
535
572
|
const { runSchemaCheck, runSchemaDump } = await import("../src/commands/schema.js");
|
|
536
573
|
const sub = positionals[0];
|
package/dist/src/cache.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export declare const CACHE_FORMAT_VERSION = 3;
|
|
2
|
-
export declare const GENERATOR_REVISION =
|
|
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;
|
package/dist/src/cache.js
CHANGED
|
@@ -1,74 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
2
|
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, unlinkSync, renameSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { isBuiltinOid } from "./pg/oids.js";
|
|
5
|
-
import {
|
|
5
|
+
import { queryId } from "./query-id.js";
|
|
6
6
|
import { rewriteNamedParameters } from "./sql-params.js";
|
|
7
7
|
export const CACHE_FORMAT_VERSION = 3;
|
|
8
|
-
export const GENERATOR_REVISION =
|
|
8
|
+
export const GENERATOR_REVISION = 6;
|
|
9
9
|
export const CACHE_MANIFEST_FILE = "cache-manifest.json";
|
|
10
|
+
export class CacheManifestStaleError extends Error {
|
|
11
|
+
constructor(path) {
|
|
12
|
+
super(`sqlx-js: cache manifest is stale: ${path}. Run \`sqlx-js prepare\`.`);
|
|
13
|
+
this.name = "CacheManifestStaleError";
|
|
14
|
+
}
|
|
15
|
+
}
|
|
10
16
|
export function portableCacheOid(oid) {
|
|
11
17
|
return isBuiltinOid(oid) ? oid : 0;
|
|
12
18
|
}
|
|
13
19
|
export function fingerprint(query) {
|
|
14
|
-
|
|
15
|
-
return createHash("sha256").update(norm).digest("hex").slice(0, 16);
|
|
16
|
-
}
|
|
17
|
-
function normalizeForFingerprint(query) {
|
|
18
|
-
let out = "";
|
|
19
|
-
let pendingSpace = false;
|
|
20
|
-
let i = 0;
|
|
21
|
-
const emit = (text) => {
|
|
22
|
-
if (pendingSpace && out.length > 0)
|
|
23
|
-
out += " ";
|
|
24
|
-
out += text;
|
|
25
|
-
pendingSpace = false;
|
|
26
|
-
};
|
|
27
|
-
const markSpace = () => {
|
|
28
|
-
if (out.length > 0)
|
|
29
|
-
pendingSpace = true;
|
|
30
|
-
};
|
|
31
|
-
while (i < query.length) {
|
|
32
|
-
const ch = query[i];
|
|
33
|
-
if (/\s/.test(ch)) {
|
|
34
|
-
markSpace();
|
|
35
|
-
i++;
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
if (ch === "-" && query[i + 1] === "-") {
|
|
39
|
-
i = readLineComment(query, i);
|
|
40
|
-
markSpace();
|
|
41
|
-
continue;
|
|
42
|
-
}
|
|
43
|
-
if (ch === "/" && query[i + 1] === "*") {
|
|
44
|
-
i = readBlockComment(query, i);
|
|
45
|
-
markSpace();
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
if (ch === "'") {
|
|
49
|
-
const next = readSingleQuoted(query, i, isEscapeStringPrefix(query, i));
|
|
50
|
-
emit(query.slice(i, next));
|
|
51
|
-
i = next;
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
if (ch === "\"") {
|
|
55
|
-
const next = readQuotedIdentifier(query, i);
|
|
56
|
-
emit(query.slice(i, next));
|
|
57
|
-
i = next;
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
if (ch === "$") {
|
|
61
|
-
const next = i === 0 || !isIdentifierContinuation(query[i - 1]) ? readDollarQuoted(query, i) : null;
|
|
62
|
-
if (next !== null) {
|
|
63
|
-
emit(query.slice(i, next));
|
|
64
|
-
i = next;
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
emit(ch);
|
|
69
|
-
i++;
|
|
70
|
-
}
|
|
71
|
-
return out;
|
|
20
|
+
return queryId(query);
|
|
72
21
|
}
|
|
73
22
|
export function effectiveNullable(c) {
|
|
74
23
|
if (c.override === "non-null")
|
|
@@ -249,7 +198,7 @@ export function readCacheManifest(cacheDir) {
|
|
|
249
198
|
if (value.cacheFormat !== CACHE_FORMAT_VERSION ||
|
|
250
199
|
value.generatorRevision !== GENERATOR_REVISION ||
|
|
251
200
|
typeof value.configHash !== "string") {
|
|
252
|
-
throw new
|
|
201
|
+
throw new CacheManifestStaleError(path);
|
|
253
202
|
}
|
|
254
203
|
return value;
|
|
255
204
|
}
|
|
@@ -259,7 +208,7 @@ export function assertCacheManifest(cacheDir, configHash) {
|
|
|
259
208
|
throw new Error(`sqlx-js: cache manifest is missing. Run \`sqlx-js prepare\` to regenerate the cache.`);
|
|
260
209
|
}
|
|
261
210
|
if (manifest.configHash !== configHash) {
|
|
262
|
-
throw new Error("sqlx-js: cache was generated with a different jsonbTypes/customTypes config. Run `sqlx-js prepare`.");
|
|
211
|
+
throw new Error("sqlx-js: cache was generated with a different jsonbTypes/customTypes config or other type/function catalog settings. Run `sqlx-js prepare`.");
|
|
263
212
|
}
|
|
264
213
|
return manifest;
|
|
265
214
|
}
|
|
@@ -6,9 +6,14 @@ export default defineConfig({
|
|
|
6
6
|
// Map jsonb columns/params to TypeScript types declared in a .d.ts, e.g.
|
|
7
7
|
// "users.settings": "SqlxJsJson.UserSettings",
|
|
8
8
|
jsonbTypes: {},
|
|
9
|
+
// Assert narrower TypeScript types for direct scalar columns.
|
|
10
|
+
columnTypes: {},
|
|
9
11
|
// Map PostgreSQL type names to TypeScript types, e.g.
|
|
10
12
|
// geometry: "GeoJSON.Geometry",
|
|
11
13
|
customTypes: {},
|
|
14
|
+
// Extension-owned functions are excluded by default. Set false to disable
|
|
15
|
+
// the generated function catalog entirely.
|
|
16
|
+
functionCatalog: {},
|
|
12
17
|
});
|
|
13
18
|
`;
|
|
14
19
|
const PGSCHEMA_CONFIG_TEMPLATE = `import { defineConfig } from "@onreza/sqlx-js";
|
|
@@ -20,7 +25,9 @@ export default defineConfig({
|
|
|
20
25
|
schemas: ["public"],
|
|
21
26
|
},
|
|
22
27
|
jsonbTypes: {},
|
|
28
|
+
columnTypes: {},
|
|
23
29
|
customTypes: {},
|
|
30
|
+
functionCatalog: {},
|
|
24
31
|
});
|
|
25
32
|
`;
|
|
26
33
|
const ENV_TEMPLATE = `# Connection string used by sqlx-js prepare/migrate and the runtime.
|
|
@@ -144,6 +151,7 @@ export function runInit(opts) {
|
|
|
144
151
|
"sqlx:offline": "sqlx-js prepare --offline",
|
|
145
152
|
"sqlx:verify": "sqlx-js prepare --verify --strict-inference",
|
|
146
153
|
"sqlx:ci": "sqlx-js ci",
|
|
154
|
+
"sqlx:queries": "sqlx-js queries --json",
|
|
147
155
|
};
|
|
148
156
|
let changed = false;
|
|
149
157
|
for (const [name, command] of Object.entries(defaults)) {
|