@fragno-dev/db 0.1.0 → 0.1.2
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/.turbo/turbo-build.log +63 -55
- package/CHANGELOG.md +18 -0
- package/dist/adapters/adapters.d.ts +11 -1
- package/dist/adapters/adapters.d.ts.map +1 -1
- package/dist/adapters/drizzle/drizzle-adapter.d.ts +9 -2
- package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +1 -1
- package/dist/adapters/drizzle/drizzle-adapter.js +21 -39
- package/dist/adapters/drizzle/drizzle-adapter.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-query.d.ts.map +1 -1
- package/dist/adapters/drizzle/drizzle-query.js +3 -2
- package/dist/adapters/drizzle/drizzle-query.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-uow-compiler.js +7 -5
- package/dist/adapters/drizzle/drizzle-uow-compiler.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-uow-decoder.js.map +1 -1
- package/dist/adapters/drizzle/drizzle-uow-executor.js.map +1 -1
- package/dist/adapters/drizzle/generate.js +107 -34
- package/dist/adapters/drizzle/generate.js.map +1 -1
- package/dist/adapters/drizzle/shared.js +14 -1
- package/dist/adapters/drizzle/shared.js.map +1 -1
- package/dist/adapters/kysely/kysely-adapter.d.ts +2 -1
- package/dist/adapters/kysely/kysely-adapter.d.ts.map +1 -1
- package/dist/adapters/kysely/kysely-adapter.js +25 -30
- package/dist/adapters/kysely/kysely-adapter.js.map +1 -1
- package/dist/adapters/kysely/kysely-query-builder.js +48 -44
- package/dist/adapters/kysely/kysely-query-builder.js.map +1 -1
- package/dist/adapters/kysely/kysely-query-compiler.js +2 -2
- package/dist/adapters/kysely/kysely-query-compiler.js.map +1 -1
- package/dist/adapters/kysely/kysely-query.js +3 -2
- package/dist/adapters/kysely/kysely-query.js.map +1 -1
- package/dist/adapters/kysely/kysely-shared.js +18 -0
- package/dist/adapters/kysely/kysely-shared.js.map +1 -0
- package/dist/adapters/kysely/kysely-uow-compiler.js +4 -3
- package/dist/adapters/kysely/kysely-uow-compiler.js.map +1 -1
- package/dist/adapters/kysely/migration/execute.js +15 -12
- package/dist/adapters/kysely/migration/execute.js.map +1 -1
- package/dist/migration-engine/auto-from-schema.js +2 -8
- package/dist/migration-engine/auto-from-schema.js.map +1 -1
- package/dist/migration-engine/create.d.ts +1 -5
- package/dist/migration-engine/create.js +1 -1
- package/dist/migration-engine/create.js.map +1 -1
- package/dist/migration-engine/generation-engine.d.ts +51 -0
- package/dist/migration-engine/generation-engine.d.ts.map +1 -0
- package/dist/migration-engine/generation-engine.js +165 -0
- package/dist/migration-engine/generation-engine.js.map +1 -0
- package/dist/migration-engine/shared.d.ts +5 -2
- package/dist/migration-engine/shared.d.ts.map +1 -1
- package/dist/migration-engine/shared.js.map +1 -1
- package/dist/mod.d.ts +0 -8
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +0 -32
- package/dist/mod.js.map +1 -1
- package/dist/query/condition-builder.js.map +1 -1
- package/dist/query/query.d.ts +2 -2
- package/dist/query/query.d.ts.map +1 -1
- package/dist/query/result-transform.js +2 -1
- package/dist/query/result-transform.js.map +1 -1
- package/dist/schema/create.d.ts +74 -16
- package/dist/schema/create.d.ts.map +1 -1
- package/dist/schema/create.js +76 -11
- package/dist/schema/create.js.map +1 -1
- package/dist/schema/serialize.js.map +1 -1
- package/dist/shared/settings-schema.js +36 -0
- package/dist/shared/settings-schema.js.map +1 -0
- package/dist/util/import-generator.js.map +1 -1
- package/dist/util/parse.js.map +1 -1
- package/package.json +10 -6
- package/src/adapters/adapters.ts +10 -3
- package/src/adapters/drizzle/drizzle-adapter-pglite.test.ts +11 -7
- package/src/adapters/drizzle/drizzle-adapter.test.ts +77 -29
- package/src/adapters/drizzle/drizzle-adapter.ts +31 -78
- package/src/adapters/drizzle/drizzle-query.ts +4 -7
- package/src/adapters/drizzle/drizzle-uow-compiler.test.ts +10 -4
- package/src/adapters/drizzle/drizzle-uow-compiler.ts +12 -6
- package/src/adapters/drizzle/drizzle-uow-decoder.ts +1 -1
- package/src/adapters/drizzle/drizzle-uow-executor.ts +1 -1
- package/src/adapters/drizzle/generate.test.ts +573 -150
- package/src/adapters/drizzle/generate.ts +187 -36
- package/src/adapters/drizzle/migrate-drizzle.test.ts +30 -6
- package/src/adapters/drizzle/shared.ts +31 -1
- package/src/adapters/drizzle/test-utils.ts +3 -1
- package/src/adapters/kysely/kysely-adapter-pglite.test.ts +25 -27
- package/src/adapters/kysely/kysely-adapter.ts +35 -58
- package/src/adapters/kysely/kysely-query-builder.ts +75 -44
- package/src/adapters/kysely/kysely-query-compiler.ts +3 -1
- package/src/adapters/kysely/kysely-query.ts +9 -3
- package/src/adapters/kysely/kysely-shared.ts +23 -0
- package/src/adapters/kysely/kysely-uow-compiler.ts +5 -2
- package/src/adapters/kysely/migration/execute-mysql.test.ts +2 -2
- package/src/adapters/kysely/migration/execute-postgres.test.ts +19 -19
- package/src/adapters/kysely/migration/execute.ts +48 -17
- package/src/adapters/kysely/migration/kysely-migrator.test.ts +19 -37
- package/src/fragment.test.ts +48 -1
- package/src/migration-engine/auto-from-schema.ts +14 -18
- package/src/migration-engine/create.ts +1 -6
- package/src/migration-engine/generation-engine.test.ts +597 -0
- package/src/migration-engine/generation-engine.ts +356 -0
- package/src/migration-engine/shared.ts +1 -4
- package/src/mod.ts +0 -66
- package/src/query/condition-builder.ts +24 -8
- package/src/query/query.ts +4 -2
- package/src/query/result-transform.ts +7 -1
- package/src/schema/create.test.ts +4 -1
- package/src/schema/create.ts +132 -24
- package/src/schema/serialize.ts +21 -7
- package/src/shared/settings-schema.ts +61 -0
- package/src/util/deep-equal.ts +21 -7
- package/src/util/import-generator.ts +3 -1
- package/src/util/parse.ts +3 -1
- package/tsdown.config.ts +1 -0
- package/.turbo/turbo-test.log +0 -36
- package/.turbo/turbo-types$colon$check.log +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drizzle-uow-compiler.js","names":["Drizzle.eq","Drizzle.ne","Drizzle.gt","Drizzle.gte","Drizzle.lt","Drizzle.lte","Drizzle.inArray","Drizzle.notInArray","Drizzle.isNull","Drizzle.isNotNull","Drizzle.like","Drizzle.notLike","Drizzle.and","Drizzle.not","Drizzle.or","processed: Record<string, unknown>","result: Record<string, Drizzle.DBQueryConfig<\"many\", boolean>>","joinColumns: Record<string, boolean>","joinOrderBy: Drizzle.SQL[] | undefined","Drizzle.asc","Drizzle.desc","joinWhere: Drizzle.SQL | undefined","joinConfig: Drizzle.DBQueryConfig<\"many\", boolean>","whereClause: Drizzle.SQL | undefined","Drizzle.count","indexColumns: AnyColumn[]","orderDirection: \"asc\" | \"desc\"","orderBy: Drizzle.SQL[] | undefined","columns: Record<string, boolean>","whereClauses: Drizzle.SQL[]","queryConfig: Drizzle.DBQueryConfig<\"many\", boolean>"],"sources":["../../../src/adapters/drizzle/drizzle-uow-compiler.ts"],"sourcesContent":["import * as Drizzle from \"drizzle-orm\";\nimport type { AnyColumn, AnySchema, AnyTable, FragnoId } from \"../../schema/create\";\nimport { Column } from \"../../schema/create\";\nimport type {\n CompiledMutation,\n MutationOperation,\n RetrievalOperation,\n UOWCompiler,\n} from \"../../query/unit-of-work\";\nimport { buildCondition, type Condition } from \"../../query/condition-builder\";\nimport type { DrizzleConfig } from \"./drizzle-adapter\";\nimport { type ColumnType, type TableType, parseDrizzle } from \"./shared\";\nimport { encodeValues, ReferenceSubquery } from \"../../query/result-transform\";\nimport { serialize } from \"../../schema/serialize\";\nimport { decodeCursor, serializeCursorValues } from \"../../query/cursor\";\nimport type { CompiledJoin } from \"../../query/orm/orm\";\nimport { getOrderedJoinColumns } from \"./join-column-utils\";\n\nexport type DrizzleCompiledQuery = {\n sql: string;\n params: unknown[];\n};\n\n/**\n * Create a Drizzle-specific Unit of Work compiler\n *\n * This compiler translates UOW operations into Drizzle query functions\n * that can be executed as a batch/transaction.\n *\n * @param schema - The database schema\n * @param config - Drizzle configuration\n * @param onQuery - Optional callback to receive compiled queries for logging/debugging\n * @returns A UOWCompiler instance for Drizzle\n */\nexport function createDrizzleUOWCompiler<TSchema extends AnySchema>(\n schema: TSchema,\n config: DrizzleConfig,\n onQuery?: (query: DrizzleCompiledQuery) => void,\n): UOWCompiler<TSchema, DrizzleCompiledQuery> {\n const [db, drizzleTables] = parseDrizzle(config.db);\n const { provider } = config;\n\n /**\n * Convert a Fragno table to a Drizzle table\n * @throws Error if table is not found in Drizzle schema\n */\n function toDrizzleTable(table: AnyTable): TableType {\n const tableName = table.ormName;\n const out = drizzleTables[tableName];\n if (out) {\n return out;\n }\n\n throw new Error(\n `[Drizzle] Unknown table name ${tableName}, is it included in your Drizzle schema?`,\n );\n }\n\n /**\n * Convert a Fragno column to a Drizzle column\n * @throws Error if column is not found in Drizzle table\n */\n function toDrizzleColumn(col: AnyColumn): ColumnType {\n const fragnoTable = schema.tables[col.tableName];\n if (!fragnoTable) {\n throw new Error(`[Drizzle] Unknown table ${col.tableName} for column ${col.ormName}.`);\n }\n\n const table = toDrizzleTable(fragnoTable);\n const out = table[col.ormName];\n if (out) {\n return out;\n }\n\n throw new Error(`[Drizzle] Unknown column name ${col.ormName} in ${fragnoTable.ormName}.`);\n }\n\n /**\n * Build a WHERE clause from a condition using Drizzle's query builder\n */\n function buildWhere(condition: Condition): Drizzle.SQL | undefined {\n if (condition.type === \"compare\") {\n const left = toDrizzleColumn(condition.a);\n const op = condition.operator;\n let right = condition.b;\n if (right instanceof Column) {\n right = toDrizzleColumn(right);\n } else {\n // Serialize non-Column values (e.g., FragnoId -> string, Date -> number for SQLite)\n right = serialize(right, condition.a, provider);\n }\n\n switch (op) {\n case \"=\":\n return Drizzle.eq(left, right);\n case \"!=\":\n return Drizzle.ne(left, right);\n case \">\":\n return Drizzle.gt(left, right);\n case \">=\":\n return Drizzle.gte(left, right);\n case \"<\":\n return Drizzle.lt(left, right);\n case \"<=\":\n return Drizzle.lte(left, right);\n case \"in\": {\n return Drizzle.inArray(left, right as never[]);\n }\n case \"not in\":\n return Drizzle.notInArray(left, right as never[]);\n case \"is\":\n return right === null ? Drizzle.isNull(left) : Drizzle.eq(left, right);\n case \"is not\":\n return right === null ? Drizzle.isNotNull(left) : Drizzle.ne(left, right);\n case \"contains\": {\n right =\n typeof right === \"string\" ? `%${right}%` : Drizzle.sql`concat('%', ${right}, '%')`;\n return Drizzle.like(left, right as string);\n }\n case \"not contains\": {\n right =\n typeof right === \"string\" ? `%${right}%` : Drizzle.sql`concat('%', ${right}, '%')`;\n return Drizzle.notLike(left, right as string);\n }\n case \"ends with\": {\n right = typeof right === \"string\" ? `%${right}` : Drizzle.sql`concat('%', ${right})`;\n return Drizzle.like(left, right as string);\n }\n case \"not ends with\": {\n right = typeof right === \"string\" ? `%${right}` : Drizzle.sql`concat('%', ${right})`;\n return Drizzle.notLike(left, right as string);\n }\n case \"starts with\": {\n right = typeof right === \"string\" ? `${right}%` : Drizzle.sql`concat(${right}, '%')`;\n return Drizzle.like(left, right as string);\n }\n case \"not starts with\": {\n right = typeof right === \"string\" ? `${right}%` : Drizzle.sql`concat(${right}, '%')`;\n return Drizzle.notLike(left, right as string);\n }\n\n default:\n throw new Error(`Unsupported operator: ${op}`);\n }\n }\n\n if (condition.type === \"and\") {\n return Drizzle.and(...condition.items.map((item) => buildWhere(item)));\n }\n\n if (condition.type === \"not\") {\n const result = buildWhere(condition.item);\n if (!result) return;\n\n return Drizzle.not(result);\n }\n\n return Drizzle.or(...condition.items.map((item) => buildWhere(item)));\n }\n\n /**\n * Process reference subqueries in encoded values, converting them to Drizzle SQL subqueries\n */\n function processReferenceSubqueries(values: Record<string, unknown>): Record<string, unknown> {\n const processed: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(values)) {\n if (value instanceof ReferenceSubquery) {\n const refTable = value.referencedTable;\n const externalId = value.externalIdValue;\n const internalIdCol = refTable.getInternalIdColumn();\n const idCol = refTable.getIdColumn();\n const drizzleRefTable = toDrizzleTable(refTable);\n const drizzleIdCol = toDrizzleColumn(idCol);\n\n // Create a parameterized SQL subquery using Drizzle's query builder\n // Safe cast: we're building a SQL subquery that returns a single bigint value\n processed[key] = db\n .select({ value: drizzleRefTable[internalIdCol.ormName] })\n .from(drizzleRefTable)\n .where(Drizzle.eq(drizzleIdCol, externalId))\n .limit(1) as unknown;\n } else {\n processed[key] = value;\n }\n }\n\n return processed;\n }\n\n /**\n * Get table from schema by name\n * @throws Error if table is not found in schema\n */\n function getTable(name: unknown): AnyTable {\n const table = schema.tables[name as string];\n if (!table) {\n throw new Error(`Invalid table name ${name}.`);\n }\n return table;\n }\n\n /**\n * Get the version to check for a given ID and checkVersion flag.\n * @returns The version to check or undefined if no check is required.\n * @throws Error if the ID is a string and checkVersion is true.\n */\n function getVersionToCheck(id: FragnoId | string, checkVersion: boolean): number | undefined {\n if (!checkVersion) {\n return undefined;\n }\n\n if (typeof id === \"string\") {\n throw new Error(\n `Cannot use checkVersion with a string ID. Version checking requires a FragnoId with version information.`,\n );\n }\n\n return id.version;\n }\n\n /**\n * Process joins recursively to support nested joins with orderBy and limit\n */\n function processJoins(\n joins: CompiledJoin[],\n ): Record<string, Drizzle.DBQueryConfig<\"many\", boolean>> {\n const result: Record<string, Drizzle.DBQueryConfig<\"many\", boolean>> = {};\n\n for (const join of joins) {\n const { options, relation } = join;\n\n if (!options) {\n continue;\n }\n\n const targetTable = relation.table;\n const joinName = relation.name;\n\n // Build columns for this join using shared utility\n const selectOption = options.select === undefined ? true : options.select;\n const orderedColumns = getOrderedJoinColumns(targetTable, selectOption);\n const joinColumns: Record<string, boolean> = {};\n for (const colName of orderedColumns) {\n joinColumns[colName] = true;\n }\n\n // Build orderBy for this join\n let joinOrderBy: Drizzle.SQL[] | undefined;\n if (options.orderBy && options.orderBy.length > 0) {\n joinOrderBy = options.orderBy.map(([col, direction]) => {\n const drizzleCol = toDrizzleColumn(col);\n return direction === \"asc\" ? Drizzle.asc(drizzleCol) : Drizzle.desc(drizzleCol);\n });\n }\n\n // Build WHERE clause for this join if provided\n let joinWhere: Drizzle.SQL | undefined;\n if (options.where) {\n joinWhere = buildWhere(options.where);\n }\n\n // Build the join config\n const joinConfig: Drizzle.DBQueryConfig<\"many\", boolean> = {\n columns: joinColumns,\n orderBy: joinOrderBy,\n limit: options.limit,\n where: joinWhere,\n };\n\n // Recursively process nested joins\n if (options.join && options.join.length > 0) {\n joinConfig.with = processJoins(options.join);\n }\n\n result[joinName] = joinConfig;\n }\n\n return result;\n }\n\n return {\n compileRetrievalOperation(op: RetrievalOperation<TSchema>): DrizzleCompiledQuery | null {\n switch (op.type) {\n case \"count\": {\n // Build WHERE clause\n let whereClause: Drizzle.SQL | undefined;\n if (op.options.where) {\n const condition = buildCondition(op.table.columns, op.options.where);\n if (condition === false) {\n // Never matches - return null\n return null;\n }\n if (condition !== true) {\n whereClause = buildWhere(condition);\n }\n }\n\n const drizzleTable = toDrizzleTable(op.table);\n const query = db.select({ count: Drizzle.count() }).from(drizzleTable);\n\n const compiledQuery = whereClause ? query.where(whereClause).toSQL() : query.toSQL();\n onQuery?.(compiledQuery);\n return compiledQuery;\n }\n\n case \"find\": {\n const {\n useIndex: _useIndex,\n orderByIndex,\n joins,\n after,\n before,\n pageSize,\n ...findOptions\n } = op.options;\n\n // Get index columns for ordering and cursor pagination\n let indexColumns: AnyColumn[] = [];\n let orderDirection: \"asc\" | \"desc\" = \"asc\";\n\n if (orderByIndex) {\n const index = op.table.indexes[orderByIndex.indexName];\n orderDirection = orderByIndex.direction;\n\n if (!index) {\n // If _primary index doesn't exist, fall back to ID column\n if (orderByIndex.indexName === \"_primary\") {\n indexColumns = [op.table.getIdColumn()];\n } else {\n throw new Error(\n `Index \"${orderByIndex.indexName}\" not found on table \"${op.table.name}\"`,\n );\n }\n } else {\n indexColumns = index.columns;\n }\n }\n\n // Convert orderByIndex to orderBy format\n let orderBy: Drizzle.SQL[] | undefined;\n if (indexColumns.length > 0) {\n orderBy = indexColumns.map((col) => {\n const drizzleCol = toDrizzleColumn(col);\n return orderDirection === \"asc\" ? Drizzle.asc(drizzleCol) : Drizzle.desc(drizzleCol);\n });\n }\n\n // Build query configuration\n const columns: Record<string, boolean> = {};\n const select = findOptions.select;\n\n if (select === true || select === undefined) {\n for (const col of Object.values(op.table.columns)) {\n columns[col.ormName] = true;\n }\n } else {\n for (const k of select) {\n columns[op.table.columns[k].ormName] = true;\n }\n // Always include hidden columns (for FragnoId construction with internal ID and version)\n for (const col of Object.values(op.table.columns)) {\n if (col.isHidden && !columns[col.ormName]) {\n columns[col.ormName] = true;\n }\n }\n }\n\n // Build WHERE clause with cursor conditions\n const whereClauses: Drizzle.SQL[] = [];\n\n // Add user-defined where clause\n if (findOptions.where) {\n const condition = buildCondition(op.table.columns, findOptions.where);\n if (condition === false) {\n // Never matches - return null to indicate this query should be skipped\n return null;\n }\n if (condition !== true) {\n const clause = buildWhere(condition);\n if (clause) {\n whereClauses.push(clause);\n }\n }\n }\n\n // Add cursor-based pagination conditions\n if ((after || before) && indexColumns.length > 0) {\n const cursor = after || before;\n const cursorData = decodeCursor(cursor!);\n const serializedValues = serializeCursorValues(cursorData, indexColumns, provider);\n\n // Build tuple comparison for cursor pagination\n // For \"after\" with \"asc\": (col1, col2, ...) > (val1, val2, ...)\n // For \"before\" with \"desc\": reverse the comparison\n const isAfter = !!after;\n const useGreaterThan =\n (isAfter && orderDirection === \"asc\") || (!isAfter && orderDirection === \"desc\");\n\n if (indexColumns.length === 1) {\n // Simple single-column case\n const col = toDrizzleColumn(indexColumns[0]!);\n const val = serializedValues[indexColumns[0]!.ormName];\n whereClauses.push(useGreaterThan ? Drizzle.gt(col, val) : Drizzle.lt(col, val));\n } else {\n // Multi-column tuple comparison using SQL\n const drizzleCols = indexColumns.map((c) => toDrizzleColumn(c));\n const vals = indexColumns.map((c) => serializedValues[c.ormName]);\n const operator = useGreaterThan ? \">\" : \"<\";\n // Safe cast: building a SQL comparison expression for cursor pagination\n // Build the tuple comparison: (col1, col2) > (val1, val2)\n const colsSQL = Drizzle.sql.join(drizzleCols, Drizzle.sql.raw(\", \"));\n const valsSQL = Drizzle.sql.join(\n vals.map((v) => Drizzle.sql`${v}`),\n Drizzle.sql.raw(\", \"),\n );\n whereClauses.push(\n Drizzle.sql`(${colsSQL}) ${Drizzle.sql.raw(operator)} (${valsSQL})`,\n );\n }\n }\n\n const whereClause = whereClauses.length > 0 ? Drizzle.and(...whereClauses) : undefined;\n\n const queryConfig: Drizzle.DBQueryConfig<\"many\", boolean> = {\n columns,\n limit: pageSize,\n where: whereClause,\n orderBy,\n with: {},\n };\n\n // Process joins recursively to support nested joins\n if (joins) {\n queryConfig.with = processJoins(joins);\n }\n\n const compiledQuery = db.query[op.table.ormName].findMany(queryConfig).toSQL();\n onQuery?.(compiledQuery);\n return compiledQuery;\n }\n }\n },\n\n compileMutationOperation(\n op: MutationOperation<TSchema>,\n ): CompiledMutation<DrizzleCompiledQuery> | null {\n switch (op.type) {\n case \"create\": {\n const table = getTable(op.table);\n const drizzleTable = toDrizzleTable(table);\n // encodeValues now handles runtime defaults automatically\n const encodedValues = encodeValues(op.values, table, true, provider);\n const values = processReferenceSubqueries(encodedValues);\n\n const compiledQuery = db.insert(drizzleTable).values(values).toSQL();\n onQuery?.(compiledQuery);\n return {\n query: compiledQuery,\n expectedAffectedRows: null, // creates don't need affected row checks\n };\n }\n\n case \"update\": {\n const table = getTable(op.table);\n const idColumn = table.getIdColumn();\n const versionColumn = table.getVersionColumn();\n const drizzleTable = toDrizzleTable(table);\n\n const externalId = typeof op.id === \"string\" ? op.id : op.id.externalId;\n const versionToCheck = getVersionToCheck(op.id, op.checkVersion);\n\n // Build WHERE clause that filters by ID and optionally by version\n const condition =\n versionToCheck !== undefined\n ? buildCondition(table.columns, (eb) =>\n eb.and(\n eb(idColumn.ormName, \"=\", externalId),\n eb(versionColumn.ormName, \"=\", versionToCheck),\n ),\n )\n : buildCondition(table.columns, (eb) => eb(idColumn.ormName, \"=\", externalId));\n\n // Handle boolean cases\n if (condition === false) {\n // Never matches - skip this operation\n return null;\n }\n\n const whereClause = condition === true ? undefined : buildWhere(condition);\n const encodedSetValues = encodeValues(op.set, table, false, provider);\n const setValues = processReferenceSubqueries(encodedSetValues);\n\n // Automatically increment _version for optimistic concurrency control\n // Safe cast: we're building a SQL expression for incrementing the version\n setValues[versionColumn.ormName] = Drizzle.sql.raw(\n `COALESCE(${versionColumn.ormName}, 0) + 1`,\n ) as unknown;\n\n const compiledQuery = db.update(drizzleTable).set(setValues).where(whereClause).toSQL();\n onQuery?.(compiledQuery);\n return {\n query: compiledQuery,\n expectedAffectedRows: op.checkVersion ? 1 : null,\n };\n }\n\n case \"delete\": {\n const table = getTable(op.table);\n const idColumn = table.getIdColumn();\n const versionColumn = table.getVersionColumn();\n const drizzleTable = toDrizzleTable(table);\n\n const externalId = typeof op.id === \"string\" ? op.id : op.id.externalId;\n const versionToCheck = getVersionToCheck(op.id, op.checkVersion);\n\n // Build WHERE clause that filters by ID and optionally by version\n const condition =\n versionToCheck !== undefined\n ? buildCondition(table.columns, (eb) =>\n eb.and(\n eb(idColumn.ormName, \"=\", externalId),\n eb(versionColumn.ormName, \"=\", versionToCheck),\n ),\n )\n : buildCondition(table.columns, (eb) => eb(idColumn.ormName, \"=\", externalId));\n\n // Handle boolean cases\n if (condition === false) {\n // Never matches - skip this operation\n return null;\n }\n\n const whereClause = condition === true ? undefined : buildWhere(condition);\n\n const compiledQuery = db.delete(drizzleTable).where(whereClause).toSQL();\n onQuery?.(compiledQuery);\n return {\n query: compiledQuery,\n expectedAffectedRows: op.checkVersion ? 1 : null,\n };\n }\n }\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"drizzle-uow-compiler.js","names":["Drizzle.eq","Drizzle.ne","Drizzle.gt","Drizzle.gte","Drizzle.lt","Drizzle.lte","Drizzle.inArray","Drizzle.notInArray","Drizzle.isNull","Drizzle.isNotNull","Drizzle.like","Drizzle.notLike","Drizzle.and","Drizzle.not","Drizzle.or","processed: Record<string, unknown>","result: Record<string, Drizzle.DBQueryConfig<\"many\", boolean>>","joinColumns: Record<string, boolean>","joinOrderBy: Drizzle.SQL[] | undefined","Drizzle.asc","Drizzle.desc","joinWhere: Drizzle.SQL | undefined","joinConfig: Drizzle.DBQueryConfig<\"many\", boolean>","whereClause: Drizzle.SQL | undefined","Drizzle.count","indexColumns: AnyColumn[]","orderDirection: \"asc\" | \"desc\"","orderBy: Drizzle.SQL[] | undefined","columns: Record<string, boolean>","whereClauses: Drizzle.SQL[]","queryConfig: Drizzle.DBQueryConfig<\"many\", boolean>"],"sources":["../../../src/adapters/drizzle/drizzle-uow-compiler.ts"],"sourcesContent":["import * as Drizzle from \"drizzle-orm\";\nimport type { AnyColumn, AnySchema, AnyTable, FragnoId } from \"../../schema/create\";\nimport { Column } from \"../../schema/create\";\nimport type {\n CompiledMutation,\n MutationOperation,\n RetrievalOperation,\n UOWCompiler,\n} from \"../../query/unit-of-work\";\nimport { buildCondition, type Condition } from \"../../query/condition-builder\";\nimport type { DrizzleConfig } from \"./drizzle-adapter\";\nimport { type ColumnType, type TableType, type TableNameMapper, parseDrizzle } from \"./shared\";\nimport { encodeValues, ReferenceSubquery } from \"../../query/result-transform\";\nimport { serialize } from \"../../schema/serialize\";\nimport { decodeCursor, serializeCursorValues } from \"../../query/cursor\";\nimport type { CompiledJoin } from \"../../query/orm/orm\";\nimport { getOrderedJoinColumns } from \"./join-column-utils\";\n\nexport type DrizzleCompiledQuery = {\n sql: string;\n params: unknown[];\n};\n\n/**\n * Create a Drizzle-specific Unit of Work compiler\n *\n * This compiler translates UOW operations into Drizzle query functions\n * that can be executed as a batch/transaction.\n *\n * @param schema - The database schema\n * @param config - Drizzle configuration\n * @param mapper - Optional table name mapper for namespace prefixing\n * @param onQuery - Optional callback to receive compiled queries for logging/debugging\n * @returns A UOWCompiler instance for Drizzle\n */\nexport function createDrizzleUOWCompiler<TSchema extends AnySchema>(\n schema: TSchema,\n config: DrizzleConfig,\n mapper?: TableNameMapper,\n onQuery?: (query: DrizzleCompiledQuery) => void,\n): UOWCompiler<TSchema, DrizzleCompiledQuery> {\n const [db, drizzleTables] = parseDrizzle(config.db);\n const { provider } = config;\n\n /**\n * Convert a Fragno table to a Drizzle table\n * @throws Error if table is not found in Drizzle schema\n */\n function toDrizzleTable(table: AnyTable): TableType {\n // Map logical table name to physical table name using the mapper\n const physicalTableName = mapper ? mapper.toPhysical(table.ormName) : table.ormName;\n const out = drizzleTables[physicalTableName];\n if (out) {\n return out;\n }\n\n throw new Error(\n `[Drizzle] Unknown table name ${physicalTableName} (logical: ${table.ormName}), is it included in your Drizzle schema?`,\n );\n }\n\n /**\n * Convert a Fragno column to a Drizzle column\n * @throws Error if column is not found in Drizzle table\n */\n function toDrizzleColumn(col: AnyColumn): ColumnType {\n const fragnoTable = schema.tables[col.tableName];\n if (!fragnoTable) {\n throw new Error(`[Drizzle] Unknown table ${col.tableName} for column ${col.ormName}.`);\n }\n\n const table = toDrizzleTable(fragnoTable);\n const out = table[col.ormName];\n if (out) {\n return out;\n }\n\n throw new Error(`[Drizzle] Unknown column name ${col.ormName} in ${fragnoTable.ormName}.`);\n }\n\n /**\n * Build a WHERE clause from a condition using Drizzle's query builder\n */\n function buildWhere(condition: Condition): Drizzle.SQL | undefined {\n if (condition.type === \"compare\") {\n const left = toDrizzleColumn(condition.a);\n const op = condition.operator;\n let right = condition.b;\n if (right instanceof Column) {\n right = toDrizzleColumn(right);\n } else {\n // Serialize non-Column values (e.g., FragnoId -> string, Date -> number for SQLite)\n right = serialize(right, condition.a, provider);\n }\n\n switch (op) {\n case \"=\":\n return Drizzle.eq(left, right);\n case \"!=\":\n return Drizzle.ne(left, right);\n case \">\":\n return Drizzle.gt(left, right);\n case \">=\":\n return Drizzle.gte(left, right);\n case \"<\":\n return Drizzle.lt(left, right);\n case \"<=\":\n return Drizzle.lte(left, right);\n case \"in\": {\n return Drizzle.inArray(left, right as never[]);\n }\n case \"not in\":\n return Drizzle.notInArray(left, right as never[]);\n case \"is\":\n return right === null ? Drizzle.isNull(left) : Drizzle.eq(left, right);\n case \"is not\":\n return right === null ? Drizzle.isNotNull(left) : Drizzle.ne(left, right);\n case \"contains\": {\n right =\n typeof right === \"string\" ? `%${right}%` : Drizzle.sql`concat('%', ${right}, '%')`;\n return Drizzle.like(left, right as string);\n }\n case \"not contains\": {\n right =\n typeof right === \"string\" ? `%${right}%` : Drizzle.sql`concat('%', ${right}, '%')`;\n return Drizzle.notLike(left, right as string);\n }\n case \"ends with\": {\n right = typeof right === \"string\" ? `%${right}` : Drizzle.sql`concat('%', ${right})`;\n return Drizzle.like(left, right as string);\n }\n case \"not ends with\": {\n right = typeof right === \"string\" ? `%${right}` : Drizzle.sql`concat('%', ${right})`;\n return Drizzle.notLike(left, right as string);\n }\n case \"starts with\": {\n right = typeof right === \"string\" ? `${right}%` : Drizzle.sql`concat(${right}, '%')`;\n return Drizzle.like(left, right as string);\n }\n case \"not starts with\": {\n right = typeof right === \"string\" ? `${right}%` : Drizzle.sql`concat(${right}, '%')`;\n return Drizzle.notLike(left, right as string);\n }\n\n default:\n throw new Error(`Unsupported operator: ${op}`);\n }\n }\n\n if (condition.type === \"and\") {\n return Drizzle.and(...condition.items.map((item) => buildWhere(item)));\n }\n\n if (condition.type === \"not\") {\n const result = buildWhere(condition.item);\n if (!result) {\n return;\n }\n\n return Drizzle.not(result);\n }\n\n return Drizzle.or(...condition.items.map((item) => buildWhere(item)));\n }\n\n /**\n * Process reference subqueries in encoded values, converting them to Drizzle SQL subqueries\n */\n function processReferenceSubqueries(values: Record<string, unknown>): Record<string, unknown> {\n const processed: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(values)) {\n if (value instanceof ReferenceSubquery) {\n const refTable = value.referencedTable;\n const externalId = value.externalIdValue;\n const internalIdCol = refTable.getInternalIdColumn();\n const idCol = refTable.getIdColumn();\n const drizzleRefTable = toDrizzleTable(refTable);\n const drizzleIdCol = toDrizzleColumn(idCol);\n\n // Create a parameterized SQL subquery using Drizzle's query builder\n // Safe cast: we're building a SQL subquery that returns a single bigint value\n processed[key] = db\n .select({ value: drizzleRefTable[internalIdCol.ormName] })\n .from(drizzleRefTable)\n .where(Drizzle.eq(drizzleIdCol, externalId))\n .limit(1) as unknown;\n } else {\n processed[key] = value;\n }\n }\n\n return processed;\n }\n\n /**\n * Get table from schema by name\n * @throws Error if table is not found in schema\n */\n function getTable(name: unknown): AnyTable {\n const table = schema.tables[name as string];\n if (!table) {\n throw new Error(`Invalid table name ${name}.`);\n }\n return table;\n }\n\n /**\n * Get the version to check for a given ID and checkVersion flag.\n * @returns The version to check or undefined if no check is required.\n * @throws Error if the ID is a string and checkVersion is true.\n */\n function getVersionToCheck(id: FragnoId | string, checkVersion: boolean): number | undefined {\n if (!checkVersion) {\n return undefined;\n }\n\n if (typeof id === \"string\") {\n throw new Error(\n `Cannot use checkVersion with a string ID. Version checking requires a FragnoId with version information.`,\n );\n }\n\n return id.version;\n }\n\n /**\n * Process joins recursively to support nested joins with orderBy and limit\n */\n function processJoins(\n joins: CompiledJoin[],\n ): Record<string, Drizzle.DBQueryConfig<\"many\", boolean>> {\n const result: Record<string, Drizzle.DBQueryConfig<\"many\", boolean>> = {};\n\n for (const join of joins) {\n const { options, relation } = join;\n\n if (!options) {\n continue;\n }\n\n const targetTable = relation.table;\n const joinName = relation.name;\n\n // Build columns for this join using shared utility\n const selectOption = options.select === undefined ? true : options.select;\n const orderedColumns = getOrderedJoinColumns(targetTable, selectOption);\n const joinColumns: Record<string, boolean> = {};\n for (const colName of orderedColumns) {\n joinColumns[colName] = true;\n }\n\n // Build orderBy for this join\n let joinOrderBy: Drizzle.SQL[] | undefined;\n if (options.orderBy && options.orderBy.length > 0) {\n joinOrderBy = options.orderBy.map(([col, direction]) => {\n const drizzleCol = toDrizzleColumn(col);\n return direction === \"asc\" ? Drizzle.asc(drizzleCol) : Drizzle.desc(drizzleCol);\n });\n }\n\n // Build WHERE clause for this join if provided\n let joinWhere: Drizzle.SQL | undefined;\n if (options.where) {\n joinWhere = buildWhere(options.where);\n }\n\n // Build the join config\n const joinConfig: Drizzle.DBQueryConfig<\"many\", boolean> = {\n columns: joinColumns,\n orderBy: joinOrderBy,\n limit: options.limit,\n where: joinWhere,\n };\n\n // Recursively process nested joins\n if (options.join && options.join.length > 0) {\n joinConfig.with = processJoins(options.join);\n }\n\n result[joinName] = joinConfig;\n }\n\n return result;\n }\n\n return {\n compileRetrievalOperation(op: RetrievalOperation<TSchema>): DrizzleCompiledQuery | null {\n switch (op.type) {\n case \"count\": {\n // Build WHERE clause\n let whereClause: Drizzle.SQL | undefined;\n if (op.options.where) {\n const condition = buildCondition(op.table.columns, op.options.where);\n if (condition === false) {\n // Never matches - return null\n return null;\n }\n if (condition !== true) {\n whereClause = buildWhere(condition);\n }\n }\n\n const drizzleTable = toDrizzleTable(op.table);\n const query = db.select({ count: Drizzle.count() }).from(drizzleTable);\n\n const compiledQuery = whereClause ? query.where(whereClause).toSQL() : query.toSQL();\n onQuery?.(compiledQuery);\n return compiledQuery;\n }\n\n case \"find\": {\n const {\n useIndex: _useIndex,\n orderByIndex,\n joins,\n after,\n before,\n pageSize,\n ...findOptions\n } = op.options;\n\n // Get index columns for ordering and cursor pagination\n let indexColumns: AnyColumn[] = [];\n let orderDirection: \"asc\" | \"desc\" = \"asc\";\n\n if (orderByIndex) {\n const index = op.table.indexes[orderByIndex.indexName];\n orderDirection = orderByIndex.direction;\n\n if (!index) {\n // If _primary index doesn't exist, fall back to ID column\n if (orderByIndex.indexName === \"_primary\") {\n indexColumns = [op.table.getIdColumn()];\n } else {\n throw new Error(\n `Index \"${orderByIndex.indexName}\" not found on table \"${op.table.name}\"`,\n );\n }\n } else {\n indexColumns = index.columns;\n }\n }\n\n // Convert orderByIndex to orderBy format\n let orderBy: Drizzle.SQL[] | undefined;\n if (indexColumns.length > 0) {\n orderBy = indexColumns.map((col) => {\n const drizzleCol = toDrizzleColumn(col);\n return orderDirection === \"asc\" ? Drizzle.asc(drizzleCol) : Drizzle.desc(drizzleCol);\n });\n }\n\n // Build query configuration\n const columns: Record<string, boolean> = {};\n const select = findOptions.select;\n\n if (select === true || select === undefined) {\n for (const col of Object.values(op.table.columns)) {\n columns[col.ormName] = true;\n }\n } else {\n for (const k of select) {\n columns[op.table.columns[k].ormName] = true;\n }\n // Always include hidden columns (for FragnoId construction with internal ID and version)\n for (const col of Object.values(op.table.columns)) {\n if (col.isHidden && !columns[col.ormName]) {\n columns[col.ormName] = true;\n }\n }\n }\n\n // Build WHERE clause with cursor conditions\n const whereClauses: Drizzle.SQL[] = [];\n\n // Add user-defined where clause\n if (findOptions.where) {\n const condition = buildCondition(op.table.columns, findOptions.where);\n if (condition === false) {\n // Never matches - return null to indicate this query should be skipped\n return null;\n }\n if (condition !== true) {\n const clause = buildWhere(condition);\n if (clause) {\n whereClauses.push(clause);\n }\n }\n }\n\n // Add cursor-based pagination conditions\n if ((after || before) && indexColumns.length > 0) {\n const cursor = after || before;\n const cursorData = decodeCursor(cursor!);\n const serializedValues = serializeCursorValues(cursorData, indexColumns, provider);\n\n // Build tuple comparison for cursor pagination\n // For \"after\" with \"asc\": (col1, col2, ...) > (val1, val2, ...)\n // For \"before\" with \"desc\": reverse the comparison\n const isAfter = !!after;\n const useGreaterThan =\n (isAfter && orderDirection === \"asc\") || (!isAfter && orderDirection === \"desc\");\n\n if (indexColumns.length === 1) {\n // Simple single-column case\n const col = toDrizzleColumn(indexColumns[0]!);\n const val = serializedValues[indexColumns[0]!.ormName];\n whereClauses.push(useGreaterThan ? Drizzle.gt(col, val) : Drizzle.lt(col, val));\n } else {\n // Multi-column tuple comparison using SQL\n const drizzleCols = indexColumns.map((c) => toDrizzleColumn(c));\n const vals = indexColumns.map((c) => serializedValues[c.ormName]);\n const operator = useGreaterThan ? \">\" : \"<\";\n // Safe cast: building a SQL comparison expression for cursor pagination\n // Build the tuple comparison: (col1, col2) > (val1, val2)\n const colsSQL = Drizzle.sql.join(drizzleCols, Drizzle.sql.raw(\", \"));\n const valsSQL = Drizzle.sql.join(\n vals.map((v) => Drizzle.sql`${v}`),\n Drizzle.sql.raw(\", \"),\n );\n whereClauses.push(\n Drizzle.sql`(${colsSQL}) ${Drizzle.sql.raw(operator)} (${valsSQL})`,\n );\n }\n }\n\n const whereClause = whereClauses.length > 0 ? Drizzle.and(...whereClauses) : undefined;\n\n const queryConfig: Drizzle.DBQueryConfig<\"many\", boolean> = {\n columns,\n limit: pageSize,\n where: whereClause,\n orderBy,\n with: {},\n };\n\n // Process joins recursively to support nested joins\n if (joins) {\n queryConfig.with = processJoins(joins);\n }\n\n const physicalTableName = mapper ? mapper.toPhysical(op.table.ormName) : op.table.ormName;\n const compiledQuery = db.query[physicalTableName].findMany(queryConfig).toSQL();\n onQuery?.(compiledQuery);\n return compiledQuery;\n }\n }\n },\n\n compileMutationOperation(\n op: MutationOperation<TSchema>,\n ): CompiledMutation<DrizzleCompiledQuery> | null {\n switch (op.type) {\n case \"create\": {\n const table = getTable(op.table);\n const drizzleTable = toDrizzleTable(table);\n // encodeValues now handles runtime defaults automatically\n const encodedValues = encodeValues(op.values, table, true, provider);\n const values = processReferenceSubqueries(encodedValues);\n\n const compiledQuery = db.insert(drizzleTable).values(values).toSQL();\n onQuery?.(compiledQuery);\n return {\n query: compiledQuery,\n expectedAffectedRows: null, // creates don't need affected row checks\n };\n }\n\n case \"update\": {\n const table = getTable(op.table);\n const idColumn = table.getIdColumn();\n const versionColumn = table.getVersionColumn();\n const drizzleTable = toDrizzleTable(table);\n\n const externalId = typeof op.id === \"string\" ? op.id : op.id.externalId;\n const versionToCheck = getVersionToCheck(op.id, op.checkVersion);\n\n // Build WHERE clause that filters by ID and optionally by version\n const condition =\n versionToCheck !== undefined\n ? buildCondition(table.columns, (eb) =>\n eb.and(\n eb(idColumn.ormName, \"=\", externalId),\n eb(versionColumn.ormName, \"=\", versionToCheck),\n ),\n )\n : buildCondition(table.columns, (eb) => eb(idColumn.ormName, \"=\", externalId));\n\n // Handle boolean cases\n if (condition === false) {\n // Never matches - skip this operation\n return null;\n }\n\n const whereClause = condition === true ? undefined : buildWhere(condition);\n const encodedSetValues = encodeValues(op.set, table, false, provider);\n const setValues = processReferenceSubqueries(encodedSetValues);\n\n // Automatically increment _version for optimistic concurrency control\n // Safe cast: we're building a SQL expression for incrementing the version\n setValues[versionColumn.ormName] = Drizzle.sql.raw(\n `COALESCE(${versionColumn.ormName}, 0) + 1`,\n ) as unknown;\n\n const compiledQuery = db.update(drizzleTable).set(setValues).where(whereClause).toSQL();\n onQuery?.(compiledQuery);\n return {\n query: compiledQuery,\n expectedAffectedRows: op.checkVersion ? 1 : null,\n };\n }\n\n case \"delete\": {\n const table = getTable(op.table);\n const idColumn = table.getIdColumn();\n const versionColumn = table.getVersionColumn();\n const drizzleTable = toDrizzleTable(table);\n\n const externalId = typeof op.id === \"string\" ? op.id : op.id.externalId;\n const versionToCheck = getVersionToCheck(op.id, op.checkVersion);\n\n // Build WHERE clause that filters by ID and optionally by version\n const condition =\n versionToCheck !== undefined\n ? buildCondition(table.columns, (eb) =>\n eb.and(\n eb(idColumn.ormName, \"=\", externalId),\n eb(versionColumn.ormName, \"=\", versionToCheck),\n ),\n )\n : buildCondition(table.columns, (eb) => eb(idColumn.ormName, \"=\", externalId));\n\n // Handle boolean cases\n if (condition === false) {\n // Never matches - skip this operation\n return null;\n }\n\n const whereClause = condition === true ? undefined : buildWhere(condition);\n\n const compiledQuery = db.delete(drizzleTable).where(whereClause).toSQL();\n onQuery?.(compiledQuery);\n return {\n query: compiledQuery,\n expectedAffectedRows: op.checkVersion ? 1 : null,\n };\n }\n }\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drizzle-uow-decoder.js","names":["result: Record<string, unknown>"],"sources":["../../../src/adapters/drizzle/drizzle-uow-decoder.ts"],"sourcesContent":["import type { AnySchema, AnyTable } from \"../../schema/create\";\nimport type { SQLProvider } from \"../../shared/providers\";\nimport type { RetrievalOperation, UOWDecoder } from \"../../query/unit-of-work\";\nimport { decodeResult } from \"../../query/result-transform\";\nimport
|
|
1
|
+
{"version":3,"file":"drizzle-uow-decoder.js","names":["result: Record<string, unknown>"],"sources":["../../../src/adapters/drizzle/drizzle-uow-decoder.ts"],"sourcesContent":["import type { AnySchema, AnyTable } from \"../../schema/create\";\nimport type { SQLProvider } from \"../../shared/providers\";\nimport type { RetrievalOperation, UOWDecoder } from \"../../query/unit-of-work\";\nimport { decodeResult } from \"../../query/result-transform\";\nimport { getOrderedJoinColumns } from \"./join-column-utils\";\nimport type { DrizzleResult } from \"./shared\";\n\n/**\n * Join information with nested join support\n */\ninterface JoinInfo {\n relation: { name: string; table: AnyTable };\n options:\n | {\n select: true | string[];\n join?: JoinInfo[];\n }\n | false;\n}\n\n/**\n * Recursively transform join arrays to objects, handling nested joins.\n *\n * Drizzle joins use `json_build_array` where nested join data is appended after the parent's columns.\n * For example, if post has columns [id, title, content, _internalId, _version] and a nested author join,\n * the array will be: [id, title, content, _internalId, _version, authorArray]\n *\n * @param value - The join array from Drizzle\n * @param joinInfo - Join metadata including nested joins\n * @param relationName - Name of the current relation (for prefixing column names)\n * @returns Object with flattened keys (relationName:columnName) for all levels\n */\nfunction transformJoinArray(\n value: unknown[],\n joinInfo: JoinInfo,\n relationName: string,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n if (joinInfo.options === false) {\n return result;\n }\n\n const targetTable = joinInfo.relation.table;\n\n // Get ordered columns using shared utility (must match compiler's column order)\n const orderedSelectedColumns = getOrderedJoinColumns(targetTable, joinInfo.options.select);\n\n // Map column values to flattened format: relationName:columnName\n for (let i = 0; i < orderedSelectedColumns.length && i < value.length; i++) {\n const columnName = orderedSelectedColumns[i];\n if (columnName) {\n result[`${relationName}:${columnName}`] = value[i];\n }\n }\n\n // Handle nested joins - they appear after all columns in the array\n if (joinInfo.options.join && joinInfo.options.join.length > 0) {\n let nestedArrayIndex = orderedSelectedColumns.length;\n\n for (const nestedJoin of joinInfo.options.join) {\n const nestedRelationName = `${relationName}:${nestedJoin.relation.name}`;\n const nestedValue = value[nestedArrayIndex];\n\n if (Array.isArray(nestedValue)) {\n // Recursively transform nested join\n const nestedResult = transformJoinArray(nestedValue, nestedJoin, nestedRelationName);\n Object.assign(result, nestedResult);\n }\n\n nestedArrayIndex++;\n }\n }\n\n return result;\n}\n\n/**\n * Drizzle joins using `json_build_array` so the result is a tuple of values that we need to map to\n * the correct columns. This function handles nested joins recursively.\n *\n * @param row - Raw database result row that may contain join arrays\n * @param op - The retrieval operation containing join information\n * @returns Transformed row with join arrays converted to objects\n */\nfunction transformJoinArraysToObjects(\n row: Record<string, unknown>,\n op: {\n type: string;\n table: AnyTable;\n options?: {\n joins?: JoinInfo[];\n };\n },\n): Record<string, unknown> {\n // Only process find operations with joins\n if (op.type !== \"find\" || !op.options?.joins) {\n return row;\n }\n\n const transformedRow = { ...row };\n\n for (const join of op.options.joins) {\n const relationName = join.relation.name;\n const value = row[relationName];\n\n // Skip if not an array (join didn't return data)\n if (!Array.isArray(value)) {\n continue;\n }\n\n // Skip if join options are false (join was disabled)\n if (join.options === false) {\n continue;\n }\n\n // Get the target table for this relation\n const relation = op.table.relations[relationName];\n if (!relation) {\n continue;\n }\n\n // Recursively transform this join and its nested joins\n const joinResult = transformJoinArray(value, join, relationName);\n Object.assign(transformedRow, joinResult);\n\n // Remove the original array property\n delete transformedRow[relationName];\n }\n\n return transformedRow;\n}\n\nexport function createDrizzleUOWDecoder<TSchema extends AnySchema>(\n _schema: TSchema,\n provider: SQLProvider,\n): UOWDecoder<TSchema, DrizzleResult> {\n return (rawResults, ops) => {\n if (rawResults.length !== ops.length) {\n throw new Error(\"rawResults and ops must have the same length\");\n }\n\n return rawResults.map((result, index) => {\n const op = ops[index] as RetrievalOperation<TSchema>;\n if (!op) {\n throw new Error(\"op must be defined\");\n }\n\n // Handle count operations - return the count value directly\n if (op.type === \"count\") {\n if (result.rows.length > 0 && result.rows[0]) {\n const row = result.rows[0];\n return (row as Record<string, unknown>)[\"count\"] as number;\n }\n return 0;\n }\n\n // Handle find operations - decode each row\n return result.rows.map((row) => {\n const transformedRow = transformJoinArraysToObjects(row, op);\n return decodeResult(transformedRow, op.table, provider);\n });\n });\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgCA,SAAS,mBACP,OACA,UACA,cACyB;CACzB,MAAMA,SAAkC,EAAE;AAE1C,KAAI,SAAS,YAAY,MACvB,QAAO;CAGT,MAAM,cAAc,SAAS,SAAS;CAGtC,MAAM,yBAAyB,sBAAsB,aAAa,SAAS,QAAQ,OAAO;AAG1F,MAAK,IAAI,IAAI,GAAG,IAAI,uBAAuB,UAAU,IAAI,MAAM,QAAQ,KAAK;EAC1E,MAAM,aAAa,uBAAuB;AAC1C,MAAI,WACF,QAAO,GAAG,aAAa,GAAG,gBAAgB,MAAM;;AAKpD,KAAI,SAAS,QAAQ,QAAQ,SAAS,QAAQ,KAAK,SAAS,GAAG;EAC7D,IAAI,mBAAmB,uBAAuB;AAE9C,OAAK,MAAM,cAAc,SAAS,QAAQ,MAAM;GAC9C,MAAM,qBAAqB,GAAG,aAAa,GAAG,WAAW,SAAS;GAClE,MAAM,cAAc,MAAM;AAE1B,OAAI,MAAM,QAAQ,YAAY,EAAE;IAE9B,MAAM,eAAe,mBAAmB,aAAa,YAAY,mBAAmB;AACpF,WAAO,OAAO,QAAQ,aAAa;;AAGrC;;;AAIJ,QAAO;;;;;;;;;;AAWT,SAAS,6BACP,KACA,IAOyB;AAEzB,KAAI,GAAG,SAAS,UAAU,CAAC,GAAG,SAAS,MACrC,QAAO;CAGT,MAAM,iBAAiB,EAAE,GAAG,KAAK;AAEjC,MAAK,MAAM,QAAQ,GAAG,QAAQ,OAAO;EACnC,MAAM,eAAe,KAAK,SAAS;EACnC,MAAM,QAAQ,IAAI;AAGlB,MAAI,CAAC,MAAM,QAAQ,MAAM,CACvB;AAIF,MAAI,KAAK,YAAY,MACnB;AAKF,MAAI,CADa,GAAG,MAAM,UAAU,cAElC;EAIF,MAAM,aAAa,mBAAmB,OAAO,MAAM,aAAa;AAChE,SAAO,OAAO,gBAAgB,WAAW;AAGzC,SAAO,eAAe;;AAGxB,QAAO;;AAGT,SAAgB,wBACd,SACA,UACoC;AACpC,SAAQ,YAAY,QAAQ;AAC1B,MAAI,WAAW,WAAW,IAAI,OAC5B,OAAM,IAAI,MAAM,+CAA+C;AAGjE,SAAO,WAAW,KAAK,QAAQ,UAAU;GACvC,MAAM,KAAK,IAAI;AACf,OAAI,CAAC,GACH,OAAM,IAAI,MAAM,qBAAqB;AAIvC,OAAI,GAAG,SAAS,SAAS;AACvB,QAAI,OAAO,KAAK,SAAS,KAAK,OAAO,KAAK,GAExC,QADY,OAAO,KAAK,GACgB;AAE1C,WAAO;;AAIT,UAAO,OAAO,KAAK,KAAK,QAAQ;AAE9B,WAAO,aADgB,6BAA6B,KAAK,GAAG,EACxB,GAAG,OAAO,SAAS;KACvD;IACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drizzle-uow-executor.js","names":["queryChunks: SQLChunk[]","match: RegExpExecArray | null","retrievalResults: DrizzleResult[]","createdInternalIds: (bigint | null)[]"],"sources":["../../../src/adapters/drizzle/drizzle-uow-executor.ts"],"sourcesContent":["import { SQL, StringChunk, sql, type SQLChunk } from \"drizzle-orm\";\nimport type { CompiledMutation, MutationResult } from \"../../query/unit-of-work\";\nimport type { DBType } from \"./shared\";\nimport type { DrizzleCompiledQuery } from \"./drizzle-uow-compiler\";\nimport type { DrizzleResult } from \"./drizzle-query\";\n\n/**\n * Convert a DrizzleCompiledQuery (SQL string + params) to a Drizzle SQL object\n *\n * This reconstructs the SQL object with proper parameter chunks by parsing\n * the SQL string and replacing placeholders ($1, $2, etc.) with Param objects.\n * Uses Drizzle's exported classes (StringChunk, Param, SQL) to build the queryChunks.\n */\nfunction toSQL(query: DrizzleCompiledQuery): SQL {\n const { sql: sqlString, params } = query;\n\n // Match parameter placeholders like $1, $2, etc.\n const placeholderRegex = /\\$(\\d+)/g;\n const queryChunks: SQLChunk[] = [];\n let lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = placeholderRegex.exec(sqlString)) !== null) {\n // Add the string chunk before the placeholder\n const textBefore = sqlString.substring(lastIndex, match.index);\n if (textBefore) {\n queryChunks.push(new StringChunk(textBefore));\n }\n\n // Add the parameter value as a Param\n const paramIndex = parseInt(match[1]!, 10) - 1; // $1 is index 0\n queryChunks.push(sql.param(params[paramIndex]));\n\n lastIndex = match.index + match[0].length;\n }\n\n // Add any remaining string after the last placeholder\n const textAfter = sqlString.substring(lastIndex);\n if (textAfter) {\n queryChunks.push(new StringChunk(textAfter));\n }\n\n // Construct SQL object directly with queryChunks (same pattern as sql`` tagged template)\n // Safe: We're reusing Drizzle's SQL constructor the same way the sql`` function does\n return new SQL(queryChunks);\n}\n\n/**\n * Get the number of affected rows from a Drizzle query result\n */\nfunction getAffectedRows(result: unknown): number {\n // Drizzle returns different formats depending on the database\n // For MySQL: array with affectedRows property\n // For PostgreSQL/SQLite: array with rowCount or similar\n if (Array.isArray(result)) {\n // This is likely a select/returning result\n return result.length;\n }\n\n // Check for MySQL-style result\n if (\n result &&\n typeof result === \"object\" &&\n \"affectedRows\" in result &&\n typeof result[\"affectedRows\"] === \"number\"\n ) {\n return result[\"affectedRows\"];\n }\n\n // Check for PostgreSQL-style result with rowCount\n if (\n result &&\n typeof result === \"object\" &&\n \"rowCount\" in result &&\n (typeof result[\"rowCount\"] === \"number\" || typeof result[\"rowCount\"] === \"bigint\")\n ) {\n const rowCount = result[\"rowCount\"];\n if (rowCount > Number.MAX_SAFE_INTEGER) {\n throw new Error(`rowCount BigInt value ${rowCount.toString()} exceeds JS safe integer range`);\n }\n return Number(rowCount);\n }\n\n // For update/delete operations, Drizzle might return an object with affected rows info\n // Try to extract it from common patterns\n if (result && typeof result === \"object\") {\n // Check for changes/changes count\n if (\"changes\" in result && typeof result[\"changes\"] === \"number\") {\n return result[\"changes\"];\n }\n }\n\n throw new Error(`Unable to determine affected rows from result: ${JSON.stringify(result)}`);\n}\n\n/**\n * Execute the retrieval phase of a Unit of Work using Drizzle\n *\n * All retrieval queries are executed inside a single transaction to ensure\n * snapshot isolation - all reads see a consistent view of the database.\n *\n * @param db - The Drizzle database instance\n * @param retrievalBatch - Array of Drizzle SQL queries\n * @returns Array of query results matching the retrieval operations order\n *\n * @example\n * ```ts\n * const retrievalResults = await executeDrizzleRetrievalPhase(db, compiled.retrievalBatch);\n * const [users, posts] = retrievalResults;\n * ```\n */\nexport async function executeDrizzleRetrievalPhase(\n db: DBType,\n retrievalBatch: DrizzleCompiledQuery[],\n): Promise<DrizzleResult[]> {\n // If no retrieval operations, return empty array immediately\n if (retrievalBatch.length === 0) {\n return [];\n }\n\n const retrievalResults: DrizzleResult[] = [];\n\n // Execute all retrieval queries inside a transaction for snapshot isolation\n await db.transaction(async (tx) => {\n for (const query of retrievalBatch) {\n const result = (await tx.execute(toSQL(query))) as DrizzleResult;\n retrievalResults.push(result);\n }\n });\n\n return retrievalResults;\n}\n\n/**\n * Execute the mutation phase of a Unit of Work using Drizzle\n *\n * All mutation queries are executed in a transaction with optimistic locking.\n * If any version check fails, the entire transaction is rolled back and\n * success=false is returned.\n *\n * @param db - The Drizzle database instance\n * @param mutationBatch - Array of compiled mutation SQL queries with expected affected rows\n * @returns Object with success flag and internal IDs from create operations\n *\n * @example\n * ```ts\n * const { success } = await executeDrizzleMutationPhase(db, compiled.mutationBatch);\n * if (!success) {\n * console.log(\"Version conflict detected, retrying...\");\n * }\n * ```\n */\nexport async function executeDrizzleMutationPhase(\n db: DBType,\n mutationBatch: CompiledMutation<DrizzleCompiledQuery>[],\n): Promise<MutationResult> {\n // If there are no mutations, return success immediately\n if (mutationBatch.length === 0) {\n return { success: true, createdInternalIds: [] };\n }\n\n const createdInternalIds: (bigint | null)[] = [];\n\n // Execute mutation batch in a transaction\n try {\n await db.transaction(async (tx) => {\n for (const compiledMutation of mutationBatch) {\n const result = await tx.execute(toSQL(compiledMutation.query));\n\n // For creates (expectedAffectedRows === null), try to extract internal ID\n if (compiledMutation.expectedAffectedRows === null) {\n // Check if result is an array with rows (RETURNING clause supported)\n if (Array.isArray(result) && result.length > 0) {\n const row = result[0] as Record<string, unknown>;\n // Look for _internalId column in the returned row\n if (\"_internalId\" in row || \"_internal_id\" in row) {\n const internalId = (row[\"_internalId\"] ?? row[\"_internal_id\"]) as bigint;\n createdInternalIds.push(internalId);\n } else {\n // RETURNING supported but _internalId not found\n createdInternalIds.push(null);\n }\n } else {\n // No RETURNING support (e.g., MySQL)\n createdInternalIds.push(null);\n }\n } else {\n // Check affected rows for updates/deletes\n const affectedRows = getAffectedRows(result);\n\n if (affectedRows !== compiledMutation.expectedAffectedRows) {\n // Version conflict detected - the UPDATE/DELETE didn't affect the expected number of rows\n // This means either the row doesn't exist or the version has changed\n throw new Error(\n `Version conflict: expected ${compiledMutation.expectedAffectedRows} rows affected, but got ${affectedRows}`,\n );\n }\n }\n }\n });\n\n return { success: true, createdInternalIds };\n } catch (error) {\n // Transaction failed - could be version conflict or other constraint violation\n // Return success=false to indicate the UOW should be retried\n if (error instanceof Error && error.message.includes(\"Version conflict\")) {\n return { success: false };\n }\n\n // Other database errors should be thrown\n throw error;\n }\n}\n"],"mappings":";;;;;;;;;;AAaA,SAAS,MAAM,OAAkC;CAC/C,MAAM,EAAE,KAAK,WAAW,WAAW;CAGnC,MAAM,mBAAmB;CACzB,MAAMA,cAA0B,EAAE;CAClC,IAAI,YAAY;CAChB,IAAIC;AAEJ,SAAQ,QAAQ,iBAAiB,KAAK,UAAU,MAAM,MAAM;EAE1D,MAAM,aAAa,UAAU,UAAU,WAAW,MAAM,MAAM;AAC9D,MAAI,WACF,aAAY,KAAK,IAAI,YAAY,WAAW,CAAC;EAI/C,MAAM,aAAa,SAAS,MAAM,IAAK,GAAG,GAAG;AAC7C,cAAY,KAAK,IAAI,MAAM,OAAO,YAAY,CAAC;AAE/C,cAAY,MAAM,QAAQ,MAAM,GAAG;;CAIrC,MAAM,YAAY,UAAU,UAAU,UAAU;AAChD,KAAI,UACF,aAAY,KAAK,IAAI,YAAY,UAAU,CAAC;AAK9C,QAAO,IAAI,IAAI,YAAY;;;;;AAM7B,SAAS,gBAAgB,QAAyB;AAIhD,KAAI,MAAM,QAAQ,OAAO,CAEvB,QAAO,OAAO;AAIhB,KACE,UACA,OAAO,WAAW,YAClB,kBAAkB,UAClB,OAAO,OAAO,oBAAoB,SAElC,QAAO,OAAO;AAIhB,KACE,UACA,OAAO,WAAW,YAClB,cAAc,WACb,OAAO,OAAO,gBAAgB,YAAY,OAAO,OAAO,gBAAgB,WACzE;EACA,MAAM,WAAW,OAAO;AACxB,MAAI,WAAW,OAAO,iBACpB,OAAM,IAAI,MAAM,yBAAyB,SAAS,UAAU,CAAC,gCAAgC;AAE/F,SAAO,OAAO,SAAS;;AAKzB,KAAI,UAAU,OAAO,WAAW,UAE9B;MAAI,aAAa,UAAU,OAAO,OAAO,eAAe,SACtD,QAAO,OAAO;;AAIlB,OAAM,IAAI,MAAM,kDAAkD,KAAK,UAAU,OAAO,GAAG;;;;;;;;;;;;;;;;;;AAmB7F,eAAsB,6BACpB,IACA,gBAC0B;AAE1B,KAAI,eAAe,WAAW,EAC5B,QAAO,EAAE;CAGX,MAAMC,mBAAoC,EAAE;AAG5C,OAAM,GAAG,YAAY,OAAO,OAAO;AACjC,OAAK,MAAM,SAAS,gBAAgB;GAClC,MAAM,SAAU,MAAM,GAAG,QAAQ,MAAM,MAAM,CAAC;AAC9C,oBAAiB,KAAK,OAAO;;GAE/B;AAEF,QAAO;;;;;;;;;;;;;;;;;;;;;AAsBT,eAAsB,4BACpB,IACA,eACyB;AAEzB,KAAI,cAAc,WAAW,EAC3B,QAAO;EAAE,SAAS;EAAM,oBAAoB,EAAE;EAAE;CAGlD,MAAMC,qBAAwC,EAAE;AAGhD,KAAI;AACF,QAAM,GAAG,YAAY,OAAO,OAAO;AACjC,QAAK,MAAM,oBAAoB,eAAe;IAC5C,MAAM,SAAS,MAAM,GAAG,QAAQ,MAAM,iBAAiB,MAAM,CAAC;AAG9D,QAAI,iBAAiB,yBAAyB,KAE5C,KAAI,MAAM,QAAQ,OAAO,IAAI,OAAO,SAAS,GAAG;KAC9C,MAAM,MAAM,OAAO;AAEnB,SAAI,iBAAiB,OAAO,kBAAkB,KAAK;MACjD,MAAM,aAAc,IAAI,kBAAkB,IAAI;AAC9C,yBAAmB,KAAK,WAAW;WAGnC,oBAAmB,KAAK,KAAK;UAI/B,oBAAmB,KAAK,KAAK;SAE1B;KAEL,MAAM,eAAe,gBAAgB,OAAO;AAE5C,SAAI,iBAAiB,iBAAiB,qBAGpC,OAAM,IAAI,MACR,8BAA8B,iBAAiB,qBAAqB,0BAA0B,eAC/F;;;IAIP;AAEF,SAAO;GAAE,SAAS;GAAM;GAAoB;UACrC,OAAO;AAGd,MAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,mBAAmB,CACtE,QAAO,EAAE,SAAS,OAAO;AAI3B,QAAM"}
|
|
1
|
+
{"version":3,"file":"drizzle-uow-executor.js","names":["queryChunks: SQLChunk[]","match: RegExpExecArray | null","retrievalResults: DrizzleResult[]","createdInternalIds: (bigint | null)[]"],"sources":["../../../src/adapters/drizzle/drizzle-uow-executor.ts"],"sourcesContent":["import { SQL, StringChunk, sql, type SQLChunk } from \"drizzle-orm\";\nimport type { CompiledMutation, MutationResult } from \"../../query/unit-of-work\";\nimport type { DBType } from \"./shared\";\nimport type { DrizzleCompiledQuery } from \"./drizzle-uow-compiler\";\nimport type { DrizzleResult } from \"./shared\";\n\n/**\n * Convert a DrizzleCompiledQuery (SQL string + params) to a Drizzle SQL object\n *\n * This reconstructs the SQL object with proper parameter chunks by parsing\n * the SQL string and replacing placeholders ($1, $2, etc.) with Param objects.\n * Uses Drizzle's exported classes (StringChunk, Param, SQL) to build the queryChunks.\n */\nfunction toSQL(query: DrizzleCompiledQuery): SQL {\n const { sql: sqlString, params } = query;\n\n // Match parameter placeholders like $1, $2, etc.\n const placeholderRegex = /\\$(\\d+)/g;\n const queryChunks: SQLChunk[] = [];\n let lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = placeholderRegex.exec(sqlString)) !== null) {\n // Add the string chunk before the placeholder\n const textBefore = sqlString.substring(lastIndex, match.index);\n if (textBefore) {\n queryChunks.push(new StringChunk(textBefore));\n }\n\n // Add the parameter value as a Param\n const paramIndex = parseInt(match[1]!, 10) - 1; // $1 is index 0\n queryChunks.push(sql.param(params[paramIndex]));\n\n lastIndex = match.index + match[0].length;\n }\n\n // Add any remaining string after the last placeholder\n const textAfter = sqlString.substring(lastIndex);\n if (textAfter) {\n queryChunks.push(new StringChunk(textAfter));\n }\n\n // Construct SQL object directly with queryChunks (same pattern as sql`` tagged template)\n // Safe: We're reusing Drizzle's SQL constructor the same way the sql`` function does\n return new SQL(queryChunks);\n}\n\n/**\n * Get the number of affected rows from a Drizzle query result\n */\nfunction getAffectedRows(result: unknown): number {\n // Drizzle returns different formats depending on the database\n // For MySQL: array with affectedRows property\n // For PostgreSQL/SQLite: array with rowCount or similar\n if (Array.isArray(result)) {\n // This is likely a select/returning result\n return result.length;\n }\n\n // Check for MySQL-style result\n if (\n result &&\n typeof result === \"object\" &&\n \"affectedRows\" in result &&\n typeof result[\"affectedRows\"] === \"number\"\n ) {\n return result[\"affectedRows\"];\n }\n\n // Check for PostgreSQL-style result with rowCount\n if (\n result &&\n typeof result === \"object\" &&\n \"rowCount\" in result &&\n (typeof result[\"rowCount\"] === \"number\" || typeof result[\"rowCount\"] === \"bigint\")\n ) {\n const rowCount = result[\"rowCount\"];\n if (rowCount > Number.MAX_SAFE_INTEGER) {\n throw new Error(`rowCount BigInt value ${rowCount.toString()} exceeds JS safe integer range`);\n }\n return Number(rowCount);\n }\n\n // For update/delete operations, Drizzle might return an object with affected rows info\n // Try to extract it from common patterns\n if (result && typeof result === \"object\") {\n // Check for changes/changes count\n if (\"changes\" in result && typeof result[\"changes\"] === \"number\") {\n return result[\"changes\"];\n }\n }\n\n throw new Error(`Unable to determine affected rows from result: ${JSON.stringify(result)}`);\n}\n\n/**\n * Execute the retrieval phase of a Unit of Work using Drizzle\n *\n * All retrieval queries are executed inside a single transaction to ensure\n * snapshot isolation - all reads see a consistent view of the database.\n *\n * @param db - The Drizzle database instance\n * @param retrievalBatch - Array of Drizzle SQL queries\n * @returns Array of query results matching the retrieval operations order\n *\n * @example\n * ```ts\n * const retrievalResults = await executeDrizzleRetrievalPhase(db, compiled.retrievalBatch);\n * const [users, posts] = retrievalResults;\n * ```\n */\nexport async function executeDrizzleRetrievalPhase(\n db: DBType,\n retrievalBatch: DrizzleCompiledQuery[],\n): Promise<DrizzleResult[]> {\n // If no retrieval operations, return empty array immediately\n if (retrievalBatch.length === 0) {\n return [];\n }\n\n const retrievalResults: DrizzleResult[] = [];\n\n // Execute all retrieval queries inside a transaction for snapshot isolation\n await db.transaction(async (tx) => {\n for (const query of retrievalBatch) {\n const result = (await tx.execute(toSQL(query))) as DrizzleResult;\n retrievalResults.push(result);\n }\n });\n\n return retrievalResults;\n}\n\n/**\n * Execute the mutation phase of a Unit of Work using Drizzle\n *\n * All mutation queries are executed in a transaction with optimistic locking.\n * If any version check fails, the entire transaction is rolled back and\n * success=false is returned.\n *\n * @param db - The Drizzle database instance\n * @param mutationBatch - Array of compiled mutation SQL queries with expected affected rows\n * @returns Object with success flag and internal IDs from create operations\n *\n * @example\n * ```ts\n * const { success } = await executeDrizzleMutationPhase(db, compiled.mutationBatch);\n * if (!success) {\n * console.log(\"Version conflict detected, retrying...\");\n * }\n * ```\n */\nexport async function executeDrizzleMutationPhase(\n db: DBType,\n mutationBatch: CompiledMutation<DrizzleCompiledQuery>[],\n): Promise<MutationResult> {\n // If there are no mutations, return success immediately\n if (mutationBatch.length === 0) {\n return { success: true, createdInternalIds: [] };\n }\n\n const createdInternalIds: (bigint | null)[] = [];\n\n // Execute mutation batch in a transaction\n try {\n await db.transaction(async (tx) => {\n for (const compiledMutation of mutationBatch) {\n const result = await tx.execute(toSQL(compiledMutation.query));\n\n // For creates (expectedAffectedRows === null), try to extract internal ID\n if (compiledMutation.expectedAffectedRows === null) {\n // Check if result is an array with rows (RETURNING clause supported)\n if (Array.isArray(result) && result.length > 0) {\n const row = result[0] as Record<string, unknown>;\n // Look for _internalId column in the returned row\n if (\"_internalId\" in row || \"_internal_id\" in row) {\n const internalId = (row[\"_internalId\"] ?? row[\"_internal_id\"]) as bigint;\n createdInternalIds.push(internalId);\n } else {\n // RETURNING supported but _internalId not found\n createdInternalIds.push(null);\n }\n } else {\n // No RETURNING support (e.g., MySQL)\n createdInternalIds.push(null);\n }\n } else {\n // Check affected rows for updates/deletes\n const affectedRows = getAffectedRows(result);\n\n if (affectedRows !== compiledMutation.expectedAffectedRows) {\n // Version conflict detected - the UPDATE/DELETE didn't affect the expected number of rows\n // This means either the row doesn't exist or the version has changed\n throw new Error(\n `Version conflict: expected ${compiledMutation.expectedAffectedRows} rows affected, but got ${affectedRows}`,\n );\n }\n }\n }\n });\n\n return { success: true, createdInternalIds };\n } catch (error) {\n // Transaction failed - could be version conflict or other constraint violation\n // Return success=false to indicate the UOW should be retried\n if (error instanceof Error && error.message.includes(\"Version conflict\")) {\n return { success: false };\n }\n\n // Other database errors should be thrown\n throw error;\n }\n}\n"],"mappings":";;;;;;;;;;AAaA,SAAS,MAAM,OAAkC;CAC/C,MAAM,EAAE,KAAK,WAAW,WAAW;CAGnC,MAAM,mBAAmB;CACzB,MAAMA,cAA0B,EAAE;CAClC,IAAI,YAAY;CAChB,IAAIC;AAEJ,SAAQ,QAAQ,iBAAiB,KAAK,UAAU,MAAM,MAAM;EAE1D,MAAM,aAAa,UAAU,UAAU,WAAW,MAAM,MAAM;AAC9D,MAAI,WACF,aAAY,KAAK,IAAI,YAAY,WAAW,CAAC;EAI/C,MAAM,aAAa,SAAS,MAAM,IAAK,GAAG,GAAG;AAC7C,cAAY,KAAK,IAAI,MAAM,OAAO,YAAY,CAAC;AAE/C,cAAY,MAAM,QAAQ,MAAM,GAAG;;CAIrC,MAAM,YAAY,UAAU,UAAU,UAAU;AAChD,KAAI,UACF,aAAY,KAAK,IAAI,YAAY,UAAU,CAAC;AAK9C,QAAO,IAAI,IAAI,YAAY;;;;;AAM7B,SAAS,gBAAgB,QAAyB;AAIhD,KAAI,MAAM,QAAQ,OAAO,CAEvB,QAAO,OAAO;AAIhB,KACE,UACA,OAAO,WAAW,YAClB,kBAAkB,UAClB,OAAO,OAAO,oBAAoB,SAElC,QAAO,OAAO;AAIhB,KACE,UACA,OAAO,WAAW,YAClB,cAAc,WACb,OAAO,OAAO,gBAAgB,YAAY,OAAO,OAAO,gBAAgB,WACzE;EACA,MAAM,WAAW,OAAO;AACxB,MAAI,WAAW,OAAO,iBACpB,OAAM,IAAI,MAAM,yBAAyB,SAAS,UAAU,CAAC,gCAAgC;AAE/F,SAAO,OAAO,SAAS;;AAKzB,KAAI,UAAU,OAAO,WAAW,UAE9B;MAAI,aAAa,UAAU,OAAO,OAAO,eAAe,SACtD,QAAO,OAAO;;AAIlB,OAAM,IAAI,MAAM,kDAAkD,KAAK,UAAU,OAAO,GAAG;;;;;;;;;;;;;;;;;;AAmB7F,eAAsB,6BACpB,IACA,gBAC0B;AAE1B,KAAI,eAAe,WAAW,EAC5B,QAAO,EAAE;CAGX,MAAMC,mBAAoC,EAAE;AAG5C,OAAM,GAAG,YAAY,OAAO,OAAO;AACjC,OAAK,MAAM,SAAS,gBAAgB;GAClC,MAAM,SAAU,MAAM,GAAG,QAAQ,MAAM,MAAM,CAAC;AAC9C,oBAAiB,KAAK,OAAO;;GAE/B;AAEF,QAAO;;;;;;;;;;;;;;;;;;;;;AAsBT,eAAsB,4BACpB,IACA,eACyB;AAEzB,KAAI,cAAc,WAAW,EAC3B,QAAO;EAAE,SAAS;EAAM,oBAAoB,EAAE;EAAE;CAGlD,MAAMC,qBAAwC,EAAE;AAGhD,KAAI;AACF,QAAM,GAAG,YAAY,OAAO,OAAO;AACjC,QAAK,MAAM,oBAAoB,eAAe;IAC5C,MAAM,SAAS,MAAM,GAAG,QAAQ,MAAM,iBAAiB,MAAM,CAAC;AAG9D,QAAI,iBAAiB,yBAAyB,KAE5C,KAAI,MAAM,QAAQ,OAAO,IAAI,OAAO,SAAS,GAAG;KAC9C,MAAM,MAAM,OAAO;AAEnB,SAAI,iBAAiB,OAAO,kBAAkB,KAAK;MACjD,MAAM,aAAc,IAAI,kBAAkB,IAAI;AAC9C,yBAAmB,KAAK,WAAW;WAGnC,oBAAmB,KAAK,KAAK;UAI/B,oBAAmB,KAAK,KAAK;SAE1B;KAEL,MAAM,eAAe,gBAAgB,OAAO;AAE5C,SAAI,iBAAiB,iBAAiB,qBAGpC,OAAM,IAAI,MACR,8BAA8B,iBAAiB,qBAAqB,0BAA0B,eAC/F;;;IAIP;AAEF,SAAO;GAAE,SAAS;GAAM;GAAoB;UACrC,OAAO;AAGd,MAAI,iBAAiB,SAAS,MAAM,QAAQ,SAAS,mBAAmB,CACtE,QAAO,EAAE,SAAS,OAAO;AAI3B,QAAM"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { InternalIdColumn } from "../../schema/create.js";
|
|
2
|
+
import { SETTINGS_TABLE_NAME, settingsSchema } from "../../shared/settings-schema.js";
|
|
2
3
|
import { schemaToDBType } from "../../schema/serialize.js";
|
|
3
4
|
import { importGenerator } from "../../util/import-generator.js";
|
|
4
5
|
import { ident, parseVarchar } from "../../util/parse.js";
|
|
6
|
+
import { createTableNameMapper } from "./shared.js";
|
|
5
7
|
|
|
6
8
|
//#region src/adapters/drizzle/generate.ts
|
|
7
9
|
const PROVIDER_IMPORTS = {
|
|
@@ -156,21 +158,26 @@ function generateColumnDefinition(ctx, column, customTypes) {
|
|
|
156
158
|
value = `sql\`${column.default.value.toString()}\``;
|
|
157
159
|
} else value = JSON.stringify(column.default.value);
|
|
158
160
|
parts.push(`default(${value})`);
|
|
159
|
-
} else if (column.default
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
161
|
+
} else if ("dbSpecial" in column.default) {
|
|
162
|
+
if (column.default.dbSpecial === "now") parts.push("defaultNow()");
|
|
163
|
+
} else if ("runtime" in column.default) {
|
|
164
|
+
if (column.default.runtime === "cuid") {
|
|
165
|
+
const idGen = ctx.idGeneratorImport ?? {
|
|
166
|
+
name: "createId",
|
|
167
|
+
from: "@fragno-dev/db/id"
|
|
168
|
+
};
|
|
169
|
+
ctx.imports.addImport(idGen.name, idGen.from);
|
|
170
|
+
parts.push(`$defaultFn(() => ${idGen.name}())`);
|
|
171
|
+
} else if (column.default.runtime === "now") parts.push("$defaultFn(() => new Date())");
|
|
172
|
+
}
|
|
167
173
|
}
|
|
168
174
|
return ` ${column.ormName}: ${parts.join(".")}`;
|
|
169
175
|
}
|
|
170
176
|
function generateAllColumns(ctx, table, customTypes) {
|
|
171
177
|
return Object.values(table.columns).map((column) => generateColumnDefinition(ctx, column, customTypes));
|
|
172
178
|
}
|
|
173
|
-
function generateForeignKeys(ctx, table) {
|
|
179
|
+
function generateForeignKeys(ctx, table, namespace) {
|
|
180
|
+
const mapper = namespace ? createTableNameMapper(namespace) : void 0;
|
|
174
181
|
const keys = [];
|
|
175
182
|
for (const relation of Object.values(table.relations)) {
|
|
176
183
|
if (relation.type === "many") continue;
|
|
@@ -181,10 +188,13 @@ function generateForeignKeys(ctx, table) {
|
|
|
181
188
|
columns.push(`table.${localCol}`);
|
|
182
189
|
const actualRefCol = refCol === "id" ? "_internalId" : refCol;
|
|
183
190
|
if (isSelfReference) foreignColumns.push(`table.${actualRefCol}`);
|
|
184
|
-
else
|
|
191
|
+
else {
|
|
192
|
+
const foreignTableRef = mapper && namespace ? mapper.toPhysical(relation.table.ormName) : relation.table.ormName;
|
|
193
|
+
foreignColumns.push(`${foreignTableRef}.${actualRefCol}`);
|
|
194
|
+
}
|
|
185
195
|
}
|
|
186
196
|
ctx.imports.addImport("foreignKey", ctx.importSource);
|
|
187
|
-
const fkName = `${table.ormName}_${relation.table.ormName}_${relation.name}_fk`;
|
|
197
|
+
const fkName = namespace && mapper ? "fk_" + mapper.toPhysical(`${table.ormName}_${relation.table.ormName}_${relation.name}`) : `${table.ormName}_${relation.table.ormName}_${relation.name}_fk`;
|
|
188
198
|
keys.push(`foreignKey({
|
|
189
199
|
columns: [${columns.join(", ")}],
|
|
190
200
|
foreignColumns: [${foreignColumns.join(", ")}],
|
|
@@ -193,33 +203,36 @@ function generateForeignKeys(ctx, table) {
|
|
|
193
203
|
}
|
|
194
204
|
return keys;
|
|
195
205
|
}
|
|
196
|
-
function generateIndexes(ctx, table) {
|
|
206
|
+
function generateIndexes(ctx, table, namespace) {
|
|
197
207
|
const indexes = [];
|
|
198
208
|
for (const idx of Object.values(table.indexes)) {
|
|
199
209
|
const columns = idx.columns.map((col) => `table.${col.ormName}`).join(", ");
|
|
210
|
+
const indexName = namespace ? `${idx.name}_${namespace}` : idx.name;
|
|
200
211
|
if (idx.unique) {
|
|
201
212
|
ctx.imports.addImport("uniqueIndex", ctx.importSource);
|
|
202
|
-
indexes.push(`uniqueIndex("${
|
|
213
|
+
indexes.push(`uniqueIndex("${indexName}").on(${columns})`);
|
|
203
214
|
} else {
|
|
204
215
|
ctx.imports.addImport("index", ctx.importSource);
|
|
205
|
-
indexes.push(`index("${
|
|
216
|
+
indexes.push(`index("${indexName}").on(${columns})`);
|
|
206
217
|
}
|
|
207
218
|
}
|
|
208
219
|
return indexes;
|
|
209
220
|
}
|
|
210
|
-
function generateTableConstraints(ctx, table) {
|
|
211
|
-
return [...generateForeignKeys(ctx, table), ...generateIndexes(ctx, table)];
|
|
221
|
+
function generateTableConstraints(ctx, table, namespace) {
|
|
222
|
+
return [...generateForeignKeys(ctx, table, namespace), ...generateIndexes(ctx, table, namespace)];
|
|
212
223
|
}
|
|
213
|
-
function generateTable(ctx, table, customTypes) {
|
|
224
|
+
function generateTable(ctx, table, customTypes, namespace) {
|
|
214
225
|
const tableFn = PROVIDER_TABLE_FUNCTIONS[ctx.provider];
|
|
215
226
|
ctx.imports.addImport(tableFn, ctx.importSource);
|
|
216
227
|
const columns = generateAllColumns(ctx, table, customTypes);
|
|
217
|
-
const constraints = generateTableConstraints(ctx, table);
|
|
218
|
-
const
|
|
228
|
+
const constraints = generateTableConstraints(ctx, table, namespace);
|
|
229
|
+
const physicalTableName = namespace ? `${table.ormName}_${namespace}` : table.ormName;
|
|
230
|
+
const exportName = namespace ? `${table.ormName}_${sanitizeNamespace(namespace)}` : table.ormName;
|
|
231
|
+
const args = [`"${physicalTableName}"`, `{\n${columns.join(",\n")}\n}`];
|
|
219
232
|
if (constraints.length > 0) args.push(`(table) => [\n${ident(constraints.join(",\n"))}\n]`);
|
|
220
|
-
return `export const ${
|
|
233
|
+
return `export const ${exportName} = ${tableFn}(${args.join(", ")})`;
|
|
221
234
|
}
|
|
222
|
-
function generateRelation(ctx, table) {
|
|
235
|
+
function generateRelation(ctx, table, namespace) {
|
|
223
236
|
const relations = [];
|
|
224
237
|
let hasOne = false;
|
|
225
238
|
let hasMany = false;
|
|
@@ -230,14 +243,16 @@ function generateRelation(ctx, table) {
|
|
|
230
243
|
if (relation.type === "one") {
|
|
231
244
|
const fields = [];
|
|
232
245
|
const references = [];
|
|
246
|
+
const tableRef$1 = namespace ? `${table.ormName}_${sanitizeNamespace(namespace)}` : table.ormName;
|
|
247
|
+
const relatedTableRef = namespace ? `${relation.table.ormName}_${sanitizeNamespace(namespace)}` : relation.table.ormName;
|
|
233
248
|
for (const [left, right] of relation.on) {
|
|
234
|
-
fields.push(`${
|
|
249
|
+
fields.push(`${tableRef$1}.${left}`);
|
|
235
250
|
const actualRight = right === "id" ? "_internalId" : right;
|
|
236
|
-
references.push(`${
|
|
251
|
+
references.push(`${relatedTableRef}.${actualRight}`);
|
|
237
252
|
}
|
|
238
253
|
options.push(`fields: [${fields.join(", ")}]`, `references: [${references.join(", ")}]`);
|
|
239
254
|
}
|
|
240
|
-
const args = [relation.table.ormName];
|
|
255
|
+
const args = [namespace ? `${relation.table.ormName}_${sanitizeNamespace(namespace)}` : relation.table.ormName];
|
|
241
256
|
if (options.length > 0) args.push(`{\n${ident(options.join(",\n"))}\n}`);
|
|
242
257
|
relations.push(ident(`${relation.name}: ${relation.type}(${args.join(", ")})`));
|
|
243
258
|
}
|
|
@@ -246,26 +261,84 @@ function generateRelation(ctx, table) {
|
|
|
246
261
|
if (hasOne) params.push("one");
|
|
247
262
|
if (hasMany) params.push("many");
|
|
248
263
|
const relationParams = params.length > 0 ? `{ ${params.join(", ")} }` : "{}";
|
|
264
|
+
const tableRef = namespace ? `${table.ormName}_${sanitizeNamespace(namespace)}` : table.ormName;
|
|
265
|
+
const relationsName = namespace ? `${table.ormName}_${sanitizeNamespace(namespace)}Relations` : `${table.ormName}Relations`;
|
|
249
266
|
ctx.imports.addImport("relations", "drizzle-orm");
|
|
250
|
-
return `export const ${
|
|
267
|
+
return `export const ${relationsName} = relations(${tableRef}, (${relationParams}) => ({
|
|
251
268
|
${relations.join(",\n")}
|
|
252
269
|
}));`;
|
|
253
270
|
}
|
|
254
|
-
|
|
271
|
+
/**
|
|
272
|
+
* Sanitize a namespace to be a valid JavaScript identifier
|
|
273
|
+
* Replaces hyphens and other invalid characters with underscores
|
|
274
|
+
*/
|
|
275
|
+
function sanitizeNamespace(namespace) {
|
|
276
|
+
return namespace.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Generate a schema export object for a fragment
|
|
280
|
+
* This groups all tables by their logical names for easier access
|
|
281
|
+
*/
|
|
282
|
+
function generateFragmentSchemaExport(schema, namespace) {
|
|
283
|
+
const entries = [];
|
|
284
|
+
for (const table of Object.values(schema.tables)) {
|
|
285
|
+
const physicalExportName = namespace ? `${table.ormName}_${sanitizeNamespace(namespace)}` : table.ormName;
|
|
286
|
+
if (namespace) {
|
|
287
|
+
const physicalTableName = namespace ? `${table.ormName}_${namespace}` : table.ormName;
|
|
288
|
+
entries.push(` "${physicalTableName}": ${physicalExportName}`);
|
|
289
|
+
}
|
|
290
|
+
entries.push(` ${table.ormName}: ${physicalExportName}`);
|
|
291
|
+
}
|
|
292
|
+
entries.push(` schemaVersion: ${schema.version}`);
|
|
293
|
+
return `export const ${namespace ? `${sanitizeNamespace(namespace)}_schema` : "_schema"} = {\n${entries.join(",\n")}\n}`;
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Generate a settings table for storing fragment versions
|
|
297
|
+
*/
|
|
298
|
+
function generateSettingsTable(ctx) {
|
|
299
|
+
const settingsTable = settingsSchema.tables[SETTINGS_TABLE_NAME];
|
|
300
|
+
return generateTable(ctx, settingsTable, []);
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Generate a schema file from one or more fragments with a shared settings table
|
|
304
|
+
*/
|
|
305
|
+
function generateSchema(fragments, provider, options) {
|
|
255
306
|
const ctx = createContext(provider, options?.idGeneratorImport);
|
|
256
307
|
const customTypes = [];
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
308
|
+
const sections = [];
|
|
309
|
+
sections.push("");
|
|
310
|
+
sections.push("// ============================================================================");
|
|
311
|
+
sections.push("// Settings Table (shared across all fragments)");
|
|
312
|
+
sections.push("// ============================================================================");
|
|
313
|
+
sections.push("");
|
|
314
|
+
sections.push(generateSettingsTable(ctx));
|
|
315
|
+
sections.push("");
|
|
316
|
+
sections.push(`export const fragnoDbSettingSchemaVersion = ${settingsSchema.version};`);
|
|
317
|
+
for (const { namespace, schema } of fragments) {
|
|
318
|
+
const fragmentTables = [];
|
|
319
|
+
fragmentTables.push("");
|
|
320
|
+
fragmentTables.push("// ============================================================================");
|
|
321
|
+
fragmentTables.push(`// Fragment: ${namespace}`);
|
|
322
|
+
fragmentTables.push("// ============================================================================");
|
|
323
|
+
for (const table of Object.values(schema.tables)) {
|
|
324
|
+
const tableCode = generateTable(ctx, table, customTypes, namespace);
|
|
325
|
+
fragmentTables.push("");
|
|
326
|
+
fragmentTables.push(tableCode);
|
|
327
|
+
const relationCode = generateRelation(ctx, table, namespace);
|
|
328
|
+
if (relationCode) {
|
|
329
|
+
fragmentTables.push("");
|
|
330
|
+
fragmentTables.push(relationCode);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
fragmentTables.push("");
|
|
334
|
+
fragmentTables.push(generateFragmentSchemaExport(schema, namespace));
|
|
335
|
+
sections.push(...fragmentTables);
|
|
263
336
|
}
|
|
264
337
|
return [
|
|
265
338
|
ctx.imports.format(),
|
|
266
339
|
...customTypes,
|
|
267
|
-
...
|
|
268
|
-
].join("\n
|
|
340
|
+
...sections
|
|
341
|
+
].join("\n");
|
|
269
342
|
}
|
|
270
343
|
|
|
271
344
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate.js","names":["parts: string[]","params: string[]","value: string","keys: string[]","columns: string[]","foreignColumns: string[]","indexes: string[]","args: string[]","relations: string[]","options: string[]","fields: string[]","references: string[]","customTypes: string[]","tables: string[]"],"sources":["../../../src/adapters/drizzle/generate.ts"],"sourcesContent":["import { importGenerator } from \"../../util/import-generator\";\nimport { ident, parseVarchar } from \"../../util/parse\";\nimport {\n type AnyColumn,\n type AnySchema,\n type AnyTable,\n InternalIdColumn,\n} from \"../../schema/create\";\nimport type { SQLProvider } from \"../../shared/providers\";\nimport { schemaToDBType, type DBTypeLiteral } from \"../../schema/serialize\";\n\n// ============================================================================\n// PROVIDER CONFIGURATION\n// ============================================================================\n\nconst PROVIDER_IMPORTS = {\n mysql: \"drizzle-orm/mysql-core\",\n postgresql: \"drizzle-orm/pg-core\",\n sqlite: \"drizzle-orm/sqlite-core\",\n} as const;\n\nconst PROVIDER_TABLE_FUNCTIONS = {\n mysql: \"mysqlTable\",\n postgresql: \"pgTable\",\n sqlite: \"sqliteTable\",\n} as const;\n\nexport type SupportedProvider = Exclude<SQLProvider, \"cockroachdb\" | \"mssql\">;\n\n// ============================================================================\n// CONTEXT\n// ============================================================================\n\ninterface GeneratorContext {\n provider: SupportedProvider;\n imports: ReturnType<typeof importGenerator>;\n importSource: string;\n generatedCustomTypes: Set<string>;\n idGeneratorImport?: { name: string; from: string };\n}\n\nfunction createContext(\n provider: SupportedProvider,\n idGeneratorImport?: { name: string; from: string },\n): GeneratorContext {\n return {\n provider,\n imports: importGenerator(),\n importSource: PROVIDER_IMPORTS[provider],\n generatedCustomTypes: new Set<string>(),\n idGeneratorImport,\n };\n}\n\n// ============================================================================\n// CUSTOM TYPE GENERATION\n// ============================================================================\n\ninterface CustomTypeOptions {\n dataType: string;\n driverDataType: string;\n databaseDataType: string;\n fromDriverCode: string;\n toDriverCode: string;\n}\n\nfunction generateCustomType(\n ctx: GeneratorContext,\n name: string,\n options: CustomTypeOptions,\n): string | undefined {\n if (ctx.generatedCustomTypes.has(name)) {\n return undefined;\n }\n\n ctx.imports.addImport(\"customType\", ctx.importSource);\n ctx.generatedCustomTypes.add(name);\n\n return `const ${name} = customType<\n {\n data: ${options.dataType};\n driverData: ${options.driverDataType};\n }\n>({\n dataType() {\n return \"${options.databaseDataType}\";\n },\n fromDriver(value) {\n ${options.fromDriverCode}\n },\n toDriver(value) {\n ${options.toDriverCode}\n }\n});`;\n}\n\nfunction generateBinaryCustomType(ctx: GeneratorContext, customTypes: string[]): string {\n const name = \"customBinary\";\n const code = generateCustomType(ctx, name, {\n dataType: \"Uint8Array\",\n driverDataType: \"Buffer\",\n databaseDataType: schemaToDBType({ type: \"binary\" }, ctx.provider),\n fromDriverCode: \"return new Uint8Array(value.buffer, value.byteOffset, value.byteLength)\",\n toDriverCode: `return value instanceof Buffer? value : Buffer.from(value)`,\n });\n\n if (code) {\n customTypes.push(code);\n }\n return name;\n}\n\n// ============================================================================\n// COLUMN TYPE MAPPING\n// ============================================================================\n\ninterface ColumnTypeFunction {\n name: string;\n isCustomType?: boolean;\n params?: string[];\n}\n\n/**\n * Maps SQL database types to Drizzle function names and parameters.\n * Uses schemaToDBType as the source of truth for type conversion.\n */\nfunction getColumnTypeFunction(\n ctx: GeneratorContext,\n column: AnyColumn,\n customTypes: string[],\n): ColumnTypeFunction {\n // Get the canonical database type from schemaToDBType\n const dbType = schemaToDBType(column, ctx.provider);\n\n // Map database types to Drizzle function names\n return mapDBTypeToDrizzleFunction(ctx, dbType, column, customTypes);\n}\n\n/**\n * Maps a database type string to a Drizzle function name and parameters.\n */\nfunction mapDBTypeToDrizzleFunction(\n ctx: GeneratorContext,\n dbType: DBTypeLiteral,\n column: AnyColumn,\n customTypes: string[],\n): ColumnTypeFunction {\n // Handle provider-specific types\n if (ctx.provider === \"postgresql\") {\n switch (dbType) {\n case \"bigserial\":\n // bigserial requires a mode parameter in Drizzle\n return { name: \"bigserial\", params: [`{ mode: \"number\" }`] };\n case \"serial\":\n return { name: \"serial\" };\n case \"boolean\":\n return { name: \"boolean\" };\n case \"bytea\":\n return { name: generateBinaryCustomType(ctx, customTypes), isCustomType: true };\n case \"json\":\n return { name: \"json\" };\n case \"text\":\n return { name: \"text\" };\n case \"bigint\":\n return { name: \"bigint\", params: [`{ mode: \"number\" }`] };\n default:\n if (dbType.startsWith(\"varchar(\")) {\n const length = parseVarchar(dbType);\n return { name: \"varchar\", params: [`{ length: ${length} }`] };\n }\n return { name: dbType };\n }\n }\n\n if (ctx.provider === \"mysql\") {\n switch (dbType) {\n case \"boolean\":\n return { name: \"boolean\" };\n case \"text\":\n return { name: \"text\" };\n case \"longblob\":\n return { name: generateBinaryCustomType(ctx, customTypes), isCustomType: true };\n case \"bigint\":\n return { name: \"bigint\" };\n default:\n if (dbType.startsWith(\"varchar(\")) {\n const length = parseVarchar(dbType);\n return { name: \"varchar\", params: [`{ length: ${length} }`] };\n }\n return { name: dbType };\n }\n }\n\n if (ctx.provider === \"sqlite\") {\n switch (dbType) {\n case \"integer\":\n // Need to determine the mode based on the original column type\n if (column.type === \"bool\") {\n return { name: \"integer\", params: [`{ mode: \"boolean\" }`] };\n }\n if (column.type === \"timestamp\" || column.type === \"date\") {\n return { name: \"integer\", params: [`{ mode: \"timestamp\" }`] };\n }\n return { name: \"integer\" };\n case \"blob\":\n // Need to determine the mode based on the original column type\n if (column.type === \"bigint\") {\n return { name: \"blob\", params: [`{ mode: \"bigint\" }`] };\n }\n return { name: generateBinaryCustomType(ctx, customTypes), isCustomType: true };\n case \"text\":\n // Check if it's JSON\n if (column.type === \"json\") {\n return { name: \"blob\", params: [`{ mode: \"json\" }`] };\n }\n return { name: \"text\" };\n case \"real\":\n return { name: \"real\" };\n default:\n return { name: dbType };\n }\n }\n\n // Fallback for other providers\n return { name: dbType };\n}\n\n// ============================================================================\n// COLUMN GENERATION\n// ============================================================================\n\nfunction generateColumnDefinition(\n ctx: GeneratorContext,\n column: AnyColumn,\n customTypes: string[],\n): string {\n const parts: string[] = [];\n const typeFn = getColumnTypeFunction(ctx, column, customTypes);\n\n // Column type with parameters\n const params: string[] = [`\"${column.name}\"`, ...(typeFn.params ?? [])];\n if (!typeFn.isCustomType) {\n ctx.imports.addImport(typeFn.name, ctx.importSource);\n }\n parts.push(`${typeFn.name}(${params.join(\", \")})`);\n\n // Primary key for internal ID\n if (column instanceof InternalIdColumn || column.role === \"internal-id\") {\n parts.push(\"primaryKey()\");\n\n // Auto-increment based on provider\n // Note: PostgreSQL uses bigserial/serial which handle auto-increment automatically\n if (ctx.provider === \"mysql\" || ctx.provider === \"sqlite\") {\n parts.push(\"autoincrement()\");\n }\n }\n\n // Nullability\n if (!column.isNullable) {\n parts.push(\"notNull()\");\n }\n\n // Default values\n if (column.default) {\n if (\"value\" in column.default) {\n let value: string;\n if (typeof column.default.value === \"bigint\") {\n ctx.imports.addImport(\"sql\", \"drizzle-orm\");\n value = `sql\\`${column.default.value.toString()}\\``;\n } else {\n value = JSON.stringify(column.default.value);\n }\n parts.push(`default(${value})`);\n } else if (column.default.runtime === \"auto\") {\n const idGen = ctx.idGeneratorImport ?? { name: \"createId\", from: \"@fragno-dev/db/id\" };\n ctx.imports.addImport(idGen.name, idGen.from);\n parts.push(`$defaultFn(() => ${idGen.name}())`);\n } else if (column.default.runtime === \"now\") {\n parts.push(\"defaultNow()\");\n }\n }\n\n return ` ${column.ormName}: ${parts.join(\".\")}`;\n}\n\nfunction generateAllColumns(\n ctx: GeneratorContext,\n table: AnyTable,\n customTypes: string[],\n): string[] {\n return Object.values(table.columns).map((column) =>\n generateColumnDefinition(ctx, column, customTypes),\n );\n}\n\n// ============================================================================\n// CONSTRAINT GENERATION\n// ============================================================================\n\nfunction generateForeignKeys(ctx: GeneratorContext, table: AnyTable): string[] {\n const keys: string[] = [];\n\n for (const relation of Object.values(table.relations)) {\n // Only \"one\" relations generate foreign keys\n // \"many\" relations don't have foreign keys (they're on the other side)\n if (relation.type === \"many\") {\n continue;\n }\n\n const columns: string[] = [];\n const foreignColumns: string[] = [];\n const isSelfReference = relation.table.ormName === table.ormName;\n\n for (const [localCol, refCol] of relation.on) {\n columns.push(`table.${localCol}`);\n // Foreign keys always reference internal IDs\n const actualRefCol = refCol === \"id\" ? \"_internalId\" : refCol;\n // For self-referencing foreign keys, use table parameter instead of table constant\n if (isSelfReference) {\n foreignColumns.push(`table.${actualRefCol}`);\n } else {\n foreignColumns.push(`${relation.table.ormName}.${actualRefCol}`);\n }\n }\n\n ctx.imports.addImport(\"foreignKey\", ctx.importSource);\n const fkName = `${table.ormName}_${relation.table.ormName}_${relation.name}_fk`;\n\n keys.push(`foreignKey({\n columns: [${columns.join(\", \")}],\n foreignColumns: [${foreignColumns.join(\", \")}],\n name: \"${fkName}\"\n})`);\n }\n\n return keys;\n}\n\nfunction generateIndexes(ctx: GeneratorContext, table: AnyTable): string[] {\n const indexes: string[] = [];\n\n for (const idx of Object.values(table.indexes)) {\n const columns = idx.columns.map((col) => `table.${col.ormName}`).join(\", \");\n\n if (idx.unique) {\n ctx.imports.addImport(\"uniqueIndex\", ctx.importSource);\n indexes.push(`uniqueIndex(\"${idx.name}\").on(${columns})`);\n } else {\n ctx.imports.addImport(\"index\", ctx.importSource);\n indexes.push(`index(\"${idx.name}\").on(${columns})`);\n }\n }\n\n return indexes;\n}\n\nfunction generateTableConstraints(ctx: GeneratorContext, table: AnyTable): string[] {\n return [...generateForeignKeys(ctx, table), ...generateIndexes(ctx, table)];\n}\n\n// ============================================================================\n// TABLE GENERATION\n// ============================================================================\n\nfunction generateTable(ctx: GeneratorContext, table: AnyTable, customTypes: string[]): string {\n const tableFn = PROVIDER_TABLE_FUNCTIONS[ctx.provider];\n ctx.imports.addImport(tableFn, ctx.importSource);\n\n const columns = generateAllColumns(ctx, table, customTypes);\n const constraints = generateTableConstraints(ctx, table);\n\n const args: string[] = [`\"${table.ormName}\"`, `{\\n${columns.join(\",\\n\")}\\n}`];\n\n if (constraints.length > 0) {\n args.push(`(table) => [\\n${ident(constraints.join(\",\\n\"))}\\n]`);\n }\n\n return `export const ${table.ormName} = ${tableFn}(${args.join(\", \")})`;\n}\n\n// ============================================================================\n// RELATION GENERATION\n// ============================================================================\n\nfunction generateRelation(ctx: GeneratorContext, table: AnyTable): string | undefined {\n const relations: string[] = [];\n let hasOne = false;\n let hasMany = false;\n\n for (const relation of Object.values(table.relations)) {\n const options: string[] = [`relationName: \"${relation.id}\"`];\n\n // Track which relation types are used\n if (relation.type === \"one\") {\n hasOne = true;\n } else if (relation.type === \"many\") {\n hasMany = true;\n }\n\n // For \"one\" relations, specify fields and references\n if (relation.type === \"one\") {\n const fields: string[] = [];\n const references: string[] = [];\n\n for (const [left, right] of relation.on) {\n fields.push(`${table.ormName}.${left}`);\n // Relations reference internal IDs\n const actualRight = right === \"id\" ? \"_internalId\" : right;\n references.push(`${relation.table.ormName}.${actualRight}`);\n }\n\n options.push(`fields: [${fields.join(\", \")}]`, `references: [${references.join(\", \")}]`);\n }\n\n const args: string[] = [relation.table.ormName];\n if (options.length > 0) {\n args.push(`{\\n${ident(options.join(\",\\n\"))}\\n}`);\n }\n\n relations.push(ident(`${relation.name}: ${relation.type}(${args.join(\", \")})`));\n }\n\n if (relations.length === 0) {\n return undefined;\n }\n\n // Only include the relation types that are actually used\n const params: string[] = [];\n if (hasOne) {\n params.push(\"one\");\n }\n if (hasMany) {\n params.push(\"many\");\n }\n const relationParams = params.length > 0 ? `{ ${params.join(\", \")} }` : \"{}\";\n\n ctx.imports.addImport(\"relations\", \"drizzle-orm\");\n return `export const ${table.ormName}Relations = relations(${table.ormName}, (${relationParams}) => ({\n${relations.join(\",\\n\")}\n}));`;\n}\n\n// ============================================================================\n// MAIN GENERATION\n// ============================================================================\n\nexport interface GenerateSchemaOptions {\n /** Custom ID generator import configuration */\n idGeneratorImport?: {\n /** Function name to import */\n name: string;\n /** Module to import from */\n from: string;\n };\n}\n\nexport function generateSchema(\n schema: AnySchema,\n provider: SupportedProvider,\n options?: GenerateSchemaOptions,\n): string {\n const ctx = createContext(provider, options?.idGeneratorImport);\n const customTypes: string[] = [];\n const tables: string[] = [];\n\n // Generate tables and collect custom types\n for (const table of Object.values(schema.tables)) {\n // Custom types might be generated during column processing\n const tableCode = generateTable(ctx, table, customTypes);\n tables.push(tableCode);\n\n const relationCode = generateRelation(ctx, table);\n if (relationCode) {\n tables.push(relationCode);\n }\n }\n\n // Assemble final output\n const lines: string[] = [ctx.imports.format(), ...customTypes, ...tables];\n return lines.join(\"\\n\\n\");\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"generate.js","names":["parts: string[]","params: string[]","value: string","keys: string[]","columns: string[]","foreignColumns: string[]","indexes: string[]","args: string[]","relations: string[]","options: string[]","fields: string[]","references: string[]","tableRef","entries: string[]","customTypes: string[]","sections: string[]","fragmentTables: string[]"],"sources":["../../../src/adapters/drizzle/generate.ts"],"sourcesContent":["import { importGenerator } from \"../../util/import-generator\";\nimport { ident, parseVarchar } from \"../../util/parse\";\nimport {\n type AnyColumn,\n type AnySchema,\n type AnyTable,\n InternalIdColumn,\n} from \"../../schema/create\";\nimport type { SQLProvider } from \"../../shared/providers\";\nimport { schemaToDBType, type DBTypeLiteral } from \"../../schema/serialize\";\nimport { createTableNameMapper } from \"./shared\";\nimport { settingsSchema, SETTINGS_TABLE_NAME } from \"../../shared/settings-schema\";\n\n// ============================================================================\n// PROVIDER CONFIGURATION\n// ============================================================================\n\nconst PROVIDER_IMPORTS = {\n mysql: \"drizzle-orm/mysql-core\",\n postgresql: \"drizzle-orm/pg-core\",\n sqlite: \"drizzle-orm/sqlite-core\",\n} as const;\n\nconst PROVIDER_TABLE_FUNCTIONS = {\n mysql: \"mysqlTable\",\n postgresql: \"pgTable\",\n sqlite: \"sqliteTable\",\n} as const;\n\nexport type SupportedProvider = Exclude<SQLProvider, \"cockroachdb\" | \"mssql\">;\n\n// ============================================================================\n// CONTEXT\n// ============================================================================\n\ninterface GeneratorContext {\n provider: SupportedProvider;\n imports: ReturnType<typeof importGenerator>;\n importSource: string;\n generatedCustomTypes: Set<string>;\n idGeneratorImport?: { name: string; from: string };\n}\n\nfunction createContext(\n provider: SupportedProvider,\n idGeneratorImport?: { name: string; from: string },\n): GeneratorContext {\n return {\n provider,\n imports: importGenerator(),\n importSource: PROVIDER_IMPORTS[provider],\n generatedCustomTypes: new Set<string>(),\n idGeneratorImport,\n };\n}\n\n// ============================================================================\n// CUSTOM TYPE GENERATION\n// ============================================================================\n\ninterface CustomTypeOptions {\n dataType: string;\n driverDataType: string;\n databaseDataType: string;\n fromDriverCode: string;\n toDriverCode: string;\n}\n\nfunction generateCustomType(\n ctx: GeneratorContext,\n name: string,\n options: CustomTypeOptions,\n): string | undefined {\n if (ctx.generatedCustomTypes.has(name)) {\n return undefined;\n }\n\n ctx.imports.addImport(\"customType\", ctx.importSource);\n ctx.generatedCustomTypes.add(name);\n\n return `const ${name} = customType<\n {\n data: ${options.dataType};\n driverData: ${options.driverDataType};\n }\n>({\n dataType() {\n return \"${options.databaseDataType}\";\n },\n fromDriver(value) {\n ${options.fromDriverCode}\n },\n toDriver(value) {\n ${options.toDriverCode}\n }\n});`;\n}\n\nfunction generateBinaryCustomType(ctx: GeneratorContext, customTypes: string[]): string {\n const name = \"customBinary\";\n const code = generateCustomType(ctx, name, {\n dataType: \"Uint8Array\",\n driverDataType: \"Buffer\",\n databaseDataType: schemaToDBType({ type: \"binary\" }, ctx.provider),\n fromDriverCode: \"return new Uint8Array(value.buffer, value.byteOffset, value.byteLength)\",\n toDriverCode: `return value instanceof Buffer? value : Buffer.from(value)`,\n });\n\n if (code) {\n customTypes.push(code);\n }\n return name;\n}\n\n// ============================================================================\n// COLUMN TYPE MAPPING\n// ============================================================================\n\ninterface ColumnTypeFunction {\n name: string;\n isCustomType?: boolean;\n params?: string[];\n}\n\n/**\n * Maps SQL database types to Drizzle function names and parameters.\n * Uses schemaToDBType as the source of truth for type conversion.\n */\nfunction getColumnTypeFunction(\n ctx: GeneratorContext,\n column: AnyColumn,\n customTypes: string[],\n): ColumnTypeFunction {\n // Get the canonical database type from schemaToDBType\n const dbType = schemaToDBType(column, ctx.provider);\n\n // Map database types to Drizzle function names\n return mapDBTypeToDrizzleFunction(ctx, dbType, column, customTypes);\n}\n\n/**\n * Maps a database type string to a Drizzle function name and parameters.\n */\nfunction mapDBTypeToDrizzleFunction(\n ctx: GeneratorContext,\n dbType: DBTypeLiteral,\n column: AnyColumn,\n customTypes: string[],\n): ColumnTypeFunction {\n // Handle provider-specific types\n if (ctx.provider === \"postgresql\") {\n switch (dbType) {\n case \"bigserial\":\n // bigserial requires a mode parameter in Drizzle\n return { name: \"bigserial\", params: [`{ mode: \"number\" }`] };\n case \"serial\":\n return { name: \"serial\" };\n case \"boolean\":\n return { name: \"boolean\" };\n case \"bytea\":\n return { name: generateBinaryCustomType(ctx, customTypes), isCustomType: true };\n case \"json\":\n return { name: \"json\" };\n case \"text\":\n return { name: \"text\" };\n case \"bigint\":\n return { name: \"bigint\", params: [`{ mode: \"number\" }`] };\n default:\n if (dbType.startsWith(\"varchar(\")) {\n const length = parseVarchar(dbType);\n return { name: \"varchar\", params: [`{ length: ${length} }`] };\n }\n return { name: dbType };\n }\n }\n\n if (ctx.provider === \"mysql\") {\n switch (dbType) {\n case \"boolean\":\n return { name: \"boolean\" };\n case \"text\":\n return { name: \"text\" };\n case \"longblob\":\n return { name: generateBinaryCustomType(ctx, customTypes), isCustomType: true };\n case \"bigint\":\n return { name: \"bigint\" };\n default:\n if (dbType.startsWith(\"varchar(\")) {\n const length = parseVarchar(dbType);\n return { name: \"varchar\", params: [`{ length: ${length} }`] };\n }\n return { name: dbType };\n }\n }\n\n if (ctx.provider === \"sqlite\") {\n switch (dbType) {\n case \"integer\":\n // Need to determine the mode based on the original column type\n if (column.type === \"bool\") {\n return { name: \"integer\", params: [`{ mode: \"boolean\" }`] };\n }\n if (column.type === \"timestamp\" || column.type === \"date\") {\n return { name: \"integer\", params: [`{ mode: \"timestamp\" }`] };\n }\n return { name: \"integer\" };\n case \"blob\":\n // Need to determine the mode based on the original column type\n if (column.type === \"bigint\") {\n return { name: \"blob\", params: [`{ mode: \"bigint\" }`] };\n }\n return { name: generateBinaryCustomType(ctx, customTypes), isCustomType: true };\n case \"text\":\n // Check if it's JSON\n if (column.type === \"json\") {\n return { name: \"blob\", params: [`{ mode: \"json\" }`] };\n }\n return { name: \"text\" };\n case \"real\":\n return { name: \"real\" };\n default:\n return { name: dbType };\n }\n }\n\n // Fallback for other providers\n return { name: dbType };\n}\n\n// ============================================================================\n// COLUMN GENERATION\n// ============================================================================\n\nfunction generateColumnDefinition(\n ctx: GeneratorContext,\n column: AnyColumn,\n customTypes: string[],\n): string {\n const parts: string[] = [];\n const typeFn = getColumnTypeFunction(ctx, column, customTypes);\n\n // Column type with parameters\n const params: string[] = [`\"${column.name}\"`, ...(typeFn.params ?? [])];\n if (!typeFn.isCustomType) {\n ctx.imports.addImport(typeFn.name, ctx.importSource);\n }\n parts.push(`${typeFn.name}(${params.join(\", \")})`);\n\n // Primary key for internal ID\n if (column instanceof InternalIdColumn || column.role === \"internal-id\") {\n parts.push(\"primaryKey()\");\n\n // Auto-increment based on provider\n // Note: PostgreSQL uses bigserial/serial which handle auto-increment automatically\n if (ctx.provider === \"mysql\" || ctx.provider === \"sqlite\") {\n parts.push(\"autoincrement()\");\n }\n }\n\n // Nullability\n if (!column.isNullable) {\n parts.push(\"notNull()\");\n }\n\n // Default values\n if (column.default) {\n if (\"value\" in column.default) {\n // Static defaults: defaultTo(value)\n let value: string;\n if (typeof column.default.value === \"bigint\") {\n ctx.imports.addImport(\"sql\", \"drizzle-orm\");\n value = `sql\\`${column.default.value.toString()}\\``;\n } else {\n value = JSON.stringify(column.default.value);\n }\n parts.push(`default(${value})`);\n } else if (\"dbSpecial\" in column.default) {\n // Database-level special functions: defaultTo(b => b.now())\n if (column.default.dbSpecial === \"now\") {\n parts.push(\"defaultNow()\");\n }\n } else if (\"runtime\" in column.default) {\n // Runtime defaults: defaultTo$()\n if (column.default.runtime === \"cuid\") {\n const idGen = ctx.idGeneratorImport ?? { name: \"createId\", from: \"@fragno-dev/db/id\" };\n ctx.imports.addImport(idGen.name, idGen.from);\n parts.push(`$defaultFn(() => ${idGen.name}())`);\n } else if (column.default.runtime === \"now\") {\n // Runtime-generated timestamp (not database-level)\n parts.push(\"$defaultFn(() => new Date())\");\n }\n // Note: Custom functions in defaultTo$(() => ...) are not supported in schema generation\n }\n }\n\n return ` ${column.ormName}: ${parts.join(\".\")}`;\n}\n\nfunction generateAllColumns(\n ctx: GeneratorContext,\n table: AnyTable,\n customTypes: string[],\n): string[] {\n return Object.values(table.columns).map((column) =>\n generateColumnDefinition(ctx, column, customTypes),\n );\n}\n\n// ============================================================================\n// CONSTRAINT GENERATION\n// ============================================================================\n\nfunction generateForeignKeys(ctx: GeneratorContext, table: AnyTable, namespace?: string): string[] {\n const mapper = namespace ? createTableNameMapper(namespace) : undefined;\n const keys: string[] = [];\n\n for (const relation of Object.values(table.relations)) {\n // Only \"one\" relations generate foreign keys\n // \"many\" relations don't have foreign keys (they're on the other side)\n if (relation.type === \"many\") {\n continue;\n }\n\n const columns: string[] = [];\n const foreignColumns: string[] = [];\n const isSelfReference = relation.table.ormName === table.ormName;\n\n for (const [localCol, refCol] of relation.on) {\n columns.push(`table.${localCol}`);\n // Foreign keys always reference internal IDs\n const actualRefCol = refCol === \"id\" ? \"_internalId\" : refCol;\n // For self-referencing foreign keys, use table parameter instead of table constant\n if (isSelfReference) {\n foreignColumns.push(`table.${actualRefCol}`);\n } else {\n // Suffix the foreign table reference with namespace if provided\n const foreignTableRef =\n mapper && namespace ? mapper.toPhysical(relation.table.ormName) : relation.table.ormName;\n foreignColumns.push(`${foreignTableRef}.${actualRefCol}`);\n }\n }\n\n ctx.imports.addImport(\"foreignKey\", ctx.importSource);\n // Include namespace in FK name to avoid collisions\n const fkName =\n namespace && mapper\n ? \"fk_\" + mapper.toPhysical(`${table.ormName}_${relation.table.ormName}_${relation.name}`)\n : `${table.ormName}_${relation.table.ormName}_${relation.name}_fk`;\n\n keys.push(`foreignKey({\n columns: [${columns.join(\", \")}],\n foreignColumns: [${foreignColumns.join(\", \")}],\n name: \"${fkName}\"\n})`);\n }\n\n return keys;\n}\n\nfunction generateIndexes(ctx: GeneratorContext, table: AnyTable, namespace?: string): string[] {\n const indexes: string[] = [];\n\n for (const idx of Object.values(table.indexes)) {\n const columns = idx.columns.map((col) => `table.${col.ormName}`).join(\", \");\n\n // Include namespace in index name to avoid collisions\n const indexName = namespace ? `${idx.name}_${namespace}` : idx.name;\n\n if (idx.unique) {\n ctx.imports.addImport(\"uniqueIndex\", ctx.importSource);\n indexes.push(`uniqueIndex(\"${indexName}\").on(${columns})`);\n } else {\n ctx.imports.addImport(\"index\", ctx.importSource);\n indexes.push(`index(\"${indexName}\").on(${columns})`);\n }\n }\n\n return indexes;\n}\n\nfunction generateTableConstraints(\n ctx: GeneratorContext,\n table: AnyTable,\n namespace?: string,\n): string[] {\n return [...generateForeignKeys(ctx, table, namespace), ...generateIndexes(ctx, table, namespace)];\n}\n\n// ============================================================================\n// TABLE GENERATION\n// ============================================================================\n\nfunction generateTable(\n ctx: GeneratorContext,\n table: AnyTable,\n customTypes: string[],\n namespace?: string,\n): string {\n const tableFn = PROVIDER_TABLE_FUNCTIONS[ctx.provider];\n ctx.imports.addImport(tableFn, ctx.importSource);\n\n const columns = generateAllColumns(ctx, table, customTypes);\n const constraints = generateTableConstraints(ctx, table, namespace);\n\n // Suffix table name with namespace if provided\n const physicalTableName = namespace ? `${table.ormName}_${namespace}` : table.ormName;\n // Sanitize namespace for use in export name (valid JS identifier)\n const exportName = namespace ? `${table.ormName}_${sanitizeNamespace(namespace)}` : table.ormName;\n\n const args: string[] = [`\"${physicalTableName}\"`, `{\\n${columns.join(\",\\n\")}\\n}`];\n\n if (constraints.length > 0) {\n args.push(`(table) => [\\n${ident(constraints.join(\",\\n\"))}\\n]`);\n }\n\n return `export const ${exportName} = ${tableFn}(${args.join(\", \")})`;\n}\n\n// ============================================================================\n// RELATION GENERATION\n// ============================================================================\n\nfunction generateRelation(\n ctx: GeneratorContext,\n table: AnyTable,\n namespace?: string,\n): string | undefined {\n const relations: string[] = [];\n let hasOne = false;\n let hasMany = false;\n\n for (const relation of Object.values(table.relations)) {\n const options: string[] = [`relationName: \"${relation.id}\"`];\n\n // Track which relation types are used\n if (relation.type === \"one\") {\n hasOne = true;\n } else if (relation.type === \"many\") {\n hasMany = true;\n }\n\n // For \"one\" relations, specify fields and references\n if (relation.type === \"one\") {\n const fields: string[] = [];\n const references: string[] = [];\n\n // Use sanitized namespace for identifier references\n const tableRef = namespace\n ? `${table.ormName}_${sanitizeNamespace(namespace)}`\n : table.ormName;\n const relatedTableRef = namespace\n ? `${relation.table.ormName}_${sanitizeNamespace(namespace)}`\n : relation.table.ormName;\n\n for (const [left, right] of relation.on) {\n fields.push(`${tableRef}.${left}`);\n // Relations reference internal IDs\n const actualRight = right === \"id\" ? \"_internalId\" : right;\n references.push(`${relatedTableRef}.${actualRight}`);\n }\n\n options.push(`fields: [${fields.join(\", \")}]`, `references: [${references.join(\", \")}]`);\n }\n\n const relatedTableRef = namespace\n ? `${relation.table.ormName}_${sanitizeNamespace(namespace)}`\n : relation.table.ormName;\n\n const args: string[] = [relatedTableRef];\n if (options.length > 0) {\n args.push(`{\\n${ident(options.join(\",\\n\"))}\\n}`);\n }\n\n relations.push(ident(`${relation.name}: ${relation.type}(${args.join(\", \")})`));\n }\n\n if (relations.length === 0) {\n return undefined;\n }\n\n // Only include the relation types that are actually used\n const params: string[] = [];\n if (hasOne) {\n params.push(\"one\");\n }\n if (hasMany) {\n params.push(\"many\");\n }\n const relationParams = params.length > 0 ? `{ ${params.join(\", \")} }` : \"{}\";\n\n const tableRef = namespace ? `${table.ormName}_${sanitizeNamespace(namespace)}` : table.ormName;\n const relationsName = namespace\n ? `${table.ormName}_${sanitizeNamespace(namespace)}Relations`\n : `${table.ormName}Relations`;\n\n ctx.imports.addImport(\"relations\", \"drizzle-orm\");\n return `export const ${relationsName} = relations(${tableRef}, (${relationParams}) => ({\n${relations.join(\",\\n\")}\n}));`;\n}\n\n// ============================================================================\n// UTILITIES\n// ============================================================================\n\n/**\n * Sanitize a namespace to be a valid JavaScript identifier\n * Replaces hyphens and other invalid characters with underscores\n */\nfunction sanitizeNamespace(namespace: string): string {\n return namespace.replace(/[^a-zA-Z0-9_]/g, \"_\");\n}\n\n/**\n * Generate a schema export object for a fragment\n * This groups all tables by their logical names for easier access\n */\nfunction generateFragmentSchemaExport(schema: AnySchema, namespace: string): string {\n const entries: string[] = [];\n\n for (const table of Object.values(schema.tables)) {\n const physicalExportName = namespace\n ? `${table.ormName}_${sanitizeNamespace(namespace)}`\n : table.ormName;\n\n if (namespace) {\n const physicalTableName = namespace ? `${table.ormName}_${namespace}` : table.ormName;\n // Use physical table name as key for Drizzle schema lookups\n entries.push(` \"${physicalTableName}\": ${physicalExportName}`);\n }\n\n // Also provide logical name for convenience\n entries.push(` ${table.ormName}: ${physicalExportName}`);\n }\n\n // Add schema version as a number\n entries.push(` schemaVersion: ${schema.version}`);\n\n const exportName = namespace ? `${sanitizeNamespace(namespace)}_schema` : \"_schema\";\n\n return `export const ${exportName} = {\\n${entries.join(\",\\n\")}\\n}`;\n}\n\n// ============================================================================\n// MAIN GENERATION\n// ============================================================================\n\nexport interface GenerateSchemaOptions {\n /** Custom ID generator import configuration */\n idGeneratorImport?: {\n /** Function name to import */\n name: string;\n /** Module to import from */\n from: string;\n };\n}\n\n/**\n * Generate a settings table for storing fragment versions\n */\nfunction generateSettingsTable(ctx: GeneratorContext): string {\n // Use centralized settings schema\n\n // Extract the table from the schema\n const settingsTable =\n settingsSchema.tables[SETTINGS_TABLE_NAME as keyof typeof settingsSchema.tables];\n\n // Generate the table using the existing generateTable function\n const customTypes: string[] = [];\n return generateTable(ctx, settingsTable, customTypes);\n}\n\n/**\n * Generate a schema file from one or more fragments with a shared settings table\n */\nexport function generateSchema(\n fragments: { namespace: string; schema: AnySchema }[],\n provider: SupportedProvider,\n options?: GenerateSchemaOptions,\n): string {\n const ctx = createContext(provider, options?.idGeneratorImport);\n const customTypes: string[] = [];\n const sections: string[] = [];\n\n // Generate settings table first\n sections.push(\"\");\n sections.push(\"// ============================================================================\");\n sections.push(\"// Settings Table (shared across all fragments)\");\n sections.push(\"// ============================================================================\");\n sections.push(\"\");\n sections.push(generateSettingsTable(ctx));\n sections.push(\"\");\n sections.push(`export const fragnoDbSettingSchemaVersion = ${settingsSchema.version};`);\n\n // Generate each fragment's tables\n for (const { namespace, schema } of fragments) {\n const fragmentTables: string[] = [];\n\n // Add section header\n fragmentTables.push(\"\");\n fragmentTables.push(\n \"// ============================================================================\",\n );\n fragmentTables.push(`// Fragment: ${namespace}`);\n fragmentTables.push(\n \"// ============================================================================\",\n );\n\n // Generate tables for this fragment\n for (const table of Object.values(schema.tables)) {\n const tableCode = generateTable(ctx, table, customTypes, namespace);\n fragmentTables.push(\"\");\n fragmentTables.push(tableCode);\n\n const relationCode = generateRelation(ctx, table, namespace);\n if (relationCode) {\n fragmentTables.push(\"\");\n fragmentTables.push(relationCode);\n }\n }\n\n // Generate schema export object\n fragmentTables.push(\"\");\n fragmentTables.push(generateFragmentSchemaExport(schema, namespace));\n\n sections.push(...fragmentTables);\n }\n\n // Assemble final output\n const lines: string[] = [ctx.imports.format(), ...customTypes, ...sections];\n return lines.join(\"\\n\");\n}\n"],"mappings":"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"}
|
|
@@ -5,7 +5,20 @@ function parseDrizzle(drizzle) {
|
|
|
5
5
|
if (!drizzleTables || Object.keys(drizzleTables).length === 0) throw new Error("Drizzle adapter requires query mode, make sure to configure it following their guide: https://orm.drizzle.team/docs/rqb.");
|
|
6
6
|
return [db, drizzleTables];
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* Creates a table name mapper for a given namespace.
|
|
10
|
+
* Physical names have format: {logicalName}_{namespace}
|
|
11
|
+
*/
|
|
12
|
+
function createTableNameMapper(namespace) {
|
|
13
|
+
return {
|
|
14
|
+
toPhysical: (logicalName) => `${logicalName}_${namespace}`,
|
|
15
|
+
toLogical: (physicalName) => {
|
|
16
|
+
if (physicalName.endsWith(`_${namespace}`)) return physicalName.slice(0, -(namespace.length + 1));
|
|
17
|
+
return physicalName;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
8
21
|
|
|
9
22
|
//#endregion
|
|
10
|
-
export { parseDrizzle };
|
|
23
|
+
export { createTableNameMapper, parseDrizzle };
|
|
11
24
|
//# sourceMappingURL=shared.js.map
|