@fragno-dev/db 0.1.14 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/.turbo/turbo-build.log +179 -139
  2. package/CHANGELOG.md +24 -0
  3. package/dist/adapters/adapters.d.ts +15 -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 +3 -1
  7. package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +1 -1
  8. package/dist/adapters/drizzle/drizzle-adapter.js +9 -2
  9. package/dist/adapters/drizzle/drizzle-adapter.js.map +1 -1
  10. package/dist/adapters/drizzle/drizzle-query.js +2 -2
  11. package/dist/adapters/drizzle/drizzle-query.js.map +1 -1
  12. package/dist/adapters/drizzle/drizzle-uow-compiler.js +27 -8
  13. package/dist/adapters/drizzle/drizzle-uow-compiler.js.map +1 -1
  14. package/dist/adapters/drizzle/drizzle-uow-decoder.js +22 -15
  15. package/dist/adapters/drizzle/drizzle-uow-decoder.js.map +1 -1
  16. package/dist/adapters/drizzle/drizzle-uow-executor.js +18 -7
  17. package/dist/adapters/drizzle/drizzle-uow-executor.js.map +1 -1
  18. package/dist/adapters/drizzle/generate.d.ts +4 -1
  19. package/dist/adapters/drizzle/generate.d.ts.map +1 -1
  20. package/dist/adapters/drizzle/generate.js +11 -18
  21. package/dist/adapters/drizzle/generate.js.map +1 -1
  22. package/dist/adapters/kysely/kysely-adapter.d.ts +3 -1
  23. package/dist/adapters/kysely/kysely-adapter.d.ts.map +1 -1
  24. package/dist/adapters/kysely/kysely-adapter.js +7 -1
  25. package/dist/adapters/kysely/kysely-adapter.js.map +1 -1
  26. package/dist/adapters/kysely/kysely-query-builder.js +1 -1
  27. package/dist/adapters/kysely/kysely-query-compiler.js +3 -2
  28. package/dist/adapters/kysely/kysely-query-compiler.js.map +1 -1
  29. package/dist/adapters/kysely/kysely-query.d.ts +1 -0
  30. package/dist/adapters/kysely/kysely-query.d.ts.map +1 -1
  31. package/dist/adapters/kysely/kysely-query.js +25 -18
  32. package/dist/adapters/kysely/kysely-query.js.map +1 -1
  33. package/dist/adapters/kysely/kysely-shared.d.ts +3 -0
  34. package/dist/adapters/kysely/kysely-shared.d.ts.map +1 -1
  35. package/dist/adapters/kysely/kysely-shared.js +16 -1
  36. package/dist/adapters/kysely/kysely-shared.js.map +1 -1
  37. package/dist/adapters/kysely/kysely-uow-compiler.js +34 -11
  38. package/dist/adapters/kysely/kysely-uow-compiler.js.map +1 -1
  39. package/dist/adapters/kysely/kysely-uow-executor.js +8 -4
  40. package/dist/adapters/kysely/kysely-uow-executor.js.map +1 -1
  41. package/dist/adapters/kysely/migration/execute-base.js +1 -1
  42. package/dist/adapters/kysely/migration/execute-base.js.map +1 -1
  43. package/dist/db-fragment-definition-builder.d.ts +152 -0
  44. package/dist/db-fragment-definition-builder.d.ts.map +1 -0
  45. package/dist/db-fragment-definition-builder.js +137 -0
  46. package/dist/db-fragment-definition-builder.js.map +1 -0
  47. package/dist/fragments/internal-fragment.d.ts +19 -0
  48. package/dist/fragments/internal-fragment.d.ts.map +1 -0
  49. package/dist/fragments/internal-fragment.js +39 -0
  50. package/dist/fragments/internal-fragment.js.map +1 -0
  51. package/dist/migration-engine/generation-engine.d.ts.map +1 -1
  52. package/dist/migration-engine/generation-engine.js +35 -15
  53. package/dist/migration-engine/generation-engine.js.map +1 -1
  54. package/dist/mod.d.ts +8 -20
  55. package/dist/mod.d.ts.map +1 -1
  56. package/dist/mod.js +7 -35
  57. package/dist/mod.js.map +1 -1
  58. package/dist/node_modules/.pnpm/rou3@0.7.8/node_modules/rou3/dist/index.js +165 -0
  59. package/dist/node_modules/.pnpm/rou3@0.7.8/node_modules/rou3/dist/index.js.map +1 -0
  60. package/dist/packages/fragno/dist/api/bind-services.js +20 -0
  61. package/dist/packages/fragno/dist/api/bind-services.js.map +1 -0
  62. package/dist/packages/fragno/dist/api/error.js +48 -0
  63. package/dist/packages/fragno/dist/api/error.js.map +1 -0
  64. package/dist/packages/fragno/dist/api/fragment-definition-builder.js +320 -0
  65. package/dist/packages/fragno/dist/api/fragment-definition-builder.js.map +1 -0
  66. package/dist/packages/fragno/dist/api/fragment-instantiator.js +487 -0
  67. package/dist/packages/fragno/dist/api/fragment-instantiator.js.map +1 -0
  68. package/dist/packages/fragno/dist/api/fragno-response.js +73 -0
  69. package/dist/packages/fragno/dist/api/fragno-response.js.map +1 -0
  70. package/dist/packages/fragno/dist/api/internal/response-stream.js +81 -0
  71. package/dist/packages/fragno/dist/api/internal/response-stream.js.map +1 -0
  72. package/dist/packages/fragno/dist/api/internal/route.js +10 -0
  73. package/dist/packages/fragno/dist/api/internal/route.js.map +1 -0
  74. package/dist/packages/fragno/dist/api/mutable-request-state.js +97 -0
  75. package/dist/packages/fragno/dist/api/mutable-request-state.js.map +1 -0
  76. package/dist/packages/fragno/dist/api/request-context-storage.js +43 -0
  77. package/dist/packages/fragno/dist/api/request-context-storage.js.map +1 -0
  78. package/dist/packages/fragno/dist/api/request-input-context.js +118 -0
  79. package/dist/packages/fragno/dist/api/request-input-context.js.map +1 -0
  80. package/dist/packages/fragno/dist/api/request-middleware.js +83 -0
  81. package/dist/packages/fragno/dist/api/request-middleware.js.map +1 -0
  82. package/dist/packages/fragno/dist/api/request-output-context.js +119 -0
  83. package/dist/packages/fragno/dist/api/request-output-context.js.map +1 -0
  84. package/dist/packages/fragno/dist/api/route.js +17 -0
  85. package/dist/packages/fragno/dist/api/route.js.map +1 -0
  86. package/dist/packages/fragno/dist/internal/symbols.js +10 -0
  87. package/dist/packages/fragno/dist/internal/symbols.js.map +1 -0
  88. package/dist/query/cursor.d.ts +10 -2
  89. package/dist/query/cursor.d.ts.map +1 -1
  90. package/dist/query/cursor.js +11 -4
  91. package/dist/query/cursor.js.map +1 -1
  92. package/dist/query/execute-unit-of-work.d.ts +123 -0
  93. package/dist/query/execute-unit-of-work.d.ts.map +1 -0
  94. package/dist/query/execute-unit-of-work.js +184 -0
  95. package/dist/query/execute-unit-of-work.js.map +1 -0
  96. package/dist/query/query.d.ts +2 -2
  97. package/dist/query/query.d.ts.map +1 -1
  98. package/dist/query/result-transform.js +4 -2
  99. package/dist/query/result-transform.js.map +1 -1
  100. package/dist/query/retry-policy.d.ts +88 -0
  101. package/dist/query/retry-policy.d.ts.map +1 -0
  102. package/dist/query/retry-policy.js +61 -0
  103. package/dist/query/retry-policy.js.map +1 -0
  104. package/dist/query/unit-of-work.d.ts +104 -50
  105. package/dist/query/unit-of-work.d.ts.map +1 -1
  106. package/dist/query/unit-of-work.js +384 -194
  107. package/dist/query/unit-of-work.js.map +1 -1
  108. package/dist/schema/serialize.js +12 -7
  109. package/dist/schema/serialize.js.map +1 -1
  110. package/dist/with-database.d.ts +28 -0
  111. package/dist/with-database.d.ts.map +1 -0
  112. package/dist/with-database.js +34 -0
  113. package/dist/with-database.js.map +1 -0
  114. package/package.json +9 -2
  115. package/src/adapters/adapters.ts +16 -0
  116. package/src/adapters/drizzle/drizzle-adapter-pglite.test.ts +80 -16
  117. package/src/adapters/drizzle/drizzle-adapter-sqlite.test.ts +158 -2
  118. package/src/adapters/drizzle/drizzle-adapter.test.ts +3 -51
  119. package/src/adapters/drizzle/drizzle-adapter.ts +20 -7
  120. package/src/adapters/drizzle/drizzle-query.ts +1 -2
  121. package/src/adapters/drizzle/drizzle-uow-compiler-mysql.test.ts +1442 -0
  122. package/src/adapters/drizzle/drizzle-uow-compiler-sqlite.test.ts +1414 -0
  123. package/src/adapters/drizzle/drizzle-uow-compiler.test.ts +21 -4
  124. package/src/adapters/drizzle/drizzle-uow-compiler.ts +44 -3
  125. package/src/adapters/drizzle/drizzle-uow-decoder.ts +32 -22
  126. package/src/adapters/drizzle/drizzle-uow-executor.ts +41 -8
  127. package/src/adapters/drizzle/generate.test.ts +102 -269
  128. package/src/adapters/drizzle/generate.ts +12 -30
  129. package/src/adapters/drizzle/test-utils.ts +36 -5
  130. package/src/adapters/kysely/kysely-adapter-pglite.test.ts +64 -20
  131. package/src/adapters/kysely/kysely-adapter-sqlite.test.ts +156 -0
  132. package/src/adapters/kysely/kysely-adapter.ts +9 -1
  133. package/src/adapters/kysely/kysely-query-compiler.ts +3 -8
  134. package/src/adapters/kysely/kysely-query.ts +34 -25
  135. package/src/adapters/kysely/kysely-shared.ts +34 -0
  136. package/src/adapters/kysely/kysely-uow-compiler.test.ts +61 -73
  137. package/src/adapters/kysely/kysely-uow-compiler.ts +44 -12
  138. package/src/adapters/kysely/kysely-uow-executor.ts +26 -7
  139. package/src/adapters/kysely/kysely-uow-joins.test.ts +31 -48
  140. package/src/adapters/kysely/migration/execute-base.ts +1 -1
  141. package/src/db-fragment-definition-builder.test.ts +887 -0
  142. package/src/db-fragment-definition-builder.ts +506 -0
  143. package/src/db-fragment-instantiator.test.ts +467 -0
  144. package/src/db-fragment-integration.test.ts +408 -0
  145. package/src/fragments/internal-fragment.test.ts +160 -0
  146. package/src/fragments/internal-fragment.ts +85 -0
  147. package/src/migration-engine/generation-engine.test.ts +58 -15
  148. package/src/migration-engine/generation-engine.ts +78 -25
  149. package/src/mod.ts +25 -52
  150. package/src/query/cursor.test.ts +119 -0
  151. package/src/query/cursor.ts +17 -4
  152. package/src/query/execute-unit-of-work.test.ts +1310 -0
  153. package/src/query/execute-unit-of-work.ts +463 -0
  154. package/src/query/query.ts +2 -2
  155. package/src/query/result-transform.test.ts +129 -0
  156. package/src/query/result-transform.ts +4 -1
  157. package/src/query/retry-policy.test.ts +217 -0
  158. package/src/query/retry-policy.ts +141 -0
  159. package/src/query/unit-of-work-coordinator.test.ts +833 -0
  160. package/src/query/unit-of-work-types.test.ts +2 -2
  161. package/src/query/unit-of-work.test.ts +873 -191
  162. package/src/query/unit-of-work.ts +602 -409
  163. package/src/schema/serialize.ts +22 -11
  164. package/src/with-database.ts +140 -0
  165. package/tsdown.config.ts +1 -0
  166. package/dist/bind-services.d.ts +0 -7
  167. package/dist/bind-services.d.ts.map +0 -1
  168. package/dist/bind-services.js +0 -14
  169. package/dist/bind-services.js.map +0 -1
  170. package/dist/fragment.d.ts +0 -173
  171. package/dist/fragment.d.ts.map +0 -1
  172. package/dist/fragment.js +0 -191
  173. package/dist/fragment.js.map +0 -1
  174. package/dist/shared/settings-schema.js +0 -36
  175. package/dist/shared/settings-schema.js.map +0 -1
  176. package/src/bind-services.test.ts +0 -214
  177. package/src/bind-services.ts +0 -37
  178. package/src/db-fragment.test.ts +0 -800
  179. package/src/fragment.ts +0 -727
  180. package/src/query/unit-of-work-multi-schema.test.ts +0 -64
  181. package/src/shared/settings-schema.ts +0 -61
  182. package/src/uow-context-integration.test.ts +0 -102
  183. package/src/uow-context.test.ts +0 -182
@@ -1,64 +0,0 @@
1
- import { describe, test } from "vitest";
2
- import { schema, column, idColumn, referenceColumn } from "../schema/create";
3
- import type { UOWCompiler, UOWExecutor, UOWDecoder } from "./unit-of-work";
4
- import { createUnitOfWork } from "./unit-of-work";
5
-
6
- // Test schemas
7
- const schema1 = schema((s) =>
8
- s.addTable("users", (t) => t.addColumn("id", idColumn()).addColumn("name", column("string"))),
9
- );
10
-
11
- const schema2 = schema((s) =>
12
- s.addTable("posts", (t) =>
13
- t
14
- .addColumn("id", idColumn())
15
- .addColumn("userId", referenceColumn())
16
- .addColumn("title", column("string")),
17
- ),
18
- );
19
-
20
- // Mock compiler, executor, and decoder
21
- const mockCompiler: UOWCompiler<string> = {
22
- compileRetrievalOperation: (op) => `RETRIEVE-${op.type}-${op.table.name}-${op.indexName}`,
23
- compileMutationOperation: (op) => ({
24
- query: `MUTATE-${op.type}`,
25
- expectedAffectedRows: null,
26
- }),
27
- };
28
-
29
- const mockExecutor: UOWExecutor<string, string> = {
30
- executeRetrievalPhase: async (queries) => queries.map((q) => `RESULT-${q}`),
31
- executeMutationPhase: async () => ({ success: true, createdInternalIds: [] }),
32
- };
33
-
34
- const mockDecoder: UOWDecoder<string> = (rawResults) => rawResults.map((r) => ({ decoded: r }));
35
-
36
- describe("Multi-Schema Unit of Work", () => {
37
- test("chained operations on view accumulate result types", async () => {
38
- const uow = createUnitOfWork(schema1, mockCompiler, mockExecutor, mockDecoder);
39
-
40
- // Chain multiple finds on same view
41
- const _view1 = uow
42
- .forSchema(schema1)
43
- .find("users", (b) => b.whereIndex("primary").select(["id"]))
44
- .find("users", (b) => b.whereIndex("primary").select(["name"]));
45
-
46
- const _view2 = uow
47
- .forSchema(schema2)
48
- .find("posts", (b) => b.whereIndex("primary").select(["title"]));
49
-
50
- await uow.executeRetrieve();
51
-
52
- // const [[users1, users2], [posts]] = await Promise.all([view1.retrievalPhase, view2.retrievalPhase]);
53
-
54
- // const [users1, users2] = await view1.retrievalPhase;
55
-
56
- // // expect(users1)
57
-
58
- // // expectTypeOf(users1).toMatchObjectType<{ id: FragnoId; name: string }[]>();
59
- // console.log({ users1, users2 });
60
-
61
- // const [posts] = await view2.retrievalPhase;
62
- // console.log({ posts });
63
- });
64
- });
@@ -1,61 +0,0 @@
1
- import type { AbstractQuery } from "../query/query";
2
- import { schema, idColumn, column, type FragnoId } from "../schema/create";
3
-
4
- export const SETTINGS_TABLE_NAME = "fragno_db_settings" as const;
5
- export const SETTINGS_NAMESPACE = "fragno-db-settings" as const;
6
-
7
- export const settingsSchema = schema((s) => {
8
- return s.addTable(SETTINGS_TABLE_NAME, (t) => {
9
- return t
10
- .addColumn("id", idColumn())
11
- .addColumn("key", column("string"))
12
- .addColumn("value", column("string"))
13
- .createIndex("unique_key", ["key"], { unique: true });
14
- });
15
- });
16
-
17
- export function createSettingsManager(
18
- // oxlint-disable-next-line no-explicit-any
19
- queryEngine: AbstractQuery<typeof settingsSchema, any>,
20
- namespace: string,
21
- ) {
22
- return {
23
- async get(key: string): Promise<{ id: FragnoId; key: string; value: string } | undefined> {
24
- const uow = queryEngine
25
- .createUnitOfWork()
26
- .find(SETTINGS_TABLE_NAME, (b) =>
27
- b.whereIndex("unique_key", (eb) => eb("key", "=", `${namespace}.${key}`)),
28
- );
29
- const [[result]] = await uow.executeRetrieve();
30
- return result; // Safe: result can be undefined if key doesn't exist
31
- },
32
-
33
- async set(key: string, value: string) {
34
- const uow = queryEngine
35
- .createUnitOfWork("createSettingsManager#set")
36
- .find(SETTINGS_TABLE_NAME, (b) =>
37
- b.whereIndex("unique_key", (eb) => eb("key", "=", `${namespace}.${key}`)),
38
- );
39
- const [[existing]] = await uow.executeRetrieve();
40
-
41
- if (existing) {
42
- uow.update(SETTINGS_TABLE_NAME, existing.id, (b) => b.set({ value }).check());
43
- } else {
44
- uow.create(SETTINGS_TABLE_NAME, {
45
- key: `${namespace}.${key}`,
46
- value,
47
- });
48
- }
49
-
50
- const { success } = await uow.executeMutations();
51
-
52
- if (!success) {
53
- throw new Error("Failed to set schema version");
54
- }
55
- },
56
-
57
- async delete(id: FragnoId) {
58
- await queryEngine.delete(SETTINGS_TABLE_NAME, id);
59
- },
60
- };
61
- }
@@ -1,102 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { defineFragmentWithDatabase } from "./fragment";
3
- import { createFragment } from "@fragno-dev/core";
4
- import { schema, column, idColumn } from "./schema/create";
5
-
6
- describe("UOW Context Integration", () => {
7
- it("should bind services to use serviceContext", () => {
8
- // Create a schema
9
- const testSchema = schema((s) => {
10
- return s.addTable("user", (t) => {
11
- return t.addColumn("id", idColumn()).addColumn("name", column("string"));
12
- });
13
- });
14
-
15
- // Define fragment with service that uses this.getUnitOfWork()
16
- const fragmentDef = defineFragmentWithDatabase<{}>("test-fragment")
17
- .withDatabase(testSchema, "test")
18
- .providesService(({ defineService }) =>
19
- defineService({
20
- createUser: function (name: string) {
21
- const uow = this.getUnitOfWork(testSchema);
22
- const userId = uow.create("user", { name });
23
- return { userId: userId.valueOf(), name };
24
- },
25
- }),
26
- );
27
-
28
- // Mock database adapter
29
- const mockSchemaView = {
30
- create: () => ({ valueOf: () => 1 }),
31
- };
32
- const mockUow = {
33
- forSchema: () => mockSchemaView,
34
- };
35
- const mockAdapter = {
36
- createQueryEngine: () => ({
37
- createUnitOfWork: () => mockUow,
38
- }),
39
- };
40
-
41
- // Create fragment instance - services should be bound
42
- const fragment = createFragment(fragmentDef, {}, [], {
43
- mountRoute: "/api/test",
44
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
45
- databaseAdapter: mockAdapter as any,
46
- });
47
-
48
- // Verify services are defined
49
- expect(fragment.services).toBeDefined();
50
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
51
- expect((fragment.services as any).createUser).toBeDefined();
52
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
53
- expect(typeof (fragment.services as any).createUser).toBe("function");
54
- });
55
-
56
- it("should bind providesService services properly", () => {
57
- const schema1 = schema((s) => {
58
- return s.addTable("post", (t) => {
59
- return t.addColumn("id", idColumn()).addColumn("title", column("string"));
60
- });
61
- });
62
-
63
- // Fragment with providesService
64
- const fragmentDef = defineFragmentWithDatabase<{}>("fragment1")
65
- .withDatabase(schema1, "fragment1")
66
- .providesService(({ defineService }) =>
67
- defineService({
68
- createPost: function (title: string) {
69
- const uow = this.getUnitOfWork(schema1);
70
- const postId = uow.create("post", { title });
71
- return { postId: postId.valueOf(), title };
72
- },
73
- }),
74
- );
75
-
76
- // Mock adapter
77
- const mockSchemaView = {
78
- create: () => ({ valueOf: () => 2 }),
79
- };
80
- const mockUow = {
81
- forSchema: () => mockSchemaView,
82
- };
83
- const mockAdapter = {
84
- createQueryEngine: () => ({
85
- createUnitOfWork: () => mockUow,
86
- }),
87
- };
88
-
89
- // Create fragment
90
- const fragment = createFragment(fragmentDef, {}, [], {
91
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
92
- databaseAdapter: mockAdapter as any,
93
- });
94
-
95
- // Verify service is properly bound
96
- expect(fragment.services).toBeDefined();
97
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
98
- expect((fragment.services as any).createPost).toBeDefined();
99
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
100
- expect(typeof (fragment.services as any).createPost).toBe("function");
101
- });
102
- });
@@ -1,182 +0,0 @@
1
- import { describe, it, expect } from "vitest";
2
- import { uowStorage, serviceContext, withUnitOfWork } from "./fragment";
3
- import type { IUnitOfWorkBase } from "./query/unit-of-work";
4
- import { schema, idColumn } from "./schema/create";
5
-
6
- // Create a simple test schema for tests
7
- const testSchema = schema((s) => {
8
- return s.addTable("test", (t) => {
9
- return t.addColumn("id", idColumn());
10
- });
11
- });
12
-
13
- describe("UOW Context", () => {
14
- describe("serviceContext.getUnitOfWork", () => {
15
- it("should throw error when called outside context", () => {
16
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
- expect(() => serviceContext.getUnitOfWork(testSchema as any)).toThrow(
18
- "No UnitOfWork in context. Service must be called within a route handler.",
19
- );
20
- });
21
-
22
- it("should return scoped UOW when called inside withUnitOfWork", async () => {
23
- const mockSchemaView = { test: "schema-view" };
24
- const mockUow = {
25
- test: "uow",
26
- forSchema: () => mockSchemaView,
27
- } as unknown as IUnitOfWorkBase;
28
-
29
- await withUnitOfWork(mockUow, () => {
30
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
- const uow = serviceContext.getUnitOfWork(testSchema as any);
32
- expect(uow).toBe(mockSchemaView);
33
- });
34
- });
35
-
36
- it("should allow nested withUnitOfWork calls", async () => {
37
- const view1 = { id: 1 };
38
- const view2 = { id: 2 };
39
- const uow1 = {
40
- id: 1,
41
- forSchema: () => view1,
42
- } as unknown as IUnitOfWorkBase;
43
- const uow2 = {
44
- id: 2,
45
- forSchema: () => view2,
46
- } as unknown as IUnitOfWorkBase;
47
-
48
- await withUnitOfWork(uow1, async () => {
49
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
- expect(serviceContext.getUnitOfWork(testSchema as any)).toBe(view1);
51
-
52
- await withUnitOfWork(uow2, () => {
53
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
54
- expect(serviceContext.getUnitOfWork(testSchema as any)).toBe(view2);
55
- });
56
-
57
- // Should be back to uow1 after nested context
58
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
59
- expect(serviceContext.getUnitOfWork(testSchema as any)).toBe(view1);
60
- });
61
- });
62
-
63
- it("should isolate UOW between parallel async calls", async () => {
64
- const view1 = { id: 1 };
65
- const view2 = { id: 2 };
66
- const uow1 = {
67
- id: 1,
68
- forSchema: () => view1,
69
- } as unknown as IUnitOfWorkBase;
70
- const uow2 = {
71
- id: 2,
72
- forSchema: () => view2,
73
- } as unknown as IUnitOfWorkBase;
74
-
75
- const promise1 = withUnitOfWork(uow1, async () => {
76
- await new Promise((resolve) => setTimeout(resolve, 10));
77
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
- return serviceContext.getUnitOfWork(testSchema as any);
79
- });
80
-
81
- const promise2 = withUnitOfWork(uow2, async () => {
82
- await new Promise((resolve) => setTimeout(resolve, 5));
83
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
84
- return serviceContext.getUnitOfWork(testSchema as any);
85
- });
86
-
87
- const [result1, result2] = await Promise.all([promise1, promise2]);
88
-
89
- expect(result1).toBe(view1);
90
- expect(result2).toBe(view2);
91
- });
92
- });
93
-
94
- describe("uowStorage", () => {
95
- it("should return undefined when no UOW is stored", () => {
96
- const stored = uowStorage.getStore();
97
- expect(stored).toBeUndefined();
98
- });
99
-
100
- it("should store and retrieve UOW", async () => {
101
- const mockUow = { test: "uow" } as unknown as IUnitOfWorkBase;
102
-
103
- await withUnitOfWork(mockUow, () => {
104
- const stored = uowStorage.getStore();
105
- expect(stored).toBe(mockUow);
106
- });
107
-
108
- // Should be undefined after exiting context
109
- const stored = uowStorage.getStore();
110
- expect(stored).toBeUndefined();
111
- });
112
- });
113
-
114
- describe("withUnitOfWork", () => {
115
- it("should execute callback with UOW in context", async () => {
116
- const mockSchemaView = { test: "schema-view" };
117
- const mockUow = {
118
- test: "uow",
119
- forSchema: () => mockSchemaView,
120
- } as unknown as IUnitOfWorkBase;
121
- let called = false;
122
-
123
- await withUnitOfWork(mockUow, () => {
124
- called = true;
125
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
126
- expect(serviceContext.getUnitOfWork(testSchema as any)).toBe(mockSchemaView);
127
- });
128
-
129
- expect(called).toBe(true);
130
- });
131
-
132
- it("should return callback result", async () => {
133
- const mockUow = { test: "uow" } as unknown as IUnitOfWorkBase;
134
- const result = await withUnitOfWork(mockUow, () => "test-result");
135
-
136
- expect(result).toBe("test-result");
137
- });
138
-
139
- it("should handle async callbacks", async () => {
140
- const mockUow = { test: "uow" } as unknown as IUnitOfWorkBase;
141
-
142
- const result = await withUnitOfWork(mockUow, async () => {
143
- await new Promise((resolve) => setTimeout(resolve, 10));
144
- return "async-result";
145
- });
146
-
147
- expect(result).toBe("async-result");
148
- });
149
-
150
- it("should propagate errors from callback", async () => {
151
- const mockUow = { test: "uow" } as unknown as IUnitOfWorkBase;
152
-
153
- let error: Error | null = null;
154
- try {
155
- await withUnitOfWork(mockUow, () => {
156
- throw new Error("test error");
157
- });
158
- } catch (e) {
159
- error = e as Error;
160
- }
161
-
162
- expect(error).toBeInstanceOf(Error);
163
- expect(error?.message).toBe("test error");
164
- });
165
-
166
- it("should clean up context after error", async () => {
167
- const mockUow = { test: "uow" } as unknown as IUnitOfWorkBase;
168
-
169
- try {
170
- await withUnitOfWork(mockUow, () => {
171
- throw new Error("test error");
172
- });
173
- } catch {
174
- // Expected error
175
- }
176
-
177
- // UOW should no longer be in context
178
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
179
- expect(() => serviceContext.getUnitOfWork(testSchema as any)).toThrow();
180
- });
181
- });
182
- });