@fragno-dev/db 0.1.13 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/.turbo/turbo-build.log +48 -41
  2. package/CHANGELOG.md +6 -0
  3. package/dist/adapters/adapters.d.ts +13 -1
  4. package/dist/adapters/adapters.d.ts.map +1 -1
  5. package/dist/adapters/adapters.js.map +1 -1
  6. package/dist/adapters/drizzle/drizzle-adapter.d.ts +2 -0
  7. package/dist/adapters/drizzle/drizzle-adapter.d.ts.map +1 -1
  8. package/dist/adapters/drizzle/drizzle-adapter.js +6 -1
  9. package/dist/adapters/drizzle/drizzle-adapter.js.map +1 -1
  10. package/dist/adapters/drizzle/drizzle-query.js +6 -4
  11. package/dist/adapters/drizzle/drizzle-query.js.map +1 -1
  12. package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts +0 -1
  13. package/dist/adapters/drizzle/drizzle-uow-compiler.d.ts.map +1 -1
  14. package/dist/adapters/drizzle/drizzle-uow-compiler.js +49 -36
  15. package/dist/adapters/drizzle/drizzle-uow-compiler.js.map +1 -1
  16. package/dist/adapters/drizzle/drizzle-uow-decoder.js +1 -1
  17. package/dist/adapters/drizzle/drizzle-uow-decoder.js.map +1 -1
  18. package/dist/adapters/drizzle/shared.d.ts +14 -1
  19. package/dist/adapters/drizzle/shared.d.ts.map +1 -0
  20. package/dist/adapters/kysely/kysely-adapter.d.ts +2 -0
  21. package/dist/adapters/kysely/kysely-adapter.d.ts.map +1 -1
  22. package/dist/adapters/kysely/kysely-adapter.js +7 -2
  23. package/dist/adapters/kysely/kysely-adapter.js.map +1 -1
  24. package/dist/adapters/kysely/kysely-query.js +5 -3
  25. package/dist/adapters/kysely/kysely-query.js.map +1 -1
  26. package/dist/adapters/kysely/kysely-shared.d.ts +11 -0
  27. package/dist/adapters/kysely/kysely-shared.d.ts.map +1 -0
  28. package/dist/adapters/kysely/kysely-uow-compiler.js +38 -9
  29. package/dist/adapters/kysely/kysely-uow-compiler.js.map +1 -1
  30. package/dist/bind-services.d.ts +7 -0
  31. package/dist/bind-services.d.ts.map +1 -0
  32. package/dist/bind-services.js +14 -0
  33. package/dist/bind-services.js.map +1 -0
  34. package/dist/fragment.d.ts +131 -12
  35. package/dist/fragment.d.ts.map +1 -1
  36. package/dist/fragment.js +107 -8
  37. package/dist/fragment.js.map +1 -1
  38. package/dist/mod.d.ts +4 -2
  39. package/dist/mod.d.ts.map +1 -1
  40. package/dist/mod.js +3 -2
  41. package/dist/mod.js.map +1 -1
  42. package/dist/query/query.d.ts +2 -2
  43. package/dist/query/query.d.ts.map +1 -1
  44. package/dist/query/unit-of-work.d.ts +100 -15
  45. package/dist/query/unit-of-work.d.ts.map +1 -1
  46. package/dist/query/unit-of-work.js +214 -7
  47. package/dist/query/unit-of-work.js.map +1 -1
  48. package/package.json +3 -3
  49. package/src/adapters/adapters.ts +14 -0
  50. package/src/adapters/drizzle/drizzle-adapter-pglite.test.ts +6 -1
  51. package/src/adapters/drizzle/drizzle-adapter-sqlite.test.ts +133 -5
  52. package/src/adapters/drizzle/drizzle-adapter.ts +16 -1
  53. package/src/adapters/drizzle/drizzle-query.ts +26 -15
  54. package/src/adapters/drizzle/drizzle-uow-compiler.test.ts +57 -57
  55. package/src/adapters/drizzle/drizzle-uow-compiler.ts +79 -39
  56. package/src/adapters/drizzle/drizzle-uow-decoder.ts +2 -5
  57. package/src/adapters/kysely/kysely-adapter-pglite.test.ts +2 -2
  58. package/src/adapters/kysely/kysely-adapter.ts +16 -1
  59. package/src/adapters/kysely/kysely-query.ts +26 -15
  60. package/src/adapters/kysely/kysely-uow-compiler.test.ts +43 -43
  61. package/src/adapters/kysely/kysely-uow-compiler.ts +50 -14
  62. package/src/adapters/kysely/kysely-uow-joins.test.ts +30 -30
  63. package/src/bind-services.test.ts +214 -0
  64. package/src/bind-services.ts +37 -0
  65. package/src/db-fragment.test.ts +800 -0
  66. package/src/fragment.ts +557 -28
  67. package/src/mod.ts +19 -0
  68. package/src/query/query.ts +2 -2
  69. package/src/query/unit-of-work-multi-schema.test.ts +64 -0
  70. package/src/query/unit-of-work-types.test.ts +13 -0
  71. package/src/query/unit-of-work.test.ts +5 -9
  72. package/src/query/unit-of-work.ts +511 -62
  73. package/src/uow-context-integration.test.ts +102 -0
  74. package/src/uow-context.test.ts +182 -0
  75. package/src/fragment.test.ts +0 -341
@@ -103,12 +103,12 @@ describe("kysely-uow-joins", () => {
103
103
 
104
104
  // Helper to create UnitOfWork for testing
105
105
  function createTestUOW(name?: string) {
106
- const mockCompiler = createKyselyUOWCompiler(userSchema, pool, "postgresql");
106
+ const mockCompiler = createKyselyUOWCompiler(pool, "postgresql");
107
107
  const mockExecutor = {
108
108
  executeRetrievalPhase: async () => [],
109
109
  executeMutationPhase: async () => ({ success: true, createdInternalIds: [] }),
110
110
  };
111
- const mockDecoder: UOWDecoder<typeof userSchema> = (rawResults, operations) => {
111
+ const mockDecoder: UOWDecoder = (rawResults, operations) => {
112
112
  if (rawResults.length !== operations.length) {
113
113
  throw new Error("rawResults and ops must have the same length");
114
114
  }
@@ -127,7 +127,7 @@ describe("kysely-uow-joins", () => {
127
127
  .join((jb) => jb.author()),
128
128
  );
129
129
 
130
- const compiler = createKyselyUOWCompiler(userSchema, pool, "postgresql");
130
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
131
131
  const compiled = uow.compile(compiler);
132
132
 
133
133
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -147,7 +147,7 @@ describe("kysely-uow-joins", () => {
147
147
  .join((jb) => jb.author((ab) => ab.select(["name", "email"]))),
148
148
  );
149
149
 
150
- const compiler = createKyselyUOWCompiler(userSchema, pool, "postgresql");
150
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
151
151
  const compiled = uow.compile(compiler);
152
152
 
153
153
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -171,7 +171,7 @@ describe("kysely-uow-joins", () => {
171
171
  ),
172
172
  );
173
173
 
174
- const compiler = createKyselyUOWCompiler(userSchema, pool, "postgresql");
174
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
175
175
  const compiled = uow.compile(compiler);
176
176
 
177
177
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -191,7 +191,7 @@ describe("kysely-uow-joins", () => {
191
191
  .join((jb) => jb.inviter((ib) => ib.select(["name"]))),
192
192
  );
193
193
 
194
- const compiler = createKyselyUOWCompiler(userSchema, pool, "postgresql");
194
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
195
195
  const compiled = uow.compile(compiler);
196
196
 
197
197
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -211,7 +211,7 @@ describe("kysely-uow-joins", () => {
211
211
  .join((jb) => jb.post((pb) => pb.select(["title"])).author((ab) => ab.select(["name"]))),
212
212
  );
213
213
 
214
- const compiler = createKyselyUOWCompiler(userSchema, pool, "postgresql");
214
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
215
215
  const compiled = uow.compile(compiler);
216
216
 
217
217
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -231,7 +231,7 @@ describe("kysely-uow-joins", () => {
231
231
  .join((jb) => jb.post((pb) => pb.select(["title"])).tag((tb) => tb.select(["name"]))),
232
232
  );
233
233
 
234
- const compiler = createKyselyUOWCompiler(userSchema, pool, "postgresql");
234
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
235
235
  const compiled = uow.compile(compiler);
236
236
 
237
237
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -261,7 +261,7 @@ describe("kysely-uow-joins", () => {
261
261
  ),
262
262
  );
263
263
 
264
- const compiler = createKyselyUOWCompiler(userSchema, pool, "postgresql");
264
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
265
265
  const compiled = uow.compile(compiler);
266
266
 
267
267
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -281,7 +281,7 @@ describe("kysely-uow-joins", () => {
281
281
  .join((jb) => jb.author((ab) => ab.select(["name"]).orderByIndex("idx_name", "asc"))),
282
282
  );
283
283
 
284
- const compiler = createKyselyUOWCompiler(userSchema, pool, "postgresql");
284
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
285
285
  const compiled = uow.compile(compiler);
286
286
 
287
287
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -302,7 +302,7 @@ describe("kysely-uow-joins", () => {
302
302
  .join((jb) => jb.author((ab) => ab.select(["name"]).pageSize(1))),
303
303
  );
304
304
 
305
- const compiler = createKyselyUOWCompiler(userSchema, pool, "postgresql");
305
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
306
306
  const compiled = uow.compile(compiler);
307
307
 
308
308
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -330,7 +330,7 @@ describe("kysely-uow-joins", () => {
330
330
  ),
331
331
  );
332
332
 
333
- const compiler = createKyselyUOWCompiler(userSchema, pool, "postgresql");
333
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
334
334
  const compiled = uow.compile(compiler);
335
335
 
336
336
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -353,7 +353,7 @@ describe("kysely-uow-joins", () => {
353
353
  .join((jb) => jb.author((ab) => ab.select(["id", "name"]))),
354
354
  );
355
355
 
356
- const compiler = createKyselyUOWCompiler(userSchema, pool, "postgresql");
356
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
357
357
  const compiled = uow.compile(compiler);
358
358
 
359
359
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -376,7 +376,7 @@ describe("kysely-uow-joins", () => {
376
376
  .join((jb) => jb.author((ab) => ab.select(["id", "name"]))),
377
377
  );
378
378
 
379
- const compiler = createKyselyUOWCompiler(userSchema, pool, "postgresql");
379
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
380
380
  const compiled = uow.compile(compiler);
381
381
 
382
382
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -401,7 +401,7 @@ describe("kysely-uow-joins", () => {
401
401
  .join((jb) => jb.author((ab) => ab.select(["id"]))),
402
402
  );
403
403
 
404
- const compiler = createKyselyUOWCompiler(userSchema, pool, "postgresql");
404
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
405
405
  const compiled = uow.compile(compiler);
406
406
 
407
407
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -423,7 +423,7 @@ describe("kysely-uow-joins", () => {
423
423
  .join((jb) => jb.author((ab) => ab.select(["id", "name"]))),
424
424
  );
425
425
 
426
- const compiler = createKyselyUOWCompiler(userSchema, pool, "postgresql");
426
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
427
427
  const compiled = uow.compile(compiler);
428
428
 
429
429
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -446,7 +446,7 @@ describe("kysely-uow-joins", () => {
446
446
  .join((jb) => jb.author((ab) => ab.select(["name"]))),
447
447
  );
448
448
 
449
- const compiler = createKyselyUOWCompiler(userSchema, pool, "postgresql");
449
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
450
450
  const compiled = uow.compile(compiler);
451
451
 
452
452
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -466,7 +466,7 @@ describe("kysely-uow-joins", () => {
466
466
  const uow = createTestUOW();
467
467
  uow.find("posts", (b) => b.whereIndex("primary").join((jb) => jb.author()));
468
468
 
469
- const compiler = createKyselyUOWCompiler(userSchema, pool, "postgresql");
469
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
470
470
  const compiled = uow.compile(compiler);
471
471
 
472
472
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -494,7 +494,7 @@ describe("kysely-uow-joins", () => {
494
494
  ),
495
495
  );
496
496
 
497
- const compiler = createKyselyUOWCompiler(userSchema, pool, "postgresql");
497
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
498
498
  const compiled = uow.compile(compiler);
499
499
 
500
500
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -517,7 +517,7 @@ describe("kysely-uow-joins", () => {
517
517
  ),
518
518
  );
519
519
 
520
- const compiler = createKyselyUOWCompiler(userSchema, pool, "postgresql");
520
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
521
521
  const compiled = uow.compile(compiler);
522
522
 
523
523
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -575,12 +575,12 @@ describe("kysely-uow-joins", () => {
575
575
  });
576
576
 
577
577
  function createCustomIdTestUOW() {
578
- const mockCompiler = createKyselyUOWCompiler(customIdSchema, pool, "postgresql");
578
+ const mockCompiler = createKyselyUOWCompiler(pool, "postgresql");
579
579
  const mockExecutor = {
580
580
  executeRetrievalPhase: async () => [],
581
581
  executeMutationPhase: async () => ({ success: true, createdInternalIds: [] }),
582
582
  };
583
- const mockDecoder: UOWDecoder<typeof customIdSchema> = (rawResults, operations) => {
583
+ const mockDecoder: UOWDecoder = (rawResults, operations) => {
584
584
  if (rawResults.length !== operations.length) {
585
585
  throw new Error("rawResults and ops must have the same length");
586
586
  }
@@ -602,7 +602,7 @@ describe("kysely-uow-joins", () => {
602
602
  ),
603
603
  );
604
604
 
605
- const compiler = createKyselyUOWCompiler(customIdSchema, pool, "postgresql");
605
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
606
606
  const compiled = uow.compile(compiler);
607
607
 
608
608
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -636,7 +636,7 @@ describe("kysely-uow-joins", () => {
636
636
  ),
637
637
  );
638
638
 
639
- const compiler = createKyselyUOWCompiler(customIdSchema, pool, "postgresql");
639
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
640
640
  const compiled = uow.compile(compiler);
641
641
 
642
642
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -655,7 +655,7 @@ describe("kysely-uow-joins", () => {
655
655
  const uow = createCustomIdTestUOW();
656
656
  uow.find("product_categories", (b) => b.whereIndex("primary").join((jb) => jb.product()));
657
657
 
658
- const compiler = createKyselyUOWCompiler(customIdSchema, pool, "postgresql");
658
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
659
659
  const compiled = uow.compile(compiler);
660
660
 
661
661
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -685,7 +685,7 @@ describe("kysely-uow-joins", () => {
685
685
  ),
686
686
  );
687
687
 
688
- const compiler = createKyselyUOWCompiler(customIdSchema, pool, "postgresql");
688
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
689
689
  const compiled = uow.compile(compiler);
690
690
 
691
691
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -717,7 +717,7 @@ describe("kysely-uow-joins", () => {
717
717
  .join((jb) => jb.author((ab) => ab.select(["id"]))),
718
718
  );
719
719
 
720
- const compiler = createKyselyUOWCompiler(userSchema, pool, "postgresql");
720
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
721
721
  const compiled = uow.compile(compiler);
722
722
 
723
723
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -741,7 +741,7 @@ describe("kysely-uow-joins", () => {
741
741
  .join((jb) => jb.author((ab) => ab.select(["id", "name"]))),
742
742
  );
743
743
 
744
- const compiler = createKyselyUOWCompiler(userSchema, pool, "postgresql");
744
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
745
745
  const compiled = uow.compile(compiler);
746
746
 
747
747
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -765,7 +765,7 @@ describe("kysely-uow-joins", () => {
765
765
  .join((jb) => jb.post((pb) => pb.select(["id"])).author((ab) => ab.select(["id"]))),
766
766
  );
767
767
 
768
- const compiler = createKyselyUOWCompiler(userSchema, pool, "postgresql");
768
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
769
769
  const compiled = uow.compile(compiler);
770
770
 
771
771
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -793,7 +793,7 @@ describe("kysely-uow-joins", () => {
793
793
  .join((jb) => jb.inviter((ib) => ib.select(["id", "name"]))),
794
794
  );
795
795
 
796
- const compiler = createKyselyUOWCompiler(userSchema, pool, "postgresql");
796
+ const compiler = createKyselyUOWCompiler(pool, "postgresql");
797
797
  const compiled = uow.compile(compiler);
798
798
 
799
799
  expect(compiled.retrievalBatch).toHaveLength(1);
@@ -0,0 +1,214 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { bindServicesToContext } from "./bind-services";
3
+ import { withUnitOfWork, type DatabaseRequestThisContext } from "./fragment";
4
+ import type { IUnitOfWorkBase } from "./query/unit-of-work";
5
+ import { schema, idColumn } from "./schema/create";
6
+
7
+ // Create a simple test schema for tests
8
+ const testSchema = schema((s) => {
9
+ return s.addTable("test", (t) => {
10
+ return t.addColumn("id", idColumn());
11
+ });
12
+ });
13
+
14
+ describe("bindServicesToContext", () => {
15
+ it("should bind simple function to context", async () => {
16
+ const mockSchemaView = { test: "schema-view" };
17
+ const mockUow = {
18
+ test: "uow",
19
+ forSchema: () => mockSchemaView,
20
+ } as unknown as IUnitOfWorkBase;
21
+
22
+ const services = {
23
+ testMethod: function (this: DatabaseRequestThisContext) {
24
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
+ return this.getUnitOfWork(testSchema as any);
26
+ },
27
+ };
28
+
29
+ const bound = bindServicesToContext(services);
30
+
31
+ const result = await withUnitOfWork(mockUow, () => bound.testMethod());
32
+
33
+ expect(result).toBe(mockSchemaView);
34
+ });
35
+
36
+ it("should bind multiple functions", async () => {
37
+ const mockSchemaView = { test: "schema-view" };
38
+ const mockUow = {
39
+ test: "uow",
40
+ forSchema: () => mockSchemaView,
41
+ } as unknown as IUnitOfWorkBase;
42
+
43
+ const services = {
44
+ method1: function (this: DatabaseRequestThisContext) {
45
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
+ return this.getUnitOfWork(testSchema as any);
47
+ },
48
+ method2: function (this: DatabaseRequestThisContext) {
49
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
+ return this.getUnitOfWork(testSchema as any);
51
+ },
52
+ };
53
+
54
+ const bound = bindServicesToContext(services);
55
+
56
+ await withUnitOfWork(mockUow, () => {
57
+ expect(bound.method1()).toBe(mockSchemaView);
58
+ expect(bound.method2()).toBe(mockSchemaView);
59
+ });
60
+ });
61
+
62
+ it("should bind nested service objects", async () => {
63
+ const mockSchemaView = { test: "schema-view" };
64
+ const mockUow = {
65
+ test: "uow",
66
+ forSchema: () => mockSchemaView,
67
+ } as unknown as IUnitOfWorkBase;
68
+
69
+ const services = {
70
+ nested: {
71
+ method: function (this: DatabaseRequestThisContext) {
72
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
+ return this.getUnitOfWork(testSchema as any);
74
+ },
75
+ },
76
+ };
77
+
78
+ const bound = bindServicesToContext(services);
79
+
80
+ const result = await withUnitOfWork(mockUow, () => bound.nested.method());
81
+
82
+ expect(result).toBe(mockSchemaView);
83
+ });
84
+
85
+ it("should preserve non-function properties", () => {
86
+ const services = {
87
+ method: function () {
88
+ return "test";
89
+ },
90
+ constant: "value",
91
+ number: 42,
92
+ };
93
+
94
+ const bound = bindServicesToContext(services);
95
+
96
+ expect(bound.constant).toBe("value");
97
+ expect(bound.number).toBe(42);
98
+ expect(bound.method()).toBe("test");
99
+ });
100
+
101
+ it("should handle functions with parameters", async () => {
102
+ const mockSchemaView = { test: "schema-view" };
103
+ const mockUow = {
104
+ test: "uow",
105
+ forSchema: () => mockSchemaView,
106
+ } as unknown as IUnitOfWorkBase;
107
+
108
+ const services = {
109
+ testMethod: function (this: DatabaseRequestThisContext, param1: string, param2: number) {
110
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
111
+ const uow = this.getUnitOfWork(testSchema as any);
112
+ return { uow, param1, param2 };
113
+ },
114
+ };
115
+
116
+ const bound = bindServicesToContext(services);
117
+
118
+ const result = await withUnitOfWork(mockUow, () => bound.testMethod("hello", 123));
119
+
120
+ expect(result.uow).toBe(mockSchemaView);
121
+ expect(result.param1).toBe("hello");
122
+ expect(result.param2).toBe(123);
123
+ });
124
+
125
+ it("should handle async functions", async () => {
126
+ const mockSchemaView = { test: "schema-view" };
127
+ const mockUow = {
128
+ test: "uow",
129
+ forSchema: () => mockSchemaView,
130
+ } as unknown as IUnitOfWorkBase;
131
+
132
+ const services = {
133
+ asyncMethod: async function (this: DatabaseRequestThisContext) {
134
+ await new Promise((resolve) => setTimeout(resolve, 10));
135
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
136
+ return this.getUnitOfWork(testSchema as any);
137
+ },
138
+ };
139
+
140
+ const bound = bindServicesToContext(services);
141
+
142
+ const result = await withUnitOfWork(mockUow, async () => await bound.asyncMethod());
143
+
144
+ expect(result).toBe(mockSchemaView);
145
+ });
146
+
147
+ it("should not bind arrays", () => {
148
+ const services = {
149
+ array: [1, 2, 3],
150
+ method: function () {
151
+ return "test";
152
+ },
153
+ };
154
+
155
+ const bound = bindServicesToContext(services);
156
+
157
+ expect(bound.array).toEqual([1, 2, 3]);
158
+ expect(Array.isArray(bound.array)).toBe(true);
159
+ });
160
+
161
+ it("should handle deeply nested objects", async () => {
162
+ const mockSchemaView = { test: "schema-view" };
163
+ const mockUow = {
164
+ test: "uow",
165
+ forSchema: () => mockSchemaView,
166
+ } as unknown as IUnitOfWorkBase;
167
+
168
+ const services = {
169
+ level1: {
170
+ level2: {
171
+ level3: {
172
+ method: function (this: DatabaseRequestThisContext) {
173
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
174
+ return this.getUnitOfWork(testSchema as any);
175
+ },
176
+ },
177
+ },
178
+ },
179
+ };
180
+
181
+ const bound = bindServicesToContext(services);
182
+
183
+ const result = await withUnitOfWork(mockUow, () => bound.level1.level2.level3.method());
184
+
185
+ expect(result).toBe(mockSchemaView);
186
+ });
187
+
188
+ it("should allow bound services to access UOW independently", async () => {
189
+ const mockSchemaView = { test: "schema-view" };
190
+ const mockUow = {
191
+ test: "uow",
192
+ forSchema: () => mockSchemaView,
193
+ } as unknown as IUnitOfWorkBase;
194
+
195
+ const services = {
196
+ getUow: function (this: DatabaseRequestThisContext) {
197
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
198
+ return this.getUnitOfWork(testSchema as any);
199
+ },
200
+ callOther: function (this: DatabaseRequestThisContext) {
201
+ // Both methods can access UOW via their own `this` context
202
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
203
+ return this.getUnitOfWork(testSchema as any);
204
+ },
205
+ };
206
+
207
+ const bound = bindServicesToContext(services);
208
+
209
+ await withUnitOfWork(mockUow, () => {
210
+ expect(bound.getUow()).toBe(mockSchemaView);
211
+ expect(bound.callOther()).toBe(mockSchemaView);
212
+ });
213
+ });
214
+ });
@@ -0,0 +1,37 @@
1
+ import { serviceContext } from "./fragment";
2
+
3
+ // Type helper to remove 'this' parameter from functions
4
+ type OmitThisParameter<T> = T extends (this: infer _This, ...args: infer A) => infer R
5
+ ? (...args: A) => R
6
+ : T;
7
+
8
+ // Recursively remove 'this' parameter from all functions in an object
9
+ export type BoundServices<T> = {
10
+ [K in keyof T]: T[K] extends (...args: never[]) => unknown
11
+ ? OmitThisParameter<T[K]>
12
+ : T[K] extends Record<string, unknown>
13
+ ? BoundServices<T[K]>
14
+ : T[K];
15
+ };
16
+
17
+ export function bindServicesToContext<T extends Record<string, unknown>>(
18
+ services: T,
19
+ ): BoundServices<T> {
20
+ const bound = {} as BoundServices<T>;
21
+
22
+ for (const [key, value] of Object.entries(services)) {
23
+ if (typeof value === "function") {
24
+ // Bind function to serviceContext
25
+ bound[key as keyof T] = value.bind(serviceContext) as BoundServices<T>[keyof T];
26
+ } else if (value && typeof value === "object" && !Array.isArray(value)) {
27
+ // Recursively bind nested service objects
28
+ bound[key as keyof T] = bindServicesToContext(
29
+ value as Record<string, unknown>,
30
+ ) as BoundServices<T>[keyof T];
31
+ } else {
32
+ bound[key as keyof T] = value as BoundServices<T>[keyof T];
33
+ }
34
+ }
35
+
36
+ return bound;
37
+ }