zenstack-kit 0.1.5 → 0.1.7
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 +8 -6
- package/dist/cli/app.d.ts.map +1 -1
- package/dist/cli/app.js +4 -1
- package/dist/cli/commands.d.ts +1 -0
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +86 -4
- package/dist/cli/prompts.d.ts.map +1 -1
- package/dist/cli/prompts.js +1 -3
- package/dist/migrations/prisma/apply.d.ts +2 -0
- package/dist/migrations/prisma/apply.d.ts.map +1 -1
- package/dist/migrations/prisma/apply.js +7 -5
- package/dist/schema/pull.d.ts +2 -0
- package/dist/schema/pull.d.ts.map +1 -1
- package/dist/schema/pull.js +102 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -79,7 +79,7 @@ The `init` command offers two options:
|
|
|
79
79
|
### 4. Generate migrations
|
|
80
80
|
|
|
81
81
|
```bash
|
|
82
|
-
zenstack-kit migrate
|
|
82
|
+
zenstack-kit migrate create --name add_posts
|
|
83
83
|
```
|
|
84
84
|
|
|
85
85
|
This creates a migration in Prisma format:
|
|
@@ -95,7 +95,7 @@ prisma/migrations/
|
|
|
95
95
|
### 5. Apply migrations
|
|
96
96
|
|
|
97
97
|
```bash
|
|
98
|
-
zenstack-kit migrate
|
|
98
|
+
zenstack-kit migrate apply
|
|
99
99
|
```
|
|
100
100
|
|
|
101
101
|
Migrations are tracked in the `_prisma_migrations` table, making them compatible with `prisma migrate deploy`.
|
|
@@ -126,12 +126,12 @@ Options:
|
|
|
126
126
|
- `--create-initial` - Create snapshot and initial migration
|
|
127
127
|
- `-c, --config <path>` - Path to zenstack-kit config file
|
|
128
128
|
|
|
129
|
-
### `zenstack-kit migrate
|
|
129
|
+
### `zenstack-kit migrate create`
|
|
130
130
|
|
|
131
131
|
Generate a new SQL migration from schema changes.
|
|
132
132
|
|
|
133
133
|
```bash
|
|
134
|
-
zenstack-kit migrate
|
|
134
|
+
zenstack-kit migrate create --name add_users
|
|
135
135
|
```
|
|
136
136
|
|
|
137
137
|
Options:
|
|
@@ -141,12 +141,12 @@ Options:
|
|
|
141
141
|
- `--dialect <dialect>` - Database dialect (`sqlite`, `postgres`, `mysql`)
|
|
142
142
|
- `-c, --config <path>` - Path to zenstack-kit config file
|
|
143
143
|
|
|
144
|
-
### `zenstack-kit migrate
|
|
144
|
+
### `zenstack-kit migrate apply`
|
|
145
145
|
|
|
146
146
|
Apply pending migrations to the database.
|
|
147
147
|
|
|
148
148
|
```bash
|
|
149
|
-
zenstack-kit migrate
|
|
149
|
+
zenstack-kit migrate apply
|
|
150
150
|
```
|
|
151
151
|
|
|
152
152
|
Options:
|
|
@@ -156,6 +156,7 @@ Options:
|
|
|
156
156
|
- `--table <name>` - Migrations table name (default: `_prisma_migrations`)
|
|
157
157
|
- `--db-schema <name>` - Database schema for migrations table (PostgreSQL only, default: `public`)
|
|
158
158
|
- `--preview` - Preview pending migrations without applying
|
|
159
|
+
- `--mark-applied` - Mark pending migrations as applied without running SQL
|
|
159
160
|
- `-c, --config <path>` - Path to zenstack-kit config file
|
|
160
161
|
|
|
161
162
|
### `zenstack-kit pull`
|
|
@@ -170,6 +171,7 @@ Options:
|
|
|
170
171
|
- `-o, --output <path>` - Output path for schema (default: `./schema.zmodel`)
|
|
171
172
|
- `--dialect <dialect>` - Database dialect
|
|
172
173
|
- `--url <url>` - Database connection URL
|
|
174
|
+
- `--preview` - Preview generated schema and diff without writing files
|
|
173
175
|
- `-c, --config <path>` - Path to zenstack-kit config file
|
|
174
176
|
|
|
175
177
|
Features:
|
package/dist/cli/app.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/cli/app.tsx"],"names":[],"mappings":";AAEA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/cli/app.tsx"],"names":[],"mappings":";AAEA;;;;;;;;GAQG;AA6SH,wBAAgB,MAAM,SAkBrB"}
|
package/dist/cli/app.js
CHANGED
|
@@ -74,6 +74,9 @@ function parseArgs() {
|
|
|
74
74
|
else if (arg === "--preview") {
|
|
75
75
|
options.preview = true;
|
|
76
76
|
}
|
|
77
|
+
else if (arg === "--mark-applied") {
|
|
78
|
+
options.markApplied = true;
|
|
79
|
+
}
|
|
77
80
|
else if (arg === "--force" || arg === "-f") {
|
|
78
81
|
options.force = true;
|
|
79
82
|
}
|
|
@@ -101,7 +104,7 @@ function Status({ type, message }) {
|
|
|
101
104
|
}
|
|
102
105
|
// Help display component
|
|
103
106
|
function HelpDisplay() {
|
|
104
|
-
return (_jsxs(Box, { flexDirection: "column", paddingY: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "zenstack-kit" }), _jsx(Text, { dimColor: true, children: "Database tooling for ZenStack schemas" }), _jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: "Commands:" }), commands.filter(c => c.value !== "exit").map((cmd) => (_jsxs(Box, { marginLeft: 2, children: [_jsx(Box, { width: 20, children: _jsx(Text, { color: "yellow", children: cmd.label }) }), _jsx(Text, { dimColor: true, children: cmd.description })] }, cmd.value))), _jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: "Options:" }), _jsxs(Box, { marginLeft: 2, flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "-s, --schema <path> Path to ZenStack schema" }), _jsx(Text, { dimColor: true, children: "-m, --migrations <path> Migrations directory" }), _jsx(Text, { dimColor: true, children: "-n, --name <name> Migration name" }), _jsx(Text, { dimColor: true, children: "--dialect <dialect> Database dialect (sqlite, postgres, mysql)" }), _jsx(Text, { dimColor: true, children: "--url <url> Database connection URL" }), _jsx(Text, { dimColor: true, children: "--create-initial Create initial migration (skip prompt)" }), _jsx(Text, { dimColor: true, children: "--baseline Create baseline only (skip prompt)" }), _jsx(Text, { dimColor: true, children: "--preview Preview pending migrations without applying" }), _jsx(Text, { dimColor: true, children: "-f, --force Force operation without confirmation" }), _jsx(Text, { dimColor: true, children: "-c, --config <path> Path to zenstack-kit config file" })] })] }));
|
|
107
|
+
return (_jsxs(Box, { flexDirection: "column", paddingY: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "zenstack-kit" }), _jsx(Text, { dimColor: true, children: "Database tooling for ZenStack schemas" }), _jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: "Commands:" }), commands.filter(c => c.value !== "exit").map((cmd) => (_jsxs(Box, { marginLeft: 2, children: [_jsx(Box, { width: 20, children: _jsx(Text, { color: "yellow", children: cmd.label }) }), _jsx(Text, { dimColor: true, children: cmd.description })] }, cmd.value))), _jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: "Options:" }), _jsxs(Box, { marginLeft: 2, flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "-s, --schema <path> Path to ZenStack schema" }), _jsx(Text, { dimColor: true, children: "-m, --migrations <path> Migrations directory" }), _jsx(Text, { dimColor: true, children: "-n, --name <name> Migration name" }), _jsx(Text, { dimColor: true, children: "--dialect <dialect> Database dialect (sqlite, postgres, mysql)" }), _jsx(Text, { dimColor: true, children: "--url <url> Database connection URL" }), _jsx(Text, { dimColor: true, children: "--create-initial Create initial migration (skip prompt)" }), _jsx(Text, { dimColor: true, children: "--baseline Create baseline only (skip prompt)" }), _jsx(Text, { dimColor: true, children: "--preview Preview pending migrations without applying" }), _jsx(Text, { dimColor: true, children: "--mark-applied Mark pending migrations as applied without running SQL" }), _jsx(Text, { dimColor: true, children: "-f, --force Force operation without confirmation" }), _jsx(Text, { dimColor: true, children: "-c, --config <path> Path to zenstack-kit config file" })] })] }));
|
|
105
108
|
}
|
|
106
109
|
function CliApp({ initialCommand, options }) {
|
|
107
110
|
const { exit } = useApp();
|
package/dist/cli/commands.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/commands.js
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import * as fs from "fs";
|
|
8
8
|
import * as path from "path";
|
|
9
|
+
import * as os from "os";
|
|
10
|
+
import { execFileSync } from "child_process";
|
|
9
11
|
import { loadConfig } from "../config/loader.js";
|
|
10
12
|
import { pullSchema } from "../schema/pull.js";
|
|
11
13
|
import { createPrismaMigration, applyPrismaMigrations, previewPrismaMigrations, hasPrismaSchemaChanges, hasSnapshot, scanMigrationFolders, writeMigrationLog, initializeSnapshot, createInitialMigration, detectPotentialRenames, } from "../migrations/prisma.js";
|
|
@@ -148,6 +150,9 @@ export async function runMigrateApply(ctx) {
|
|
|
148
150
|
throw new CommandError("Database connection URL is required for non-sqlite dialects.");
|
|
149
151
|
}
|
|
150
152
|
const databasePath = dialect === "sqlite" ? connectionUrl : undefined;
|
|
153
|
+
if (ctx.options.preview && ctx.options.markApplied) {
|
|
154
|
+
throw new CommandError("Cannot use --preview and --mark-applied together.");
|
|
155
|
+
}
|
|
151
156
|
// Preview mode - show pending migrations without applying
|
|
152
157
|
if (ctx.options.preview) {
|
|
153
158
|
ctx.log("info", "Preview mode - no changes will be applied.");
|
|
@@ -171,12 +176,21 @@ export async function runMigrateApply(ctx) {
|
|
|
171
176
|
ctx.log("info", `${preview.alreadyApplied.length} migration(s) already applied`);
|
|
172
177
|
}
|
|
173
178
|
for (const migration of preview.pending) {
|
|
174
|
-
|
|
175
|
-
|
|
179
|
+
const statementCount = migration.sql
|
|
180
|
+
.split(/;(?:\s*\n|\s*$)/)
|
|
181
|
+
.map((s) => s.trim())
|
|
182
|
+
.filter((s) => s.length > 0 && !s.startsWith("--")).length;
|
|
183
|
+
ctx.log("info", `Migration: ${migration.name} (${statementCount} statement${statementCount === 1 ? "" : "s"})`);
|
|
176
184
|
}
|
|
185
|
+
ctx.log("info", "Use the migration.sql files to review full SQL.");
|
|
177
186
|
return;
|
|
178
187
|
}
|
|
179
|
-
ctx.
|
|
188
|
+
if (ctx.options.markApplied) {
|
|
189
|
+
ctx.log("info", "Marking migrations as applied (no SQL will be executed)...");
|
|
190
|
+
}
|
|
191
|
+
else {
|
|
192
|
+
ctx.log("info", "Applying migrations...");
|
|
193
|
+
}
|
|
180
194
|
const result = await applyPrismaMigrations({
|
|
181
195
|
migrationsFolder: outputPath,
|
|
182
196
|
dialect,
|
|
@@ -184,6 +198,7 @@ export async function runMigrateApply(ctx) {
|
|
|
184
198
|
databasePath,
|
|
185
199
|
migrationsTable,
|
|
186
200
|
migrationsSchema,
|
|
201
|
+
markApplied: ctx.options.markApplied,
|
|
187
202
|
});
|
|
188
203
|
// Handle coherence errors
|
|
189
204
|
if (result.coherenceErrors && result.coherenceErrors.length > 0) {
|
|
@@ -210,7 +225,8 @@ export async function runMigrateApply(ctx) {
|
|
|
210
225
|
return;
|
|
211
226
|
}
|
|
212
227
|
for (const item of result.applied) {
|
|
213
|
-
ctx.
|
|
228
|
+
const action = ctx.options.markApplied ? "Marked applied" : "Applied";
|
|
229
|
+
ctx.log("success", `${action}: ${item.migrationName} (${item.duration}ms)`);
|
|
214
230
|
}
|
|
215
231
|
if (result.failed) {
|
|
216
232
|
throw new CommandError(`Migration failed: ${result.failed.migrationName} - ${result.failed.error}`);
|
|
@@ -334,6 +350,42 @@ export async function runPull(ctx) {
|
|
|
334
350
|
return;
|
|
335
351
|
}
|
|
336
352
|
}
|
|
353
|
+
if (ctx.options.preview) {
|
|
354
|
+
ctx.log("info", "Preview mode - no files will be written.");
|
|
355
|
+
const result = await pullSchema({
|
|
356
|
+
dialect,
|
|
357
|
+
connectionUrl,
|
|
358
|
+
databasePath,
|
|
359
|
+
outputPath: schemaOutputPath,
|
|
360
|
+
writeFile: false,
|
|
361
|
+
});
|
|
362
|
+
if (fs.existsSync(schemaOutputPath)) {
|
|
363
|
+
const diffOutput = await buildSchemaDiff(schemaOutputPath, result.schema);
|
|
364
|
+
if (diffOutput) {
|
|
365
|
+
const { text, truncated } = truncateLines(diffOutput, 200);
|
|
366
|
+
ctx.log("info", `Diff (existing -> generated):\n${text}`);
|
|
367
|
+
if (truncated) {
|
|
368
|
+
ctx.log("info", "Diff truncated. Use --output to write and inspect the full schema.");
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
ctx.log("info", "Diff unavailable; showing generated schema preview.");
|
|
373
|
+
const { text, truncated } = truncateLines(result.schema, 200);
|
|
374
|
+
ctx.log("info", `Generated schema:\n${text}`);
|
|
375
|
+
if (truncated) {
|
|
376
|
+
ctx.log("info", "Preview truncated. Use --output to write and inspect the full schema.");
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
const { text, truncated } = truncateLines(result.schema, 200);
|
|
382
|
+
ctx.log("info", `Generated schema:\n${text}`);
|
|
383
|
+
if (truncated) {
|
|
384
|
+
ctx.log("info", "Preview truncated. Use --output to write and inspect the full schema.");
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
337
389
|
ctx.log("info", "Pulling schema from database...");
|
|
338
390
|
const result = await pullSchema({
|
|
339
391
|
dialect,
|
|
@@ -349,3 +401,33 @@ export async function runPull(ctx) {
|
|
|
349
401
|
ctx.log("warning", "You should run 'zenstack-kit init' to reset the snapshot after reviewing the schema.");
|
|
350
402
|
}
|
|
351
403
|
}
|
|
404
|
+
async function buildSchemaDiff(existingPath, nextSchema) {
|
|
405
|
+
const tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), "zenstack-kit-pull-"));
|
|
406
|
+
const nextPath = path.join(tempDir, "schema.zmodel");
|
|
407
|
+
try {
|
|
408
|
+
await fs.promises.writeFile(nextPath, nextSchema, "utf-8");
|
|
409
|
+
try {
|
|
410
|
+
return execFileSync("git", ["diff", "--no-index", "--no-color", "--", existingPath, nextPath], { encoding: "utf-8" });
|
|
411
|
+
}
|
|
412
|
+
catch (error) {
|
|
413
|
+
const stdout = error.stdout;
|
|
414
|
+
if (stdout) {
|
|
415
|
+
return stdout.toString();
|
|
416
|
+
}
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
finally {
|
|
421
|
+
await fs.promises.rm(tempDir, { recursive: true, force: true });
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
function truncateLines(text, maxLines) {
|
|
425
|
+
const lines = text.split("\n");
|
|
426
|
+
if (lines.length <= maxLines) {
|
|
427
|
+
return { text, truncated: false };
|
|
428
|
+
}
|
|
429
|
+
return {
|
|
430
|
+
text: lines.slice(0, maxLines).join("\n"),
|
|
431
|
+
truncated: true,
|
|
432
|
+
};
|
|
433
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/cli/prompts.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAMH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,cAAc,GAAG,UAAU,GAAG,gBAAgB,CAAC;AACjF,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,IAAI,CAAC;AAsCzC;;GAEG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,UAAU,CAAC,CAyBhE;AAED;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,UAAU,CAAC,CAyB3D;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CA+BjF;AAED,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/cli/prompts.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAMH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,cAAc,GAAG,UAAU,GAAG,gBAAgB,CAAC;AACjF,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,IAAI,CAAC;AAsCzC;;GAEG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,UAAU,CAAC,CAyBhE;AAED;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,UAAU,CAAC,CAyB3D;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CA+BjF;AAED,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,eAAe,CAAC;AAyCtD;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,WAAW,GAAE,MAAoB,GAAG,OAAO,CAAC,MAAM,CAAC,CAc5F;AAED,MAAM,MAAM,sBAAsB,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEzD;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,CAkCnG;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAyBvF;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,YAAY,CAAC,CAyBvB"}
|
package/dist/cli/prompts.js
CHANGED
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
* Interactive prompts for the init command using ink
|
|
4
4
|
*/
|
|
5
5
|
import React, { useState } from "react";
|
|
6
|
-
import { render, Box, Text } from "ink";
|
|
6
|
+
import { render, Box, Text, useInput } from "ink";
|
|
7
7
|
import SelectInput from "ink-select-input";
|
|
8
8
|
function SelectPrompt({ message, items, onSelect }) {
|
|
9
9
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
@@ -101,8 +101,6 @@ function TextInputPrompt({ message, placeholder, onSubmit, }) {
|
|
|
101
101
|
setValue((prev) => prev + input);
|
|
102
102
|
}
|
|
103
103
|
};
|
|
104
|
-
// Use ink's useInput hook
|
|
105
|
-
const { useInput } = require("ink");
|
|
106
104
|
useInput(handleInput);
|
|
107
105
|
return (_jsx(Box, { flexDirection: "column", children: _jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: "? " }), _jsxs(Text, { children: [message, " "] }), _jsxs(Text, { dimColor: true, children: ["(", placeholder, "): "] }), _jsx(Text, { children: value }), _jsx(Text, { color: "gray", children: "\u2588" })] }) }));
|
|
108
106
|
}
|
|
@@ -12,6 +12,8 @@ export interface ApplyPrismaMigrationsOptions {
|
|
|
12
12
|
migrationsTable?: string;
|
|
13
13
|
/** Migrations schema (PostgreSQL only, default: public) */
|
|
14
14
|
migrationsSchema?: string;
|
|
15
|
+
/** Mark migrations as applied without executing SQL */
|
|
16
|
+
markApplied?: boolean;
|
|
15
17
|
}
|
|
16
18
|
export interface ApplyPrismaMigrationsResult {
|
|
17
19
|
applied: Array<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../../src/migrations/prisma/apply.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAIjE,MAAM,WAAW,4BAA4B;IAC3C,6BAA6B;IAC7B,gBAAgB,EAAE,MAAM,CAAC;IACzB,uBAAuB;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,8BAA8B;IAC9B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2BAA2B;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,gBAAgB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"apply.d.ts","sourceRoot":"","sources":["../../../src/migrations/prisma/apply.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAIjE,MAAM,WAAW,4BAA4B;IAC3C,6BAA6B;IAC7B,gBAAgB,EAAE,MAAM,CAAC;IACzB,uBAAuB;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,8BAA8B;IAC9B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2BAA2B;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,uDAAuD;IACvD,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,KAAK,CAAC;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5D,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,MAAM,CAAC,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAClD,eAAe,CAAC,EAAE,uBAAuB,EAAE,CAAC;CAC7C;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,kBAAkB,GAAG,iBAAiB,GAAG,mBAAmB,GAAG,gBAAgB,GAAG,mBAAmB,CAAC;IAC5G,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,uBAAuB,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,6BAA6B;IAC5C,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9C,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AA2QD;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,2BAA2B,CAAC,CAyHtC;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,6BAA6B,CAAC,CA0DxC"}
|
|
@@ -304,11 +304,13 @@ export async function applyPrismaMigrations(options) {
|
|
|
304
304
|
}
|
|
305
305
|
const startTime = Date.now();
|
|
306
306
|
try {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
307
|
+
if (!options.markApplied) {
|
|
308
|
+
// Execute the migration SQL using direct driver access
|
|
309
|
+
await executeRawSql(options.dialect, sqlContent, {
|
|
310
|
+
connectionUrl: options.connectionUrl,
|
|
311
|
+
databasePath: options.databasePath,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
312
314
|
// Record the migration (still use Kysely for this since it's simple INSERT)
|
|
313
315
|
await recordMigration(db, migrationsTable, migrationsSchema, options.dialect, folderName, checksum);
|
|
314
316
|
result.applied.push({
|
package/dist/schema/pull.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../src/schema/pull.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEnF,MAAM,WAAW,WAAW;IAC1B,uBAAuB;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,8BAA8B;IAC9B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gDAAgD;IAChD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6BAA6B;IAC7B,UAAU,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../src/schema/pull.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAEnF,MAAM,WAAW,WAAW;IAC1B,uBAAuB;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,8BAA8B;IAC9B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gDAAgD;IAChD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6BAA6B;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AA8lBD,wBAAsB,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CA4C1E"}
|
package/dist/schema/pull.js
CHANGED
|
@@ -52,6 +52,10 @@ function normalizeType(dataType) {
|
|
|
52
52
|
const isArray = lower.endsWith("[]");
|
|
53
53
|
const base = isArray ? lower.slice(0, -2) : lower;
|
|
54
54
|
const normalized = base.replace(/\(.+\)/, "").trim();
|
|
55
|
+
if (normalized.includes("uuid") || normalized.includes("citext"))
|
|
56
|
+
return { type: "String", isArray };
|
|
57
|
+
if (normalized.includes("jsonb"))
|
|
58
|
+
return { type: "Json", isArray };
|
|
55
59
|
if (normalized.includes("bigint"))
|
|
56
60
|
return { type: "BigInt", isArray };
|
|
57
61
|
if (normalized.includes("int"))
|
|
@@ -87,7 +91,7 @@ function buildDatasourceBlock(dialect) {
|
|
|
87
91
|
].join("\n");
|
|
88
92
|
}
|
|
89
93
|
function buildModelBlock(options) {
|
|
90
|
-
const { table, foreignKeys, indexes, primaryKeys, allTables } = options;
|
|
94
|
+
const { table, foreignKeys, indexes, primaryKeys, allTables, columnDefaults } = options;
|
|
91
95
|
const modelName = toPascalCase(table.name) || "Model";
|
|
92
96
|
const fieldLines = [];
|
|
93
97
|
// Get primary key columns for this table
|
|
@@ -123,6 +127,45 @@ function buildModelBlock(options) {
|
|
|
123
127
|
}
|
|
124
128
|
}
|
|
125
129
|
}
|
|
130
|
+
const getDefaultExpr = (columnName) => columnDefaults.get(columnName) ?? null;
|
|
131
|
+
const buildDefaultAttribute = (defaultExpr, dataType) => {
|
|
132
|
+
if (!defaultExpr)
|
|
133
|
+
return null;
|
|
134
|
+
const normalized = defaultExpr.trim();
|
|
135
|
+
if (!normalized || normalized.toLowerCase() === "null")
|
|
136
|
+
return null;
|
|
137
|
+
const lower = normalized.toLowerCase();
|
|
138
|
+
if (lower.includes("nextval("))
|
|
139
|
+
return null;
|
|
140
|
+
if (lower === "current_timestamp" ||
|
|
141
|
+
lower === "current_timestamp()" ||
|
|
142
|
+
lower === "now()" ||
|
|
143
|
+
lower.includes("datetime('now") ||
|
|
144
|
+
lower.includes("now()")) {
|
|
145
|
+
return "@default(now())";
|
|
146
|
+
}
|
|
147
|
+
if (lower.includes("uuid_generate_v4()") || lower.includes("gen_random_uuid()") || lower === "uuid()") {
|
|
148
|
+
return "@default(uuid())";
|
|
149
|
+
}
|
|
150
|
+
if (lower === "true" || lower === "false") {
|
|
151
|
+
return `@default(${lower})`;
|
|
152
|
+
}
|
|
153
|
+
if ((lower === "0" || lower === "1") && dataType.toLowerCase().includes("bool")) {
|
|
154
|
+
return `@default(${lower === "1" ? "true" : "false"})`;
|
|
155
|
+
}
|
|
156
|
+
if (/^-?\d+(\.\d+)?$/.test(normalized)) {
|
|
157
|
+
return `@default(${normalized})`;
|
|
158
|
+
}
|
|
159
|
+
if ((normalized.startsWith("'") && normalized.endsWith("'")) ||
|
|
160
|
+
(normalized.startsWith("\"") && normalized.endsWith("\""))) {
|
|
161
|
+
const unquoted = normalized
|
|
162
|
+
.slice(1, -1)
|
|
163
|
+
.replace(/''/g, "'")
|
|
164
|
+
.replace(/\\"/g, "\"");
|
|
165
|
+
return `@default(${JSON.stringify(unquoted)})`;
|
|
166
|
+
}
|
|
167
|
+
return "@default(dbgenerated())";
|
|
168
|
+
};
|
|
126
169
|
const sortedColumns = [...table.columns].sort((a, b) => a.name.localeCompare(b.name));
|
|
127
170
|
for (const column of sortedColumns) {
|
|
128
171
|
const fieldName = toCamelCase(column.name) || column.name;
|
|
@@ -146,7 +189,13 @@ function buildModelBlock(options) {
|
|
|
146
189
|
modifiers.push(`@map("${column.name}")`);
|
|
147
190
|
}
|
|
148
191
|
if (column.hasDefaultValue && !modifiers.some((m) => m.includes("@default"))) {
|
|
149
|
-
|
|
192
|
+
const attr = buildDefaultAttribute(getDefaultExpr(column.name), column.dataType);
|
|
193
|
+
if (attr) {
|
|
194
|
+
modifiers.push(attr);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
modifiers.push("@default(dbgenerated())");
|
|
198
|
+
}
|
|
150
199
|
}
|
|
151
200
|
const typeSuffix = isArray ? "[]" : "";
|
|
152
201
|
const modifierText = modifiers.length > 0 ? ` ${modifiers.join(" ")}` : "";
|
|
@@ -389,6 +438,51 @@ async function extractPrimaryKeys(db, dialect, tableNames) {
|
|
|
389
438
|
}
|
|
390
439
|
return primaryKeys;
|
|
391
440
|
}
|
|
441
|
+
async function extractColumnDefaults(db, dialect, tableNames) {
|
|
442
|
+
const defaultsByTable = new Map();
|
|
443
|
+
const tableSet = new Set(tableNames);
|
|
444
|
+
const setDefault = (table, column, value) => {
|
|
445
|
+
if (!defaultsByTable.has(table)) {
|
|
446
|
+
defaultsByTable.set(table, new Map());
|
|
447
|
+
}
|
|
448
|
+
defaultsByTable.get(table).set(column, value);
|
|
449
|
+
};
|
|
450
|
+
if (dialect === "sqlite") {
|
|
451
|
+
for (const tableName of tableNames) {
|
|
452
|
+
const result = await sql `
|
|
453
|
+
PRAGMA table_info(${sql.raw(`"${tableName}"`)})
|
|
454
|
+
`.execute(db);
|
|
455
|
+
for (const row of result.rows) {
|
|
456
|
+
setDefault(tableName, row.name, row.dflt_value);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
else if (dialect === "postgres") {
|
|
461
|
+
const result = await sql `
|
|
462
|
+
SELECT table_name, column_name, column_default
|
|
463
|
+
FROM information_schema.columns
|
|
464
|
+
WHERE table_schema = 'public'
|
|
465
|
+
`.execute(db);
|
|
466
|
+
for (const row of result.rows) {
|
|
467
|
+
if (!tableSet.has(row.table_name))
|
|
468
|
+
continue;
|
|
469
|
+
setDefault(row.table_name, row.column_name, row.column_default);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
else if (dialect === "mysql") {
|
|
473
|
+
const result = await sql `
|
|
474
|
+
SELECT TABLE_NAME, COLUMN_NAME, COLUMN_DEFAULT
|
|
475
|
+
FROM information_schema.COLUMNS
|
|
476
|
+
WHERE TABLE_SCHEMA = DATABASE()
|
|
477
|
+
`.execute(db);
|
|
478
|
+
for (const row of result.rows) {
|
|
479
|
+
if (!tableSet.has(row.TABLE_NAME))
|
|
480
|
+
continue;
|
|
481
|
+
setDefault(row.TABLE_NAME, row.COLUMN_NAME, row.COLUMN_DEFAULT);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return defaultsByTable;
|
|
485
|
+
}
|
|
392
486
|
export async function pullSchema(options) {
|
|
393
487
|
const { db, destroy } = await createKyselyAdapter({
|
|
394
488
|
dialect: options.dialect,
|
|
@@ -403,16 +497,20 @@ export async function pullSchema(options) {
|
|
|
403
497
|
const foreignKeys = await extractForeignKeys(db, options.dialect);
|
|
404
498
|
const indexes = await extractIndexes(db, options.dialect, tableNames);
|
|
405
499
|
const primaryKeys = await extractPrimaryKeys(db, options.dialect, tableNames);
|
|
500
|
+
const columnDefaultsByTable = await extractColumnDefaults(db, options.dialect, tableNames);
|
|
406
501
|
const blocks = filtered.map((table) => buildModelBlock({
|
|
407
502
|
table,
|
|
408
503
|
foreignKeys,
|
|
409
504
|
indexes,
|
|
410
505
|
primaryKeys,
|
|
411
506
|
allTables,
|
|
507
|
+
columnDefaults: columnDefaultsByTable.get(table.name) ?? new Map(),
|
|
412
508
|
}));
|
|
413
509
|
const schema = [buildDatasourceBlock(options.dialect), ...blocks].join("\n\n");
|
|
414
|
-
|
|
415
|
-
|
|
510
|
+
if (options.writeFile !== false) {
|
|
511
|
+
await fs.mkdir(path.dirname(options.outputPath), { recursive: true });
|
|
512
|
+
await fs.writeFile(options.outputPath, schema.trimEnd() + "\n", "utf-8");
|
|
513
|
+
}
|
|
416
514
|
return {
|
|
417
515
|
outputPath: options.outputPath,
|
|
418
516
|
schema,
|