zenstack-kit 0.1.1

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 (93) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +313 -0
  3. package/dist/cli/app.d.ts +12 -0
  4. package/dist/cli/app.d.ts.map +1 -0
  5. package/dist/cli/app.js +253 -0
  6. package/dist/cli/commands.d.ts +70 -0
  7. package/dist/cli/commands.d.ts.map +1 -0
  8. package/dist/cli/commands.js +308 -0
  9. package/dist/cli/index.d.ts +12 -0
  10. package/dist/cli/index.d.ts.map +1 -0
  11. package/dist/cli/index.js +12 -0
  12. package/dist/cli/prompt-provider.d.ts +10 -0
  13. package/dist/cli/prompt-provider.d.ts.map +1 -0
  14. package/dist/cli/prompt-provider.js +41 -0
  15. package/dist/cli/prompts.d.ts +27 -0
  16. package/dist/cli/prompts.d.ts.map +1 -0
  17. package/dist/cli/prompts.js +133 -0
  18. package/dist/cli.d.ts +12 -0
  19. package/dist/cli.d.ts.map +1 -0
  20. package/dist/cli.js +240 -0
  21. package/dist/config/index.d.ts +96 -0
  22. package/dist/config/index.d.ts.map +1 -0
  23. package/dist/config/index.js +48 -0
  24. package/dist/config/loader.d.ts +11 -0
  25. package/dist/config/loader.d.ts.map +1 -0
  26. package/dist/config/loader.js +44 -0
  27. package/dist/config-loader.d.ts +6 -0
  28. package/dist/config-loader.d.ts.map +1 -0
  29. package/dist/config-loader.js +36 -0
  30. package/dist/config.d.ts +62 -0
  31. package/dist/config.d.ts.map +1 -0
  32. package/dist/config.js +44 -0
  33. package/dist/index.d.ts +19 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +23 -0
  36. package/dist/init-prompts.d.ts +13 -0
  37. package/dist/init-prompts.d.ts.map +1 -0
  38. package/dist/init-prompts.js +64 -0
  39. package/dist/introspect.d.ts +54 -0
  40. package/dist/introspect.d.ts.map +1 -0
  41. package/dist/introspect.js +75 -0
  42. package/dist/kysely-adapter.d.ts +49 -0
  43. package/dist/kysely-adapter.d.ts.map +1 -0
  44. package/dist/kysely-adapter.js +74 -0
  45. package/dist/migrate-apply.d.ts +18 -0
  46. package/dist/migrate-apply.d.ts.map +1 -0
  47. package/dist/migrate-apply.js +61 -0
  48. package/dist/migrate.d.ts +108 -0
  49. package/dist/migrate.d.ts.map +1 -0
  50. package/dist/migrate.js +127 -0
  51. package/dist/migrations/apply.d.ts +18 -0
  52. package/dist/migrations/apply.d.ts.map +1 -0
  53. package/dist/migrations/apply.js +61 -0
  54. package/dist/migrations/diff.d.ts +161 -0
  55. package/dist/migrations/diff.d.ts.map +1 -0
  56. package/dist/migrations/diff.js +620 -0
  57. package/dist/migrations/prisma.d.ts +193 -0
  58. package/dist/migrations/prisma.d.ts.map +1 -0
  59. package/dist/migrations/prisma.js +929 -0
  60. package/dist/migrations.d.ts +161 -0
  61. package/dist/migrations.d.ts.map +1 -0
  62. package/dist/migrations.js +620 -0
  63. package/dist/prisma-migrations.d.ts +160 -0
  64. package/dist/prisma-migrations.d.ts.map +1 -0
  65. package/dist/prisma-migrations.js +789 -0
  66. package/dist/prompts.d.ts +10 -0
  67. package/dist/prompts.d.ts.map +1 -0
  68. package/dist/prompts.js +41 -0
  69. package/dist/pull.d.ts +23 -0
  70. package/dist/pull.d.ts.map +1 -0
  71. package/dist/pull.js +424 -0
  72. package/dist/schema/introspect.d.ts +54 -0
  73. package/dist/schema/introspect.d.ts.map +1 -0
  74. package/dist/schema/introspect.js +75 -0
  75. package/dist/schema/pull.d.ts +23 -0
  76. package/dist/schema/pull.d.ts.map +1 -0
  77. package/dist/schema/pull.js +424 -0
  78. package/dist/schema/snapshot.d.ts +46 -0
  79. package/dist/schema/snapshot.d.ts.map +1 -0
  80. package/dist/schema/snapshot.js +278 -0
  81. package/dist/schema-snapshot.d.ts +45 -0
  82. package/dist/schema-snapshot.d.ts.map +1 -0
  83. package/dist/schema-snapshot.js +265 -0
  84. package/dist/sql/compiler.d.ts +74 -0
  85. package/dist/sql/compiler.d.ts.map +1 -0
  86. package/dist/sql/compiler.js +270 -0
  87. package/dist/sql/kysely-adapter.d.ts +49 -0
  88. package/dist/sql/kysely-adapter.d.ts.map +1 -0
  89. package/dist/sql/kysely-adapter.js +74 -0
  90. package/dist/sql-compiler.d.ts +74 -0
  91. package/dist/sql-compiler.d.ts.map +1 -0
  92. package/dist/sql-compiler.js +243 -0
  93. package/package.json +81 -0
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Interactive prompts for the init command using ink
3
+ */
4
+ export type InitChoice = "skip" | "reinitialize" | "baseline" | "create_initial";
5
+ /**
6
+ * Prompt user when snapshot already exists (Case A)
7
+ */
8
+ export declare function promptSnapshotExists(): Promise<InitChoice>;
9
+ /**
10
+ * Prompt user for fresh init when no migrations exist (Case C)
11
+ */
12
+ export declare function promptFreshInit(): Promise<InitChoice>;
13
+ //# sourceMappingURL=init-prompts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-prompts.d.ts","sourceRoot":"","sources":["../src/init-prompts.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAMH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,cAAc,GAAG,UAAU,GAAG,gBAAgB,CAAC;AAsCjF;;GAEG;AACH,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,UAAU,CAAC,CAyBhE;AAED;;GAEG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,UAAU,CAAC,CAyB3D"}
@@ -0,0 +1,64 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Interactive prompts for the init command using ink
4
+ */
5
+ import { useState } from "react";
6
+ import { render, Box, Text } from "ink";
7
+ import SelectInput from "ink-select-input";
8
+ function SelectPrompt({ message, items, onSelect }) {
9
+ const [selectedIndex, setSelectedIndex] = useState(0);
10
+ const handleSelect = (item) => {
11
+ onSelect(item.value);
12
+ };
13
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { color: "cyan", children: "? " }), _jsx(Text, { children: message })] }), _jsx(SelectInput, { items: items, onSelect: handleSelect, onHighlight: (item) => {
14
+ const idx = items.findIndex((i) => i.value === item.value);
15
+ if (idx !== -1)
16
+ setSelectedIndex(idx);
17
+ } }), items[selectedIndex]?.description && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: [" ", items[selectedIndex].description] }) }))] }));
18
+ }
19
+ /**
20
+ * Prompt user when snapshot already exists (Case A)
21
+ */
22
+ export async function promptSnapshotExists() {
23
+ return new Promise((resolve) => {
24
+ const { unmount, waitUntilExit } = render(_jsx(SelectPrompt, { message: "Snapshot already exists. What would you like to do?", items: [
25
+ {
26
+ label: "Skip",
27
+ value: "skip",
28
+ description: "Do nothing and exit",
29
+ },
30
+ {
31
+ label: "Reinitialize",
32
+ value: "reinitialize",
33
+ description: "Overwrite snapshot and rebuild migration log from existing migrations",
34
+ },
35
+ ], onSelect: (value) => {
36
+ unmount();
37
+ resolve(value);
38
+ } }));
39
+ waitUntilExit();
40
+ });
41
+ }
42
+ /**
43
+ * Prompt user for fresh init when no migrations exist (Case C)
44
+ */
45
+ export async function promptFreshInit() {
46
+ return new Promise((resolve) => {
47
+ const { unmount, waitUntilExit } = render(_jsx(SelectPrompt, { message: "No migrations found. What would you like to do?", items: [
48
+ {
49
+ label: "Baseline only",
50
+ value: "baseline",
51
+ description: "Create snapshot only - use when database already matches schema",
52
+ },
53
+ {
54
+ label: "Create initial migration",
55
+ value: "create_initial",
56
+ description: "Create snapshot + initial migration - use when database is empty",
57
+ },
58
+ ], onSelect: (value) => {
59
+ unmount();
60
+ resolve(value);
61
+ } }));
62
+ waitUntilExit();
63
+ });
64
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Schema introspection utilities
3
+ *
4
+ * Provides functionality to introspect ZenStack schemas and databases,
5
+ * extracting model and field information for code generation.
6
+ */
7
+ export interface FieldInfo {
8
+ /** Field name */
9
+ name: string;
10
+ /** Field type (String, Int, Boolean, etc.) */
11
+ type: string;
12
+ /** Whether the field is optional */
13
+ isOptional: boolean;
14
+ /** Whether the field is an array */
15
+ isArray: boolean;
16
+ /** Whether this is a relation field */
17
+ isRelation: boolean;
18
+ /** Whether this is the primary key */
19
+ isId: boolean;
20
+ /** Whether the field has a default value */
21
+ hasDefault: boolean;
22
+ /** Whether the field is unique */
23
+ isUnique: boolean;
24
+ /** Related model name (for relations) */
25
+ relationModel?: string;
26
+ }
27
+ export interface ModelInfo {
28
+ /** Model name */
29
+ name: string;
30
+ /** Table name in database */
31
+ tableName: string;
32
+ /** Model fields */
33
+ fields: FieldInfo[];
34
+ }
35
+ export interface SchemaInfo {
36
+ /** All models in the schema */
37
+ models: ModelInfo[];
38
+ /** Schema version or hash */
39
+ version: string;
40
+ }
41
+ interface IntrospectOptions {
42
+ /** Path to ZenStack schema file */
43
+ schemaPath?: string;
44
+ /** Database connection URL (for database introspection) */
45
+ databaseUrl?: string;
46
+ /** Output path for generated schema */
47
+ outputPath?: string;
48
+ }
49
+ /**
50
+ * Introspect schema from file or database
51
+ */
52
+ export declare function introspectSchema(options: IntrospectOptions): Promise<SchemaInfo>;
53
+ export {};
54
+ //# sourceMappingURL=introspect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"introspect.d.ts","sourceRoot":"","sources":["../src/introspect.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,MAAM,WAAW,SAAS;IACxB,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,UAAU,EAAE,OAAO,CAAC;IACpB,oCAAoC;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,uCAAuC;IACvC,UAAU,EAAE,OAAO,CAAC;IACpB,sCAAsC;IACtC,IAAI,EAAE,OAAO,CAAC;IACd,4CAA4C;IAC5C,UAAU,EAAE,OAAO,CAAC;IACpB,kCAAkC;IAClC,QAAQ,EAAE,OAAO,CAAC;IAClB,yCAAyC;IACzC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,SAAS;IACxB,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,6BAA6B;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,mBAAmB;IACnB,MAAM,EAAE,SAAS,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,+BAA+B;IAC/B,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,6BAA6B;IAC7B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,iBAAiB;IACzB,mCAAmC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAoED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CAUtF"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Schema introspection utilities
3
+ *
4
+ * Provides functionality to introspect ZenStack schemas and databases,
5
+ * extracting model and field information for code generation.
6
+ */
7
+ import * as fs from "fs/promises";
8
+ /**
9
+ * Parse a .zmodel file and extract schema information
10
+ * This is a simplified parser - in production, you'd use ZenStack's AST
11
+ */
12
+ async function parseZModelFile(schemaPath) {
13
+ const content = await fs.readFile(schemaPath, "utf-8");
14
+ const models = [];
15
+ // Simple regex-based parser for demonstration
16
+ // In production, integrate with ZenStack's parser
17
+ const modelRegex = /model\s+(\w+)\s*\{([^}]+)\}/g;
18
+ const fieldRegex = /^\s*(\w+)\s+(\w+)(\[\])?\s*(\?)?\s*(.*?)$/gm;
19
+ let modelMatch;
20
+ while ((modelMatch = modelRegex.exec(content)) !== null) {
21
+ const modelName = modelMatch[1];
22
+ const modelBody = modelMatch[2];
23
+ const fields = [];
24
+ let fieldMatch;
25
+ const fieldPattern = /^\s*(\w+)\s+(\w+)(\[\])?\s*(\?)?(.*)$/gm;
26
+ while ((fieldMatch = fieldPattern.exec(modelBody)) !== null) {
27
+ const [, name, type, isArray, isOptional, modifiers] = fieldMatch;
28
+ // Skip if it looks like a directive
29
+ if (name.startsWith("@@") || name.startsWith("//"))
30
+ continue;
31
+ const isId = modifiers?.includes("@id") || false;
32
+ const hasDefault = modifiers?.includes("@default") || false;
33
+ const isUnique = modifiers?.includes("@unique") || isId;
34
+ const isRelation = modifiers?.includes("@relation") || false;
35
+ fields.push({
36
+ name,
37
+ type,
38
+ isOptional: !!isOptional,
39
+ isArray: !!isArray,
40
+ isRelation,
41
+ isId,
42
+ hasDefault,
43
+ isUnique,
44
+ relationModel: isRelation ? type : undefined,
45
+ });
46
+ }
47
+ models.push({
48
+ name: modelName,
49
+ tableName: modelName.toLowerCase(),
50
+ fields,
51
+ });
52
+ }
53
+ // Generate a simple version hash
54
+ const version = Buffer.from(content).toString("base64").slice(0, 8);
55
+ return { models, version };
56
+ }
57
+ /**
58
+ * Introspect a database and generate schema information
59
+ */
60
+ async function introspectDatabase(databaseUrl) {
61
+ void databaseUrl;
62
+ throw new Error("Database introspection is not supported.");
63
+ }
64
+ /**
65
+ * Introspect schema from file or database
66
+ */
67
+ export async function introspectSchema(options) {
68
+ if (options.schemaPath) {
69
+ return parseZModelFile(options.schemaPath);
70
+ }
71
+ if (options.databaseUrl) {
72
+ return introspectDatabase(options.databaseUrl);
73
+ }
74
+ throw new Error("Either schemaPath or databaseUrl must be provided");
75
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Kysely database adapter
3
+ *
4
+ * Provides utilities to create Kysely instances configured for use
5
+ * with ZenStack-generated types.
6
+ */
7
+ import type { Kysely } from "kysely";
8
+ export type KyselyDialect = "sqlite" | "postgres" | "mysql";
9
+ export interface KyselyAdapterOptions {
10
+ /** Database dialect */
11
+ dialect: KyselyDialect;
12
+ /** Database connection URL */
13
+ connectionUrl?: string;
14
+ /** SQLite database path (for SQLite dialect) */
15
+ databasePath?: string;
16
+ /** Connection pool settings */
17
+ pool?: {
18
+ min?: number;
19
+ max?: number;
20
+ };
21
+ }
22
+ export interface KyselyAdapter<DB> {
23
+ /** The Kysely instance */
24
+ db: Kysely<DB>;
25
+ /** Destroy the connection pool */
26
+ destroy: () => Promise<void>;
27
+ }
28
+ /**
29
+ * Creates a Kysely adapter for use with ZenStack schemas
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * import { createKyselyAdapter } from "zenstack-kit";
34
+ * import type { Database } from "./generated/kysely-types";
35
+ *
36
+ * const { db, destroy } = await createKyselyAdapter<Database>({
37
+ * dialect: "postgres",
38
+ * connectionUrl: process.env.DATABASE_URL,
39
+ * });
40
+ *
41
+ * // Use db for queries
42
+ * const users = await db.selectFrom("user").selectAll().execute();
43
+ *
44
+ * // Clean up
45
+ * await destroy();
46
+ * ```
47
+ */
48
+ export declare function createKyselyAdapter<DB>(options: KyselyAdapterOptions): Promise<KyselyAdapter<DB>>;
49
+ //# sourceMappingURL=kysely-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kysely-adapter.d.ts","sourceRoot":"","sources":["../src/kysely-adapter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAW,MAAM,QAAQ,CAAC;AAE9C,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAC;AAE5D,MAAM,WAAW,oBAAoB;IACnC,uBAAuB;IACvB,OAAO,EAAE,aAAa,CAAC;IACvB,8BAA8B;IAC9B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gDAAgD;IAChD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+BAA+B;IAC/B,IAAI,CAAC,EAAE;QACL,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED,MAAM,WAAW,aAAa,CAAC,EAAE;IAC/B,0BAA0B;IAC1B,EAAE,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IACf,kCAAkC;IAClC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,mBAAmB,CAAC,EAAE,EAC1C,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAwD5B"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Kysely database adapter
3
+ *
4
+ * Provides utilities to create Kysely instances configured for use
5
+ * with ZenStack-generated types.
6
+ */
7
+ /**
8
+ * Creates a Kysely adapter for use with ZenStack schemas
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * import { createKyselyAdapter } from "zenstack-kit";
13
+ * import type { Database } from "./generated/kysely-types";
14
+ *
15
+ * const { db, destroy } = await createKyselyAdapter<Database>({
16
+ * dialect: "postgres",
17
+ * connectionUrl: process.env.DATABASE_URL,
18
+ * });
19
+ *
20
+ * // Use db for queries
21
+ * const users = await db.selectFrom("user").selectAll().execute();
22
+ *
23
+ * // Clean up
24
+ * await destroy();
25
+ * ```
26
+ */
27
+ export async function createKyselyAdapter(options) {
28
+ // Dynamic imports based on dialect to avoid bundling unused drivers
29
+ let dialect;
30
+ switch (options.dialect) {
31
+ case "sqlite": {
32
+ const { default: Database } = await import("better-sqlite3");
33
+ const { SqliteDialect } = await import("kysely");
34
+ dialect = new SqliteDialect({
35
+ database: new Database(options.databasePath || ":memory:"),
36
+ });
37
+ break;
38
+ }
39
+ case "postgres": {
40
+ // Note: User needs to install pg package
41
+ const { Pool } = await import("pg");
42
+ const { PostgresDialect } = await import("kysely");
43
+ dialect = new PostgresDialect({
44
+ pool: new Pool({
45
+ connectionString: options.connectionUrl,
46
+ min: options.pool?.min ?? 2,
47
+ max: options.pool?.max ?? 10,
48
+ }),
49
+ });
50
+ break;
51
+ }
52
+ case "mysql": {
53
+ // Note: User needs to install mysql2 package
54
+ const mysql = await import("mysql2");
55
+ const { MysqlDialect } = await import("kysely");
56
+ dialect = new MysqlDialect({
57
+ pool: mysql.createPool({
58
+ uri: options.connectionUrl,
59
+ }),
60
+ });
61
+ break;
62
+ }
63
+ default:
64
+ throw new Error(`Unsupported dialect: ${options.dialect}`);
65
+ }
66
+ const { Kysely } = await import("kysely");
67
+ const db = new Kysely({ dialect });
68
+ return {
69
+ db,
70
+ destroy: async () => {
71
+ await db.destroy();
72
+ },
73
+ };
74
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Apply migrations using Kysely's migrator
3
+ */
4
+ import type { KyselyDialect } from "./kysely-adapter.js";
5
+ export interface ApplyMigrationsOptions {
6
+ migrationsFolder: string;
7
+ dialect: KyselyDialect;
8
+ connectionUrl?: string;
9
+ databasePath?: string;
10
+ }
11
+ export interface ApplyMigrationsResult {
12
+ results: Array<{
13
+ migrationName: string;
14
+ status: string;
15
+ }>;
16
+ }
17
+ export declare function applyMigrations(options: ApplyMigrationsOptions): Promise<ApplyMigrationsResult>;
18
+ //# sourceMappingURL=migrate-apply.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrate-apply.d.ts","sourceRoot":"","sources":["../src/migrate-apply.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGzD,MAAM,WAAW,sBAAsB;IACrC,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,EAAE,aAAa,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,KAAK,CAAC;QAAE,aAAa,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAC1D;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CA4DrG"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Apply migrations using Kysely's migrator
3
+ */
4
+ import * as path from "path";
5
+ import { createKyselyAdapter } from "./kysely-adapter.js";
6
+ export async function applyMigrations(options) {
7
+ const databasePath = options.databasePath ??
8
+ (options.dialect === "sqlite" ? resolveSqlitePath(options.connectionUrl) : undefined);
9
+ const { db, destroy } = await createKyselyAdapter({
10
+ dialect: options.dialect,
11
+ connectionUrl: options.connectionUrl,
12
+ databasePath,
13
+ });
14
+ try {
15
+ const { Migrator } = await import("kysely");
16
+ const fs = await import("fs/promises");
17
+ const { default: jiti } = await import("jiti");
18
+ const loader = jiti(import.meta.url, { interopDefault: true });
19
+ const provider = {
20
+ async getMigrations() {
21
+ const entries = await fs.readdir(options.migrationsFolder);
22
+ const files = entries
23
+ .filter((file) => /\.(ts|js|mjs|cjs)$/.test(file))
24
+ .sort((a, b) => a.localeCompare(b));
25
+ const migrations = {};
26
+ for (const file of files) {
27
+ const filePath = path.join(options.migrationsFolder, file);
28
+ const mod = loader(filePath);
29
+ const migration = mod.default ?? mod;
30
+ if (!migration?.up || !migration?.down) {
31
+ throw new Error(`Migration file is missing up/down exports: ${file}`);
32
+ }
33
+ migrations[path.parse(file).name] = migration;
34
+ }
35
+ return migrations;
36
+ },
37
+ };
38
+ const migrator = new Migrator({ db, provider });
39
+ const { error, results } = await migrator.migrateToLatest();
40
+ if (error) {
41
+ throw error;
42
+ }
43
+ return {
44
+ results: results?.map((result) => ({
45
+ migrationName: result.migrationName,
46
+ status: result.status,
47
+ })) ?? [],
48
+ };
49
+ }
50
+ finally {
51
+ await destroy();
52
+ }
53
+ }
54
+ function resolveSqlitePath(url) {
55
+ if (!url)
56
+ return undefined;
57
+ if (url.startsWith("file:")) {
58
+ return url.slice("file:".length);
59
+ }
60
+ return url;
61
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * High-level programmatic API for zenstack-kit migrations
3
+ *
4
+ * This module provides a simple interface for applying migrations from code,
5
+ * useful for running migrations during application startup or in CI/CD pipelines.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { migrate } from "zenstack-kit";
10
+ *
11
+ * // Apply migrations using config file
12
+ * await migrate();
13
+ *
14
+ * // Apply migrations with explicit options
15
+ * await migrate({
16
+ * migrationsFolder: "./prisma/migrations",
17
+ * dialect: "postgres",
18
+ * connectionUrl: process.env.DATABASE_URL,
19
+ * });
20
+ *
21
+ * // Preview migrations without applying
22
+ * const result = await migrate({ preview: true });
23
+ * console.log("Pending migrations:", result.pending);
24
+ * ```
25
+ */
26
+ import { type ApplyPrismaMigrationsResult, type PreviewPrismaMigrationsResult } from "./migrations/prisma.js";
27
+ import type { KyselyDialect } from "./sql/kysely-adapter.js";
28
+ export interface MigrateOptions {
29
+ /**
30
+ * Path to migrations folder.
31
+ * If not provided, will be read from config file.
32
+ */
33
+ migrationsFolder?: string;
34
+ /**
35
+ * Database dialect: "sqlite", "postgres", or "mysql"
36
+ * If not provided, will be read from config file.
37
+ */
38
+ dialect?: KyselyDialect;
39
+ /**
40
+ * Database connection URL (for postgres/mysql).
41
+ * If not provided, will be read from config file.
42
+ */
43
+ connectionUrl?: string;
44
+ /**
45
+ * SQLite database file path.
46
+ * If not provided, will be read from config file.
47
+ */
48
+ databasePath?: string;
49
+ /**
50
+ * Migrations table name.
51
+ * @default "_prisma_migrations"
52
+ */
53
+ migrationsTable?: string;
54
+ /**
55
+ * Migrations schema (PostgreSQL only).
56
+ * @default "public"
57
+ */
58
+ migrationsSchema?: string;
59
+ /**
60
+ * If true, preview pending migrations without applying them.
61
+ * @default false
62
+ */
63
+ preview?: boolean;
64
+ /**
65
+ * Current working directory for config resolution.
66
+ * @default process.cwd()
67
+ */
68
+ cwd?: string;
69
+ }
70
+ export type MigrateResult = (ApplyPrismaMigrationsResult & {
71
+ mode: "apply";
72
+ }) | (PreviewPrismaMigrationsResult & {
73
+ mode: "preview";
74
+ });
75
+ /**
76
+ * Apply or preview database migrations programmatically.
77
+ *
78
+ * This function can be used in application code to run migrations during startup,
79
+ * or in scripts and CI/CD pipelines.
80
+ *
81
+ * @param options - Migration options. If not provided, reads from config file.
82
+ * @returns Result of migration apply or preview operation.
83
+ * @throws Error if migrations fail or required configuration is missing.
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * // Run migrations on app startup
88
+ * import { migrate } from "zenstack-kit";
89
+ *
90
+ * async function main() {
91
+ * const result = await migrate();
92
+ * console.log(`Applied ${result.applied.length} migrations`);
93
+ *
94
+ * // Start your app...
95
+ * }
96
+ * ```
97
+ *
98
+ * @example
99
+ * ```typescript
100
+ * // Preview in development
101
+ * const preview = await migrate({ preview: true });
102
+ * if (preview.pending.length > 0) {
103
+ * console.log("Pending migrations:", preview.pending.map(m => m.name));
104
+ * }
105
+ * ```
106
+ */
107
+ export declare function migrate(options?: MigrateOptions): Promise<MigrateResult>;
108
+ //# sourceMappingURL=migrate.d.ts.map
@@ -0,0 +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,CAsFlF"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * High-level programmatic API for zenstack-kit migrations
3
+ *
4
+ * This module provides a simple interface for applying migrations from code,
5
+ * useful for running migrations during application startup or in CI/CD pipelines.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { migrate } from "zenstack-kit";
10
+ *
11
+ * // Apply migrations using config file
12
+ * await migrate();
13
+ *
14
+ * // Apply migrations with explicit options
15
+ * await migrate({
16
+ * migrationsFolder: "./prisma/migrations",
17
+ * dialect: "postgres",
18
+ * connectionUrl: process.env.DATABASE_URL,
19
+ * });
20
+ *
21
+ * // Preview migrations without applying
22
+ * const result = await migrate({ preview: true });
23
+ * console.log("Pending migrations:", result.pending);
24
+ * ```
25
+ */
26
+ import { loadConfig } from "./config/loader.js";
27
+ import { applyPrismaMigrations, previewPrismaMigrations, } from "./migrations/prisma.js";
28
+ import * as path from "path";
29
+ /**
30
+ * Apply or preview database migrations programmatically.
31
+ *
32
+ * This function can be used in application code to run migrations during startup,
33
+ * or in scripts and CI/CD pipelines.
34
+ *
35
+ * @param options - Migration options. If not provided, reads from config file.
36
+ * @returns Result of migration apply or preview operation.
37
+ * @throws Error if migrations fail or required configuration is missing.
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * // Run migrations on app startup
42
+ * import { migrate } from "zenstack-kit";
43
+ *
44
+ * async function main() {
45
+ * const result = await migrate();
46
+ * console.log(`Applied ${result.applied.length} migrations`);
47
+ *
48
+ * // Start your app...
49
+ * }
50
+ * ```
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * // Preview in development
55
+ * const preview = await migrate({ preview: true });
56
+ * if (preview.pending.length > 0) {
57
+ * console.log("Pending migrations:", preview.pending.map(m => m.name));
58
+ * }
59
+ * ```
60
+ */
61
+ export async function migrate(options = {}) {
62
+ const cwd = options.cwd ?? process.cwd();
63
+ // Load config if options not fully provided
64
+ let migrationsFolder = options.migrationsFolder;
65
+ let dialect = options.dialect;
66
+ let connectionUrl = options.connectionUrl;
67
+ let databasePath = options.databasePath;
68
+ if (!migrationsFolder || !dialect) {
69
+ const loaded = await loadConfig(cwd);
70
+ if (loaded) {
71
+ const { config, configDir } = loaded;
72
+ if (!migrationsFolder) {
73
+ const relativeFolder = config.migrations?.migrationsFolder ?? "./prisma/migrations";
74
+ migrationsFolder = path.resolve(configDir, relativeFolder);
75
+ }
76
+ if (!dialect) {
77
+ dialect = (config.dialect ?? "sqlite");
78
+ }
79
+ if (!connectionUrl && !databasePath) {
80
+ const dbCredentials = config.dbCredentials;
81
+ if (dialect === "sqlite") {
82
+ databasePath = dbCredentials?.file;
83
+ }
84
+ else {
85
+ connectionUrl = dbCredentials?.url;
86
+ }
87
+ }
88
+ }
89
+ }
90
+ // Validate required options
91
+ if (!migrationsFolder) {
92
+ throw new Error("migrationsFolder is required. Provide it in options or create a zenstack-kit.config.ts file.");
93
+ }
94
+ if (!dialect) {
95
+ throw new Error("dialect is required. Provide it in options or create a zenstack-kit.config.ts file.");
96
+ }
97
+ if (dialect !== "sqlite" && !connectionUrl) {
98
+ throw new Error("connectionUrl is required for postgres/mysql. Provide it in options or in your config file.");
99
+ }
100
+ const migrationsTable = options.migrationsTable ?? "_prisma_migrations";
101
+ const migrationsSchema = options.migrationsSchema ?? "public";
102
+ // Preview mode
103
+ if (options.preview) {
104
+ const result = await previewPrismaMigrations({
105
+ migrationsFolder,
106
+ dialect,
107
+ connectionUrl,
108
+ databasePath,
109
+ migrationsTable,
110
+ migrationsSchema,
111
+ });
112
+ return { ...result, mode: "preview" };
113
+ }
114
+ // Apply mode
115
+ const result = await applyPrismaMigrations({
116
+ migrationsFolder,
117
+ dialect,
118
+ connectionUrl,
119
+ databasePath,
120
+ migrationsTable,
121
+ migrationsSchema,
122
+ });
123
+ if (result.failed) {
124
+ throw new Error(`Migration failed: ${result.failed.migrationName} - ${result.failed.error}`);
125
+ }
126
+ return { ...result, mode: "apply" };
127
+ }