@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.
- package/.turbo/turbo-build.log +34 -30
- package/CHANGELOG.md +49 -0
- package/dist/adapters/generic-sql/query/where-builder.js +1 -1
- package/dist/db-fragment-definition-builder.d.ts +31 -39
- package/dist/db-fragment-definition-builder.d.ts.map +1 -1
- package/dist/db-fragment-definition-builder.js +20 -16
- package/dist/db-fragment-definition-builder.js.map +1 -1
- package/dist/fragments/internal-fragment.d.ts +94 -8
- package/dist/fragments/internal-fragment.d.ts.map +1 -1
- package/dist/fragments/internal-fragment.js +56 -55
- package/dist/fragments/internal-fragment.js.map +1 -1
- package/dist/hooks/hooks.d.ts +5 -3
- package/dist/hooks/hooks.d.ts.map +1 -1
- package/dist/hooks/hooks.js +38 -37
- package/dist/hooks/hooks.js.map +1 -1
- package/dist/mod.d.ts +3 -3
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +4 -4
- package/dist/mod.js.map +1 -1
- package/dist/query/unit-of-work/execute-unit-of-work.d.ts +367 -80
- package/dist/query/unit-of-work/execute-unit-of-work.d.ts.map +1 -1
- package/dist/query/unit-of-work/execute-unit-of-work.js +448 -148
- package/dist/query/unit-of-work/execute-unit-of-work.js.map +1 -1
- package/dist/query/unit-of-work/unit-of-work.d.ts +35 -11
- package/dist/query/unit-of-work/unit-of-work.d.ts.map +1 -1
- package/dist/query/unit-of-work/unit-of-work.js +49 -19
- package/dist/query/unit-of-work/unit-of-work.js.map +1 -1
- package/dist/query/value-decoding.js +1 -1
- package/dist/schema/create.d.ts +2 -3
- package/dist/schema/create.d.ts.map +1 -1
- package/dist/schema/create.js +2 -5
- package/dist/schema/create.js.map +1 -1
- package/dist/schema/generate-id.d.ts +20 -0
- package/dist/schema/generate-id.d.ts.map +1 -0
- package/dist/schema/generate-id.js +28 -0
- package/dist/schema/generate-id.js.map +1 -0
- package/dist/sql-driver/dialects/durable-object-dialect.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/adapters/drizzle/drizzle-adapter-pglite.test.ts +1 -0
- package/src/adapters/drizzle/drizzle-adapter-sqlite3.test.ts +41 -25
- package/src/adapters/generic-sql/test/generic-drizzle-adapter-sqlite3.test.ts +39 -25
- package/src/db-fragment-definition-builder.test.ts +58 -42
- package/src/db-fragment-definition-builder.ts +78 -88
- package/src/db-fragment-instantiator.test.ts +64 -88
- package/src/db-fragment-integration.test.ts +292 -142
- package/src/fragments/internal-fragment.test.ts +272 -266
- package/src/fragments/internal-fragment.ts +155 -122
- package/src/hooks/hooks.test.ts +268 -264
- package/src/hooks/hooks.ts +74 -63
- package/src/mod.ts +14 -4
- package/src/query/unit-of-work/execute-unit-of-work.test.ts +1582 -998
- package/src/query/unit-of-work/execute-unit-of-work.ts +1746 -343
- package/src/query/unit-of-work/tx-builder.test.ts +1041 -0
- package/src/query/unit-of-work/unit-of-work-coordinator.test.ts +269 -21
- package/src/query/unit-of-work/unit-of-work.test.ts +64 -0
- package/src/query/unit-of-work/unit-of-work.ts +65 -30
- package/src/schema/create.ts +2 -5
- package/src/schema/generate-id.test.ts +57 -0
- package/src/schema/generate-id.ts +38 -0
- package/src/shared/config.ts +0 -10
- package/src/shared/connection-pool.ts +0 -24
- package/src/shared/prisma.ts +0 -45
package/src/hooks/hooks.ts
CHANGED
|
@@ -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
|
|
9
|
+
* Contains the idempotency key for idempotency and database access.
|
|
9
10
|
*/
|
|
10
11
|
export interface HookContext {
|
|
11
12
|
/**
|
|
12
|
-
* Unique
|
|
13
|
+
* Unique idempotency key for this transaction.
|
|
13
14
|
* Use this for idempotency checks in your hook implementation.
|
|
14
15
|
*/
|
|
15
|
-
|
|
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.
|
|
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
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
122
|
+
if (pendingEvents.length === 0) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
118
125
|
|
|
119
|
-
|
|
120
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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";
|