@onreza/sqlx-js 0.5.0 → 0.7.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.
@@ -2,13 +2,7 @@
2
2
  import { existsSync, readFileSync } from "node:fs";
3
3
  import { dirname, join, resolve } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
- import { PrepareFatalError, runPrepare, } from "../src/commands/prepare.js";
6
- import { runWatch } from "../src/commands/watch.js";
7
- import { migrateArchiveList, migrateArchiveRestore, migrateCheck, migrateDev, migrateRun, migrateInfo, migrateRevert, migrateAdd, migrateSquash, migrateVerify, } from "../src/commands/migrate.js";
8
- import { applyShadowMigrations, runSchemaCheck, runSchemaDump } from "../src/commands/schema.js";
9
- import { runInit } from "../src/commands/init.js";
10
- import { runPgschemaCommand, runPgschemaInstall } from "../src/commands/pgschema.js";
11
- import { runDoctor } from "../src/commands/doctor.js";
5
+ import { parseArgs } from "node:util";
12
6
  import { assertSupportedRuntime, loadConfig, loadRootEnv } from "../src/config.js";
13
7
  function packageVersion() {
14
8
  const here = dirname(fileURLToPath(import.meta.url));
@@ -22,33 +16,39 @@ function packageVersion() {
22
16
  throw new Error("sqlx-js: cannot locate package.json for version");
23
17
  }
24
18
  const VERSION = packageVersion();
25
- function help() {
26
- console.error(`sqlx-js — compile-time-checked SQL for TypeScript + Postgres (v${VERSION})
19
+ const HELP = {
20
+ root: `sqlx-js — compile-time-checked SQL for TypeScript + Postgres (v${VERSION})
27
21
 
28
22
  usage:
29
23
  sqlx-js init [--root <dir>] [--schema-provider builtin|pgschema]
30
- sqlx-js doctor [--root <dir>] [--json]
31
- sqlx-js db install | check | plan | apply [--root <dir>] [-- <pgschema args>]
32
- sqlx-js prepare [--check | --verify | --watch] [--json] [--root <dir>] [--dts <path>] [--no-prune] [--shadow-url <url>]
33
- 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]
34
- sqlx-js schema dump | check [--schema <path>] [--manifest <path>] [--no-manifest] [--shadow-url <url>]
24
+ sqlx-js doctor [--root <dir>] [--dts <path>] [--json]
25
+ sqlx-js db install | check [--root <dir>]
26
+ sqlx-js db plan | apply [--root <dir>] [-- <pgschema args>]
27
+ 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 schema dump [--schema <path>] [--manifest <path>] [--no-manifest] [--shadow-url <url>]
30
+ sqlx-js schema check [--schema <path>] [--shadow-url <url>]
35
31
  sqlx-js --version
32
+ sqlx-js-diagnostics github|unix < prepare-diagnostics.json
36
33
 
37
34
  env:
38
- DATABASE_URL=postgres://... (supports sslmode, cert paths, application_name, connect_timeout, statement_timeout)
35
+ DATABASE_URL=postgres://... (supports sslmode, cert paths, application_name, options, connect_timeout, statement_timeout)
39
36
  SHADOW_DATABASE_URL=postgres://... (optional pre-created disposable shadow DB)
40
37
  SHADOW_ADMIN_DATABASE_URL=postgres://... (optional admin URL for auto-created shadow DBs)
41
38
 
42
39
  flags:
43
40
  --root <dir> scan root (default: cwd)
44
41
  --dts <path> declarations output (default: <root>/sqlx-js-env.d.ts)
45
- --check offline mode: verify scanned queries exist in cache, no DB
42
+ --check read-only offline verification of cache and declarations
43
+ --offline regenerate declarations from committed cache, no DB
46
44
  --verify prepare against DB/shadow and compare committed generated artifacts
47
45
  --watch re-prepare on file change (persistent PG connection)
48
46
  --no-prune keep orphaned cache entries (default: remove)
49
47
  --migrations <dir> migrations directory (default: <root>/migrations)
50
48
  --dry-run validate and print migrate run/revert plan without applying migrations
51
49
  --json machine-readable output for prepare and migration inspection/dry-run commands
50
+ --jsonl streaming machine-readable output for prepare --watch
51
+ --strict-inference fail when query inference degrades or emits unknown types
52
52
  --force allow archive restore to overwrite existing migration files
53
53
  --lock-timeout <ms> advisory-lock acquisition timeout for migrate run/revert/dev/verify/squash
54
54
  --shadow-url <url> use an existing disposable shadow DB instead of auto-creating one
@@ -59,38 +59,210 @@ flags:
59
59
  --manifest <path> LLM schema manifest path (default: <root>/.sqlx-js/schema/schema.md)
60
60
  --no-manifest skip writing the LLM schema manifest during schema dump
61
61
  --schema-provider <name> init schema workflow: builtin (default) or pgschema
62
- `);
63
- process.exit(2);
62
+ `,
63
+ init: `usage: sqlx-js init [--root <dir>] [--schema-provider builtin|pgschema]`,
64
+ doctor: `usage: sqlx-js doctor [--root <dir>] [--dts <path>] [--json]`,
65
+ db: `usage: sqlx-js db install | check [--root <dir>] | plan | apply [--root <dir>] [-- <pgschema args>]`,
66
+ 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]`,
68
+ schema: `usage: sqlx-js schema dump [--schema <path>] [--manifest <path>] [--no-manifest] [--shadow-url <url>] | check [--schema <path>] [--shadow-url <url>]`,
69
+ };
70
+ function printHelp(scope, error = false) {
71
+ (error ? console.error : console.log)(HELP[scope]);
64
72
  }
65
- const passthroughIndex = process.argv.indexOf("--");
66
- const cliArgv = passthroughIndex >= 0 ? process.argv.slice(0, passthroughIndex) : process.argv;
67
- const passthroughArgs = passthroughIndex >= 0 ? process.argv.slice(passthroughIndex + 1) : [];
68
- function arg(name, def) {
69
- const eq = `${name}=`;
70
- for (let i = 0; i < cliArgv.length; i++) {
71
- const a = cliArgv[i];
72
- if (a === name)
73
- return cliArgv[i + 1] ?? def;
74
- if (a.startsWith(eq))
75
- return a.slice(eq.length);
76
- }
77
- return def;
73
+ function exitHelp(scope) {
74
+ printHelp(scope);
75
+ process.exit(0);
78
76
  }
79
- function flag(name) {
80
- for (const a of cliArgv) {
81
- if (a === name)
82
- return true;
83
- }
84
- return false;
77
+ function usageError(message, scope = "root") {
78
+ console.error(`sqlx-js: ${message}`);
79
+ printHelp(scope, true);
80
+ process.exit(2);
85
81
  }
86
- const cmd = process.argv[2];
82
+ const rawArgv = process.argv.slice(2);
83
+ const passthroughIndex = rawArgv.indexOf("--");
84
+ const cliArgv = passthroughIndex >= 0 ? rawArgv.slice(0, passthroughIndex) : rawArgv;
85
+ const passthroughArgs = passthroughIndex >= 0 ? rawArgv.slice(passthroughIndex + 1) : [];
86
+ const cmd = cliArgv[0];
87
+ const scopes = new Set(["init", "doctor", "db", "prepare", "migrate", "schema"]);
87
88
  if (cmd === "--version" || cmd === "-v") {
88
89
  console.log(VERSION);
89
90
  process.exit(0);
90
91
  }
91
- if (cmd === "--help" || cmd === "-h" || !cmd) {
92
- help();
92
+ if (!cmd || cmd === "--help" || cmd === "-h")
93
+ exitHelp("root");
94
+ if (!scopes.has(cmd))
95
+ usageError(`unknown command ${JSON.stringify(cmd)}`);
96
+ const scope = cmd;
97
+ if (cliArgv.includes("--help") || cliArgv.includes("-h"))
98
+ exitHelp(scope);
99
+ if (passthroughIndex >= 0 && cmd !== "db")
100
+ usageError("arguments after -- are only supported by sqlx-js db", scope);
101
+ const ROOT_OPTIONS = {
102
+ root: { type: "string" },
103
+ help: { type: "boolean", short: "h" },
104
+ };
105
+ function optionsFor(command, subcommand) {
106
+ if (command === "init")
107
+ return { ...ROOT_OPTIONS, "schema-provider": { type: "string" } };
108
+ if (command === "doctor")
109
+ return { ...ROOT_OPTIONS, dts: { type: "string" }, json: { type: "boolean" } };
110
+ if (command === "db")
111
+ return ROOT_OPTIONS;
112
+ if (command === "prepare") {
113
+ return {
114
+ ...ROOT_OPTIONS,
115
+ dts: { type: "string" },
116
+ check: { type: "boolean" },
117
+ offline: { type: "boolean" },
118
+ verify: { type: "boolean" },
119
+ watch: { type: "boolean" },
120
+ json: { type: "boolean" },
121
+ jsonl: { type: "boolean" },
122
+ "no-prune": { type: "boolean" },
123
+ "shadow-url": { type: "string" },
124
+ migrations: { type: "string" },
125
+ "strict-inference": { type: "boolean" },
126
+ };
127
+ }
128
+ if (command === "schema") {
129
+ const common = {
130
+ ...ROOT_OPTIONS,
131
+ schema: { type: "string" },
132
+ "shadow-url": { type: "string" },
133
+ migrations: { type: "string" },
134
+ };
135
+ return subcommand === "dump"
136
+ ? { ...common, manifest: { type: "string" }, "no-manifest": { type: "boolean" } }
137
+ : common;
138
+ }
139
+ const common = { ...ROOT_OPTIONS, migrations: { type: "string" } };
140
+ if (subcommand === "run") {
141
+ return { ...common, "dry-run": { type: "boolean" }, json: { type: "boolean" }, "lock-timeout": { type: "string" } };
142
+ }
143
+ if (subcommand === "info" || subcommand === "check")
144
+ return { ...common, json: { type: "boolean" } };
145
+ if (subcommand === "dev") {
146
+ return {
147
+ ...common,
148
+ "shadow-admin-url": { type: "string" },
149
+ "shadow-url": { type: "string" },
150
+ "lock-timeout": { type: "string" },
151
+ "no-prune": { type: "boolean" },
152
+ "strict-inference": { type: "boolean" },
153
+ };
154
+ }
155
+ if (subcommand === "verify") {
156
+ return {
157
+ ...common,
158
+ "shadow-admin-url": { type: "string" },
159
+ "shadow-url": { type: "string" },
160
+ "lock-timeout": { type: "string" },
161
+ "strict-inference": { type: "boolean" },
162
+ };
163
+ }
164
+ if (subcommand === "revert") {
165
+ return {
166
+ ...common,
167
+ "dry-run": { type: "boolean" },
168
+ json: { type: "boolean" },
169
+ "shadow-admin-url": { type: "string" },
170
+ "shadow-url": { type: "string" },
171
+ "lock-timeout": { type: "string" },
172
+ };
173
+ }
174
+ if (subcommand === "squash") {
175
+ return {
176
+ ...common,
177
+ "shadow-admin-url": { type: "string" },
178
+ "shadow-url": { type: "string" },
179
+ "lock-timeout": { type: "string" },
180
+ replace: { type: "boolean" },
181
+ "pg-dump": { type: "string" },
182
+ };
183
+ }
184
+ if (subcommand === "archive")
185
+ return { ...common, force: { type: "boolean" } };
186
+ return common;
187
+ }
188
+ const commandArgv = cliArgv.slice(1);
189
+ const subcommand = commandArgv[0]?.startsWith("-") ? undefined : commandArgv[0];
190
+ let parsed;
191
+ try {
192
+ parsed = parseArgs({
193
+ args: commandArgv,
194
+ options: optionsFor(cmd, subcommand),
195
+ strict: true,
196
+ allowPositionals: true,
197
+ });
198
+ }
199
+ catch (error) {
200
+ usageError(error.message, scope);
201
+ }
202
+ const values = parsed.values;
203
+ const positionals = parsed.positionals;
204
+ function arg(name, def) {
205
+ const value = values[name.replace(/^--/, "")];
206
+ return typeof value === "string" ? value : def;
207
+ }
208
+ function flag(name) {
209
+ return values[name.replace(/^--/, "")] === true;
210
+ }
211
+ function requirePositionals(min, max, label) {
212
+ if (positionals.length < min || positionals.length > max) {
213
+ usageError(`${label}: expected ${min === max ? min : `${min} to ${max}`} positional argument(s)`, scope);
214
+ }
215
+ }
216
+ function validateInvocation() {
217
+ if (cmd === "init" || cmd === "doctor" || cmd === "prepare") {
218
+ requirePositionals(0, 0, cmd);
219
+ return;
220
+ }
221
+ if (cmd === "db") {
222
+ requirePositionals(1, 1, "db");
223
+ const sub = positionals[0];
224
+ if (sub !== "install" && sub !== "check" && sub !== "plan" && sub !== "apply") {
225
+ usageError(`unknown db command ${JSON.stringify(sub)}`, "db");
226
+ }
227
+ if (passthroughArgs.length > 0 && sub !== "plan" && sub !== "apply") {
228
+ usageError(`db ${sub} does not accept arguments after --`, "db");
229
+ }
230
+ return;
231
+ }
232
+ if (cmd === "schema") {
233
+ requirePositionals(1, 1, "schema");
234
+ const sub = positionals[0];
235
+ if (sub !== "dump" && sub !== "check")
236
+ usageError(`unknown schema command ${JSON.stringify(sub)}`, "schema");
237
+ return;
238
+ }
239
+ const sub = positionals[0];
240
+ if (!sub)
241
+ usageError("migrate command is required", "migrate");
242
+ if (["dev", "verify", "run", "info", "check", "revert"].includes(sub)) {
243
+ requirePositionals(1, 1, `migrate ${sub}`);
244
+ return;
245
+ }
246
+ if (sub === "add" || sub === "squash") {
247
+ requirePositionals(2, 2, `migrate ${sub}`);
248
+ return;
249
+ }
250
+ if (sub === "archive") {
251
+ const action = positionals[1];
252
+ if (action === "list") {
253
+ requirePositionals(2, 2, "migrate archive list");
254
+ if (flag("--force"))
255
+ usageError("--force is only supported by migrate archive restore", "migrate");
256
+ }
257
+ else if (action === "restore")
258
+ requirePositionals(3, 3, "migrate archive restore");
259
+ else
260
+ usageError(`unknown migrate archive command ${JSON.stringify(action)}`, "migrate");
261
+ return;
262
+ }
263
+ usageError(`unknown migrate command ${JSON.stringify(sub)}`, "migrate");
93
264
  }
265
+ validateInvocation();
94
266
  const root = resolve(arg("--root", process.cwd()));
95
267
  if (cmd !== "doctor") {
96
268
  try {
@@ -102,14 +274,21 @@ if (cmd !== "doctor") {
102
274
  }
103
275
  }
104
276
  let envError;
105
- try {
106
- loadRootEnv(root);
107
- }
108
- catch (e) {
109
- envError = e.message;
110
- if (cmd !== "doctor") {
111
- console.error(envError);
112
- process.exit(2);
277
+ const needsEnvironment = cmd === "doctor" ||
278
+ cmd === "schema" ||
279
+ (cmd === "db" && (positionals[0] === "plan" || positionals[0] === "apply")) ||
280
+ (cmd === "prepare" && !flag("--check") && !flag("--offline")) ||
281
+ (cmd === "migrate" && !["add", "check", "archive"].includes(positionals[0]));
282
+ if (needsEnvironment) {
283
+ try {
284
+ loadRootEnv(root);
285
+ }
286
+ catch (e) {
287
+ envError = e.message;
288
+ if (cmd !== "doctor") {
289
+ console.error(envError);
290
+ process.exit(2);
291
+ }
113
292
  }
114
293
  }
115
294
  const databaseUrl = process.env.DATABASE_URL ?? "";
@@ -118,13 +297,14 @@ const shadowUrl = shadowUrlArg ?? process.env.SHADOW_DATABASE_URL;
118
297
  const shadowAdminUrl = arg("--shadow-admin-url") ?? process.env.SHADOW_ADMIN_DATABASE_URL;
119
298
  const cacheDir = join(root, ".sqlx-js");
120
299
  const dtsArg = arg("--dts");
121
- const dtsPath = dtsArg ? resolve(dtsArg) : join(root, "sqlx-js-env.d.ts");
300
+ const dtsPath = dtsArg ? resolve(root, dtsArg) : join(root, "sqlx-js-env.d.ts");
122
301
  const migrationsDir = join(root, arg("--migrations", "migrations"));
123
302
  const schemaArg = arg("--schema");
124
- const schemaPath = schemaArg ? resolve(schemaArg) : join(root, ".sqlx-js/schema/schema.json");
303
+ const schemaPath = schemaArg ? resolve(root, schemaArg) : join(root, ".sqlx-js/schema/schema.json");
125
304
  const manifestArg = arg("--manifest");
126
- const manifestPath = manifestArg ? resolve(manifestArg) : join(root, ".sqlx-js/schema/schema.md");
305
+ const manifestPath = manifestArg ? resolve(root, manifestArg) : join(root, ".sqlx-js/schema/schema.md");
127
306
  if (cmd === "init") {
307
+ const { runInit } = await import("../src/commands/init.js");
128
308
  const provider = arg("--schema-provider", "builtin");
129
309
  if (provider !== "builtin" && provider !== "pgschema") {
130
310
  console.error("--schema-provider must be builtin or pgschema");
@@ -133,10 +313,12 @@ if (cmd === "init") {
133
313
  runInit({ root, schemaProvider: provider });
134
314
  }
135
315
  else if (cmd === "doctor") {
316
+ const { runDoctor } = await import("../src/commands/doctor.js");
136
317
  await runDoctor({ root, databaseUrl, cacheDir, dtsPath, json: flag("--json"), envError });
137
318
  }
138
319
  else if (cmd === "db") {
139
- const sub = process.argv[3];
320
+ const { runPgschemaCommand, runPgschemaInstall } = await import("../src/commands/pgschema.js");
321
+ const sub = positionals[0];
140
322
  if (sub === "install") {
141
323
  try {
142
324
  await runPgschemaInstall({ root });
@@ -147,8 +329,6 @@ else if (cmd === "db") {
147
329
  }
148
330
  }
149
331
  else {
150
- if (sub !== "check" && sub !== "plan" && sub !== "apply")
151
- help();
152
332
  try {
153
333
  runPgschemaCommand({
154
334
  root,
@@ -165,13 +345,24 @@ else if (cmd === "db") {
165
345
  }
166
346
  }
167
347
  else if (cmd === "prepare") {
348
+ const { PrepareFatalError, runPrepare } = await import("../src/commands/prepare.js");
168
349
  const prepareCheck = flag("--check");
350
+ const prepareOffline = flag("--offline");
169
351
  const prepareVerify = flag("--verify");
170
352
  const prepareWatch = flag("--watch");
171
353
  const prepareJson = flag("--json");
172
- const prepareMode = prepareVerify ? "verify" : prepareCheck ? "check" : "prepare";
354
+ const prepareJsonl = flag("--jsonl");
355
+ const prepareMode = prepareVerify ? "verify" : prepareCheck ? "check" : prepareOffline ? "offline" : "prepare";
173
356
  const failPrepare = (message, phase, exitCode = 2, location = {}) => {
174
- if (prepareJson) {
357
+ if (prepareJsonl) {
358
+ console.log(JSON.stringify({
359
+ formatVersion: 1,
360
+ event: "error",
361
+ timestamp: new Date().toISOString(),
362
+ diagnostic: { severity: "error", phase, message, ...location },
363
+ }));
364
+ }
365
+ else if (prepareJson) {
175
366
  console.log(JSON.stringify({
176
367
  formatVersion: 1,
177
368
  ok: false,
@@ -189,19 +380,28 @@ else if (cmd === "prepare") {
189
380
  }
190
381
  process.exit(exitCode);
191
382
  };
192
- if ([prepareCheck, prepareVerify, prepareWatch].filter(Boolean).length > 1) {
193
- failPrepare("--check, --verify, and --watch are mutually exclusive", "config");
383
+ if ([prepareCheck, prepareOffline, prepareVerify, prepareWatch].filter(Boolean).length > 1) {
384
+ failPrepare("--check, --offline, --verify, and --watch are mutually exclusive", "config");
194
385
  }
195
- if (prepareCheck && shadowUrlArg) {
196
- failPrepare("--shadow-url cannot be used with prepare --check; use live prepare or schema check --shadow-url", "config");
386
+ if ((prepareCheck || prepareOffline) && shadowUrlArg) {
387
+ failPrepare("--shadow-url cannot be used with offline prepare modes; use live prepare or schema check --shadow-url", "config");
197
388
  }
198
389
  if (prepareWatch && prepareJson) {
199
390
  failPrepare("--watch and --json are mutually exclusive", "config");
200
391
  }
201
- const prepareShadowUrl = prepareCheck ? undefined : shadowUrl;
392
+ if (prepareJson && prepareJsonl) {
393
+ failPrepare("--json and --jsonl are mutually exclusive", "config");
394
+ }
395
+ if (prepareJsonl && !prepareWatch) {
396
+ failPrepare("--jsonl is only supported by prepare --watch", "config");
397
+ }
398
+ if ((prepareCheck || prepareOffline || prepareVerify) && flag("--no-prune")) {
399
+ failPrepare("--no-prune is only supported by live prepare and prepare --watch", "config");
400
+ }
401
+ const prepareShadowUrl = prepareCheck || prepareOffline ? undefined : shadowUrl;
202
402
  const prepareDatabaseUrl = prepareShadowUrl ?? databaseUrl;
203
- if (!prepareCheck && !prepareDatabaseUrl) {
204
- failPrepare("DATABASE_URL is required for prepare (use --check for offline)", "connect");
403
+ if (!prepareCheck && !prepareOffline && !prepareDatabaseUrl) {
404
+ failPrepare("DATABASE_URL is required for prepare (use --check or --offline without a database)", "connect");
205
405
  }
206
406
  const opts = {
207
407
  root,
@@ -209,13 +409,20 @@ else if (cmd === "prepare") {
209
409
  cacheDir,
210
410
  dtsPath,
211
411
  check: prepareCheck,
412
+ offline: prepareOffline,
212
413
  verify: prepareVerify,
213
414
  json: prepareJson,
214
415
  prune: !flag("--no-prune"),
416
+ strictInference: flag("--strict-inference"),
215
417
  };
418
+ const applyShadowMigrations = prepareShadowUrl
419
+ ? (await import("../src/commands/schema.js")).applyShadowMigrations
420
+ : undefined;
216
421
  if (prepareWatch) {
422
+ const { runWatch } = await import("../src/commands/watch.js");
217
423
  await runWatch({
218
424
  ...opts,
425
+ jsonl: prepareJsonl,
219
426
  ...(prepareShadowUrl
220
427
  ? {
221
428
  beforePrepare: async () => {
@@ -252,7 +459,8 @@ else if (cmd === "prepare") {
252
459
  }
253
460
  }
254
461
  else if (cmd === "schema") {
255
- const sub = process.argv[3];
462
+ const { runSchemaCheck, runSchemaDump } = await import("../src/commands/schema.js");
463
+ const sub = positionals[0];
256
464
  const schemaDatabaseUrl = shadowUrl ?? databaseUrl;
257
465
  if (!schemaDatabaseUrl) {
258
466
  console.error("DATABASE_URL is required for schema commands (or pass --shadow-url)");
@@ -270,19 +478,21 @@ else if (cmd === "schema") {
270
478
  await runSchemaDump(opts);
271
479
  else if (sub === "check")
272
480
  await runSchemaCheck(opts);
273
- else
274
- help();
275
481
  }
276
482
  else if (cmd === "migrate") {
277
- const sub = process.argv[3];
483
+ const { migrateArchiveList, migrateArchiveRestore, migrateCheck, migrateDev, migrateRun, migrateInfo, migrateRevert, migrateAdd, migrateSquash, migrateVerify, } = await import("../src/commands/migrate.js");
484
+ const sub = positionals[0];
278
485
  const revertDryRun = sub === "revert" && flag("--dry-run");
279
486
  const workflowShadowOnly = ((sub === "dev" || sub === "verify" || sub === "squash") && !!shadowUrl) || (revertDryRun && !!shadowUrl);
280
- if (!databaseUrl && sub !== "add" && sub !== "check" && sub !== "squash" && sub !== "archive" && !workflowShadowOnly) {
487
+ if (!databaseUrl && sub !== "add" && sub !== "check" && sub !== "archive" && !workflowShadowOnly) {
281
488
  console.error("DATABASE_URL is required");
282
489
  process.exit(2);
283
490
  }
284
491
  const tRaw = arg("--lock-timeout");
285
492
  const lockTimeoutMs = tRaw ? Number(tRaw) : undefined;
493
+ if (lockTimeoutMs !== undefined && !Number.isFinite(lockTimeoutMs)) {
494
+ usageError("--lock-timeout must be a finite number of milliseconds", "migrate");
495
+ }
286
496
  if (sub === "dev") {
287
497
  await migrateDev({
288
498
  root,
@@ -294,6 +504,7 @@ else if (cmd === "migrate") {
294
504
  shadowUrl,
295
505
  shadowAdminUrl,
296
506
  lockTimeoutMs,
507
+ strictInference: flag("--strict-inference"),
297
508
  });
298
509
  }
299
510
  else if (sub === "verify") {
@@ -306,15 +517,18 @@ else if (cmd === "migrate") {
306
517
  shadowUrl,
307
518
  shadowAdminUrl,
308
519
  lockTimeoutMs,
520
+ strictInference: flag("--strict-inference"),
309
521
  });
310
522
  }
311
523
  else if (sub === "run") {
312
524
  await migrateRun({ databaseUrl, migrationsDir, lockTimeoutMs, dryRun: flag("--dry-run"), json: flag("--json") });
313
525
  }
314
- else if (sub === "info")
526
+ else if (sub === "info") {
315
527
  await migrateInfo({ databaseUrl, migrationsDir, json: flag("--json") });
316
- else if (sub === "check")
528
+ }
529
+ else if (sub === "check") {
317
530
  migrateCheck({ migrationsDir, json: flag("--json") });
531
+ }
318
532
  else if (sub === "revert") {
319
533
  await migrateRevert({
320
534
  databaseUrl,
@@ -327,19 +541,11 @@ else if (cmd === "migrate") {
327
541
  });
328
542
  }
329
543
  else if (sub === "add") {
330
- const name = process.argv[4];
331
- if (!name) {
332
- console.error("migrate add: name required");
333
- process.exit(2);
334
- }
544
+ const name = positionals[1];
335
545
  migrateAdd({ databaseUrl, migrationsDir, name });
336
546
  }
337
547
  else if (sub === "squash") {
338
- const name = process.argv[4];
339
- if (!name) {
340
- console.error("migrate squash: name required");
341
- process.exit(2);
342
- }
548
+ const name = positionals[1];
343
549
  await migrateSquash({
344
550
  databaseUrl,
345
551
  migrationsDir,
@@ -352,23 +558,16 @@ else if (cmd === "migrate") {
352
558
  });
353
559
  }
354
560
  else if (sub === "archive") {
355
- const action = process.argv[4];
356
- if (action === "list")
561
+ const action = positionals[1];
562
+ if (action === "list") {
357
563
  migrateArchiveList({ migrationsDir });
564
+ }
358
565
  else if (action === "restore") {
359
- const name = process.argv[5];
360
- if (!name) {
361
- console.error("migrate archive restore: name required");
362
- process.exit(2);
363
- }
566
+ const name = positionals[2];
364
567
  migrateArchiveRestore({ migrationsDir, name, force: flag("--force") });
365
568
  }
366
- else
367
- help();
368
569
  }
369
- else
370
- help();
371
570
  }
372
571
  else {
373
- help();
572
+ usageError(`unknown command ${JSON.stringify(cmd)}`);
374
573
  }
@@ -1,5 +1,5 @@
1
1
  export declare const CACHE_FORMAT_VERSION = 2;
2
- export declare const GENERATOR_REVISION = 3;
2
+ export declare const GENERATOR_REVISION = 4;
3
3
  export declare const CACHE_MANIFEST_FILE = "cache-manifest.json";
4
4
  export type CacheManifest = {
5
5
  cacheFormat: typeof CACHE_FORMAT_VERSION;
package/dist/src/cache.js CHANGED
@@ -3,7 +3,7 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync, unlink
3
3
  import { join } from "node:path";
4
4
  import { isBuiltinOid } from "./pg/oids.js";
5
5
  export const CACHE_FORMAT_VERSION = 2;
6
- export const GENERATOR_REVISION = 3;
6
+ export const GENERATOR_REVISION = 4;
7
7
  export const CACHE_MANIFEST_FILE = "cache-manifest.json";
8
8
  export function portableCacheOid(oid) {
9
9
  return isBuiltinOid(oid) ? oid : 0;
@@ -23,7 +23,9 @@ export function emitDts(outPath, entries, functions = []) {
23
23
  lines.push("// AUTO-GENERATED by sqlx-js. Do not edit.");
24
24
  lines.push("// Run `sqlx-js prepare` to regenerate.");
25
25
  lines.push("");
26
- emitModule(lines, "@onreza/sqlx-js", entries, functions);
26
+ emitRegistry(lines, entries, functions);
27
+ lines.push("");
28
+ emitModuleAugmentation(lines, "@onreza/sqlx-js");
27
29
  lines.push("");
28
30
  lines.push("export {};");
29
31
  const tmp = `${outPath}.tmp-${randomBytes(4).toString("hex")}`;
@@ -39,9 +41,8 @@ export function emitDts(outPath, entries, functions = []) {
39
41
  throw err;
40
42
  }
41
43
  }
42
- function emitModule(lines, moduleName, entries, functions) {
43
- lines.push(`declare module ${JSON.stringify(moduleName)} {`);
44
- lines.push(" interface KnownQueries {");
44
+ function emitRegistry(lines, entries, functions) {
45
+ lines.push("export interface SqlxJsGeneratedQueries {");
45
46
  const inlineSeen = new Set();
46
47
  const inlinePairs = [];
47
48
  for (const e of entries) {
@@ -58,12 +59,12 @@ function emitModule(lines, moduleName, entries, functions) {
58
59
  inlineSeen.add(query);
59
60
  const { params, row } = entrySignature(entry);
60
61
  if (entry.degraded)
61
- lines.push(` /** Nullability inference degraded: ${entry.degraded.reason}. All result columns conservatively typed as nullable. */`);
62
- lines.push(` ${JSON.stringify(query)}: { params: ${params}; row: ${row} };`);
62
+ lines.push(` /** Nullability inference degraded: ${entry.degraded.reason}. All result columns conservatively typed as nullable. */`);
63
+ lines.push(` ${JSON.stringify(query)}: { params: ${params}; row: ${row} };`);
63
64
  }
64
- lines.push(" }");
65
+ lines.push("}");
65
66
  lines.push("");
66
- lines.push(" interface KnownFileQueries {");
67
+ lines.push("export interface SqlxJsGeneratedFileQueries {");
67
68
  const filePairs = [];
68
69
  for (const e of entries) {
69
70
  if (!e.filePaths || e.filePaths.length === 0)
@@ -78,21 +79,33 @@ function emitModule(lines, moduleName, entries, functions) {
78
79
  filePathsSeen.add(path);
79
80
  const { params, row } = entrySignature(entry);
80
81
  if (entry.degraded)
81
- lines.push(` /** Nullability inference degraded: ${entry.degraded.reason}. */`);
82
- lines.push(` ${JSON.stringify(path)}: { params: ${params}; row: ${row} };`);
82
+ lines.push(` /** Nullability inference degraded: ${entry.degraded.reason}. */`);
83
+ lines.push(` ${JSON.stringify(path)}: { params: ${params}; row: ${row} };`);
83
84
  }
84
- lines.push(" }");
85
+ lines.push("}");
85
86
  lines.push("");
86
- lines.push(" interface KnownFunctions {");
87
+ lines.push("export interface SqlxJsGeneratedFunctions {");
87
88
  const functionSeen = new Set();
88
89
  for (const fn of functions.slice().sort((a, b) => a.signature.localeCompare(b.signature))) {
89
90
  if (functionSeen.has(fn.signature))
90
91
  continue;
91
92
  functionSeen.add(fn.signature);
92
93
  const params = inputParams(fn);
93
- lines.push(` ${JSON.stringify(fn.signature)}: { kind: ${JSON.stringify(fn.kind)}; params: ${params}; returns: ${fn.returns}; returnsSet: ${fn.returnsSet} };`);
94
+ lines.push(` ${JSON.stringify(fn.signature)}: { kind: ${JSON.stringify(fn.kind)}; params: ${params}; returns: ${fn.returns}; returnsSet: ${fn.returnsSet} };`);
94
95
  }
95
- lines.push(" }");
96
+ lines.push("}");
97
+ lines.push("");
98
+ lines.push("export interface SqlxJsGeneratedRegistry {");
99
+ lines.push(" queries: SqlxJsGeneratedQueries;");
100
+ lines.push(" fileQueries: SqlxJsGeneratedFileQueries;");
101
+ lines.push(" functions: SqlxJsGeneratedFunctions;");
102
+ lines.push("}");
103
+ }
104
+ function emitModuleAugmentation(lines, moduleName) {
105
+ lines.push(`declare module ${JSON.stringify(moduleName)} {`);
106
+ lines.push(" interface KnownQueries extends SqlxJsGeneratedQueries {}");
107
+ lines.push(" interface KnownFileQueries extends SqlxJsGeneratedFileQueries {}");
108
+ lines.push(" interface KnownFunctions extends SqlxJsGeneratedFunctions {}");
96
109
  lines.push("}");
97
110
  }
98
111
  function inputParams(fn) {