@fragno-dev/db 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/.turbo/turbo-build.log +61 -53
  2. package/CHANGELOG.md +12 -0
  3. package/dist/adapters/adapters.d.ts +11 -1
  4. package/dist/adapters/adapters.d.ts.map +1 -1
  5. package/dist/adapters/drizzle/drizzle-adapter.d.ts +9 -2
  6. package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +1 -1
  7. package/dist/adapters/drizzle/drizzle-adapter.js +21 -39
  8. package/dist/adapters/drizzle/drizzle-adapter.js.map +1 -1
  9. package/dist/adapters/drizzle/drizzle-query.d.ts.map +1 -1
  10. package/dist/adapters/drizzle/drizzle-query.js +3 -2
  11. package/dist/adapters/drizzle/drizzle-query.js.map +1 -1
  12. package/dist/adapters/drizzle/drizzle-uow-compiler.js +8 -6
  13. package/dist/adapters/drizzle/drizzle-uow-compiler.js.map +1 -1
  14. package/dist/adapters/drizzle/drizzle-uow-decoder.js.map +1 -1
  15. package/dist/adapters/drizzle/drizzle-uow-executor.js.map +1 -1
  16. package/dist/adapters/drizzle/generate.js +107 -34
  17. package/dist/adapters/drizzle/generate.js.map +1 -1
  18. package/dist/adapters/drizzle/shared.js +14 -1
  19. package/dist/adapters/drizzle/shared.js.map +1 -1
  20. package/dist/adapters/kysely/kysely-adapter.d.ts +2 -1
  21. package/dist/adapters/kysely/kysely-adapter.d.ts.map +1 -1
  22. package/dist/adapters/kysely/kysely-adapter.js +25 -30
  23. package/dist/adapters/kysely/kysely-adapter.js.map +1 -1
  24. package/dist/adapters/kysely/kysely-query-builder.js +48 -44
  25. package/dist/adapters/kysely/kysely-query-builder.js.map +1 -1
  26. package/dist/adapters/kysely/kysely-query-compiler.js +2 -2
  27. package/dist/adapters/kysely/kysely-query-compiler.js.map +1 -1
  28. package/dist/adapters/kysely/kysely-query.js +3 -2
  29. package/dist/adapters/kysely/kysely-query.js.map +1 -1
  30. package/dist/adapters/kysely/kysely-shared.js +18 -0
  31. package/dist/adapters/kysely/kysely-shared.js.map +1 -0
  32. package/dist/adapters/kysely/kysely-uow-compiler.js +4 -3
  33. package/dist/adapters/kysely/kysely-uow-compiler.js.map +1 -1
  34. package/dist/adapters/kysely/migration/execute.js +15 -12
  35. package/dist/adapters/kysely/migration/execute.js.map +1 -1
  36. package/dist/migration-engine/auto-from-schema.js +2 -8
  37. package/dist/migration-engine/auto-from-schema.js.map +1 -1
  38. package/dist/migration-engine/create.d.ts +1 -5
  39. package/dist/migration-engine/create.js +1 -1
  40. package/dist/migration-engine/create.js.map +1 -1
  41. package/dist/migration-engine/generation-engine.d.ts +51 -0
  42. package/dist/migration-engine/generation-engine.d.ts.map +1 -0
  43. package/dist/migration-engine/generation-engine.js +165 -0
  44. package/dist/migration-engine/generation-engine.js.map +1 -0
  45. package/dist/migration-engine/shared.d.ts +5 -2
  46. package/dist/migration-engine/shared.d.ts.map +1 -1
  47. package/dist/migration-engine/shared.js.map +1 -1
  48. package/dist/mod.d.ts +0 -8
  49. package/dist/mod.d.ts.map +1 -1
  50. package/dist/mod.js +0 -32
  51. package/dist/mod.js.map +1 -1
  52. package/dist/query/condition-builder.js.map +1 -1
  53. package/dist/query/result-transform.js +2 -1
  54. package/dist/query/result-transform.js.map +1 -1
  55. package/dist/schema/create.d.ts +74 -16
  56. package/dist/schema/create.d.ts.map +1 -1
  57. package/dist/schema/create.js +76 -11
  58. package/dist/schema/create.js.map +1 -1
  59. package/dist/schema/serialize.js.map +1 -1
  60. package/dist/shared/settings-schema.js +36 -0
  61. package/dist/shared/settings-schema.js.map +1 -0
  62. package/dist/util/import-generator.js.map +1 -1
  63. package/dist/util/parse.js.map +1 -1
  64. package/package.json +8 -2
  65. package/src/adapters/adapters.ts +10 -3
  66. package/src/adapters/drizzle/drizzle-adapter-pglite.test.ts +11 -7
  67. package/src/adapters/drizzle/drizzle-adapter.test.ts +77 -29
  68. package/src/adapters/drizzle/drizzle-adapter.ts +31 -78
  69. package/src/adapters/drizzle/drizzle-query.ts +4 -7
  70. package/src/adapters/drizzle/drizzle-uow-compiler.test.ts +9 -3
  71. package/src/adapters/drizzle/drizzle-uow-compiler.ts +12 -6
  72. package/src/adapters/drizzle/drizzle-uow-decoder.ts +1 -1
  73. package/src/adapters/drizzle/drizzle-uow-executor.ts +1 -1
  74. package/src/adapters/drizzle/generate.test.ts +573 -150
  75. package/src/adapters/drizzle/generate.ts +187 -36
  76. package/src/adapters/drizzle/migrate-drizzle.test.ts +30 -6
  77. package/src/adapters/drizzle/shared.ts +31 -1
  78. package/src/adapters/drizzle/test-utils.ts +3 -1
  79. package/src/adapters/kysely/kysely-adapter-pglite.test.ts +25 -27
  80. package/src/adapters/kysely/kysely-adapter.ts +35 -58
  81. package/src/adapters/kysely/kysely-query-builder.ts +75 -44
  82. package/src/adapters/kysely/kysely-query-compiler.ts +3 -1
  83. package/src/adapters/kysely/kysely-query.ts +8 -2
  84. package/src/adapters/kysely/kysely-shared.ts +23 -0
  85. package/src/adapters/kysely/kysely-uow-compiler.ts +5 -2
  86. package/src/adapters/kysely/migration/execute-mysql.test.ts +2 -2
  87. package/src/adapters/kysely/migration/execute-postgres.test.ts +19 -19
  88. package/src/adapters/kysely/migration/execute.ts +48 -17
  89. package/src/adapters/kysely/migration/kysely-migrator.test.ts +19 -37
  90. package/src/fragment.test.ts +1 -0
  91. package/src/migration-engine/auto-from-schema.ts +14 -18
  92. package/src/migration-engine/create.ts +1 -6
  93. package/src/migration-engine/generation-engine.test.ts +597 -0
  94. package/src/migration-engine/generation-engine.ts +356 -0
  95. package/src/migration-engine/shared.ts +1 -4
  96. package/src/mod.ts +0 -66
  97. package/src/query/condition-builder.ts +24 -8
  98. package/src/query/result-transform.ts +7 -1
  99. package/src/schema/create.test.ts +4 -1
  100. package/src/schema/create.ts +132 -24
  101. package/src/schema/serialize.ts +21 -7
  102. package/src/shared/settings-schema.ts +61 -0
  103. package/src/util/deep-equal.ts +21 -7
  104. package/src/util/import-generator.ts +3 -1
  105. package/src/util/parse.ts +3 -1
  106. package/tsdown.config.ts +1 -0
  107. package/.turbo/turbo-test.log +0 -37
  108. package/.turbo/turbo-types$colon$check.log +0 -1
@@ -1,15 +1,15 @@
1
1
  import { sql, type Kysely } from "kysely";
2
2
  import type { SQLProvider } from "../../shared/providers";
3
3
  import type { DatabaseAdapter } from "../adapters";
4
- import { schemaToDBType } from "../../schema/serialize";
5
4
  import { createMigrator, type Migrator } from "../../migration-engine/create";
6
5
  import type { AnySchema } from "../../schema/create";
7
6
  import type { CustomOperation, MigrationOperation } from "../../migration-engine/shared";
8
7
  import { execute } from "./migration/execute";
9
8
  import type { AbstractQuery } from "../../query/query";
10
9
  import { fromKysely } from "./kysely-query";
11
-
12
- const SETTINGS_TABLE_NAME = "fragno_db_settings" as const;
10
+ import { createTableNameMapper } from "./kysely-shared";
11
+ import { createHash } from "node:crypto";
12
+ import { SETTINGS_TABLE_NAME } from "../../shared/settings-schema";
13
13
 
14
14
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
15
  type KyselyAny = Kysely<any>;
@@ -26,17 +26,26 @@ export class KyselyAdapter implements DatabaseAdapter {
26
26
  this.#kyselyConfig = config;
27
27
  }
28
28
 
29
- createQueryEngine<T extends AnySchema>(schema: T, _namespace: string): AbstractQuery<T> {
30
- return fromKysely(schema, this.#kyselyConfig);
29
+ createQueryEngine<T extends AnySchema>(schema: T, namespace: string): AbstractQuery<T> {
30
+ // Only create mapper if namespace is non-empty
31
+ const mapper = namespace ? createTableNameMapper(namespace) : undefined;
32
+ return fromKysely(schema, this.#kyselyConfig, mapper);
33
+ }
34
+
35
+ async isConnectionHealthy(): Promise<boolean> {
36
+ try {
37
+ const result = await this.#kyselyConfig.db.executeQuery(
38
+ sql`SELECT 1 as healthy`.compile(this.#kyselyConfig.db),
39
+ );
40
+ return (result.rows[0] as Record<string, unknown>)["healthy"] === 1;
41
+ } catch {
42
+ return false;
43
+ }
31
44
  }
32
45
 
33
46
  createMigrationEngine(schema: AnySchema, namespace: string): Migrator {
34
- const manager = createSettingsManager(
35
- this.#kyselyConfig.db,
36
- this.#kyselyConfig.provider,
37
- SETTINGS_TABLE_NAME,
38
- namespace,
39
- );
47
+ const manager = createSettingsManager(this.#kyselyConfig.db, namespace);
48
+ const mapper = namespace ? createTableNameMapper(namespace) : undefined;
40
49
 
41
50
  const onCustomNode = (node: CustomOperation, db: KyselyAny) => {
42
51
  const statement = sql.raw(node["sql"] as string);
@@ -63,7 +72,7 @@ export class KyselyAdapter implements DatabaseAdapter {
63
72
  }
64
73
 
65
74
  return operations.flatMap((op) =>
66
- execute(op, this.#kyselyConfig, (node) => onCustomNode(node, db)),
75
+ execute(op, this.#kyselyConfig, (node) => onCustomNode(node, db), mapper),
67
76
  );
68
77
  };
69
78
 
@@ -97,69 +106,38 @@ export class KyselyAdapter implements DatabaseAdapter {
97
106
  return v ? parseInt(v) : 0;
98
107
  },
99
108
  updateSettingsInMigration: async (fromVersion, toVersion) => {
100
- // Only create the settings table if we're migrating from version 0
101
- if (fromVersion === 0) {
102
- return [
103
- { type: "custom", sql: manager.initTable() },
104
- {
105
- type: "custom",
106
- sql: manager.insert(`schema_version`, toVersion.toString()),
107
- },
108
- ];
109
- }
110
-
111
109
  return [
112
110
  {
113
111
  type: "custom",
114
- sql: manager.update(`schema_version`, toVersion.toString()),
112
+ sql:
113
+ fromVersion === 0
114
+ ? manager.insert(`schema_version`, toVersion.toString())
115
+ : manager.update(`schema_version`, toVersion.toString()),
115
116
  },
116
117
  ];
117
118
  },
118
119
  },
119
120
  });
120
121
 
121
- // Add getDefaultFileName implementation
122
- migrator.getDefaultFileName = (namespace: string, fromVersion: number, toVersion: number) => {
123
- const date = new Date().toISOString().split("T")[0].replace(/-/g, "");
124
- const safeName = namespace.replace(/[^a-z0-9-]/gi, "_");
125
- return `${date}_${safeName}_migration_${fromVersion}_to_${toVersion}.sql`;
126
- };
127
-
128
122
  return migrator;
129
123
  }
130
124
 
131
125
  getSchemaVersion(namespace: string) {
132
- const manager = createSettingsManager(
133
- this.#kyselyConfig.db,
134
- this.#kyselyConfig.provider,
135
- SETTINGS_TABLE_NAME,
136
- namespace,
137
- );
126
+ const manager = createSettingsManager(this.#kyselyConfig.db, namespace);
138
127
 
139
128
  return manager.get(`schema_version`);
140
129
  }
141
130
  }
142
131
 
143
- function createSettingsManager(
144
- db: KyselyAny,
145
- provider: SQLProvider,
146
- settingsTableName: string,
147
- namespace: string,
148
- ) {
149
- function initTable() {
150
- return db.schema
151
- .createTable(settingsTableName)
152
- .addColumn("key", provider === "sqlite" ? "text" : "varchar(255)", (col) => col.primaryKey())
153
- .addColumn("value", sql.raw(schemaToDBType({ type: "string" }, provider)), (col) =>
154
- col.notNull(),
155
- );
156
- }
132
+ function createSettingsManager(db: KyselyAny, namespace: string) {
133
+ // Settings table is never namespaced, but keys include namespace prefix
134
+ const tableName = SETTINGS_TABLE_NAME;
157
135
 
158
136
  return {
159
137
  async get(key: string): Promise<string | undefined> {
160
138
  try {
161
139
  const result = await db
162
- .selectFrom(settingsTableName)
140
+ .selectFrom(tableName)
163
141
  .where("key", "=", sql.lit(`${namespace}.${key}`))
164
142
  .select(["value"])
165
143
  .executeTakeFirstOrThrow();
@@ -169,14 +147,13 @@ function createSettingsManager(
169
147
  }
170
148
  },
171
149
 
172
- initTable() {
173
- return initTable().compile().sql;
174
- },
175
-
176
150
  insert(key: string, value: string) {
177
151
  return db
178
- .insertInto(settingsTableName)
152
+ .insertInto(tableName)
179
153
  .values({
154
+ id: sql.lit(
155
+ createHash("md5").update(`${namespace}.${key}`).digest("base64url").replace(/=/g, ""),
156
+ ),
180
157
  key: sql.lit(`${namespace}.${key}`),
181
158
  value: sql.lit(value),
182
159
  })
@@ -185,7 +162,7 @@ function createSettingsManager(
185
162
 
186
163
  update(key: string, value: string) {
187
164
  return db
188
- .updateTable(settingsTableName)
165
+ .updateTable(tableName)
189
166
  .set({
190
167
  value: sql.lit(value),
191
168
  })
@@ -14,11 +14,13 @@ import type { Condition } from "../../query/condition-builder";
14
14
  import { serialize } from "../../schema/serialize";
15
15
  import type { CompiledJoin, SimplifyFindOptions } from "../../query/orm/orm";
16
16
  import { decodeResult, encodeValues, ReferenceSubquery } from "../../query/result-transform";
17
+ import type { TableNameMapper } from "./kysely-shared";
17
18
 
18
19
  /**
19
20
  * Returns the fully qualified SQL name for a column (table.column).
20
21
  *
21
22
  * @param column - The column to get the full name for
23
+ * @param mapper - Optional table name mapper for namespace prefixing
22
24
  * @returns The fully qualified SQL name in the format "tableName.columnName"
23
25
  * @internal
24
26
  *
@@ -28,8 +30,9 @@ import { decodeResult, encodeValues, ReferenceSubquery } from "../../query/resul
28
30
  * // Returns: "users.email"
29
31
  * ```
30
32
  */
31
- export function fullSQLName(column: AnyColumn) {
32
- return `${column.tableName}.${column.name}`;
33
+ export function fullSQLName(column: AnyColumn, mapper?: TableNameMapper) {
34
+ const tableName = mapper ? mapper.toPhysical(column.tableName) : column.tableName;
35
+ return `${tableName}.${column.name}`;
33
36
  }
34
37
 
35
38
  /**
@@ -60,6 +63,7 @@ export function buildWhere(
60
63
  condition: Condition,
61
64
  eb: ExpressionBuilder<any, any>, // eslint-disable-line @typescript-eslint/no-explicit-any
62
65
  provider: SQLProvider,
66
+ mapper?: TableNameMapper,
63
67
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
64
68
  ): ExpressionWrapper<any, any, SqlBool> {
65
69
  if (condition.type === "compare") {
@@ -78,47 +82,55 @@ export function buildWhere(
78
82
  case "contains":
79
83
  v = "like";
80
84
  rhs =
81
- val instanceof Column ? sql`concat('%', ${eb.ref(fullSQLName(val))}, '%')` : `%${val}%`;
85
+ val instanceof Column
86
+ ? sql`concat('%', ${eb.ref(fullSQLName(val, mapper))}, '%')`
87
+ : `%${val}%`;
82
88
  break;
83
89
  case "not contains":
84
90
  v = "not like";
85
91
  rhs =
86
- val instanceof Column ? sql`concat('%', ${eb.ref(fullSQLName(val))}, '%')` : `%${val}%`;
92
+ val instanceof Column
93
+ ? sql`concat('%', ${eb.ref(fullSQLName(val, mapper))}, '%')`
94
+ : `%${val}%`;
87
95
  break;
88
96
  case "starts with":
89
97
  v = "like";
90
- rhs = val instanceof Column ? sql`concat(${eb.ref(fullSQLName(val))}, '%')` : `${val}%`;
98
+ rhs =
99
+ val instanceof Column ? sql`concat(${eb.ref(fullSQLName(val, mapper))}, '%')` : `${val}%`;
91
100
  break;
92
101
  case "not starts with":
93
102
  v = "not like";
94
- rhs = val instanceof Column ? sql`concat(${eb.ref(fullSQLName(val))}, '%')` : `${val}%`;
103
+ rhs =
104
+ val instanceof Column ? sql`concat(${eb.ref(fullSQLName(val, mapper))}, '%')` : `${val}%`;
95
105
  break;
96
106
  case "ends with":
97
107
  v = "like";
98
- rhs = val instanceof Column ? sql`concat('%', ${eb.ref(fullSQLName(val))})` : `%${val}`;
108
+ rhs =
109
+ val instanceof Column ? sql`concat('%', ${eb.ref(fullSQLName(val, mapper))})` : `%${val}`;
99
110
  break;
100
111
  case "not ends with":
101
112
  v = "not like";
102
- rhs = val instanceof Column ? sql`concat('%', ${eb.ref(fullSQLName(val))})` : `%${val}`;
113
+ rhs =
114
+ val instanceof Column ? sql`concat('%', ${eb.ref(fullSQLName(val, mapper))})` : `%${val}`;
103
115
  break;
104
116
  default:
105
117
  v = op;
106
- rhs = val instanceof Column ? eb.ref(fullSQLName(val)) : val;
118
+ rhs = val instanceof Column ? eb.ref(fullSQLName(val, mapper)) : val;
107
119
  }
108
120
 
109
- return eb(fullSQLName(left), v, rhs);
121
+ return eb(fullSQLName(left, mapper), v, rhs);
110
122
  }
111
123
 
112
124
  // Nested conditions
113
125
  if (condition.type === "and") {
114
- return eb.and(condition.items.map((v) => buildWhere(v, eb, provider)));
126
+ return eb.and(condition.items.map((v) => buildWhere(v, eb, provider, mapper)));
115
127
  }
116
128
 
117
129
  if (condition.type === "not") {
118
- return eb.not(buildWhere(condition.item, eb, provider));
130
+ return eb.not(buildWhere(condition.item, eb, provider, mapper));
119
131
  }
120
132
 
121
- return eb.or(condition.items.map((v) => buildWhere(v, eb, provider)));
133
+ return eb.or(condition.items.map((v) => buildWhere(v, eb, provider, mapper)));
122
134
  }
123
135
 
124
136
  /**
@@ -353,14 +365,17 @@ export async function findMany(
353
365
  *
354
366
  * @param values - The encoded values that may contain ReferenceSubquery objects
355
367
  * @param kysely - The Kysely database instance for building subqueries
368
+ * @param mapper - Optional table name mapper for namespace prefixing
356
369
  * @returns Processed values with subqueries in place of ReferenceSubquery markers
357
370
  * @internal
358
371
  */
359
372
  function processReferenceSubqueries(
360
373
  values: Record<string, unknown>,
361
374
  kysely: Kysely<any>, // eslint-disable-line @typescript-eslint/no-explicit-any
375
+ mapper?: TableNameMapper,
362
376
  ): Record<string, unknown> {
363
377
  const processed: Record<string, unknown> = {};
378
+ const getTableName = (table: AnyTable) => (mapper ? mapper.toPhysical(table.name) : table.name);
364
379
 
365
380
  for (const [key, value] of Object.entries(values)) {
366
381
  if (value instanceof ReferenceSubquery) {
@@ -369,7 +384,7 @@ function processReferenceSubqueries(
369
384
 
370
385
  // Build a subquery: SELECT _internal_id FROM referenced_table WHERE id = external_id LIMIT 1
371
386
  processed[key] = kysely
372
- .selectFrom(refTable.name)
387
+ .selectFrom(getTableName(refTable))
373
388
  .select(refTable.getInternalIdColumn().name)
374
389
  .where(refTable.getIdColumn().name, "=", externalId)
375
390
  .limit(1);
@@ -399,21 +414,27 @@ function processReferenceSubqueries(
399
414
  * const result = await kysely.executeQuery(query);
400
415
  * ```
401
416
  */
402
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
403
- export function createKyselyQueryBuilder(kysely: Kysely<any>, provider: SQLProvider) {
417
+ export function createKyselyQueryBuilder(
418
+ kysely: Kysely<any>, // eslint-disable-line @typescript-eslint/no-explicit-any
419
+ provider: SQLProvider,
420
+ mapper?: TableNameMapper,
421
+ ) {
422
+ // Helper to get the physical table name (with namespace suffix if mapper is provided)
423
+ const getTableName = (table: AnyTable) => (mapper ? mapper.toPhysical(table.name) : table.name);
424
+
404
425
  return {
405
426
  count(table: AnyTable, { where }: { where?: Condition }): CompiledQuery {
406
- let query = kysely.selectFrom(table.name).select(kysely.fn.countAll().as("count"));
427
+ let query = kysely.selectFrom(getTableName(table)).select(kysely.fn.countAll().as("count"));
407
428
  if (where) {
408
- query = query.where((b) => buildWhere(where, b, provider));
429
+ query = query.where((b) => buildWhere(where, b, provider, mapper));
409
430
  }
410
431
  return query.compile();
411
432
  },
412
433
 
413
434
  create(table: AnyTable, values: Record<string, unknown>): CompiledQuery {
414
435
  const encodedValues = encodeValues(values, table, true, provider);
415
- const processedValues = processReferenceSubqueries(encodedValues, kysely);
416
- const insert = kysely.insertInto(table.name).values(processedValues);
436
+ const processedValues = processReferenceSubqueries(encodedValues, kysely, mapper);
437
+ const insert = kysely.insertInto(getTableName(table)).values(processedValues);
417
438
 
418
439
  if (provider === "mssql") {
419
440
  return (
@@ -425,7 +446,9 @@ export function createKyselyQueryBuilder(kysely: Kysely<any>, provider: SQLProvi
425
446
  }
426
447
 
427
448
  if (provider === "postgresql" || provider === "sqlite") {
428
- return insert.returning(mapSelect(true, table)).compile();
449
+ return insert
450
+ .returning(mapSelect(true, table, { tableName: getTableName(table) }))
451
+ .compile();
429
452
  }
430
453
 
431
454
  // For MySQL/other providers, return the insert query
@@ -436,11 +459,11 @@ export function createKyselyQueryBuilder(kysely: Kysely<any>, provider: SQLProvi
436
459
  table: T,
437
460
  v: SimplifyFindOptions<FindManyOptions<T>>,
438
461
  ): CompiledQuery {
439
- let query = kysely.selectFrom(table.name);
462
+ let query = kysely.selectFrom(getTableName(table));
440
463
 
441
464
  const where = v.where;
442
465
  if (where) {
443
- query = query.where((eb) => buildWhere(where, eb, provider));
466
+ query = query.where((eb) => buildWhere(where, eb, provider, mapper));
444
467
  }
445
468
 
446
469
  if (v.offset !== undefined) {
@@ -453,7 +476,7 @@ export function createKyselyQueryBuilder(kysely: Kysely<any>, provider: SQLProvi
453
476
 
454
477
  if (v.orderBy) {
455
478
  for (const [col, mode] of v.orderBy) {
456
- query = query.orderBy(fullSQLName(col), mode);
479
+ query = query.orderBy(fullSQLName(col, mapper), mode);
457
480
  }
458
481
  }
459
482
 
@@ -488,7 +511,7 @@ export function createKyselyQueryBuilder(kysely: Kysely<any>, provider: SQLProvi
488
511
  }),
489
512
  );
490
513
 
491
- query = query.leftJoin(`${targetTable.name} as ${joinName}`, (b) =>
514
+ query = query.leftJoin(`${getTableName(targetTable)} as ${joinName}`, (b) =>
492
515
  b.on((eb) => {
493
516
  const conditions = [];
494
517
  for (const [left, right] of relation.on) {
@@ -507,7 +530,7 @@ export function createKyselyQueryBuilder(kysely: Kysely<any>, provider: SQLProvi
507
530
  }
508
531
 
509
532
  if (joinOptions.where) {
510
- conditions.push(buildWhere(joinOptions.where, eb, provider));
533
+ conditions.push(buildWhere(joinOptions.where, eb, provider, mapper));
511
534
  }
512
535
 
513
536
  return eb.and(conditions);
@@ -519,10 +542,12 @@ export function createKyselyQueryBuilder(kysely: Kysely<any>, provider: SQLProvi
519
542
  }
520
543
  };
521
544
 
522
- processJoins(v.join, table, table.name);
545
+ processJoins(v.join, table, getTableName(table));
523
546
 
524
547
  const compiledSelect = selectBuilder.compile();
525
- mappedSelect.push(...mapSelect(compiledSelect.result, table));
548
+ mappedSelect.push(
549
+ ...mapSelect(compiledSelect.result, table, { tableName: getTableName(table) }),
550
+ );
526
551
 
527
552
  return query.select(mappedSelect).compile();
528
553
  },
@@ -535,26 +560,26 @@ export function createKyselyQueryBuilder(kysely: Kysely<any>, provider: SQLProvi
535
560
  },
536
561
  ): CompiledQuery {
537
562
  const encoded = encodeValues(v.set, table, false, provider);
538
- const processed = processReferenceSubqueries(encoded, kysely);
563
+ const processed = processReferenceSubqueries(encoded, kysely, mapper);
539
564
 
540
565
  // Automatically increment _version for optimistic concurrency control
541
566
  const versionCol = table.getVersionColumn();
542
567
  // Safe cast: we're building a SQL expression for incrementing the version
543
568
  processed[versionCol.name] = sql.raw(`COALESCE(${versionCol.name}, 0) + 1`) as unknown;
544
569
 
545
- let query = kysely.updateTable(table.name).set(processed);
570
+ let query = kysely.updateTable(getTableName(table)).set(processed);
546
571
  const { where } = v;
547
572
  if (where) {
548
- query = query.where((eb) => buildWhere(where, eb, provider));
573
+ query = query.where((eb) => buildWhere(where, eb, provider, mapper));
549
574
  }
550
575
  return query.compile();
551
576
  },
552
577
 
553
578
  upsertCheck(table: AnyTable, where: Condition | undefined): CompiledQuery {
554
579
  const idColumn = table.getIdColumn();
555
- let query = kysely.selectFrom(table.name).select([`${idColumn.name} as id`]);
580
+ let query = kysely.selectFrom(getTableName(table)).select([`${idColumn.name} as id`]);
556
581
  if (where) {
557
- query = query.where((b) => buildWhere(where, b, provider));
582
+ query = query.where((b) => buildWhere(where, b, provider, mapper));
558
583
  }
559
584
  return query.limit(1).compile();
560
585
  },
@@ -566,13 +591,13 @@ export function createKyselyQueryBuilder(kysely: Kysely<any>, provider: SQLProvi
566
591
  top?: boolean,
567
592
  ): CompiledQuery {
568
593
  const encoded = encodeValues(update, table, false, provider);
569
- const processed = processReferenceSubqueries(encoded, kysely);
570
- let query = kysely.updateTable(table.name).set(processed);
594
+ const processed = processReferenceSubqueries(encoded, kysely, mapper);
595
+ let query = kysely.updateTable(getTableName(table)).set(processed);
571
596
  if (top) {
572
597
  query = query.top(1);
573
598
  }
574
599
  if (where) {
575
- query = query.where((b) => buildWhere(where, b, provider));
600
+ query = query.where((b) => buildWhere(where, b, provider, mapper));
576
601
  }
577
602
  return query.compile();
578
603
  },
@@ -580,20 +605,26 @@ export function createKyselyQueryBuilder(kysely: Kysely<any>, provider: SQLProvi
580
605
  upsertUpdateById(table: AnyTable, update: Record<string, unknown>, id: unknown): CompiledQuery {
581
606
  const idColumn = table.getIdColumn();
582
607
  const encoded = encodeValues(update, table, false, provider);
583
- const processed = processReferenceSubqueries(encoded, kysely);
584
- return kysely.updateTable(table.name).set(processed).where(idColumn.name, "=", id).compile();
608
+ const processed = processReferenceSubqueries(encoded, kysely, mapper);
609
+ return kysely
610
+ .updateTable(getTableName(table))
611
+ .set(processed)
612
+ .where(idColumn.name, "=", id)
613
+ .compile();
585
614
  },
586
615
 
587
616
  createMany(table: AnyTable, values: Record<string, unknown>[]): CompiledQuery {
588
617
  const encodedValues = values.map((v) => encodeValues(v, table, true, provider));
589
- const processedValues = encodedValues.map((v) => processReferenceSubqueries(v, kysely));
590
- return kysely.insertInto(table.name).values(processedValues).compile();
618
+ const processedValues = encodedValues.map((v) =>
619
+ processReferenceSubqueries(v, kysely, mapper),
620
+ );
621
+ return kysely.insertInto(getTableName(table)).values(processedValues).compile();
591
622
  },
592
623
 
593
624
  deleteMany(table: AnyTable, { where }: { where?: Condition }): CompiledQuery {
594
- let query = kysely.deleteFrom(table.name);
625
+ let query = kysely.deleteFrom(getTableName(table));
595
626
  if (where) {
596
- query = query.where((eb) => buildWhere(where, eb, provider));
627
+ query = query.where((eb) => buildWhere(where, eb, provider, mapper));
597
628
  }
598
629
  return query.compile();
599
630
  },
@@ -601,8 +632,8 @@ export function createKyselyQueryBuilder(kysely: Kysely<any>, provider: SQLProvi
601
632
  findById(table: AnyTable, idValue: unknown): CompiledQuery {
602
633
  const idColumn = table.getIdColumn();
603
634
  return kysely
604
- .selectFrom(table.name)
605
- .select(mapSelect(true, table))
635
+ .selectFrom(getTableName(table))
636
+ .select(mapSelect(true, table, { tableName: getTableName(table) }))
606
637
  .where(idColumn.name, "=", idValue)
607
638
  .limit(1)
608
639
  .compile();
@@ -5,6 +5,7 @@ import { buildFindOptions } from "../../query/orm/orm";
5
5
  import type { KyselyConfig } from "./kysely-adapter";
6
6
  import { createKyselyQueryBuilder } from "./kysely-query-builder";
7
7
  import type { ConditionBuilder, Condition } from "../../query/condition-builder";
8
+ import type { TableNameMapper } from "./kysely-shared";
8
9
 
9
10
  /**
10
11
  * Internal query compiler interface for Kysely
@@ -27,9 +28,10 @@ export interface KyselyQueryCompiler {
27
28
  export function createKyselyQueryCompiler<T extends AnySchema>(
28
29
  schema: T,
29
30
  config: KyselyConfig,
31
+ mapper?: TableNameMapper,
30
32
  ): KyselyQueryCompiler {
31
33
  const { db: kysely, provider } = config;
32
- const queryBuilder = createKyselyQueryBuilder(kysely, provider);
34
+ const queryBuilder = createKyselyQueryBuilder(kysely, provider, mapper);
33
35
 
34
36
  function toTable(name: unknown): AnyTable {
35
37
  const table = schema.tables[name as string];
@@ -7,6 +7,7 @@ import { createKyselyUOWCompiler } from "./kysely-uow-compiler";
7
7
  import { executeKyselyRetrievalPhase, executeKyselyMutationPhase } from "./kysely-uow-executor";
8
8
  import { UnitOfWork } from "../../query/unit-of-work";
9
9
  import type { CompiledQuery } from "kysely";
10
+ import type { TableNameMapper } from "./kysely-shared";
10
11
 
11
12
  /**
12
13
  * Creates a Kysely-based query engine for the given schema.
@@ -17,6 +18,7 @@ import type { CompiledQuery } from "kysely";
17
18
  *
18
19
  * @param schema - The database schema definition
19
20
  * @param config - Kysely configuration containing the database instance and provider
21
+ * @param mapper - Optional table name mapper for namespace prefixing
20
22
  * @returns An AbstractQuery instance for performing database operations
21
23
  *
22
24
  * @example
@@ -32,9 +34,13 @@ import type { CompiledQuery } from "kysely";
32
34
  * });
33
35
  * ```
34
36
  */
35
- export function fromKysely<T extends AnySchema>(schema: T, config: KyselyConfig): AbstractQuery<T> {
37
+ export function fromKysely<T extends AnySchema>(
38
+ schema: T,
39
+ config: KyselyConfig,
40
+ mapper?: TableNameMapper,
41
+ ): AbstractQuery<T> {
36
42
  const { db: kysely, provider } = config;
37
- const uowCompiler = createKyselyUOWCompiler(schema, config);
43
+ const uowCompiler = createKyselyUOWCompiler(schema, config, mapper);
38
44
 
39
45
  function createUOW(name?: string): UnitOfWork<T, []> {
40
46
  const executor: UOWExecutor<CompiledQuery, unknown> = {
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Maps logical table names (used by fragment authors) to physical table names (with namespace suffix)
3
+ */
4
+ export interface TableNameMapper {
5
+ toPhysical(logicalName: string): string;
6
+ toLogical(physicalName: string): string;
7
+ }
8
+
9
+ /**
10
+ * Creates a table name mapper for a given namespace.
11
+ * Physical names have format: {logicalName}_{namespace}
12
+ */
13
+ export function createTableNameMapper(namespace: string): TableNameMapper {
14
+ return {
15
+ toPhysical: (logicalName: string) => `${logicalName}_${namespace}`,
16
+ toLogical: (physicalName: string) => {
17
+ if (physicalName.endsWith(`_${namespace}`)) {
18
+ return physicalName.slice(0, -(namespace.length + 1));
19
+ }
20
+ return physicalName;
21
+ },
22
+ };
23
+ }
@@ -12,6 +12,7 @@ import { createKyselyQueryBuilder } from "./kysely-query-builder";
12
12
  import { buildCondition, type Condition } from "../../query/condition-builder";
13
13
  import { decodeCursor, serializeCursorValues } from "../../query/cursor";
14
14
  import type { AnySelectClause } from "../../query/query";
15
+ import type { TableNameMapper } from "./kysely-shared";
15
16
 
16
17
  /**
17
18
  * Create a Kysely-specific Unit of Work compiler
@@ -21,14 +22,16 @@ import type { AnySelectClause } from "../../query/query";
21
22
  *
22
23
  * @param schema - The database schema
23
24
  * @param config - Kysely configuration
25
+ * @param mapper - Optional table name mapper for namespace prefixing
24
26
  * @returns A UOWCompiler instance for Kysely
25
27
  */
26
28
  export function createKyselyUOWCompiler<TSchema extends AnySchema>(
27
29
  schema: TSchema,
28
30
  config: KyselyConfig,
31
+ mapper?: TableNameMapper,
29
32
  ): UOWCompiler<TSchema, CompiledQuery> {
30
- const queryCompiler = createKyselyQueryCompiler(schema, config);
31
- const queryBuilder = createKyselyQueryBuilder(config.db, config.provider);
33
+ const queryCompiler = createKyselyQueryCompiler(schema, config, mapper);
34
+ const queryBuilder = createKyselyQueryBuilder(config.db, config.provider, mapper);
32
35
  const { provider } = config;
33
36
 
34
37
  function toTable(name: unknown) {
@@ -168,7 +168,7 @@ describe("execute() - MySQL", () => {
168
168
  type: "timestamp",
169
169
  isNullable: false,
170
170
  role: "regular",
171
- default: { runtime: "now" },
171
+ default: { dbSpecial: "now" },
172
172
  },
173
173
  ],
174
174
  };
@@ -679,7 +679,7 @@ describe("execute() - MySQL", () => {
679
679
  type: "timestamp",
680
680
  isNullable: false,
681
681
  role: "regular",
682
- default: { runtime: "now" },
682
+ default: { dbSpecial: "now" },
683
683
  },
684
684
  updateDataType: false,
685
685
  updateNullable: false,