@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
@@ -80,6 +80,7 @@ describe("DrizzleAdapter PGLite", () => {
80
80
  "drizzle-adapter-pglite",
81
81
  testSchema,
82
82
  "postgresql",
83
+ "namespace",
83
84
  );
84
85
 
85
86
  // Create Drizzle instance with PGLite (in-memory Postgres)
@@ -103,13 +104,16 @@ describe("DrizzleAdapter PGLite", () => {
103
104
  provider: "postgresql",
104
105
  });
105
106
 
107
+ expect(await adapter.isConnectionHealthy()).toBe(true);
108
+
106
109
  return async () => {
107
110
  await cleanup();
108
111
  };
109
112
  }, 12000);
110
113
 
111
114
  it("should execute Unit of Work with version checking", async () => {
112
- const queryEngine = adapter.createQueryEngine(testSchema, "test");
115
+ // Pass namespace to ensure mapper translates logical table names to physical (prefixed) names
116
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
113
117
 
114
118
  // Create initial user using UOW
115
119
  const createUow = queryEngine.createUnitOfWork("create-user").create("users", {
@@ -194,7 +198,7 @@ describe("DrizzleAdapter PGLite", () => {
194
198
  });
195
199
 
196
200
  it("should support count operations", async () => {
197
- const queryEngine = adapter.createQueryEngine(testSchema, "test");
201
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
198
202
 
199
203
  // Create some users
200
204
  await queryEngine
@@ -215,7 +219,7 @@ describe("DrizzleAdapter PGLite", () => {
215
219
  });
216
220
 
217
221
  it("should support cursor-based pagination", async () => {
218
- const queryEngine = adapter.createQueryEngine(testSchema, "test");
222
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
219
223
 
220
224
  const createUow = queryEngine
221
225
  .createUnitOfWork("create-users")
@@ -262,7 +266,7 @@ describe("DrizzleAdapter PGLite", () => {
262
266
  });
263
267
 
264
268
  it("should support joins", async () => {
265
- const queryEngine = adapter.createQueryEngine(testSchema, "test");
269
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
266
270
  const queries: DrizzleCompiledQuery[] = [];
267
271
 
268
272
  const createUow = queryEngine
@@ -298,7 +302,7 @@ describe("DrizzleAdapter PGLite", () => {
298
302
 
299
303
  const [query] = queries;
300
304
  expect(query.sql).toMatchInlineSnapshot(
301
- `"select "emails"."id", "emails"."user_id", "emails"."email", "emails"."is_primary", "emails"."_internalId", "emails"."_version", "emails_user"."data" as "user" from "emails" "emails" left join lateral (select json_build_array("emails_user"."name", "emails_user"."id", "emails_user"."age", "emails_user"."_internalId", "emails_user"."_version") as "data" from (select * from "users" "emails_user" where "emails_user"."_internalId" = "emails"."user_id" limit $1) "emails_user") "emails_user" on true"`,
305
+ `"select "emails_namespace"."id", "emails_namespace"."user_id", "emails_namespace"."email", "emails_namespace"."is_primary", "emails_namespace"."_internalId", "emails_namespace"."_version", "emails_namespace_user"."data" as "user" from "emails_namespace" "emails_namespace" left join lateral (select json_build_array("emails_namespace_user"."name", "emails_namespace_user"."id", "emails_namespace_user"."age", "emails_namespace_user"."_internalId", "emails_namespace_user"."_version") as "data" from (select * from "users_namespace" "emails_namespace_user" where "emails_namespace_user"."_internalId" = "emails_namespace"."user_id" limit $1) "emails_namespace_user") "emails_namespace_user" on true"`,
302
306
  );
303
307
 
304
308
  expect(email).toMatchObject({
@@ -323,7 +327,7 @@ describe("DrizzleAdapter PGLite", () => {
323
327
  });
324
328
 
325
329
  it("should support complex nested joins (comments -> post -> author)", async () => {
326
- const queryEngine = adapter.createQueryEngine(testSchema, "test");
330
+ const queryEngine = adapter.createQueryEngine(testSchema, "namespace");
327
331
  const queries: DrizzleCompiledQuery[] = [];
328
332
 
329
333
  // Create a user (author)
@@ -427,7 +431,7 @@ describe("DrizzleAdapter PGLite", () => {
427
431
 
428
432
  const [query] = queries;
429
433
  expect(query.sql).toMatchInlineSnapshot(
430
- `"select "comments"."id", "comments"."post_id", "comments"."user_id", "comments"."text", "comments"."_internalId", "comments"."_version", "comments_post"."data" as "post", "comments_commenter"."data" as "commenter" from "comments" "comments" left join lateral (select json_build_array("comments_post"."id", "comments_post"."title", "comments_post"."content", "comments_post"."_internalId", "comments_post"."_version", "comments_post_author"."data") as "data" from (select * from "posts" "comments_post" where "comments_post"."_internalId" = "comments"."post_id" order by "comments_post"."id" desc limit $1) "comments_post" left join lateral (select json_build_array("comments_post_author"."id", "comments_post_author"."name", "comments_post_author"."age", "comments_post_author"."_internalId", "comments_post_author"."_version") as "data" from (select * from "users" "comments_post_author" where "comments_post_author"."_internalId" = "comments_post"."user_id" order by "comments_post_author"."name" asc limit $2) "comments_post_author") "comments_post_author" on true) "comments_post" on true left join lateral (select json_build_array("comments_commenter"."id", "comments_commenter"."name", "comments_commenter"."_internalId", "comments_commenter"."_version") as "data" from (select * from "users" "comments_commenter" where "comments_commenter"."_internalId" = "comments"."user_id" limit $3) "comments_commenter") "comments_commenter" on true"`,
434
+ `"select "comments_namespace"."id", "comments_namespace"."post_id", "comments_namespace"."user_id", "comments_namespace"."text", "comments_namespace"."_internalId", "comments_namespace"."_version", "comments_namespace_post"."data" as "post", "comments_namespace_commenter"."data" as "commenter" from "comments_namespace" "comments_namespace" left join lateral (select json_build_array("comments_namespace_post"."id", "comments_namespace_post"."title", "comments_namespace_post"."content", "comments_namespace_post"."_internalId", "comments_namespace_post"."_version", "comments_namespace_post_author"."data") as "data" from (select * from "posts_namespace" "comments_namespace_post" where "comments_namespace_post"."_internalId" = "comments_namespace"."post_id" order by "comments_namespace_post"."id" desc limit $1) "comments_namespace_post" left join lateral (select json_build_array("comments_namespace_post_author"."id", "comments_namespace_post_author"."name", "comments_namespace_post_author"."age", "comments_namespace_post_author"."_internalId", "comments_namespace_post_author"."_version") as "data" from (select * from "users_namespace" "comments_namespace_post_author" where "comments_namespace_post_author"."_internalId" = "comments_namespace_post"."user_id" order by "comments_namespace_post_author"."name" asc limit $2) "comments_namespace_post_author") "comments_namespace_post_author" on true) "comments_namespace_post" on true left join lateral (select json_build_array("comments_namespace_commenter"."id", "comments_namespace_commenter"."name", "comments_namespace_commenter"."_internalId", "comments_namespace_commenter"."_version") as "data" from (select * from "users_namespace" "comments_namespace_commenter" where "comments_namespace_commenter"."_internalId" = "comments_namespace"."user_id" limit $3) "comments_namespace_commenter") "comments_namespace_commenter" on true"`,
431
435
  );
432
436
  });
433
437
  });
@@ -15,7 +15,7 @@ describe("DrizzleAdapter", () => {
15
15
  provider: "postgresql",
16
16
  });
17
17
 
18
- const generator = adapter.createSchemaGenerator(testSchema, "test");
18
+ const generator = adapter.createSchemaGenerator([{ schema: testSchema, namespace: "test" }]);
19
19
  const result = generator.generateSchema({ path: "schema.ts" });
20
20
 
21
21
  expect(result.path).toBe("schema.ts");
@@ -23,22 +23,38 @@ describe("DrizzleAdapter", () => {
23
23
  "import { pgTable, varchar, text, bigserial, integer, uniqueIndex } from "drizzle-orm/pg-core"
24
24
  import { createId } from "@fragno-dev/db/id"
25
25
 
26
- export const users = pgTable("users", {
27
- id: varchar("id", { length: 30 }).notNull().$defaultFn(() => createId()),
28
- name: text("name").notNull(),
29
- _internalId: bigserial("_internalId", { mode: "number" }).primaryKey().notNull(),
30
- _version: integer("_version").notNull().default(0)
31
- })
26
+ // ============================================================================
27
+ // Settings Table (shared across all fragments)
28
+ // ============================================================================
32
29
 
33
30
  export const fragno_db_settings = pgTable("fragno_db_settings", {
34
31
  id: varchar("id", { length: 30 }).notNull().$defaultFn(() => createId()),
35
32
  key: text("key").notNull(),
36
- value: text("value").notNull().default("1"),
33
+ value: text("value").notNull(),
37
34
  _internalId: bigserial("_internalId", { mode: "number" }).primaryKey().notNull(),
38
35
  _version: integer("_version").notNull().default(0)
39
36
  }, (table) => [
40
37
  uniqueIndex("unique_key").on(table.key)
41
- ])"
38
+ ])
39
+
40
+ export const fragnoDbSettingSchemaVersion = 1;
41
+
42
+ // ============================================================================
43
+ // Fragment: test
44
+ // ============================================================================
45
+
46
+ export const users_test = pgTable("users_test", {
47
+ id: varchar("id", { length: 30 }).notNull().$defaultFn(() => createId()),
48
+ name: text("name").notNull(),
49
+ _internalId: bigserial("_internalId", { mode: "number" }).primaryKey().notNull(),
50
+ _version: integer("_version").notNull().default(0)
51
+ })
52
+
53
+ export const test_schema = {
54
+ "users_test": users_test,
55
+ users: users_test,
56
+ schemaVersion: 1
57
+ }"
42
58
  `);
43
59
  });
44
60
 
@@ -48,7 +64,7 @@ describe("DrizzleAdapter", () => {
48
64
  provider: "sqlite",
49
65
  });
50
66
 
51
- const generator = adapter.createSchemaGenerator(testSchema, "test");
67
+ const generator = adapter.createSchemaGenerator([{ schema: testSchema, namespace: "test" }]);
52
68
  const result = generator.generateSchema({ path: "schema.ts" });
53
69
 
54
70
  expect(result.path).toBe("schema.ts");
@@ -56,22 +72,38 @@ describe("DrizzleAdapter", () => {
56
72
  "import { sqliteTable, text, integer, uniqueIndex } from "drizzle-orm/sqlite-core"
57
73
  import { createId } from "@fragno-dev/db/id"
58
74
 
59
- export const users = sqliteTable("users", {
60
- id: text("id").notNull().$defaultFn(() => createId()),
61
- name: text("name").notNull(),
62
- _internalId: integer("_internalId").primaryKey().autoincrement().notNull(),
63
- _version: integer("_version").notNull().default(0)
64
- })
75
+ // ============================================================================
76
+ // Settings Table (shared across all fragments)
77
+ // ============================================================================
65
78
 
66
79
  export const fragno_db_settings = sqliteTable("fragno_db_settings", {
67
80
  id: text("id").notNull().$defaultFn(() => createId()),
68
81
  key: text("key").notNull(),
69
- value: text("value").notNull().default("1"),
82
+ value: text("value").notNull(),
70
83
  _internalId: integer("_internalId").primaryKey().autoincrement().notNull(),
71
84
  _version: integer("_version").notNull().default(0)
72
85
  }, (table) => [
73
86
  uniqueIndex("unique_key").on(table.key)
74
- ])"
87
+ ])
88
+
89
+ export const fragnoDbSettingSchemaVersion = 1;
90
+
91
+ // ============================================================================
92
+ // Fragment: test
93
+ // ============================================================================
94
+
95
+ export const users_test = sqliteTable("users_test", {
96
+ id: text("id").notNull().$defaultFn(() => createId()),
97
+ name: text("name").notNull(),
98
+ _internalId: integer("_internalId").primaryKey().autoincrement().notNull(),
99
+ _version: integer("_version").notNull().default(0)
100
+ })
101
+
102
+ export const test_schema = {
103
+ "users_test": users_test,
104
+ users: users_test,
105
+ schemaVersion: 1
106
+ }"
75
107
  `);
76
108
  });
77
109
 
@@ -81,10 +113,10 @@ describe("DrizzleAdapter", () => {
81
113
  provider: "postgresql",
82
114
  });
83
115
 
84
- const generator = adapter.createSchemaGenerator(testSchema, "myapp");
116
+ const generator = adapter.createSchemaGenerator([{ schema: testSchema, namespace: "myapp" }]);
85
117
  const result = generator.generateSchema();
86
118
 
87
- expect(result.path).toBe("drizzle-schema-myapp.ts");
119
+ expect(result.path).toBe("fragno-schema.ts");
88
120
  });
89
121
 
90
122
  it("should preserve original schema tables", () => {
@@ -93,7 +125,7 @@ describe("DrizzleAdapter", () => {
93
125
  provider: "postgresql",
94
126
  });
95
127
 
96
- const generator = adapter.createSchemaGenerator(testSchema, "test");
128
+ const generator = adapter.createSchemaGenerator([{ schema: testSchema, namespace: "test" }]);
97
129
  const result = generator.generateSchema();
98
130
 
99
131
  // Original table should still be there
@@ -101,22 +133,38 @@ describe("DrizzleAdapter", () => {
101
133
  "import { pgTable, varchar, text, bigserial, integer, uniqueIndex } from "drizzle-orm/pg-core"
102
134
  import { createId } from "@fragno-dev/db/id"
103
135
 
104
- export const users = pgTable("users", {
105
- id: varchar("id", { length: 30 }).notNull().$defaultFn(() => createId()),
106
- name: text("name").notNull(),
107
- _internalId: bigserial("_internalId", { mode: "number" }).primaryKey().notNull(),
108
- _version: integer("_version").notNull().default(0)
109
- })
136
+ // ============================================================================
137
+ // Settings Table (shared across all fragments)
138
+ // ============================================================================
110
139
 
111
140
  export const fragno_db_settings = pgTable("fragno_db_settings", {
112
141
  id: varchar("id", { length: 30 }).notNull().$defaultFn(() => createId()),
113
142
  key: text("key").notNull(),
114
- value: text("value").notNull().default("1"),
143
+ value: text("value").notNull(),
115
144
  _internalId: bigserial("_internalId", { mode: "number" }).primaryKey().notNull(),
116
145
  _version: integer("_version").notNull().default(0)
117
146
  }, (table) => [
118
147
  uniqueIndex("unique_key").on(table.key)
119
- ])"
148
+ ])
149
+
150
+ export const fragnoDbSettingSchemaVersion = 1;
151
+
152
+ // ============================================================================
153
+ // Fragment: test
154
+ // ============================================================================
155
+
156
+ export const users_test = pgTable("users_test", {
157
+ id: varchar("id", { length: 30 }).notNull().$defaultFn(() => createId()),
158
+ name: text("name").notNull(),
159
+ _internalId: bigserial("_internalId", { mode: "number" }).primaryKey().notNull(),
160
+ _version: integer("_version").notNull().default(0)
161
+ })
162
+
163
+ export const test_schema = {
164
+ "users_test": users_test,
165
+ users: users_test,
166
+ schemaVersion: 1
167
+ }"
120
168
  `);
121
169
  });
122
170
  });
@@ -1,19 +1,12 @@
1
1
  import type { DatabaseAdapter } from "../adapters";
2
- import {
3
- column,
4
- idColumn,
5
- schema,
6
- SchemaBuilder,
7
- type AnySchema,
8
- type FragnoId,
9
- } from "../../schema/create";
2
+ import { type AnySchema } from "../../schema/create";
10
3
  import type { AbstractQuery } from "../../query/query";
11
4
  import type { SchemaGenerator } from "../../schema-generator/schema-generator";
12
5
  import { generateSchema } from "./generate";
13
6
  import { fromDrizzle, type DrizzleUOWConfig } from "./drizzle-query";
14
- import { createId } from "../../id";
15
-
16
- const SETTINGS_TABLE_NAME = "fragno_db_settings" as const;
7
+ import { createTableNameMapper, type DBType, type DrizzleResult } from "./shared";
8
+ import { createSettingsManager, settingsSchema } from "../../shared/settings-schema";
9
+ import { sql } from "drizzle-orm";
17
10
 
18
11
  export interface DrizzleConfig {
19
12
  db: unknown;
@@ -27,92 +20,52 @@ export class DrizzleAdapter implements DatabaseAdapter<DrizzleUOWConfig> {
27
20
  this.#drizzleConfig = config;
28
21
  }
29
22
 
30
- #createFullSchema<T extends AnySchema>(schema: T) {
31
- return new SchemaBuilder()
32
- .mergeWithExistingSchema(schema)
33
- .mergeWithExistingSchema(createSettingsSchema(schema.version))
34
- .build();
23
+ get provider(): "sqlite" | "mysql" | "postgresql" {
24
+ return this.#drizzleConfig.provider;
25
+ }
26
+
27
+ async isConnectionHealthy(): Promise<boolean> {
28
+ try {
29
+ const result = (await (this.#drizzleConfig.db as DBType).execute(
30
+ sql`SELECT 1 as healthy`,
31
+ )) as DrizzleResult;
32
+ return result.rows[0]["healthy"] === 1;
33
+ } catch {
34
+ return false;
35
+ }
35
36
  }
36
37
 
37
38
  async getSchemaVersion(namespace: string): Promise<string | undefined> {
38
- const queryEngine = this.createQueryEngine(createSettingsSchema(0), namespace);
39
+ const queryEngine = this.createQueryEngine(settingsSchema, namespace);
39
40
  const manager = createSettingsManager(queryEngine, namespace);
40
- const randomId = createId();
41
-
42
- const result = await manager.createKeyWithDefault(randomId);
43
- if (result) {
44
- await manager.delete(result.id);
45
- }
46
41
 
42
+ // Try to read the version key directly
43
+ const result = await manager.get("version");
47
44
  return result?.value;
48
45
  }
49
46
 
50
47
  createQueryEngine<TSchema extends AnySchema>(
51
48
  schema: TSchema,
52
- _namespace: string,
49
+ namespace: string,
53
50
  ): AbstractQuery<TSchema, DrizzleUOWConfig> {
54
- return fromDrizzle(schema, this.#drizzleConfig);
51
+ // Only create mapper if namespace is non-empty
52
+ const mapper = namespace ? createTableNameMapper(namespace) : undefined;
53
+ return fromDrizzle(schema, this.#drizzleConfig, mapper);
55
54
  }
56
55
 
57
- createSchemaGenerator(schema: AnySchema, namespace: string): SchemaGenerator {
56
+ createSchemaGenerator(
57
+ fragments: { schema: AnySchema; namespace: string }[],
58
+ options?: { path?: string },
59
+ ): SchemaGenerator {
58
60
  return {
59
- generateSchema: (options) => {
60
- const path = options?.path ?? `drizzle-schema-${namespace}.ts`;
61
-
62
- const schemaWithSettingsTable = this.#createFullSchema(schema);
61
+ generateSchema: (genOptions) => {
62
+ const path = genOptions?.path ?? options?.path ?? "fragno-schema.ts";
63
63
 
64
64
  return {
65
- schema: generateSchema(schemaWithSettingsTable, this.#drizzleConfig.provider),
65
+ schema: generateSchema(fragments, this.#drizzleConfig.provider),
66
66
  path,
67
67
  };
68
68
  },
69
69
  };
70
70
  }
71
71
  }
72
-
73
- function createSettingsSchema(version: number) {
74
- return schema((s) => {
75
- return s.addTable(SETTINGS_TABLE_NAME, (t) => {
76
- return t
77
- .addColumn("id", idColumn())
78
- .addColumn("key", column("string"))
79
- .addColumn("value", column("string").defaultTo(String(version)))
80
- .createIndex("unique_key", ["key"], { unique: true });
81
- });
82
- });
83
- }
84
-
85
- function createSettingsManager(
86
- queryEngine: AbstractQuery<ReturnType<typeof createSettingsSchema>, DrizzleUOWConfig>,
87
- namespace: string,
88
- ) {
89
- return {
90
- async createKeyWithDefault(key: string) {
91
- const writeUow = queryEngine
92
- .createUnitOfWork("createKeyWithDefault")
93
- .create(SETTINGS_TABLE_NAME, {
94
- key: `${namespace}.${key}`,
95
- });
96
- const { success } = await writeUow.executeMutations();
97
- if (!success) {
98
- throw new Error("Failed to create key with default");
99
- }
100
-
101
- return this.get(key);
102
- },
103
-
104
- async get(key: string): Promise<{ id: FragnoId; key: string; value: string } | undefined> {
105
- const uow = queryEngine
106
- .createUnitOfWork()
107
- .find(SETTINGS_TABLE_NAME, (b) =>
108
- b.whereIndex("unique_key", (eb) => eb("key", "=", `${namespace}.${key}`)),
109
- );
110
- const [[result]] = await uow.executeRetrieve();
111
- return result; // FIXME: result should be maybe undefined
112
- },
113
-
114
- async delete(id: FragnoId) {
115
- await queryEngine.delete(SETTINGS_TABLE_NAME, id);
116
- },
117
- };
118
- }
@@ -5,14 +5,9 @@ import type { CompiledMutation, UOWExecutor } from "../../query/unit-of-work";
5
5
  import { createDrizzleUOWCompiler, type DrizzleCompiledQuery } from "./drizzle-uow-compiler";
6
6
  import { executeDrizzleRetrievalPhase, executeDrizzleMutationPhase } from "./drizzle-uow-executor";
7
7
  import { UnitOfWork } from "../../query/unit-of-work";
8
- import { parseDrizzle } from "./shared";
8
+ import { parseDrizzle, type DrizzleResult, type TableNameMapper } from "./shared";
9
9
  import { createDrizzleUOWDecoder } from "./drizzle-uow-decoder";
10
10
 
11
- export interface DrizzleResult {
12
- rows: Record<string, unknown>[];
13
- affectedRows: number;
14
- }
15
-
16
11
  /**
17
12
  * Configuration options for creating a Drizzle Unit of Work
18
13
  */
@@ -33,6 +28,7 @@ export interface DrizzleUOWConfig {
33
28
  *
34
29
  * @param schema - The database schema definition
35
30
  * @param config - Drizzle configuration containing the database instance and provider
31
+ * @param mapper - Optional table name mapper for namespace prefixing
36
32
  * @returns An AbstractQuery instance for performing database operations
37
33
  *
38
34
  * @example
@@ -48,12 +44,13 @@ export interface DrizzleUOWConfig {
48
44
  export function fromDrizzle<T extends AnySchema>(
49
45
  schema: T,
50
46
  config: DrizzleConfig,
47
+ mapper?: TableNameMapper,
51
48
  ): AbstractQuery<T, DrizzleUOWConfig> {
52
49
  const [db] = parseDrizzle(config.db);
53
50
  const { provider } = config;
54
51
 
55
52
  function createUOW(name?: string, uowConfig?: DrizzleUOWConfig) {
56
- const uowCompiler = createDrizzleUOWCompiler(schema, config, uowConfig?.onQuery);
53
+ const uowCompiler = createDrizzleUOWCompiler(schema, config, mapper, uowConfig?.onQuery);
57
54
 
58
55
  const executor: UOWExecutor<DrizzleCompiledQuery, DrizzleResult> = {
59
56
  executeRetrievalPhase: (retrievalBatch: DrizzleCompiledQuery[]) =>
@@ -755,9 +755,15 @@ describe("drizzle-uow-compiler", () => {
755
755
  return t
756
756
  .addColumn("id", idColumn())
757
757
  .addColumn("message", column("string"))
758
- .addColumn("sessionId", column("string").defaultTo$("auto")) // runtime auto
759
- .addColumn("timestamp", column("timestamp").defaultTo$("now")) // runtime now
760
- .addColumn("counter", column("integer").defaultTo$((() => 42) as () => number)) // runtime function
758
+ .addColumn(
759
+ "sessionId",
760
+ column("string").defaultTo$((b) => b.cuid()),
761
+ ) // runtime cuid
762
+ .addColumn(
763
+ "timestamp",
764
+ column("timestamp").defaultTo$((b) => b.now()),
765
+ ) // runtime now
766
+ .addColumn("counter", column("integer").defaultTo$(42)) // runtime function
761
767
  .addColumn("status", column("string").defaultTo("pending")) // static default
762
768
  .createIndex("idx_session", ["sessionId"]);
763
769
  });
@@ -9,7 +9,7 @@ import type {
9
9
  } from "../../query/unit-of-work";
10
10
  import { buildCondition, type Condition } from "../../query/condition-builder";
11
11
  import type { DrizzleConfig } from "./drizzle-adapter";
12
- import { type ColumnType, type TableType, parseDrizzle } from "./shared";
12
+ import { type ColumnType, type TableType, type TableNameMapper, parseDrizzle } from "./shared";
13
13
  import { encodeValues, ReferenceSubquery } from "../../query/result-transform";
14
14
  import { serialize } from "../../schema/serialize";
15
15
  import { decodeCursor, serializeCursorValues } from "../../query/cursor";
@@ -29,12 +29,14 @@ export type DrizzleCompiledQuery = {
29
29
  *
30
30
  * @param schema - The database schema
31
31
  * @param config - Drizzle configuration
32
+ * @param mapper - Optional table name mapper for namespace prefixing
32
33
  * @param onQuery - Optional callback to receive compiled queries for logging/debugging
33
34
  * @returns A UOWCompiler instance for Drizzle
34
35
  */
35
36
  export function createDrizzleUOWCompiler<TSchema extends AnySchema>(
36
37
  schema: TSchema,
37
38
  config: DrizzleConfig,
39
+ mapper?: TableNameMapper,
38
40
  onQuery?: (query: DrizzleCompiledQuery) => void,
39
41
  ): UOWCompiler<TSchema, DrizzleCompiledQuery> {
40
42
  const [db, drizzleTables] = parseDrizzle(config.db);
@@ -45,14 +47,15 @@ export function createDrizzleUOWCompiler<TSchema extends AnySchema>(
45
47
  * @throws Error if table is not found in Drizzle schema
46
48
  */
47
49
  function toDrizzleTable(table: AnyTable): TableType {
48
- const tableName = table.ormName;
49
- const out = drizzleTables[tableName];
50
+ // Map logical table name to physical table name using the mapper
51
+ const physicalTableName = mapper ? mapper.toPhysical(table.ormName) : table.ormName;
52
+ const out = drizzleTables[physicalTableName];
50
53
  if (out) {
51
54
  return out;
52
55
  }
53
56
 
54
57
  throw new Error(
55
- `[Drizzle] Unknown table name ${tableName}, is it included in your Drizzle schema?`,
58
+ `[Drizzle] Unknown table name ${physicalTableName} (logical: ${table.ormName}), is it included in your Drizzle schema?`,
56
59
  );
57
60
  }
58
61
 
@@ -150,7 +153,9 @@ export function createDrizzleUOWCompiler<TSchema extends AnySchema>(
150
153
 
151
154
  if (condition.type === "not") {
152
155
  const result = buildWhere(condition.item);
153
- if (!result) return;
156
+ if (!result) {
157
+ return;
158
+ }
154
159
 
155
160
  return Drizzle.not(result);
156
161
  }
@@ -435,7 +440,8 @@ export function createDrizzleUOWCompiler<TSchema extends AnySchema>(
435
440
  queryConfig.with = processJoins(joins);
436
441
  }
437
442
 
438
- const compiledQuery = db.query[op.table.ormName].findMany(queryConfig).toSQL();
443
+ const physicalTableName = mapper ? mapper.toPhysical(op.table.ormName) : op.table.ormName;
444
+ const compiledQuery = db.query[physicalTableName].findMany(queryConfig).toSQL();
439
445
  onQuery?.(compiledQuery);
440
446
  return compiledQuery;
441
447
  }
@@ -2,8 +2,8 @@ import type { AnySchema, AnyTable } from "../../schema/create";
2
2
  import type { SQLProvider } from "../../shared/providers";
3
3
  import type { RetrievalOperation, UOWDecoder } from "../../query/unit-of-work";
4
4
  import { decodeResult } from "../../query/result-transform";
5
- import type { DrizzleResult } from "./drizzle-query";
6
5
  import { getOrderedJoinColumns } from "./join-column-utils";
6
+ import type { DrizzleResult } from "./shared";
7
7
 
8
8
  /**
9
9
  * Join information with nested join support
@@ -2,7 +2,7 @@ import { SQL, StringChunk, sql, type SQLChunk } from "drizzle-orm";
2
2
  import type { CompiledMutation, MutationResult } from "../../query/unit-of-work";
3
3
  import type { DBType } from "./shared";
4
4
  import type { DrizzleCompiledQuery } from "./drizzle-uow-compiler";
5
- import type { DrizzleResult } from "./drizzle-query";
5
+ import type { DrizzleResult } from "./shared";
6
6
 
7
7
  /**
8
8
  * Convert a DrizzleCompiledQuery (SQL string + params) to a Drizzle SQL object