peta-migrate 0.2.2 → 0.3.0
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 +3 -3
- package/dist/cli.mjs +21 -13
- package/dist/index.d.mts +19 -26
- package/dist/index.mjs +3 -3
- package/dist/{pusher-Be3BYUQM.mjs → pusher-DZQMrZdN.mjs} +2 -28
- package/dist/{snapshot-CO180dF3.mjs → snapshot-CWK4gVnT.mjs} +67 -87
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -86,13 +86,13 @@ Creates a generator that produces migration code from model definitions.
|
|
|
86
86
|
### Configuration
|
|
87
87
|
|
|
88
88
|
```ts
|
|
89
|
-
import {
|
|
89
|
+
import type { PetaMigrateConfig } from "peta-migrate"
|
|
90
90
|
|
|
91
|
-
const config =
|
|
91
|
+
const config: PetaMigrateConfig = {
|
|
92
92
|
migrationsDir: "./migrations",
|
|
93
93
|
models: ["./src/models/*.ts"],
|
|
94
94
|
getKysely: () => db,
|
|
95
|
-
}
|
|
95
|
+
}
|
|
96
96
|
```
|
|
97
97
|
|
|
98
98
|
| Option | Type | Description |
|
package/dist/cli.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as createMigrationGenerator, c as loadConfig, d as computeChecksum, f as loadChecksums, i as createMigrationRunner, l as loadMigrationFiles, m as verifyChecksum, n as loadSnapshot,
|
|
2
|
-
import { mkdirSync, writeFileSync } from "node:fs";
|
|
1
|
+
import { a as createMigrationGenerator, c as loadConfig, d as computeChecksum, f as loadChecksums, i as createMigrationRunner, l as loadMigrationFiles, m as verifyChecksum, n as loadSnapshot, p as saveChecksums, r as saveSnapshot, s as diffSnapshots, t as createSnapshot, u as loadModels } from "./snapshot-CWK4gVnT.mjs";
|
|
2
|
+
import { mkdirSync, readdirSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import cac from "cac";
|
|
5
5
|
import ora from "ora";
|
|
@@ -24,7 +24,7 @@ async function run() {
|
|
|
24
24
|
spinner.text = "Generating migration...";
|
|
25
25
|
const gen = createMigrationGenerator();
|
|
26
26
|
const snapshotPath = resolve(config.migrationsDir, "snapshot.json");
|
|
27
|
-
const prevSnapshot =
|
|
27
|
+
const prevSnapshot = loadSnapshot(snapshotPath);
|
|
28
28
|
let code;
|
|
29
29
|
if (prevSnapshot) {
|
|
30
30
|
const currentSnapshot = createSnapshot(models);
|
|
@@ -33,12 +33,12 @@ async function run() {
|
|
|
33
33
|
spinner.succeed("No schema changes detected since last snapshot.");
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
|
-
code = gen.generateMigrationFromDiff(diffs
|
|
37
|
-
|
|
36
|
+
code = gen.generateMigrationFromDiff(diffs);
|
|
37
|
+
saveSnapshot(snapshotPath, currentSnapshot);
|
|
38
38
|
spinner.text = `Generated incremental migration (${diffs.length} change(s))`;
|
|
39
39
|
} else {
|
|
40
40
|
code = gen.generateInitialMigration(models);
|
|
41
|
-
|
|
41
|
+
saveSnapshot(snapshotPath, createSnapshot(models));
|
|
42
42
|
spinner.text = `Generated initial migration (${models.size} model(s))`;
|
|
43
43
|
}
|
|
44
44
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:T.Z]/g, "").slice(0, 14);
|
|
@@ -54,7 +54,7 @@ async function run() {
|
|
|
54
54
|
});
|
|
55
55
|
cli.command("migrate:diff", "Preview schema changes without writing a migration").action(async () => {
|
|
56
56
|
const config = await loadConfig();
|
|
57
|
-
const prevSnapshot =
|
|
57
|
+
const prevSnapshot = loadSnapshot(resolve(config.migrationsDir, "snapshot.json"));
|
|
58
58
|
if (!prevSnapshot) {
|
|
59
59
|
console.log("No snapshot found. Run `migrate:generate` first to create one.");
|
|
60
60
|
return;
|
|
@@ -145,9 +145,9 @@ async function run() {
|
|
|
145
145
|
return;
|
|
146
146
|
}
|
|
147
147
|
spinner.text = "Pushing schema to database...";
|
|
148
|
-
const { pushSchema } = await import("./pusher-
|
|
148
|
+
const { pushSchema } = await import("./pusher-DZQMrZdN.mjs").then((n) => n.n);
|
|
149
149
|
const created = await pushSchema(config.getKysely(), models);
|
|
150
|
-
|
|
150
|
+
saveSnapshot(resolve(config.migrationsDir, "snapshot.json"), createSnapshot(models));
|
|
151
151
|
if (created.length === 0) spinner.succeed("Schema is up to date (no new tables).");
|
|
152
152
|
else spinner.succeed(`Created tables: ${created.join(", ")}`);
|
|
153
153
|
});
|
|
@@ -161,18 +161,26 @@ async function run() {
|
|
|
161
161
|
writeFileSync(filename, `import type { Kysely } from "kysely"\n\nexport async function seed(db: Kysely<any>): Promise<void> {\n // TODO: add seed data\n}\n`);
|
|
162
162
|
spinner.succeed(`Created ${filename}`);
|
|
163
163
|
} else {
|
|
164
|
-
const { readdirSync } = await import("node:fs");
|
|
165
164
|
const spinner = ora("Running seeds...").start();
|
|
166
165
|
const seedFiles = readdirSync(config.migrationsDir).filter((f) => f.includes("_seed_") && (f.endsWith(".ts") || f.endsWith(".js"))).sort();
|
|
167
166
|
if (seedFiles.length === 0) {
|
|
168
167
|
spinner.warn("No seed files found.");
|
|
169
168
|
return;
|
|
170
169
|
}
|
|
171
|
-
|
|
170
|
+
let passed = 0;
|
|
171
|
+
let failed = 0;
|
|
172
|
+
for (const file of seedFiles) try {
|
|
172
173
|
const mod = await import(resolve(config.migrationsDir, file));
|
|
173
|
-
if (mod.seed)
|
|
174
|
+
if (mod.seed) {
|
|
175
|
+
await mod.seed(config.getKysely());
|
|
176
|
+
passed++;
|
|
177
|
+
}
|
|
178
|
+
} catch (err) {
|
|
179
|
+
console.error(`❌ Seed "${file}" failed:`, err.message);
|
|
180
|
+
failed++;
|
|
174
181
|
}
|
|
175
|
-
spinner.succeed(`Executed ${
|
|
182
|
+
if (failed === 0) spinner.succeed(`Executed ${passed} seed(s)`);
|
|
183
|
+
else spinner.fail(`Executed ${passed} seed(s), ${failed} failed`);
|
|
176
184
|
}
|
|
177
185
|
});
|
|
178
186
|
cli.help();
|
package/dist/index.d.mts
CHANGED
|
@@ -42,11 +42,6 @@ interface PetaMigrateConfig {
|
|
|
42
42
|
models: string[] | string;
|
|
43
43
|
getKysely: () => Kysely<unknown>;
|
|
44
44
|
}
|
|
45
|
-
interface ResolvedConfig {
|
|
46
|
-
migrationsDir: string;
|
|
47
|
-
models: string[] | string;
|
|
48
|
-
getKysely: () => Kysely<unknown>;
|
|
49
|
-
}
|
|
50
45
|
interface SchemaColumn {
|
|
51
46
|
name: string;
|
|
52
47
|
type: string;
|
|
@@ -81,8 +76,7 @@ interface SchemaDiff {
|
|
|
81
76
|
}
|
|
82
77
|
//#endregion
|
|
83
78
|
//#region src/config.d.ts
|
|
84
|
-
declare function
|
|
85
|
-
declare function loadConfig(): Promise<ResolvedConfig>;
|
|
79
|
+
declare function loadConfig(): Promise<PetaMigrateConfig>;
|
|
86
80
|
declare function loadMigrationFiles(dir: string): Promise<MigrationFile[]>;
|
|
87
81
|
/**
|
|
88
82
|
* Load model definitions from glob patterns.
|
|
@@ -99,14 +93,11 @@ declare function loadModels(patterns: string | string[]): Promise<Map<string, Mo
|
|
|
99
93
|
declare function diffSnapshots(prev: SchemaSnapshot, next: SchemaSnapshot): SchemaDiff[];
|
|
100
94
|
//#endregion
|
|
101
95
|
//#region src/generator.d.ts
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
generateMigrationFromDiff(diffs: SchemaDiff[], options?: GeneratorOptions): string;
|
|
108
|
-
}
|
|
109
|
-
declare function createMigrationGenerator(): MigrationGenerator;
|
|
96
|
+
declare function createMigrationGenerator(): {
|
|
97
|
+
generateInitialMigration: (models: Map<string, ModelDefinition>) => string;
|
|
98
|
+
generateMigrationFromDiff: (diffs: SchemaDiff[]) => string;
|
|
99
|
+
};
|
|
100
|
+
type MigrationGenerator = ReturnType<typeof createMigrationGenerator>;
|
|
110
101
|
//#endregion
|
|
111
102
|
//#region src/pusher.d.ts
|
|
112
103
|
/**
|
|
@@ -117,14 +108,14 @@ declare function createMigrationGenerator(): MigrationGenerator;
|
|
|
117
108
|
declare function pushSchema(db: Kysely<unknown>, models: Map<string, ModelDefinition>): Promise<string[]>;
|
|
118
109
|
//#endregion
|
|
119
110
|
//#region src/runner.d.ts
|
|
120
|
-
|
|
121
|
-
ensureTable()
|
|
122
|
-
getCompleted()
|
|
123
|
-
up(migrations: MigrationFile[])
|
|
124
|
-
down(migrations: MigrationFile[])
|
|
125
|
-
status(migrations: MigrationFile[])
|
|
126
|
-
}
|
|
127
|
-
|
|
111
|
+
declare function createMigrationRunner(db: Kysely<any>, table?: string): {
|
|
112
|
+
ensureTable: () => Promise<void>;
|
|
113
|
+
getCompleted: () => Promise<MigrationRecord[]>;
|
|
114
|
+
up: (migrations: MigrationFile[]) => Promise<void>;
|
|
115
|
+
down: (migrations: MigrationFile[]) => Promise<void>;
|
|
116
|
+
status: (migrations: MigrationFile[]) => Promise<MigrationStatus>;
|
|
117
|
+
};
|
|
118
|
+
type MigrationRunner = ReturnType<typeof createMigrationRunner>;
|
|
128
119
|
//#endregion
|
|
129
120
|
//#region src/snapshot.d.ts
|
|
130
121
|
/**
|
|
@@ -133,11 +124,13 @@ declare function createMigrationRunner(db: Kysely<Record<string, never>>, table?
|
|
|
133
124
|
declare function createSnapshot(models: Map<string, ModelDefinition>): SchemaSnapshot;
|
|
134
125
|
/**
|
|
135
126
|
* Load snapshot from a JSON file path.
|
|
127
|
+
* Returns null when the file doesn't exist.
|
|
128
|
+
* Throws when the file is corrupt or has an invalid shape.
|
|
136
129
|
*/
|
|
137
|
-
declare function loadSnapshot(filePath: string):
|
|
130
|
+
declare function loadSnapshot(filePath: string): SchemaSnapshot | null;
|
|
138
131
|
/**
|
|
139
132
|
* Save snapshot to a JSON file path.
|
|
140
133
|
*/
|
|
141
|
-
declare function saveSnapshot(filePath: string, snapshot: SchemaSnapshot):
|
|
134
|
+
declare function saveSnapshot(filePath: string, snapshot: SchemaSnapshot): void;
|
|
142
135
|
//#endregion
|
|
143
|
-
export { type
|
|
136
|
+
export { type MigrationFile, type MigrationGenerator, type MigrationRecord, type MigrationRunner, type MigrationStatus, type PetaMigrateConfig, type SchemaColumn, type SchemaDiff, type SchemaIndex, type SchemaSnapshot, type SchemaTable, computeChecksum, createMigrationGenerator, createMigrationRunner, createSnapshot, diffSnapshots, loadChecksums, loadConfig, loadMigrationFiles, loadModels, loadSnapshot, pushSchema, saveChecksums, saveSnapshot, verifyChecksum };
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { t as pushSchema } from "./pusher-
|
|
2
|
-
import { a as createMigrationGenerator, c as loadConfig, d as computeChecksum, f as loadChecksums, i as createMigrationRunner, l as loadMigrationFiles, m as verifyChecksum, n as loadSnapshot,
|
|
3
|
-
export { computeChecksum, createMigrationGenerator, createMigrationRunner, createSnapshot,
|
|
1
|
+
import { t as pushSchema } from "./pusher-DZQMrZdN.mjs";
|
|
2
|
+
import { a as createMigrationGenerator, c as loadConfig, d as computeChecksum, f as loadChecksums, i as createMigrationRunner, l as loadMigrationFiles, m as verifyChecksum, n as loadSnapshot, p as saveChecksums, r as saveSnapshot, s as diffSnapshots, t as createSnapshot, u as loadModels } from "./snapshot-CWK4gVnT.mjs";
|
|
3
|
+
export { computeChecksum, createMigrationGenerator, createMigrationRunner, createSnapshot, diffSnapshots, loadChecksums, loadConfig, loadMigrationFiles, loadModels, loadSnapshot, pushSchema, saveChecksums, saveSnapshot, verifyChecksum };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { o as columnDataTypeToSql } from "./snapshot-CWK4gVnT.mjs";
|
|
1
2
|
//#region \0rolldown/runtime.js
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __exportAll = (all, no_symbols) => {
|
|
@@ -24,7 +25,7 @@ async function pushSchema(db, models) {
|
|
|
24
25
|
if (await tableExists(db, model.table)) continue;
|
|
25
26
|
let qb = db.schema.createTable(model.table).ifNotExists();
|
|
26
27
|
const columns = model.columns;
|
|
27
|
-
for (const [name, col] of Object.entries(columns)) qb = qb.addColumn(name,
|
|
28
|
+
for (const [name, col] of Object.entries(columns)) qb = qb.addColumn(name, columnDataTypeToSql(col.dataType, col.args), (cb) => buildColumn(col, cb));
|
|
28
29
|
await qb.execute();
|
|
29
30
|
createdTables.push(model.table);
|
|
30
31
|
for (const [colName, col] of Object.entries(columns)) if (col.hasConstraint("index") && !col.isPrimaryKey && !col.isUnique) await db.schema.createIndex(`${model.table}_${colName}_index`).on(model.table).column(colName).execute();
|
|
@@ -57,32 +58,5 @@ function buildColumn(col, cb) {
|
|
|
57
58
|
}
|
|
58
59
|
return cb;
|
|
59
60
|
}
|
|
60
|
-
function mapPushType(col) {
|
|
61
|
-
switch (col.dataType) {
|
|
62
|
-
case "integer":
|
|
63
|
-
case "smallint":
|
|
64
|
-
case "bigint":
|
|
65
|
-
case "text":
|
|
66
|
-
case "boolean":
|
|
67
|
-
case "timestamp":
|
|
68
|
-
case "date":
|
|
69
|
-
case "float":
|
|
70
|
-
case "double":
|
|
71
|
-
case "uuid": return col.dataType;
|
|
72
|
-
case "string": {
|
|
73
|
-
const max = col.args[0];
|
|
74
|
-
return max != null ? `varchar(${max})` : "varchar";
|
|
75
|
-
}
|
|
76
|
-
case "json":
|
|
77
|
-
case "jsonb": return "json";
|
|
78
|
-
case "decimal": {
|
|
79
|
-
const p = col.args[0];
|
|
80
|
-
const s = col.args[1];
|
|
81
|
-
return p != null ? `decimal(${p}, ${s ?? 0})` : "decimal";
|
|
82
|
-
}
|
|
83
|
-
case "enum": return "text";
|
|
84
|
-
default: return col.dataType;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
61
|
//#endregion
|
|
88
62
|
export { pusher_exports as n, pushSchema as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { Migrator } from "kysely/migration";
|
|
5
5
|
//#region src/checksum.ts
|
|
@@ -39,9 +39,6 @@ function verifyChecksum(migrationsDir, name, filePath) {
|
|
|
39
39
|
}
|
|
40
40
|
//#endregion
|
|
41
41
|
//#region src/config.ts
|
|
42
|
-
function defineConfig(config) {
|
|
43
|
-
return config;
|
|
44
|
-
}
|
|
45
42
|
async function loadConfig() {
|
|
46
43
|
const candidates = [
|
|
47
44
|
"peta.config.ts",
|
|
@@ -52,8 +49,13 @@ async function loadConfig() {
|
|
|
52
49
|
for (const file of candidates) try {
|
|
53
50
|
mod = await import(resolve(process.cwd(), file));
|
|
54
51
|
break;
|
|
55
|
-
} catch {
|
|
56
|
-
|
|
52
|
+
} catch (err) {
|
|
53
|
+
console.warn(`⚠ Failed to load config from "${file}":`, err.message);
|
|
54
|
+
}
|
|
55
|
+
if (!mod) {
|
|
56
|
+
const tried = candidates.map((f) => `\`${resolve(process.cwd(), f)}\``).join(", ");
|
|
57
|
+
throw new Error(`No configuration file found. Tried: ${tried}.\nCreate one in your project root and export a PetaMigrateConfig object.`);
|
|
58
|
+
}
|
|
57
59
|
const config = mod.default ?? mod;
|
|
58
60
|
return {
|
|
59
61
|
migrationsDir: config.migrationsDir,
|
|
@@ -62,8 +64,14 @@ async function loadConfig() {
|
|
|
62
64
|
};
|
|
63
65
|
}
|
|
64
66
|
async function loadMigrationFiles(dir) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
+
let files;
|
|
68
|
+
try {
|
|
69
|
+
files = readdirSync(dir);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
if (err.code === "ENOENT") throw new Error(`Migrations directory "${dir}" not found. Run \`peta migrate:init\` to create it.`);
|
|
72
|
+
throw err;
|
|
73
|
+
}
|
|
74
|
+
files = files.filter((f) => f.endsWith(".ts") || f.endsWith(".js")).sort();
|
|
67
75
|
const migrations = [];
|
|
68
76
|
for (const file of files) {
|
|
69
77
|
const mod = await import(resolve(dir, file));
|
|
@@ -87,9 +95,7 @@ async function loadModels(patterns) {
|
|
|
87
95
|
for (const raw of patternList) {
|
|
88
96
|
let pattern = raw;
|
|
89
97
|
if (!pattern.startsWith("/")) pattern = resolve(cwd, pattern);
|
|
90
|
-
const
|
|
91
|
-
const files = Array.from(glob.scanSync(cwd));
|
|
92
|
-
for (const file of files) {
|
|
98
|
+
for (const file of new Bun.Glob(pattern).scanSync({ cwd })) {
|
|
93
99
|
const fullPath = resolve(cwd, file);
|
|
94
100
|
try {
|
|
95
101
|
const mod = await import(fullPath);
|
|
@@ -194,9 +200,32 @@ function columnsDiffer(a, b) {
|
|
|
194
200
|
return false;
|
|
195
201
|
}
|
|
196
202
|
//#endregion
|
|
203
|
+
//#region src/column-mapper.ts
|
|
204
|
+
/**
|
|
205
|
+
* Map a Column's dataType + args to a SQL type string.
|
|
206
|
+
* Extracted from snapshot.ts, pusher.ts, generator.ts (which each had an identical copy).
|
|
207
|
+
*/
|
|
208
|
+
function columnDataTypeToSql(dataType, args) {
|
|
209
|
+
switch (dataType) {
|
|
210
|
+
case "string": {
|
|
211
|
+
const max = args[0];
|
|
212
|
+
return max != null ? `varchar(${max})` : "varchar";
|
|
213
|
+
}
|
|
214
|
+
case "json":
|
|
215
|
+
case "jsonb": return "json";
|
|
216
|
+
case "decimal": {
|
|
217
|
+
const p = args[0];
|
|
218
|
+
const s = args[1];
|
|
219
|
+
return p != null ? `decimal(${p}, ${s ?? 0})` : "decimal";
|
|
220
|
+
}
|
|
221
|
+
case "enum": return "text";
|
|
222
|
+
default: return dataType;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
//#endregion
|
|
197
226
|
//#region src/generator.ts
|
|
198
227
|
function createMigrationGenerator() {
|
|
199
|
-
function generateInitialMigration(models
|
|
228
|
+
function generateInitialMigration(models) {
|
|
200
229
|
const parts = [];
|
|
201
230
|
const indexParts = [];
|
|
202
231
|
const warnings = [];
|
|
@@ -205,7 +234,7 @@ function createMigrationGenerator() {
|
|
|
205
234
|
const table = modelDef.table;
|
|
206
235
|
if (!table) continue;
|
|
207
236
|
parts.push(generateCreateTable(table, modelDef.columns));
|
|
208
|
-
for (const [colName, col] of Object.entries(modelDef.columns)) if (col.hasConstraint("index") && !col.isPrimaryKey && !col.isUnique) indexParts.push(
|
|
237
|
+
for (const [colName, col] of Object.entries(modelDef.columns)) if (col.hasConstraint("index") && !col.isPrimaryKey && !col.isUnique) indexParts.push(generateCreateIndexFromSchema(table, `${table}_${colName}_index`, [colName]));
|
|
209
238
|
for (const [, rel] of Object.entries(modelDef.relations ?? {})) if (rel.type === "manyToMany") {
|
|
210
239
|
const through = rel.throughTable;
|
|
211
240
|
if (through && !registeredTables.has(through)) warnings.push(`// ⚠ Detected ManyToMany "${modelDef.name}" references table "${through}" but no model is registered for it.\n// Add a model to include the pivot table.`);
|
|
@@ -213,9 +242,9 @@ function createMigrationGenerator() {
|
|
|
213
242
|
}
|
|
214
243
|
const upBody = [...parts, ...indexParts].join("\n\n");
|
|
215
244
|
const downTables = [...models.values()].filter((m) => m.table).map((m) => ` await db.schema.dropTable("${m.table}").ifExists().execute()`).join("\n");
|
|
216
|
-
return `import type { Kysely } from "kysely"\
|
|
245
|
+
return `import type { Kysely } from "kysely"\n\nexport async function up(db: Kysely<any>): Promise<void> {\n${warnings.length > 0 ? ` // Warnings:\n${warnings.join("\n")}\n\n` : ""}${upBody}\n}\n\nexport async function down(db: Kysely<any>): Promise<void> {\n${downTables}\n}\n`;
|
|
217
246
|
}
|
|
218
|
-
function generateMigrationFromDiff(diffs
|
|
247
|
+
function generateMigrationFromDiff(diffs) {
|
|
219
248
|
const upParts = [];
|
|
220
249
|
const downParts = [];
|
|
221
250
|
const warnings = [];
|
|
@@ -273,7 +302,7 @@ function createMigrationGenerator() {
|
|
|
273
302
|
}
|
|
274
303
|
const upBody = upParts.join("\n\n");
|
|
275
304
|
const downBody = downParts.join("\n\n");
|
|
276
|
-
return `import type { Kysely } from "kysely"\
|
|
305
|
+
return `import type { Kysely } from "kysely"\n\nexport async function up(db: Kysely<any>): Promise<void> {\n${warnings.length > 0 ? ` // Warnings:\n${warnings.join("\n")}\n\n` : ""}${upBody}\n}\n\nexport async function down(db: Kysely<any>): Promise<void> {\n${downBody}\n}\n`;
|
|
277
306
|
}
|
|
278
307
|
return {
|
|
279
308
|
generateInitialMigration,
|
|
@@ -282,7 +311,7 @@ function createMigrationGenerator() {
|
|
|
282
311
|
}
|
|
283
312
|
function generateCreateTable(table, columns) {
|
|
284
313
|
const lines = [` await db.schema.createTable("${table}").ifNotExists()`];
|
|
285
|
-
for (const [name, col] of Object.entries(columns)) lines.push(` .addColumn("${name}", "${
|
|
314
|
+
for (const [name, col] of Object.entries(columns)) lines.push(` .addColumn("${name}", "${columnDataTypeToSql(col.dataType, col.args)}"${columnCallback(col)})`);
|
|
286
315
|
lines.push(" .execute()");
|
|
287
316
|
return lines.join("\n");
|
|
288
317
|
}
|
|
@@ -301,14 +330,6 @@ function generateCreateTableFromSchema(table, columns) {
|
|
|
301
330
|
lines.push(" .execute()");
|
|
302
331
|
return lines.join("\n");
|
|
303
332
|
}
|
|
304
|
-
function generateCreateIndex(table, column) {
|
|
305
|
-
return [
|
|
306
|
-
` await db.schema.createIndex("${table}_${column}_index")`,
|
|
307
|
-
` .on("${table}")`,
|
|
308
|
-
` .column("${column}")`,
|
|
309
|
-
" .execute()"
|
|
310
|
-
].join("\n");
|
|
311
|
-
}
|
|
312
333
|
function generateCreateIndexFromSchema(table, indexName, columns) {
|
|
313
334
|
const lines = [` await db.schema.createIndex("${indexName}").on("${table}")`];
|
|
314
335
|
for (const col of columns) lines.push(` .column("${col}")`);
|
|
@@ -342,33 +363,6 @@ function columnCallbackFromSchema(col) {
|
|
|
342
363
|
if (col.references) calls.push(`references("${col.references.table}.${col.references.column}")`);
|
|
343
364
|
return calls.length === 0 ? "" : `, (c) => c.${calls.join(".")}`;
|
|
344
365
|
}
|
|
345
|
-
function mapType(col) {
|
|
346
|
-
switch (col.dataType) {
|
|
347
|
-
case "integer":
|
|
348
|
-
case "smallint":
|
|
349
|
-
case "bigint":
|
|
350
|
-
case "text":
|
|
351
|
-
case "boolean":
|
|
352
|
-
case "timestamp":
|
|
353
|
-
case "date":
|
|
354
|
-
case "float":
|
|
355
|
-
case "double":
|
|
356
|
-
case "uuid": return col.dataType;
|
|
357
|
-
case "string": {
|
|
358
|
-
const max = col.args[0];
|
|
359
|
-
return max != null ? `varchar(${max})` : "varchar";
|
|
360
|
-
}
|
|
361
|
-
case "json":
|
|
362
|
-
case "jsonb": return "json";
|
|
363
|
-
case "decimal": {
|
|
364
|
-
const p = col.args[0];
|
|
365
|
-
const s = col.args[1];
|
|
366
|
-
return p != null ? `decimal(${p}, ${s ?? 0})` : "decimal";
|
|
367
|
-
}
|
|
368
|
-
case "enum": return "text";
|
|
369
|
-
default: return col.dataType;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
366
|
//#endregion
|
|
373
367
|
//#region src/runner.ts
|
|
374
368
|
function createMigrationRunner(db, table = "_peta_migrations") {
|
|
@@ -488,7 +482,7 @@ function columnToSchema(name, col) {
|
|
|
488
482
|
}
|
|
489
483
|
return {
|
|
490
484
|
name,
|
|
491
|
-
type:
|
|
485
|
+
type: columnDataTypeToSql(col.dataType, col.args),
|
|
492
486
|
isNullable: col.isNullable ?? false,
|
|
493
487
|
isPrimaryKey: col.isPrimaryKey ?? false,
|
|
494
488
|
isUnique: col.isUnique ?? false,
|
|
@@ -496,51 +490,37 @@ function columnToSchema(name, col) {
|
|
|
496
490
|
references
|
|
497
491
|
};
|
|
498
492
|
}
|
|
499
|
-
function mapSnapshotType(col) {
|
|
500
|
-
switch (col.dataType) {
|
|
501
|
-
case "integer":
|
|
502
|
-
case "smallint":
|
|
503
|
-
case "bigint":
|
|
504
|
-
case "text":
|
|
505
|
-
case "boolean":
|
|
506
|
-
case "timestamp":
|
|
507
|
-
case "date":
|
|
508
|
-
case "float":
|
|
509
|
-
case "double":
|
|
510
|
-
case "uuid": return col.dataType;
|
|
511
|
-
case "string": {
|
|
512
|
-
const max = col.args[0];
|
|
513
|
-
return max != null ? `varchar(${max})` : "varchar";
|
|
514
|
-
}
|
|
515
|
-
case "json":
|
|
516
|
-
case "jsonb": return "json";
|
|
517
|
-
case "decimal": {
|
|
518
|
-
const p = col.args[0];
|
|
519
|
-
const s = col.args[1];
|
|
520
|
-
return p != null ? `decimal(${p}, ${s ?? 0})` : "decimal";
|
|
521
|
-
}
|
|
522
|
-
case "enum": return "text";
|
|
523
|
-
default: return col.dataType;
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
493
|
/**
|
|
527
494
|
* Load snapshot from a JSON file path.
|
|
495
|
+
* Returns null when the file doesn't exist.
|
|
496
|
+
* Throws when the file is corrupt or has an invalid shape.
|
|
528
497
|
*/
|
|
529
|
-
|
|
498
|
+
function loadSnapshot(filePath) {
|
|
499
|
+
let raw;
|
|
530
500
|
try {
|
|
531
|
-
|
|
532
|
-
const data = readFileSync(filePath, "utf-8");
|
|
533
|
-
return JSON.parse(data);
|
|
501
|
+
raw = readFileSync(filePath, "utf-8");
|
|
534
502
|
} catch {
|
|
535
503
|
return null;
|
|
536
504
|
}
|
|
505
|
+
let parsed;
|
|
506
|
+
try {
|
|
507
|
+
parsed = JSON.parse(raw);
|
|
508
|
+
} catch {
|
|
509
|
+
throw new Error(`Corrupt snapshot file "${filePath}". Delete it and re-run \`peta migrate:generate\` to recreate it.`);
|
|
510
|
+
}
|
|
511
|
+
if (!isSchemaSnapshot(parsed)) throw new Error(`Invalid snapshot shape in "${filePath}". Delete it and re-run \`peta migrate:generate\` to recreate it.`);
|
|
512
|
+
return parsed;
|
|
513
|
+
}
|
|
514
|
+
function isSchemaSnapshot(value) {
|
|
515
|
+
if (typeof value !== "object" || value === null) return false;
|
|
516
|
+
const obj = value;
|
|
517
|
+
return obj.version === 1 && Array.isArray(obj.tables) && obj.tables.every((t) => typeof t === "object" && t !== null && typeof t.name === "string" && Array.isArray(t.columns));
|
|
537
518
|
}
|
|
538
519
|
/**
|
|
539
520
|
* Save snapshot to a JSON file path.
|
|
540
521
|
*/
|
|
541
|
-
|
|
542
|
-
const { writeFileSync } = await import("node:fs");
|
|
522
|
+
function saveSnapshot(filePath, snapshot) {
|
|
543
523
|
writeFileSync(filePath, JSON.stringify(snapshot, null, 2));
|
|
544
524
|
}
|
|
545
525
|
//#endregion
|
|
546
|
-
export { createMigrationGenerator as a, loadConfig as c, computeChecksum as d, loadChecksums as f, createMigrationRunner as i, loadMigrationFiles as l, verifyChecksum as m, loadSnapshot as n,
|
|
526
|
+
export { createMigrationGenerator as a, loadConfig as c, computeChecksum as d, loadChecksums as f, createMigrationRunner as i, loadMigrationFiles as l, verifyChecksum as m, loadSnapshot as n, columnDataTypeToSql as o, saveChecksums as p, saveSnapshot as r, diffSnapshots as s, createSnapshot as t, loadModels as u };
|