pecunia-root 0.2.1 → 0.2.3

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,7 +86,11 @@ 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[]") return columnDataType.toLowerCase().includes("json");
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
  }
@@ -226,13 +230,13 @@ async function getMigrations(config) {
226
230
  },
227
231
  "string[]": {
228
232
  sqlite: "text",
229
- postgres: "jsonb",
233
+ postgres: sql`text[]`,
230
234
  mysql: "json",
231
235
  mssql: "varchar(8000)"
232
236
  },
233
237
  "number[]": {
234
238
  sqlite: "text",
235
- postgres: "jsonb",
239
+ postgres: sql`integer[]`,
236
240
  mysql: "json",
237
241
  mssql: "varchar(8000)"
238
242
  }
@@ -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\"], // 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 const normalized = columnDataType.toLowerCase();\n // PostgreSQL arrays: text[], integer[], etc.\n if (normalized.includes(\"[]\")) return true;\n // Fallback to JSON for other databases\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\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: 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\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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pecunia-root",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -95,7 +95,7 @@
95
95
  "jose": "^6.1.0",
96
96
  "kysely": "^0.28.5",
97
97
  "nanostores": "^1.0.1",
98
- "pecunia-core": "^0.1.2",
98
+ "pecunia-core": "^0.1.3",
99
99
  "zod": "^4.1.12"
100
100
  },
101
101
  "devDependencies": {