@onreza/sqlx-js 0.2.0 → 0.4.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 +73 -29
- package/ROADMAP.md +2 -1
- package/dist/bin/sqlx-js.js +51 -11
- package/dist/src/cache.d.ts +1 -0
- package/dist/src/cache.js +128 -1
- package/dist/src/codegen.d.ts +2 -1
- package/dist/src/codegen.js +36 -17
- package/dist/src/commands/init.d.ts +1 -0
- package/dist/src/commands/init.js +36 -5
- package/dist/src/commands/pgschema.d.ts +25 -0
- package/dist/src/commands/pgschema.js +178 -0
- package/dist/src/commands/prepare.d.ts +1 -0
- package/dist/src/commands/prepare.js +48 -15
- package/dist/src/config.d.ts +6 -0
- package/dist/src/function-cache.d.ts +18 -0
- package/dist/src/function-cache.js +38 -0
- package/dist/src/index.d.ts +14 -0
- package/dist/src/pg/functions.d.ts +4 -0
- package/dist/src/pg/functions.js +150 -0
- package/dist/src/pg/oids.js +5 -2
- package/dist/src/pg/param-map.d.ts +2 -1
- package/dist/src/pg/param-map.js +181 -40
- package/dist/src/pg/schema.d.ts +1 -0
- package/dist/src/pg/schema.js +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Compile-time-checked raw SQL for TypeScript + PostgreSQL. Inspired by Rust's [sqlx](https://github.com/launchbadge/sqlx).
|
|
4
4
|
|
|
5
|
-
You write plain SQL strings. A `prepare` step validates them against your database via the PostgreSQL wire protocol and generates a TypeScript declaration file. Wrong column names
|
|
5
|
+
You write plain SQL strings. A `prepare` step validates them against your database via the PostgreSQL wire protocol and generates a TypeScript declaration file. Wrong column names and stale queries fail during `prepare`; mismatched parameter types and row usage become TypeScript errors.
|
|
6
6
|
|
|
7
|
-
The runtime uses [Postgres.js](https://github.com/porsager/postgres)
|
|
7
|
+
The runtime uses [Postgres.js](https://github.com/porsager/postgres) through a single adapter instead of a Bun-specific client. The published CLI is a **Node ≥ 18** binary (`#!/usr/bin/env node`) and can also be run through Bun's npm tooling.
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
10
|
import { sql } from "@onreza/sqlx-js";
|
|
@@ -24,11 +24,11 @@ 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.
|
|
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`.
|
|
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
|
-
- **External SQL files** via `sql.file("queries/foo.sql", ...)` —
|
|
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
33
|
- **Array params** for `text[]`, `int[]`, etc. are auto-serialised to PostgreSQL array literals (`{a,b,c}`) at runtime — no more `string_to_array` workaround.
|
|
34
34
|
- **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.
|
|
@@ -36,12 +36,14 @@ const rows = await sql(
|
|
|
36
36
|
- **Linear migrations** with hash tampering detection.
|
|
37
37
|
- **Migration squash baselines** via `migrate squash`: generate a schema-only baseline from a shadow database, then hash-adopt it on already-migrated databases.
|
|
38
38
|
- **Runtime `migrate()`** with PostgreSQL advisory lock, safe for multi-replica startup.
|
|
39
|
+
- **Optional pgschema workflow** via `init --schema-provider pgschema` and `sqlx-js db install|check|plan|apply` for PostgreSQL schema-as-code projects.
|
|
39
40
|
- **Offline cache** committed to your repo. CI verifies via `prepare --check` without a database.
|
|
40
41
|
- **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.
|
|
41
43
|
- **Shadow database validation** via `migrate dev` / `migrate verify`: auto-create a disposable shadow DB, apply migrations, validate SQL, and drop it afterwards.
|
|
42
44
|
- **Safe identifier quoting** via `sql.id(...)`, backed by the committed schema snapshot whitelist.
|
|
43
|
-
- **
|
|
44
|
-
- **Watch mode**:
|
|
45
|
+
- **Single runtime adapter**: Postgres.js backs the runtime on Node/Bun-compatible environments — no Bun.SQL-specific adapter to choose.
|
|
46
|
+
- **Watch mode**: debounced re-prepare with a warm `PgClient` + `SchemaCache` on `.ts` / `.tsx` / `.mts` / `.cts` / `.sql` changes.
|
|
45
47
|
- **Cache pruning** removes orphaned entries automatically (toggleable with `--no-prune`).
|
|
46
48
|
|
|
47
49
|
## Install
|
|
@@ -64,6 +66,16 @@ npx @onreza/sqlx-js init
|
|
|
64
66
|
|
|
65
67
|
Creates `sqlx-js.config.ts`, a `migrations/` directory, and `.env.example` if they don't already exist (it never overwrites existing files), then prints the next steps. Skip it if you prefer to wire things up manually.
|
|
66
68
|
|
|
69
|
+
For declarative PostgreSQL schema management, scaffold the pgschema workflow instead:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npx @onreza/sqlx-js init --schema-provider pgschema
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
This creates `schema.sql` and configures `schema.provider = "pgschema"` in `sqlx-js.config.ts`. The npm package does not bundle pgschema, but `sqlx-js db install` downloads the pinned pgschema binary into `node_modules/.cache/sqlx-js/pgschema/`; then `sqlx-js db check` verifies it.
|
|
76
|
+
|
|
77
|
+
The managed pgschema workflow supports Linux and macOS. On Windows, run sqlx-js under WSL/Linux/macOS or use the built-in `sqlx-js migrate` workflow.
|
|
78
|
+
|
|
67
79
|
### 1. Configure the database URL
|
|
68
80
|
|
|
69
81
|
```bash
|
|
@@ -73,7 +85,7 @@ DATABASE_URL=postgres://user:password@localhost:5432/your_db
|
|
|
73
85
|
# DATABASE_URL=postgres://user:password@db.example.com:5432/your_db?sslmode=require
|
|
74
86
|
```
|
|
75
87
|
|
|
76
|
-
Supported `sslmode` values: `disable`, `prefer` (default — try TLS, fall back to plaintext), `require` (TLS or fail), `verify-ca`, `verify-full`. For a private/self-signed CA, point `sslrootcert` (and optionally `sslcert` / `sslkey` for client certs) at PEM files: `?sslmode=verify-full&sslrootcert=/etc/ssl/ca.pem`. `application_name`, `connect_timeout
|
|
88
|
+
Supported `sslmode` values: `disable`, `prefer` (default — try TLS, fall back to plaintext), `require` (TLS or fail), `verify-ca`, `verify-full`. For a private/self-signed CA, point `sslrootcert` (and optionally `sslcert` / `sslkey` for client certs) at PEM files: `?sslmode=verify-full&sslrootcert=/etc/ssl/ca.pem`. `application_name`, `connect_timeout` (seconds), and `statement_timeout` (milliseconds) are also honored when provided as URL parameters.
|
|
77
89
|
|
|
78
90
|
### 2. Create a migration
|
|
79
91
|
|
|
@@ -81,7 +93,7 @@ Supported `sslmode` values: `disable`, `prefer` (default — try TLS, fall back
|
|
|
81
93
|
npx @onreza/sqlx-js migrate add init
|
|
82
94
|
```
|
|
83
95
|
|
|
84
|
-
Edit the
|
|
96
|
+
The command creates matching `.up.sql` and `.down.sql` stubs. Edit the `.up.sql` file (`migrations/0001_init.up.sql`):
|
|
85
97
|
|
|
86
98
|
```sql
|
|
87
99
|
CREATE TABLE users (
|
|
@@ -99,7 +111,7 @@ During local development, validate the migration and regenerate query artifacts
|
|
|
99
111
|
npx @onreza/sqlx-js migrate dev
|
|
100
112
|
```
|
|
101
113
|
|
|
102
|
-
`migrate dev` does not touch your application database. It creates a temporary shadow database using `DATABASE_URL` credentials, applies migrations from scratch, validates that the latest
|
|
114
|
+
`migrate dev` does not touch your application database. It creates a temporary shadow database using `DATABASE_URL` credentials, applies migrations from scratch, validates that the latest migration's `.down.sql` restores the previous schema (squash baselines may omit `.down.sql`), prepares SQL queries against the resulting schema, writes `.sqlx-js/` and `sqlx-js-env.d.ts`, then drops the shadow database.
|
|
103
115
|
|
|
104
116
|
When you want to update your local application database, run:
|
|
105
117
|
|
|
@@ -152,7 +164,7 @@ Unknown queries, wrong parameter types, and dynamic strings are compile errors.
|
|
|
152
164
|
|
|
153
165
|
### `sql.file(path, ...params)`
|
|
154
166
|
|
|
155
|
-
Load SQL from an external file.
|
|
167
|
+
Load SQL from an external file. At prepare time the scanner reads the path relative to the source file. The generated `KnownFileQueries` key is the resolved SQL file path relative to `--root`; at runtime `sql.file(...)` reads the string argument relative to `process.cwd()`.
|
|
156
168
|
|
|
157
169
|
```ts
|
|
158
170
|
// queries/top_admins.sql
|
|
@@ -165,7 +177,7 @@ const admins = await sql.file("queries/top_admins.sql", "admin", 5);
|
|
|
165
177
|
// admins: { id: bigint; name: string }[]
|
|
166
178
|
```
|
|
167
179
|
|
|
168
|
-
File-backed queries are emitted into a separate `KnownFileQueries` interface
|
|
180
|
+
File-backed queries are emitted into a separate `KnownFileQueries` interface. Because the type key is the root-relative resolved SQL file path, keep file-backed call sites under a convention where that key matches the runtime string literal; the example keeps those call sites at the project root. Nested source-relative file paths are a current limitation.
|
|
169
181
|
|
|
170
182
|
### `sql.one(query, ...params)` and `sql.optional(query, ...params)`
|
|
171
183
|
|
|
@@ -240,7 +252,7 @@ const orderBy = sql.id("users", "created_at");
|
|
|
240
252
|
await unsafe(`SELECT id, email FROM ${sql.id("users")} ORDER BY ${orderBy} DESC`);
|
|
241
253
|
```
|
|
242
254
|
|
|
243
|
-
The default snapshot path is `.sqlx-js/schema/schema.json`. Override it at runtime with `SQLX_JS_SCHEMA_PATH`. Pass schema-qualified identifiers as separate segments: `sql.id("public", "users")`, not `sql.id("public.users")`.
|
|
255
|
+
The default snapshot path is `.sqlx-js/schema/schema.json`. Override it at runtime with `SQLX_JS_SCHEMA_PATH`. `sql.id(...)` accepts one to three identifier segments. Pass schema-qualified identifiers as separate segments: `sql.id("public", "users")`, not `sql.id("public.users")`.
|
|
244
256
|
|
|
245
257
|
### `migrate(options)`
|
|
246
258
|
|
|
@@ -286,13 +298,13 @@ setClient(createClient(process.env.DATABASE_URL, {
|
|
|
286
298
|
statementTimeoutMs: 5000,
|
|
287
299
|
// Fires after every query/transaction statement, success or failure.
|
|
288
300
|
onQuery: ({ query, params, durationMs, rowCount, error }) => {
|
|
289
|
-
if (error) logger.error({ query, error }); //
|
|
301
|
+
if (error) logger.error({ query, error }); // database errors are PgError
|
|
290
302
|
else if (durationMs > 200) logger.warn({ slow: query, durationMs, rowCount });
|
|
291
303
|
},
|
|
292
304
|
}));
|
|
293
305
|
```
|
|
294
306
|
|
|
295
|
-
The `onQuery` hook
|
|
307
|
+
The `onQuery` hook is the integration point for metrics, tracing, and slow-query logging — sqlx-js does not log queries itself. 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.
|
|
296
308
|
|
|
297
309
|
### `clearSqlFileCache()`
|
|
298
310
|
|
|
@@ -313,7 +325,7 @@ try {
|
|
|
313
325
|
}
|
|
314
326
|
```
|
|
315
327
|
|
|
316
|
-
`sql.one` throws `NoRowsError` on 0 rows and `TooManyRowsError` (with `.actual`) on >1. Any database error raised by the runtime
|
|
328
|
+
`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.
|
|
317
329
|
|
|
318
330
|
### Transactions with options
|
|
319
331
|
|
|
@@ -334,18 +346,19 @@ In addition to `import { sql } from "@onreza/sqlx-js"`, the scanner recognises `
|
|
|
334
346
|
## CLI
|
|
335
347
|
|
|
336
348
|
```
|
|
337
|
-
sqlx-js init [--root <dir>]
|
|
349
|
+
sqlx-js init [--root <dir>] [--schema-provider builtin|pgschema]
|
|
350
|
+
sqlx-js db install | check | plan | apply [--root <dir>] [-- <pgschema args>]
|
|
338
351
|
sqlx-js prepare [--check | --watch] [--root <dir>] [--dts <path>] [--no-prune] [--shadow-url <url>]
|
|
339
|
-
sqlx-js migrate dev [--shadow-admin-url <url> | --shadow-url <url>] | verify [--shadow-admin-url <url> | --shadow-url <url>] | run [--dry-run] [--json] [--lock-timeout <ms>] | info [--json] | check [--json] | revert [--dry-run] [--json] [--shadow-admin-url <url> | --shadow-url <url>] | add <name> | squash <name> [--shadow-admin-url <url> | --shadow-url <url>] [--replace] | archive list | archive restore <name> [--force]
|
|
352
|
+
sqlx-js migrate dev [--shadow-admin-url <url> | --shadow-url <url>] [--lock-timeout <ms>] | verify [--shadow-admin-url <url> | --shadow-url <url>] [--lock-timeout <ms>] | 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]
|
|
340
353
|
sqlx-js schema dump | check [--schema <path>] [--manifest <path>] [--no-manifest] [--shadow-url <url>]
|
|
341
354
|
sqlx-js --version | --help
|
|
342
355
|
```
|
|
343
356
|
|
|
344
|
-
`prepare` describes queries across a small connection pool (default 8, override with `SQLX_JS_PREPARE_CONCURRENCY`) for faster cold runs on large projects.
|
|
357
|
+
Regular `prepare` describes queries across a small connection pool (default 8, override with `SQLX_JS_PREPARE_CONCURRENCY`) for faster cold runs on large projects. Watch mode keeps one session warm and reuses it between debounced changes.
|
|
345
358
|
|
|
346
359
|
| Flag | Meaning |
|
|
347
360
|
|-----------------------|--------------------------------------------------------------------------------------|
|
|
348
|
-
| `--check` | Offline: verify
|
|
361
|
+
| `--check` | Offline: verify every scanned query is present in cache, no database required. |
|
|
349
362
|
| `--watch` | Persistent connection, re-prepare on file change. |
|
|
350
363
|
| `--root <dir>` | Source/cache/migrations root (default: cwd). |
|
|
351
364
|
| `--dts <path>` | Declarations output (default: `<root>/sqlx-js-env.d.ts`). |
|
|
@@ -354,7 +367,7 @@ sqlx-js --version | --help
|
|
|
354
367
|
| `--dry-run` | For `migrate run` / `migrate revert`: validate without applying to the target DB. |
|
|
355
368
|
| `--json` | Machine-readable output for `migrate info/check` and migration dry-runs. |
|
|
356
369
|
| `--force` | For `migrate archive restore`: allow overwriting existing migration files. |
|
|
357
|
-
| `--lock-timeout <ms>` | Advisory-lock acquisition timeout for `migrate run` / `
|
|
370
|
+
| `--lock-timeout <ms>` | Advisory-lock acquisition timeout for `migrate run` / `revert` / `dev` / `verify` / `squash`. |
|
|
358
371
|
| `--shadow-url <url>` | Use an existing disposable shadow DB instead of auto-creating one. |
|
|
359
372
|
| `--shadow-admin-url <url>` | Admin/maintenance DB URL used to auto-create shadow DBs. |
|
|
360
373
|
| `--replace` | For `migrate squash`: archive replaced migration files after writing the baseline. |
|
|
@@ -362,13 +375,28 @@ sqlx-js --version | --help
|
|
|
362
375
|
| `--schema <path>` | Schema snapshot path (default: `<root>/.sqlx-js/schema/schema.json`). |
|
|
363
376
|
| `--manifest <path>` | LLM schema manifest path (default: `<root>/.sqlx-js/schema/schema.md`). |
|
|
364
377
|
| `--no-manifest` | Skip writing the LLM schema manifest during `schema dump`. |
|
|
378
|
+
| `--schema-provider <name>` | For `init`: `builtin` (default) or `pgschema`. |
|
|
365
379
|
|
|
366
|
-
|
|
380
|
+
Flags that take a value accept both `--flag value` and `--flag=value` forms.
|
|
367
381
|
|
|
368
|
-
`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.
|
|
382
|
+
`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`, `connect_timeout` (seconds), and `statement_timeout` (milliseconds).
|
|
369
383
|
|
|
370
384
|
### Development and deployment flows
|
|
371
385
|
|
|
386
|
+
For complex PostgreSQL schemas with functions, triggers, RLS, grants, partitions, and other schema-level objects, prefer pgschema for DDL ownership and use sqlx-js for application-query typing:
|
|
387
|
+
|
|
388
|
+
```bash
|
|
389
|
+
sqlx-js init --schema-provider pgschema
|
|
390
|
+
sqlx-js db install
|
|
391
|
+
sqlx-js db check
|
|
392
|
+
# edit schema.sql
|
|
393
|
+
sqlx-js db plan -- --output-json plan.json
|
|
394
|
+
sqlx-js db apply -- --auto-approve
|
|
395
|
+
sqlx-js prepare
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
`sqlx-js db install` installs the pinned pgschema version used by this sqlx-js release. `sqlx-js db check`, `plan`, and `apply` use `schema.command` when configured; otherwise they prefer the managed binary under `node_modules/.cache/sqlx-js/pgschema/` and fall back to `pgschema` on `PATH`. `plan` and file-backed `apply` translate `DATABASE_URL` into `--host`, `--port`, `--db`, `--user`, `--file`, and `--schema` arguments, pass the password through `PGPASSWORD`, pass TLS settings through `PGSSLMODE` / `PGSSLROOTCERT` / `PGSSLCERT` / `PGSSLKEY`, and forward any arguments after `--` directly to pgschema. `sqlx-js db apply -- --plan plan.json` applies a reviewed pgschema plan without requiring the local `schema.sql` file. The schema provider is configured in `sqlx-js.config.ts`; by default the schema file is `schema.sql` and the schema is `public`. The pinned pgschema 1.12.0 CLI accepts a single `--schema` value, so sqlx-js rejects pgschema configs with more than one schema instead of silently applying only one.
|
|
399
|
+
|
|
372
400
|
Use `migrate dev` while developing migrations and SQL:
|
|
373
401
|
|
|
374
402
|
```bash
|
|
@@ -377,7 +405,9 @@ sqlx-js migrate add add_users
|
|
|
377
405
|
sqlx-js migrate dev
|
|
378
406
|
```
|
|
379
407
|
|
|
380
|
-
`migrate dev` creates a disposable shadow database, applies all migrations from scratch, validates that the latest
|
|
408
|
+
`migrate dev` creates a disposable shadow database, applies all migrations from scratch, validates that the latest migration's `.down.sql` restores the previous schema (squash baselines may omit `.down.sql`), prepares project SQL against the shadow schema, writes `.sqlx-js/` plus `sqlx-js-env.d.ts`, and drops the shadow database. This means you can keep editing a local WIP migration before it is merged. You do not need to drop your application database or create a new migration for every local edit.
|
|
409
|
+
|
|
410
|
+
The built-in `migrate` workflow is kept for simple projects and embedded application startup. PostgreSQL-heavy schema lifecycle features belong in pgschema rather than in sqlx-js.
|
|
381
411
|
|
|
382
412
|
Use `migrate verify` in PR/CI before merge:
|
|
383
413
|
|
|
@@ -428,7 +458,7 @@ On an empty database, the baseline runs as ordinary schema SQL. On an already-mi
|
|
|
428
458
|
- `.sqlx-js/schema/schema.json` — machine-readable contract for runtime identifier whitelisting and CI drift checks.
|
|
429
459
|
- `.sqlx-js/schema/schema.md` — compact LLM-facing manifest with tables, columns, constraints, indexes, types, and functions.
|
|
430
460
|
|
|
431
|
-
`schema check` re-introspects the database and fails if the committed snapshot is stale. With `--shadow-url`, both `prepare` and `schema dump/check` first apply pending migrations to the shadow database, then use that database as the source of truth. In watch mode, pending shadow migrations are checked before every re-prepare; when a migration is applied, the prepare session is reopened so schema metadata is not reused across DDL changes.
|
|
461
|
+
`schema check` re-introspects the database and fails if the committed snapshot is stale. With `--shadow-url`, both `prepare` and `schema dump/check` first apply pending migrations to the shadow database, then use that database as the source of truth. Unlike `migrate dev` / `verify` / `squash`, these commands do not clear an explicit shadow database first. In watch mode, pending shadow migrations are checked before every re-prepare; when a migration is applied, the prepare session is reopened so schema metadata is not reused across DDL changes.
|
|
432
462
|
|
|
433
463
|
### Error output
|
|
434
464
|
|
|
@@ -449,6 +479,11 @@ Phases reported separately: `describe failed`, `analyze failed`, `paramMap faile
|
|
|
449
479
|
import type { SqlxJsConfig } from "@onreza/sqlx-js";
|
|
450
480
|
|
|
451
481
|
const config: SqlxJsConfig = {
|
|
482
|
+
schema: {
|
|
483
|
+
provider: "pgschema",
|
|
484
|
+
file: "schema.sql",
|
|
485
|
+
schemas: ["public"],
|
|
486
|
+
},
|
|
452
487
|
jsonbTypes: {
|
|
453
488
|
"users.settings": "SqlxJsJson.UserSettings",
|
|
454
489
|
"posts.meta": "SqlxJsJson.PostMeta",
|
|
@@ -459,6 +494,8 @@ const config: SqlxJsConfig = {
|
|
|
459
494
|
export default config;
|
|
460
495
|
```
|
|
461
496
|
|
|
497
|
+
The `schema` block is optional. Use `provider: "pgschema"` when sqlx-js should delegate schema planning/apply commands to pgschema. `command` can override the managed binary lookup and point at another executable. With the pinned pgschema 1.12.0 CLI, `schemas` must contain exactly one schema name.
|
|
498
|
+
|
|
462
499
|
Declare the referenced types anywhere in your project (`.d.ts` file is conventional):
|
|
463
500
|
|
|
464
501
|
```ts
|
|
@@ -477,7 +514,7 @@ declare global {
|
|
|
477
514
|
export {};
|
|
478
515
|
```
|
|
479
516
|
|
|
480
|
-
After re-running `prepare`, every `jsonb` column or parameter declared in `jsonbTypes` is checked against the corresponding TypeScript type.
|
|
517
|
+
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` for parameters, both exported by `@onreza/sqlx-js`, so 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. Top-level SQL `null` is added separately as `| null` only when the mapped database parameter is nullable.
|
|
481
518
|
|
|
482
519
|
### Extension types and `customTypes`
|
|
483
520
|
|
|
@@ -494,7 +531,7 @@ sqlx-js ships with a built-in registry that resolves popular PostgreSQL extensio
|
|
|
494
531
|
| `lquery` | `string` | ltree |
|
|
495
532
|
| `ltxtquery` | `string` | ltree |
|
|
496
533
|
|
|
497
|
-
Add or override mappings via `customTypes` in `sqlx-js.config.ts`. Keys are `pg_type.typname` values (the bare type name
|
|
534
|
+
Add or override mappings via `customTypes` in `sqlx-js.config.ts`. Keys are `pg_type.typname` values (the bare type name). The registry is global by type name, so two schemas with the same `typname` cannot be mapped differently:
|
|
498
535
|
|
|
499
536
|
```ts
|
|
500
537
|
import type { SqlxJsConfig } from "@onreza/sqlx-js";
|
|
@@ -535,7 +572,10 @@ Commit the generated `sqlx-js-env.d.ts` and the `.sqlx-js/` cache directory to y
|
|
|
535
572
|
|
|
536
573
|
```yaml
|
|
537
574
|
- run: bun install
|
|
538
|
-
- run: sqlx-js migrate verify #
|
|
575
|
+
- run: sqlx-js migrate verify # built-in migration workflow
|
|
576
|
+
# or, when schema.provider is "pgschema":
|
|
577
|
+
- run: sqlx-js db install
|
|
578
|
+
- run: sqlx-js db plan -- --output-json plan.json
|
|
539
579
|
- run: sqlx-js prepare --check # fails if any query is missing from the committed cache
|
|
540
580
|
- run: sqlx-js schema check # fails if the committed schema snapshot is stale
|
|
541
581
|
- run: tsc --noEmit # fails if types are stale
|
|
@@ -543,7 +583,9 @@ Commit the generated `sqlx-js-env.d.ts` and the `.sqlx-js/` cache directory to y
|
|
|
543
583
|
- run: bun run build # emits publishable JS + declarations under dist/
|
|
544
584
|
```
|
|
545
585
|
|
|
546
|
-
The `migrate verify` step needs `DATABASE_URL` credentials that can either create a temporary database or use `--shadow-admin-url` / `--shadow-url`. It does not write `.sqlx-js/` or `sqlx-js-env.d.ts`. The `prepare --check` step then runs without a database; your committed offline cache is the source of truth. `schema check` intentionally uses a live database because it verifies the committed schema contract against PostgreSQL.
|
|
586
|
+
The `migrate verify` step needs `DATABASE_URL` credentials that can either create a temporary database or use `--shadow-admin-url` / `--shadow-url`. It does not write `.sqlx-js/` or `sqlx-js-env.d.ts`. For pgschema projects, `sqlx-js db plan` checks the desired `schema.sql` against the target database and leaves application query typing to `prepare`. The `prepare --check` step then runs without a database; your committed offline cache is the source of truth. `schema check` intentionally uses a live database because it verifies the committed schema contract against PostgreSQL.
|
|
587
|
+
|
|
588
|
+
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.
|
|
547
589
|
|
|
548
590
|
## Contributing
|
|
549
591
|
|
|
@@ -561,14 +603,16 @@ Releases are automated via `release-please`: pushes to `main` accumulate into a
|
|
|
561
603
|
`sqlx-js` is a young library. Known gaps:
|
|
562
604
|
|
|
563
605
|
- PostgreSQL only (no MySQL or SQLite).
|
|
606
|
+
- The scanner only follows direct named imports and namespace imports from `@onreza/sqlx-js`; it does not follow re-exports, dynamic aliases, or tagged-template calls.
|
|
564
607
|
- `INSERT INTO t VALUES (...)` without an explicit column list isn't parameter-mapped.
|
|
565
608
|
- `SELECT *` falls back to conservative nullability.
|
|
609
|
+
- Statements without a row description, such as `UPDATE ...` without `RETURNING`, are emitted with `row: never`, so the public return type is `Promise<never[]>`.
|
|
566
610
|
- Nested CTE references (CTE-`b` referencing CTE-`a` in the same `WITH`) and `WITH RECURSIVE` are not analysed transitively — at worst this produces extra `T | null`. Use `AS "id!"` overrides if needed.
|
|
567
611
|
- Column names whose **real** name (not an alias) ends with `!` or `?` are not supported — the runtime strips those suffixes assuming an override. Use `AS "alias"` if you have such a column.
|
|
568
612
|
- 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.
|
|
569
613
|
- The **internal** wire client (used by `migrate run`, `prepare`, and the runtime `migrate()` helper) reads `sslmode`, `sslrootcert`/`sslcert`/`sslkey`, `application_name`, `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`).
|
|
570
614
|
- `connect_timeout` bounds the entire internal-client connect, including the TLS handshake and SCRAM authentication.
|
|
571
|
-
- `sql.file(path)`
|
|
615
|
+
- `sql.file(path)` has a path-key mismatch to be aware of: prepare resolves the file relative to the source file, codegen keys it by root-relative resolved path, and runtime reads the literal path relative to `process.cwd()`. Keep a project convention and verify with `tsc` after `prepare`.
|
|
572
616
|
|
|
573
617
|
See [ROADMAP.md](./ROADMAP.md) for what's planned.
|
|
574
618
|
|
package/ROADMAP.md
CHANGED
|
@@ -6,7 +6,8 @@ Items already shipped live in the [README](./README.md) feature list; this file
|
|
|
6
6
|
|
|
7
7
|
| Feature | ROI | Notes |
|
|
8
8
|
|---------|-----|-------|
|
|
9
|
-
|
|
|
9
|
+
| pgschema integration hardening | 8 | Basic `init --schema-provider pgschema` plus managed `sqlx-js db install/check/plan/apply` exists. Continue with better docs, CI examples, schema snapshot handoff, and migration guidance for projects that outgrow built-in migrations. |
|
|
10
|
+
| 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. |
|
|
10
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. |
|
|
11
12
|
| 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. |
|
|
12
13
|
| `INSERT INTO t VALUES (...)` without column list | 3 | Map params by `pg_attribute attnum` ordering. Rare in practice — most teams use explicit column lists. |
|
package/dist/bin/sqlx-js.js
CHANGED
|
@@ -7,6 +7,8 @@ import { runWatch } from "../src/commands/watch.js";
|
|
|
7
7
|
import { migrateArchiveList, migrateArchiveRestore, migrateCheck, migrateDev, migrateRun, migrateInfo, migrateRevert, migrateAdd, migrateSquash, migrateVerify, } from "../src/commands/migrate.js";
|
|
8
8
|
import { applyShadowMigrations, runSchemaCheck, runSchemaDump } from "../src/commands/schema.js";
|
|
9
9
|
import { runInit } from "../src/commands/init.js";
|
|
10
|
+
import { runPgschemaCommand, runPgschemaInstall } from "../src/commands/pgschema.js";
|
|
11
|
+
import { loadConfig } from "../src/config.js";
|
|
10
12
|
function packageVersion() {
|
|
11
13
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
12
14
|
for (const path of [join(here, "../package.json"), join(here, "../../package.json")]) {
|
|
@@ -23,28 +25,29 @@ function help() {
|
|
|
23
25
|
console.error(`sqlx-js — compile-time-checked SQL for TypeScript + Postgres (v${VERSION})
|
|
24
26
|
|
|
25
27
|
usage:
|
|
26
|
-
sqlx-js init [--root <dir>]
|
|
28
|
+
sqlx-js init [--root <dir>] [--schema-provider builtin|pgschema]
|
|
29
|
+
sqlx-js db install | check | plan | apply [--root <dir>] [-- <pgschema args>]
|
|
27
30
|
sqlx-js prepare [--check | --watch] [--root <dir>] [--dts <path>] [--no-prune] [--shadow-url <url>]
|
|
28
|
-
sqlx-js migrate dev [--shadow-admin-url <url> | --shadow-url <url>] | verify [--shadow-admin-url <url> | --shadow-url <url>] | 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] | archive list | archive restore <name> [--force]
|
|
31
|
+
sqlx-js migrate dev [--shadow-admin-url <url> | --shadow-url <url>] [--lock-timeout <ms>] | verify [--shadow-admin-url <url> | --shadow-url <url>] [--lock-timeout <ms>] | 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
32
|
sqlx-js schema dump | check [--schema <path>] [--manifest <path>] [--no-manifest] [--shadow-url <url>]
|
|
30
33
|
sqlx-js --version
|
|
31
34
|
|
|
32
35
|
env:
|
|
33
|
-
DATABASE_URL=postgres://... (supports
|
|
36
|
+
DATABASE_URL=postgres://... (supports sslmode, cert paths, application_name, connect_timeout, statement_timeout)
|
|
34
37
|
SHADOW_DATABASE_URL=postgres://... (optional pre-created disposable shadow DB)
|
|
35
38
|
SHADOW_ADMIN_DATABASE_URL=postgres://... (optional admin URL for auto-created shadow DBs)
|
|
36
39
|
|
|
37
40
|
flags:
|
|
38
41
|
--root <dir> scan root (default: cwd)
|
|
39
42
|
--dts <path> declarations output (default: <root>/sqlx-js-env.d.ts)
|
|
40
|
-
--check offline mode:
|
|
43
|
+
--check offline mode: verify scanned queries exist in cache, no DB
|
|
41
44
|
--watch re-prepare on file change (persistent PG connection)
|
|
42
45
|
--no-prune keep orphaned cache entries (default: remove)
|
|
43
46
|
--migrations <dir> migrations directory (default: <root>/migrations)
|
|
44
47
|
--dry-run validate and print migrate run/revert plan without applying migrations
|
|
45
48
|
--json machine-readable output for migrate info/check and migrate run/revert --dry-run
|
|
46
49
|
--force allow archive restore to overwrite existing migration files
|
|
47
|
-
--lock-timeout <ms> advisory-lock acquisition timeout for migrate run/revert
|
|
50
|
+
--lock-timeout <ms> advisory-lock acquisition timeout for migrate run/revert/dev/verify/squash
|
|
48
51
|
--shadow-url <url> use an existing disposable shadow DB instead of auto-creating one
|
|
49
52
|
--shadow-admin-url <url> admin/maintenance DB URL used to auto-create shadow DBs
|
|
50
53
|
--replace archive replaced migrations after migrate squash writes the baseline
|
|
@@ -52,23 +55,26 @@ flags:
|
|
|
52
55
|
--schema <path> schema snapshot path (default: <root>/.sqlx-js/schema/schema.json)
|
|
53
56
|
--manifest <path> LLM schema manifest path (default: <root>/.sqlx-js/schema/schema.md)
|
|
54
57
|
--no-manifest skip writing the LLM schema manifest during schema dump
|
|
58
|
+
--schema-provider <name> init schema workflow: builtin (default) or pgschema
|
|
55
59
|
`);
|
|
56
60
|
process.exit(2);
|
|
57
61
|
}
|
|
62
|
+
const passthroughIndex = process.argv.indexOf("--");
|
|
63
|
+
const cliArgv = passthroughIndex >= 0 ? process.argv.slice(0, passthroughIndex) : process.argv;
|
|
64
|
+
const passthroughArgs = passthroughIndex >= 0 ? process.argv.slice(passthroughIndex + 1) : [];
|
|
58
65
|
function arg(name, def) {
|
|
59
|
-
const argv = process.argv;
|
|
60
66
|
const eq = `${name}=`;
|
|
61
|
-
for (let i = 0; i <
|
|
62
|
-
const a =
|
|
67
|
+
for (let i = 0; i < cliArgv.length; i++) {
|
|
68
|
+
const a = cliArgv[i];
|
|
63
69
|
if (a === name)
|
|
64
|
-
return
|
|
70
|
+
return cliArgv[i + 1] ?? def;
|
|
65
71
|
if (a.startsWith(eq))
|
|
66
72
|
return a.slice(eq.length);
|
|
67
73
|
}
|
|
68
74
|
return def;
|
|
69
75
|
}
|
|
70
76
|
function flag(name) {
|
|
71
|
-
for (const a of
|
|
77
|
+
for (const a of cliArgv) {
|
|
72
78
|
if (a === name)
|
|
73
79
|
return true;
|
|
74
80
|
}
|
|
@@ -96,7 +102,41 @@ const schemaPath = schemaArg ? resolve(schemaArg) : join(root, ".sqlx-js/schema/
|
|
|
96
102
|
const manifestArg = arg("--manifest");
|
|
97
103
|
const manifestPath = manifestArg ? resolve(manifestArg) : join(root, ".sqlx-js/schema/schema.md");
|
|
98
104
|
if (cmd === "init") {
|
|
99
|
-
|
|
105
|
+
const provider = arg("--schema-provider", "builtin");
|
|
106
|
+
if (provider !== "builtin" && provider !== "pgschema") {
|
|
107
|
+
console.error("--schema-provider must be builtin or pgschema");
|
|
108
|
+
process.exit(2);
|
|
109
|
+
}
|
|
110
|
+
runInit({ root, schemaProvider: provider });
|
|
111
|
+
}
|
|
112
|
+
else if (cmd === "db") {
|
|
113
|
+
const sub = process.argv[3];
|
|
114
|
+
if (sub === "install") {
|
|
115
|
+
try {
|
|
116
|
+
await runPgschemaInstall({ root });
|
|
117
|
+
}
|
|
118
|
+
catch (e) {
|
|
119
|
+
console.error(e.message);
|
|
120
|
+
process.exit(2);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
if (sub !== "check" && sub !== "plan" && sub !== "apply")
|
|
125
|
+
help();
|
|
126
|
+
try {
|
|
127
|
+
runPgschemaCommand({
|
|
128
|
+
root,
|
|
129
|
+
databaseUrl,
|
|
130
|
+
config: await loadConfig(root),
|
|
131
|
+
subcommand: sub,
|
|
132
|
+
passthrough: passthroughArgs,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
catch (e) {
|
|
136
|
+
console.error(e.message);
|
|
137
|
+
process.exit(2);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
100
140
|
}
|
|
101
141
|
else if (cmd === "prepare") {
|
|
102
142
|
if (flag("--check") && shadowUrlArg) {
|
package/dist/src/cache.d.ts
CHANGED
package/dist/src/cache.js
CHANGED
|
@@ -2,9 +2,136 @@ import { createHash, 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
|
export function fingerprint(query) {
|
|
5
|
-
const norm = query
|
|
5
|
+
const norm = normalizeForFingerprint(query);
|
|
6
6
|
return createHash("sha256").update(norm).digest("hex").slice(0, 16);
|
|
7
7
|
}
|
|
8
|
+
function normalizeForFingerprint(query) {
|
|
9
|
+
let out = "";
|
|
10
|
+
let pendingSpace = false;
|
|
11
|
+
let i = 0;
|
|
12
|
+
const emit = (text) => {
|
|
13
|
+
if (pendingSpace && out.length > 0)
|
|
14
|
+
out += " ";
|
|
15
|
+
out += text;
|
|
16
|
+
pendingSpace = false;
|
|
17
|
+
};
|
|
18
|
+
const markSpace = () => {
|
|
19
|
+
if (out.length > 0)
|
|
20
|
+
pendingSpace = true;
|
|
21
|
+
};
|
|
22
|
+
while (i < query.length) {
|
|
23
|
+
const ch = query[i];
|
|
24
|
+
if (/\s/.test(ch)) {
|
|
25
|
+
markSpace();
|
|
26
|
+
i++;
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (ch === "-" && query[i + 1] === "-") {
|
|
30
|
+
i = readLineComment(query, i);
|
|
31
|
+
markSpace();
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (ch === "/" && query[i + 1] === "*") {
|
|
35
|
+
i = readBlockComment(query, i);
|
|
36
|
+
markSpace();
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (ch === "'") {
|
|
40
|
+
const next = readSingleQuoted(query, i, isEscapeStringPrefix(query, i));
|
|
41
|
+
emit(query.slice(i, next));
|
|
42
|
+
i = next;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (ch === "\"") {
|
|
46
|
+
const next = readQuotedIdentifier(query, i);
|
|
47
|
+
emit(query.slice(i, next));
|
|
48
|
+
i = next;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (ch === "$") {
|
|
52
|
+
const next = readDollarQuoted(query, i);
|
|
53
|
+
if (next !== null) {
|
|
54
|
+
emit(query.slice(i, next));
|
|
55
|
+
i = next;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
emit(ch);
|
|
60
|
+
i++;
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
function readSingleQuoted(query, start, escapeBackslash) {
|
|
65
|
+
let i = start + 1;
|
|
66
|
+
while (i < query.length) {
|
|
67
|
+
const ch = query[i];
|
|
68
|
+
if (escapeBackslash && ch === "\\") {
|
|
69
|
+
i += 2;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (ch === "'") {
|
|
73
|
+
if (query[i + 1] === "'") {
|
|
74
|
+
i += 2;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
return i + 1;
|
|
78
|
+
}
|
|
79
|
+
i++;
|
|
80
|
+
}
|
|
81
|
+
return query.length;
|
|
82
|
+
}
|
|
83
|
+
function readQuotedIdentifier(query, start) {
|
|
84
|
+
let i = start + 1;
|
|
85
|
+
while (i < query.length) {
|
|
86
|
+
if (query[i] === "\"") {
|
|
87
|
+
if (query[i + 1] === "\"") {
|
|
88
|
+
i += 2;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
return i + 1;
|
|
92
|
+
}
|
|
93
|
+
i++;
|
|
94
|
+
}
|
|
95
|
+
return query.length;
|
|
96
|
+
}
|
|
97
|
+
function readDollarQuoted(query, start) {
|
|
98
|
+
let tagEnd = start + 1;
|
|
99
|
+
while (tagEnd < query.length && /[A-Za-z0-9_]/.test(query[tagEnd]))
|
|
100
|
+
tagEnd++;
|
|
101
|
+
if (query[tagEnd] !== "$")
|
|
102
|
+
return null;
|
|
103
|
+
const tag = query.slice(start, tagEnd + 1);
|
|
104
|
+
const end = query.indexOf(tag, tagEnd + 1);
|
|
105
|
+
return end === -1 ? query.length : end + tag.length;
|
|
106
|
+
}
|
|
107
|
+
function readLineComment(query, start) {
|
|
108
|
+
const end = query.indexOf("\n", start + 2);
|
|
109
|
+
return end === -1 ? query.length : end + 1;
|
|
110
|
+
}
|
|
111
|
+
function readBlockComment(query, start) {
|
|
112
|
+
let depth = 1;
|
|
113
|
+
let i = start + 2;
|
|
114
|
+
while (i < query.length && depth > 0) {
|
|
115
|
+
if (query[i] === "/" && query[i + 1] === "*") {
|
|
116
|
+
depth++;
|
|
117
|
+
i += 2;
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (query[i] === "*" && query[i + 1] === "/") {
|
|
121
|
+
depth--;
|
|
122
|
+
i += 2;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
i++;
|
|
126
|
+
}
|
|
127
|
+
return i;
|
|
128
|
+
}
|
|
129
|
+
function isEscapeStringPrefix(query, quoteIndex) {
|
|
130
|
+
if (quoteIndex === 0 || query[quoteIndex - 1]?.toLowerCase() !== "e")
|
|
131
|
+
return false;
|
|
132
|
+
const beforePrefix = query[quoteIndex - 2];
|
|
133
|
+
return beforePrefix === undefined || !/[A-Za-z0-9_$]/.test(beforePrefix);
|
|
134
|
+
}
|
|
8
135
|
export function effectiveNullable(c) {
|
|
9
136
|
if (c.override === "non-null")
|
|
10
137
|
return false;
|
package/dist/src/codegen.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import { type CacheEntry } from "./cache.js";
|
|
2
|
-
|
|
2
|
+
import type { FunctionEntry } from "./function-cache.js";
|
|
3
|
+
export declare function emitDts(outPath: string, entries: CacheEntry[], functions?: FunctionEntry[]): void;
|