peta-migrate 0.1.1 → 0.2.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.
- package/LICENSE +21 -0
- package/README.md +6 -5
- package/bin/peta +3 -0
- package/dist/cli.mjs +117 -9
- package/dist/index.d.mts +88 -379
- package/dist/index.mjs +3 -2
- package/dist/pusher-Be3BYUQM.mjs +88 -0
- package/dist/snapshot-DopEB8mx.mjs +550 -0
- package/package.json +13 -5
- package/dist/runner-DOQsuaSQ.mjs +0 -180
package/dist/runner-DOQsuaSQ.mjs
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import { resolve } from "node:path";
|
|
2
|
-
import { sql } from "kysely";
|
|
3
|
-
//#region src/config.ts
|
|
4
|
-
function defineConfig(config) {
|
|
5
|
-
return config;
|
|
6
|
-
}
|
|
7
|
-
async function loadConfig() {
|
|
8
|
-
const candidates = [
|
|
9
|
-
"peta.config.ts",
|
|
10
|
-
"peta.config.js",
|
|
11
|
-
"peta.config.mjs"
|
|
12
|
-
];
|
|
13
|
-
let mod = null;
|
|
14
|
-
for (const file of candidates) try {
|
|
15
|
-
mod = await import(resolve(process.cwd(), file));
|
|
16
|
-
break;
|
|
17
|
-
} catch {}
|
|
18
|
-
if (!mod) throw new Error("No peta.config.ts found. Create one in your project root.");
|
|
19
|
-
const config = mod.default ?? mod;
|
|
20
|
-
return {
|
|
21
|
-
migrationsDir: config.migrationsDir,
|
|
22
|
-
models: config.models,
|
|
23
|
-
getKysely: config.getKysely
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
async function loadMigrationFiles(dir) {
|
|
27
|
-
const { readdirSync } = await import("node:fs");
|
|
28
|
-
const files = readdirSync(dir).filter((f) => f.endsWith(".ts") || f.endsWith(".js")).sort();
|
|
29
|
-
const migrations = [];
|
|
30
|
-
for (const file of files) {
|
|
31
|
-
const mod = await import(resolve(dir, file));
|
|
32
|
-
if (mod.up) migrations.push({
|
|
33
|
-
name: file.replace(/\.(ts|js)$/, ""),
|
|
34
|
-
up: mod.up,
|
|
35
|
-
down: mod.down ?? (async () => {})
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
return migrations;
|
|
39
|
-
}
|
|
40
|
-
//#endregion
|
|
41
|
-
//#region src/generator.ts
|
|
42
|
-
function createMigrationGenerator() {
|
|
43
|
-
function generateInitialMigration(models, options = {}) {
|
|
44
|
-
options.name;
|
|
45
|
-
const parts = [];
|
|
46
|
-
const indexParts = [];
|
|
47
|
-
const warnings = [];
|
|
48
|
-
const registeredTables = new Set([...models.values()].map((m) => m.table).filter(Boolean));
|
|
49
|
-
for (const [, modelDef] of models) {
|
|
50
|
-
const table = modelDef.table;
|
|
51
|
-
if (!table) continue;
|
|
52
|
-
parts.push(generateCreateTable(table, modelDef.columns));
|
|
53
|
-
for (const [colName, col] of Object.entries(modelDef.columns)) if (col.hasConstraint("index") && !col.isPrimaryKey && !col.isUnique) indexParts.push(generateCreateIndex(table, colName));
|
|
54
|
-
for (const [, rel] of Object.entries(modelDef.relations ?? {})) if (rel.type === "manyToMany") {
|
|
55
|
-
const through = rel.throughTable;
|
|
56
|
-
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.`);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
const upBody = [...parts, ...indexParts].join("\n\n");
|
|
60
|
-
const downTables = [...models.values()].filter((m) => m.table).map((m) => ` await db.schema.dropTable("${m.table}").ifExists().execute()`).join("\n");
|
|
61
|
-
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`;
|
|
62
|
-
}
|
|
63
|
-
return { generateInitialMigration };
|
|
64
|
-
}
|
|
65
|
-
function generateCreateTable(table, columns) {
|
|
66
|
-
const lines = [` await db.schema.createTable("${table}").ifNotExists()`];
|
|
67
|
-
for (const [name, col] of Object.entries(columns)) lines.push(` .addColumn("${name}", "${mapType(col)}"${columnCallback(col)})`);
|
|
68
|
-
lines.push(" .execute()");
|
|
69
|
-
return lines.join("\n");
|
|
70
|
-
}
|
|
71
|
-
function generateCreateIndex(table, column) {
|
|
72
|
-
return [
|
|
73
|
-
` await db.schema.createIndex("${table}_${column}_index")`,
|
|
74
|
-
` .on("${table}")`,
|
|
75
|
-
` .column("${column}")`,
|
|
76
|
-
" .execute()"
|
|
77
|
-
].join("\n");
|
|
78
|
-
}
|
|
79
|
-
function columnCallback(col) {
|
|
80
|
-
const calls = [];
|
|
81
|
-
if (col.isPrimaryKey) {
|
|
82
|
-
if (col.dataType === "integer") calls.push("autoIncrement()");
|
|
83
|
-
calls.push("primaryKey()");
|
|
84
|
-
}
|
|
85
|
-
if (!col.isNullable && !col.isPrimaryKey) calls.push("notNull()");
|
|
86
|
-
const dv = col.defaultValue;
|
|
87
|
-
if (dv !== void 0 && typeof dv !== "function") calls.push(`defaultTo(${JSON.stringify(dv)})`);
|
|
88
|
-
if (col.isUnique && !col.isPrimaryKey) calls.push("unique()");
|
|
89
|
-
const refConstraint = col.constraints.find((c) => c.type === "references");
|
|
90
|
-
if (refConstraint?.args[0]) {
|
|
91
|
-
const targetTable = (typeof refConstraint.args[0] === "function" ? refConstraint.args[0]() : refConstraint.args[0])?.table;
|
|
92
|
-
const targetColumns = refConstraint.args[1];
|
|
93
|
-
if (typeof targetTable === "string" && targetTable && targetColumns?.length) calls.push(`references("${targetTable}.${targetColumns[0]}")`);
|
|
94
|
-
}
|
|
95
|
-
return calls.length === 0 ? "" : `, (c) => c.${calls.join(".")}`;
|
|
96
|
-
}
|
|
97
|
-
function mapType(col) {
|
|
98
|
-
switch (col.dataType) {
|
|
99
|
-
case "integer":
|
|
100
|
-
case "smallint":
|
|
101
|
-
case "bigint":
|
|
102
|
-
case "text":
|
|
103
|
-
case "boolean":
|
|
104
|
-
case "timestamp":
|
|
105
|
-
case "date":
|
|
106
|
-
case "float":
|
|
107
|
-
case "double":
|
|
108
|
-
case "uuid": return col.dataType;
|
|
109
|
-
case "string": {
|
|
110
|
-
const max = col.args[0];
|
|
111
|
-
return max != null ? `varchar(${max})` : "varchar";
|
|
112
|
-
}
|
|
113
|
-
case "json":
|
|
114
|
-
case "jsonb": return "json";
|
|
115
|
-
case "decimal": {
|
|
116
|
-
const p = col.args[0];
|
|
117
|
-
const s = col.args[1];
|
|
118
|
-
return p != null ? `decimal(${p}, ${s ?? 0})` : "decimal";
|
|
119
|
-
}
|
|
120
|
-
case "enum": return "text";
|
|
121
|
-
default: return col.dataType;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
//#endregion
|
|
125
|
-
//#region src/runner.ts
|
|
126
|
-
function createMigrationRunner(db, table = "_peta_migrations") {
|
|
127
|
-
async function ensureTable() {
|
|
128
|
-
await db.schema.createTable(table).ifNotExists().addColumn("name", "varchar", (c) => c.notNull().primaryKey()).addColumn("applied_at", "timestamp", (c) => c.notNull().defaultTo(sql`CURRENT_TIMESTAMP`)).execute();
|
|
129
|
-
}
|
|
130
|
-
async function getCompleted() {
|
|
131
|
-
try {
|
|
132
|
-
return (await db.selectFrom(table).select(["name", "applied_at"]).orderBy("name", "asc").execute()).map((r) => ({
|
|
133
|
-
name: String(r.name),
|
|
134
|
-
appliedAt: String(r.applied_at)
|
|
135
|
-
}));
|
|
136
|
-
} catch {
|
|
137
|
-
return [];
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
async function up(migrations) {
|
|
141
|
-
await ensureTable();
|
|
142
|
-
const completed = await getCompleted();
|
|
143
|
-
const completedNames = new Set(completed.map((r) => r.name));
|
|
144
|
-
for (const m of migrations.filter((m) => !completedNames.has(m.name)).sort(byName)) {
|
|
145
|
-
await m.up(db);
|
|
146
|
-
await db.insertInto(table).values({
|
|
147
|
-
name: m.name,
|
|
148
|
-
applied_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
149
|
-
}).execute();
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
async function down(migrations) {
|
|
153
|
-
const completed = await getCompleted();
|
|
154
|
-
if (completed.length === 0 || migrations.length === 0) return;
|
|
155
|
-
for (const m of migrations.filter((m) => completed.some((r) => r.name === m.name)).sort(byName).reverse()) {
|
|
156
|
-
await m.down(db);
|
|
157
|
-
await db.deleteFrom(table).where("name", "=", m.name).execute();
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
async function status(migrations) {
|
|
161
|
-
const completed = await getCompleted();
|
|
162
|
-
const completedNames = new Set(completed.map((r) => r.name));
|
|
163
|
-
return {
|
|
164
|
-
completed,
|
|
165
|
-
pending: migrations.filter((m) => !completedNames.has(m.name)).sort(byName)
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
return {
|
|
169
|
-
ensureTable,
|
|
170
|
-
getCompleted,
|
|
171
|
-
up,
|
|
172
|
-
down,
|
|
173
|
-
status
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
function byName(a, b) {
|
|
177
|
-
return a.name.localeCompare(b.name);
|
|
178
|
-
}
|
|
179
|
-
//#endregion
|
|
180
|
-
export { loadMigrationFiles as a, loadConfig as i, createMigrationGenerator as n, defineConfig as r, createMigrationRunner as t };
|