@fragno-dev/db 0.1.13 → 0.1.15

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.
Files changed (178) hide show
  1. package/.turbo/turbo-build.log +179 -132
  2. package/CHANGELOG.md +30 -0
  3. package/dist/adapters/adapters.d.ts +27 -1
  4. package/dist/adapters/adapters.d.ts.map +1 -1
  5. package/dist/adapters/adapters.js.map +1 -1
  6. package/dist/adapters/drizzle/drizzle-adapter.d.ts +5 -1
  7. package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +1 -1
  8. package/dist/adapters/drizzle/drizzle-adapter.js +15 -3
  9. package/dist/adapters/drizzle/drizzle-adapter.js.map +1 -1
  10. package/dist/adapters/drizzle/drizzle-query.js +7 -5
  11. package/dist/adapters/drizzle/drizzle-query.js.map +1 -1
  12. package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts +0 -1
  13. package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts.map +1 -1
  14. package/dist/adapters/drizzle/drizzle-uow-compiler.js +76 -44
  15. package/dist/adapters/drizzle/drizzle-uow-compiler.js.map +1 -1
  16. package/dist/adapters/drizzle/drizzle-uow-decoder.js +23 -16
  17. package/dist/adapters/drizzle/drizzle-uow-decoder.js.map +1 -1
  18. package/dist/adapters/drizzle/drizzle-uow-executor.js +18 -7
  19. package/dist/adapters/drizzle/drizzle-uow-executor.js.map +1 -1
  20. package/dist/adapters/drizzle/generate.d.ts +4 -1
  21. package/dist/adapters/drizzle/generate.d.ts.map +1 -1
  22. package/dist/adapters/drizzle/generate.js +11 -18
  23. package/dist/adapters/drizzle/generate.js.map +1 -1
  24. package/dist/adapters/drizzle/shared.d.ts +14 -1
  25. package/dist/adapters/drizzle/shared.d.ts.map +1 -0
  26. package/dist/adapters/kysely/kysely-adapter.d.ts +5 -1
  27. package/dist/adapters/kysely/kysely-adapter.d.ts.map +1 -1
  28. package/dist/adapters/kysely/kysely-adapter.js +14 -3
  29. package/dist/adapters/kysely/kysely-adapter.js.map +1 -1
  30. package/dist/adapters/kysely/kysely-query-builder.js +1 -1
  31. package/dist/adapters/kysely/kysely-query-compiler.js +3 -2
  32. package/dist/adapters/kysely/kysely-query-compiler.js.map +1 -1
  33. package/dist/adapters/kysely/kysely-query.d.ts +1 -0
  34. package/dist/adapters/kysely/kysely-query.d.ts.map +1 -1
  35. package/dist/adapters/kysely/kysely-query.js +28 -19
  36. package/dist/adapters/kysely/kysely-query.js.map +1 -1
  37. package/dist/adapters/kysely/kysely-shared.d.ts +14 -0
  38. package/dist/adapters/kysely/kysely-shared.d.ts.map +1 -0
  39. package/dist/adapters/kysely/kysely-shared.js +16 -1
  40. package/dist/adapters/kysely/kysely-shared.js.map +1 -1
  41. package/dist/adapters/kysely/kysely-uow-compiler.js +68 -16
  42. package/dist/adapters/kysely/kysely-uow-compiler.js.map +1 -1
  43. package/dist/adapters/kysely/kysely-uow-executor.js +8 -4
  44. package/dist/adapters/kysely/kysely-uow-executor.js.map +1 -1
  45. package/dist/adapters/kysely/migration/execute-base.js +1 -1
  46. package/dist/adapters/kysely/migration/execute-base.js.map +1 -1
  47. package/dist/db-fragment-definition-builder.d.ts +152 -0
  48. package/dist/db-fragment-definition-builder.d.ts.map +1 -0
  49. package/dist/db-fragment-definition-builder.js +137 -0
  50. package/dist/db-fragment-definition-builder.js.map +1 -0
  51. package/dist/fragments/internal-fragment.d.ts +19 -0
  52. package/dist/fragments/internal-fragment.d.ts.map +1 -0
  53. package/dist/fragments/internal-fragment.js +39 -0
  54. package/dist/fragments/internal-fragment.js.map +1 -0
  55. package/dist/migration-engine/generation-engine.d.ts.map +1 -1
  56. package/dist/migration-engine/generation-engine.js +35 -15
  57. package/dist/migration-engine/generation-engine.js.map +1 -1
  58. package/dist/mod.d.ts +8 -18
  59. package/dist/mod.d.ts.map +1 -1
  60. package/dist/mod.js +7 -34
  61. package/dist/mod.js.map +1 -1
  62. package/dist/node_modules/.pnpm/rou3@0.7.8/node_modules/rou3/dist/index.js +165 -0
  63. package/dist/node_modules/.pnpm/rou3@0.7.8/node_modules/rou3/dist/index.js.map +1 -0
  64. package/dist/packages/fragno/dist/api/bind-services.js +20 -0
  65. package/dist/packages/fragno/dist/api/bind-services.js.map +1 -0
  66. package/dist/packages/fragno/dist/api/error.js +48 -0
  67. package/dist/packages/fragno/dist/api/error.js.map +1 -0
  68. package/dist/packages/fragno/dist/api/fragment-definition-builder.js +320 -0
  69. package/dist/packages/fragno/dist/api/fragment-definition-builder.js.map +1 -0
  70. package/dist/packages/fragno/dist/api/fragment-instantiator.js +487 -0
  71. package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +1 -0
  72. package/dist/packages/fragno/dist/api/fragno-response.js +73 -0
  73. package/dist/packages/fragno/dist/api/fragno-response.js.map +1 -0
  74. package/dist/packages/fragno/dist/api/internal/response-stream.js +81 -0
  75. package/dist/packages/fragno/dist/api/internal/response-stream.js.map +1 -0
  76. package/dist/packages/fragno/dist/api/internal/route.js +10 -0
  77. package/dist/packages/fragno/dist/api/internal/route.js.map +1 -0
  78. package/dist/packages/fragno/dist/api/mutable-request-state.js +97 -0
  79. package/dist/packages/fragno/dist/api/mutable-request-state.js.map +1 -0
  80. package/dist/packages/fragno/dist/api/request-context-storage.js +43 -0
  81. package/dist/packages/fragno/dist/api/request-context-storage.js.map +1 -0
  82. package/dist/packages/fragno/dist/api/request-input-context.js +118 -0
  83. package/dist/packages/fragno/dist/api/request-input-context.js.map +1 -0
  84. package/dist/packages/fragno/dist/api/request-middleware.js +83 -0
  85. package/dist/packages/fragno/dist/api/request-middleware.js.map +1 -0
  86. package/dist/packages/fragno/dist/api/request-output-context.js +119 -0
  87. package/dist/packages/fragno/dist/api/request-output-context.js.map +1 -0
  88. package/dist/packages/fragno/dist/api/route.js +17 -0
  89. package/dist/packages/fragno/dist/api/route.js.map +1 -0
  90. package/dist/packages/fragno/dist/internal/symbols.js +10 -0
  91. package/dist/packages/fragno/dist/internal/symbols.js.map +1 -0
  92. package/dist/query/cursor.d.ts +10 -2
  93. package/dist/query/cursor.d.ts.map +1 -1
  94. package/dist/query/cursor.js +11 -4
  95. package/dist/query/cursor.js.map +1 -1
  96. package/dist/query/execute-unit-of-work.d.ts +123 -0
  97. package/dist/query/execute-unit-of-work.d.ts.map +1 -0
  98. package/dist/query/execute-unit-of-work.js +184 -0
  99. package/dist/query/execute-unit-of-work.js.map +1 -0
  100. package/dist/query/query.d.ts +3 -3
  101. package/dist/query/query.d.ts.map +1 -1
  102. package/dist/query/result-transform.js +4 -2
  103. package/dist/query/result-transform.js.map +1 -1
  104. package/dist/query/retry-policy.d.ts +88 -0
  105. package/dist/query/retry-policy.d.ts.map +1 -0
  106. package/dist/query/retry-policy.js +61 -0
  107. package/dist/query/retry-policy.js.map +1 -0
  108. package/dist/query/unit-of-work.d.ts +171 -32
  109. package/dist/query/unit-of-work.d.ts.map +1 -1
  110. package/dist/query/unit-of-work.js +530 -133
  111. package/dist/query/unit-of-work.js.map +1 -1
  112. package/dist/schema/serialize.js +12 -7
  113. package/dist/schema/serialize.js.map +1 -1
  114. package/dist/with-database.d.ts +28 -0
  115. package/dist/with-database.d.ts.map +1 -0
  116. package/dist/with-database.js +34 -0
  117. package/dist/with-database.js.map +1 -0
  118. package/package.json +10 -3
  119. package/src/adapters/adapters.ts +30 -0
  120. package/src/adapters/drizzle/drizzle-adapter-pglite.test.ts +86 -17
  121. package/src/adapters/drizzle/drizzle-adapter-sqlite.test.ts +291 -7
  122. package/src/adapters/drizzle/drizzle-adapter.test.ts +3 -51
  123. package/src/adapters/drizzle/drizzle-adapter.ts +35 -7
  124. package/src/adapters/drizzle/drizzle-query.ts +25 -15
  125. package/src/adapters/drizzle/drizzle-uow-compiler-mysql.test.ts +1442 -0
  126. package/src/adapters/drizzle/drizzle-uow-compiler-sqlite.test.ts +1414 -0
  127. package/src/adapters/drizzle/drizzle-uow-compiler.test.ts +78 -61
  128. package/src/adapters/drizzle/drizzle-uow-compiler.ts +123 -42
  129. package/src/adapters/drizzle/drizzle-uow-decoder.ts +34 -27
  130. package/src/adapters/drizzle/drizzle-uow-executor.ts +41 -8
  131. package/src/adapters/drizzle/generate.test.ts +102 -269
  132. package/src/adapters/drizzle/generate.ts +12 -30
  133. package/src/adapters/drizzle/test-utils.ts +36 -5
  134. package/src/adapters/kysely/kysely-adapter-pglite.test.ts +66 -22
  135. package/src/adapters/kysely/kysely-adapter-sqlite.test.ts +156 -0
  136. package/src/adapters/kysely/kysely-adapter.ts +25 -2
  137. package/src/adapters/kysely/kysely-query-compiler.ts +3 -8
  138. package/src/adapters/kysely/kysely-query.ts +57 -37
  139. package/src/adapters/kysely/kysely-shared.ts +34 -0
  140. package/src/adapters/kysely/kysely-uow-compiler.test.ts +62 -74
  141. package/src/adapters/kysely/kysely-uow-compiler.ts +92 -24
  142. package/src/adapters/kysely/kysely-uow-executor.ts +26 -7
  143. package/src/adapters/kysely/kysely-uow-joins.test.ts +33 -50
  144. package/src/adapters/kysely/migration/execute-base.ts +1 -1
  145. package/src/db-fragment-definition-builder.test.ts +887 -0
  146. package/src/db-fragment-definition-builder.ts +506 -0
  147. package/src/db-fragment-instantiator.test.ts +467 -0
  148. package/src/db-fragment-integration.test.ts +408 -0
  149. package/src/fragments/internal-fragment.test.ts +160 -0
  150. package/src/fragments/internal-fragment.ts +85 -0
  151. package/src/migration-engine/generation-engine.test.ts +58 -15
  152. package/src/migration-engine/generation-engine.ts +78 -25
  153. package/src/mod.ts +35 -43
  154. package/src/query/cursor.test.ts +119 -0
  155. package/src/query/cursor.ts +17 -4
  156. package/src/query/execute-unit-of-work.test.ts +1310 -0
  157. package/src/query/execute-unit-of-work.ts +463 -0
  158. package/src/query/query.ts +4 -4
  159. package/src/query/result-transform.test.ts +129 -0
  160. package/src/query/result-transform.ts +4 -1
  161. package/src/query/retry-policy.test.ts +217 -0
  162. package/src/query/retry-policy.ts +141 -0
  163. package/src/query/unit-of-work-coordinator.test.ts +833 -0
  164. package/src/query/unit-of-work-types.test.ts +15 -2
  165. package/src/query/unit-of-work.test.ts +878 -200
  166. package/src/query/unit-of-work.ts +963 -321
  167. package/src/schema/serialize.ts +22 -11
  168. package/src/with-database.ts +140 -0
  169. package/tsdown.config.ts +1 -0
  170. package/dist/fragment.d.ts +0 -54
  171. package/dist/fragment.d.ts.map +0 -1
  172. package/dist/fragment.js +0 -92
  173. package/dist/fragment.js.map +0 -1
  174. package/dist/shared/settings-schema.js +0 -36
  175. package/dist/shared/settings-schema.js.map +0 -1
  176. package/src/fragment.test.ts +0 -341
  177. package/src/fragment.ts +0 -198
  178. package/src/shared/settings-schema.ts +0 -61
@@ -66,7 +66,7 @@ function transformJoinArraysToObjects(row, op, provider) {
66
66
  }
67
67
  return transformedRow;
68
68
  }
69
- function createDrizzleUOWDecoder(_schema, provider) {
69
+ function createDrizzleUOWDecoder(provider) {
70
70
  return (rawResults, ops) => {
71
71
  if (rawResults.length !== ops.length) throw new Error("rawResults and ops must have the same length");
72
72
  return rawResults.map((result, index) => {
@@ -86,24 +86,31 @@ function createDrizzleUOWDecoder(_schema, provider) {
86
86
  });
87
87
  if (op.withCursor) {
88
88
  let cursor;
89
- if (decodedRows.length > 0 && op.options.orderByIndex && op.options.pageSize) {
90
- const lastItem = decodedRows[decodedRows.length - 1];
91
- const indexName = op.options.orderByIndex.indexName;
92
- let indexColumns;
93
- if (indexName === "_primary") indexColumns = [op.table.getIdColumn()];
94
- else {
95
- const index$1 = op.table.indexes[indexName];
96
- if (index$1) indexColumns = index$1.columns;
89
+ let hasNextPage = false;
90
+ let items = decodedRows;
91
+ if (op.options.pageSize && decodedRows.length > op.options.pageSize) {
92
+ hasNextPage = true;
93
+ items = decodedRows.slice(0, op.options.pageSize);
94
+ if (op.options.orderByIndex) {
95
+ const lastItem = items[items.length - 1];
96
+ const indexName = op.options.orderByIndex.indexName;
97
+ let indexColumns;
98
+ if (indexName === "_primary") indexColumns = [op.table.getIdColumn()];
99
+ else {
100
+ const index$1 = op.table.indexes[indexName];
101
+ if (index$1) indexColumns = index$1.columns;
102
+ }
103
+ if (indexColumns && lastItem) cursor = createCursorFromRecord(lastItem, indexColumns, {
104
+ indexName: op.options.orderByIndex.indexName,
105
+ orderDirection: op.options.orderByIndex.direction,
106
+ pageSize: op.options.pageSize
107
+ });
97
108
  }
98
- if (indexColumns && lastItem) cursor = createCursorFromRecord(lastItem, indexColumns, {
99
- indexName: op.options.orderByIndex.indexName,
100
- orderDirection: op.options.orderByIndex.direction,
101
- pageSize: op.options.pageSize
102
- });
103
109
  }
104
110
  return {
105
- items: decodedRows,
106
- cursor
111
+ items,
112
+ cursor,
113
+ hasNextPage
107
114
  };
108
115
  }
109
116
  return decodedRows;
@@ -1 +1 @@
1
- {"version":3,"file":"drizzle-uow-decoder.js","names":["result: Record<string, unknown>","cursor: Cursor | undefined","index"],"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\";\nimport { createCursorFromRecord, Cursor, type CursorResult } from \"../../query/cursor\";\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 provider: SQLProvider,\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 let value = row[relationName];\n\n // For SQLite, json_array returns a JSON string that needs to be parsed\n if (provider === \"sqlite\" && typeof value === \"string\") {\n try {\n value = JSON.parse(value) as unknown;\n } catch {\n // If parsing fails, skip this join\n continue;\n }\n }\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] as Record<string, unknown>;\n const countValue = row[\"count\"] ?? row[\"count(*)\"];\n\n if (typeof countValue !== \"number\") {\n throw new Error(`Unexpected result for count, received: ${countValue}`);\n }\n\n return countValue;\n }\n return 0;\n }\n\n // Handle find operations - decode each row\n const decodedRows = result.rows.map((row) => {\n const transformedRow = transformJoinArraysToObjects(row, op, provider);\n return decodeResult(transformedRow, op.table, provider);\n });\n\n // If cursor generation is requested, wrap in CursorResult\n if (op.withCursor) {\n let cursor: Cursor | undefined;\n\n // Generate cursor from last item if results exist\n if (decodedRows.length > 0 && op.options.orderByIndex && op.options.pageSize) {\n const lastItem = decodedRows[decodedRows.length - 1];\n const indexName = op.options.orderByIndex.indexName;\n\n // Get index columns\n let indexColumns;\n if (indexName === \"_primary\") {\n indexColumns = [op.table.getIdColumn()];\n } else {\n const index = op.table.indexes[indexName];\n if (index) {\n indexColumns = index.columns;\n }\n }\n\n if (indexColumns && lastItem) {\n cursor = createCursorFromRecord(lastItem as Record<string, unknown>, indexColumns, {\n indexName: op.options.orderByIndex.indexName,\n orderDirection: op.options.orderByIndex.direction,\n pageSize: op.options.pageSize,\n });\n }\n }\n\n const cursorResult: CursorResult<unknown> = {\n items: decodedRows,\n cursor,\n };\n return cursorResult;\n }\n\n return decodedRows;\n });\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"drizzle-uow-decoder.js","names":["result: Record<string, unknown>","cursor: Cursor | undefined","index"],"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\";\nimport { createCursorFromRecord, Cursor, type CursorResult } from \"../../query/cursor\";\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 provider: SQLProvider,\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 let value = row[relationName];\n\n // For SQLite, json_array returns a JSON string that needs to be parsed\n if (provider === \"sqlite\" && typeof value === \"string\") {\n try {\n value = JSON.parse(value) as unknown;\n } catch {\n // If parsing fails, skip this join\n continue;\n }\n }\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(provider: SQLProvider): UOWDecoder<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<AnySchema>;\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] as Record<string, unknown>;\n const countValue = row[\"count\"] ?? row[\"count(*)\"];\n\n if (typeof countValue !== \"number\") {\n throw new Error(`Unexpected result for count, received: ${countValue}`);\n }\n\n return countValue;\n }\n return 0;\n }\n\n // Handle find operations - decode each row\n const decodedRows = result.rows.map((row) => {\n const transformedRow = transformJoinArraysToObjects(row, op, provider);\n return decodeResult(transformedRow, op.table, provider);\n });\n\n // If cursor generation is requested, wrap in CursorResult\n if (op.withCursor) {\n let cursor: Cursor | undefined;\n let hasNextPage = false;\n let items = decodedRows;\n\n // Check if there are more results (we fetched pageSize + 1)\n if (op.options.pageSize && decodedRows.length > op.options.pageSize) {\n hasNextPage = true;\n // Trim to requested pageSize\n items = decodedRows.slice(0, op.options.pageSize);\n\n // Generate cursor from the last item we're returning\n if (op.options.orderByIndex) {\n const lastItem = items[items.length - 1];\n const indexName = op.options.orderByIndex.indexName;\n\n // Get index columns\n let indexColumns;\n if (indexName === \"_primary\") {\n indexColumns = [op.table.getIdColumn()];\n } else {\n const index = op.table.indexes[indexName];\n if (index) {\n indexColumns = index.columns;\n }\n }\n\n if (indexColumns && lastItem) {\n cursor = createCursorFromRecord(lastItem as Record<string, unknown>, indexColumns, {\n indexName: op.options.orderByIndex.indexName,\n orderDirection: op.options.orderByIndex.direction,\n pageSize: op.options.pageSize,\n });\n }\n }\n }\n\n const cursorResult: CursorResult<unknown> = {\n items,\n cursor,\n hasNextPage,\n };\n return cursorResult;\n }\n\n return decodedRows;\n });\n };\n}\n"],"mappings":"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"}
@@ -70,6 +70,15 @@ function extractCreatedInternalId(result) {
70
70
  }
71
71
  return null;
72
72
  }
73
+ function validateReturnedRows(result, expected) {
74
+ let rowCount = 0;
75
+ if (Array.isArray(result)) rowCount = result.length;
76
+ else if (result && typeof result === "object" && "rows" in result) {
77
+ const rows = result.rows;
78
+ rowCount = Array.isArray(rows) ? rows.length : 0;
79
+ }
80
+ if (rowCount !== expected) throw new Error(`Version conflict: expected ${expected} rows returned, but got ${rowCount}`);
81
+ }
73
82
  function validateAffectedRows(result, expected) {
74
83
  const actual = getAffectedRows(result);
75
84
  if (actual !== expected) throw new Error(`Version conflict: expected ${expected} rows affected, but got ${actual}`);
@@ -118,20 +127,22 @@ async function executeDrizzleMutationPhase(db, mutationBatch, provider) {
118
127
  const createdInternalIds = [];
119
128
  try {
120
129
  await executeInTransaction(db, provider, (syncDb) => {
121
- for (const { query, expectedAffectedRows } of mutationBatch) {
130
+ for (const { query, expectedAffectedRows, expectedReturnedRows } of mutationBatch) {
122
131
  const sqlObj = toSQL(query, provider);
123
- const result = syncDb.run(sqlObj);
124
- if (expectedAffectedRows === null) createdInternalIds.push(extractCreatedInternalId(result));
125
- else validateAffectedRows(result, expectedAffectedRows);
132
+ const result = expectedReturnedRows !== null ? syncDb.all(sqlObj) : syncDb.run(sqlObj);
133
+ if (expectedAffectedRows === null && expectedReturnedRows === null) createdInternalIds.push(extractCreatedInternalId(result));
134
+ if (expectedReturnedRows !== null) validateReturnedRows(result, expectedReturnedRows);
135
+ if (expectedAffectedRows !== null) validateAffectedRows(result, expectedAffectedRows);
126
136
  }
127
137
  }, async (tx) => {
128
- for (const { query, expectedAffectedRows } of mutationBatch) {
138
+ for (const { query, expectedAffectedRows, expectedReturnedRows } of mutationBatch) {
129
139
  const sqlObj = toSQL(query, provider);
130
140
  const executeMethod = tx.execute ?? tx.run;
131
141
  if (!executeMethod) throw new Error("Transaction object has neither execute nor run method");
132
142
  const result = await executeMethod.call(tx, sqlObj);
133
- if (expectedAffectedRows === null) createdInternalIds.push(extractCreatedInternalId(result));
134
- else validateAffectedRows(result, expectedAffectedRows);
143
+ if (expectedAffectedRows === null && expectedReturnedRows === null) createdInternalIds.push(extractCreatedInternalId(result));
144
+ if (expectedReturnedRows !== null) validateReturnedRows(result, expectedReturnedRows);
145
+ if (expectedAffectedRows !== null) validateAffectedRows(result, expectedAffectedRows);
135
146
  }
136
147
  });
137
148
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"drizzle-uow-executor.js","names":["queryChunks: SQLChunk[]","match: RegExpExecArray | null","chunks: SQLChunk[]","retrievalResults: DrizzleResult[]","result: 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\";\nimport { BaseSQLiteDatabase } from \"drizzle-orm/sqlite-core\";\nimport { SQLiteSyncDialect } from \"drizzle-orm/sqlite-core\";\n\ntype SyncSQLiteDB = BaseSQLiteDatabase<\n \"sync\",\n unknown,\n Record<string, never>,\n Record<string, never>\n>;\n\nfunction isSyncSQLite(db: unknown): boolean {\n return (\n db instanceof BaseSQLiteDatabase &&\n \"dialect\" in db &&\n (db as { dialect?: unknown }).dialect instanceof SQLiteSyncDialect\n );\n}\n\nfunction assertSyncSQLite(db: unknown): asserts db is SyncSQLiteDB {\n if (!isSyncSQLite(db)) {\n throw new Error(\"Expected synchronous SQLite database (better-sqlite3)\");\n }\n}\n\nfunction postgresToSQL(sqlString: string, params: unknown[]): SQLChunk[] {\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 const textBefore = sqlString.substring(lastIndex, match.index);\n if (textBefore) {\n queryChunks.push(new StringChunk(textBefore));\n }\n\n const paramIndex = parseInt(match[1]!, 10) - 1;\n queryChunks.push(sql.param(params[paramIndex]));\n\n lastIndex = match.index + match[0].length;\n }\n\n const textAfter = sqlString.substring(lastIndex);\n if (textAfter) {\n queryChunks.push(new StringChunk(textAfter));\n }\n\n return queryChunks;\n}\n\nfunction sqliteToSQL(sqlString: string, params: unknown[]): SQLChunk[] {\n const chunks: SQLChunk[] = [];\n let currentIndex = 0;\n\n const parts = sqlString.split(\"?\");\n for (let i = 0; i < parts.length; i++) {\n if (parts[i]) {\n chunks.push(new StringChunk(parts[i]));\n }\n if (i < parts.length - 1 && currentIndex < params.length) {\n chunks.push(sql.param(params[currentIndex++]));\n }\n }\n\n return chunks;\n}\n\nfunction toSQL(query: DrizzleCompiledQuery, provider: \"sqlite\" | \"mysql\" | \"postgresql\"): SQL {\n const { sql: sqlString, params } = query;\n\n const queryChunks =\n provider === \"sqlite\" ? sqliteToSQL(sqlString, params) : postgresToSQL(sqlString, params);\n\n return new SQL(queryChunks);\n}\n\nfunction getAffectedRows(result: unknown): number {\n if (Array.isArray(result)) {\n return result.length;\n }\n\n if (result && typeof result === \"object\") {\n // libsql uses rowsAffected\n if (\"rowsAffected\" in result && typeof result[\"rowsAffected\"] === \"number\") {\n return result[\"rowsAffected\"];\n }\n\n if (\"affectedRows\" in result && typeof result[\"affectedRows\"] === \"number\") {\n return result[\"affectedRows\"];\n }\n\n if (\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(\n `rowCount BigInt value ${rowCount.toString()} exceeds JS safe integer range`,\n );\n }\n return Number(rowCount);\n }\n\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\nasync function executeInTransaction(\n db: DBType,\n provider: \"sqlite\" | \"mysql\" | \"postgresql\",\n syncExecutor: (db: SyncSQLiteDB) => void,\n asyncExecutor: (tx: {\n execute?: (sql: SQL) => Promise<unknown>;\n run?: (sql: SQL) => Promise<unknown>;\n }) => Promise<void>,\n): Promise<void> {\n if (provider === \"sqlite\" && isSyncSQLite(db)) {\n assertSyncSQLite(db);\n db.transaction(() => syncExecutor(db));\n } else {\n await db.transaction(\n async (tx) =>\n await asyncExecutor(\n tx as { execute?: (sql: SQL) => Promise<unknown>; run?: (sql: SQL) => Promise<unknown> },\n ),\n );\n }\n}\n\nfunction extractCreatedInternalId(result: unknown): bigint | null {\n if (result && typeof result === \"object\" && \"lastInsertRowid\" in result) {\n if (typeof result.lastInsertRowid === \"bigint\") {\n return result.lastInsertRowid;\n }\n\n if (typeof result.lastInsertRowid === \"number\") {\n return BigInt(result.lastInsertRowid);\n }\n\n throw new Error(`Unexpected lastInsertRowid type: ${typeof result.lastInsertRowid}`);\n }\n\n if (Array.isArray(result) && result.length > 0) {\n const row = result[0] as Record<string, unknown>;\n if (\"_internalId\" in row || \"_internal_id\" in row) {\n return (row[\"_internalId\"] ?? row[\"_internal_id\"]) as bigint;\n }\n }\n\n return null;\n}\n\nfunction validateAffectedRows(result: unknown, expected: number): void {\n const actual = getAffectedRows(result);\n if (actual !== expected) {\n throw new Error(`Version conflict: expected ${expected} rows affected, but got ${actual}`);\n }\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 */\nexport async function executeDrizzleRetrievalPhase(\n db: DBType,\n retrievalBatch: DrizzleCompiledQuery[],\n provider: \"sqlite\" | \"mysql\" | \"postgresql\",\n): Promise<DrizzleResult[]> {\n if (retrievalBatch.length === 0) {\n return [];\n }\n\n const retrievalResults: DrizzleResult[] = [];\n\n await executeInTransaction(\n db,\n provider,\n (syncDb) => {\n for (const query of retrievalBatch) {\n const sqlObj = toSQL(query, provider);\n const rows = syncDb.all(sqlObj as never) as Record<string, unknown>[];\n const result: DrizzleResult = { rows, affectedRows: 0 };\n retrievalResults.push(result);\n }\n },\n async (tx) => {\n for (const query of retrievalBatch) {\n const sqlObj = toSQL(query, provider);\n // Fallback to run when execute is not available (e.g., libsql)\n const executeMethod = tx.execute ?? tx.run;\n if (!executeMethod) {\n throw new Error(\"Transaction object has neither execute nor run method\");\n }\n const result = (await executeMethod.call(tx, sqlObj)) as DrizzleResult;\n retrievalResults.push(result);\n }\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 */\nexport async function executeDrizzleMutationPhase(\n db: DBType,\n mutationBatch: CompiledMutation<DrizzleCompiledQuery>[],\n provider: \"sqlite\" | \"mysql\" | \"postgresql\",\n): Promise<MutationResult> {\n if (mutationBatch.length === 0) {\n return { success: true, createdInternalIds: [] };\n }\n\n const createdInternalIds: (bigint | null)[] = [];\n\n try {\n await executeInTransaction(\n db,\n provider,\n (syncDb) => {\n for (const { query, expectedAffectedRows } of mutationBatch) {\n const sqlObj = toSQL(query, provider);\n // Type assertion needed due to drizzle-orm version mismatch in dependencies\n const result = syncDb.run(sqlObj as never);\n\n if (expectedAffectedRows === null) {\n createdInternalIds.push(extractCreatedInternalId(result));\n } else {\n validateAffectedRows(result, expectedAffectedRows);\n }\n }\n },\n async (tx) => {\n for (const { query, expectedAffectedRows } of mutationBatch) {\n const sqlObj = toSQL(query, provider);\n // Fallback to run when execute is not available (e.g., libsql)\n const executeMethod = tx.execute ?? tx.run;\n if (!executeMethod) {\n throw new Error(\"Transaction object has neither execute nor run method\");\n }\n const result = await executeMethod.call(tx, sqlObj);\n\n if (expectedAffectedRows === null) {\n createdInternalIds.push(extractCreatedInternalId(result));\n } else {\n validateAffectedRows(result, expectedAffectedRows);\n }\n }\n },\n );\n\n return { success: true, createdInternalIds };\n } catch (error) {\n if (error instanceof Error && error.message.includes(\"Version conflict\")) {\n return { success: false };\n }\n throw error;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"drizzle-uow-executor.js","names":["queryChunks: SQLChunk[]","match: RegExpExecArray | null","chunks: SQLChunk[]","retrievalResults: DrizzleResult[]","result: 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\";\nimport { BaseSQLiteDatabase } from \"drizzle-orm/sqlite-core\";\nimport { SQLiteSyncDialect } from \"drizzle-orm/sqlite-core\";\n\ntype SyncSQLiteDB = BaseSQLiteDatabase<\n \"sync\",\n unknown,\n Record<string, never>,\n Record<string, never>\n>;\n\nfunction isSyncSQLite(db: unknown): boolean {\n return (\n db instanceof BaseSQLiteDatabase &&\n \"dialect\" in db &&\n (db as { dialect?: unknown }).dialect instanceof SQLiteSyncDialect\n );\n}\n\nfunction assertSyncSQLite(db: unknown): asserts db is SyncSQLiteDB {\n if (!isSyncSQLite(db)) {\n throw new Error(\"Expected synchronous SQLite database (better-sqlite3)\");\n }\n}\n\nfunction postgresToSQL(sqlString: string, params: unknown[]): SQLChunk[] {\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 const textBefore = sqlString.substring(lastIndex, match.index);\n if (textBefore) {\n queryChunks.push(new StringChunk(textBefore));\n }\n\n const paramIndex = parseInt(match[1]!, 10) - 1;\n queryChunks.push(sql.param(params[paramIndex]));\n\n lastIndex = match.index + match[0].length;\n }\n\n const textAfter = sqlString.substring(lastIndex);\n if (textAfter) {\n queryChunks.push(new StringChunk(textAfter));\n }\n\n return queryChunks;\n}\n\nfunction sqliteToSQL(sqlString: string, params: unknown[]): SQLChunk[] {\n const chunks: SQLChunk[] = [];\n let currentIndex = 0;\n\n const parts = sqlString.split(\"?\");\n for (let i = 0; i < parts.length; i++) {\n if (parts[i]) {\n chunks.push(new StringChunk(parts[i]));\n }\n if (i < parts.length - 1 && currentIndex < params.length) {\n chunks.push(sql.param(params[currentIndex++]));\n }\n }\n\n return chunks;\n}\n\nfunction toSQL(query: DrizzleCompiledQuery, provider: \"sqlite\" | \"mysql\" | \"postgresql\"): SQL {\n const { sql: sqlString, params } = query;\n\n const queryChunks =\n provider === \"sqlite\" ? sqliteToSQL(sqlString, params) : postgresToSQL(sqlString, params);\n return new SQL(queryChunks);\n}\n\nfunction getAffectedRows(result: unknown): number {\n if (Array.isArray(result)) {\n return result.length;\n }\n\n if (result && typeof result === \"object\") {\n // libsql uses rowsAffected\n if (\"rowsAffected\" in result && typeof result[\"rowsAffected\"] === \"number\") {\n return result[\"rowsAffected\"];\n }\n\n if (\"affectedRows\" in result && typeof result[\"affectedRows\"] === \"number\") {\n return result[\"affectedRows\"];\n }\n\n if (\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(\n `rowCount BigInt value ${rowCount.toString()} exceeds JS safe integer range`,\n );\n }\n return Number(rowCount);\n }\n\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\nasync function executeInTransaction(\n db: DBType,\n provider: \"sqlite\" | \"mysql\" | \"postgresql\",\n syncExecutor: (db: SyncSQLiteDB) => void,\n asyncExecutor: (tx: {\n execute?: (sql: SQL) => Promise<unknown>;\n run?: (sql: SQL) => Promise<unknown>;\n }) => Promise<void>,\n): Promise<void> {\n if (provider === \"sqlite\" && isSyncSQLite(db)) {\n assertSyncSQLite(db);\n db.transaction(() => syncExecutor(db));\n } else {\n await db.transaction(\n async (tx) =>\n await asyncExecutor(\n tx as { execute?: (sql: SQL) => Promise<unknown>; run?: (sql: SQL) => Promise<unknown> },\n ),\n );\n }\n}\n\nfunction extractCreatedInternalId(result: unknown): bigint | null {\n if (result && typeof result === \"object\" && \"lastInsertRowid\" in result) {\n if (typeof result.lastInsertRowid === \"bigint\") {\n return result.lastInsertRowid;\n }\n\n if (typeof result.lastInsertRowid === \"number\") {\n return BigInt(result.lastInsertRowid);\n }\n\n throw new Error(`Unexpected lastInsertRowid type: ${typeof result.lastInsertRowid}`);\n }\n\n if (Array.isArray(result) && result.length > 0) {\n const row = result[0] as Record<string, unknown>;\n if (\"_internalId\" in row || \"_internal_id\" in row) {\n return (row[\"_internalId\"] ?? row[\"_internal_id\"]) as bigint;\n }\n }\n\n return null;\n}\n\nfunction validateReturnedRows(result: unknown, expected: number): void {\n // For SELECT queries, check the number of rows returned\n // Result can be an array (sync) or an object with rows property (async)\n let rowCount = 0;\n if (Array.isArray(result)) {\n rowCount = result.length;\n } else if (result && typeof result === \"object\" && \"rows\" in result) {\n const rows = (result as { rows: unknown[] }).rows;\n rowCount = Array.isArray(rows) ? rows.length : 0;\n }\n\n if (rowCount !== expected) {\n throw new Error(`Version conflict: expected ${expected} rows returned, but got ${rowCount}`);\n }\n}\n\nfunction validateAffectedRows(result: unknown, expected: number): void {\n // For UPDATE/DELETE queries, check affected rows\n const actual = getAffectedRows(result);\n if (actual !== expected) {\n throw new Error(`Version conflict: expected ${expected} rows affected, but got ${actual}`);\n }\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 */\nexport async function executeDrizzleRetrievalPhase(\n db: DBType,\n retrievalBatch: DrizzleCompiledQuery[],\n provider: \"sqlite\" | \"mysql\" | \"postgresql\",\n): Promise<DrizzleResult[]> {\n if (retrievalBatch.length === 0) {\n return [];\n }\n\n const retrievalResults: DrizzleResult[] = [];\n\n await executeInTransaction(\n db,\n provider,\n (syncDb) => {\n for (const query of retrievalBatch) {\n const sqlObj = toSQL(query, provider);\n const rows = syncDb.all(sqlObj as never) as Record<string, unknown>[];\n const result: DrizzleResult = { rows, affectedRows: 0 };\n retrievalResults.push(result);\n }\n },\n async (tx) => {\n for (const query of retrievalBatch) {\n const sqlObj = toSQL(query, provider);\n // Fallback to run when execute is not available (e.g., libsql)\n const executeMethod = tx.execute ?? tx.run;\n if (!executeMethod) {\n throw new Error(\"Transaction object has neither execute nor run method\");\n }\n const result = (await executeMethod.call(tx, sqlObj)) as DrizzleResult;\n retrievalResults.push(result);\n }\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 */\nexport async function executeDrizzleMutationPhase(\n db: DBType,\n mutationBatch: CompiledMutation<DrizzleCompiledQuery>[],\n provider: \"sqlite\" | \"mysql\" | \"postgresql\",\n): Promise<MutationResult> {\n if (mutationBatch.length === 0) {\n return { success: true, createdInternalIds: [] };\n }\n\n const createdInternalIds: (bigint | null)[] = [];\n\n try {\n await executeInTransaction(\n db,\n provider,\n (syncDb) => {\n for (const { query, expectedAffectedRows, expectedReturnedRows } of mutationBatch) {\n const sqlObj = toSQL(query, provider);\n\n // Type assertion needed due to drizzle-orm version mismatch in dependencies\n const result =\n expectedReturnedRows !== null\n ? syncDb.all(sqlObj as never)\n : syncDb.run(sqlObj as never);\n\n if (expectedAffectedRows === null && expectedReturnedRows === null) {\n createdInternalIds.push(extractCreatedInternalId(result));\n }\n\n if (expectedReturnedRows !== null) {\n validateReturnedRows(result, expectedReturnedRows);\n }\n\n if (expectedAffectedRows !== null) {\n validateAffectedRows(result, expectedAffectedRows);\n }\n }\n },\n async (tx) => {\n for (const { query, expectedAffectedRows, expectedReturnedRows } of mutationBatch) {\n const sqlObj = toSQL(query, provider);\n\n // Fallback to run when execute is not available (e.g., libsql)\n const executeMethod = tx.execute ?? tx.run;\n if (!executeMethod) {\n throw new Error(\"Transaction object has neither execute nor run method\");\n }\n const result = await executeMethod.call(tx, sqlObj);\n\n if (expectedAffectedRows === null && expectedReturnedRows === null) {\n createdInternalIds.push(extractCreatedInternalId(result));\n }\n\n if (expectedReturnedRows !== null) {\n validateReturnedRows(result, expectedReturnedRows);\n }\n\n if (expectedAffectedRows !== null) {\n validateAffectedRows(result, expectedAffectedRows);\n }\n }\n },\n );\n\n return { success: true, createdInternalIds };\n } catch (error) {\n if (error instanceof Error && error.message.includes(\"Version conflict\")) {\n return { success: false };\n }\n throw error;\n }\n}\n"],"mappings":"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"}
@@ -13,7 +13,10 @@ interface GenerateSchemaOptions {
13
13
  };
14
14
  }
15
15
  /**
16
- * Generate a schema file from one or more fragments with a shared settings table
16
+ * Generate a settings table for storing fragment versions
17
+ */
18
+ /**
19
+ * Generate a schema file from one or more fragments with automatic de-duplication
17
20
  */
18
21
  declare function generateSchema(fragments: {
19
22
  namespace: string;
@@ -1 +1 @@
1
- {"version":3,"file":"generate.d.ts","names":[],"sources":["../../../src/adapters/drizzle/generate.ts"],"sourcesContent":[],"mappings":";;;;KA8BY,iBAAA,GAAoB,QAAQ;UAqjBvB,qBAAA;EArjBL;EAqjBK,iBAAA,CAAA,EAAA;IA4BD;IAC0B,IAAA,EAAA,MAAA;IAC9B;IACA,IAAA,EAAA,MAAA;EAAqB,CAAA;;;;;iBAHjB,cAAA;;UAC0B;eAC9B,6BACA"}
1
+ {"version":3,"file":"generate.d.ts","names":[],"sources":["../../../src/adapters/drizzle/generate.ts"],"sourcesContent":[],"mappings":";;;;KA6BY,iBAAA,GAAoB,QAAQ;UAyjBvB,qBAAA;EAzjBL;EAyjBK,iBAAA,CAAA,EAAA;IAgBD;IAC0B,IAAA,EAAA,MAAA;IAC9B;IACA,IAAA,EAAA,MAAA;EAAqB,CAAA;;;;;;;;iBAHjB,cAAA;;UAC0B;eAC9B,6BACA"}
@@ -1,6 +1,5 @@
1
1
  import { InternalIdColumn } from "../../schema/create.js";
2
2
  import { schemaToDBType } from "../../schema/serialize.js";
3
- import { SETTINGS_TABLE_NAME, settingsSchema } from "../../shared/settings-schema.js";
4
3
  import { importGenerator } from "../../util/import-generator.js";
5
4
  import { ident, parseVarchar } from "../../util/parse.js";
6
5
  import { createTableNameMapper, sanitizeNamespace } from "./shared.js";
@@ -100,7 +99,11 @@ function mapDBTypeToDrizzleFunction(ctx, dbType, column, customTypes) {
100
99
  name: generateBinaryCustomType(ctx, customTypes),
101
100
  isCustomType: true
102
101
  };
103
- case "bigint": return { name: "bigint" };
102
+ case "bigint": return {
103
+ name: "bigint",
104
+ params: [`{ mode: "number" }`]
105
+ };
106
+ case "integer": return { name: "int" };
104
107
  default:
105
108
  if (dbType.startsWith("varchar(")) return {
106
109
  name: "varchar",
@@ -306,26 +309,14 @@ function generateFragmentSchemaExport(schema, namespace, tablesWithRelations) {
306
309
  /**
307
310
  * Generate a settings table for storing fragment versions
308
311
  */
309
- function generateSettingsTable(ctx) {
310
- const settingsTable = settingsSchema.tables[SETTINGS_TABLE_NAME];
311
- return generateTable(ctx, settingsTable, []);
312
- }
313
312
  /**
314
- * Generate a schema file from one or more fragments with a shared settings table
313
+ * Generate a schema file from one or more fragments with automatic de-duplication
315
314
  */
316
315
  function generateSchema(fragments, provider, options) {
317
316
  const ctx = createContext(provider, options?.idGeneratorImport);
318
317
  const customTypes = [];
319
318
  const sections = [];
320
- sections.push("");
321
- sections.push("// ============================================================================");
322
- sections.push("// Settings Table (shared across all fragments)");
323
- sections.push("// ============================================================================");
324
- sections.push("");
325
- sections.push(generateSettingsTable(ctx));
326
- sections.push("");
327
- sections.push(`export const fragnoDbSettingSchemaVersion = ${settingsSchema.version};`);
328
- for (const { namespace, schema } of fragments) {
319
+ for (const { schema, namespace } of fragments) {
329
320
  const fragmentTables = [];
330
321
  fragmentTables.push("");
331
322
  fragmentTables.push("// ============================================================================");
@@ -354,8 +345,10 @@ function generateSchema(fragments, provider, options) {
354
345
  tablesWithRelations.add(table.name);
355
346
  }
356
347
  }
357
- fragmentTables.push("");
358
- fragmentTables.push(generateFragmentSchemaExport(schema, namespace, tablesWithRelations));
348
+ if (namespace !== "") {
349
+ fragmentTables.push("");
350
+ fragmentTables.push(generateFragmentSchemaExport(schema, namespace, tablesWithRelations));
351
+ }
359
352
  sections.push(...fragmentTables);
360
353
  }
361
354
  return [
@@ -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[]","tableRef","drizzleEntries: 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 type Relation,\n InternalIdColumn,\n} from \"../../schema/create\";\nimport type { SQLProvider } from \"../../shared/providers\";\nimport { schemaToDBType, type DBTypeLiteral } from \"../../schema/serialize\";\nimport { createTableNameMapper, sanitizeNamespace } 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 if (ctx.provider === \"sqlite\") {\n // SQLite uses primaryKey({ autoIncrement: true })\n parts.push(\"primaryKey({ autoIncrement: true })\");\n } else if (ctx.provider === \"mysql\") {\n // MySQL uses primaryKey().autoincrement()\n parts.push(\"primaryKey()\");\n parts.push(\"autoincrement()\");\n } else {\n // PostgreSQL just uses primaryKey()\n parts.push(\"primaryKey()\");\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 = namespace\n ? `${relation.table.ormName}_${sanitizeNamespace(namespace)}`\n : 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, and sanitize for use as database table name\n // Database table names must also be valid identifiers to work with Drizzle's relational query system\n const physicalTableName = namespace\n ? `${table.ormName}_${sanitizeNamespace(namespace)}`\n : table.ormName;\n // Same sanitized name for TypeScript export\n const exportName = physicalTableName;\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 inverseRelations?: Array<{ fromTable: AnyTable; relation: Relation }>,\n): string | undefined {\n const relations: string[] = [];\n let hasOne = false;\n let hasMany = false;\n\n // Generate explicit relations defined on this table\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 // Generate inverse relations for tables that reference this table\n // Drizzle requires both sides of a relation to be defined\n if (inverseRelations && inverseRelations.length > 0) {\n for (const { fromTable, relation } of inverseRelations) {\n // Only generate inverse for \"one\" relations (they become \"many\" on this side)\n if (relation.type === \"one\") {\n hasMany = true;\n\n const fromTableRef = namespace\n ? `${fromTable.ormName}_${sanitizeNamespace(namespace)}`\n : fromTable.ormName;\n\n // Generate inverse relation name with consistent suffix\n // e.g., if session has \"sessionOwner\" relation to user, user gets \"sessionList\" inverse relation\n const inverseRelationName = `${fromTable.ormName}List`;\n\n const options: string[] = [`relationName: \"${relation.id}\"`];\n const args: string[] = [fromTableRef, `{\\n${ident(options.join(\",\\n\"))}\\n}`];\n\n relations.push(ident(`${inverseRelationName}: many(${args.join(\", \")})`));\n }\n }\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 * Generate a schema export object for a fragment\n * This groups all tables by their logical names for easier access\n */\nfunction generateFragmentSchemaExport(\n schema: AnySchema,\n namespace: string,\n tablesWithRelations?: Set<string>,\n): string {\n const drizzleEntries: 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 // Add physical table name to drizzle schema\n drizzleEntries.push(` ${physicalExportName}: ${physicalExportName}`);\n\n // Include relations for this table if they exist (either explicit or inverse)\n if (tablesWithRelations?.has(table.name)) {\n const relationsName = namespace\n ? `${table.ormName}_${sanitizeNamespace(namespace)}Relations`\n : `${table.ormName}Relations`;\n\n drizzleEntries.push(` ${relationsName}: ${relationsName}`);\n }\n\n // Add convenience aliases WITH their relations to work around Drizzle bug\n // The key insight: Drizzle needs BOTH the table alias AND its relations alias\n // in the same schema object for relational queries to work\n if (namespace) {\n drizzleEntries.push(` ${table.ormName}: ${physicalExportName}`);\n\n // Also add the relations under the aliased name if they exist\n if (tablesWithRelations?.has(table.name)) {\n const physicalRelationsName = `${table.ormName}_${sanitizeNamespace(namespace)}Relations`;\n const aliasRelationsName = `${table.ormName}Relations`;\n drizzleEntries.push(` ${aliasRelationsName}: ${physicalRelationsName}`);\n }\n }\n }\n\n // Add schema version as a number\n drizzleEntries.push(` schemaVersion: ${schema.version}`);\n\n const exportName = namespace ? `${sanitizeNamespace(namespace)}_schema` : \"_schema\";\n\n return `export const ${exportName} = {\\n${drizzleEntries.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\n // Build a map of inverse relations for tables that are referenced but don't have their own relations\n // This is needed for Drizzle's relational query API to work correctly\n const inverseRelations = new Map<string, Array<{ fromTable: AnyTable; relation: Relation }>>();\n for (const table of Object.values(schema.tables)) {\n for (const relation of Object.values(table.relations)) {\n // Track this relation as an inverse on the target table\n const targetTableName = relation.table.name;\n if (!inverseRelations.has(targetTableName)) {\n inverseRelations.set(targetTableName, []);\n }\n inverseRelations.get(targetTableName)!.push({ fromTable: table, relation });\n }\n }\n\n // Generate relations for all tables (both explicit and inverse)\n const tablesWithRelations = new Set<string>();\n for (const table of Object.values(schema.tables)) {\n const relationCode = generateRelation(\n ctx,\n table,\n namespace,\n inverseRelations.get(table.name),\n );\n if (relationCode) {\n fragmentTables.push(\"\");\n fragmentTables.push(relationCode);\n tablesWithRelations.add(table.name);\n }\n }\n\n // Generate schema export object\n fragmentTables.push(\"\");\n fragmentTables.push(generateFragmentSchemaExport(schema, namespace, tablesWithRelations));\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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"}
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","drizzleEntries: 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 type Relation,\n InternalIdColumn,\n} from \"../../schema/create\";\nimport type { SQLProvider } from \"../../shared/providers\";\nimport { schemaToDBType, type DBTypeLiteral } from \"../../schema/serialize\";\nimport { createTableNameMapper, sanitizeNamespace } from \"./shared\";\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 // MySQL bigint requires mode parameter\n return { name: \"bigint\", params: [`{ mode: \"number\" }`] };\n case \"integer\":\n // MySQL uses \"int\" not \"integer\" in Drizzle ORM\n return { name: \"int\" };\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 if (ctx.provider === \"sqlite\") {\n // SQLite uses primaryKey({ autoIncrement: true })\n parts.push(\"primaryKey({ autoIncrement: true })\");\n } else if (ctx.provider === \"mysql\") {\n // MySQL uses primaryKey().autoincrement()\n parts.push(\"primaryKey()\");\n parts.push(\"autoincrement()\");\n } else {\n // PostgreSQL just uses primaryKey()\n parts.push(\"primaryKey()\");\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 = namespace\n ? `${relation.table.ormName}_${sanitizeNamespace(namespace)}`\n : 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, and sanitize for use as database table name\n // Database table names must also be valid identifiers to work with Drizzle's relational query system\n const physicalTableName = namespace\n ? `${table.ormName}_${sanitizeNamespace(namespace)}`\n : table.ormName;\n // Same sanitized name for TypeScript export\n const exportName = physicalTableName;\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 inverseRelations?: Array<{ fromTable: AnyTable; relation: Relation }>,\n): string | undefined {\n const relations: string[] = [];\n let hasOne = false;\n let hasMany = false;\n\n // Generate explicit relations defined on this table\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 // Generate inverse relations for tables that reference this table\n // Drizzle requires both sides of a relation to be defined\n if (inverseRelations && inverseRelations.length > 0) {\n for (const { fromTable, relation } of inverseRelations) {\n // Only generate inverse for \"one\" relations (they become \"many\" on this side)\n if (relation.type === \"one\") {\n hasMany = true;\n\n const fromTableRef = namespace\n ? `${fromTable.ormName}_${sanitizeNamespace(namespace)}`\n : fromTable.ormName;\n\n // Generate inverse relation name with consistent suffix\n // e.g., if session has \"sessionOwner\" relation to user, user gets \"sessionList\" inverse relation\n const inverseRelationName = `${fromTable.ormName}List`;\n\n const options: string[] = [`relationName: \"${relation.id}\"`];\n const args: string[] = [fromTableRef, `{\\n${ident(options.join(\",\\n\"))}\\n}`];\n\n relations.push(ident(`${inverseRelationName}: many(${args.join(\", \")})`));\n }\n }\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 * Generate a schema export object for a fragment\n * This groups all tables by their logical names for easier access\n */\nfunction generateFragmentSchemaExport(\n schema: AnySchema,\n namespace: string,\n tablesWithRelations?: Set<string>,\n): string {\n const drizzleEntries: 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 // Add physical table name to drizzle schema\n drizzleEntries.push(` ${physicalExportName}: ${physicalExportName}`);\n\n // Include relations for this table if they exist (either explicit or inverse)\n if (tablesWithRelations?.has(table.name)) {\n const relationsName = namespace\n ? `${table.ormName}_${sanitizeNamespace(namespace)}Relations`\n : `${table.ormName}Relations`;\n\n drizzleEntries.push(` ${relationsName}: ${relationsName}`);\n }\n\n // Add convenience aliases WITH their relations to work around Drizzle bug\n // The key insight: Drizzle needs BOTH the table alias AND its relations alias\n // in the same schema object for relational queries to work\n if (namespace) {\n drizzleEntries.push(` ${table.ormName}: ${physicalExportName}`);\n\n // Also add the relations under the aliased name if they exist\n if (tablesWithRelations?.has(table.name)) {\n const physicalRelationsName = `${table.ormName}_${sanitizeNamespace(namespace)}Relations`;\n const aliasRelationsName = `${table.ormName}Relations`;\n drizzleEntries.push(` ${aliasRelationsName}: ${physicalRelationsName}`);\n }\n }\n }\n\n // Add schema version as a number\n drizzleEntries.push(` schemaVersion: ${schema.version}`);\n\n const exportName = namespace ? `${sanitizeNamespace(namespace)}_schema` : \"_schema\";\n\n return `export const ${exportName} = {\\n${drizzleEntries.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 */\n/**\n * Generate a schema file from one or more fragments with automatic de-duplication\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 for (const { schema, namespace } 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\n // Build a map of inverse relations for tables that are referenced but don't have their own relations\n // This is needed for Drizzle's relational query API to work correctly\n const inverseRelations = new Map<string, Array<{ fromTable: AnyTable; relation: Relation }>>();\n for (const table of Object.values(schema.tables)) {\n for (const relation of Object.values(table.relations)) {\n // Track this relation as an inverse on the target table\n const targetTableName = relation.table.name;\n if (!inverseRelations.has(targetTableName)) {\n inverseRelations.set(targetTableName, []);\n }\n inverseRelations.get(targetTableName)!.push({ fromTable: table, relation });\n }\n }\n\n // Generate relations for all tables (both explicit and inverse)\n const tablesWithRelations = new Set<string>();\n for (const table of Object.values(schema.tables)) {\n const relationCode = generateRelation(\n ctx,\n table,\n namespace,\n inverseRelations.get(table.name),\n );\n if (relationCode) {\n fragmentTables.push(\"\");\n fragmentTables.push(relationCode);\n tablesWithRelations.add(table.name);\n }\n }\n\n // Generate schema export object (skip for empty namespace to avoid duplicate _schema exports)\n if (namespace !== \"\") {\n fragmentTables.push(\"\");\n fragmentTables.push(generateFragmentSchemaExport(schema, namespace, tablesWithRelations));\n }\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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"}
@@ -1 +1,14 @@
1
- import "drizzle-orm";
1
+ import "drizzle-orm";
2
+
3
+ //#region src/adapters/drizzle/shared.d.ts
4
+
5
+ /**
6
+ * Maps logical table names (used by fragment authors) to physical table names (with namespace suffix)
7
+ */
8
+ interface TableNameMapper {
9
+ toPhysical(logicalName: string): string;
10
+ toLogical(physicalName: string): string;
11
+ }
12
+ //#endregion
13
+ export { TableNameMapper };
14
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","names":[],"sources":["../../../src/adapters/drizzle/shared.ts"],"sourcesContent":[],"mappings":";;;;;;;UA2BiB,eAAA"}
@@ -2,9 +2,11 @@ import { AnySchema } from "../../schema/create.js";
2
2
  import { Migrator } from "../../migration-engine/create.js";
3
3
  import { SQLProvider } from "../../shared/providers.js";
4
4
  import { AbstractQuery } from "../../query/query.js";
5
- import { DatabaseAdapter, fragnoDatabaseAdapterNameFakeSymbol, fragnoDatabaseAdapterVersionFakeSymbol } from "../adapters.js";
5
+ import { DatabaseAdapter, DatabaseContextStorage, fragnoDatabaseAdapterNameFakeSymbol, fragnoDatabaseAdapterVersionFakeSymbol } from "../adapters.js";
6
+ import { TableNameMapper } from "./kysely-shared.js";
6
7
  import { KyselyUOWConfig } from "./kysely-query.js";
7
8
  import { Kysely } from "kysely";
9
+ import { RequestContextStorage } from "@fragno-dev/core/internal/request-context-storage";
8
10
 
9
11
  //#region src/adapters/kysely/kysely-adapter.d.ts
10
12
  type KyselyAny = Kysely<any>;
@@ -17,7 +19,9 @@ declare class KyselyAdapter implements DatabaseAdapter<KyselyUOWConfig> {
17
19
  constructor(config: KyselyConfig);
18
20
  get [fragnoDatabaseAdapterNameFakeSymbol](): string;
19
21
  get [fragnoDatabaseAdapterVersionFakeSymbol](): number;
22
+ get contextStorage(): RequestContextStorage<DatabaseContextStorage>;
20
23
  close(): Promise<void>;
24
+ createTableNameMapper(namespace: string): TableNameMapper;
21
25
  createQueryEngine<T extends AnySchema>(schema: T, namespace: string): AbstractQuery<T, KyselyUOWConfig>;
22
26
  isConnectionHealthy(): Promise<boolean>;
23
27
  createMigrationEngine(schema: AnySchema, namespace: string): Migrator;
@@ -1 +1 @@
1
- {"version":3,"file":"kysely-adapter.d.ts","names":[],"sources":["../../../src/adapters/kysely/kysely-adapter.ts"],"sourcesContent":[],"mappings":";;;;;;;;;KAoBK,SAAA,GAAY;UAEA,YAAA;EAFZ,EAAA,EAGC,SAHQ,GAAA,CAAA,GAAA,GAGW,SAHF,GAGc,OAHd,CAGsB,SAHtB,CAAA,CAAA;EAEN,QAAA,EAEL,WAFiB;;AACJ,cAIZ,aAAA,YAAyB,eAJb,CAI6B,eAJ7B,CAAA,CAAA;EAAoB,CAAA,OAAA;EAAR,WAAA,CAAA,MAAA,EAQf,YARe;EACzB,KAYL,mCAAA,GAZK,EAAA,MAAA;EAAW,KAgBhB,sCAAA,GAhBgB,EAAA,MAAA;EAGV,KAAA,CAAA,CAAA,EAiBI,OAjBU,CAAA,IAAA,CAAA;EAA2B,iBAAA,CAAA,UAqBxB,SArBwB,CAAA,CAAA,MAAA,EAsB1C,CAtB0C,EAAA,SAAA,EAAA,MAAA,CAAA,EAwBjD,aAxBiD,CAwBnC,CAxBmC,EAwBhC,eAxBgC,CAAA;EAIhC,mBAAA,CAAA,CAAA,EA0BS,OA1BT,CAAA,OAAA,CAAA;EAKf,qBAAA,CAAA,MAAA,EAiCyB,SAjCzB,EAAA,SAAA,EAAA,MAAA,CAAA,EAiCwD,QAjCxD;EAIA,gBAAA,CAAA,SAAA,EAAA,MAAA,CAAA,EAuJsC,OAvJtC,CAAA,MAAA,GAAA,SAAA,CAAA"}
1
+ {"version":3,"file":"kysely-adapter.d.ts","names":[],"sources":["../../../src/adapters/kysely/kysely-adapter.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;;KAsBK,SAAA,GAAY;UAEA,YAAA;MACX,mBAAmB,YAAY,QAAQ;EAHxC,QAAA,EAIO,WAJE;AAEd;AACM,cAIO,aAAA,YAAyB,eAJhC,CAIgD,eAJhD,CAAA,CAAA;EAAmB,CAAA,OAAA;EAAoB,WAAA,CAAA,MAAA,EAUvB,YAVuB;EAAR,KAgB9B,mCAAA,GAhB8B,EAAA,MAAA;EACzB,KAmBL,sCAAA,GAnBK,EAAA,MAAA;EAAW,IAAA,cAAA,CAAA,CAAA,EAuBC,qBAvBD,CAuBuB,sBAvBvB,CAAA;EAGV,KAAA,CAAA,CAAA,EAwBI,OAxBU,CAAA,IAAA,CAAA;EAA2B,qBAAA,CAAA,SAAA,EAAA,MAAA,CAAA,EAwB9B,eAxB8B;EAMhC,iBAAA,CAAA,UA0BQ,SA1BR,CAAA,CAAA,MAAA,EA2BV,CA3BU,EAAA,SAAA,EAAA,MAAA,CAAA,EA6BjB,aA7BiB,CA6BH,CA7BG,EA6BA,eA7BA,CAAA;EAMf,mBAAA,CAAA,CAAA,EAuCwB,OAvCxB,CAAA,OAAA,CAAA;EAIA,qBAAA,CAAA,MAAA,EA+CyB,SA/CzB,EAAA,SAAA,EAAA,MAAA,CAAA,EA+CwD,QA/CxD;EAIuC,gBAAA,CAAA,SAAA,EAAA,MAAA,CAAA,EAqKD,OArKC,CAAA,MAAA,GAAA,SAAA,CAAA"}
@@ -1,9 +1,10 @@
1
+ import { RequestContextStorage } from "../../packages/fragno/dist/api/request-context-storage.js";
2
+ import { SETTINGS_TABLE_NAME } from "../../fragments/internal-fragment.js";
1
3
  import { fragnoDatabaseAdapterNameFakeSymbol, fragnoDatabaseAdapterVersionFakeSymbol } from "../adapters.js";
2
- import { SETTINGS_TABLE_NAME } from "../../shared/settings-schema.js";
3
4
  import { createMigrator } from "../../migration-engine/create.js";
4
5
  import { execute, preprocessOperations } from "./migration/execute.js";
5
- import { fromKysely } from "./kysely-query.js";
6
6
  import { createTableNameMapper } from "./kysely-shared.js";
7
+ import { fromKysely } from "./kysely-query.js";
7
8
  import { createKyselyConnectionPool } from "./kysely-connection-pool.js";
8
9
  import { sql } from "kysely";
9
10
  import { createHash } from "node:crypto";
@@ -12,9 +13,12 @@ import { createHash } from "node:crypto";
12
13
  var KyselyAdapter = class {
13
14
  #connectionPool;
14
15
  #provider;
16
+ #schemaNamespaceMap = /* @__PURE__ */ new WeakMap();
17
+ #contextStorage;
15
18
  constructor(config) {
16
19
  this.#connectionPool = createKyselyConnectionPool(config.db);
17
20
  this.#provider = config.provider;
21
+ this.#contextStorage = new RequestContextStorage();
18
22
  }
19
23
  get [fragnoDatabaseAdapterNameFakeSymbol]() {
20
24
  return "kysely";
@@ -22,12 +26,19 @@ var KyselyAdapter = class {
22
26
  get [fragnoDatabaseAdapterVersionFakeSymbol]() {
23
27
  return 0;
24
28
  }
29
+ get contextStorage() {
30
+ return this.#contextStorage;
31
+ }
25
32
  async close() {
26
33
  await this.#connectionPool.close();
27
34
  }
35
+ createTableNameMapper(namespace) {
36
+ return createTableNameMapper(namespace);
37
+ }
28
38
  createQueryEngine(schema, namespace) {
39
+ this.#schemaNamespaceMap.set(schema, namespace);
29
40
  const mapper = namespace ? createTableNameMapper(namespace) : void 0;
30
- return fromKysely(schema, this.#connectionPool, this.#provider, mapper);
41
+ return fromKysely(schema, this.#connectionPool, this.#provider, mapper, void 0, this.#schemaNamespaceMap);
31
42
  }
32
43
  async isConnectionHealthy() {
33
44
  const conn = await this.#connectionPool.connect();