@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.
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
3
  const CONFIG_TEMPLATE = `import { defineConfig } from "@onreza/sqlx-js";
4
4
 
@@ -33,10 +33,32 @@ const PGSCHEMA_TEMPLATE = `-- Desired PostgreSQL schema managed by pgschema.
33
33
  -- sqlx-js db plan -- --output-json plan.json
34
34
  -- sqlx-js db apply -- --auto-approve
35
35
  `;
36
+ const DTS_TEMPLATE = `// AUTO-GENERATED by sqlx-js. Do not edit.
37
+ // Run \`sqlx-js prepare\` to regenerate.
38
+
39
+ export interface SqlxJsGeneratedQueries {}
40
+ export interface SqlxJsGeneratedFileQueries {}
41
+ export interface SqlxJsGeneratedFunctions {}
42
+ export interface SqlxJsGeneratedRegistry {
43
+ queries: SqlxJsGeneratedQueries;
44
+ fileQueries: SqlxJsGeneratedFileQueries;
45
+ functions: SqlxJsGeneratedFunctions;
46
+ }
47
+
48
+ declare module "@onreza/sqlx-js" {
49
+ interface KnownQueries extends SqlxJsGeneratedQueries {}
50
+ interface KnownFileQueries extends SqlxJsGeneratedFileQueries {}
51
+ interface KnownFunctions extends SqlxJsGeneratedFunctions {}
52
+ }
53
+
54
+ export {};
55
+ `;
36
56
  export function runInit(opts) {
37
57
  const log = opts.log ?? console.log;
38
58
  const created = [];
59
+ const updated = [];
39
60
  const skipped = [];
61
+ const notes = [];
40
62
  const ensureFile = (rel, content) => {
41
63
  const full = join(opts.root, rel);
42
64
  if (existsSync(full)) {
@@ -63,10 +85,82 @@ export function runInit(opts) {
63
85
  else
64
86
  ensureDir("migrations");
65
87
  ensureFile(".env.example", ENV_TEMPLATE);
88
+ ensureFile("sqlx-js-env.d.ts", DTS_TEMPLATE);
89
+ const updateJson = (rel, mutate) => {
90
+ const full = join(opts.root, rel);
91
+ if (!existsSync(full))
92
+ return;
93
+ const source = readFileSync(full, "utf8");
94
+ let value;
95
+ try {
96
+ value = JSON.parse(source);
97
+ }
98
+ catch {
99
+ notes.push(`${rel}: manual update required because the file is not strict JSON`);
100
+ return;
101
+ }
102
+ if (!value || typeof value !== "object" || Array.isArray(value))
103
+ return;
104
+ if (!mutate(value))
105
+ return;
106
+ const indent = source.match(/\n([ \t]+)"/)?.[1] ?? " ";
107
+ writeFileSync(full, JSON.stringify(value, null, indent) + "\n");
108
+ updated.push(rel);
109
+ };
110
+ updateJson("tsconfig.json", (value) => {
111
+ const files = value.files;
112
+ if (files !== undefined && (!Array.isArray(files) || !files.every((item) => typeof item === "string"))) {
113
+ notes.push("tsconfig.json: manual update required because files is not an array of strings");
114
+ return false;
115
+ }
116
+ if (Array.isArray(files)) {
117
+ if (files.includes("sqlx-js-env.d.ts"))
118
+ return false;
119
+ files.push("sqlx-js-env.d.ts");
120
+ return true;
121
+ }
122
+ const include = value.include;
123
+ if (include !== undefined && (!Array.isArray(include) || !include.every((item) => typeof item === "string"))) {
124
+ notes.push("tsconfig.json: manual update required because include is not an array of strings");
125
+ return false;
126
+ }
127
+ if (Array.isArray(include)) {
128
+ if (include.includes("sqlx-js-env.d.ts"))
129
+ return false;
130
+ include.push("sqlx-js-env.d.ts");
131
+ return true;
132
+ }
133
+ return false;
134
+ });
135
+ updateJson("package.json", (value) => {
136
+ if (value.scripts !== undefined && (!value.scripts || typeof value.scripts !== "object" || Array.isArray(value.scripts))) {
137
+ notes.push("package.json: manual script update required because scripts is not an object");
138
+ return false;
139
+ }
140
+ const scripts = (value.scripts ??= {});
141
+ const defaults = {
142
+ "sqlx:prepare": "sqlx-js prepare",
143
+ "sqlx:check": "sqlx-js prepare --check",
144
+ "sqlx:offline": "sqlx-js prepare --offline",
145
+ "sqlx:verify": "sqlx-js prepare --verify --strict-inference",
146
+ };
147
+ let changed = false;
148
+ for (const [name, command] of Object.entries(defaults)) {
149
+ if (scripts[name] !== undefined)
150
+ continue;
151
+ scripts[name] = command;
152
+ changed = true;
153
+ }
154
+ return changed;
155
+ });
66
156
  for (const f of created)
67
157
  log(`created ${f}`);
158
+ for (const f of updated)
159
+ log(`updated ${f}`);
68
160
  for (const f of skipped)
69
161
  log(`exists ${f} (left unchanged)`);
162
+ for (const note of notes)
163
+ log(`manual ${note}`);
70
164
  log("");
71
165
  log("Next steps:");
72
166
  log(" 1. Set DATABASE_URL (see .env.example).");
@@ -78,7 +172,7 @@ export function runInit(opts) {
78
172
  }
79
173
  else {
80
174
  log(" 2. Add a migration: sqlx-js migrate add init");
81
- log(" 3. Make sure tsconfig.json \"include\" covers sqlx-js-env.d.ts.");
82
- log(" 4. Develop locally: sqlx-js migrate dev");
175
+ log(" 3. Check the project setup: sqlx-js doctor");
176
+ log(" 4. Develop locally: sqlx-js migrate dev --strict-inference");
83
177
  }
84
178
  }
@@ -1,4 +1,5 @@
1
1
  import { PgClient } from "../pg/wire.js";
2
+ export * from "../migration-core.js";
2
3
  export type MigrateOptions = {
3
4
  databaseUrl: string;
4
5
  migrationsDir: string;
@@ -12,91 +13,7 @@ export type MigrationWorkflowOptions = MigrateOptions & {
12
13
  shadowAdminUrl?: string;
13
14
  lockKey?: number | bigint;
14
15
  lockTimeoutMs?: number;
15
- };
16
- export type MigrationStore = {
17
- table: string;
18
- };
19
- export declare const DEFAULT_MIGRATE_LOCK_KEY = 18750938867203960n;
20
- export declare function ensureTable(c: PgClient): Promise<MigrationStore>;
21
- export declare function listApplied(c: PgClient, store?: MigrationStore): Promise<Map<number, {
22
- name: string;
23
- hash: string;
24
- }>>;
25
- export type ApplyOutcome = {
26
- kind: "applied";
27
- version: number;
28
- name: string;
29
- } | {
30
- kind: "adopted";
31
- version: number;
32
- name: string;
33
- replaced: number;
34
- } | {
35
- kind: "tampered";
36
- version: number;
37
- name: string;
38
- applied: string;
39
- current: string;
40
- } | {
41
- kind: "failed";
42
- version: number;
43
- name: string;
44
- error: string;
45
- };
46
- export type PlanOutcome = {
47
- kind: "pending";
48
- version: number;
49
- name: string;
50
- } | {
51
- kind: "adoptable";
52
- version: number;
53
- name: string;
54
- replaced: number;
55
- } | {
56
- kind: "tampered";
57
- version: number;
58
- name: string;
59
- applied: string;
60
- current: string;
61
- } | {
62
- kind: "failed";
63
- version: number;
64
- name: string;
65
- error: string;
66
- };
67
- export type MigrationPlanItem = {
68
- kind: "apply";
69
- version: number;
70
- name: string;
71
- } | {
72
- kind: "adopt";
73
- version: number;
74
- name: string;
75
- replaced: number;
76
- };
77
- export type MigrationPlanDiagnostic = Extract<PlanOutcome, {
78
- kind: "tampered" | "failed";
79
- }>;
80
- export type MigrationPlanSnapshot = {
81
- ok: boolean;
82
- pending: number;
83
- adoptable: number;
84
- tampered: number;
85
- failed: number;
86
- steps: MigrationPlanItem[];
87
- diagnostics: MigrationPlanDiagnostic[];
88
- };
89
- export type MigrationInfoStatus = "applied" | "pending" | "adoptable" | "superseded" | "tampered" | "failed";
90
- export type MigrationInfoItem = {
91
- version: number;
92
- name: string;
93
- status: MigrationInfoStatus;
94
- detail?: string;
95
- };
96
- export type MigrationInfoSnapshot = {
97
- historyTable: string | null;
98
- summary: Record<MigrationInfoStatus, number>;
99
- items: MigrationInfoItem[];
16
+ strictInference?: boolean;
100
17
  };
101
18
  export type MigrationArchive = {
102
19
  name: string;
@@ -151,22 +68,6 @@ export type RevertDryRunOutcome = {
151
68
  error: string;
152
69
  };
153
70
  export declare function checkMigrationFiles(migrationsDir: string): MigrationCheckReport;
154
- export declare function planPending(c: PgClient, migrationsDir: string, onEvent?: (e: PlanOutcome) => void): Promise<{
155
- pending: number;
156
- adoptable: number;
157
- tampered: number;
158
- failed: number;
159
- steps: MigrationPlanItem[];
160
- }>;
161
- export declare function inspectMigrationPlan(c: PgClient, migrationsDir: string): Promise<MigrationPlanSnapshot>;
162
- export declare function inspectMigrations(c: PgClient, migrationsDir: string): Promise<MigrationInfoSnapshot>;
163
- export declare function applyPending(c: PgClient, migrationsDir: string, onEvent?: (e: ApplyOutcome) => void): Promise<{
164
- applied: number;
165
- tampered: number;
166
- failed: number;
167
- }>;
168
- export declare function acquireMigrateLock(c: PgClient, lockKey?: number | bigint, timeoutMs?: number): Promise<void>;
169
- export declare function releaseMigrateLock(c: PgClient, lockKey?: number | bigint): Promise<void>;
170
71
  export declare function sanitizePgDumpSchema(sql: string): string;
171
72
  export declare function listMigrationArchives(migrationsDir: string): MigrationArchive[];
172
73
  export declare function restoreMigrationArchive(migrationsDir: string, archiveName: string, opts?: {