zenstack-kit 0.1.11 → 0.1.13
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 +15 -1
- package/dist/cli/app.d.ts.map +1 -1
- package/dist/cli/app.js +14 -2
- package/dist/cli/commands.d.ts +3 -0
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +38 -17
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/migrate.d.ts +9 -0
- package/dist/migrate.d.ts.map +1 -1
- package/dist/migrate.js +2 -0
- package/dist/migrations/prisma/apply.d.ts +4 -0
- package/dist/migrations/prisma/apply.d.ts.map +1 -1
- package/dist/migrations/prisma/apply.js +63 -14
- package/dist/migrations/prisma/create.d.ts +14 -0
- package/dist/migrations/prisma/create.d.ts.map +1 -1
- package/dist/migrations/prisma/create.js +31 -0
- package/dist/migrations/prisma/diff.d.ts +8 -1
- package/dist/migrations/prisma/diff.d.ts.map +1 -1
- package/dist/migrations/prisma/diff.js +71 -1
- package/dist/migrations/prisma.d.ts +1 -1
- package/dist/migrations/prisma.d.ts.map +1 -1
- package/dist/migrations/prisma.js +1 -1
- package/dist/schema/introspect.js +1 -1
- package/dist/schema/pull.d.ts.map +1 -1
- package/dist/schema/pull.js +60 -4
- package/dist/schema/snapshot.d.ts +7 -0
- package/dist/schema/snapshot.d.ts.map +1 -1
- package/dist/schema/snapshot.js +25 -7
- package/dist/sql/compiler.d.ts +20 -1
- package/dist/sql/compiler.d.ts.map +1 -1
- package/dist/sql/compiler.js +72 -11
- package/package.json +6 -1
package/README.md
CHANGED
|
@@ -100,6 +100,16 @@ zenstack-kit migrate apply
|
|
|
100
100
|
|
|
101
101
|
Migrations are tracked in the `_prisma_migrations` table, making them compatible with `prisma migrate deploy`.
|
|
102
102
|
|
|
103
|
+
### Migration log and checksums
|
|
104
|
+
|
|
105
|
+
zenstack-kit maintains `meta/_migration_log` alongside your migrations. It stores a SHA256 checksum for each `migration.sql` so integrity checks work even when you never apply migrations locally.
|
|
106
|
+
|
|
107
|
+
Flow:
|
|
108
|
+
- On `migrate create`, a checksum entry is appended to the log.
|
|
109
|
+
- On `migrate apply`, applied migrations are verified against the database and log.
|
|
110
|
+
- For pending migrations, the log is auto-updated from disk by default (so edits or empty migrations do not require manual rehash).
|
|
111
|
+
- Use `--strict` (or `ZENSTACK_MIGRATION_STRICT=1`) to disable auto-rehash and fail on any pending mismatch (recommended for CI).
|
|
112
|
+
|
|
103
113
|
## CLI Commands
|
|
104
114
|
|
|
105
115
|
Run `zenstack-kit` without arguments to launch the interactive menu, or run commands directly. For CI or non-TTY environments, pass `--no-ui` to bypass Ink.
|
|
@@ -142,6 +152,8 @@ Options:
|
|
|
142
152
|
- `-s, --schema <path>` - Path to ZenStack schema
|
|
143
153
|
- `-m, --migrations <path>` - Migrations directory
|
|
144
154
|
- `--dialect <dialect>` - Database dialect (`sqlite`, `postgres`, `mysql`)
|
|
155
|
+
- `--empty` - Create an empty migration (no schema diff)
|
|
156
|
+
- `--update-snapshot` - Update snapshot when used with `--empty`
|
|
145
157
|
- `-c, --config <path>` - Path to zenstack-kit config file
|
|
146
158
|
|
|
147
159
|
### `zenstack-kit migrate apply`
|
|
@@ -158,13 +170,15 @@ Options:
|
|
|
158
170
|
- `--url <url>` - Database connection URL (overrides config)
|
|
159
171
|
- `--table <name>` - Migrations table name (default: `_prisma_migrations`)
|
|
160
172
|
- `--db-schema <name>` - Database schema for migrations table (PostgreSQL only, default: `public`)
|
|
173
|
+
- `--migration <name>` - Apply a single migration (must be the next pending one)
|
|
161
174
|
- `--preview` - Preview pending migrations without applying
|
|
162
175
|
- `--mark-applied` - Mark pending migrations as applied without running SQL
|
|
176
|
+
- `--strict` - Enforce pending migration log checksums (no auto-rehash)
|
|
163
177
|
- `-c, --config <path>` - Path to zenstack-kit config file
|
|
164
178
|
|
|
165
179
|
### `zenstack-kit migrate rehash`
|
|
166
180
|
|
|
167
|
-
Rebuild the migration log checksums from the `migration.sql` files (useful after manual edits).
|
|
181
|
+
Rebuild the migration log checksums from the `migration.sql` files (useful after manual edits or when strict mode fails).
|
|
168
182
|
|
|
169
183
|
```bash
|
|
170
184
|
zenstack-kit migrate rehash
|
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;;;;;;;;;GASG;
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/cli/app.tsx"],"names":[],"mappings":";AAEA;;;;;;;;;GASG;AA4YH,wBAAgB,MAAM,SA4BrB"}
|
package/dist/cli/app.js
CHANGED
|
@@ -89,6 +89,15 @@ function parseArgs() {
|
|
|
89
89
|
else if (arg === "--mark-applied") {
|
|
90
90
|
options.markApplied = true;
|
|
91
91
|
}
|
|
92
|
+
else if (arg === "--strict") {
|
|
93
|
+
options.strict = true;
|
|
94
|
+
}
|
|
95
|
+
else if (arg === "--empty") {
|
|
96
|
+
options.empty = true;
|
|
97
|
+
}
|
|
98
|
+
else if (arg === "--update-snapshot") {
|
|
99
|
+
options.updateSnapshot = true;
|
|
100
|
+
}
|
|
92
101
|
else if (arg === "--force" || arg === "-f") {
|
|
93
102
|
options.force = true;
|
|
94
103
|
}
|
|
@@ -116,7 +125,7 @@ function Status({ type, message }) {
|
|
|
116
125
|
}
|
|
117
126
|
// Help display component
|
|
118
127
|
function HelpDisplay() {
|
|
119
|
-
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: "--migration <name> Target a single migration (rehash
|
|
128
|
+
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: "--migration <name> Target a single migration (apply/rehash)" }), _jsx(Text, { dimColor: true, children: "--no-ui Disable Ink UI (useful for CI/non-TTY)" }), _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: "--empty Create an empty migration (no schema diff)" }), _jsx(Text, { dimColor: true, children: "--update-snapshot Update snapshot when used with --empty" }), _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: "--strict Enforce pending migration log checksums (no auto-rehash)" }), _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" })] })] }));
|
|
120
129
|
}
|
|
121
130
|
function CliApp({ initialCommand, options }) {
|
|
122
131
|
const { exit } = useApp();
|
|
@@ -255,12 +264,15 @@ function printHelpText() {
|
|
|
255
264
|
" -n, --name <name> Migration name",
|
|
256
265
|
" --dialect <dialect> Database dialect (sqlite, postgres, mysql)",
|
|
257
266
|
" --url <url> Database connection URL",
|
|
258
|
-
" --migration <name> Target a single migration (rehash
|
|
267
|
+
" --migration <name> Target a single migration (apply/rehash)",
|
|
259
268
|
" --no-ui Disable Ink UI (useful for CI/non-TTY)",
|
|
260
269
|
" --create-initial Create initial migration (skip prompt)",
|
|
261
270
|
" --baseline Create baseline only (skip prompt)",
|
|
271
|
+
" --empty Create an empty migration (no schema diff)",
|
|
272
|
+
" --update-snapshot Update snapshot when used with --empty",
|
|
262
273
|
" --preview Preview pending migrations without applying",
|
|
263
274
|
" --mark-applied Mark pending migrations as applied without running SQL",
|
|
275
|
+
" --strict Enforce pending migration log checksums (no auto-rehash)",
|
|
264
276
|
" -f, --force Force operation without confirmation",
|
|
265
277
|
" -c, --config <path> Path to zenstack-kit config file",
|
|
266
278
|
];
|
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
|
@@ -10,7 +10,7 @@ import * as os from "os";
|
|
|
10
10
|
import { execFileSync } from "child_process";
|
|
11
11
|
import { loadConfig } from "../config/loader.js";
|
|
12
12
|
import { pullSchema } from "../schema/pull.js";
|
|
13
|
-
import { createPrismaMigration, applyPrismaMigrations, previewPrismaMigrations, hasPrismaSchemaChanges, hasSnapshot, scanMigrationFolders, readMigrationLog, writeMigrationLog, getMigrationLogPath, calculateChecksum, initializeSnapshot, createInitialMigration, detectPotentialRenames, } from "../migrations/prisma.js";
|
|
13
|
+
import { createPrismaMigration, createEmptyMigration, applyPrismaMigrations, previewPrismaMigrations, hasPrismaSchemaChanges, hasSnapshot, scanMigrationFolders, readMigrationLog, writeMigrationLog, getMigrationLogPath, calculateChecksum, initializeSnapshot, createInitialMigration, detectPotentialRenames, } from "../migrations/prisma.js";
|
|
14
14
|
export class CommandError extends Error {
|
|
15
15
|
constructor(message) {
|
|
16
16
|
super(message);
|
|
@@ -64,17 +64,22 @@ export async function runMigrateGenerate(ctx) {
|
|
|
64
64
|
if (!snapshotExists) {
|
|
65
65
|
throw new CommandError("No snapshot found. Run 'zenstack-kit init' first.");
|
|
66
66
|
}
|
|
67
|
+
const isEmpty = Boolean(ctx.options.empty);
|
|
67
68
|
ctx.log("info", "Generating migration...");
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
if (!isEmpty) {
|
|
70
|
+
const hasChanges = await hasPrismaSchemaChanges({
|
|
71
|
+
schemaPath,
|
|
72
|
+
outputPath,
|
|
73
|
+
});
|
|
74
|
+
if (!hasChanges) {
|
|
75
|
+
ctx.log("warning", "No schema changes detected");
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
75
78
|
}
|
|
76
79
|
// Detect potential renames and prompt user to disambiguate
|
|
77
|
-
const potentialRenames =
|
|
80
|
+
const potentialRenames = isEmpty
|
|
81
|
+
? { tables: [], columns: [] }
|
|
82
|
+
: await detectPotentialRenames({ schemaPath, outputPath });
|
|
78
83
|
const renameTables = [];
|
|
79
84
|
const renameColumns = [];
|
|
80
85
|
// Prompt for table renames
|
|
@@ -112,20 +117,30 @@ export async function runMigrateGenerate(ctx) {
|
|
|
112
117
|
return;
|
|
113
118
|
}
|
|
114
119
|
}
|
|
115
|
-
const migration =
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
const migration = isEmpty
|
|
121
|
+
? await createEmptyMigration({
|
|
122
|
+
name,
|
|
123
|
+
schemaPath,
|
|
124
|
+
outputPath,
|
|
125
|
+
updateSnapshot: ctx.options.updateSnapshot,
|
|
126
|
+
})
|
|
127
|
+
: await createPrismaMigration({
|
|
128
|
+
name,
|
|
129
|
+
schemaPath,
|
|
130
|
+
outputPath,
|
|
131
|
+
dialect,
|
|
132
|
+
renameTables: renameTables.length > 0 ? renameTables : undefined,
|
|
133
|
+
renameColumns: renameColumns.length > 0 ? renameColumns : undefined,
|
|
134
|
+
});
|
|
123
135
|
if (!migration) {
|
|
124
136
|
ctx.log("warning", "No schema changes detected");
|
|
125
137
|
return;
|
|
126
138
|
}
|
|
127
139
|
ctx.log("success", `Migration created: ${migration.folderName}/migration.sql`);
|
|
128
140
|
ctx.log("info", `Path: ${migration.folderPath}`);
|
|
141
|
+
if (isEmpty && ctx.options.updateSnapshot) {
|
|
142
|
+
ctx.log("info", "Snapshot updated to current schema");
|
|
143
|
+
}
|
|
129
144
|
ctx.log("info", "Next: run 'zenstack-kit migrate apply' (or --preview to review SQL).");
|
|
130
145
|
}
|
|
131
146
|
/**
|
|
@@ -191,6 +206,10 @@ export async function runMigrateApply(ctx) {
|
|
|
191
206
|
else {
|
|
192
207
|
ctx.log("info", "Applying migrations...");
|
|
193
208
|
}
|
|
209
|
+
const strictEnv = process.env.ZENSTACK_MIGRATION_STRICT;
|
|
210
|
+
const strict = ctx.options.strict === true ||
|
|
211
|
+
strictEnv === "1" ||
|
|
212
|
+
(strictEnv ? strictEnv.toLowerCase() === "true" : false);
|
|
194
213
|
const result = await applyPrismaMigrations({
|
|
195
214
|
migrationsFolder: outputPath,
|
|
196
215
|
dialect,
|
|
@@ -199,6 +218,8 @@ export async function runMigrateApply(ctx) {
|
|
|
199
218
|
migrationsTable,
|
|
200
219
|
migrationsSchema,
|
|
201
220
|
markApplied: ctx.options.markApplied,
|
|
221
|
+
strict,
|
|
222
|
+
targetMigration: ctx.options.migration,
|
|
202
223
|
});
|
|
203
224
|
// Handle coherence errors
|
|
204
225
|
if (result.coherenceErrors && result.coherenceErrors.length > 0) {
|
package/dist/index.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export { introspectSchema, type SchemaInfo, type ModelInfo, type FieldInfo } fro
|
|
|
10
10
|
export { createMigration, getSchemaDiff, hasSchemaChanges, initSnapshot, type MigrationOptions, type Migration, type InitSnapshotOptions, type InitSnapshotResult, } from "./migrations/diff.js";
|
|
11
11
|
export { applyMigrations, type ApplyMigrationsOptions } from "./migrations/apply.js";
|
|
12
12
|
export { setPromptProvider, type PromptProvider } from "./cli/prompt-provider.js";
|
|
13
|
-
export { createPrismaMigration, applyPrismaMigrations, previewPrismaMigrations, hasPrismaSchemaChanges, createInitialMigration, initializeSnapshot, hasSnapshot, scanMigrationFolders, readMigrationLog, writeMigrationLog, appendToMigrationLog, getMigrationLogPath, calculateChecksum, detectPotentialRenames, type PrismaMigrationOptions, type PrismaMigration, type ApplyPrismaMigrationsOptions, type ApplyPrismaMigrationsResult, type PreviewPrismaMigrationsResult, type CreateInitialMigrationOptions, type MigrationLogEntry, type PotentialTableRename, type PotentialColumnRename, type PotentialRenames, } from "./migrations/prisma.js";
|
|
13
|
+
export { createPrismaMigration, createEmptyMigration, applyPrismaMigrations, previewPrismaMigrations, hasPrismaSchemaChanges, createInitialMigration, initializeSnapshot, hasSnapshot, scanMigrationFolders, readMigrationLog, writeMigrationLog, appendToMigrationLog, getMigrationLogPath, calculateChecksum, detectPotentialRenames, type PrismaMigrationOptions, type PrismaMigration, type CreateEmptyMigrationOptions, type ApplyPrismaMigrationsOptions, type ApplyPrismaMigrationsResult, type PreviewPrismaMigrationsResult, type CreateInitialMigrationOptions, type MigrationLogEntry, type PotentialTableRename, type PotentialColumnRename, type PotentialRenames, } from "./migrations/prisma.js";
|
|
14
14
|
export { migrate, type MigrateOptions, type MigrateResult } from "./migrate.js";
|
|
15
15
|
export { defineConfig, type ZenStackKitConfig } from "./config/index.js";
|
|
16
16
|
export { type RenameChoice } from "./cli/prompts.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,gBAAgB,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAC3G,OAAO,EACL,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACxB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAGlF,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,uBAAuB,EACvB,sBAAsB,EACtB,sBAAsB,EACtB,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EACpB,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,EAChC,KAAK,6BAA6B,EAClC,KAAK,6BAA6B,EAClC,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,GACtB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAGhF,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGrD,OAAO,EACL,mBAAmB,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,GACnB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,gBAAgB,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAC3G,OAAO,EACL,eAAe,EACf,aAAa,EACb,gBAAgB,EAChB,YAAY,EACZ,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACd,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,GACxB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,KAAK,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAGlF,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,qBAAqB,EACrB,uBAAuB,EACvB,sBAAsB,EACtB,sBAAsB,EACtB,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EACpB,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EACpB,KAAK,2BAA2B,EAChC,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,EAChC,KAAK,6BAA6B,EAClC,KAAK,6BAA6B,EAClC,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,GACtB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAGhF,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGrD,OAAO,EACL,mBAAmB,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,GACnB,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ export { createMigration, getSchemaDiff, hasSchemaChanges, initSnapshot, } from
|
|
|
12
12
|
export { applyMigrations } from "./migrations/apply.js";
|
|
13
13
|
export { setPromptProvider } from "./cli/prompt-provider.js";
|
|
14
14
|
// Prisma-compatible migrations (default)
|
|
15
|
-
export { createPrismaMigration, applyPrismaMigrations, previewPrismaMigrations, hasPrismaSchemaChanges, createInitialMigration, initializeSnapshot, hasSnapshot, scanMigrationFolders, readMigrationLog, writeMigrationLog, appendToMigrationLog, getMigrationLogPath, calculateChecksum, detectPotentialRenames, } from "./migrations/prisma.js";
|
|
15
|
+
export { createPrismaMigration, createEmptyMigration, applyPrismaMigrations, previewPrismaMigrations, hasPrismaSchemaChanges, createInitialMigration, initializeSnapshot, hasSnapshot, scanMigrationFolders, readMigrationLog, writeMigrationLog, appendToMigrationLog, getMigrationLogPath, calculateChecksum, detectPotentialRenames, } from "./migrations/prisma.js";
|
|
16
16
|
// High-level programmatic API
|
|
17
17
|
export { migrate } from "./migrate.js";
|
|
18
18
|
// CLI utilities
|
package/dist/migrate.d.ts
CHANGED
|
@@ -61,6 +61,15 @@ export interface MigrateOptions {
|
|
|
61
61
|
* @default false
|
|
62
62
|
*/
|
|
63
63
|
preview?: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Apply a single migration by name (must be the next pending one).
|
|
66
|
+
*/
|
|
67
|
+
migration?: string;
|
|
68
|
+
/**
|
|
69
|
+
* Enforce pending migration log checksums (no auto-rehash).
|
|
70
|
+
* @default false
|
|
71
|
+
*/
|
|
72
|
+
strict?: boolean;
|
|
64
73
|
/**
|
|
65
74
|
* Current working directory for config resolution.
|
|
66
75
|
* @default process.cwd()
|
package/dist/migrate.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,EAGL,KAAK,2BAA2B,EAChC,KAAK,6BAA6B,EACnC,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAG7D,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;OAGG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;IAExB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,aAAa,GACrB,CAAC,2BAA2B,GAAG;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,GACjD,CAAC,6BAA6B,GAAG;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAsB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,aAAa,CAAC,
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../src/migrate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,EAGL,KAAK,2BAA2B,EAChC,KAAK,6BAA6B,EACnC,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAG7D,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;OAGG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;IAExB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,aAAa,GACrB,CAAC,2BAA2B,GAAG;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC,GACjD,CAAC,6BAA6B,GAAG;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC,CAAC;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAsB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,aAAa,CAAC,CAwFlF"}
|
package/dist/migrate.js
CHANGED
|
@@ -119,6 +119,8 @@ export async function migrate(options = {}) {
|
|
|
119
119
|
databasePath,
|
|
120
120
|
migrationsTable,
|
|
121
121
|
migrationsSchema,
|
|
122
|
+
strict: options.strict,
|
|
123
|
+
targetMigration: options.migration,
|
|
122
124
|
});
|
|
123
125
|
if (result.failed) {
|
|
124
126
|
throw new Error(`Migration failed: ${result.failed.migrationName} - ${result.failed.error}`);
|
|
@@ -14,6 +14,10 @@ export interface ApplyPrismaMigrationsOptions {
|
|
|
14
14
|
migrationsSchema?: string;
|
|
15
15
|
/** Mark migrations as applied without executing SQL */
|
|
16
16
|
markApplied?: boolean;
|
|
17
|
+
/** Enforce checksum/log consistency for pending migrations (no auto-rehash) */
|
|
18
|
+
strict?: boolean;
|
|
19
|
+
/** Apply a single migration by name */
|
|
20
|
+
targetMigration?: string;
|
|
17
21
|
}
|
|
18
22
|
export interface ApplyPrismaMigrationsResult {
|
|
19
23
|
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;IAC1B,uDAAuD;IACvD,WAAW,CAAC,EAAE,OAAO,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;IACtB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,uCAAuC;IACvC,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;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,CAoLtC;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,6BAA6B,CAAC,CA0DxC"}
|
|
@@ -3,7 +3,7 @@ import * as path from "path";
|
|
|
3
3
|
import * as crypto from "crypto";
|
|
4
4
|
import { sql } from "kysely";
|
|
5
5
|
import { createKyselyAdapter } from "../../sql/kysely-adapter.js";
|
|
6
|
-
import { calculateChecksum, readMigrationLog } from "./log.js";
|
|
6
|
+
import { calculateChecksum, readMigrationLog, writeMigrationLog } from "./log.js";
|
|
7
7
|
/**
|
|
8
8
|
* Ensure _prisma_migrations table exists
|
|
9
9
|
*/
|
|
@@ -276,7 +276,34 @@ export async function applyPrismaMigrations(options) {
|
|
|
276
276
|
applied: [],
|
|
277
277
|
alreadyApplied: [],
|
|
278
278
|
};
|
|
279
|
-
|
|
279
|
+
const logIndex = new Map(migrationLog.map((entry, index) => [entry.name, index]));
|
|
280
|
+
let logUpdated = false;
|
|
281
|
+
let migrationFoldersToApply = migrationFoldersWithSql;
|
|
282
|
+
if (options.targetMigration) {
|
|
283
|
+
const target = options.targetMigration;
|
|
284
|
+
const pendingMigrations = migrationFoldersWithSql.filter((name) => !appliedMigrations.has(name));
|
|
285
|
+
if (appliedMigrations.has(target)) {
|
|
286
|
+
result.alreadyApplied.push(target);
|
|
287
|
+
return result;
|
|
288
|
+
}
|
|
289
|
+
if (!migrationFoldersWithSql.includes(target)) {
|
|
290
|
+
result.failed = {
|
|
291
|
+
migrationName: target,
|
|
292
|
+
error: `Migration ${target} not found in migrations folder.`,
|
|
293
|
+
};
|
|
294
|
+
return result;
|
|
295
|
+
}
|
|
296
|
+
if (pendingMigrations[0] !== target) {
|
|
297
|
+
result.failed = {
|
|
298
|
+
migrationName: target,
|
|
299
|
+
error: `Migration ${target} is not the next pending migration.\n` +
|
|
300
|
+
`Apply pending migrations in order or omit --migration.`,
|
|
301
|
+
};
|
|
302
|
+
return result;
|
|
303
|
+
}
|
|
304
|
+
migrationFoldersToApply = [target];
|
|
305
|
+
}
|
|
306
|
+
for (const folderName of migrationFoldersToApply) {
|
|
280
307
|
if (appliedMigrations.has(folderName)) {
|
|
281
308
|
result.alreadyApplied.push(folderName);
|
|
282
309
|
continue;
|
|
@@ -290,18 +317,35 @@ export async function applyPrismaMigrations(options) {
|
|
|
290
317
|
continue; // Skip if no migration.sql
|
|
291
318
|
}
|
|
292
319
|
const checksum = calculateChecksum(sqlContent);
|
|
293
|
-
// Verify checksum against migration log (
|
|
294
|
-
const
|
|
295
|
-
if (
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
}
|
|
304
|
-
|
|
320
|
+
// Verify or update checksum against migration log (pending migrations only)
|
|
321
|
+
const logEntryIndex = logIndex.get(folderName);
|
|
322
|
+
if (logEntryIndex === undefined) {
|
|
323
|
+
if (options.strict) {
|
|
324
|
+
result.failed = {
|
|
325
|
+
migrationName: folderName,
|
|
326
|
+
error: `Migration ${folderName} is missing from the migration log.\n` +
|
|
327
|
+
`Run 'zenstack-kit migrate rehash' to rebuild log checksums or disable strict mode.`,
|
|
328
|
+
};
|
|
329
|
+
break;
|
|
330
|
+
}
|
|
331
|
+
migrationLog.push({ name: folderName, checksum });
|
|
332
|
+
logIndex.set(folderName, migrationLog.length - 1);
|
|
333
|
+
logUpdated = true;
|
|
334
|
+
}
|
|
335
|
+
else if (migrationLog[logEntryIndex].checksum !== checksum) {
|
|
336
|
+
if (options.strict) {
|
|
337
|
+
result.failed = {
|
|
338
|
+
migrationName: folderName,
|
|
339
|
+
error: `Checksum mismatch for migration ${folderName}.\n` +
|
|
340
|
+
`Expected: ${migrationLog[logEntryIndex].checksum}\n` +
|
|
341
|
+
`Found: ${checksum}\n` +
|
|
342
|
+
`The migration file may have been modified after generation.\n` +
|
|
343
|
+
`If you intended this, run 'zenstack-kit migrate rehash' to rebuild log checksums.`,
|
|
344
|
+
};
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
migrationLog[logEntryIndex] = { name: folderName, checksum };
|
|
348
|
+
logUpdated = true;
|
|
305
349
|
}
|
|
306
350
|
const startTime = Date.now();
|
|
307
351
|
try {
|
|
@@ -327,6 +371,11 @@ export async function applyPrismaMigrations(options) {
|
|
|
327
371
|
break; // Stop on first failure
|
|
328
372
|
}
|
|
329
373
|
}
|
|
374
|
+
if (logUpdated) {
|
|
375
|
+
migrationLog.sort((a, b) => a.name.localeCompare(b.name));
|
|
376
|
+
await writeMigrationLog(options.migrationsFolder, migrationLog);
|
|
377
|
+
logUpdated = false;
|
|
378
|
+
}
|
|
330
379
|
return result;
|
|
331
380
|
}
|
|
332
381
|
finally {
|
|
@@ -40,10 +40,24 @@ export interface CreateInitialMigrationOptions {
|
|
|
40
40
|
/** Database dialect for SQL generation */
|
|
41
41
|
dialect: KyselyDialect;
|
|
42
42
|
}
|
|
43
|
+
export interface CreateEmptyMigrationOptions {
|
|
44
|
+
/** Migration name */
|
|
45
|
+
name: string;
|
|
46
|
+
/** Path to ZenStack schema file */
|
|
47
|
+
schemaPath: string;
|
|
48
|
+
/** Output directory for migration files */
|
|
49
|
+
outputPath: string;
|
|
50
|
+
/** Update snapshot to current schema */
|
|
51
|
+
updateSnapshot?: boolean;
|
|
52
|
+
}
|
|
43
53
|
/**
|
|
44
54
|
* Generate timestamp string for migration folder name
|
|
45
55
|
*/
|
|
46
56
|
export declare function generateTimestamp(): string;
|
|
57
|
+
/**
|
|
58
|
+
* Create a Prisma-compatible empty migration
|
|
59
|
+
*/
|
|
60
|
+
export declare function createEmptyMigration(options: CreateEmptyMigrationOptions): Promise<PrismaMigration>;
|
|
47
61
|
/**
|
|
48
62
|
* Create a Prisma-compatible migration
|
|
49
63
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/migrations/prisma/create.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAMjE,MAAM,WAAW,sBAAsB;IACrC,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,OAAO,EAAE,aAAa,CAAC;IACvB,4BAA4B;IAC5B,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,6BAA6B;IAC7B,aAAa,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpE;AAED,MAAM,WAAW,eAAe;IAC9B,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,6BAA6B;IAC5C,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,OAAO,EAAE,aAAa,CAAC;CACxB;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAU1C;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAiDjC;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,eAAe,CAAC,CAwC1B;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,OAAO,CAAC,CAqBnB"}
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/migrations/prisma/create.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAMjE,MAAM,WAAW,sBAAsB;IACrC,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,OAAO,EAAE,aAAa,CAAC;IACvB,4BAA4B;IAC5B,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnD,6BAA6B;IAC7B,aAAa,CAAC,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpE;AAED,MAAM,WAAW,eAAe;IAC9B,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,6BAA6B;IAC5C,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,0CAA0C;IAC1C,OAAO,EAAE,aAAa,CAAC;CACxB;AAED,MAAM,WAAW,2BAA2B;IAC1C,qBAAqB;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,wCAAwC;IACxC,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CAU1C;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,eAAe,CAAC,CAgC1B;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAiDjC;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,eAAe,CAAC,CAwC1B;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,OAAO,CAAC,CAqBnB"}
|
|
@@ -18,6 +18,37 @@ export function generateTimestamp() {
|
|
|
18
18
|
String(now.getSeconds()).padStart(2, "0"),
|
|
19
19
|
].join("");
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Create a Prisma-compatible empty migration
|
|
23
|
+
*/
|
|
24
|
+
export async function createEmptyMigration(options) {
|
|
25
|
+
const timestamp = Date.now();
|
|
26
|
+
const timestampStr = generateTimestamp();
|
|
27
|
+
const safeName = options.name.replace(/[^a-z0-9]/gi, "_").toLowerCase();
|
|
28
|
+
const folderName = `${timestampStr}_${safeName}`;
|
|
29
|
+
const folderPath = path.join(options.outputPath, folderName);
|
|
30
|
+
const sqlContent = [
|
|
31
|
+
`-- Migration: ${options.name}`,
|
|
32
|
+
`-- Generated at: ${new Date(timestamp).toISOString()}`,
|
|
33
|
+
"",
|
|
34
|
+
"",
|
|
35
|
+
].join("\n");
|
|
36
|
+
await fs.mkdir(folderPath, { recursive: true });
|
|
37
|
+
await fs.writeFile(path.join(folderPath, "migration.sql"), sqlContent, "utf-8");
|
|
38
|
+
if (options.updateSnapshot) {
|
|
39
|
+
const currentSchema = await generateSchemaSnapshot(options.schemaPath);
|
|
40
|
+
const { snapshotPath } = getSnapshotPaths(options.outputPath);
|
|
41
|
+
await writeSnapshot(snapshotPath, currentSchema);
|
|
42
|
+
}
|
|
43
|
+
const checksum = calculateChecksum(sqlContent);
|
|
44
|
+
await appendToMigrationLog(options.outputPath, { name: folderName, checksum });
|
|
45
|
+
return {
|
|
46
|
+
folderName,
|
|
47
|
+
folderPath,
|
|
48
|
+
sql: sqlContent,
|
|
49
|
+
timestamp,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
21
52
|
/**
|
|
22
53
|
* Create a Prisma-compatible migration
|
|
23
54
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { KyselyDialect } from "../../sql/kysely-adapter.js";
|
|
2
|
-
import type { SchemaSnapshot, SchemaTable, SchemaColumn } from "../../schema/snapshot.js";
|
|
2
|
+
import type { SchemaSnapshot, SchemaTable, SchemaColumn, SchemaEnum } from "../../schema/snapshot.js";
|
|
3
3
|
export declare function diffSchemas(previous: SchemaSnapshot | null, current: SchemaSnapshot): {
|
|
4
4
|
addedModels: SchemaTable[];
|
|
5
5
|
removedModels: SchemaTable[];
|
|
@@ -83,6 +83,13 @@ export declare function diffSchemas(previous: SchemaSnapshot | null, current: Sc
|
|
|
83
83
|
from: string;
|
|
84
84
|
to: string;
|
|
85
85
|
}>;
|
|
86
|
+
addedEnums: SchemaEnum[];
|
|
87
|
+
removedEnums: SchemaEnum[];
|
|
88
|
+
alteredEnums: {
|
|
89
|
+
enumName: string;
|
|
90
|
+
addedValues: string[];
|
|
91
|
+
removedValues: string[];
|
|
92
|
+
}[];
|
|
86
93
|
};
|
|
87
94
|
type PrismaDiff = ReturnType<typeof diffSchemas>;
|
|
88
95
|
export declare function applyRenameMappings(diff: PrismaDiff, renameTables?: Array<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../../src/migrations/prisma/diff.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../../src/migrations/prisma/diff.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AA4KtG,wBAAgB,WAAW,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,EAAE,OAAO,EAAE,cAAc;;;;mBAsB5C,MAAM;gBAAU,YAAY;;;mBAC1B,MAAM;gBAAU,YAAY;;;mBAEvD,MAAM;oBACL,MAAM;kBACR,YAAY;iBACb,YAAY;;;mBAGV,MAAM;oBACL;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,EAAE,CAAA;SAAE;;;mBAGpC,MAAM;oBACL;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,EAAE,CAAA;SAAE;;;mBAGpC,MAAM;eACV;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,EAAE,CAAA;SAAE;;;mBAG/B,MAAM;eACV;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,EAAE,CAAA;SAAE;;;mBAG/B,MAAM;oBACL;YACV,IAAI,EAAE,MAAM,CAAC;YACb,OAAO,EAAE,MAAM,EAAE,CAAC;YAClB,eAAe,EAAE,MAAM,CAAC;YACxB,iBAAiB,EAAE,MAAM,EAAE,CAAC;SAC7B;;;mBAGU,MAAM;oBACL;YACV,IAAI,EAAE,MAAM,CAAC;YACb,OAAO,EAAE,MAAM,EAAE,CAAC;YAClB,eAAe,EAAE,MAAM,CAAC;YACxB,iBAAiB,EAAE,MAAM,EAAE,CAAC;SAC7B;;;mBAGU,MAAM;mBACN;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,EAAE,CAAA;SAAE;kBACpC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,MAAM,EAAE,CAAA;SAAE;;mBA4ExB,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;oBAClC,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;;;;kBA/ClE,MAAM;qBACH,MAAM,EAAE;uBACN,MAAM,EAAE;;EAkD1B;AAED,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAkGjD,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,UAAU,EAChB,YAAY,GAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAM,EACtD,aAAa,GAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAM,GACrE,UAAU,CA8FZ;AA2CD;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,UAAU,CAAC,OAAO,WAAW,CAAC,EACpC,OAAO,EAAE,aAAa,GACrB;IAAE,EAAE,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAuPlC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { compileCreateTable, compileDropTable, compileAddColumn, compileDropColumn, compileRenameTable, compileRenameColumn, compileCreateIndex, compileDropIndex, compileAddUniqueConstraint, compileDropConstraint, compileAddForeignKeyConstraint, compileAddPrimaryKeyConstraint, compileAlterColumn, } from "../../sql/compiler.js";
|
|
1
|
+
import { compileCreateTable, compileDropTable, compileAddColumn, compileDropColumn, compileRenameTable, compileRenameColumn, compileCreateIndex, compileDropIndex, compileAddUniqueConstraint, compileDropConstraint, compileAddForeignKeyConstraint, compileAddPrimaryKeyConstraint, compileAlterColumn, compileCreateEnum, compileDropEnum, compileAddEnumValue, } from "../../sql/compiler.js";
|
|
2
2
|
function diffTableChanges(previousModel, currentModel, tableName) {
|
|
3
3
|
const addedFields = [];
|
|
4
4
|
const removedFields = [];
|
|
@@ -140,6 +140,37 @@ export function diffSchemas(previous, current) {
|
|
|
140
140
|
removedForeignKeys.push(...modelDiff.removedForeignKeys);
|
|
141
141
|
primaryKeyChanges.push(...modelDiff.primaryKeyChanges);
|
|
142
142
|
}
|
|
143
|
+
// Diff enums
|
|
144
|
+
const previousEnums = new Map();
|
|
145
|
+
const currentEnums = new Map();
|
|
146
|
+
(previous?.enums ?? []).forEach((e) => previousEnums.set(e.name, e));
|
|
147
|
+
(current.enums ?? []).forEach((e) => currentEnums.set(e.name, e));
|
|
148
|
+
const addedEnums = [];
|
|
149
|
+
const removedEnums = [];
|
|
150
|
+
const alteredEnums = [];
|
|
151
|
+
for (const [enumName, enumDef] of currentEnums.entries()) {
|
|
152
|
+
if (!previousEnums.has(enumName)) {
|
|
153
|
+
addedEnums.push(enumDef);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
for (const [enumName, enumDef] of previousEnums.entries()) {
|
|
157
|
+
if (!currentEnums.has(enumName)) {
|
|
158
|
+
removedEnums.push(enumDef);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Check for altered enums (added/removed values)
|
|
162
|
+
for (const [enumName, currentEnum] of currentEnums.entries()) {
|
|
163
|
+
const previousEnum = previousEnums.get(enumName);
|
|
164
|
+
if (!previousEnum)
|
|
165
|
+
continue;
|
|
166
|
+
const prevValues = new Set(previousEnum.values);
|
|
167
|
+
const currValues = new Set(currentEnum.values);
|
|
168
|
+
const addedValues = currentEnum.values.filter((v) => !prevValues.has(v));
|
|
169
|
+
const removedValues = previousEnum.values.filter((v) => !currValues.has(v));
|
|
170
|
+
if (addedValues.length > 0 || removedValues.length > 0) {
|
|
171
|
+
alteredEnums.push({ enumName, addedValues, removedValues });
|
|
172
|
+
}
|
|
173
|
+
}
|
|
143
174
|
return {
|
|
144
175
|
addedModels,
|
|
145
176
|
removedModels,
|
|
@@ -155,6 +186,9 @@ export function diffSchemas(previous, current) {
|
|
|
155
186
|
primaryKeyChanges,
|
|
156
187
|
renamedTables: [],
|
|
157
188
|
renamedColumns: [],
|
|
189
|
+
addedEnums,
|
|
190
|
+
removedEnums,
|
|
191
|
+
alteredEnums,
|
|
158
192
|
};
|
|
159
193
|
}
|
|
160
194
|
function columnsSignature(columns) {
|
|
@@ -329,6 +363,32 @@ export function buildSqlStatements(diff, dialect) {
|
|
|
329
363
|
const up = [];
|
|
330
364
|
const down = [];
|
|
331
365
|
const compileOpts = { dialect };
|
|
366
|
+
// Create enums FIRST (before tables that use them)
|
|
367
|
+
for (const enumDef of diff.addedEnums) {
|
|
368
|
+
const sql = compileCreateEnum(enumDef, compileOpts);
|
|
369
|
+
if (sql) {
|
|
370
|
+
up.push(sql);
|
|
371
|
+
const dropSql = compileDropEnum(enumDef.name, compileOpts);
|
|
372
|
+
if (dropSql)
|
|
373
|
+
down.unshift(dropSql);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
// Add new values to existing enums
|
|
377
|
+
for (const altered of diff.alteredEnums) {
|
|
378
|
+
for (const value of altered.addedValues) {
|
|
379
|
+
const sql = compileAddEnumValue(altered.enumName, value, compileOpts);
|
|
380
|
+
if (sql) {
|
|
381
|
+
up.push(sql);
|
|
382
|
+
// Note: PostgreSQL doesn't support removing enum values easily,
|
|
383
|
+
// so we don't add a down migration for added values
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
// Note: Removing enum values in PostgreSQL requires recreating the type
|
|
387
|
+
// which is complex and potentially data-losing. We skip this for now.
|
|
388
|
+
if (altered.removedValues.length > 0 && dialect === "postgres") {
|
|
389
|
+
up.push(`-- WARNING: Removing enum values (${altered.removedValues.join(", ")}) from "${altered.enumName}" requires manual migration`);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
332
392
|
// Table renames
|
|
333
393
|
for (const rename of diff.renamedTables) {
|
|
334
394
|
up.push(compileRenameTable(rename.from, rename.to, compileOpts));
|
|
@@ -438,5 +498,15 @@ export function buildSqlStatements(diff, dialect) {
|
|
|
438
498
|
up.push(compileAddForeignKeyConstraint(tableName, foreignKey.name, foreignKey.columns, foreignKey.referencedTable, foreignKey.referencedColumns, compileOpts));
|
|
439
499
|
down.unshift(compileDropConstraint(tableName, foreignKey.name, compileOpts));
|
|
440
500
|
}
|
|
501
|
+
// Drop enums LAST (after tables that use them are dropped)
|
|
502
|
+
for (const enumDef of diff.removedEnums) {
|
|
503
|
+
const sql = compileDropEnum(enumDef.name, compileOpts);
|
|
504
|
+
if (sql) {
|
|
505
|
+
up.push(sql);
|
|
506
|
+
const createSql = compileCreateEnum(enumDef, compileOpts);
|
|
507
|
+
if (createSql)
|
|
508
|
+
down.unshift(createSql);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
441
511
|
return { up, down };
|
|
442
512
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { createPrismaMigration, createInitialMigration, hasPrismaSchemaChanges, type PrismaMigrationOptions, type PrismaMigration, type CreateInitialMigrationOptions, } from "./prisma/create.js";
|
|
1
|
+
export { createEmptyMigration, createPrismaMigration, createInitialMigration, hasPrismaSchemaChanges, type PrismaMigrationOptions, type PrismaMigration, type CreateInitialMigrationOptions, type CreateEmptyMigrationOptions, } from "./prisma/create.js";
|
|
2
2
|
export { applyPrismaMigrations, previewPrismaMigrations, type ApplyPrismaMigrationsOptions, type ApplyPrismaMigrationsResult, type PreviewPrismaMigrationsResult, type MigrationCoherenceError, type MigrationCoherenceResult, } from "./prisma/apply.js";
|
|
3
3
|
export { readMigrationLog, writeMigrationLog, appendToMigrationLog, scanMigrationFolders, getMigrationLogPath, calculateChecksum, type MigrationLogEntry, } from "./prisma/log.js";
|
|
4
4
|
export { initializeSnapshot, hasSnapshot, getSnapshotPaths, writeSnapshot, } from "./prisma/snapshot.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prisma.d.ts","sourceRoot":"","sources":["../../src/migrations/prisma.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EACpB,KAAK,6BAA6B,
|
|
1
|
+
{"version":3,"file":"prisma.d.ts","sourceRoot":"","sources":["../../src/migrations/prisma.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EACpB,KAAK,6BAA6B,EAClC,KAAK,2BAA2B,GACjC,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,qBAAqB,EACrB,uBAAuB,EACvB,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,EAChC,KAAK,6BAA6B,EAClC,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,GAC9B,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,kBAAkB,EAClB,WAAW,EACX,gBAAgB,EAChB,aAAa,GACd,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EACL,sBAAsB,EACtB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,GACtB,MAAM,oBAAoB,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { createPrismaMigration, createInitialMigration, hasPrismaSchemaChanges, } from "./prisma/create.js";
|
|
1
|
+
export { createEmptyMigration, createPrismaMigration, createInitialMigration, hasPrismaSchemaChanges, } from "./prisma/create.js";
|
|
2
2
|
export { applyPrismaMigrations, previewPrismaMigrations, } from "./prisma/apply.js";
|
|
3
3
|
export { readMigrationLog, writeMigrationLog, appendToMigrationLog, scanMigrationFolders, getMigrationLogPath, calculateChecksum, } from "./prisma/log.js";
|
|
4
4
|
export { initializeSnapshot, hasSnapshot, getSnapshotPaths, writeSnapshot, } from "./prisma/snapshot.js";
|
|
@@ -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;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;
|
|
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;AAsqBD,wBAAsB,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAkD1E"}
|
package/dist/schema/pull.js
CHANGED
|
@@ -91,7 +91,9 @@ function buildDatasourceBlock(dialect) {
|
|
|
91
91
|
].join("\n");
|
|
92
92
|
}
|
|
93
93
|
function buildModelBlock(options) {
|
|
94
|
-
const { table, foreignKeys, indexes, primaryKeys, allTables, columnDefaults } = options;
|
|
94
|
+
const { table, foreignKeys, indexes, primaryKeys, allTables, columnDefaults, enums = [] } = options;
|
|
95
|
+
// Build a map of enum names for quick lookup
|
|
96
|
+
const enumNames = new Set(enums.map((e) => e.name));
|
|
95
97
|
const modelName = toPascalCase(table.name) || "Model";
|
|
96
98
|
const fieldLines = [];
|
|
97
99
|
// Get primary key columns for this table
|
|
@@ -170,7 +172,18 @@ function buildModelBlock(options) {
|
|
|
170
172
|
for (const column of sortedColumns) {
|
|
171
173
|
const fieldName = toCamelCase(column.name) || column.name;
|
|
172
174
|
const mapped = fieldName !== column.name;
|
|
173
|
-
|
|
175
|
+
// Check if the column type is an enum (PostgreSQL stores udt_name for enum types)
|
|
176
|
+
const rawDataType = column.dataType.replace(/\[\]$/, ""); // Remove array suffix
|
|
177
|
+
const isEnumType = enumNames.has(rawDataType);
|
|
178
|
+
const isArray = column.dataType.endsWith("[]");
|
|
179
|
+
let type;
|
|
180
|
+
if (isEnumType) {
|
|
181
|
+
type = toPascalCase(rawDataType);
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
const normalized = normalizeType(column.dataType);
|
|
185
|
+
type = normalized.type;
|
|
186
|
+
}
|
|
174
187
|
const optional = column.isNullable ? "?" : "";
|
|
175
188
|
const modifiers = [];
|
|
176
189
|
const isPkColumn = pkColumns.has(column.name);
|
|
@@ -483,6 +496,44 @@ async function extractColumnDefaults(db, dialect, tableNames) {
|
|
|
483
496
|
}
|
|
484
497
|
return defaultsByTable;
|
|
485
498
|
}
|
|
499
|
+
async function extractEnums(db, dialect) {
|
|
500
|
+
const enums = [];
|
|
501
|
+
if (dialect === "postgres") {
|
|
502
|
+
// Query PostgreSQL system catalogs for enum types
|
|
503
|
+
const result = await sql `
|
|
504
|
+
SELECT
|
|
505
|
+
t.typname as enum_name,
|
|
506
|
+
e.enumlabel as enum_value
|
|
507
|
+
FROM pg_type t
|
|
508
|
+
JOIN pg_enum e ON t.oid = e.enumtypid
|
|
509
|
+
JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
|
|
510
|
+
WHERE n.nspname = 'public'
|
|
511
|
+
ORDER BY t.typname, e.enumsortorder
|
|
512
|
+
`.execute(db);
|
|
513
|
+
// Group by enum name
|
|
514
|
+
const enumMap = new Map();
|
|
515
|
+
for (const row of result.rows) {
|
|
516
|
+
if (!enumMap.has(row.enum_name)) {
|
|
517
|
+
enumMap.set(row.enum_name, []);
|
|
518
|
+
}
|
|
519
|
+
enumMap.get(row.enum_name).push(row.enum_value);
|
|
520
|
+
}
|
|
521
|
+
for (const [name, values] of enumMap) {
|
|
522
|
+
enums.push({ name, values });
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
// MySQL and SQLite don't have standalone enum types
|
|
526
|
+
// MySQL uses inline ENUM definitions in column types
|
|
527
|
+
return enums;
|
|
528
|
+
}
|
|
529
|
+
function buildEnumBlock(enumInfo) {
|
|
530
|
+
const lines = [`enum ${toPascalCase(enumInfo.name)} {`];
|
|
531
|
+
for (const value of enumInfo.values) {
|
|
532
|
+
lines.push(` ${value}`);
|
|
533
|
+
}
|
|
534
|
+
lines.push("}");
|
|
535
|
+
return lines.join("\n");
|
|
536
|
+
}
|
|
486
537
|
export async function pullSchema(options) {
|
|
487
538
|
const { db, destroy } = await createKyselyAdapter({
|
|
488
539
|
dialect: options.dialect,
|
|
@@ -498,15 +549,20 @@ export async function pullSchema(options) {
|
|
|
498
549
|
const indexes = await extractIndexes(db, options.dialect, tableNames);
|
|
499
550
|
const primaryKeys = await extractPrimaryKeys(db, options.dialect, tableNames);
|
|
500
551
|
const columnDefaultsByTable = await extractColumnDefaults(db, options.dialect, tableNames);
|
|
501
|
-
const
|
|
552
|
+
const enums = await extractEnums(db, options.dialect);
|
|
553
|
+
// Build enum blocks
|
|
554
|
+
const enumBlocks = enums.map((e) => buildEnumBlock(e));
|
|
555
|
+
// Build model blocks
|
|
556
|
+
const modelBlocks = filtered.map((table) => buildModelBlock({
|
|
502
557
|
table,
|
|
503
558
|
foreignKeys,
|
|
504
559
|
indexes,
|
|
505
560
|
primaryKeys,
|
|
506
561
|
allTables,
|
|
507
562
|
columnDefaults: columnDefaultsByTable.get(table.name) ?? new Map(),
|
|
563
|
+
enums, // Pass enums for type mapping
|
|
508
564
|
}));
|
|
509
|
-
const schema = [buildDatasourceBlock(options.dialect), ...
|
|
565
|
+
const schema = [buildDatasourceBlock(options.dialect), ...enumBlocks, ...modelBlocks].join("\n\n");
|
|
510
566
|
if (options.writeFile !== false) {
|
|
511
567
|
await fs.mkdir(path.dirname(options.outputPath), { recursive: true });
|
|
512
568
|
await fs.writeFile(options.outputPath, schema.trimEnd() + "\n", "utf-8");
|
|
@@ -10,6 +10,8 @@ export interface SchemaColumn {
|
|
|
10
10
|
isArray: boolean;
|
|
11
11
|
default?: string | number | boolean;
|
|
12
12
|
isAutoincrement?: boolean;
|
|
13
|
+
/** If true, type refers to an enum name rather than a SQL type */
|
|
14
|
+
isEnum?: boolean;
|
|
13
15
|
}
|
|
14
16
|
export interface SchemaConstraint {
|
|
15
17
|
name: string;
|
|
@@ -33,8 +35,13 @@ export interface SchemaTable {
|
|
|
33
35
|
indexes: SchemaIndex[];
|
|
34
36
|
foreignKeys: SchemaForeignKey[];
|
|
35
37
|
}
|
|
38
|
+
export interface SchemaEnum {
|
|
39
|
+
name: string;
|
|
40
|
+
values: string[];
|
|
41
|
+
}
|
|
36
42
|
export interface SchemaSnapshot {
|
|
37
43
|
tables: SchemaTable[];
|
|
44
|
+
enums: SchemaEnum[];
|
|
38
45
|
}
|
|
39
46
|
export interface SchemaSnapshotFile {
|
|
40
47
|
version: 2;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../src/schema/snapshot.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAWH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACpC,eAAe,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../src/schema/snapshot.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAWH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACpC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,kEAAkE;IAClE,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,iBAAiB,EAAE,gBAAgB,EAAE,CAAC;IACtC,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,WAAW,EAAE,gBAAgB,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,cAAc,CAAC;CACxB;AAiVD,wBAAsB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAkBxF;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,cAAc,GAAG,kBAAkB,CAMzE"}
|
package/dist/schema/snapshot.js
CHANGED
|
@@ -68,12 +68,17 @@ function getDefaultValue(field) {
|
|
|
68
68
|
// Return function name for other functions
|
|
69
69
|
return { hasDefault: true, default: `${funcName}()` };
|
|
70
70
|
}
|
|
71
|
+
// Handle enum default values like @default(USER) - these are ReferenceExpr
|
|
72
|
+
if (expr.$type === "ReferenceExpr") {
|
|
73
|
+
const enumValue = expr.target.$refText;
|
|
74
|
+
return { hasDefault: true, default: enumValue };
|
|
75
|
+
}
|
|
71
76
|
return { hasDefault: true };
|
|
72
77
|
}
|
|
73
78
|
function getTableName(model) {
|
|
74
79
|
const mapAttr = getAttribute(model, "@@map");
|
|
75
80
|
const mapped = getAttributeStringArg(mapAttr, ["name", "map"]);
|
|
76
|
-
return mapped ?? model.name
|
|
81
|
+
return mapped ?? model.name;
|
|
77
82
|
}
|
|
78
83
|
function getColumnName(field) {
|
|
79
84
|
const mapAttr = getAttribute(field, "@map");
|
|
@@ -124,12 +129,12 @@ function buildForeignKeyName(tableName, columns, _referencedTable, _referencedCo
|
|
|
124
129
|
function getFieldType(field) {
|
|
125
130
|
const ref = field.type.reference?.ref;
|
|
126
131
|
if (ref && isDataModel(ref)) {
|
|
127
|
-
return { type: ref.name, isRelation: true };
|
|
132
|
+
return { type: ref.name, isRelation: true, isEnum: false };
|
|
128
133
|
}
|
|
129
134
|
if (ref && isEnum(ref)) {
|
|
130
|
-
return { type: ref.name, isRelation: false };
|
|
135
|
+
return { type: ref.name, isRelation: false, isEnum: true };
|
|
131
136
|
}
|
|
132
|
-
return { type: field.type.type ?? "String", isRelation: false };
|
|
137
|
+
return { type: field.type.type ?? "String", isRelation: false, isEnum: false };
|
|
133
138
|
}
|
|
134
139
|
function getRelationFieldNames(field) {
|
|
135
140
|
const relationAttr = getAttribute(field, "@relation");
|
|
@@ -164,14 +169,16 @@ function parseModel(model) {
|
|
|
164
169
|
}
|
|
165
170
|
const defaultInfo = getDefaultValue(field);
|
|
166
171
|
const columnName = getColumnName(field);
|
|
167
|
-
|
|
172
|
+
// For enum types, store the enum name directly; for other types, map to SQL type
|
|
173
|
+
const columnType = typeInfo.isEnum ? typeInfo.type : mapFieldTypeToSQL(typeInfo.type);
|
|
168
174
|
columns.push({
|
|
169
175
|
name: columnName,
|
|
170
|
-
type:
|
|
176
|
+
type: columnType,
|
|
171
177
|
notNull: !field.type.optional,
|
|
172
178
|
isArray: field.type.array ?? false,
|
|
173
179
|
default: defaultInfo.default,
|
|
174
180
|
isAutoincrement: defaultInfo.isAutoincrement,
|
|
181
|
+
isEnum: typeInfo.isEnum || undefined,
|
|
175
182
|
});
|
|
176
183
|
}
|
|
177
184
|
const modelIdAttr = getAttribute(model, "@@id");
|
|
@@ -257,6 +264,13 @@ function parseModel(model) {
|
|
|
257
264
|
foreignKeys: foreignKeys.sort((a, b) => a.name.localeCompare(b.name)),
|
|
258
265
|
};
|
|
259
266
|
}
|
|
267
|
+
function parseEnum(enumDecl) {
|
|
268
|
+
const values = enumDecl.fields.map((field) => field.name);
|
|
269
|
+
return {
|
|
270
|
+
name: enumDecl.name,
|
|
271
|
+
values,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
260
274
|
export async function generateSchemaSnapshot(schemaPath) {
|
|
261
275
|
const loadResult = await loadDocument(schemaPath);
|
|
262
276
|
if (!loadResult.success) {
|
|
@@ -267,7 +281,11 @@ export async function generateSchemaSnapshot(schemaPath) {
|
|
|
267
281
|
const tables = dataModels
|
|
268
282
|
.map((model) => parseModel(model))
|
|
269
283
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
270
|
-
|
|
284
|
+
const enumDecls = loadResult.model.declarations.filter(isEnum);
|
|
285
|
+
const enums = enumDecls
|
|
286
|
+
.map((enumDecl) => parseEnum(enumDecl))
|
|
287
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
288
|
+
return { tables, enums };
|
|
271
289
|
}
|
|
272
290
|
export function createSnapshot(schema) {
|
|
273
291
|
return {
|
package/dist/sql/compiler.d.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* without requiring a database connection.
|
|
6
6
|
*/
|
|
7
7
|
import type { KyselyDialect } from "./kysely-adapter.js";
|
|
8
|
-
import type { SchemaTable, SchemaColumn } from "../schema/snapshot.js";
|
|
8
|
+
import type { SchemaTable, SchemaColumn, SchemaEnum } from "../schema/snapshot.js";
|
|
9
9
|
export interface SqlMigration {
|
|
10
10
|
up: string[];
|
|
11
11
|
down: string[];
|
|
@@ -71,4 +71,23 @@ export declare function compileAlterColumn(tableName: string, columnName: string
|
|
|
71
71
|
setDefault?: string | number | boolean;
|
|
72
72
|
dropDefault?: boolean;
|
|
73
73
|
}, options: CompileSqlOptions): string[];
|
|
74
|
+
/**
|
|
75
|
+
* Compile a CREATE TYPE ... AS ENUM statement for PostgreSQL
|
|
76
|
+
* For MySQL and SQLite, enums are handled differently (inline or as text)
|
|
77
|
+
*/
|
|
78
|
+
export declare function compileCreateEnum(enumDef: SchemaEnum, options: CompileSqlOptions): string | null;
|
|
79
|
+
/**
|
|
80
|
+
* Compile a DROP TYPE statement for PostgreSQL
|
|
81
|
+
*/
|
|
82
|
+
export declare function compileDropEnum(enumName: string, options: CompileSqlOptions): string | null;
|
|
83
|
+
/**
|
|
84
|
+
* Compile an ALTER TYPE ... ADD VALUE statement for PostgreSQL
|
|
85
|
+
* Adds a new value to an existing enum type
|
|
86
|
+
*/
|
|
87
|
+
export declare function compileAddEnumValue(enumName: string, value: string, options: CompileSqlOptions): string | null;
|
|
88
|
+
/**
|
|
89
|
+
* Map column type considering enum types
|
|
90
|
+
* For enum columns, returns the enum type name (PostgreSQL) or text (other dialects)
|
|
91
|
+
*/
|
|
92
|
+
export declare function mapColumnTypeWithEnum(column: SchemaColumn, dialect: KyselyDialect): string;
|
|
74
93
|
//# sourceMappingURL=compiler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../src/sql/compiler.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAgBH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../../src/sql/compiler.ts"],"names":[],"mappings":"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"}
|
package/dist/sql/compiler.js
CHANGED
|
@@ -47,10 +47,12 @@ export function compileCreateTable(model, options) {
|
|
|
47
47
|
const db = createCompilerDb(options.dialect);
|
|
48
48
|
let builder = db.schema.createTable(model.name);
|
|
49
49
|
for (const column of model.columns) {
|
|
50
|
-
const columnType =
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
const columnType = column.isEnum
|
|
51
|
+
? mapColumnTypeWithEnum(column, options.dialect)
|
|
52
|
+
: mapColumnType(column.type, options.dialect, {
|
|
53
|
+
isArray: column.isArray,
|
|
54
|
+
isAutoincrement: column.isAutoincrement,
|
|
55
|
+
});
|
|
54
56
|
builder = builder.addColumn(column.name, sql.raw(columnType), (cb) => {
|
|
55
57
|
// For SERIAL types in PostgreSQL, NOT NULL is implicit and we don't need defaults
|
|
56
58
|
const isSerialType = column.isAutoincrement && options.dialect === "postgres";
|
|
@@ -58,7 +60,7 @@ export function compileCreateTable(model, options) {
|
|
|
58
60
|
cb = cb.notNull();
|
|
59
61
|
}
|
|
60
62
|
if (column.default !== undefined && !isSerialType) {
|
|
61
|
-
cb = cb.defaultTo(sql.raw(formatDefault(column.default, options.dialect)));
|
|
63
|
+
cb = cb.defaultTo(sql.raw(formatDefault(column.default, options.dialect, column)));
|
|
62
64
|
}
|
|
63
65
|
return cb;
|
|
64
66
|
});
|
|
@@ -89,10 +91,12 @@ export function compileDropTable(tableName, options) {
|
|
|
89
91
|
*/
|
|
90
92
|
export function compileAddColumn(tableName, column, options) {
|
|
91
93
|
const db = createCompilerDb(options.dialect);
|
|
92
|
-
const columnType =
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
const columnType = column.isEnum
|
|
95
|
+
? mapColumnTypeWithEnum(column, options.dialect)
|
|
96
|
+
: mapColumnType(column.type, options.dialect, {
|
|
97
|
+
isArray: column.isArray,
|
|
98
|
+
isAutoincrement: column.isAutoincrement,
|
|
99
|
+
});
|
|
96
100
|
return (db.schema
|
|
97
101
|
.alterTable(tableName)
|
|
98
102
|
.addColumn(column.name, sql.raw(columnType), (cb) => {
|
|
@@ -101,7 +105,7 @@ export function compileAddColumn(tableName, column, options) {
|
|
|
101
105
|
cb = cb.notNull();
|
|
102
106
|
}
|
|
103
107
|
if (column.default !== undefined && !isSerialType) {
|
|
104
|
-
cb = cb.defaultTo(sql.raw(formatDefault(column.default, options.dialect)));
|
|
108
|
+
cb = cb.defaultTo(sql.raw(formatDefault(column.default, options.dialect, column)));
|
|
105
109
|
}
|
|
106
110
|
return cb;
|
|
107
111
|
})
|
|
@@ -251,12 +255,17 @@ function mapColumnType(type, dialect, options) {
|
|
|
251
255
|
/**
|
|
252
256
|
* Format a default value for SQL
|
|
253
257
|
*/
|
|
254
|
-
function formatDefault(value, dialect) {
|
|
258
|
+
function formatDefault(value, dialect, column) {
|
|
255
259
|
if (typeof value === "string") {
|
|
256
260
|
// Check if it's a function call like now() or autoincrement()
|
|
257
261
|
if (/^\w+\([^)]*\)$/.test(value)) {
|
|
258
262
|
return value;
|
|
259
263
|
}
|
|
264
|
+
// For enum columns in PostgreSQL, we need to cast the default value
|
|
265
|
+
if (column?.isEnum && dialect === "postgres") {
|
|
266
|
+
const escapedValue = value.replace(/'/g, "''");
|
|
267
|
+
return `'${escapedValue}'::"${column.type}"`;
|
|
268
|
+
}
|
|
260
269
|
// Escape string values
|
|
261
270
|
return `'${value.replace(/'/g, "''")}'`;
|
|
262
271
|
}
|
|
@@ -268,3 +277,55 @@ function formatDefault(value, dialect) {
|
|
|
268
277
|
}
|
|
269
278
|
return String(value);
|
|
270
279
|
}
|
|
280
|
+
/**
|
|
281
|
+
* Compile a CREATE TYPE ... AS ENUM statement for PostgreSQL
|
|
282
|
+
* For MySQL and SQLite, enums are handled differently (inline or as text)
|
|
283
|
+
*/
|
|
284
|
+
export function compileCreateEnum(enumDef, options) {
|
|
285
|
+
if (options.dialect !== "postgres") {
|
|
286
|
+
// MySQL and SQLite don't have standalone enum types
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
const values = enumDef.values.map((v) => `'${v.replace(/'/g, "''")}'`).join(", ");
|
|
290
|
+
return `CREATE TYPE "${enumDef.name}" AS ENUM (${values});`;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Compile a DROP TYPE statement for PostgreSQL
|
|
294
|
+
*/
|
|
295
|
+
export function compileDropEnum(enumName, options) {
|
|
296
|
+
if (options.dialect !== "postgres") {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
return `DROP TYPE IF EXISTS "${enumName}";`;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Compile an ALTER TYPE ... ADD VALUE statement for PostgreSQL
|
|
303
|
+
* Adds a new value to an existing enum type
|
|
304
|
+
*/
|
|
305
|
+
export function compileAddEnumValue(enumName, value, options) {
|
|
306
|
+
if (options.dialect !== "postgres") {
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
const escapedValue = value.replace(/'/g, "''");
|
|
310
|
+
return `ALTER TYPE "${enumName}" ADD VALUE '${escapedValue}';`;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Map column type considering enum types
|
|
314
|
+
* For enum columns, returns the enum type name (PostgreSQL) or text (other dialects)
|
|
315
|
+
*/
|
|
316
|
+
export function mapColumnTypeWithEnum(column, dialect) {
|
|
317
|
+
if (column.isEnum) {
|
|
318
|
+
if (dialect === "postgres") {
|
|
319
|
+
// Use the native enum type for PostgreSQL
|
|
320
|
+
const baseType = `"${column.type}"`;
|
|
321
|
+
return column.isArray ? `${baseType}[]` : baseType;
|
|
322
|
+
}
|
|
323
|
+
// For MySQL and SQLite, fall back to text
|
|
324
|
+
return column.isArray ? "text[]" : "text";
|
|
325
|
+
}
|
|
326
|
+
// Use existing type mapping for non-enum columns
|
|
327
|
+
return mapColumnType(column.type, dialect, {
|
|
328
|
+
isArray: column.isArray,
|
|
329
|
+
isAutoincrement: column.isAutoincrement,
|
|
330
|
+
});
|
|
331
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zenstack-kit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"description": "Drizzle-kit like CLI tooling for ZenStack schemas with Kysely support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -73,5 +73,10 @@
|
|
|
73
73
|
"pg": "^8.13.1",
|
|
74
74
|
"typescript": "^5.9.3",
|
|
75
75
|
"vitest": "^4.0.16"
|
|
76
|
+
},
|
|
77
|
+
"pnpm": {
|
|
78
|
+
"onlyBuiltDependencies": [
|
|
79
|
+
"better-sqlite3"
|
|
80
|
+
]
|
|
76
81
|
}
|
|
77
82
|
}
|