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 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 { defineConfig } from "peta-migrate"
89
+ import type { PetaMigrateConfig } from "peta-migrate"
90
90
 
91
- const config = defineConfig({
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, o as diffSnapshots, p as saveChecksums, r as saveSnapshot, t as createSnapshot, u as loadModels } from "./snapshot-CO180dF3.mjs";
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 = await loadSnapshot(snapshotPath);
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, { name: name ?? "changes" });
37
- await saveSnapshot(snapshotPath, currentSnapshot);
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
- await saveSnapshot(snapshotPath, createSnapshot(models));
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 = await loadSnapshot(resolve(config.migrationsDir, "snapshot.json"));
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-Be3BYUQM.mjs").then((n) => n.n);
148
+ const { pushSchema } = await import("./pusher-DZQMrZdN.mjs").then((n) => n.n);
149
149
  const created = await pushSchema(config.getKysely(), models);
150
- await saveSnapshot(resolve(config.migrationsDir, "snapshot.json"), createSnapshot(models));
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
- for (const file of seedFiles) {
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) await mod.seed(config.getKysely());
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 ${seedFiles.length} seed(s)`);
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 defineConfig(config: PetaMigrateConfig): PetaMigrateConfig;
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
- interface GeneratorOptions {
103
- name?: string;
104
- }
105
- interface MigrationGenerator {
106
- generateInitialMigration(models: Map<string, ModelDefinition>, options?: GeneratorOptions): string;
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
- interface MigrationRunner {
121
- ensureTable(): Promise<void>;
122
- getCompleted(): Promise<MigrationRecord[]>;
123
- up(migrations: MigrationFile[]): Promise<void>;
124
- down(migrations: MigrationFile[]): Promise<void>;
125
- status(migrations: MigrationFile[]): Promise<MigrationStatus>;
126
- }
127
- declare function createMigrationRunner(db: Kysely<Record<string, never>>, table?: string): MigrationRunner;
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): Promise<SchemaSnapshot | null>;
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): Promise<void>;
134
+ declare function saveSnapshot(filePath: string, snapshot: SchemaSnapshot): void;
142
135
  //#endregion
143
- export { type GeneratorOptions, type MigrationFile, type MigrationGenerator, type MigrationRecord, type MigrationRunner, type MigrationStatus, type PetaMigrateConfig, type ResolvedConfig, type SchemaColumn, type SchemaDiff, type SchemaIndex, type SchemaSnapshot, type SchemaTable, computeChecksum, createMigrationGenerator, createMigrationRunner, createSnapshot, defineConfig, diffSnapshots, loadChecksums, loadConfig, loadMigrationFiles, loadModels, loadSnapshot, pushSchema, saveChecksums, saveSnapshot, verifyChecksum };
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-Be3BYUQM.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, o as diffSnapshots, p as saveChecksums, r as saveSnapshot, s as defineConfig, t as createSnapshot, u as loadModels } from "./snapshot-CO180dF3.mjs";
3
- export { computeChecksum, createMigrationGenerator, createMigrationRunner, createSnapshot, defineConfig, diffSnapshots, loadChecksums, loadConfig, loadMigrationFiles, loadModels, loadSnapshot, pushSchema, saveChecksums, saveSnapshot, verifyChecksum };
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, mapPushType(col), (cb) => buildColumn(col, cb));
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
- if (!mod) throw new Error("No peta.config.ts found. Create one in your project root.");
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
- const { readdirSync } = await import("node:fs");
66
- const files = readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".js")).sort();
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 glob = new Bun.Glob(pattern);
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, _options = {}) {
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(generateCreateIndex(table, colName));
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"\nimport { sql } 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`;
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, _options = {}) {
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"\nimport { sql } 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`;
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}", "${mapType(col)}"${columnCallback(col)})`);
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: mapSnapshotType(col),
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
- async function loadSnapshot(filePath) {
498
+ function loadSnapshot(filePath) {
499
+ let raw;
530
500
  try {
531
- const { readFileSync } = await import("node:fs");
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
- async function saveSnapshot(filePath, snapshot) {
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, diffSnapshots as o, saveChecksums as p, saveSnapshot as r, defineConfig as s, createSnapshot as t, loadModels as u };
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 };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "peta-migrate",
3
3
  "type": "module",
4
4
  "private": false,
5
- "version": "0.2.2",
5
+ "version": "0.3.0",
6
6
  "description": "Migration tools for peta-orm",
7
7
  "license": "MIT",
8
8
  "repository": {