@fragno-dev/db 0.1.13 → 0.1.14

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 (75) hide show
  1. package/.turbo/turbo-build.log +48 -41
  2. package/CHANGELOG.md +6 -0
  3. package/dist/adapters/adapters.d.ts +13 -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 +2 -0
  7. package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +1 -1
  8. package/dist/adapters/drizzle/drizzle-adapter.js +6 -1
  9. package/dist/adapters/drizzle/drizzle-adapter.js.map +1 -1
  10. package/dist/adapters/drizzle/drizzle-query.js +6 -4
  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 +49 -36
  15. package/dist/adapters/drizzle/drizzle-uow-compiler.js.map +1 -1
  16. package/dist/adapters/drizzle/drizzle-uow-decoder.js +1 -1
  17. package/dist/adapters/drizzle/drizzle-uow-decoder.js.map +1 -1
  18. package/dist/adapters/drizzle/shared.d.ts +14 -1
  19. package/dist/adapters/drizzle/shared.d.ts.map +1 -0
  20. package/dist/adapters/kysely/kysely-adapter.d.ts +2 -0
  21. package/dist/adapters/kysely/kysely-adapter.d.ts.map +1 -1
  22. package/dist/adapters/kysely/kysely-adapter.js +7 -2
  23. package/dist/adapters/kysely/kysely-adapter.js.map +1 -1
  24. package/dist/adapters/kysely/kysely-query.js +5 -3
  25. package/dist/adapters/kysely/kysely-query.js.map +1 -1
  26. package/dist/adapters/kysely/kysely-shared.d.ts +11 -0
  27. package/dist/adapters/kysely/kysely-shared.d.ts.map +1 -0
  28. package/dist/adapters/kysely/kysely-uow-compiler.js +38 -9
  29. package/dist/adapters/kysely/kysely-uow-compiler.js.map +1 -1
  30. package/dist/bind-services.d.ts +7 -0
  31. package/dist/bind-services.d.ts.map +1 -0
  32. package/dist/bind-services.js +14 -0
  33. package/dist/bind-services.js.map +1 -0
  34. package/dist/fragment.d.ts +131 -12
  35. package/dist/fragment.d.ts.map +1 -1
  36. package/dist/fragment.js +107 -8
  37. package/dist/fragment.js.map +1 -1
  38. package/dist/mod.d.ts +4 -2
  39. package/dist/mod.d.ts.map +1 -1
  40. package/dist/mod.js +3 -2
  41. package/dist/mod.js.map +1 -1
  42. package/dist/query/query.d.ts +2 -2
  43. package/dist/query/query.d.ts.map +1 -1
  44. package/dist/query/unit-of-work.d.ts +100 -15
  45. package/dist/query/unit-of-work.d.ts.map +1 -1
  46. package/dist/query/unit-of-work.js +214 -7
  47. package/dist/query/unit-of-work.js.map +1 -1
  48. package/package.json +3 -3
  49. package/src/adapters/adapters.ts +14 -0
  50. package/src/adapters/drizzle/drizzle-adapter-pglite.test.ts +6 -1
  51. package/src/adapters/drizzle/drizzle-adapter-sqlite.test.ts +133 -5
  52. package/src/adapters/drizzle/drizzle-adapter.ts +16 -1
  53. package/src/adapters/drizzle/drizzle-query.ts +26 -15
  54. package/src/adapters/drizzle/drizzle-uow-compiler.test.ts +57 -57
  55. package/src/adapters/drizzle/drizzle-uow-compiler.ts +79 -39
  56. package/src/adapters/drizzle/drizzle-uow-decoder.ts +2 -5
  57. package/src/adapters/kysely/kysely-adapter-pglite.test.ts +2 -2
  58. package/src/adapters/kysely/kysely-adapter.ts +16 -1
  59. package/src/adapters/kysely/kysely-query.ts +26 -15
  60. package/src/adapters/kysely/kysely-uow-compiler.test.ts +43 -43
  61. package/src/adapters/kysely/kysely-uow-compiler.ts +50 -14
  62. package/src/adapters/kysely/kysely-uow-joins.test.ts +30 -30
  63. package/src/bind-services.test.ts +214 -0
  64. package/src/bind-services.ts +37 -0
  65. package/src/db-fragment.test.ts +800 -0
  66. package/src/fragment.ts +557 -28
  67. package/src/mod.ts +19 -0
  68. package/src/query/query.ts +2 -2
  69. package/src/query/unit-of-work-multi-schema.test.ts +64 -0
  70. package/src/query/unit-of-work-types.test.ts +13 -0
  71. package/src/query/unit-of-work.test.ts +5 -9
  72. package/src/query/unit-of-work.ts +511 -62
  73. package/src/uow-context-integration.test.ts +102 -0
  74. package/src/uow-context.test.ts +182 -0
  75. package/src/fragment.test.ts +0 -341
@@ -40,6 +40,8 @@ var UpdateManySpecialBuilder = class {
40
40
  * @param pool - Connection pool for acquiring database connections
41
41
  * @param provider - SQL provider (postgresql, mysql, sqlite, etc.)
42
42
  * @param mapper - Optional table name mapper for namespace prefixing
43
+ * @param uowConfig - Optional UOW configuration
44
+ * @param schemaNamespaceMap - Optional WeakMap for schema-to-namespace lookups
43
45
  * @returns An AbstractQuery instance for performing database operations
44
46
  *
45
47
  * @example
@@ -53,9 +55,9 @@ var UpdateManySpecialBuilder = class {
53
55
  * });
54
56
  * ```
55
57
  */
56
- function fromKysely(schema, pool, provider, mapper, uowConfig) {
58
+ function fromKysely(schema, pool, provider, mapper, uowConfig, schemaNamespaceMap) {
57
59
  function createUOW(opts) {
58
- const uowCompiler = createKyselyUOWCompiler(schema, pool, provider, mapper);
60
+ const uowCompiler = createKyselyUOWCompiler(pool, provider, mapper);
59
61
  const executor = {
60
62
  async executeRetrievalPhase(retrievalBatch) {
61
63
  if (opts.config?.dryRun) return retrievalBatch.map(() => []);
@@ -124,7 +126,7 @@ function fromKysely(schema, pool, provider, mapper, uowConfig) {
124
126
  const actualQuery = query && typeof query === "object" && "expectedAffectedRows" in query ? query.query : query;
125
127
  opts.config?.onQuery?.(actualQuery);
126
128
  }
127
- });
129
+ }, schemaNamespaceMap);
128
130
  }
129
131
  return {
130
132
  async find(tableName, builderFn) {
@@ -1 +1 @@
1
- {"version":3,"file":"kysely-query.js","names":["#indexName","#condition","#setValues","executor: UOWExecutor<CompiledQuery, unknown>","decoder: UOWDecoder<T>","cursor: Cursor | undefined","index"],"sources":["../../../src/adapters/kysely/kysely-query.ts"],"sourcesContent":["import type { AbstractQuery, TableToUpdateValues } from \"../../query/query\";\nimport type { AnySchema, AnyTable } from \"../../schema/create\";\nimport type {\n CompiledMutation,\n UOWDecoder,\n UOWExecutor,\n ValidIndexName,\n} from \"../../query/unit-of-work\";\nimport { decodeResult } from \"../../query/result-transform\";\nimport { createKyselyUOWCompiler } from \"./kysely-uow-compiler\";\nimport { executeKyselyRetrievalPhase, executeKyselyMutationPhase } from \"./kysely-uow-executor\";\nimport { UnitOfWork } from \"../../query/unit-of-work\";\nimport type { CompiledQuery, Kysely } from \"kysely\";\nimport type { TableNameMapper } from \"./kysely-shared\";\nimport type { ConnectionPool } from \"../../shared/connection-pool\";\nimport type { SQLProvider } from \"../../shared/providers\";\nimport { createCursorFromRecord, Cursor, type CursorResult } from \"../../query/cursor\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype KyselyAny = Kysely<any>;\n\n/**\n * Configuration options for creating a Kysely Unit of Work\n */\nexport interface KyselyUOWConfig {\n /**\n * Optional callback to receive compiled SQL queries for logging/debugging\n * This callback is invoked for each query as it's compiled\n */\n onQuery?: (query: CompiledQuery) => void;\n /**\n * If true, the query will not be executed and the query will be returned. Not respected for UOWs\n * since those have to be manually executed.\n */\n dryRun?: boolean;\n}\n\n/**\n * Special builder for updateMany operations that captures configuration\n */\nclass UpdateManySpecialBuilder<TTable extends AnyTable> {\n #indexName?: string;\n #condition?: unknown;\n #setValues?: TableToUpdateValues<TTable>;\n\n whereIndex<TIndexName extends ValidIndexName<TTable>>(\n indexName: TIndexName,\n condition?: unknown,\n ): this {\n this.#indexName = indexName as string;\n this.#condition = condition;\n return this;\n }\n\n set(values: TableToUpdateValues<TTable>): this {\n this.#setValues = values;\n return this;\n }\n\n getConfig() {\n return {\n indexName: this.#indexName,\n condition: this.#condition,\n setValues: this.#setValues,\n };\n }\n}\n\n/**\n * Creates a Kysely-based query engine for the given schema.\n *\n * This is the main entry point for creating a database query interface using Kysely.\n * It uses a compiler-based architecture where queries are compiled to SQL and then executed,\n * enabling features like SQL snapshot testing.\n *\n * @param schema - The database schema definition\n * @param pool - Connection pool for acquiring database connections\n * @param provider - SQL provider (postgresql, mysql, sqlite, etc.)\n * @param mapper - Optional table name mapper for namespace prefixing\n * @returns An AbstractQuery instance for performing database operations\n *\n * @example\n * ```ts\n * const pool = createSimpleConnectionPool(kysely);\n * const queryEngine = fromKysely(mySchema, pool, 'postgresql');\n *\n * const users = await queryEngine.findMany('users', {\n * where: (b) => b('age', '>', 18),\n * orderBy: [['name', 'asc']]\n * });\n * ```\n */\nexport function fromKysely<T extends AnySchema>(\n schema: T,\n pool: ConnectionPool<KyselyAny>,\n provider: SQLProvider,\n mapper?: TableNameMapper,\n uowConfig?: KyselyUOWConfig,\n): AbstractQuery<T, KyselyUOWConfig> {\n function createUOW(opts: { name?: string; config?: KyselyUOWConfig }) {\n const uowCompiler = createKyselyUOWCompiler(schema, pool, provider, mapper);\n\n const executor: UOWExecutor<CompiledQuery, unknown> = {\n async executeRetrievalPhase(retrievalBatch: CompiledQuery[]) {\n // In dryRun mode, skip execution and return empty results\n if (opts.config?.dryRun) {\n return retrievalBatch.map(() => []);\n }\n\n const conn = await pool.connect();\n try {\n return await executeKyselyRetrievalPhase(conn.db, retrievalBatch);\n } finally {\n await conn.release();\n }\n },\n async executeMutationPhase(mutationBatch: CompiledMutation<CompiledQuery>[]) {\n // In dryRun mode, skip execution and return success with mock internal IDs\n if (opts.config?.dryRun) {\n return {\n success: true,\n createdInternalIds: mutationBatch.map(() => null),\n };\n }\n\n const conn = await pool.connect();\n try {\n return await executeKyselyMutationPhase(conn.db, mutationBatch);\n } finally {\n await conn.release();\n }\n },\n };\n\n // Create a decoder function to transform raw results into application format\n const decoder: UOWDecoder<T> = (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((rows, index) => {\n const op = ops[index];\n if (!op) {\n throw new Error(\"op must be defined\");\n }\n\n // Handle count operations differently - return the count number directly\n if (op.type === \"count\") {\n const rowArray = rows as Record<string, unknown>[];\n const firstRow = rowArray[0];\n if (!firstRow) {\n return 0;\n }\n const count = Number(firstRow[\"count\"]);\n if (Number.isNaN(count)) {\n throw new Error(`Unexpected result for count, received: ${count}`);\n }\n return count;\n }\n\n // Each result is an array of rows - decode each row\n const rowArray = rows as Record<string, unknown>[];\n const decodedRows = rowArray.map((row) => decodeResult(row, op.table, provider));\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 result: CursorResult<unknown> = {\n items: decodedRows,\n cursor,\n };\n return result;\n }\n\n return decodedRows;\n });\n };\n\n const { onQuery, ...restUowConfig } = opts.config ?? {};\n\n return new UnitOfWork(schema, uowCompiler, executor, decoder, opts.name, {\n ...restUowConfig,\n onQuery: (query) => {\n // CompiledMutation has { query: CompiledQuery, expectedAffectedRows: number | null }\n // CompiledQuery has { query: QueryAST, sql: string, parameters: unknown[] }\n // Check for expectedAffectedRows to distinguish CompiledMutation from CompiledQuery\n const actualQuery =\n query && typeof query === \"object\" && \"expectedAffectedRows\" in query\n ? (query as CompiledMutation<CompiledQuery>).query\n : (query as CompiledQuery);\n\n opts.config?.onQuery?.(actualQuery);\n },\n });\n }\n\n return {\n async find(tableName, builderFn) {\n const uow = createUOW({ config: uowConfig });\n // Safe: builderFn returns a FindBuilder (or void), which matches UnitOfWork signature\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n uow.find(tableName, builderFn as any);\n // executeRetrieve returns an array of results (one per find operation)\n // Since we only have one find, unwrap the first result\n const [result]: unknown[][] = await uow.executeRetrieve();\n return result ?? [];\n },\n\n async findWithCursor(tableName, builderFn) {\n // Safe: builderFn returns a FindBuilder, which matches UnitOfWork signature\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const uow = createUOW({ config: uowConfig }).findWithCursor(tableName, builderFn as any);\n // executeRetrieve returns an array of results (one per find operation)\n // Since we only have one findWithCursor, unwrap the first result\n const [result] = await uow.executeRetrieve();\n return result as CursorResult<unknown>;\n },\n\n async findFirst(tableName, builderFn) {\n const uow = createUOW({ config: uowConfig });\n if (builderFn) {\n uow.find(tableName, (b) => {\n builderFn(b);\n return b.pageSize(1);\n });\n } else {\n uow.find(tableName, (b) => b.whereIndex(\"primary\").pageSize(1));\n }\n // executeRetrieve runs an array of `find` operation results, which each return an array of rows\n const [result]: unknown[][] = await uow.executeRetrieve();\n return result?.[0] ?? null;\n },\n\n async create(tableName, values) {\n const uow = createUOW({ config: uowConfig });\n uow.create(tableName, values);\n const { success } = await uow.executeMutations();\n if (!success) {\n throw new Error(\"Failed to create record\");\n }\n\n const createdIds = uow.getCreatedIds();\n const createdId = createdIds[0];\n if (!createdId) {\n throw new Error(\"Failed to get created ID\");\n }\n return createdId;\n },\n\n async createMany(tableName, valuesArray) {\n const uow = createUOW({ config: uowConfig });\n for (const values of valuesArray) {\n uow.create(tableName, values);\n }\n const { success } = await uow.executeMutations();\n if (!success) {\n throw new Error(\"Failed to create records\");\n }\n\n return uow.getCreatedIds();\n },\n\n async update(tableName, id, builderFn) {\n const uow = createUOW({ config: uowConfig });\n uow.update(tableName, id, builderFn);\n const { success } = await uow.executeMutations();\n if (!success) {\n throw new Error(\"Failed to update record (version conflict or record not found)\");\n }\n },\n\n async updateMany(tableName, builderFn) {\n const table = schema.tables[tableName];\n if (!table) {\n throw new Error(`Table ${tableName} not found in schema`);\n }\n\n const specialBuilder = new UpdateManySpecialBuilder<typeof table>();\n builderFn(specialBuilder);\n\n const { indexName, condition, setValues } = specialBuilder.getConfig();\n\n if (!indexName) {\n throw new Error(\"whereIndex() must be called in updateMany\");\n }\n if (!setValues) {\n throw new Error(\"set() must be called in updateMany\");\n }\n\n const findUow = createUOW({ config: uowConfig });\n findUow.find(tableName, (b) => {\n if (condition) {\n // Safe: condition is captured from whereIndex call with proper typing\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return b.whereIndex(indexName as ValidIndexName<typeof table>, condition as any);\n }\n return b.whereIndex(indexName as ValidIndexName<typeof table>);\n });\n const [records]: unknown[][] = await findUow.executeRetrieve();\n\n if (!records || records.length === 0) {\n return;\n }\n\n const updateUow = createUOW({ config: uowConfig });\n for (const record of records as Array<{ id: unknown }>) {\n updateUow.update(tableName, record.id as string, (b) => b.set(setValues));\n }\n const { success } = await updateUow.executeMutations();\n if (!success) {\n throw new Error(\"Failed to update records (version conflict)\");\n }\n },\n\n async delete(tableName, id, builderFn) {\n const uow = createUOW({ config: uowConfig });\n uow.delete(tableName, id, builderFn);\n const { success } = await uow.executeMutations();\n if (!success) {\n throw new Error(\"Failed to delete record (version conflict or record not found)\");\n }\n },\n\n async deleteMany(tableName, builderFn) {\n const findUow = createUOW({ config: uowConfig });\n findUow.find(tableName, builderFn);\n const [records]: unknown[][] = await findUow.executeRetrieve();\n\n if (!records || records.length === 0) {\n return;\n }\n\n const deleteUow = createUOW({ config: uowConfig });\n for (const record of records as Array<{ id: unknown }>) {\n deleteUow.delete(tableName, record.id as string);\n }\n const { success } = await deleteUow.executeMutations();\n if (!success) {\n throw new Error(\"Failed to delete records (version conflict)\");\n }\n },\n\n createUnitOfWork(name, nestedUowConfig) {\n return createUOW({\n name,\n config: {\n ...uowConfig,\n ...nestedUowConfig,\n },\n });\n },\n } as AbstractQuery<T, KyselyUOWConfig>;\n}\n"],"mappings":";;;;;;;;;;AAwCA,IAAM,2BAAN,MAAwD;CACtD;CACA;CACA;CAEA,WACE,WACA,WACM;AACN,QAAKA,YAAa;AAClB,QAAKC,YAAa;AAClB,SAAO;;CAGT,IAAI,QAA2C;AAC7C,QAAKC,YAAa;AAClB,SAAO;;CAGT,YAAY;AACV,SAAO;GACL,WAAW,MAAKF;GAChB,WAAW,MAAKC;GAChB,WAAW,MAAKC;GACjB;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BL,SAAgB,WACd,QACA,MACA,UACA,QACA,WACmC;CACnC,SAAS,UAAU,MAAmD;EACpE,MAAM,cAAc,wBAAwB,QAAQ,MAAM,UAAU,OAAO;EAE3E,MAAMC,WAAgD;GACpD,MAAM,sBAAsB,gBAAiC;AAE3D,QAAI,KAAK,QAAQ,OACf,QAAO,eAAe,UAAU,EAAE,CAAC;IAGrC,MAAM,OAAO,MAAM,KAAK,SAAS;AACjC,QAAI;AACF,YAAO,MAAM,4BAA4B,KAAK,IAAI,eAAe;cACzD;AACR,WAAM,KAAK,SAAS;;;GAGxB,MAAM,qBAAqB,eAAkD;AAE3E,QAAI,KAAK,QAAQ,OACf,QAAO;KACL,SAAS;KACT,oBAAoB,cAAc,UAAU,KAAK;KAClD;IAGH,MAAM,OAAO,MAAM,KAAK,SAAS;AACjC,QAAI;AACF,YAAO,MAAM,2BAA2B,KAAK,IAAI,cAAc;cACvD;AACR,WAAM,KAAK,SAAS;;;GAGzB;EAGD,MAAMC,WAA0B,YAAY,QAAQ;AAClD,OAAI,WAAW,WAAW,IAAI,OAC5B,OAAM,IAAI,MAAM,+CAA+C;AAGjE,UAAO,WAAW,KAAK,MAAM,UAAU;IACrC,MAAM,KAAK,IAAI;AACf,QAAI,CAAC,GACH,OAAM,IAAI,MAAM,qBAAqB;AAIvC,QAAI,GAAG,SAAS,SAAS;KAEvB,MAAM,WADW,KACS;AAC1B,SAAI,CAAC,SACH,QAAO;KAET,MAAM,QAAQ,OAAO,SAAS,SAAS;AACvC,SAAI,OAAO,MAAM,MAAM,CACrB,OAAM,IAAI,MAAM,0CAA0C,QAAQ;AAEpE,YAAO;;IAKT,MAAM,cADW,KACY,KAAK,QAAQ,aAAa,KAAK,GAAG,OAAO,SAAS,CAAC;AAGhF,QAAI,GAAG,YAAY;KACjB,IAAIC;AAGJ,SAAI,YAAY,SAAS,KAAK,GAAG,QAAQ,gBAAgB,GAAG,QAAQ,UAAU;MAC5E,MAAM,WAAW,YAAY,YAAY,SAAS;MAClD,MAAM,YAAY,GAAG,QAAQ,aAAa;MAG1C,IAAI;AACJ,UAAI,cAAc,WAChB,gBAAe,CAAC,GAAG,MAAM,aAAa,CAAC;WAClC;OACL,MAAMC,UAAQ,GAAG,MAAM,QAAQ;AAC/B,WAAIA,QACF,gBAAeA,QAAM;;AAIzB,UAAI,gBAAgB,SAClB,UAAS,uBAAuB,UAAqC,cAAc;OACjF,WAAW,GAAG,QAAQ,aAAa;OACnC,gBAAgB,GAAG,QAAQ,aAAa;OACxC,UAAU,GAAG,QAAQ;OACtB,CAAC;;AAQN,YAJsC;MACpC,OAAO;MACP;MACD;;AAIH,WAAO;KACP;;EAGJ,MAAM,EAAE,QAAS,GAAG,kBAAkB,KAAK,UAAU,EAAE;AAEvD,SAAO,IAAI,WAAW,QAAQ,aAAa,UAAU,SAAS,KAAK,MAAM;GACvE,GAAG;GACH,UAAU,UAAU;IAIlB,MAAM,cACJ,SAAS,OAAO,UAAU,YAAY,0BAA0B,QAC3D,MAA0C,QAC1C;AAEP,SAAK,QAAQ,UAAU,YAAY;;GAEtC,CAAC;;AAGJ,QAAO;EACL,MAAM,KAAK,WAAW,WAAW;GAC/B,MAAM,MAAM,UAAU,EAAE,QAAQ,WAAW,CAAC;AAG5C,OAAI,KAAK,WAAW,UAAiB;GAGrC,MAAM,CAAC,UAAuB,MAAM,IAAI,iBAAiB;AACzD,UAAO,UAAU,EAAE;;EAGrB,MAAM,eAAe,WAAW,WAAW;GAMzC,MAAM,CAAC,UAAU,MAHL,UAAU,EAAE,QAAQ,WAAW,CAAC,CAAC,eAAe,WAAW,UAAiB,CAG7D,iBAAiB;AAC5C,UAAO;;EAGT,MAAM,UAAU,WAAW,WAAW;GACpC,MAAM,MAAM,UAAU,EAAE,QAAQ,WAAW,CAAC;AAC5C,OAAI,UACF,KAAI,KAAK,YAAY,MAAM;AACzB,cAAU,EAAE;AACZ,WAAO,EAAE,SAAS,EAAE;KACpB;OAEF,KAAI,KAAK,YAAY,MAAM,EAAE,WAAW,UAAU,CAAC,SAAS,EAAE,CAAC;GAGjE,MAAM,CAAC,UAAuB,MAAM,IAAI,iBAAiB;AACzD,UAAO,SAAS,MAAM;;EAGxB,MAAM,OAAO,WAAW,QAAQ;GAC9B,MAAM,MAAM,UAAU,EAAE,QAAQ,WAAW,CAAC;AAC5C,OAAI,OAAO,WAAW,OAAO;GAC7B,MAAM,EAAE,YAAY,MAAM,IAAI,kBAAkB;AAChD,OAAI,CAAC,QACH,OAAM,IAAI,MAAM,0BAA0B;GAI5C,MAAM,YADa,IAAI,eAAe,CACT;AAC7B,OAAI,CAAC,UACH,OAAM,IAAI,MAAM,2BAA2B;AAE7C,UAAO;;EAGT,MAAM,WAAW,WAAW,aAAa;GACvC,MAAM,MAAM,UAAU,EAAE,QAAQ,WAAW,CAAC;AAC5C,QAAK,MAAM,UAAU,YACnB,KAAI,OAAO,WAAW,OAAO;GAE/B,MAAM,EAAE,YAAY,MAAM,IAAI,kBAAkB;AAChD,OAAI,CAAC,QACH,OAAM,IAAI,MAAM,2BAA2B;AAG7C,UAAO,IAAI,eAAe;;EAG5B,MAAM,OAAO,WAAW,IAAI,WAAW;GACrC,MAAM,MAAM,UAAU,EAAE,QAAQ,WAAW,CAAC;AAC5C,OAAI,OAAO,WAAW,IAAI,UAAU;GACpC,MAAM,EAAE,YAAY,MAAM,IAAI,kBAAkB;AAChD,OAAI,CAAC,QACH,OAAM,IAAI,MAAM,iEAAiE;;EAIrF,MAAM,WAAW,WAAW,WAAW;AAErC,OAAI,CADU,OAAO,OAAO,WAE1B,OAAM,IAAI,MAAM,SAAS,UAAU,sBAAsB;GAG3D,MAAM,iBAAiB,IAAI,0BAAwC;AACnE,aAAU,eAAe;GAEzB,MAAM,EAAE,WAAW,WAAW,cAAc,eAAe,WAAW;AAEtE,OAAI,CAAC,UACH,OAAM,IAAI,MAAM,4CAA4C;AAE9D,OAAI,CAAC,UACH,OAAM,IAAI,MAAM,qCAAqC;GAGvD,MAAM,UAAU,UAAU,EAAE,QAAQ,WAAW,CAAC;AAChD,WAAQ,KAAK,YAAY,MAAM;AAC7B,QAAI,UAGF,QAAO,EAAE,WAAW,WAA2C,UAAiB;AAElF,WAAO,EAAE,WAAW,UAA0C;KAC9D;GACF,MAAM,CAAC,WAAwB,MAAM,QAAQ,iBAAiB;AAE9D,OAAI,CAAC,WAAW,QAAQ,WAAW,EACjC;GAGF,MAAM,YAAY,UAAU,EAAE,QAAQ,WAAW,CAAC;AAClD,QAAK,MAAM,UAAU,QACnB,WAAU,OAAO,WAAW,OAAO,KAAe,MAAM,EAAE,IAAI,UAAU,CAAC;GAE3E,MAAM,EAAE,YAAY,MAAM,UAAU,kBAAkB;AACtD,OAAI,CAAC,QACH,OAAM,IAAI,MAAM,8CAA8C;;EAIlE,MAAM,OAAO,WAAW,IAAI,WAAW;GACrC,MAAM,MAAM,UAAU,EAAE,QAAQ,WAAW,CAAC;AAC5C,OAAI,OAAO,WAAW,IAAI,UAAU;GACpC,MAAM,EAAE,YAAY,MAAM,IAAI,kBAAkB;AAChD,OAAI,CAAC,QACH,OAAM,IAAI,MAAM,iEAAiE;;EAIrF,MAAM,WAAW,WAAW,WAAW;GACrC,MAAM,UAAU,UAAU,EAAE,QAAQ,WAAW,CAAC;AAChD,WAAQ,KAAK,WAAW,UAAU;GAClC,MAAM,CAAC,WAAwB,MAAM,QAAQ,iBAAiB;AAE9D,OAAI,CAAC,WAAW,QAAQ,WAAW,EACjC;GAGF,MAAM,YAAY,UAAU,EAAE,QAAQ,WAAW,CAAC;AAClD,QAAK,MAAM,UAAU,QACnB,WAAU,OAAO,WAAW,OAAO,GAAa;GAElD,MAAM,EAAE,YAAY,MAAM,UAAU,kBAAkB;AACtD,OAAI,CAAC,QACH,OAAM,IAAI,MAAM,8CAA8C;;EAIlE,iBAAiB,MAAM,iBAAiB;AACtC,UAAO,UAAU;IACf;IACA,QAAQ;KACN,GAAG;KACH,GAAG;KACJ;IACF,CAAC;;EAEL"}
1
+ {"version":3,"file":"kysely-query.js","names":["#indexName","#condition","#setValues","executor: UOWExecutor<CompiledQuery, unknown>","decoder: UOWDecoder<unknown>","cursor: Cursor | undefined","index"],"sources":["../../../src/adapters/kysely/kysely-query.ts"],"sourcesContent":["import type { AbstractQuery, TableToUpdateValues } from \"../../query/query\";\nimport type { AnySchema, AnyTable } from \"../../schema/create\";\nimport type {\n CompiledMutation,\n UOWDecoder,\n UOWExecutor,\n ValidIndexName,\n} from \"../../query/unit-of-work\";\nimport { decodeResult } from \"../../query/result-transform\";\nimport { createKyselyUOWCompiler } from \"./kysely-uow-compiler\";\nimport { executeKyselyRetrievalPhase, executeKyselyMutationPhase } from \"./kysely-uow-executor\";\nimport { UnitOfWork } from \"../../query/unit-of-work\";\nimport type { CompiledQuery, Kysely } from \"kysely\";\nimport type { TableNameMapper } from \"./kysely-shared\";\nimport type { ConnectionPool } from \"../../shared/connection-pool\";\nimport type { SQLProvider } from \"../../shared/providers\";\nimport { createCursorFromRecord, Cursor, type CursorResult } from \"../../query/cursor\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype KyselyAny = Kysely<any>;\n\n/**\n * Configuration options for creating a Kysely Unit of Work\n */\nexport interface KyselyUOWConfig {\n /**\n * Optional callback to receive compiled SQL queries for logging/debugging\n * This callback is invoked for each query as it's compiled\n */\n onQuery?: (query: CompiledQuery) => void;\n /**\n * If true, the query will not be executed and the query will be returned. Not respected for UOWs\n * since those have to be manually executed.\n */\n dryRun?: boolean;\n}\n\n/**\n * Special builder for updateMany operations that captures configuration\n */\nclass UpdateManySpecialBuilder<TTable extends AnyTable> {\n #indexName?: string;\n #condition?: unknown;\n #setValues?: TableToUpdateValues<TTable>;\n\n whereIndex<TIndexName extends ValidIndexName<TTable>>(\n indexName: TIndexName,\n condition?: unknown,\n ): this {\n this.#indexName = indexName as string;\n this.#condition = condition;\n return this;\n }\n\n set(values: TableToUpdateValues<TTable>): this {\n this.#setValues = values;\n return this;\n }\n\n getConfig() {\n return {\n indexName: this.#indexName,\n condition: this.#condition,\n setValues: this.#setValues,\n };\n }\n}\n\n/**\n * Creates a Kysely-based query engine for the given schema.\n *\n * This is the main entry point for creating a database query interface using Kysely.\n * It uses a compiler-based architecture where queries are compiled to SQL and then executed,\n * enabling features like SQL snapshot testing.\n *\n * @param schema - The database schema definition\n * @param pool - Connection pool for acquiring database connections\n * @param provider - SQL provider (postgresql, mysql, sqlite, etc.)\n * @param mapper - Optional table name mapper for namespace prefixing\n * @param uowConfig - Optional UOW configuration\n * @param schemaNamespaceMap - Optional WeakMap for schema-to-namespace lookups\n * @returns An AbstractQuery instance for performing database operations\n *\n * @example\n * ```ts\n * const pool = createSimpleConnectionPool(kysely);\n * const queryEngine = fromKysely(mySchema, pool, 'postgresql');\n *\n * const users = await queryEngine.findMany('users', {\n * where: (b) => b('age', '>', 18),\n * orderBy: [['name', 'asc']]\n * });\n * ```\n */\nexport function fromKysely<T extends AnySchema>(\n schema: T,\n pool: ConnectionPool<KyselyAny>,\n provider: SQLProvider,\n mapper?: TableNameMapper,\n uowConfig?: KyselyUOWConfig,\n schemaNamespaceMap?: WeakMap<AnySchema, string>,\n): AbstractQuery<T, KyselyUOWConfig> {\n function createUOW(opts: { name?: string; config?: KyselyUOWConfig }) {\n const uowCompiler = createKyselyUOWCompiler(pool, provider, mapper);\n\n const executor: UOWExecutor<CompiledQuery, unknown> = {\n async executeRetrievalPhase(retrievalBatch: CompiledQuery[]) {\n // In dryRun mode, skip execution and return empty results\n if (opts.config?.dryRun) {\n return retrievalBatch.map(() => []);\n }\n\n const conn = await pool.connect();\n try {\n return await executeKyselyRetrievalPhase(conn.db, retrievalBatch);\n } finally {\n await conn.release();\n }\n },\n async executeMutationPhase(mutationBatch: CompiledMutation<CompiledQuery>[]) {\n // In dryRun mode, skip execution and return success with mock internal IDs\n if (opts.config?.dryRun) {\n return {\n success: true,\n createdInternalIds: mutationBatch.map(() => null),\n };\n }\n\n const conn = await pool.connect();\n try {\n return await executeKyselyMutationPhase(conn.db, mutationBatch);\n } finally {\n await conn.release();\n }\n },\n };\n\n // Create a decoder function to transform raw results into application format\n const decoder: UOWDecoder<unknown> = (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((rows, index) => {\n const op = ops[index];\n if (!op) {\n throw new Error(\"op must be defined\");\n }\n\n // Handle count operations differently - return the count number directly\n if (op.type === \"count\") {\n const rowArray = rows as Record<string, unknown>[];\n const firstRow = rowArray[0];\n if (!firstRow) {\n return 0;\n }\n const count = Number(firstRow[\"count\"]);\n if (Number.isNaN(count)) {\n throw new Error(`Unexpected result for count, received: ${count}`);\n }\n return count;\n }\n\n // Each result is an array of rows - decode each row\n const rowArray = rows as Record<string, unknown>[];\n const decodedRows = rowArray.map((row) => decodeResult(row, op.table, provider));\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 result: CursorResult<unknown> = {\n items: decodedRows,\n cursor,\n };\n return result;\n }\n\n return decodedRows;\n });\n };\n\n const { onQuery, ...restUowConfig } = opts.config ?? {};\n\n return new UnitOfWork(\n schema,\n uowCompiler,\n executor,\n decoder,\n opts.name,\n {\n ...restUowConfig,\n onQuery: (query) => {\n // CompiledMutation has { query: CompiledQuery, expectedAffectedRows: number | null }\n // CompiledQuery has { query: QueryAST, sql: string, parameters: unknown[] }\n // Check for expectedAffectedRows to distinguish CompiledMutation from CompiledQuery\n const actualQuery =\n query && typeof query === \"object\" && \"expectedAffectedRows\" in query\n ? (query as CompiledMutation<CompiledQuery>).query\n : (query as CompiledQuery);\n\n opts.config?.onQuery?.(actualQuery);\n },\n },\n schemaNamespaceMap,\n );\n }\n\n return {\n async find(tableName, builderFn) {\n const uow = createUOW({ config: uowConfig });\n // Safe: builderFn returns a FindBuilder (or void), which matches UnitOfWork signature\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n uow.find(tableName, builderFn as any);\n // executeRetrieve returns an array of results (one per find operation)\n // Since we only have one find, unwrap the first result\n const [result]: unknown[][] = await uow.executeRetrieve();\n return result ?? [];\n },\n\n async findWithCursor(tableName, builderFn) {\n // Safe: builderFn returns a FindBuilder, which matches UnitOfWork signature\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const uow = createUOW({ config: uowConfig }).findWithCursor(tableName, builderFn as any);\n // executeRetrieve returns an array of results (one per find operation)\n // Since we only have one findWithCursor, unwrap the first result\n const [result] = await uow.executeRetrieve();\n return result as CursorResult<unknown>;\n },\n\n async findFirst(tableName, builderFn) {\n const uow = createUOW({ config: uowConfig });\n if (builderFn) {\n uow.find(tableName, (b) => {\n builderFn(b);\n return b.pageSize(1);\n });\n } else {\n uow.find(tableName, (b) => b.whereIndex(\"primary\").pageSize(1));\n }\n // executeRetrieve runs an array of `find` operation results, which each return an array of rows\n const [result]: unknown[][] = await uow.executeRetrieve();\n return result?.[0] ?? null;\n },\n\n async create(tableName, values) {\n const uow = createUOW({ config: uowConfig });\n uow.create(tableName, values);\n const { success } = await uow.executeMutations();\n if (!success) {\n throw new Error(\"Failed to create record\");\n }\n\n const createdIds = uow.getCreatedIds();\n const createdId = createdIds[0];\n if (!createdId) {\n throw new Error(\"Failed to get created ID\");\n }\n return createdId;\n },\n\n async createMany(tableName, valuesArray) {\n const uow = createUOW({ config: uowConfig });\n for (const values of valuesArray) {\n uow.create(tableName, values);\n }\n const { success } = await uow.executeMutations();\n if (!success) {\n throw new Error(\"Failed to create records\");\n }\n\n return uow.getCreatedIds();\n },\n\n async update(tableName, id, builderFn) {\n const uow = createUOW({ config: uowConfig });\n uow.update(tableName, id, builderFn);\n const { success } = await uow.executeMutations();\n if (!success) {\n throw new Error(\"Failed to update record (version conflict or record not found)\");\n }\n },\n\n async updateMany(tableName, builderFn) {\n const table = schema.tables[tableName];\n if (!table) {\n throw new Error(`Table ${tableName} not found in schema`);\n }\n\n const specialBuilder = new UpdateManySpecialBuilder<typeof table>();\n builderFn(specialBuilder);\n\n const { indexName, condition, setValues } = specialBuilder.getConfig();\n\n if (!indexName) {\n throw new Error(\"whereIndex() must be called in updateMany\");\n }\n if (!setValues) {\n throw new Error(\"set() must be called in updateMany\");\n }\n\n const findUow = createUOW({ config: uowConfig });\n findUow.find(tableName, (b) => {\n if (condition) {\n // Safe: condition is captured from whereIndex call with proper typing\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return b.whereIndex(indexName as ValidIndexName<typeof table>, condition as any);\n }\n return b.whereIndex(indexName as ValidIndexName<typeof table>);\n });\n const [records]: unknown[][] = await findUow.executeRetrieve();\n\n if (!records || records.length === 0) {\n return;\n }\n\n const updateUow = createUOW({ config: uowConfig });\n for (const record of records as Array<{ id: unknown }>) {\n updateUow.update(tableName, record.id as string, (b) => b.set(setValues));\n }\n const { success } = await updateUow.executeMutations();\n if (!success) {\n throw new Error(\"Failed to update records (version conflict)\");\n }\n },\n\n async delete(tableName, id, builderFn) {\n const uow = createUOW({ config: uowConfig });\n uow.delete(tableName, id, builderFn);\n const { success } = await uow.executeMutations();\n if (!success) {\n throw new Error(\"Failed to delete record (version conflict or record not found)\");\n }\n },\n\n async deleteMany(tableName, builderFn) {\n const findUow = createUOW({ config: uowConfig });\n findUow.find(tableName, builderFn);\n const [records]: unknown[][] = await findUow.executeRetrieve();\n\n if (!records || records.length === 0) {\n return;\n }\n\n const deleteUow = createUOW({ config: uowConfig });\n for (const record of records as Array<{ id: unknown }>) {\n deleteUow.delete(tableName, record.id as string);\n }\n const { success } = await deleteUow.executeMutations();\n if (!success) {\n throw new Error(\"Failed to delete records (version conflict)\");\n }\n },\n\n createUnitOfWork(name, nestedUowConfig) {\n return createUOW({\n name,\n config: {\n ...uowConfig,\n ...nestedUowConfig,\n },\n });\n },\n } as AbstractQuery<T, KyselyUOWConfig>;\n}\n"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ //#region src/adapters/kysely/kysely-shared.d.ts
2
+ /**
3
+ * Maps logical table names (used by fragment authors) to physical table names (with namespace suffix)
4
+ */
5
+ interface TableNameMapper {
6
+ toPhysical(logicalName: string): string;
7
+ toLogical(physicalName: string): string;
8
+ }
9
+ //#endregion
10
+ export { TableNameMapper };
11
+ //# sourceMappingURL=kysely-shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kysely-shared.d.ts","names":[],"sources":["../../../src/adapters/kysely/kysely-shared.ts"],"sourcesContent":[],"mappings":";;AAGA;;UAAiB,eAAA"}
@@ -2,6 +2,7 @@ import { decodeCursor, serializeCursorValues } from "../../query/cursor.js";
2
2
  import { buildCondition } from "../../query/condition-builder.js";
3
3
  import { createKyselyQueryBuilder } from "./kysely-query-builder.js";
4
4
  import { createKyselyQueryCompiler } from "./kysely-query-compiler.js";
5
+ import { createTableNameMapper } from "./kysely-shared.js";
5
6
 
6
7
  //#region src/adapters/kysely/kysely-uow-compiler.ts
7
8
  /**
@@ -10,22 +11,49 @@ import { createKyselyQueryCompiler } from "./kysely-query-compiler.js";
10
11
  * This compiler translates UOW operations into Kysely CompiledQuery objects
11
12
  * that can be executed as a batch/transaction.
12
13
  *
13
- * @param schema - The database schema
14
14
  * @param pool - Connection pool for acquiring database connections
15
15
  * @param provider - SQL provider (postgresql, mysql, sqlite, etc.)
16
- * @param mapper - Optional table name mapper for namespace prefixing
16
+ * @param mapper - Optional table name mapper for namespace prefixing (fallback for operations without explicit namespace)
17
17
  * @returns A UOWCompiler instance for Kysely
18
18
  */
19
- function createKyselyUOWCompiler(schema, pool, provider, mapper) {
20
- const queryCompiler = createKyselyQueryCompiler(schema, pool, provider, mapper);
21
- const queryBuilder = createKyselyQueryBuilder(pool.getDatabaseSync(), provider, mapper);
22
- function toTable(name) {
19
+ function createKyselyUOWCompiler(pool, provider, mapper) {
20
+ const kysely = pool.getDatabaseSync();
21
+ /**
22
+ * Get the mapper for a specific operation
23
+ * Uses operation's namespace if provided, otherwise falls back to the default mapper
24
+ */
25
+ function getMapperForOperation(namespace) {
26
+ if (namespace) return createTableNameMapper(namespace);
27
+ return mapper;
28
+ }
29
+ const compilerCache = /* @__PURE__ */ new Map();
30
+ const builderCache = /* @__PURE__ */ new Map();
31
+ function getQueryCompiler(schema, namespace) {
32
+ const cacheKey = namespace;
33
+ let compiler = compilerCache.get(cacheKey);
34
+ if (!compiler) {
35
+ compiler = createKyselyQueryCompiler(schema, pool, provider, getMapperForOperation(namespace));
36
+ compilerCache.set(cacheKey, compiler);
37
+ }
38
+ return compiler;
39
+ }
40
+ function getQueryBuilder(namespace) {
41
+ const cacheKey = namespace;
42
+ let builder = builderCache.get(cacheKey);
43
+ if (!builder) {
44
+ builder = createKyselyQueryBuilder(kysely, provider, getMapperForOperation(namespace));
45
+ builderCache.set(cacheKey, builder);
46
+ }
47
+ return builder;
48
+ }
49
+ function toTable(schema, name) {
23
50
  const table = schema.tables[name];
24
51
  if (!table) throw new Error(`Invalid table name ${name}.`);
25
52
  return table;
26
53
  }
27
54
  return {
28
55
  compileRetrievalOperation(op) {
56
+ const queryCompiler = getQueryCompiler(op.schema, op.namespace);
29
57
  switch (op.type) {
30
58
  case "count": return queryCompiler.count(op.table.name, { where: op.options.where });
31
59
  case "find": {
@@ -70,7 +98,7 @@ function createKyselyUOWCompiler(schema, pool, provider, mapper) {
70
98
  items: [combinedWhere, cursorCondition]
71
99
  };
72
100
  else combinedWhere = cursorCondition;
73
- if (join && join.length > 0) return queryBuilder.findMany(op.table, {
101
+ if (join && join.length > 0) return getQueryBuilder(op.namespace).findMany(op.table, {
74
102
  select: findManyOptions.select ?? true,
75
103
  where: combinedWhere,
76
104
  orderBy,
@@ -87,13 +115,14 @@ function createKyselyUOWCompiler(schema, pool, provider, mapper) {
87
115
  }
88
116
  },
89
117
  compileMutationOperation(op) {
118
+ const queryCompiler = getQueryCompiler(op.schema, op.namespace);
90
119
  switch (op.type) {
91
120
  case "create": return {
92
121
  query: queryCompiler.create(op.table, op.values),
93
122
  expectedAffectedRows: null
94
123
  };
95
124
  case "update": {
96
- const table = toTable(op.table);
125
+ const table = toTable(op.schema, op.table);
97
126
  const idColumn = table.getIdColumn();
98
127
  const versionColumn = table.getVersionColumn();
99
128
  const externalId = typeof op.id === "string" ? op.id : op.id.externalId;
@@ -109,7 +138,7 @@ function createKyselyUOWCompiler(schema, pool, provider, mapper) {
109
138
  } : null;
110
139
  }
111
140
  case "delete": {
112
- const table = toTable(op.table);
141
+ const table = toTable(op.schema, op.table);
113
142
  const idColumn = table.getIdColumn();
114
143
  const versionColumn = table.getVersionColumn();
115
144
  const externalId = typeof op.id === "string" ? op.id : op.id.externalId;
@@ -1 +1 @@
1
- {"version":3,"file":"kysely-uow-compiler.js","names":["indexColumns: AnyColumn[]","orderDirection: \"asc\" | \"desc\"","orderBy: [AnyColumn, \"asc\" | \"desc\"][] | undefined","cursorCondition: Condition | undefined","combinedWhere: Condition | undefined"],"sources":["../../../src/adapters/kysely/kysely-uow-compiler.ts"],"sourcesContent":["import type { CompiledQuery, Kysely } from \"kysely\";\nimport type { AnyColumn, AnySchema, FragnoId } from \"../../schema/create\";\nimport type {\n CompiledMutation,\n MutationOperation,\n RetrievalOperation,\n UOWCompiler,\n} from \"../../query/unit-of-work\";\nimport { createKyselyQueryCompiler } from \"./kysely-query-compiler\";\nimport { createKyselyQueryBuilder } from \"./kysely-query-builder\";\nimport { buildCondition, type Condition } from \"../../query/condition-builder\";\nimport { decodeCursor, serializeCursorValues } from \"../../query/cursor\";\nimport type { AnySelectClause } from \"../../query/query\";\nimport type { TableNameMapper } from \"./kysely-shared\";\nimport type { ConnectionPool } from \"../../shared/connection-pool\";\nimport type { SQLProvider } from \"../../shared/providers\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype KyselyAny = Kysely<any>;\n\n/**\n * Create a Kysely-specific Unit of Work compiler\n *\n * This compiler translates UOW operations into Kysely CompiledQuery objects\n * that can be executed as a batch/transaction.\n *\n * @param schema - The database schema\n * @param pool - Connection pool for acquiring database connections\n * @param provider - SQL provider (postgresql, mysql, sqlite, etc.)\n * @param mapper - Optional table name mapper for namespace prefixing\n * @returns A UOWCompiler instance for Kysely\n */\nexport function createKyselyUOWCompiler<TSchema extends AnySchema>(\n schema: TSchema,\n pool: ConnectionPool<KyselyAny>,\n provider: SQLProvider,\n mapper?: TableNameMapper,\n): UOWCompiler<TSchema, CompiledQuery> {\n const queryCompiler = createKyselyQueryCompiler(schema, pool, provider, mapper);\n // Get kysely instance for query building (compilation doesn't execute, just builds SQL)\n const kysely = pool.getDatabaseSync();\n const queryBuilder = createKyselyQueryBuilder(kysely, provider, mapper);\n\n function toTable(name: unknown) {\n const table = schema.tables[name as string];\n if (!table) {\n throw new Error(`Invalid table name ${name}.`);\n }\n return table;\n }\n\n return {\n compileRetrievalOperation(op: RetrievalOperation<TSchema>): CompiledQuery | null {\n switch (op.type) {\n case \"count\": {\n return queryCompiler.count(op.table.name, {\n where: op.options.where,\n });\n }\n\n case \"find\": {\n // Map UOW FindOptions to query compiler's FindManyOptions\n const {\n useIndex: _useIndex,\n orderByIndex,\n joins: join,\n after,\n before,\n pageSize,\n ...findManyOptions\n } = op.options;\n\n // Get index columns for ordering and cursor pagination\n let indexColumns: AnyColumn[] = [];\n let orderDirection: \"asc\" | \"desc\" = \"asc\";\n\n if (orderByIndex) {\n const index = op.table.indexes[orderByIndex.indexName];\n orderDirection = orderByIndex.direction;\n\n if (!index) {\n // If _primary index doesn't exist, fall back to internal ID column\n // (which is the actual primary key and maintains insertion order)\n if (orderByIndex.indexName === \"_primary\") {\n indexColumns = [op.table.getIdColumn()];\n } else {\n throw new Error(\n `Index \"${orderByIndex.indexName}\" not found on table \"${op.table.name}\"`,\n );\n }\n } else {\n // Order by all columns in the index with the specified direction\n indexColumns = index.columns;\n }\n }\n\n // Convert orderByIndex to orderBy format\n let orderBy: [AnyColumn, \"asc\" | \"desc\"][] | undefined;\n if (indexColumns.length > 0) {\n orderBy = indexColumns.map((col) => [col, orderDirection]);\n }\n\n // Handle cursor pagination - build a cursor condition\n let cursorCondition: Condition | undefined;\n\n if ((after || before) && indexColumns.length > 0) {\n const cursor = after || before;\n // Decode cursor if it's a string, otherwise use it as-is\n const cursorObj = typeof cursor === \"string\" ? decodeCursor(cursor!) : cursor!;\n const serializedValues = serializeCursorValues(cursorObj, indexColumns, provider);\n\n // Build tuple comparison for cursor pagination\n // For \"after\" with \"asc\": (col1, col2, ...) > (val1, val2, ...)\n // For \"before\" with \"desc\": reverse the comparison\n const isAfter = !!after;\n const useGreaterThan =\n (isAfter && orderDirection === \"asc\") || (!isAfter && orderDirection === \"desc\");\n\n if (indexColumns.length === 1) {\n // Simple single-column case\n const col = indexColumns[0]!;\n const val = serializedValues[col.ormName];\n const operator = useGreaterThan ? \">\" : \"<\";\n cursorCondition = {\n type: \"compare\",\n a: col,\n operator,\n b: val,\n };\n } else {\n // Multi-column tuple comparison - not yet supported for Kysely\n throw new Error(\n \"Multi-column cursor pagination is not yet supported in Kysely Unit of Work implementation\",\n );\n }\n }\n\n // Combine user where clause with cursor condition\n let combinedWhere: Condition | undefined;\n if (findManyOptions.where) {\n const whereResult = buildCondition(op.table.columns, findManyOptions.where);\n if (whereResult === true) {\n combinedWhere = undefined;\n } else if (whereResult === false) {\n return null;\n } else {\n combinedWhere = whereResult;\n }\n }\n\n if (cursorCondition) {\n if (combinedWhere) {\n combinedWhere = {\n type: \"and\",\n items: [combinedWhere, cursorCondition],\n };\n } else {\n combinedWhere = cursorCondition;\n }\n }\n\n // When we have joins or need to bypass buildFindOptions, use queryBuilder directly\n if (join && join.length > 0) {\n return queryBuilder.findMany(op.table, {\n // Safe cast: select from UOW matches SimplifyFindOptions requirement\n select: (findManyOptions.select ?? true) as AnySelectClause,\n where: combinedWhere,\n orderBy,\n limit: pageSize,\n join,\n });\n }\n\n return queryCompiler.findMany(op.table.name, {\n ...findManyOptions,\n where: combinedWhere ? () => combinedWhere! : undefined,\n orderBy: orderBy?.map(([col, dir]) => [col.ormName, dir]),\n limit: pageSize,\n });\n }\n }\n },\n\n compileMutationOperation(\n op: MutationOperation<TSchema>,\n ): CompiledMutation<CompiledQuery> | null {\n switch (op.type) {\n case \"create\":\n // queryCompiler.create() calls encodeValues() which handles runtime defaults\n return {\n query: queryCompiler.create(op.table, op.values),\n expectedAffectedRows: null, // creates don't need affected row checks\n };\n\n case \"update\": {\n const table = toTable(op.table);\n const idColumn = table.getIdColumn();\n const versionColumn = table.getVersionColumn();\n\n const externalId = typeof op.id === \"string\" ? op.id : op.id.externalId;\n const versionToCheck = getVersionToCheck(op.id, op.checkVersion);\n\n // Build WHERE clause that filters by ID and optionally by version\n const whereClause =\n versionToCheck !== undefined\n ? () =>\n buildCondition(table.columns, (eb) =>\n eb.and(\n eb(idColumn.ormName, \"=\", externalId),\n eb(versionColumn.ormName, \"=\", versionToCheck),\n ),\n )\n : () => buildCondition(table.columns, (eb) => eb(idColumn.ormName, \"=\", externalId));\n\n const query = queryCompiler.updateMany(op.table, {\n where: whereClause,\n set: op.set,\n });\n\n return query\n ? {\n query,\n expectedAffectedRows: op.checkVersion ? 1 : null,\n }\n : null;\n }\n\n case \"delete\": {\n const table = toTable(op.table);\n const idColumn = table.getIdColumn();\n const versionColumn = table.getVersionColumn();\n\n // Extract external ID based on whether op.id is FragnoId or string\n const externalId = typeof op.id === \"string\" ? op.id : op.id.externalId;\n const versionToCheck = getVersionToCheck(op.id, op.checkVersion);\n\n // Build WHERE clause that filters by ID and optionally by version\n const whereClause =\n versionToCheck !== undefined\n ? () =>\n buildCondition(table.columns, (eb) =>\n eb.and(\n eb(idColumn.ormName, \"=\", externalId),\n eb(versionColumn.ormName, \"=\", versionToCheck),\n ),\n )\n : () => buildCondition(table.columns, (eb) => eb(idColumn.ormName, \"=\", externalId));\n\n const query = queryCompiler.deleteMany(op.table, {\n where: whereClause,\n });\n\n return query\n ? {\n query,\n expectedAffectedRows: op.checkVersion ? 1 : null,\n }\n : null;\n }\n }\n },\n };\n}\n\n/**\n * Get the version to check for a given ID and checkVersion flag.\n * @returns The version to check or undefined if no check is required.\n * @throws Error if the ID is a string and checkVersion is true.\n */\nfunction getVersionToCheck(id: FragnoId | string, checkVersion: boolean): number | undefined {\n if (!checkVersion) {\n return undefined;\n }\n\n if (typeof id === \"string\") {\n throw new Error(\n `Cannot use checkVersion with a string ID. Version checking requires a FragnoId with version information.`,\n );\n }\n\n return id.version;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"kysely-uow-compiler.js","names":["indexColumns: AnyColumn[]","orderDirection: \"asc\" | \"desc\"","orderBy: [AnyColumn, \"asc\" | \"desc\"][] | undefined","cursorCondition: Condition | undefined","combinedWhere: Condition | undefined"],"sources":["../../../src/adapters/kysely/kysely-uow-compiler.ts"],"sourcesContent":["import type { CompiledQuery, Kysely } from \"kysely\";\nimport type { AnyColumn, AnySchema, FragnoId } from \"../../schema/create\";\nimport type {\n CompiledMutation,\n MutationOperation,\n RetrievalOperation,\n UOWCompiler,\n} from \"../../query/unit-of-work\";\nimport { createKyselyQueryCompiler } from \"./kysely-query-compiler\";\nimport { createKyselyQueryBuilder } from \"./kysely-query-builder\";\nimport { buildCondition, type Condition } from \"../../query/condition-builder\";\nimport { decodeCursor, serializeCursorValues } from \"../../query/cursor\";\nimport type { AnySelectClause } from \"../../query/query\";\nimport { type TableNameMapper, createTableNameMapper } from \"./kysely-shared\";\nimport type { ConnectionPool } from \"../../shared/connection-pool\";\nimport type { SQLProvider } from \"../../shared/providers\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype KyselyAny = Kysely<any>;\n\n/**\n * Create a Kysely-specific Unit of Work compiler\n *\n * This compiler translates UOW operations into Kysely CompiledQuery objects\n * that can be executed as a batch/transaction.\n *\n * @param pool - Connection pool for acquiring database connections\n * @param provider - SQL provider (postgresql, mysql, sqlite, etc.)\n * @param mapper - Optional table name mapper for namespace prefixing (fallback for operations without explicit namespace)\n * @returns A UOWCompiler instance for Kysely\n */\nexport function createKyselyUOWCompiler(\n pool: ConnectionPool<KyselyAny>,\n provider: SQLProvider,\n mapper?: TableNameMapper,\n): UOWCompiler<CompiledQuery> {\n // Get kysely instance for query building (compilation doesn't execute, just builds SQL)\n const kysely = pool.getDatabaseSync();\n\n /**\n * Get the mapper for a specific operation\n * Uses operation's namespace if provided, otherwise falls back to the default mapper\n */\n function getMapperForOperation(namespace: string | undefined): TableNameMapper | undefined {\n if (namespace) {\n return createTableNameMapper(namespace);\n }\n return mapper;\n }\n\n // Cache query compilers and builders by namespace for performance\n const compilerCache = new Map<string | undefined, ReturnType<typeof createKyselyQueryCompiler>>();\n const builderCache = new Map<string | undefined, ReturnType<typeof createKyselyQueryBuilder>>();\n\n function getQueryCompiler(schema: AnySchema, namespace: string | undefined) {\n const cacheKey = namespace;\n let compiler = compilerCache.get(cacheKey);\n if (!compiler) {\n const opMapper = getMapperForOperation(namespace);\n compiler = createKyselyQueryCompiler(schema, pool, provider, opMapper);\n compilerCache.set(cacheKey, compiler);\n }\n return compiler;\n }\n\n function getQueryBuilder(namespace: string | undefined) {\n const cacheKey = namespace;\n let builder = builderCache.get(cacheKey);\n if (!builder) {\n const opMapper = getMapperForOperation(namespace);\n builder = createKyselyQueryBuilder(kysely, provider, opMapper);\n builderCache.set(cacheKey, builder);\n }\n return builder;\n }\n\n function toTable(schema: AnySchema, name: unknown) {\n const table = schema.tables[name as string];\n if (!table) {\n throw new Error(`Invalid table name ${name}.`);\n }\n return table;\n }\n\n return {\n compileRetrievalOperation(op: RetrievalOperation<AnySchema>): CompiledQuery | null {\n const queryCompiler = getQueryCompiler(op.schema, op.namespace);\n switch (op.type) {\n case \"count\": {\n return queryCompiler.count(op.table.name, {\n where: op.options.where,\n });\n }\n\n case \"find\": {\n // Map UOW FindOptions to query compiler's FindManyOptions\n const {\n useIndex: _useIndex,\n orderByIndex,\n joins: join,\n after,\n before,\n pageSize,\n ...findManyOptions\n } = op.options;\n\n // Get index columns for ordering and cursor pagination\n let indexColumns: AnyColumn[] = [];\n let orderDirection: \"asc\" | \"desc\" = \"asc\";\n\n if (orderByIndex) {\n const index = op.table.indexes[orderByIndex.indexName];\n orderDirection = orderByIndex.direction;\n\n if (!index) {\n // If _primary index doesn't exist, fall back to internal ID column\n // (which is the actual primary key and maintains insertion order)\n if (orderByIndex.indexName === \"_primary\") {\n indexColumns = [op.table.getIdColumn()];\n } else {\n throw new Error(\n `Index \"${orderByIndex.indexName}\" not found on table \"${op.table.name}\"`,\n );\n }\n } else {\n // Order by all columns in the index with the specified direction\n indexColumns = index.columns;\n }\n }\n\n // Convert orderByIndex to orderBy format\n let orderBy: [AnyColumn, \"asc\" | \"desc\"][] | undefined;\n if (indexColumns.length > 0) {\n orderBy = indexColumns.map((col) => [col, orderDirection]);\n }\n\n // Handle cursor pagination - build a cursor condition\n let cursorCondition: Condition | undefined;\n\n if ((after || before) && indexColumns.length > 0) {\n const cursor = after || before;\n // Decode cursor if it's a string, otherwise use it as-is\n const cursorObj = typeof cursor === \"string\" ? decodeCursor(cursor!) : cursor!;\n const serializedValues = serializeCursorValues(cursorObj, indexColumns, provider);\n\n // Build tuple comparison for cursor pagination\n // For \"after\" with \"asc\": (col1, col2, ...) > (val1, val2, ...)\n // For \"before\" with \"desc\": reverse the comparison\n const isAfter = !!after;\n const useGreaterThan =\n (isAfter && orderDirection === \"asc\") || (!isAfter && orderDirection === \"desc\");\n\n if (indexColumns.length === 1) {\n // Simple single-column case\n const col = indexColumns[0]!;\n const val = serializedValues[col.ormName];\n const operator = useGreaterThan ? \">\" : \"<\";\n cursorCondition = {\n type: \"compare\",\n a: col,\n operator,\n b: val,\n };\n } else {\n // Multi-column tuple comparison - not yet supported for Kysely\n throw new Error(\n \"Multi-column cursor pagination is not yet supported in Kysely Unit of Work implementation\",\n );\n }\n }\n\n // Combine user where clause with cursor condition\n let combinedWhere: Condition | undefined;\n if (findManyOptions.where) {\n const whereResult = buildCondition(op.table.columns, findManyOptions.where);\n if (whereResult === true) {\n combinedWhere = undefined;\n } else if (whereResult === false) {\n return null;\n } else {\n combinedWhere = whereResult;\n }\n }\n\n if (cursorCondition) {\n if (combinedWhere) {\n combinedWhere = {\n type: \"and\",\n items: [combinedWhere, cursorCondition],\n };\n } else {\n combinedWhere = cursorCondition;\n }\n }\n\n // When we have joins or need to bypass buildFindOptions, use operation-specific queryBuilder\n if (join && join.length > 0) {\n const queryBuilder = getQueryBuilder(op.namespace);\n return queryBuilder.findMany(op.table, {\n // Safe cast: select from UOW matches SimplifyFindOptions requirement\n select: (findManyOptions.select ?? true) as AnySelectClause,\n where: combinedWhere,\n orderBy,\n limit: pageSize,\n join,\n });\n }\n\n return queryCompiler.findMany(op.table.name, {\n ...findManyOptions,\n where: combinedWhere ? () => combinedWhere! : undefined,\n orderBy: orderBy?.map(([col, dir]) => [col.ormName, dir]),\n limit: pageSize,\n });\n }\n }\n },\n\n compileMutationOperation(\n op: MutationOperation<AnySchema>,\n ): CompiledMutation<CompiledQuery> | null {\n const queryCompiler = getQueryCompiler(op.schema, op.namespace);\n switch (op.type) {\n case \"create\":\n // queryCompiler.create() calls encodeValues() which handles runtime defaults\n return {\n query: queryCompiler.create(op.table, op.values),\n expectedAffectedRows: null, // creates don't need affected row checks\n };\n\n case \"update\": {\n const table = toTable(op.schema, op.table);\n const idColumn = table.getIdColumn();\n const versionColumn = table.getVersionColumn();\n\n const externalId = typeof op.id === \"string\" ? op.id : op.id.externalId;\n const versionToCheck = getVersionToCheck(op.id, op.checkVersion);\n\n // Build WHERE clause that filters by ID and optionally by version\n const whereClause =\n versionToCheck !== undefined\n ? () =>\n buildCondition(table.columns, (eb) =>\n eb.and(\n eb(idColumn.ormName, \"=\", externalId),\n eb(versionColumn.ormName, \"=\", versionToCheck),\n ),\n )\n : () => buildCondition(table.columns, (eb) => eb(idColumn.ormName, \"=\", externalId));\n\n const query = queryCompiler.updateMany(op.table, {\n where: whereClause,\n set: op.set,\n });\n\n return query\n ? {\n query,\n expectedAffectedRows: op.checkVersion ? 1 : null,\n }\n : null;\n }\n\n case \"delete\": {\n const table = toTable(op.schema, op.table);\n const idColumn = table.getIdColumn();\n const versionColumn = table.getVersionColumn();\n\n // Extract external ID based on whether op.id is FragnoId or string\n const externalId = typeof op.id === \"string\" ? op.id : op.id.externalId;\n const versionToCheck = getVersionToCheck(op.id, op.checkVersion);\n\n // Build WHERE clause that filters by ID and optionally by version\n const whereClause =\n versionToCheck !== undefined\n ? () =>\n buildCondition(table.columns, (eb) =>\n eb.and(\n eb(idColumn.ormName, \"=\", externalId),\n eb(versionColumn.ormName, \"=\", versionToCheck),\n ),\n )\n : () => buildCondition(table.columns, (eb) => eb(idColumn.ormName, \"=\", externalId));\n\n const query = queryCompiler.deleteMany(op.table, {\n where: whereClause,\n });\n\n return query\n ? {\n query,\n expectedAffectedRows: op.checkVersion ? 1 : null,\n }\n : null;\n }\n }\n },\n };\n}\n\n/**\n * Get the version to check for a given ID and checkVersion flag.\n * @returns The version to check or undefined if no check is required.\n * @throws Error if the ID is a string and checkVersion is true.\n */\nfunction getVersionToCheck(id: FragnoId | string, checkVersion: boolean): number | undefined {\n if (!checkVersion) {\n return undefined;\n }\n\n if (typeof id === \"string\") {\n throw new Error(\n `Cannot use checkVersion with a string ID. Version checking requires a FragnoId with version information.`,\n );\n }\n\n return id.version;\n}\n"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ //#region src/bind-services.d.ts
2
+ type OmitThisParameter<T> = T extends ((this: infer _This, ...args: infer A) => infer R) ? (...args: A) => R : T;
3
+ type BoundServices<T> = { [K in keyof T]: T[K] extends ((...args: never[]) => unknown) ? OmitThisParameter<T[K]> : T[K] extends Record<string, unknown> ? BoundServices<T[K]> : T[K] };
4
+ declare function bindServicesToContext<T extends Record<string, unknown>>(services: T): BoundServices<T>;
5
+ //#endregion
6
+ export { BoundServices, bindServicesToContext };
7
+ //# sourceMappingURL=bind-services.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bind-services.d.ts","names":[],"sources":["../src/bind-services.ts"],"sourcesContent":[],"mappings":";KAGK,uBAAuB,yEACd,MAAM,IAChB;AAFC,KAKO,aALU,CAAA,CAAA,CAAA,GAAA,QAAM,MAMd,CANc,GAMV,CANU,CAMR,CANQ,CAAA,UAAA,CAAA,GAAA,IAAA,EAAA,KAAA,EAAA,EAAA,GAAA,OAAA,IAOtB,iBAPsB,CAOJ,CAPI,CAOF,CAPE,CAAA,CAAA,GAQtB,CARsB,CAQpB,CARoB,CAAA,SAQT,MARS,CAAA,MAAA,EAAA,OAAA,CAAA,GASpB,aAToB,CASN,CATM,CASJ,CATI,CAAA,CAAA,GAUpB,CAVoB,CAUlB,CAVkB,CAAA,EACd;AAAM,iBAYJ,qBAZI,CAAA,UAY4B,MAZ5B,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,CAAA,QAAA,EAaR,CAbQ,CAAA,EAcjB,aAdiB,CAcH,CAdG,CAAA"}
@@ -0,0 +1,14 @@
1
+ import { serviceContext } from "./fragment.js";
2
+
3
+ //#region src/bind-services.ts
4
+ function bindServicesToContext(services) {
5
+ const bound = {};
6
+ for (const [key, value] of Object.entries(services)) if (typeof value === "function") bound[key] = value.bind(serviceContext);
7
+ else if (value && typeof value === "object" && !Array.isArray(value)) bound[key] = bindServicesToContext(value);
8
+ else bound[key] = value;
9
+ return bound;
10
+ }
11
+
12
+ //#endregion
13
+ export { bindServicesToContext };
14
+ //# sourceMappingURL=bind-services.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bind-services.js","names":[],"sources":["../src/bind-services.ts"],"sourcesContent":["import { serviceContext } from \"./fragment\";\n\n// Type helper to remove 'this' parameter from functions\ntype OmitThisParameter<T> = T extends (this: infer _This, ...args: infer A) => infer R\n ? (...args: A) => R\n : T;\n\n// Recursively remove 'this' parameter from all functions in an object\nexport type BoundServices<T> = {\n [K in keyof T]: T[K] extends (...args: never[]) => unknown\n ? OmitThisParameter<T[K]>\n : T[K] extends Record<string, unknown>\n ? BoundServices<T[K]>\n : T[K];\n};\n\nexport function bindServicesToContext<T extends Record<string, unknown>>(\n services: T,\n): BoundServices<T> {\n const bound = {} as BoundServices<T>;\n\n for (const [key, value] of Object.entries(services)) {\n if (typeof value === \"function\") {\n // Bind function to serviceContext\n bound[key as keyof T] = value.bind(serviceContext) as BoundServices<T>[keyof T];\n } else if (value && typeof value === \"object\" && !Array.isArray(value)) {\n // Recursively bind nested service objects\n bound[key as keyof T] = bindServicesToContext(\n value as Record<string, unknown>,\n ) as BoundServices<T>[keyof T];\n } else {\n bound[key as keyof T] = value as BoundServices<T>[keyof T];\n }\n }\n\n return bound;\n}\n"],"mappings":";;;AAgBA,SAAgB,sBACd,UACkB;CAClB,MAAM,QAAQ,EAAE;AAEhB,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,SAAS,CACjD,KAAI,OAAO,UAAU,WAEnB,OAAM,OAAkB,MAAM,KAAK,eAAe;UACzC,SAAS,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,MAAM,CAEpE,OAAM,OAAkB,sBACtB,MACD;KAED,OAAM,OAAkB;AAI5B,QAAO"}
@@ -1,10 +1,36 @@
1
1
  import { AnySchema } from "./schema/create.js";
2
+ import { IUnitOfWorkBase, UnitOfWorkSchemaView } from "./query/unit-of-work.js";
2
3
  import { AbstractQuery } from "./query/query.js";
3
4
  import { DatabaseAdapter } from "./adapters/adapters.js";
4
- import { FragmentDefinition, FragnoPublicConfig } from "@fragno-dev/core";
5
+ import { BoundServices, bindServicesToContext } from "./bind-services.js";
6
+ import { AsyncLocalStorage } from "async_hooks";
7
+ import { RequestThisContext } from "@fragno-dev/core/api";
8
+ import { FragmentDefinition, FragnoPublicConfig, RequestInputContext, RequestOutputContext } from "@fragno-dev/core";
5
9
 
6
10
  //#region src/fragment.d.ts
7
-
11
+ declare const uowStorage: AsyncLocalStorage<IUnitOfWorkBase>;
12
+ /**
13
+ * Service context for database fragments, providing access to the Unit of Work.
14
+ */
15
+ interface DatabaseRequestThisContext extends RequestThisContext {
16
+ /**
17
+ * Get the Unit of Work from the current context.
18
+ * @param schema - Optional schema to get a typed view. If not provided, returns the base UOW.
19
+ * @returns IUnitOfWorkBase if no schema provided, or typed UnitOfWorkSchemaView if schema provided.
20
+ */
21
+ getUnitOfWork(): IUnitOfWorkBase;
22
+ getUnitOfWork<TSchema extends AnySchema>(schema: TSchema): UnitOfWorkSchemaView<TSchema, [], any>;
23
+ }
24
+ declare const serviceContext: DatabaseRequestThisContext;
25
+ declare function withUnitOfWork<T>(uow: IUnitOfWorkBase, callback: () => T): Promise<T>;
26
+ /**
27
+ * Type helper that enforces DatabaseRequestThisContext on all functions in a service object
28
+ */
29
+ type WithDatabaseThis<T> = { [K in keyof T]: T[K] extends ((...args: infer A) => infer R) ? (this: DatabaseRequestThisContext, ...args: A) => R : T[K] extends Record<string, unknown> ? WithDatabaseThis<T[K]> : T[K] };
30
+ /**
31
+ * Route handler type for database fragments with access to Unit of Work.
32
+ */
33
+ type DatabaseRouteHandler = (this: DatabaseRequestThisContext, inputContext: RequestInputContext, outputContext: RequestOutputContext) => Promise<Response>;
8
34
  /**
9
35
  * Extended FragnoPublicConfig that includes a database adapter.
10
36
  * Use this type when creating fragments with database support.
@@ -19,8 +45,16 @@ type DatabaseFragmentContext<TSchema extends AnySchema> = {
19
45
  databaseAdapter: DatabaseAdapter<any>;
20
46
  orm: AbstractQuery<TSchema>;
21
47
  };
22
- declare class DatabaseFragmentBuilder<const TSchema extends AnySchema, const TConfig, const TDeps = {}, const TServices extends Record<string, unknown> = {}> {
48
+ declare class DatabaseFragmentBuilder<const TSchema extends AnySchema, const TConfig, const TDeps = {}, const TServices = {}, const TUsedServices = {}, const TProvidedServices = {}> {
23
49
  #private;
50
+ readonly $types: {
51
+ schema: TSchema;
52
+ config: TConfig;
53
+ deps: TDeps;
54
+ services: TServices;
55
+ usedServices: TUsedServices;
56
+ providedServices: TProvidedServices;
57
+ };
24
58
  constructor(options: {
25
59
  name: string;
26
60
  schema?: TSchema;
@@ -32,23 +66,108 @@ declare class DatabaseFragmentBuilder<const TSchema extends AnySchema, const TCo
32
66
  services?: (context: {
33
67
  config: TConfig;
34
68
  fragnoConfig: FragnoPublicConfig;
35
- deps: TDeps;
69
+ deps: TDeps & TUsedServices;
70
+ defineService: <T extends Record<string, unknown>>(services: WithDatabaseThis<T>) => WithDatabaseThis<T>;
36
71
  } & DatabaseFragmentContext<TSchema>) => TServices;
72
+ usedServices?: Record<string, {
73
+ name: string;
74
+ required: boolean;
75
+ }>;
76
+ providedServices?: Record<string, unknown>;
37
77
  });
38
78
  get $requiredOptions(): FragnoPublicConfigWithDatabase;
39
- get definition(): FragmentDefinition<TConfig, TDeps, TServices>;
40
- withDatabase<TNewSchema extends AnySchema>(schema: TNewSchema, namespace?: string): DatabaseFragmentBuilder<TNewSchema, TConfig, TDeps, TServices>;
79
+ get definition(): FragmentDefinition<TConfig, TDeps, BoundServices<TServices>, {
80
+ databaseSchema?: TSchema;
81
+ databaseNamespace: string;
82
+ }, BoundServices<TUsedServices>, BoundServices<TProvidedServices>, DatabaseRequestThisContext>;
83
+ withDatabase<TNewSchema extends AnySchema>(schema: TNewSchema, namespace?: string): DatabaseFragmentBuilder<TNewSchema, TConfig, TDeps, TServices, TUsedServices, TProvidedServices>;
41
84
  withDependencies<TNewDeps>(fn: (context: {
42
85
  config: TConfig;
43
86
  fragnoConfig: FragnoPublicConfig;
44
- } & DatabaseFragmentContext<TSchema>) => TNewDeps): DatabaseFragmentBuilder<TSchema, TConfig, TNewDeps, {}>;
45
- withServices<TNewServices extends Record<string, unknown>>(fn: (context: {
87
+ } & DatabaseFragmentContext<TSchema>) => TNewDeps): DatabaseFragmentBuilder<TSchema, TConfig, TNewDeps, {}, TUsedServices, TProvidedServices>;
88
+ /**
89
+ * Declare that this fragment uses a service.
90
+ * @param serviceName - The name of the service to use
91
+ * @param options - Optional configuration: { optional: boolean } (defaults to required)
92
+ */
93
+ usesService<TServiceName extends string, TService>(serviceName: TServiceName, options?: {
94
+ optional?: false;
95
+ }): DatabaseFragmentBuilder<TSchema, TConfig, TDeps, TServices, TUsedServices & { [K in TServiceName]: TService }, TProvidedServices>;
96
+ usesService<TServiceName extends string, TService>(serviceName: TServiceName, options: {
97
+ optional: true;
98
+ }): DatabaseFragmentBuilder<TSchema, TConfig, TDeps, TServices, TUsedServices & { [K in TServiceName]: TService | undefined }, TProvidedServices>;
99
+ /**
100
+ * Define services for this fragment (unnamed).
101
+ * Functions in the service will have access to DatabaseRequestThisContext via `this` if using `defineService`.
102
+ *
103
+ * @example
104
+ * With `this` context:
105
+ * ```ts
106
+ * .providesService(({ defineService }) => defineService({
107
+ * createUser: function(name: string) {
108
+ * const uow = this.getUnitOfWork(mySchema);
109
+ * return uow.create('user', { name });
110
+ * }
111
+ * }))
112
+ * ```
113
+ *
114
+ * Without `this` context:
115
+ * ```ts
116
+ * .providesService(({ db }) => ({
117
+ * createUser: async (name: string) => {
118
+ * return db.create('user', { name });
119
+ * }
120
+ * }))
121
+ * ```
122
+ */
123
+ providesService<TNewServices>(fn: (context: {
46
124
  config: TConfig;
47
125
  fragnoConfig: FragnoPublicConfig;
48
- deps: TDeps;
49
- } & DatabaseFragmentContext<TSchema>) => TNewServices): DatabaseFragmentBuilder<TSchema, TConfig, TDeps, TNewServices>;
126
+ deps: TDeps & TUsedServices;
127
+ db: AbstractQuery<TSchema>;
128
+ defineService: <T extends Record<string, unknown>>(services: WithDatabaseThis<T>) => WithDatabaseThis<T>;
129
+ }) => TNewServices): DatabaseFragmentBuilder<TSchema, TConfig, TDeps, TNewServices, TUsedServices, TProvidedServices>;
130
+ /**
131
+ * Provide a named service that other fragments can use.
132
+ * Functions in the service will have access to DatabaseRequestThisContext via `this` if using `defineService`.
133
+ * You can also pass a service object directly instead of a callback.
134
+ *
135
+ * @example
136
+ * With callback and `this` context:
137
+ * ```ts
138
+ * .providesService("myService", ({ defineService }) => defineService({
139
+ * createUser: function(name: string) {
140
+ * const uow = this.getUnitOfWork(mySchema);
141
+ * return uow.create('user', { name });
142
+ * }
143
+ * }))
144
+ * ```
145
+ *
146
+ * With callback, no `this` context:
147
+ * ```ts
148
+ * .providesService("myService", ({ db }) => ({
149
+ * createUser: async (name: string) => {
150
+ * return db.create('user', { name });
151
+ * }
152
+ * }))
153
+ * ```
154
+ *
155
+ * With direct object:
156
+ * ```ts
157
+ * .providesService("myService", {
158
+ * createUser: async (name: string) => { ... }
159
+ * })
160
+ * ```
161
+ */
162
+ providesService<TServiceName extends string, TService>(serviceName: TServiceName, fnOrService: ((context: {
163
+ config: TConfig;
164
+ fragnoConfig: FragnoPublicConfig;
165
+ deps: TDeps & TUsedServices;
166
+ db: AbstractQuery<TSchema>;
167
+ defineService: <T extends Record<string, unknown>>(services: WithDatabaseThis<T>) => WithDatabaseThis<T>;
168
+ }) => TService) | TService): DatabaseFragmentBuilder<TSchema, TConfig, TDeps, TServices, TUsedServices, TProvidedServices & { [K in TServiceName]: BoundServices<TService> }>;
50
169
  }
51
- declare function defineFragmentWithDatabase<TConfig = {}>(name: string): DatabaseFragmentBuilder<never, TConfig, {}, {}>;
170
+ declare function defineFragmentWithDatabase<TConfig = {}>(name: string): DatabaseFragmentBuilder<never, TConfig, {}, {}, {}, {}>;
52
171
  //#endregion
53
- export { DatabaseFragmentBuilder, DatabaseFragmentContext, FragnoPublicConfigWithDatabase, defineFragmentWithDatabase };
172
+ export { type BoundServices, DatabaseFragmentBuilder, DatabaseFragmentContext, DatabaseRequestThisContext, DatabaseRouteHandler, FragnoPublicConfigWithDatabase, bindServicesToContext, defineFragmentWithDatabase, serviceContext, uowStorage, withUnitOfWork };
54
173
  //# sourceMappingURL=fragment.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fragment.d.ts","names":[],"sources":["../src/fragment.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AASA;AAQA;AAAoD,KARxC,8BAAA,GAAiC,kBAQO,GAAA;EAEjC,eAAA,EARA,eAQA,CAAA,GAAA,CAAA;CACE;;;AAGrB;AACwB,KAPZ,uBAOY,CAAA,gBAP4B,SAO5B,CAAA,GAAA;EAGE,eAAA,EARP,eAQO,CAAA,GAAA,CAAA;EAqBb,GAAA,EA5BN,aA4BM,CA5BQ,OA4BR,CAAA;CAIG;AACM,cA9BT,uBA8BS,CAAA,sBA7BE,SA6BF,EAAA,aAAA,EAAA,cAAA,CAAA,CAAA,EAAA,wBA1BI,MA0BJ,CAAA,MAAA,EAAA,OAAA,CAAA,GAAA,CAAA,CAAA,CAAA,CAAA;EACY,CAAA,OAAA;EAAxB,WAAA,CAAA,OAAA,EAAA;IACD,IAAA,EAAA,MAAA;IAGO,MAAA,CAAA,EAVH,OAUG;IACM,SAAA,CAAA,EAAA,MAAA;IACR,YAAA,CAAA,EAAA,CAAA,OAAA,EAAA;MACoB,MAAA,EATlB,OASkB;MAAxB,YAAA,EARY,kBAQZ;IACD,CAAA,GARC,uBAQD,CARyB,OAQzB,CAAA,EAAA,GAPA,KAOA;IASiB,QAAA,CAAA,EAAA,CAAA,OAAA,EAAA;MAIa,MAAA,EAjBvB,OAiBuB;MAAS,YAAA,EAhB1B,kBAgB0B;MAAO,IAAA,EAfzC,KAeyC;IAAnC,CAAA,GAdV,uBAcU,CAdc,OAcd,CAAA,EAAA,GAbX,SAaW;EAoDc,CAAA;EACtB,IAAA,gBAAA,CAAA,CAAA,EAzDc,8BAyDd;EAEiB,IAAA,UAAA,CAAA,CAAA,EAvDT,kBAuDS,CAvDU,OAuDV,EAvDmB,KAuDnB,EAvD0B,SAuD1B,CAAA;EAAY,YAAA,CAAA,mBAHP,SAGO,CAAA,CAAA,MAAA,EAF7B,UAE6B,EAAA,SAAA,CAAA,EAAA,MAAA,CAAA,EAApC,uBAAoC,CAAZ,UAAY,EAAA,OAAA,EAAS,KAAT,EAAgB,SAAhB,CAAA;EAAS,gBAAA,CAAA,QAAA,CAAA,CAAA,EAAA,EAAA,CAAA,OAAA,EAAA;IAAO,MAAA,EA4BzC,OA5ByC;IAApD,YAAA,EA6BiB,kBA7BjB;EA4BW,CAAA,GAEN,uBAFM,CAEkB,OAFlB,CAAA,EAAA,GAGP,QAHO,CAAA,EAIX,uBAJW,CAIa,OAJb,EAIsB,OAJtB,EAI+B,QAJ/B,EAAA,CAAA,CAAA,CAAA;EACM,YAAA,CAAA,qBAac,MAbd,CAAA,MAAA,EAAA,OAAA,CAAA,CAAA,CAAA,EAAA,EAAA,CAAA,OAAA,EAAA;IACY,MAAA,EAelB,OAfkB;IAAxB,YAAA,EAgBY,kBAhBZ;IACD,IAAA,EAgBK,KAhBL;EACoB,CAAA,GAgBnB,uBAhBmB,CAgBK,OAhBL,CAAA,EAAA,GAiBpB,YAjBoB,CAAA,EAkBxB,uBAlBwB,CAkBA,OAlBA,EAkBS,OAlBT,EAkBkB,KAlBlB,EAkByB,YAlBzB,CAAA;;AAAkB,iBA6B/B,0BA7B+B,CAAA,UAAA,CAAA,CAAA,CAAA,CAAA,IAAA,EAAA,MAAA,CAAA,EA+B5C,uBA/B4C,CAAA,KAAA,EA+Bb,OA/Ba,EAAA,CAAA,CAAA,EAAA,CAAA,CAAA,CAAA"}
1
+ {"version":3,"file":"fragment.d.ts","names":[],"sources":["../src/fragment.ts"],"sourcesContent":[],"mappings":"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"}