@fragno-dev/db 0.2.0 → 0.2.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 (62) hide show
  1. package/.turbo/turbo-build.log +34 -30
  2. package/CHANGELOG.md +49 -0
  3. package/dist/adapters/generic-sql/query/where-builder.js +1 -1
  4. package/dist/db-fragment-definition-builder.d.ts +31 -39
  5. package/dist/db-fragment-definition-builder.d.ts.map +1 -1
  6. package/dist/db-fragment-definition-builder.js +20 -16
  7. package/dist/db-fragment-definition-builder.js.map +1 -1
  8. package/dist/fragments/internal-fragment.d.ts +94 -8
  9. package/dist/fragments/internal-fragment.d.ts.map +1 -1
  10. package/dist/fragments/internal-fragment.js +56 -55
  11. package/dist/fragments/internal-fragment.js.map +1 -1
  12. package/dist/hooks/hooks.d.ts +5 -3
  13. package/dist/hooks/hooks.d.ts.map +1 -1
  14. package/dist/hooks/hooks.js +38 -37
  15. package/dist/hooks/hooks.js.map +1 -1
  16. package/dist/mod.d.ts +3 -3
  17. package/dist/mod.d.ts.map +1 -1
  18. package/dist/mod.js +4 -4
  19. package/dist/mod.js.map +1 -1
  20. package/dist/query/unit-of-work/execute-unit-of-work.d.ts +367 -80
  21. package/dist/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -1
  22. package/dist/query/unit-of-work/execute-unit-of-work.js +448 -148
  23. package/dist/query/unit-of-work/execute-unit-of-work.js.map +1 -1
  24. package/dist/query/unit-of-work/unit-of-work.d.ts +35 -11
  25. package/dist/query/unit-of-work/unit-of-work.d.ts.map +1 -1
  26. package/dist/query/unit-of-work/unit-of-work.js +49 -19
  27. package/dist/query/unit-of-work/unit-of-work.js.map +1 -1
  28. package/dist/query/value-decoding.js +1 -1
  29. package/dist/schema/create.d.ts +2 -3
  30. package/dist/schema/create.d.ts.map +1 -1
  31. package/dist/schema/create.js +2 -5
  32. package/dist/schema/create.js.map +1 -1
  33. package/dist/schema/generate-id.d.ts +20 -0
  34. package/dist/schema/generate-id.d.ts.map +1 -0
  35. package/dist/schema/generate-id.js +28 -0
  36. package/dist/schema/generate-id.js.map +1 -0
  37. package/dist/sql-driver/dialects/durable-object-dialect.d.ts.map +1 -1
  38. package/package.json +3 -3
  39. package/src/adapters/drizzle/drizzle-adapter-pglite.test.ts +1 -0
  40. package/src/adapters/drizzle/drizzle-adapter-sqlite3.test.ts +41 -25
  41. package/src/adapters/generic-sql/test/generic-drizzle-adapter-sqlite3.test.ts +39 -25
  42. package/src/db-fragment-definition-builder.test.ts +58 -42
  43. package/src/db-fragment-definition-builder.ts +78 -88
  44. package/src/db-fragment-instantiator.test.ts +64 -88
  45. package/src/db-fragment-integration.test.ts +292 -142
  46. package/src/fragments/internal-fragment.test.ts +272 -266
  47. package/src/fragments/internal-fragment.ts +155 -122
  48. package/src/hooks/hooks.test.ts +268 -264
  49. package/src/hooks/hooks.ts +74 -63
  50. package/src/mod.ts +14 -4
  51. package/src/query/unit-of-work/execute-unit-of-work.test.ts +1582 -998
  52. package/src/query/unit-of-work/execute-unit-of-work.ts +1746 -343
  53. package/src/query/unit-of-work/tx-builder.test.ts +1041 -0
  54. package/src/query/unit-of-work/unit-of-work-coordinator.test.ts +269 -21
  55. package/src/query/unit-of-work/unit-of-work.test.ts +64 -0
  56. package/src/query/unit-of-work/unit-of-work.ts +65 -30
  57. package/src/schema/create.ts +2 -5
  58. package/src/schema/generate-id.test.ts +57 -0
  59. package/src/schema/generate-id.ts +38 -0
  60. package/src/shared/config.ts +0 -10
  61. package/src/shared/connection-pool.ts +0 -24
  62. package/src/shared/prisma.ts +0 -45
@@ -2,17 +2,18 @@ import type { RetryPolicy } from "../query/unit-of-work/retry-policy";
2
2
  import { ExponentialBackoffRetryPolicy } from "../query/unit-of-work/retry-policy";
3
3
  import type { IUnitOfWork } from "../query/unit-of-work/unit-of-work";
4
4
  import type { InternalFragmentInstance } from "../fragments/internal-fragment";
5
+ import type { TxResult } from "../query/unit-of-work/execute-unit-of-work";
5
6
 
6
7
  /**
7
8
  * Context available in hook functions via `this`.
8
- * Contains the nonce for idempotency and database access.
9
+ * Contains the idempotency key for idempotency and database access.
9
10
  */
10
11
  export interface HookContext {
11
12
  /**
12
- * Unique nonce for this transaction.
13
+ * Unique idempotency key for this transaction.
13
14
  * Use this for idempotency checks in your hook implementation.
14
15
  */
15
- nonce: string;
16
+ idempotencyKey: string;
16
17
  }
17
18
 
18
19
  /**
@@ -95,7 +96,7 @@ export function prepareHookMutations(uow: IUnitOfWork, config: HookProcessorConf
95
96
  lastAttemptAt: null,
96
97
  nextRetryAt: null,
97
98
  error: null,
98
- nonce: uow.nonce,
99
+ nonce: uow.idempotencyKey,
99
100
  });
100
101
  }
101
102
  }
@@ -108,72 +109,82 @@ export async function processHooks(config: HookProcessorConfig): Promise<void> {
108
109
  const { hooks, namespace, internalFragment, defaultRetryPolicy } = config;
109
110
  const retryPolicy = defaultRetryPolicy ?? new ExponentialBackoffRetryPolicy({ maxRetries: 5 });
110
111
 
111
- await internalFragment.inContext(async function () {
112
- return await this.uow(async ({ executeRetrieve, executeMutate }) => {
113
- const pendingEventsPromise =
114
- internalFragment.services.hookService.getPendingHookEvents(namespace);
115
- await executeRetrieve();
112
+ // Get pending events
113
+ const pendingEvents = await internalFragment.inContext(async function () {
114
+ return await this.handlerTx()
115
+ .withServiceCalls(
116
+ () => [internalFragment.services.hookService.getPendingHookEvents(namespace)] as const,
117
+ )
118
+ .transform(({ serviceResult: [events] }) => events)
119
+ .execute();
120
+ });
116
121
 
117
- const pendingEvents = await pendingEventsPromise;
122
+ if (pendingEvents.length === 0) {
123
+ return;
124
+ }
118
125
 
119
- if (pendingEvents.length === 0) {
120
- return;
126
+ // Process events (async work outside transaction)
127
+ const processedEvents = await Promise.allSettled(
128
+ pendingEvents.map(async (event) => {
129
+ const hookFn = hooks[event.hookName];
130
+ if (!hookFn) {
131
+ return {
132
+ eventId: event.id,
133
+ status: "failed" as const,
134
+ error: `Hook '${event.hookName}' not found in hooks map`,
135
+ attempts: event.attempts,
136
+ maxAttempts: event.maxAttempts,
137
+ };
121
138
  }
122
139
 
123
- const processedEvents = await Promise.allSettled(
124
- pendingEvents.map(async (event) => {
125
- const hookFn = hooks[event.hookName];
126
- if (!hookFn) {
127
- return {
128
- eventId: event.id,
129
- status: "failed" as const,
130
- error: `Hook '${event.hookName}' not found in hooks map`,
131
- attempts: event.attempts,
132
- maxAttempts: event.maxAttempts,
133
- };
134
- }
140
+ try {
141
+ const hookContext: HookContext = { idempotencyKey: event.idempotencyKey };
142
+ await hookFn.call(hookContext, event.payload);
143
+ return {
144
+ eventId: event.id,
145
+ status: "completed" as const,
146
+ };
147
+ } catch (error) {
148
+ const errorMessage = error instanceof Error ? error.message : String(error);
149
+ return {
150
+ eventId: event.id,
151
+ status: "failed" as const,
152
+ error: errorMessage,
153
+ attempts: event.attempts,
154
+ maxAttempts: event.maxAttempts,
155
+ };
156
+ }
157
+ }),
158
+ );
135
159
 
136
- try {
137
- const hookContext: HookContext = { nonce: event.nonce };
138
- await hookFn.call(hookContext, event.payload);
139
- return {
140
- eventId: event.id,
141
- status: "completed" as const,
142
- };
143
- } catch (error) {
144
- const errorMessage = error instanceof Error ? error.message : String(error);
145
- return {
146
- eventId: event.id,
147
- status: "failed" as const,
148
- error: errorMessage,
149
- attempts: event.attempts,
150
- maxAttempts: event.maxAttempts,
151
- };
160
+ // Mark events as completed/failed
161
+ await internalFragment.inContext(async function () {
162
+ await this.handlerTx()
163
+ .withServiceCalls(() => {
164
+ const txResults: TxResult<void>[] = [];
165
+ for (const processedEvent of processedEvents) {
166
+ if (processedEvent.status === "rejected") {
167
+ continue;
152
168
  }
153
- }),
154
- );
155
169
 
156
- for (const processedEvent of processedEvents) {
157
- if (processedEvent.status === "rejected") {
158
- continue;
159
- }
160
-
161
- const { eventId, status } = processedEvent.value;
162
-
163
- if (status === "completed") {
164
- internalFragment.services.hookService.markHookCompleted(eventId);
165
- } else if (status === "failed") {
166
- const { error, attempts } = processedEvent.value;
167
- internalFragment.services.hookService.markHookFailed(
168
- eventId,
169
- error,
170
- attempts,
171
- retryPolicy,
172
- );
170
+ const { eventId, status } = processedEvent.value;
171
+
172
+ if (status === "completed") {
173
+ txResults.push(internalFragment.services.hookService.markHookCompleted(eventId));
174
+ } else if (status === "failed") {
175
+ const { error, attempts } = processedEvent.value;
176
+ txResults.push(
177
+ internalFragment.services.hookService.markHookFailed(
178
+ eventId,
179
+ error,
180
+ attempts,
181
+ retryPolicy,
182
+ ),
183
+ );
184
+ }
173
185
  }
174
- }
175
-
176
- await executeMutate();
177
- });
186
+ return txResults;
187
+ })
188
+ .execute();
178
189
  });
179
190
  }
package/src/mod.ts CHANGED
@@ -101,10 +101,20 @@ export {
101
101
  } from "./query/unit-of-work/retry-policy";
102
102
 
103
103
  export {
104
- executeUnitOfWork,
105
- type ExecuteUnitOfWorkResult,
106
- type ExecuteUnitOfWorkCallbacks,
107
- type ExecuteUnitOfWorkOptions,
104
+ ConcurrencyConflictError,
105
+ // Builder pattern exports
106
+ ServiceTxBuilder,
107
+ HandlerTxBuilder,
108
+ createServiceTxBuilder,
109
+ createHandlerTxBuilder,
110
+ type TxResult,
111
+ // Builder context types
112
+ type ServiceBuilderMutateContext,
113
+ type HandlerBuilderMutateContext,
114
+ type BuilderTransformContextWithMutate,
115
+ type BuilderTransformContextWithoutMutate,
116
+ type ExtractServiceRetrieveResults,
117
+ type ExtractServiceFinalResults,
108
118
  } from "./query/unit-of-work/execute-unit-of-work";
109
119
 
110
120
  export type { BoundServices } from "@fragno-dev/core";