pecunia-root 0.2.2 → 0.2.4
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.
|
@@ -86,14 +86,14 @@ function matchType(columnDataType, fieldType, dbType) {
|
|
|
86
86
|
function normalize(type) {
|
|
87
87
|
return type.toLowerCase().split("(")[0].trim();
|
|
88
88
|
}
|
|
89
|
-
if (fieldType === "string[]" || fieldType === "number[]")
|
|
89
|
+
if (fieldType === "string[]" || fieldType === "number[]") {
|
|
90
|
+
const normalized = columnDataType.toLowerCase();
|
|
91
|
+
if (normalized.includes("[]")) return true;
|
|
92
|
+
return normalized.includes("json");
|
|
93
|
+
}
|
|
90
94
|
const types = map[dbType];
|
|
91
95
|
return (Array.isArray(fieldType) ? types.string?.map((t) => t.toLowerCase()) : types[fieldType].map((t) => t.toLowerCase()))?.includes(normalize(columnDataType));
|
|
92
96
|
}
|
|
93
|
-
/**
|
|
94
|
-
* Get the current PostgreSQL schema (search_path) for the database connection
|
|
95
|
-
* Returns the first schema in the search_path, defaulting to 'public' if not found
|
|
96
|
-
*/
|
|
97
97
|
async function getPostgresSchema(db) {
|
|
98
98
|
try {
|
|
99
99
|
const result = await sql`SHOW search_path`.execute(db);
|
|
@@ -116,15 +116,6 @@ async function getMigrations(config) {
|
|
|
116
116
|
if (dbType === "postgres") {
|
|
117
117
|
currentSchema = await getPostgresSchema(db);
|
|
118
118
|
console.debug(`PostgreSQL migration: Using schema '${currentSchema}' (from search_path)`);
|
|
119
|
-
try {
|
|
120
|
-
if (!(await sql`
|
|
121
|
-
SELECT schema_name
|
|
122
|
-
FROM information_schema.schemata
|
|
123
|
-
WHERE schema_name = ${currentSchema}
|
|
124
|
-
`.execute(db)).rows[0]) console.warn(`Schema '${currentSchema}' does not exist. Tables will be inspected from available schemas. Consider creating the schema first or checking your database configuration.`);
|
|
125
|
-
} catch (error) {
|
|
126
|
-
console.debug(`Could not verify schema existence: ${error instanceof Error ? error.message : String(error)}`);
|
|
127
|
-
}
|
|
128
119
|
}
|
|
129
120
|
const allTableMetadata = await db.introspection.getTables();
|
|
130
121
|
let tableMetadata = allTableMetadata;
|
|
@@ -137,7 +128,6 @@ async function getMigrations(config) {
|
|
|
137
128
|
`.execute(db);
|
|
138
129
|
const tableNamesInSchema = new Set(tablesInSchema.rows.map((row) => row.table_name));
|
|
139
130
|
tableMetadata = allTableMetadata.filter((table) => table.schema === currentSchema && tableNamesInSchema.has(table.name));
|
|
140
|
-
console.debug(`Found ${tableMetadata.length} table(s) in schema '${currentSchema}': ${tableMetadata.map((t) => t.name).join(", ") || "(none)"}`);
|
|
141
131
|
} catch (error) {
|
|
142
132
|
console.warn(`Could not filter tables by schema. Using all discovered tables. Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
143
133
|
}
|
|
@@ -146,16 +136,16 @@ async function getMigrations(config) {
|
|
|
146
136
|
for (const [key, value] of Object.entries(billingEngineSchema)) {
|
|
147
137
|
const table = tableMetadata.find((t) => t.name === key);
|
|
148
138
|
if (!table) {
|
|
149
|
-
const
|
|
139
|
+
const existing = toBeCreated.findIndex((t) => t.table === key);
|
|
150
140
|
const tableData = {
|
|
151
141
|
table: key,
|
|
152
142
|
fields: value.fields,
|
|
153
143
|
order: value.order || Infinity
|
|
154
144
|
};
|
|
155
145
|
const insertIndex = toBeCreated.findIndex((t) => (t.order || Infinity) > tableData.order);
|
|
156
|
-
if (insertIndex === -1) if (
|
|
157
|
-
else toBeCreated[
|
|
158
|
-
...toBeCreated[
|
|
146
|
+
if (insertIndex === -1) if (existing === -1) toBeCreated.push(tableData);
|
|
147
|
+
else toBeCreated[existing].fields = {
|
|
148
|
+
...toBeCreated[existing].fields,
|
|
159
149
|
...value.fields
|
|
160
150
|
};
|
|
161
151
|
else toBeCreated.splice(insertIndex, 0, tableData);
|
|
@@ -226,13 +216,13 @@ async function getMigrations(config) {
|
|
|
226
216
|
},
|
|
227
217
|
"string[]": {
|
|
228
218
|
sqlite: "text",
|
|
229
|
-
postgres:
|
|
219
|
+
postgres: sql`text[]`,
|
|
230
220
|
mysql: "json",
|
|
231
221
|
mssql: "varchar(8000)"
|
|
232
222
|
},
|
|
233
223
|
"number[]": {
|
|
234
224
|
sqlite: "text",
|
|
235
|
-
postgres:
|
|
225
|
+
postgres: sql`integer[]`,
|
|
236
226
|
mysql: "json",
|
|
237
227
|
mssql: "varchar(8000)"
|
|
238
228
|
}
|
|
@@ -242,7 +232,7 @@ async function getMigrations(config) {
|
|
|
242
232
|
return typeMap.foreignKeyId[provider];
|
|
243
233
|
}
|
|
244
234
|
if (Array.isArray(type)) return "text";
|
|
245
|
-
if (!(type in typeMap)) throw new Error(`Unsupported field type '${String(type)}' for field '${fieldName}'
|
|
235
|
+
if (!(type in typeMap)) throw new Error(`Unsupported field type '${String(type)}' for field '${fieldName}'.`);
|
|
246
236
|
return typeMap[type][provider];
|
|
247
237
|
}
|
|
248
238
|
const getModelName = initGetModelName({
|
|
@@ -263,20 +253,17 @@ async function getMigrations(config) {
|
|
|
263
253
|
return `${model}.${field}`;
|
|
264
254
|
}
|
|
265
255
|
}
|
|
256
|
+
const applyColumnOptions = (field, col) => {
|
|
257
|
+
col = field.required === true ? col.notNull() : col;
|
|
258
|
+
if (field.references) col = col.references(getReferencePath(field.references.model, field.references.field)).onDelete(field.references.onDelete || "no action");
|
|
259
|
+
if (field.unique) col = col.unique();
|
|
260
|
+
if (field.type === "date" && typeof field.defaultValue === "function" && (dbType === "postgres" || dbType === "mysql" || dbType === "mssql")) col = dbType === "mysql" ? col.defaultTo(sql`CURRENT_TIMESTAMP(3)`) : col.defaultTo(sql`CURRENT_TIMESTAMP`);
|
|
261
|
+
return col;
|
|
262
|
+
};
|
|
266
263
|
if (toBeAdded.length) for (const table of toBeAdded) for (const [fieldName, field] of Object.entries(table.fields)) {
|
|
267
264
|
const type = getType(field, fieldName);
|
|
268
|
-
if (field.index) {
|
|
269
|
-
|
|
270
|
-
migrations.push(index);
|
|
271
|
-
}
|
|
272
|
-
const built = db.schema.alterTable(table.table).addColumn(fieldName, type, (col) => {
|
|
273
|
-
col = field.required !== false ? col.notNull() : col;
|
|
274
|
-
if (field.references) col = col.references(getReferencePath(field.references.model, field.references.field)).onDelete(field.references.onDelete || "cascade");
|
|
275
|
-
if (field.unique) col = col.unique();
|
|
276
|
-
if (field.type === "date" && typeof field.defaultValue === "function" && (dbType === "postgres" || dbType === "mysql" || dbType === "mssql")) col = dbType === "mysql" ? col.defaultTo(sql`CURRENT_TIMESTAMP(3)`) : col.defaultTo(sql`CURRENT_TIMESTAMP`);
|
|
277
|
-
return col;
|
|
278
|
-
});
|
|
279
|
-
migrations.push(built);
|
|
265
|
+
if (field.index) migrations.push(db.schema.alterTable(table.table).addIndex(`${table.table}_${fieldName}_idx`));
|
|
266
|
+
migrations.push(db.schema.alterTable(table.table).addColumn(fieldName, type, (col) => applyColumnOptions(field, col)));
|
|
280
267
|
}
|
|
281
268
|
const toBeIndexed = [];
|
|
282
269
|
if (toBeCreated.length) for (const table of toBeCreated) {
|
|
@@ -287,13 +274,7 @@ async function getMigrations(config) {
|
|
|
287
274
|
});
|
|
288
275
|
for (const [fieldName, field] of Object.entries(table.fields)) {
|
|
289
276
|
const type = getType(field, fieldName);
|
|
290
|
-
dbT = dbT.addColumn(fieldName, type, (col) =>
|
|
291
|
-
col = field.required !== false ? col.notNull() : col;
|
|
292
|
-
if (field.references) col = col.references(getReferencePath(field.references.model, field.references.field)).onDelete(field.references.onDelete || "cascade");
|
|
293
|
-
if (field.unique) col = col.unique();
|
|
294
|
-
if (field.type === "date" && typeof field.defaultValue === "function" && (dbType === "postgres" || dbType === "mysql" || dbType === "mssql")) col = dbType === "mysql" ? col.defaultTo(sql`CURRENT_TIMESTAMP(3)`) : col.defaultTo(sql`CURRENT_TIMESTAMP`);
|
|
295
|
-
return col;
|
|
296
|
-
});
|
|
277
|
+
dbT = dbT.addColumn(fieldName, type, (col) => applyColumnOptions(field, col));
|
|
297
278
|
if (field.index) {
|
|
298
279
|
const idx = db.schema.createIndex(`${table.table}_${fieldName}_${field.unique ? "uidx" : "idx"}`).on(table.table).columns([fieldName]);
|
|
299
280
|
toBeIndexed.push(field.unique ? idx.unique() : idx);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/db/migrations/index.ts"],"sourcesContent":["import type { PecuniaOptions } from \"pecunia-core\";\nimport type { DBFieldAttribute, DBFieldType } from \"pecunia-core\";\nimport { initGetFieldName, initGetModelName } from \"pecunia-core\";\nimport type {\n AlterTableBuilder,\n AlterTableColumnAlteringBuilder,\n ColumnDataType,\n CreateIndexBuilder,\n CreateTableBuilder,\n Kysely,\n RawBuilder,\n} from \"kysely\";\nimport { sql } from \"kysely\";\nimport { createKyselyAdapter } from \"../../adapters/kysely-adapter/dialect\";\nimport type { KyselyDatabaseDialectType } from \"pecunia-core\";\nimport { getSchema } from \"../schema/get-schema\";\nimport { getPaymentTables } from \"pecunia-core\";\n\ntype DbTypeBuckets = Record<\n \"string\" | \"number\" | \"boolean\" | \"date\" | \"json\",\n string[]\n>;\n\nconst postgresMap = {\n string: [\"character varying\", \"varchar\", \"text\", \"uuid\"],\n number: [\n \"int4\",\n \"integer\",\n \"bigint\",\n \"smallint\",\n \"numeric\",\n \"real\",\n \"double precision\",\n ],\n boolean: [\"bool\", \"boolean\"],\n date: [\"timestamptz\", \"timestamp\", \"date\"],\n json: [\"json\", \"jsonb\"],\n};\n\nconst mysqlMap = {\n string: [\"varchar\", \"text\", \"uuid\"],\n number: [\n \"integer\",\n \"int\",\n \"bigint\",\n \"smallint\",\n \"decimal\",\n \"float\",\n \"double\",\n ],\n boolean: [\"boolean\", \"tinyint\"],\n date: [\"timestamp\", \"datetime\", \"date\"],\n json: [\"json\"],\n};\n\nconst sqliteMap = {\n string: [\"TEXT\"],\n number: [\"INTEGER\", \"REAL\"],\n boolean: [\"INTEGER\", \"BOOLEAN\"], // 0 or 1\n date: [\"DATE\", \"INTEGER\"],\n json: [\"TEXT\"],\n};\n\nconst mssqlMap = {\n string: [\"varchar\", \"nvarchar\", \"uniqueidentifier\"],\n number: [\"int\", \"bigint\", \"smallint\", \"decimal\", \"float\", \"double\"],\n boolean: [\"bit\", \"smallint\"],\n date: [\"datetime2\", \"date\", \"datetime\"],\n json: [\"varchar\", \"nvarchar\"],\n};\n\nconst map = {\n postgres: postgresMap,\n mysql: mysqlMap,\n sqlite: sqliteMap,\n mssql: mssqlMap,\n};\n\nexport function matchType(\n columnDataType: string,\n fieldType: DBFieldType,\n dbType: KyselyDatabaseDialectType,\n) {\n function normalize(type: string) {\n return type.toLowerCase().split(\"(\")[0]!.trim();\n }\n\n if (fieldType === \"string[]\" || fieldType === \"number[]\") {\n return columnDataType.toLowerCase().includes(\"json\");\n }\n\n const types: Partial<DbTypeBuckets> = map[dbType]!;\n const expected = Array.isArray(fieldType)\n ? types.string?.map((t) => t.toLowerCase())\n : types[fieldType]!.map((t: string) => t.toLowerCase());\n\n return expected?.includes(normalize(columnDataType));\n}\n\n/**\n * Get the current PostgreSQL schema (search_path) for the database connection\n * Returns the first schema in the search_path, defaulting to 'public' if not found\n */\nasync function getPostgresSchema(db: Kysely<unknown>): Promise<string> {\n try {\n const result = await sql<{ search_path: string }>`SHOW search_path`.execute(\n db,\n );\n\n if (result.rows[0]?.search_path) {\n const schemas = result.rows[0].search_path\n .split(\",\")\n .map((s) => s.trim())\n .map((s) => s.replace(/^[\"']|[\"']$/g, \"\"))\n .filter((s) => !s.startsWith(\"$\"));\n\n return schemas[0] || \"public\";\n }\n } catch {\n // fall back to public schema\n }\n\n return \"public\";\n}\n\nexport async function getMigrations(config: PecuniaOptions) {\n const billingEngineSchema = getSchema(config);\n\n let { kysely: db, databaseType: dbType } = await createKyselyAdapter(config);\n\n if (!dbType) {\n console.warn(\n \"Could not determine database type, defaulting to sqlite. Please provide a type in the database options to avoid this.\",\n );\n dbType = \"sqlite\";\n }\n\n if (!db) {\n console.error(\n \"Only kysely adapter is supported for migrations. You can use `generate` command to generate the schema, if you're using a different adapter.\",\n );\n process.exit(1);\n }\n\n // For PostgreSQL, detect and log the current schema being used\n let currentSchema = \"public\";\n if (dbType === \"postgres\") {\n currentSchema = await getPostgresSchema(db);\n console.debug(\n `PostgreSQL migration: Using schema '${currentSchema}' (from search_path)`,\n );\n\n // Verify the schema exists\n try {\n const schemaCheck = await sql<{ schema_name: string }>`\n SELECT schema_name\n FROM information_schema.schemata\n WHERE schema_name = ${currentSchema}\n `.execute(db);\n\n if (!schemaCheck.rows[0]) {\n console.warn(\n `Schema '${currentSchema}' does not exist. Tables will be inspected from available schemas. Consider creating the schema first or checking your database configuration.`,\n );\n }\n } catch (error) {\n console.debug(\n `Could not verify schema existence: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n }\n\n const allTableMetadata = await db.introspection.getTables();\n\n // For PostgreSQL, filter tables to only those in the target schema\n let tableMetadata = allTableMetadata;\n if (dbType === \"postgres\") {\n try {\n const tablesInSchema = await sql<{\n table_name: string;\n }>`\n SELECT table_name\n FROM information_schema.tables\n WHERE table_schema = ${currentSchema}\n AND table_type = 'BASE TABLE'\n `.execute(db);\n\n const tableNamesInSchema = new Set(\n tablesInSchema.rows.map((row) => row.table_name),\n );\n\n tableMetadata = allTableMetadata.filter(\n (table) =>\n table.schema === currentSchema && tableNamesInSchema.has(table.name),\n );\n\n console.debug(\n `Found ${tableMetadata.length} table(s) in schema '${currentSchema}': ${\n tableMetadata.map((t) => t.name).join(\", \") || \"(none)\"\n }`,\n );\n } catch (error) {\n console.warn(\n `Could not filter tables by schema. Using all discovered tables. Error: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n }\n\n const toBeCreated: {\n table: string;\n fields: Record<string, DBFieldAttribute>;\n order: number;\n }[] = [];\n\n const toBeAdded: {\n table: string;\n fields: Record<string, DBFieldAttribute>;\n order: number;\n }[] = [];\n\n for (const [key, value] of Object.entries(billingEngineSchema)) {\n const table = tableMetadata.find((t) => t.name === key);\n\n if (!table) {\n const tIndex = toBeCreated.findIndex((t) => t.table === key);\n const tableData = {\n table: key,\n fields: value.fields,\n order: value.order || Infinity,\n };\n\n const insertIndex = toBeCreated.findIndex(\n (t) => (t.order || Infinity) > tableData.order,\n );\n\n if (insertIndex === -1) {\n if (tIndex === -1) {\n toBeCreated.push(tableData);\n } else {\n toBeCreated[tIndex]!.fields = {\n ...toBeCreated[tIndex]!.fields,\n ...value.fields,\n };\n }\n } else {\n toBeCreated.splice(insertIndex, 0, tableData);\n }\n\n continue;\n }\n\n const toBeAddedFields: Record<string, DBFieldAttribute> = {};\n\n for (const [fieldName, field] of Object.entries(value.fields)) {\n const column = table.columns.find((c) => c.name === fieldName);\n\n if (!column) {\n toBeAddedFields[fieldName] = field;\n continue;\n }\n\n if (matchType(column.dataType, field.type, dbType)) {\n continue;\n }\n\n console.warn(\n `Field ${fieldName} in table ${key} has a different type in the database. Expected ${field.type} but got ${column.dataType}.`,\n );\n }\n\n if (Object.keys(toBeAddedFields).length > 0) {\n toBeAdded.push({\n table: key,\n fields: toBeAddedFields,\n order: value.order || Infinity,\n });\n }\n }\n\n const migrations: (\n | AlterTableColumnAlteringBuilder\n | ReturnType<AlterTableBuilder[\"addIndex\"]>\n | CreateTableBuilder<string, string>\n | CreateIndexBuilder\n )[] = [];\n\n // Adapter-enforced strategy: UUID ids at the DB level\n const useUUIDs = true;\n\n function getType(field: DBFieldAttribute, fieldName: string) {\n const type = field.type;\n const provider = dbType || \"sqlite\";\n\n type StringOnlyUnion<T> = T extends string ? T : never;\n\n const typeMap: Record<\n StringOnlyUnion<DBFieldType> | \"id\" | \"foreignKeyId\",\n Record<KyselyDatabaseDialectType, ColumnDataType | RawBuilder<unknown>>\n > = {\n string: {\n sqlite: \"text\",\n postgres: \"text\",\n mysql: field.unique\n ? \"varchar(255)\"\n : field.references\n ? \"varchar(36)\"\n : field.sortable\n ? \"varchar(255)\"\n : field.index\n ? \"varchar(255)\"\n : \"text\",\n mssql:\n field.unique || field.sortable\n ? \"varchar(255)\"\n : field.references\n ? \"varchar(36)\"\n : \"varchar(8000)\",\n },\n boolean: {\n sqlite: \"integer\",\n postgres: \"boolean\",\n mysql: \"boolean\",\n mssql: \"smallint\",\n },\n number: {\n sqlite: field.bigint ? \"bigint\" : \"integer\",\n postgres: field.bigint ? \"bigint\" : \"integer\",\n mysql: field.bigint ? \"bigint\" : \"integer\",\n mssql: field.bigint ? \"bigint\" : \"integer\",\n },\n date: {\n sqlite: \"date\",\n postgres: \"timestamptz\",\n mysql: \"timestamp(3)\",\n mssql: sql`datetime2(3)`,\n },\n json: {\n sqlite: \"text\",\n postgres: \"jsonb\",\n mysql: \"json\",\n mssql: \"varchar(8000)\",\n },\n id: {\n postgres: useUUIDs ? \"uuid\" : \"text\",\n mysql: \"varchar(36)\",\n mssql: \"varchar(36)\", // ideally UNIQUEIDENTIFIER, but not in Kysely's type interface\n sqlite: \"text\",\n },\n foreignKeyId: {\n postgres: useUUIDs ? \"uuid\" : \"text\",\n mysql: \"varchar(36)\",\n mssql: \"varchar(36)\",\n sqlite: \"text\",\n },\n \"string[]\": {\n sqlite: \"text\",\n postgres: \"jsonb\",\n mysql: \"json\",\n mssql: \"varchar(8000)\",\n },\n \"number[]\": {\n sqlite: \"text\",\n postgres: \"jsonb\",\n mysql: \"json\",\n mssql: \"varchar(8000)\",\n },\n } as const;\n\n if (fieldName === \"id\" || field.references?.field === \"id\") {\n if (fieldName === \"id\") return typeMap.id[provider];\n return typeMap.foreignKeyId[provider];\n }\n\n if (Array.isArray(type)) return \"text\";\n\n if (!(type in typeMap)) {\n throw new Error(\n `Unsupported field type '${String(type)}' for field '${fieldName}'. Allowed types are: string, number, boolean, date, string[], number[]. If you need to store structured data, store it as a JSON string (type: \"string\") or split it into primitive fields. See https://better-auth.com/docs/advanced/schema#additional-fields`,\n );\n }\n\n return typeMap[type][provider];\n }\n\n const getModelName = initGetModelName({\n schema: getPaymentTables(config),\n usePlural: false,\n });\n\n const getFieldName = initGetFieldName({\n schema: getPaymentTables(config),\n usePlural: false,\n });\n\n function getReferencePath(model: string, field: string): string {\n try {\n const modelName = getModelName(model);\n const fieldName = getFieldName({ model, field });\n return `${modelName}.${fieldName}`;\n } catch {\n return `${model}.${field}`;\n }\n }\n\n if (toBeAdded.length) {\n for (const table of toBeAdded) {\n for (const [fieldName, field] of Object.entries(table.fields)) {\n const type = getType(field, fieldName);\n\n if (field.index) {\n const index = db.schema\n .alterTable(table.table)\n .addIndex(`${table.table}_${fieldName}_idx`);\n migrations.push(index);\n }\n\n const built = db.schema\n .alterTable(table.table)\n .addColumn(fieldName, type, (col) => {\n col = field.required !== false ? col.notNull() : col;\n\n if (field.references) {\n col = col\n .references(\n getReferencePath(\n field.references.model,\n field.references.field,\n ),\n )\n .onDelete(field.references.onDelete || \"cascade\");\n }\n\n if (field.unique) col = col.unique();\n\n if (\n field.type === \"date\" &&\n typeof field.defaultValue === \"function\" &&\n (dbType === \"postgres\" ||\n dbType === \"mysql\" ||\n dbType === \"mssql\")\n ) {\n col =\n dbType === \"mysql\"\n ? col.defaultTo(sql`CURRENT_TIMESTAMP(3)`)\n : col.defaultTo(sql`CURRENT_TIMESTAMP`);\n }\n\n return col;\n });\n\n migrations.push(built);\n }\n }\n }\n\n const toBeIndexed: CreateIndexBuilder[] = [];\n\n if (toBeCreated.length) {\n for (const table of toBeCreated) {\n const idType = getType({ type: \"string\" }, \"id\");\n\n let dbT = db.schema\n .createTable(table.table)\n .addColumn(\"id\", idType, (col) => {\n // UUID IDs at DB level\n if (dbType === \"postgres\") {\n return col\n .primaryKey()\n .defaultTo(sql`pg_catalog.gen_random_uuid()`)\n .notNull();\n }\n // For non-postgres, don't assume a DB-level UUID generator exists.\n // UUIDs can still be generated in application code.\n return col.primaryKey().notNull();\n });\n\n for (const [fieldName, field] of Object.entries(table.fields)) {\n const type = getType(field, fieldName);\n\n dbT = dbT.addColumn(fieldName, type, (col) => {\n col = field.required !== false ? col.notNull() : col;\n\n if (field.references) {\n col = col\n .references(\n getReferencePath(\n field.references.model,\n field.references.field,\n ),\n )\n .onDelete(field.references.onDelete || \"cascade\");\n }\n\n if (field.unique) col = col.unique();\n\n if (\n field.type === \"date\" &&\n typeof field.defaultValue === \"function\" &&\n (dbType === \"postgres\" || dbType === \"mysql\" || dbType === \"mssql\")\n ) {\n col =\n dbType === \"mysql\"\n ? col.defaultTo(sql`CURRENT_TIMESTAMP(3)`)\n : col.defaultTo(sql`CURRENT_TIMESTAMP`);\n }\n\n return col;\n });\n\n if (field.index) {\n const idx = db.schema\n .createIndex(\n `${table.table}_${fieldName}_${field.unique ? \"uidx\" : \"idx\"}`,\n )\n .on(table.table)\n .columns([fieldName]);\n\n toBeIndexed.push(field.unique ? idx.unique() : idx);\n }\n }\n\n migrations.push(dbT);\n }\n }\n\n for (const index of toBeIndexed) {\n migrations.push(index);\n }\n\n async function runMigrations() {\n for (const migration of migrations) {\n await migration.execute();\n }\n }\n\n async function compileMigrations() {\n const compiled = migrations.map((m) => m.compile().sql);\n return compiled.join(\";\\n\\n\") + \";\";\n }\n\n return { toBeCreated, toBeAdded, runMigrations, compileMigrations };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/db/migrations/index.ts"],"sourcesContent":["import type { PecuniaOptions } from \"pecunia-core\";\nimport type { DBFieldAttribute, DBFieldType } from \"pecunia-core\";\nimport { initGetFieldName, initGetModelName } from \"pecunia-core\";\nimport type {\n AlterTableBuilder,\n AlterTableColumnAlteringBuilder,\n ColumnDataType,\n CreateIndexBuilder,\n CreateTableBuilder,\n Kysely,\n RawBuilder,\n} from \"kysely\";\nimport { sql } from \"kysely\";\nimport { createKyselyAdapter } from \"../../adapters/kysely-adapter/dialect\";\nimport type { KyselyDatabaseDialectType } from \"pecunia-core\";\nimport { getSchema } from \"../schema/get-schema\";\nimport { getPaymentTables } from \"pecunia-core\";\n\ntype DbTypeBuckets = Record<\n \"string\" | \"number\" | \"boolean\" | \"date\" | \"json\",\n string[]\n>;\n\nconst postgresMap = {\n string: [\"character varying\", \"varchar\", \"text\", \"uuid\"],\n number: [\n \"int4\",\n \"integer\",\n \"bigint\",\n \"smallint\",\n \"numeric\",\n \"real\",\n \"double precision\",\n ],\n boolean: [\"bool\", \"boolean\"],\n date: [\"timestamptz\", \"timestamp\", \"date\"],\n json: [\"json\", \"jsonb\"],\n};\n\nconst mysqlMap = {\n string: [\"varchar\", \"text\", \"uuid\"],\n number: [\n \"integer\",\n \"int\",\n \"bigint\",\n \"smallint\",\n \"decimal\",\n \"float\",\n \"double\",\n ],\n boolean: [\"boolean\", \"tinyint\"],\n date: [\"timestamp\", \"datetime\", \"date\"],\n json: [\"json\"],\n};\n\nconst sqliteMap = {\n string: [\"TEXT\"],\n number: [\"INTEGER\", \"REAL\"],\n boolean: [\"INTEGER\", \"BOOLEAN\"],\n date: [\"DATE\", \"INTEGER\"],\n json: [\"TEXT\"],\n};\n\nconst mssqlMap = {\n string: [\"varchar\", \"nvarchar\", \"uniqueidentifier\"],\n number: [\"int\", \"bigint\", \"smallint\", \"decimal\", \"float\", \"double\"],\n boolean: [\"bit\", \"smallint\"],\n date: [\"datetime2\", \"date\", \"datetime\"],\n json: [\"varchar\", \"nvarchar\"],\n};\n\nconst map = {\n postgres: postgresMap,\n mysql: mysqlMap,\n sqlite: sqliteMap,\n mssql: mssqlMap,\n};\n\nexport function matchType(\n columnDataType: string,\n fieldType: DBFieldType,\n dbType: KyselyDatabaseDialectType,\n) {\n function normalize(type: string) {\n return type.toLowerCase().split(\"(\")[0]!.trim();\n }\n\n if (fieldType === \"string[]\" || fieldType === \"number[]\") {\n const normalized = columnDataType.toLowerCase();\n if (normalized.includes(\"[]\")) return true;\n return normalized.includes(\"json\");\n }\n\n const types: Partial<DbTypeBuckets> = map[dbType]!;\n const expected = Array.isArray(fieldType)\n ? types.string?.map((t) => t.toLowerCase())\n : types[fieldType]!.map((t: string) => t.toLowerCase());\n\n return expected?.includes(normalize(columnDataType));\n}\n\nasync function getPostgresSchema(db: Kysely<unknown>): Promise<string> {\n try {\n const result = await sql<{ search_path: string }>`SHOW search_path`.execute(\n db,\n );\n\n if (result.rows[0]?.search_path) {\n const schemas = result.rows[0].search_path\n .split(\",\")\n .map((s) => s.trim())\n .map((s) => s.replace(/^[\"']|[\"']$/g, \"\"))\n .filter((s) => !s.startsWith(\"$\"));\n\n return schemas[0] || \"public\";\n }\n } catch {\n // fall back\n }\n\n return \"public\";\n}\n\nexport async function getMigrations(config: PecuniaOptions) {\n const billingEngineSchema = getSchema(config);\n\n let { kysely: db, databaseType: dbType } = await createKyselyAdapter(config);\n\n if (!dbType) {\n console.warn(\n \"Could not determine database type, defaulting to sqlite. Please provide a type in the database options to avoid this.\",\n );\n dbType = \"sqlite\";\n }\n\n if (!db) {\n console.error(\n \"Only kysely adapter is supported for migrations. You can use `generate` command to generate the schema, if you're using a different adapter.\",\n );\n process.exit(1);\n }\n\n let currentSchema = \"public\";\n if (dbType === \"postgres\") {\n currentSchema = await getPostgresSchema(db);\n console.debug(\n `PostgreSQL migration: Using schema '${currentSchema}' (from search_path)`,\n );\n }\n\n const allTableMetadata = await db.introspection.getTables();\n\n let tableMetadata = allTableMetadata;\n if (dbType === \"postgres\") {\n try {\n const tablesInSchema = await sql<{ table_name: string }>`\n SELECT table_name\n FROM information_schema.tables\n WHERE table_schema = ${currentSchema}\n AND table_type = 'BASE TABLE'\n `.execute(db);\n\n const tableNamesInSchema = new Set(\n tablesInSchema.rows.map((row) => row.table_name),\n );\n\n tableMetadata = allTableMetadata.filter(\n (table) =>\n table.schema === currentSchema && tableNamesInSchema.has(table.name),\n );\n } catch (error) {\n console.warn(\n `Could not filter tables by schema. Using all discovered tables. Error: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n }\n }\n\n const toBeCreated: {\n table: string;\n fields: Record<string, DBFieldAttribute>;\n order: number;\n }[] = [];\n\n const toBeAdded: {\n table: string;\n fields: Record<string, DBFieldAttribute>;\n order: number;\n }[] = [];\n\n for (const [key, value] of Object.entries(billingEngineSchema)) {\n const table = tableMetadata.find((t) => t.name === key);\n\n if (!table) {\n const existing = toBeCreated.findIndex((t) => t.table === key);\n const tableData = {\n table: key,\n fields: value.fields,\n order: value.order || Infinity,\n };\n\n const insertIndex = toBeCreated.findIndex(\n (t) => (t.order || Infinity) > tableData.order,\n );\n\n if (insertIndex === -1) {\n if (existing === -1) toBeCreated.push(tableData);\n else {\n toBeCreated[existing]!.fields = {\n ...toBeCreated[existing]!.fields,\n ...value.fields,\n };\n }\n } else {\n toBeCreated.splice(insertIndex, 0, tableData);\n }\n\n continue;\n }\n\n const toBeAddedFields: Record<string, DBFieldAttribute> = {};\n\n for (const [fieldName, field] of Object.entries(value.fields)) {\n const column = table.columns.find((c) => c.name === fieldName);\n\n if (!column) {\n toBeAddedFields[fieldName] = field;\n continue;\n }\n\n if (matchType(column.dataType, field.type, dbType)) continue;\n\n console.warn(\n `Field ${fieldName} in table ${key} has a different type in the database. Expected ${field.type} but got ${column.dataType}.`,\n );\n }\n\n if (Object.keys(toBeAddedFields).length > 0) {\n toBeAdded.push({\n table: key,\n fields: toBeAddedFields,\n order: value.order || Infinity,\n });\n }\n }\n\n const migrations: (\n | AlterTableColumnAlteringBuilder\n | ReturnType<AlterTableBuilder[\"addIndex\"]>\n | CreateTableBuilder<string, string>\n | CreateIndexBuilder\n )[] = [];\n\n const useUUIDs = true;\n\n function getType(field: DBFieldAttribute, fieldName: string) {\n const type = field.type;\n const provider = dbType || \"sqlite\";\n\n type StringOnlyUnion<T> = T extends string ? T : never;\n\n const typeMap: Record<\n StringOnlyUnion<DBFieldType> | \"id\" | \"foreignKeyId\",\n Record<KyselyDatabaseDialectType, ColumnDataType | RawBuilder<unknown>>\n > = {\n string: {\n sqlite: \"text\",\n postgres: \"text\",\n mysql: field.unique\n ? \"varchar(255)\"\n : field.references\n ? \"varchar(36)\"\n : field.sortable\n ? \"varchar(255)\"\n : field.index\n ? \"varchar(255)\"\n : \"text\",\n mssql:\n field.unique || field.sortable\n ? \"varchar(255)\"\n : field.references\n ? \"varchar(36)\"\n : \"varchar(8000)\",\n },\n boolean: {\n sqlite: \"integer\",\n postgres: \"boolean\",\n mysql: \"boolean\",\n mssql: \"smallint\",\n },\n number: {\n sqlite: field.bigint ? \"bigint\" : \"integer\",\n postgres: field.bigint ? \"bigint\" : \"integer\",\n mysql: field.bigint ? \"bigint\" : \"integer\",\n mssql: field.bigint ? \"bigint\" : \"integer\",\n },\n date: {\n sqlite: \"date\",\n postgres: \"timestamptz\",\n mysql: \"timestamp(3)\",\n mssql: sql`datetime2(3)`,\n },\n json: {\n sqlite: \"text\",\n postgres: \"jsonb\",\n mysql: \"json\",\n mssql: \"varchar(8000)\",\n },\n id: {\n postgres: useUUIDs ? \"uuid\" : \"text\",\n mysql: \"varchar(36)\",\n mssql: \"varchar(36)\",\n sqlite: \"text\",\n },\n foreignKeyId: {\n postgres: useUUIDs ? \"uuid\" : \"text\",\n mysql: \"varchar(36)\",\n mssql: \"varchar(36)\",\n sqlite: \"text\",\n },\n \"string[]\": {\n sqlite: \"text\",\n postgres: sql`text[]`,\n mysql: \"json\",\n mssql: \"varchar(8000)\",\n },\n \"number[]\": {\n sqlite: \"text\",\n postgres: sql`integer[]`,\n mysql: \"json\",\n mssql: \"varchar(8000)\",\n },\n } as const;\n\n if (fieldName === \"id\" || field.references?.field === \"id\") {\n if (fieldName === \"id\") return typeMap.id[provider];\n return typeMap.foreignKeyId[provider];\n }\n\n if (Array.isArray(type)) return \"text\";\n if (!(type in typeMap)) {\n throw new Error(\n `Unsupported field type '${String(type)}' for field '${fieldName}'.`,\n );\n }\n\n return typeMap[type][provider];\n }\n\n const getModelName = initGetModelName({\n schema: getPaymentTables(config),\n usePlural: false,\n });\n\n const getFieldName = initGetFieldName({\n schema: getPaymentTables(config),\n usePlural: false,\n });\n\n function getReferencePath(model: string, field: string): string {\n try {\n const modelName = getModelName(model);\n const fieldName = getFieldName({ model, field });\n return `${modelName}.${fieldName}`;\n } catch {\n return `${model}.${field}`;\n }\n }\n\n const applyColumnOptions = (\n field: DBFieldAttribute,\n col: any, // Kysely column builder type is gnarly; keep minimal changes\n ) => {\n // Align with Drizzle generator: only NOT NULL if explicitly required: true\n col = field.required === true ? col.notNull() : col;\n\n if (field.references) {\n col = col\n .references(\n getReferencePath(field.references.model, field.references.field),\n )\n // Safer default: don't cascade unless explicitly requested\n .onDelete(field.references.onDelete || \"no action\");\n }\n\n if (field.unique) col = col.unique();\n\n if (\n field.type === \"date\" &&\n typeof field.defaultValue === \"function\" &&\n (dbType === \"postgres\" || dbType === \"mysql\" || dbType === \"mssql\")\n ) {\n col =\n dbType === \"mysql\"\n ? col.defaultTo(sql`CURRENT_TIMESTAMP(3)`)\n : col.defaultTo(sql`CURRENT_TIMESTAMP`);\n }\n\n return col;\n };\n\n if (toBeAdded.length) {\n for (const table of toBeAdded) {\n for (const [fieldName, field] of Object.entries(table.fields)) {\n const type = getType(field, fieldName);\n\n if (field.index) {\n migrations.push(\n db.schema\n .alterTable(table.table)\n .addIndex(`${table.table}_${fieldName}_idx`),\n );\n }\n\n migrations.push(\n db.schema\n .alterTable(table.table)\n .addColumn(fieldName, type, (col) => applyColumnOptions(field, col)),\n );\n }\n }\n }\n\n const toBeIndexed: CreateIndexBuilder[] = [];\n\n if (toBeCreated.length) {\n for (const table of toBeCreated) {\n const idType = getType({ type: \"string\" }, \"id\");\n\n let dbT = db.schema\n .createTable(table.table)\n .addColumn(\"id\", idType, (col) => {\n if (dbType === \"postgres\") {\n return col\n .primaryKey()\n .defaultTo(sql`pg_catalog.gen_random_uuid()`)\n .notNull();\n }\n return col.primaryKey().notNull();\n });\n\n for (const [fieldName, field] of Object.entries(table.fields)) {\n const type = getType(field, fieldName);\n\n dbT = dbT.addColumn(fieldName, type, (col) =>\n applyColumnOptions(field, col),\n );\n\n if (field.index) {\n const idx = db.schema\n .createIndex(\n `${table.table}_${fieldName}_${field.unique ? \"uidx\" : \"idx\"}`,\n )\n .on(table.table)\n .columns([fieldName]);\n\n toBeIndexed.push(field.unique ? idx.unique() : idx);\n }\n }\n\n migrations.push(dbT);\n }\n }\n\n for (const index of toBeIndexed) migrations.push(index);\n\n async function runMigrations() {\n for (const migration of migrations) {\n await migration.execute();\n }\n }\n\n async function compileMigrations() {\n // Debug SQL string only. For exact reproduction across dialects, include parameters too.\n const compiled = migrations.map((m) => m.compile().sql);\n return compiled.join(\";\\n\\n\") + \";\";\n }\n\n return { toBeCreated, toBeAdded, runMigrations, compileMigrations };\n}"],"mappings":"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"}
|