zenstack-kit 0.1.5 → 0.1.8

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 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:generate --name add_posts
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:apply
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:generate`
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:generate --name add_users
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:apply`
144
+ ### `zenstack-kit migrate apply`
145
145
 
146
146
  Apply pending migrations to the database.
147
147
 
148
148
  ```bash
149
- zenstack-kit migrate:apply
149
+ zenstack-kit migrate apply
150
150
  ```
151
151
 
152
152
  Options:
@@ -156,6 +156,20 @@ 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
160
+ - `-c, --config <path>` - Path to zenstack-kit config file
161
+
162
+ ### `zenstack-kit migrate rehash`
163
+
164
+ Rebuild the migration log checksums from the `migration.sql` files (useful after manual edits).
165
+
166
+ ```bash
167
+ zenstack-kit migrate rehash
168
+ ```
169
+
170
+ Options:
171
+ - `-m, --migrations <path>` - Migrations directory
172
+ - `--migration <name>` - Rehash a single migration folder
159
173
  - `-c, --config <path>` - Path to zenstack-kit config file
160
174
 
161
175
  ### `zenstack-kit pull`
@@ -170,6 +184,7 @@ Options:
170
184
  - `-o, --output <path>` - Output path for schema (default: `./schema.zmodel`)
171
185
  - `--dialect <dialect>` - Database dialect
172
186
  - `--url <url>` - Database connection URL
187
+ - `--preview` - Preview generated schema and diff without writing files
173
188
  - `-c, --config <path>` - Path to zenstack-kit config file
174
189
 
175
190
  Features:
package/dist/cli/app.d.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  * Commands:
6
6
  * migrate create Generate a new SQL migration
7
7
  * migrate apply Apply pending migrations
8
+ * migrate rehash Rebuild migration log checksums from migration.sql files
8
9
  * init Initialize snapshot from existing schema
9
10
  * pull Introspect database and generate schema
10
11
  */
@@ -1 +1 @@
1
- {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/cli/app.tsx"],"names":[],"mappings":";AAEA;;;;;;;;GAQG;AA0SH,wBAAgB,MAAM,SAkBrB"}
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/cli/app.tsx"],"names":[],"mappings":";AAEA;;;;;;;;;GASG;AAuTH,wBAAgB,MAAM,SAkBrB"}
package/dist/cli/app.js CHANGED
@@ -6,17 +6,19 @@ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-run
6
6
  * Commands:
7
7
  * migrate create Generate a new SQL migration
8
8
  * migrate apply Apply pending migrations
9
+ * migrate rehash Rebuild migration log checksums from migration.sql files
9
10
  * init Initialize snapshot from existing schema
10
11
  * pull Introspect database and generate schema
11
12
  */
12
13
  import { useState, useEffect } from "react";
13
14
  import { render, Box, Text, useApp, useInput } from "ink";
14
15
  import SelectInput from "ink-select-input";
15
- import { runMigrateGenerate, runMigrateApply, runInit, runPull, CommandError, } from "./commands.js";
16
+ import { runMigrateGenerate, runMigrateApply, runMigrateRehash, runInit, runPull, CommandError, } from "./commands.js";
16
17
  import { promptSnapshotExists, promptFreshInit, promptPullConfirm, promptTableRename, promptColumnRename, promptMigrationName, promptMigrationConfirm, } from "./prompts.js";
17
18
  const commands = [
18
19
  { label: "migrate create", value: "migrate create", description: "Generate a new SQL migration file" },
19
20
  { label: "migrate apply", value: "migrate apply", description: "Apply pending SQL migrations" },
21
+ { label: "migrate rehash", value: "migrate rehash", description: "Rebuild migration log checksums" },
20
22
  { label: "init", value: "init", description: "Initialize snapshot from existing schema" },
21
23
  { label: "pull", value: "pull", description: "Introspect database and generate schema" },
22
24
  { label: "help", value: "help", description: "Show help information" },
@@ -29,7 +31,7 @@ function parseArgs() {
29
31
  let command;
30
32
  for (let i = 0; i < args.length; i++) {
31
33
  const arg = args[i];
32
- // Handle "migrate create" and "migrate apply" subcommands
34
+ // Handle "migrate create", "migrate apply", and "migrate rehash" subcommands
33
35
  if (arg === "migrate" && args[i + 1] === "create") {
34
36
  command = "migrate create";
35
37
  i++; // Skip the next argument
@@ -38,6 +40,10 @@ function parseArgs() {
38
40
  command = "migrate apply";
39
41
  i++; // Skip the next argument
40
42
  }
43
+ else if (arg === "migrate" && args[i + 1] === "rehash") {
44
+ command = "migrate rehash";
45
+ i++; // Skip the next argument
46
+ }
41
47
  else if (arg === "init" || arg === "pull" || arg === "help") {
42
48
  command = arg;
43
49
  }
@@ -50,6 +56,9 @@ function parseArgs() {
50
56
  else if (arg === "--migrations" || arg === "-m") {
51
57
  options.migrations = args[++i];
52
58
  }
59
+ else if (arg === "--migration") {
60
+ options.migration = args[++i];
61
+ }
53
62
  else if (arg === "--dialect") {
54
63
  options.dialect = args[++i];
55
64
  }
@@ -74,6 +83,9 @@ function parseArgs() {
74
83
  else if (arg === "--preview") {
75
84
  options.preview = true;
76
85
  }
86
+ else if (arg === "--mark-applied") {
87
+ options.markApplied = true;
88
+ }
77
89
  else if (arg === "--force" || arg === "-f") {
78
90
  options.force = true;
79
91
  }
@@ -101,7 +113,7 @@ function Status({ type, message }) {
101
113
  }
102
114
  // Help display component
103
115
  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" })] })] }));
116
+ 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 only)" }), _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
117
  }
106
118
  function CliApp({ initialCommand, options }) {
107
119
  const { exit } = useApp();
@@ -167,6 +179,9 @@ function CliApp({ initialCommand, options }) {
167
179
  else if (command === "migrate apply") {
168
180
  await runMigrateApply(ctx);
169
181
  }
182
+ else if (command === "migrate rehash") {
183
+ await runMigrateRehash(ctx);
184
+ }
170
185
  else if (command === "init") {
171
186
  await runInit(ctx);
172
187
  }
@@ -11,6 +11,7 @@ export interface CommandOptions {
11
11
  schema?: string;
12
12
  migrations?: string;
13
13
  name?: string;
14
+ migration?: string;
14
15
  dialect?: string;
15
16
  url?: string;
16
17
  output?: string;
@@ -19,6 +20,7 @@ export interface CommandOptions {
19
20
  baseline?: boolean;
20
21
  createInitial?: boolean;
21
22
  preview?: boolean;
23
+ markApplied?: boolean;
22
24
  force?: boolean;
23
25
  config?: string;
24
26
  }
@@ -63,6 +65,10 @@ export declare function runMigrateGenerate(ctx: CommandContext): Promise<void>;
63
65
  * migrate:apply command
64
66
  */
65
67
  export declare function runMigrateApply(ctx: CommandContext): Promise<void>;
68
+ /**
69
+ * migrate:rehash command
70
+ */
71
+ export declare function runMigrateRehash(ctx: CommandContext): Promise<void>;
66
72
  /**
67
73
  * init command
68
74
  */
@@ -1 +1 @@
1
- {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"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"}
@@ -6,9 +6,11 @@
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
- import { createPrismaMigration, applyPrismaMigrations, previewPrismaMigrations, hasPrismaSchemaChanges, hasSnapshot, scanMigrationFolders, writeMigrationLog, initializeSnapshot, createInitialMigration, detectPotentialRenames, } from "../migrations/prisma.js";
13
+ import { createPrismaMigration, applyPrismaMigrations, previewPrismaMigrations, hasPrismaSchemaChanges, hasSnapshot, scanMigrationFolders, readMigrationLog, writeMigrationLog, getMigrationLogPath, calculateChecksum, initializeSnapshot, createInitialMigration, detectPotentialRenames, } from "../migrations/prisma.js";
12
14
  export class CommandError extends Error {
13
15
  constructor(message) {
14
16
  super(message);
@@ -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
- ctx.log("info", `Migration: ${migration.name}`);
175
- ctx.log("info", `SQL:\n${migration.sql}`);
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.log("info", "Applying migrations...");
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) {
@@ -198,8 +213,10 @@ export async function runMigrateApply(ctx) {
198
213
  ctx.log("error", " - Migrations were applied manually without using zenstack-kit");
199
214
  ctx.log("error", " - The migration log file was modified or deleted");
200
215
  ctx.log("error", " - Different migration histories exist across environments");
216
+ ctx.log("error", " - Migration.sql files were edited after being logged");
201
217
  ctx.log("error", "");
202
218
  ctx.log("error", "To resolve, ensure your migration log matches your database state.");
219
+ ctx.log("error", "If you edited migration.sql files, run 'zenstack-kit migrate rehash' to rebuild log checksums.");
203
220
  throw new CommandError("Migration history is inconsistent");
204
221
  }
205
222
  if (result.applied.length === 0 && !result.failed) {
@@ -210,12 +227,52 @@ export async function runMigrateApply(ctx) {
210
227
  return;
211
228
  }
212
229
  for (const item of result.applied) {
213
- ctx.log("success", `Applied: ${item.migrationName} (${item.duration}ms)`);
230
+ const action = ctx.options.markApplied ? "Marked applied" : "Applied";
231
+ ctx.log("success", `${action}: ${item.migrationName} (${item.duration}ms)`);
214
232
  }
215
233
  if (result.failed) {
216
234
  throw new CommandError(`Migration failed: ${result.failed.migrationName} - ${result.failed.error}`);
217
235
  }
218
236
  }
237
+ /**
238
+ * migrate:rehash command
239
+ */
240
+ export async function runMigrateRehash(ctx) {
241
+ const { outputPath } = await resolveConfig(ctx);
242
+ const targetMigration = ctx.options.migration;
243
+ if (targetMigration) {
244
+ const sqlPath = path.join(outputPath, targetMigration, "migration.sql");
245
+ if (!fs.existsSync(sqlPath)) {
246
+ throw new CommandError(`Migration not found: ${targetMigration}`);
247
+ }
248
+ const sqlContent = await fs.promises.readFile(sqlPath, "utf-8");
249
+ const checksum = calculateChecksum(sqlContent);
250
+ const entries = await readMigrationLog(outputPath);
251
+ const existingIndex = entries.findIndex((e) => e.name === targetMigration);
252
+ if (existingIndex === -1) {
253
+ entries.push({ name: targetMigration, checksum });
254
+ }
255
+ else {
256
+ entries[existingIndex] = { name: targetMigration, checksum };
257
+ }
258
+ await writeMigrationLog(outputPath, entries);
259
+ const logPath = getMigrationLogPath(outputPath);
260
+ ctx.log("success", `Updated checksum for ${targetMigration}`);
261
+ ctx.log("info", `Log: ${logPath}`);
262
+ ctx.log("warning", "If this migration was already applied, make sure the database checksum matches the updated log.");
263
+ return;
264
+ }
265
+ const migrations = await scanMigrationFolders(outputPath);
266
+ if (migrations.length === 0) {
267
+ ctx.log("warning", "No migrations found.");
268
+ return;
269
+ }
270
+ await writeMigrationLog(outputPath, migrations);
271
+ const logPath = getMigrationLogPath(outputPath);
272
+ ctx.log("success", `Migration log rebuilt with ${migrations.length} migration(s)`);
273
+ ctx.log("info", `Log: ${logPath}`);
274
+ ctx.log("warning", "If these migrations were already applied, make sure the database checksums match the updated log.");
275
+ }
219
276
  /**
220
277
  * init command
221
278
  */
@@ -334,6 +391,42 @@ export async function runPull(ctx) {
334
391
  return;
335
392
  }
336
393
  }
394
+ if (ctx.options.preview) {
395
+ ctx.log("info", "Preview mode - no files will be written.");
396
+ const result = await pullSchema({
397
+ dialect,
398
+ connectionUrl,
399
+ databasePath,
400
+ outputPath: schemaOutputPath,
401
+ writeFile: false,
402
+ });
403
+ if (fs.existsSync(schemaOutputPath)) {
404
+ const diffOutput = await buildSchemaDiff(schemaOutputPath, result.schema);
405
+ if (diffOutput) {
406
+ const { text, truncated } = truncateLines(diffOutput, 200);
407
+ ctx.log("info", `Diff (existing -> generated):\n${text}`);
408
+ if (truncated) {
409
+ ctx.log("info", "Diff truncated. Use --output to write and inspect the full schema.");
410
+ }
411
+ }
412
+ else {
413
+ ctx.log("info", "Diff unavailable; showing generated schema preview.");
414
+ const { text, truncated } = truncateLines(result.schema, 200);
415
+ ctx.log("info", `Generated schema:\n${text}`);
416
+ if (truncated) {
417
+ ctx.log("info", "Preview truncated. Use --output to write and inspect the full schema.");
418
+ }
419
+ }
420
+ }
421
+ else {
422
+ const { text, truncated } = truncateLines(result.schema, 200);
423
+ ctx.log("info", `Generated schema:\n${text}`);
424
+ if (truncated) {
425
+ ctx.log("info", "Preview truncated. Use --output to write and inspect the full schema.");
426
+ }
427
+ }
428
+ return;
429
+ }
337
430
  ctx.log("info", "Pulling schema from database...");
338
431
  const result = await pullSchema({
339
432
  dialect,
@@ -349,3 +442,33 @@ export async function runPull(ctx) {
349
442
  ctx.log("warning", "You should run 'zenstack-kit init' to reset the snapshot after reviewing the schema.");
350
443
  }
351
444
  }
445
+ async function buildSchemaDiff(existingPath, nextSchema) {
446
+ const tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), "zenstack-kit-pull-"));
447
+ const nextPath = path.join(tempDir, "schema.zmodel");
448
+ try {
449
+ await fs.promises.writeFile(nextPath, nextSchema, "utf-8");
450
+ try {
451
+ return execFileSync("git", ["diff", "--no-index", "--no-color", "--", existingPath, nextPath], { encoding: "utf-8" });
452
+ }
453
+ catch (error) {
454
+ const stdout = error.stdout;
455
+ if (stdout) {
456
+ return stdout.toString();
457
+ }
458
+ return null;
459
+ }
460
+ }
461
+ finally {
462
+ await fs.promises.rm(tempDir, { recursive: true, force: true });
463
+ }
464
+ }
465
+ function truncateLines(text, maxLines) {
466
+ const lines = text.split("\n");
467
+ if (lines.length <= maxLines) {
468
+ return { text, truncated: false };
469
+ }
470
+ return {
471
+ text: lines.slice(0, maxLines).join("\n"),
472
+ truncated: true,
473
+ };
474
+ }
@@ -5,6 +5,7 @@
5
5
  * Commands:
6
6
  * migrate:generate Generate a new SQL migration
7
7
  * migrate:apply Apply pending migrations
8
+ * migrate:rehash Rebuild migration log checksums
8
9
  * init Initialize snapshot from existing schema
9
10
  * pull Introspect database and generate schema
10
11
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;GAQG"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;GASG"}
package/dist/cli/index.js CHANGED
@@ -5,6 +5,7 @@
5
5
  * Commands:
6
6
  * migrate:generate Generate a new SQL migration
7
7
  * migrate:apply Apply pending migrations
8
+ * migrate:rehash Rebuild migration log checksums
8
9
  * init Initialize snapshot from existing schema
9
10
  * pull Introspect database and generate schema
10
11
  */
@@ -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;AA2CtD;;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"}
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"}
@@ -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;CAC3B;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,CAuHtC;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,6BAA6B,CAAC,CA0DxC"}
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,CA0HtC;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,6BAA6B,CAAC,CA0DxC"}
@@ -298,17 +298,20 @@ export async function applyPrismaMigrations(options) {
298
298
  error: `Checksum mismatch for migration ${folderName}.\n` +
299
299
  `Expected: ${logEntry.checksum}\n` +
300
300
  `Found: ${checksum}\n` +
301
- `The migration file may have been modified after generation.`,
301
+ `The migration file may have been modified after generation.\n` +
302
+ `If you intended this, run 'zenstack-kit migrate rehash' to rebuild log checksums.`,
302
303
  };
303
304
  break;
304
305
  }
305
306
  const startTime = Date.now();
306
307
  try {
307
- // Execute the migration SQL using direct driver access
308
- await executeRawSql(options.dialect, sqlContent, {
309
- connectionUrl: options.connectionUrl,
310
- databasePath: options.databasePath,
311
- });
308
+ if (!options.markApplied) {
309
+ // Execute the migration SQL using direct driver access
310
+ await executeRawSql(options.dialect, sqlContent, {
311
+ connectionUrl: options.connectionUrl,
312
+ databasePath: options.databasePath,
313
+ });
314
+ }
312
315
  // Record the migration (still use Kysely for this since it's simple INSERT)
313
316
  await recordMigration(db, migrationsTable, migrationsSchema, options.dialect, folderName, checksum);
314
317
  result.applied.push({
@@ -13,6 +13,8 @@ export interface PullOptions {
13
13
  databasePath?: string;
14
14
  /** Output path for schema */
15
15
  outputPath: string;
16
+ /** Write the schema to outputPath (default: true) */
17
+ writeFile?: boolean;
16
18
  }
17
19
  export interface PullResult {
18
20
  outputPath: string;
@@ -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;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;CACpB;AAofD,wBAAsB,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAwC1E"}
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"}
@@ -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
- modifiers.push("@default(dbgenerated())");
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
- await fs.mkdir(path.dirname(options.outputPath), { recursive: true });
415
- await fs.writeFile(options.outputPath, schema.trimEnd() + "\n", "utf-8");
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zenstack-kit",
3
- "version": "0.1.5",
3
+ "version": "0.1.8",
4
4
  "description": "Drizzle-kit like CLI tooling for ZenStack schemas with Kysely support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",