zenstack-kit 0.1.4 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +18 -6
  2. package/dist/cli/app.d.ts.map +1 -1
  3. package/dist/cli/app.js +7 -1
  4. package/dist/cli/commands.d.ts +2 -0
  5. package/dist/cli/commands.d.ts.map +1 -1
  6. package/dist/cli/commands.js +97 -6
  7. package/dist/cli/prompts.d.ts.map +1 -1
  8. package/dist/cli/prompts.js +1 -3
  9. package/dist/config/loader.d.ts +1 -1
  10. package/dist/config/loader.d.ts.map +1 -1
  11. package/dist/config/loader.js +11 -9
  12. package/dist/migrations/prisma/apply.d.ts +54 -0
  13. package/dist/migrations/prisma/apply.d.ts.map +1 -0
  14. package/dist/migrations/prisma/apply.js +384 -0
  15. package/dist/migrations/prisma/create.d.ts +63 -0
  16. package/dist/migrations/prisma/create.d.ts.map +1 -0
  17. package/dist/migrations/prisma/create.js +119 -0
  18. package/dist/migrations/prisma/diff.d.ts +104 -0
  19. package/dist/migrations/prisma/diff.d.ts.map +1 -0
  20. package/dist/migrations/prisma/diff.js +442 -0
  21. package/dist/migrations/prisma/log.d.ts +31 -0
  22. package/dist/migrations/prisma/log.d.ts.map +1 -0
  23. package/dist/migrations/prisma/log.js +101 -0
  24. package/dist/migrations/prisma/rename.d.ts +23 -0
  25. package/dist/migrations/prisma/rename.d.ts.map +1 -0
  26. package/dist/migrations/prisma/rename.js +57 -0
  27. package/dist/migrations/prisma/snapshot.d.ts +32 -0
  28. package/dist/migrations/prisma/snapshot.d.ts.map +1 -0
  29. package/dist/migrations/prisma/snapshot.js +65 -0
  30. package/dist/migrations/prisma.d.ts +5 -202
  31. package/dist/migrations/prisma.d.ts.map +1 -1
  32. package/dist/migrations/prisma.js +5 -1168
  33. package/dist/schema/pull.d.ts +2 -0
  34. package/dist/schema/pull.d.ts.map +1 -1
  35. package/dist/schema/pull.js +102 -4
  36. package/package.json +1 -1
package/README.md CHANGED
@@ -79,7 +79,7 @@ The `init` command offers two options:
79
79
  ### 4. Generate migrations
80
80
 
81
81
  ```bash
82
- zenstack-kit migrate: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`.
@@ -124,13 +124,14 @@ Options:
124
124
  - `-m, --migrations <path>` - Migrations directory
125
125
  - `--baseline` - Create snapshot only, no migration
126
126
  - `--create-initial` - Create snapshot and initial migration
127
+ - `-c, --config <path>` - Path to zenstack-kit config file
127
128
 
128
- ### `zenstack-kit migrate:generate`
129
+ ### `zenstack-kit migrate create`
129
130
 
130
131
  Generate a new SQL migration from schema changes.
131
132
 
132
133
  ```bash
133
- zenstack-kit migrate:generate --name add_users
134
+ zenstack-kit migrate create --name add_users
134
135
  ```
135
136
 
136
137
  Options:
@@ -138,13 +139,14 @@ Options:
138
139
  - `-s, --schema <path>` - Path to ZenStack schema
139
140
  - `-m, --migrations <path>` - Migrations directory
140
141
  - `--dialect <dialect>` - Database dialect (`sqlite`, `postgres`, `mysql`)
142
+ - `-c, --config <path>` - Path to zenstack-kit config file
141
143
 
142
- ### `zenstack-kit migrate:apply`
144
+ ### `zenstack-kit migrate apply`
143
145
 
144
146
  Apply pending migrations to the database.
145
147
 
146
148
  ```bash
147
- zenstack-kit migrate:apply
149
+ zenstack-kit migrate apply
148
150
  ```
149
151
 
150
152
  Options:
@@ -153,6 +155,9 @@ Options:
153
155
  - `--url <url>` - Database connection URL (overrides config)
154
156
  - `--table <name>` - Migrations table name (default: `_prisma_migrations`)
155
157
  - `--db-schema <name>` - Database schema for migrations table (PostgreSQL only, default: `public`)
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
156
161
 
157
162
  ### `zenstack-kit pull`
158
163
 
@@ -166,6 +171,8 @@ Options:
166
171
  - `-o, --output <path>` - Output path for schema (default: `./schema.zmodel`)
167
172
  - `--dialect <dialect>` - Database dialect
168
173
  - `--url <url>` - Database connection URL
174
+ - `--preview` - Preview generated schema and diff without writing files
175
+ - `-c, --config <path>` - Path to zenstack-kit config file
169
176
 
170
177
  Features:
171
178
  - Detects tables, columns, and types
@@ -289,6 +296,11 @@ const { db, destroy } = await createKyselyAdapter({
289
296
  await destroy();
290
297
  ```
291
298
 
299
+ ## Experimental
300
+
301
+ The `introspectSchema` API is experimental and uses a simplified parser. Expect
302
+ limitations with complex schemas.
303
+
292
304
  ## Prisma Compatibility
293
305
 
294
306
  zenstack-kit is designed to be compatible with Prisma's migration system:
@@ -1 +1 @@
1
- {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/cli/app.tsx"],"names":[],"mappings":";AAEA;;;;;;;;GAQG;AAuSH,wBAAgB,MAAM,SAkBrB"}
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/cli/app.tsx"],"names":[],"mappings":";AAEA;;;;;;;;GAQG;AA6SH,wBAAgB,MAAM,SAkBrB"}
package/dist/cli/app.js CHANGED
@@ -74,9 +74,15 @@ function parseArgs() {
74
74
  else if (arg === "--preview") {
75
75
  options.preview = true;
76
76
  }
77
+ else if (arg === "--mark-applied") {
78
+ options.markApplied = true;
79
+ }
77
80
  else if (arg === "--force" || arg === "-f") {
78
81
  options.force = true;
79
82
  }
83
+ else if (arg === "--config" || arg === "-c") {
84
+ options.config = args[++i];
85
+ }
80
86
  }
81
87
  return { command, options };
82
88
  }
@@ -98,7 +104,7 @@ function Status({ type, message }) {
98
104
  }
99
105
  // Help display component
100
106
  function HelpDisplay() {
101
- 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" })] })] }));
107
+ return (_jsxs(Box, { flexDirection: "column", paddingY: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: "zenstack-kit" }), _jsx(Text, { dimColor: true, children: "Database tooling for ZenStack schemas" }), _jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: "Commands:" }), commands.filter(c => c.value !== "exit").map((cmd) => (_jsxs(Box, { marginLeft: 2, children: [_jsx(Box, { width: 20, children: _jsx(Text, { color: "yellow", children: cmd.label }) }), _jsx(Text, { dimColor: true, children: cmd.description })] }, cmd.value))), _jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: "Options:" }), _jsxs(Box, { marginLeft: 2, flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "-s, --schema <path> Path to ZenStack schema" }), _jsx(Text, { dimColor: true, children: "-m, --migrations <path> Migrations directory" }), _jsx(Text, { dimColor: true, children: "-n, --name <name> Migration name" }), _jsx(Text, { dimColor: true, children: "--dialect <dialect> Database dialect (sqlite, postgres, mysql)" }), _jsx(Text, { dimColor: true, children: "--url <url> Database connection URL" }), _jsx(Text, { dimColor: true, children: "--create-initial Create initial migration (skip prompt)" }), _jsx(Text, { dimColor: true, children: "--baseline Create baseline only (skip prompt)" }), _jsx(Text, { dimColor: true, children: "--preview Preview pending migrations without applying" }), _jsx(Text, { dimColor: true, children: "--mark-applied Mark pending migrations as applied without running SQL" }), _jsx(Text, { dimColor: true, children: "-f, --force Force operation without confirmation" }), _jsx(Text, { dimColor: true, children: "-c, --config <path> Path to zenstack-kit config file" })] })] }));
102
108
  }
103
109
  function CliApp({ initialCommand, options }) {
104
110
  const { exit } = useApp();
@@ -19,7 +19,9 @@ export interface CommandOptions {
19
19
  baseline?: boolean;
20
20
  createInitial?: boolean;
21
21
  preview?: boolean;
22
+ markApplied?: boolean;
22
23
  force?: boolean;
24
+ config?: string;
23
25
  }
24
26
  export interface CommandContext {
25
27
  cwd: string;
@@ -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,6 +6,8 @@
6
6
  */
7
7
  import * as fs from "fs";
8
8
  import * as path from "path";
9
+ import * as os from "os";
10
+ import { execFileSync } from "child_process";
9
11
  import { loadConfig } from "../config/loader.js";
10
12
  import { pullSchema } from "../schema/pull.js";
11
13
  import { createPrismaMigration, applyPrismaMigrations, previewPrismaMigrations, hasPrismaSchemaChanges, hasSnapshot, scanMigrationFolders, writeMigrationLog, initializeSnapshot, createInitialMigration, detectPotentialRenames, } from "../migrations/prisma.js";
@@ -19,8 +21,11 @@ export class CommandError extends Error {
19
21
  * Load and validate config, returning resolved paths
20
22
  */
21
23
  export async function resolveConfig(ctx) {
22
- const loaded = await loadConfig(ctx.cwd);
24
+ const loaded = await loadConfig(ctx.cwd, ctx.options.config);
23
25
  if (!loaded) {
26
+ if (ctx.options.config) {
27
+ throw new CommandError(`Config file not found: ${ctx.options.config}`);
28
+ }
24
29
  throw new CommandError("No zenstack-kit config file found.");
25
30
  }
26
31
  const { config, configDir } = loaded;
@@ -121,6 +126,7 @@ export async function runMigrateGenerate(ctx) {
121
126
  }
122
127
  ctx.log("success", `Migration created: ${migration.folderName}/migration.sql`);
123
128
  ctx.log("info", `Path: ${migration.folderPath}`);
129
+ ctx.log("info", "Next: run 'zenstack-kit migrate apply' (or --preview to review SQL).");
124
130
  }
125
131
  /**
126
132
  * migrate:apply command
@@ -144,9 +150,12 @@ export async function runMigrateApply(ctx) {
144
150
  throw new CommandError("Database connection URL is required for non-sqlite dialects.");
145
151
  }
146
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
+ }
147
156
  // Preview mode - show pending migrations without applying
148
157
  if (ctx.options.preview) {
149
- ctx.log("info", "Preview mode - showing pending migrations:");
158
+ ctx.log("info", "Preview mode - no changes will be applied.");
150
159
  const preview = await previewPrismaMigrations({
151
160
  migrationsFolder: outputPath,
152
161
  dialect,
@@ -162,13 +171,26 @@ export async function runMigrateApply(ctx) {
162
171
  }
163
172
  return;
164
173
  }
174
+ ctx.log("info", `Pending migrations: ${preview.pending.length}`);
175
+ if (preview.alreadyApplied.length > 0) {
176
+ ctx.log("info", `${preview.alreadyApplied.length} migration(s) already applied`);
177
+ }
165
178
  for (const migration of preview.pending) {
166
- ctx.log("info", `Pending: ${migration.name}`);
167
- 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"})`);
168
184
  }
185
+ ctx.log("info", "Use the migration.sql files to review full SQL.");
169
186
  return;
170
187
  }
171
- 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
+ }
172
194
  const result = await applyPrismaMigrations({
173
195
  migrationsFolder: outputPath,
174
196
  dialect,
@@ -176,6 +198,7 @@ export async function runMigrateApply(ctx) {
176
198
  databasePath,
177
199
  migrationsTable,
178
200
  migrationsSchema,
201
+ markApplied: ctx.options.markApplied,
179
202
  });
180
203
  // Handle coherence errors
181
204
  if (result.coherenceErrors && result.coherenceErrors.length > 0) {
@@ -202,7 +225,8 @@ export async function runMigrateApply(ctx) {
202
225
  return;
203
226
  }
204
227
  for (const item of result.applied) {
205
- ctx.log("success", `Applied: ${item.migrationName} (${item.duration}ms)`);
228
+ const action = ctx.options.markApplied ? "Marked applied" : "Applied";
229
+ ctx.log("success", `${action}: ${item.migrationName} (${item.duration}ms)`);
206
230
  }
207
231
  if (result.failed) {
208
232
  throw new CommandError(`Migration failed: ${result.failed.migrationName} - ${result.failed.error}`);
@@ -326,6 +350,42 @@ export async function runPull(ctx) {
326
350
  return;
327
351
  }
328
352
  }
353
+ if (ctx.options.preview) {
354
+ ctx.log("info", "Preview mode - no files will be written.");
355
+ const result = await pullSchema({
356
+ dialect,
357
+ connectionUrl,
358
+ databasePath,
359
+ outputPath: schemaOutputPath,
360
+ writeFile: false,
361
+ });
362
+ if (fs.existsSync(schemaOutputPath)) {
363
+ const diffOutput = await buildSchemaDiff(schemaOutputPath, result.schema);
364
+ if (diffOutput) {
365
+ const { text, truncated } = truncateLines(diffOutput, 200);
366
+ ctx.log("info", `Diff (existing -> generated):\n${text}`);
367
+ if (truncated) {
368
+ ctx.log("info", "Diff truncated. Use --output to write and inspect the full schema.");
369
+ }
370
+ }
371
+ else {
372
+ ctx.log("info", "Diff unavailable; showing generated schema preview.");
373
+ const { text, truncated } = truncateLines(result.schema, 200);
374
+ ctx.log("info", `Generated schema:\n${text}`);
375
+ if (truncated) {
376
+ ctx.log("info", "Preview truncated. Use --output to write and inspect the full schema.");
377
+ }
378
+ }
379
+ }
380
+ else {
381
+ const { text, truncated } = truncateLines(result.schema, 200);
382
+ ctx.log("info", `Generated schema:\n${text}`);
383
+ if (truncated) {
384
+ ctx.log("info", "Preview truncated. Use --output to write and inspect the full schema.");
385
+ }
386
+ }
387
+ return;
388
+ }
329
389
  ctx.log("info", "Pulling schema from database...");
330
390
  const result = await pullSchema({
331
391
  dialect,
@@ -335,8 +395,39 @@ export async function runPull(ctx) {
335
395
  });
336
396
  ctx.log("success", `Schema generated: ${result.outputPath}`);
337
397
  ctx.log("info", `${result.tableCount} table(s) introspected`);
398
+ ctx.log("info", "Next: review the schema, then run 'zenstack-kit init' to reset the snapshot.");
338
399
  // If we have existing migrations, warn about resetting
339
400
  if (snapshotExists || migrations.length > 0) {
340
401
  ctx.log("warning", "You should run 'zenstack-kit init' to reset the snapshot after reviewing the schema.");
341
402
  }
342
403
  }
404
+ async function buildSchemaDiff(existingPath, nextSchema) {
405
+ const tempDir = await fs.promises.mkdtemp(path.join(os.tmpdir(), "zenstack-kit-pull-"));
406
+ const nextPath = path.join(tempDir, "schema.zmodel");
407
+ try {
408
+ await fs.promises.writeFile(nextPath, nextSchema, "utf-8");
409
+ try {
410
+ return execFileSync("git", ["diff", "--no-index", "--no-color", "--", existingPath, nextPath], { encoding: "utf-8" });
411
+ }
412
+ catch (error) {
413
+ const stdout = error.stdout;
414
+ if (stdout) {
415
+ return stdout.toString();
416
+ }
417
+ return null;
418
+ }
419
+ }
420
+ finally {
421
+ await fs.promises.rm(tempDir, { recursive: true, force: true });
422
+ }
423
+ }
424
+ function truncateLines(text, maxLines) {
425
+ const lines = text.split("\n");
426
+ if (lines.length <= maxLines) {
427
+ return { text, truncated: false };
428
+ }
429
+ return {
430
+ text: lines.slice(0, maxLines).join("\n"),
431
+ truncated: true,
432
+ };
433
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/cli/prompts.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAMH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,cAAc,GAAG,UAAU,GAAG,gBAAgB,CAAC;AACjF,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,IAAI,CAAC;AAsCzC;;GAEG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,UAAU,CAAC,CAyBhE;AAED;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,UAAU,CAAC,CAyB3D;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CA+BjF;AAED,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,eAAe,CAAC;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
  }
@@ -7,5 +7,5 @@ export interface LoadedConfig {
7
7
  configPath: string;
8
8
  configDir: string;
9
9
  }
10
- export declare function loadConfig(cwd: string): Promise<LoadedConfig | null>;
10
+ export declare function loadConfig(cwd: string, configPath?: string): Promise<LoadedConfig | null>;
11
11
  //# sourceMappingURL=loader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAUpD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CA4B1E"}
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAUpD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CA+B/F"}
@@ -10,32 +10,34 @@ const CONFIG_FILES = [
10
10
  "zenstack-kit.config.mjs",
11
11
  "zenstack-kit.config.cjs",
12
12
  ];
13
- export async function loadConfig(cwd) {
14
- const configPath = CONFIG_FILES.map((file) => path.join(cwd, file)).find((file) => fs.existsSync(file));
15
- if (!configPath) {
13
+ export async function loadConfig(cwd, configPath) {
14
+ const resolvedConfigPath = configPath ? path.resolve(cwd, configPath) : null;
15
+ const configPathToLoad = resolvedConfigPath ??
16
+ CONFIG_FILES.map((file) => path.join(cwd, file)).find((file) => fs.existsSync(file));
17
+ if (!configPathToLoad) {
16
18
  return null;
17
19
  }
18
- const ext = path.extname(configPath);
20
+ const ext = path.extname(configPathToLoad);
19
21
  let config;
20
22
  if (ext === ".cjs") {
21
23
  const require = createRequire(import.meta.url);
22
- const loaded = require(configPath);
24
+ const loaded = require(configPathToLoad);
23
25
  config = (loaded.default ?? loaded);
24
26
  }
25
27
  else if (ext === ".js" || ext === ".mjs") {
26
- const loaded = await import(pathToFileUrl(configPath));
28
+ const loaded = await import(pathToFileUrl(configPathToLoad));
27
29
  config = (loaded.default ?? loaded);
28
30
  }
29
31
  else {
30
32
  const { default: jiti } = await import("jiti");
31
33
  const loader = jiti(import.meta.url, { interopDefault: true });
32
- const loaded = loader(configPath);
34
+ const loaded = loader(configPathToLoad);
33
35
  config = (loaded.default ?? loaded);
34
36
  }
35
37
  return {
36
38
  config,
37
- configPath,
38
- configDir: path.dirname(configPath),
39
+ configPath: configPathToLoad,
40
+ configDir: path.dirname(configPathToLoad),
39
41
  };
40
42
  }
41
43
  function pathToFileUrl(filePath) {
@@ -0,0 +1,54 @@
1
+ import type { KyselyDialect } from "../../sql/kysely-adapter.js";
2
+ export interface ApplyPrismaMigrationsOptions {
3
+ /** Migrations folder path */
4
+ migrationsFolder: string;
5
+ /** Database dialect */
6
+ dialect: KyselyDialect;
7
+ /** Database connection URL */
8
+ connectionUrl?: string;
9
+ /** SQLite database path */
10
+ databasePath?: string;
11
+ /** Migrations table name (default: _prisma_migrations) */
12
+ migrationsTable?: string;
13
+ /** Migrations schema (PostgreSQL only, default: public) */
14
+ migrationsSchema?: string;
15
+ /** Mark migrations as applied without executing SQL */
16
+ markApplied?: boolean;
17
+ }
18
+ export interface ApplyPrismaMigrationsResult {
19
+ applied: Array<{
20
+ migrationName: string;
21
+ duration: number;
22
+ }>;
23
+ alreadyApplied: string[];
24
+ failed?: {
25
+ migrationName: string;
26
+ error: string;
27
+ };
28
+ coherenceErrors?: MigrationCoherenceError[];
29
+ }
30
+ export interface MigrationCoherenceError {
31
+ type: "missing_from_log" | "missing_from_db" | "missing_from_disk" | "order_mismatch" | "checksum_mismatch";
32
+ migrationName: string;
33
+ details: string;
34
+ }
35
+ export interface MigrationCoherenceResult {
36
+ isCoherent: boolean;
37
+ errors: MigrationCoherenceError[];
38
+ }
39
+ export interface PreviewPrismaMigrationsResult {
40
+ pending: Array<{
41
+ name: string;
42
+ sql: string;
43
+ }>;
44
+ alreadyApplied: string[];
45
+ }
46
+ /**
47
+ * Apply pending Prisma migrations
48
+ */
49
+ export declare function applyPrismaMigrations(options: ApplyPrismaMigrationsOptions): Promise<ApplyPrismaMigrationsResult>;
50
+ /**
51
+ * Preview pending migrations without applying them
52
+ */
53
+ export declare function previewPrismaMigrations(options: ApplyPrismaMigrationsOptions): Promise<PreviewPrismaMigrationsResult>;
54
+ //# sourceMappingURL=apply.d.ts.map
@@ -0,0 +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;CACvB;AAED,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,KAAK,CAAC;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5D,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,MAAM,CAAC,EAAE;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAClD,eAAe,CAAC,EAAE,uBAAuB,EAAE,CAAC;CAC7C;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,kBAAkB,GAAG,iBAAiB,GAAG,mBAAmB,GAAG,gBAAgB,GAAG,mBAAmB,CAAC;IAC5G,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,OAAO,CAAC;IACpB,MAAM,EAAE,uBAAuB,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,6BAA6B;IAC5C,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9C,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AA2QD;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,2BAA2B,CAAC,CAyHtC;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,6BAA6B,CAAC,CA0DxC"}