@beignet/core 0.0.50 → 0.0.51
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/CHANGELOG.md +8 -0
- package/README.md +53 -26
- package/dist/application/index.d.ts +5 -5
- package/dist/application/index.d.ts.map +1 -1
- package/dist/application/index.js +5 -4
- package/dist/application/index.js.map +1 -1
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +6 -3
- package/dist/events/index.js.map +1 -1
- package/dist/events/payload-state.d.ts +4 -0
- package/dist/events/payload-state.d.ts.map +1 -0
- package/dist/events/payload-state.js +11 -0
- package/dist/events/payload-state.js.map +1 -0
- package/dist/locks/index.d.ts.map +1 -1
- package/dist/locks/index.js +0 -4
- package/dist/locks/index.js.map +1 -1
- package/dist/outbox/index.d.ts +163 -15
- package/dist/outbox/index.d.ts.map +1 -1
- package/dist/outbox/index.js +1009 -152
- package/dist/outbox/index.js.map +1 -1
- package/dist/payments/index.d.ts.map +1 -1
- package/dist/payments/index.js +0 -4
- package/dist/payments/index.js.map +1 -1
- package/dist/ports/index.d.ts +1 -1
- package/dist/ports/index.d.ts.map +1 -1
- package/dist/ports/index.js.map +1 -1
- package/dist/ports/testing.d.ts +13 -0
- package/dist/ports/testing.d.ts.map +1 -1
- package/dist/ports/testing.js +12 -0
- package/dist/ports/testing.js.map +1 -1
- package/dist/ports/unit-of-work.d.ts +9 -7
- package/dist/ports/unit-of-work.d.ts.map +1 -1
- package/dist/ports/unit-of-work.js +16 -6
- package/dist/ports/unit-of-work.js.map +1 -1
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/provider.d.ts +0 -38
- package/dist/providers/provider.d.ts.map +1 -1
- package/dist/providers/provider.js.map +1 -1
- package/dist/search/index.d.ts.map +1 -1
- package/dist/search/index.js +0 -4
- package/dist/search/index.js.map +1 -1
- package/dist/server/hooks/cors.d.ts +5 -0
- package/dist/server/hooks/cors.d.ts.map +1 -1
- package/dist/server/hooks/cors.js +29 -1
- package/dist/server/hooks/cors.js.map +1 -1
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +34 -9
- package/dist/server/server.js.map +1 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +12 -6
- package/dist/testing/index.js.map +1 -1
- package/package.json +1 -1
- package/skills/app-architecture/SKILL.md +14 -0
- package/src/application/index.ts +18 -7
- package/src/events/index.ts +9 -6
- package/src/events/payload-state.ts +24 -0
- package/src/locks/index.ts +0 -4
- package/src/outbox/index.ts +1382 -175
- package/src/payments/index.ts +0 -4
- package/src/ports/index.ts +3 -0
- package/src/ports/testing.ts +31 -0
- package/src/ports/unit-of-work.ts +28 -14
- package/src/providers/index.ts +0 -1
- package/src/providers/provider.ts +0 -40
- package/src/search/index.ts +0 -4
- package/src/server/hooks/cors.ts +42 -0
- package/src/server/server.ts +52 -19
- package/src/testing/index.ts +20 -9
package/src/payments/index.ts
CHANGED
|
@@ -624,10 +624,6 @@ export function createMemoryPaymentsProvider(
|
|
|
624
624
|
|
|
625
625
|
return createProvider({
|
|
626
626
|
name,
|
|
627
|
-
metadata: {
|
|
628
|
-
ports: ["payments"],
|
|
629
|
-
watchers: ["payments"],
|
|
630
|
-
},
|
|
631
627
|
setup({ ports }) {
|
|
632
628
|
const instrumentation = createProviderInstrumentation(ports, {
|
|
633
629
|
providerName: name,
|
package/src/ports/index.ts
CHANGED
|
@@ -283,6 +283,7 @@ export type {
|
|
|
283
283
|
ClaimedOutboxMessage,
|
|
284
284
|
OutboxAdminPort,
|
|
285
285
|
OutboxClaimBatchOptions,
|
|
286
|
+
OutboxClaimBatchResult,
|
|
286
287
|
OutboxCountMessagesOptions,
|
|
287
288
|
OutboxDeleteResult,
|
|
288
289
|
OutboxEnqueueInput,
|
|
@@ -296,6 +297,8 @@ export type {
|
|
|
296
297
|
OutboxPort,
|
|
297
298
|
OutboxPruneDeliveredInput,
|
|
298
299
|
OutboxPurgeDeadLetteredInput,
|
|
300
|
+
OutboxRenewClaimInput,
|
|
301
|
+
OutboxRenewClaimResult,
|
|
299
302
|
OutboxRequeueMessageInput,
|
|
300
303
|
} from "../outbox/index.js";
|
|
301
304
|
/**
|
package/src/ports/testing.ts
CHANGED
|
@@ -953,6 +953,19 @@ export interface OutboxDrainResultExpectation {
|
|
|
953
953
|
* Expected dead-lettered count.
|
|
954
954
|
*/
|
|
955
955
|
deadLettered?: number;
|
|
956
|
+
/**
|
|
957
|
+
* Expected count of eligible messages dead-lettered after exhausting their
|
|
958
|
+
* claim attempt budget.
|
|
959
|
+
*/
|
|
960
|
+
abandonedDeadLettered?: number;
|
|
961
|
+
/**
|
|
962
|
+
* Expected count of messages whose durable settlement remained unknown.
|
|
963
|
+
*/
|
|
964
|
+
settlementFailed?: number;
|
|
965
|
+
/**
|
|
966
|
+
* Expected count of messages whose active claim could not be confirmed.
|
|
967
|
+
*/
|
|
968
|
+
leaseLost?: number;
|
|
956
969
|
}
|
|
957
970
|
|
|
958
971
|
/**
|
|
@@ -2421,6 +2434,24 @@ function outboxDrainResultMatches(
|
|
|
2421
2434
|
) {
|
|
2422
2435
|
return false;
|
|
2423
2436
|
}
|
|
2437
|
+
if (
|
|
2438
|
+
expectation.abandonedDeadLettered !== undefined &&
|
|
2439
|
+
result.abandonedDeadLettered !== expectation.abandonedDeadLettered
|
|
2440
|
+
) {
|
|
2441
|
+
return false;
|
|
2442
|
+
}
|
|
2443
|
+
if (
|
|
2444
|
+
expectation.settlementFailed !== undefined &&
|
|
2445
|
+
result.settlementFailed !== expectation.settlementFailed
|
|
2446
|
+
) {
|
|
2447
|
+
return false;
|
|
2448
|
+
}
|
|
2449
|
+
if (
|
|
2450
|
+
expectation.leaseLost !== undefined &&
|
|
2451
|
+
result.leaseLost !== expectation.leaseLost
|
|
2452
|
+
) {
|
|
2453
|
+
return false;
|
|
2454
|
+
}
|
|
2424
2455
|
|
|
2425
2456
|
return true;
|
|
2426
2457
|
}
|
|
@@ -2,6 +2,10 @@ import {
|
|
|
2
2
|
type EventPublishOptions,
|
|
3
3
|
parseEventPayload,
|
|
4
4
|
} from "../events/index.js";
|
|
5
|
+
import {
|
|
6
|
+
isEventPayloadParsed,
|
|
7
|
+
markEventPayloadParsed,
|
|
8
|
+
} from "../events/payload-state.js";
|
|
5
9
|
import type {
|
|
6
10
|
DomainEventDef,
|
|
7
11
|
EventBusPort,
|
|
@@ -83,17 +87,18 @@ export interface RecordedDomainEvent {
|
|
|
83
87
|
/**
|
|
84
88
|
* Event definition used to validate the payload before publishing.
|
|
85
89
|
*/
|
|
86
|
-
event: DomainEventDef;
|
|
90
|
+
readonly event: DomainEventDef;
|
|
87
91
|
/**
|
|
88
92
|
* Stable event name.
|
|
89
93
|
*/
|
|
90
|
-
eventName: string;
|
|
94
|
+
readonly eventName: string;
|
|
91
95
|
/**
|
|
92
|
-
*
|
|
96
|
+
* Recorded payload. Use-case helpers store parsed schema output; direct
|
|
97
|
+
* recorder calls are validated when the buffer is flushed.
|
|
93
98
|
*/
|
|
94
|
-
payload: unknown;
|
|
99
|
+
readonly payload: unknown;
|
|
95
100
|
/** Optional metadata propagated when the event is flushed. */
|
|
96
|
-
options?: EventPublishOptions;
|
|
101
|
+
readonly options?: EventPublishOptions;
|
|
97
102
|
}
|
|
98
103
|
|
|
99
104
|
/**
|
|
@@ -119,7 +124,7 @@ export interface DomainEventRecorderPort {
|
|
|
119
124
|
*/
|
|
120
125
|
export interface BufferedDomainEventRecorder extends DomainEventRecorderPort {
|
|
121
126
|
/**
|
|
122
|
-
* Return recorded events without clearing them.
|
|
127
|
+
* Return a snapshot of recorded events without clearing them.
|
|
123
128
|
*/
|
|
124
129
|
entries(): readonly RecordedDomainEvent[];
|
|
125
130
|
/**
|
|
@@ -127,7 +132,8 @@ export interface BufferedDomainEventRecorder extends DomainEventRecorderPort {
|
|
|
127
132
|
*/
|
|
128
133
|
clear(): void;
|
|
129
134
|
/**
|
|
130
|
-
*
|
|
135
|
+
* Publish recorded events in FIFO order, validating entries recorded
|
|
136
|
+
* directly without the use-case event helper.
|
|
131
137
|
*/
|
|
132
138
|
flush(eventBus: EventBusPort): Promise<void>;
|
|
133
139
|
}
|
|
@@ -223,19 +229,25 @@ export function createObservedUnitOfWork<TxPorts>(
|
|
|
223
229
|
*/
|
|
224
230
|
export function createDomainEventRecorder(): BufferedDomainEventRecorder {
|
|
225
231
|
const records: RecordedDomainEvent[] = [];
|
|
232
|
+
const parsedRecords = new WeakSet<RecordedDomainEvent>();
|
|
226
233
|
|
|
227
234
|
return {
|
|
228
235
|
record(event, payload, options) {
|
|
229
|
-
|
|
236
|
+
const record: RecordedDomainEvent = {
|
|
230
237
|
event,
|
|
231
238
|
eventName: event.name,
|
|
232
239
|
payload,
|
|
233
|
-
...(options ? { options } : {}),
|
|
234
|
-
}
|
|
240
|
+
...(options?.trace ? { options: { trace: options.trace } } : {}),
|
|
241
|
+
};
|
|
242
|
+
if (isEventPayloadParsed(options)) parsedRecords.add(record);
|
|
243
|
+
records.push(record);
|
|
235
244
|
},
|
|
236
245
|
|
|
237
246
|
entries() {
|
|
238
|
-
return records
|
|
247
|
+
return records.map((record) => ({
|
|
248
|
+
...record,
|
|
249
|
+
...(record.options ? { options: { ...record.options } } : {}),
|
|
250
|
+
}));
|
|
239
251
|
},
|
|
240
252
|
|
|
241
253
|
clear() {
|
|
@@ -245,11 +257,13 @@ export function createDomainEventRecorder(): BufferedDomainEventRecorder {
|
|
|
245
257
|
async flush(eventBus) {
|
|
246
258
|
while (records.length > 0) {
|
|
247
259
|
const record = records[0];
|
|
248
|
-
|
|
260
|
+
const payload = parsedRecords.has(record)
|
|
261
|
+
? record.payload
|
|
262
|
+
: await parseEventPayload(record.event, record.payload);
|
|
249
263
|
await eventBus.publish(
|
|
250
264
|
record.event,
|
|
251
|
-
|
|
252
|
-
record.options,
|
|
265
|
+
payload as never,
|
|
266
|
+
markEventPayloadParsed(record.options),
|
|
253
267
|
);
|
|
254
268
|
records.shift();
|
|
255
269
|
}
|
package/src/providers/index.ts
CHANGED
|
@@ -126,41 +126,6 @@ export type ProviderSetupResult<
|
|
|
126
126
|
): MaybePromise<void>;
|
|
127
127
|
};
|
|
128
128
|
|
|
129
|
-
/**
|
|
130
|
-
* Static provider metadata used by docs and app-local tooling.
|
|
131
|
-
*
|
|
132
|
-
* Metadata is descriptive. It does not change provider setup, ordering, or
|
|
133
|
-
* runtime port merging behavior.
|
|
134
|
-
*
|
|
135
|
-
* Reusable provider packages should also declare package-owned
|
|
136
|
-
* `beignet.provider` metadata in package.json so external tooling can inspect
|
|
137
|
-
* provider facts without importing runtime code.
|
|
138
|
-
*/
|
|
139
|
-
export interface ServiceProviderMetadata {
|
|
140
|
-
/**
|
|
141
|
-
* Package that exports this provider, when it comes from a reusable package.
|
|
142
|
-
*/
|
|
143
|
-
packageName?: string;
|
|
144
|
-
/**
|
|
145
|
-
* App port keys this provider contributes or replaces.
|
|
146
|
-
*/
|
|
147
|
-
ports?: readonly string[];
|
|
148
|
-
/**
|
|
149
|
-
* App port keys this provider expects previous providers or base app ports to
|
|
150
|
-
* have installed before setup runs.
|
|
151
|
-
*/
|
|
152
|
-
requires?: readonly string[];
|
|
153
|
-
/**
|
|
154
|
-
* Environment variables this provider reads directly or via config loading.
|
|
155
|
-
*/
|
|
156
|
-
env?: readonly string[];
|
|
157
|
-
/**
|
|
158
|
-
* Devtools watcher names this provider can emit through provider
|
|
159
|
-
* instrumentation.
|
|
160
|
-
*/
|
|
161
|
-
watchers?: readonly string[];
|
|
162
|
-
}
|
|
163
|
-
|
|
164
129
|
/**
|
|
165
130
|
* A service provider that can extend or replace ports during app initialization.
|
|
166
131
|
*
|
|
@@ -205,11 +170,6 @@ export interface ServiceProvider<
|
|
|
205
170
|
*/
|
|
206
171
|
name: string;
|
|
207
172
|
|
|
208
|
-
/**
|
|
209
|
-
* Optional static metadata for docs and diagnostics.
|
|
210
|
-
*/
|
|
211
|
-
metadata?: ServiceProviderMetadata;
|
|
212
|
-
|
|
213
173
|
/**
|
|
214
174
|
* Optional configuration definition.
|
|
215
175
|
* If provided, the config will be loaded and validated before calling setup.
|
package/src/search/index.ts
CHANGED
|
@@ -351,10 +351,6 @@ export function createMemorySearchProvider(
|
|
|
351
351
|
|
|
352
352
|
return createProvider({
|
|
353
353
|
name,
|
|
354
|
-
metadata: {
|
|
355
|
-
ports: ["search"],
|
|
356
|
-
watchers: ["search"],
|
|
357
|
-
},
|
|
358
354
|
setup({ ports }) {
|
|
359
355
|
const instrumentation = createProviderInstrumentation(ports, {
|
|
360
356
|
providerName: name,
|
package/src/server/hooks/cors.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* CORS hook utilities for @beignet/core/server
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import { BEIGNET_ERROR_OWNER_HEADER } from "../../contracts/types.js";
|
|
5
6
|
import type {
|
|
6
7
|
HttpRequestLike,
|
|
7
8
|
HttpResponseHeaders,
|
|
@@ -25,6 +26,11 @@ export interface CorsConfig {
|
|
|
25
26
|
* Allowed request headers.
|
|
26
27
|
*/
|
|
27
28
|
headers?: string[];
|
|
29
|
+
/**
|
|
30
|
+
* Additional response headers browser JavaScript may read. Beignet always
|
|
31
|
+
* exposes its framework error-ownership header.
|
|
32
|
+
*/
|
|
33
|
+
exposedHeaders?: string[];
|
|
28
34
|
/**
|
|
29
35
|
* Whether credentialed requests are allowed.
|
|
30
36
|
*/
|
|
@@ -35,6 +41,7 @@ const DEFAULT_CORS: Required<CorsConfig> = {
|
|
|
35
41
|
origins: "*",
|
|
36
42
|
methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
|
|
37
43
|
headers: ["Content-Type", "Authorization"],
|
|
44
|
+
exposedHeaders: [],
|
|
38
45
|
credentials: false,
|
|
39
46
|
};
|
|
40
47
|
|
|
@@ -43,6 +50,7 @@ function resolveCorsConfig(config: CorsConfig): Required<CorsConfig> {
|
|
|
43
50
|
origins: config.origins ?? DEFAULT_CORS.origins,
|
|
44
51
|
methods: config.methods ?? DEFAULT_CORS.methods,
|
|
45
52
|
headers: config.headers ?? DEFAULT_CORS.headers,
|
|
53
|
+
exposedHeaders: config.exposedHeaders ?? DEFAULT_CORS.exposedHeaders,
|
|
46
54
|
credentials: config.credentials ?? DEFAULT_CORS.credentials,
|
|
47
55
|
};
|
|
48
56
|
|
|
@@ -71,6 +79,35 @@ function appendVaryOrigin(headers: HttpResponseHeaders): void {
|
|
|
71
79
|
headers[varyKey] = current ? `${current}, Origin` : "Origin";
|
|
72
80
|
}
|
|
73
81
|
|
|
82
|
+
function appendCommaSeparatedHeader(
|
|
83
|
+
headers: HttpResponseHeaders,
|
|
84
|
+
name: string,
|
|
85
|
+
appended: readonly string[],
|
|
86
|
+
): void {
|
|
87
|
+
const key =
|
|
88
|
+
Object.keys(headers).find(
|
|
89
|
+
(candidate) => candidate.toLowerCase() === name.toLowerCase(),
|
|
90
|
+
) ?? name;
|
|
91
|
+
const existing = headers[key];
|
|
92
|
+
const values = [
|
|
93
|
+
...(typeof existing === "string"
|
|
94
|
+
? existing.split(",")
|
|
95
|
+
: (existing ?? []).flatMap((value) => value.split(","))),
|
|
96
|
+
...appended,
|
|
97
|
+
]
|
|
98
|
+
.map((value) => value.trim())
|
|
99
|
+
.filter(Boolean);
|
|
100
|
+
const seen = new Set<string>();
|
|
101
|
+
const unique = values.filter((value) => {
|
|
102
|
+
const normalized = value.toLowerCase();
|
|
103
|
+
if (seen.has(normalized)) return false;
|
|
104
|
+
seen.add(normalized);
|
|
105
|
+
return true;
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
headers[key] = unique.join(", ");
|
|
109
|
+
}
|
|
110
|
+
|
|
74
111
|
/**
|
|
75
112
|
* Apply CORS response headers to a mutable header record.
|
|
76
113
|
*
|
|
@@ -86,6 +123,7 @@ export function applyCorsHeaders(
|
|
|
86
123
|
origins,
|
|
87
124
|
methods,
|
|
88
125
|
headers: allowedHeaders,
|
|
126
|
+
exposedHeaders,
|
|
89
127
|
credentials,
|
|
90
128
|
} = resolveCorsConfig(corsConfig);
|
|
91
129
|
|
|
@@ -101,6 +139,10 @@ export function applyCorsHeaders(
|
|
|
101
139
|
|
|
102
140
|
headers["Access-Control-Allow-Methods"] = methods.join(", ");
|
|
103
141
|
headers["Access-Control-Allow-Headers"] = allowedHeaders.join(", ");
|
|
142
|
+
appendCommaSeparatedHeader(headers, "Access-Control-Expose-Headers", [
|
|
143
|
+
BEIGNET_ERROR_OWNER_HEADER,
|
|
144
|
+
...exposedHeaders,
|
|
145
|
+
]);
|
|
104
146
|
|
|
105
147
|
if (credentials) {
|
|
106
148
|
headers["Access-Control-Allow-Credentials"] = "true";
|
package/src/server/server.ts
CHANGED
|
@@ -669,24 +669,6 @@ export async function createServer<
|
|
|
669
669
|
registryNeedsSort = true;
|
|
670
670
|
};
|
|
671
671
|
|
|
672
|
-
const createBuilder = <C extends HttpContractConfig>(
|
|
673
|
-
contract: C,
|
|
674
|
-
shouldRegister: boolean,
|
|
675
|
-
): RouteBuilder<Ctx, C> => ({
|
|
676
|
-
handle: (fn) => {
|
|
677
|
-
const wrapped = buildHandler(
|
|
678
|
-
options,
|
|
679
|
-
finalPorts,
|
|
680
|
-
contextRuntime,
|
|
681
|
-
contract,
|
|
682
|
-
fn,
|
|
683
|
-
hooks,
|
|
684
|
-
);
|
|
685
|
-
if (shouldRegister) registerRoute(contract, fn);
|
|
686
|
-
return wrapped;
|
|
687
|
-
},
|
|
688
|
-
});
|
|
689
|
-
|
|
690
672
|
if (options.routes) {
|
|
691
673
|
try {
|
|
692
674
|
for (const route of options.routes) {
|
|
@@ -917,7 +899,58 @@ export async function createServer<
|
|
|
917
899
|
api,
|
|
918
900
|
route: (contractLike) => {
|
|
919
901
|
const contract = resolveContract(contractLike);
|
|
920
|
-
return
|
|
902
|
+
return {
|
|
903
|
+
handle: (fn) => {
|
|
904
|
+
const wrapped = buildHandler(
|
|
905
|
+
options,
|
|
906
|
+
finalPorts,
|
|
907
|
+
contextRuntime,
|
|
908
|
+
contract,
|
|
909
|
+
fn,
|
|
910
|
+
hooks,
|
|
911
|
+
);
|
|
912
|
+
registerRoute(contract, fn);
|
|
913
|
+
const compiled = compilePath(contract.path);
|
|
914
|
+
|
|
915
|
+
return async (req: HttpRequestLike) => {
|
|
916
|
+
const method = req.method.toUpperCase();
|
|
917
|
+
let url: URL;
|
|
918
|
+
try {
|
|
919
|
+
url = parseHttpRequestUrl(req.url);
|
|
920
|
+
} catch (error) {
|
|
921
|
+
return await executeFallback(
|
|
922
|
+
{
|
|
923
|
+
contract: fallbackContract("invalidRequestUrl", method, "/"),
|
|
924
|
+
handler: async () => {
|
|
925
|
+
throw error;
|
|
926
|
+
},
|
|
927
|
+
},
|
|
928
|
+
req,
|
|
929
|
+
{},
|
|
930
|
+
);
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
const requestMethod = method;
|
|
934
|
+
const contractMethod = contract.method.toUpperCase();
|
|
935
|
+
const methodMatches =
|
|
936
|
+
requestMethod === contractMethod ||
|
|
937
|
+
(requestMethod === "HEAD" && contractMethod === "GET");
|
|
938
|
+
if (!methodMatches || !compiled.pattern.test(url.pathname)) {
|
|
939
|
+
const pathname = url.pathname || "/";
|
|
940
|
+
return await executeFallback(
|
|
941
|
+
{
|
|
942
|
+
contract: fallbackContract("notFound", method, pathname),
|
|
943
|
+
handler: notFoundHandler,
|
|
944
|
+
},
|
|
945
|
+
req,
|
|
946
|
+
{},
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
return await wrapped(req);
|
|
951
|
+
};
|
|
952
|
+
},
|
|
953
|
+
};
|
|
921
954
|
},
|
|
922
955
|
rawRoute: (init) => ({
|
|
923
956
|
handle: (fn) => {
|
package/src/testing/index.ts
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
} from "../notifications/index.js";
|
|
16
16
|
import {
|
|
17
17
|
createMemoryOutbox,
|
|
18
|
-
|
|
18
|
+
createOutboxEventRecorder,
|
|
19
19
|
type MemoryOutboxPort,
|
|
20
20
|
} from "../outbox/index.js";
|
|
21
21
|
import {
|
|
@@ -489,10 +489,17 @@ export function createTestPorts<
|
|
|
489
489
|
outbox: MemoryOutboxPort;
|
|
490
490
|
}
|
|
491
491
|
).outbox;
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
492
|
+
const outboxRecorder = createOutboxEventRecorder(outbox);
|
|
493
|
+
await events.flush({
|
|
494
|
+
publish(event, payload, options) {
|
|
495
|
+
return outboxRecorder.record(event, payload, options);
|
|
496
|
+
},
|
|
497
|
+
subscribe() {
|
|
498
|
+
throw new Error(
|
|
499
|
+
"The test outbox event publisher does not support subscriptions.",
|
|
500
|
+
);
|
|
501
|
+
},
|
|
502
|
+
});
|
|
496
503
|
}
|
|
497
504
|
await transactionOptions?.afterCommit?.(tx);
|
|
498
505
|
},
|
|
@@ -619,18 +626,19 @@ function completeTestPortOverrides<Ports extends AnyPorts>(
|
|
|
619
626
|
|
|
620
627
|
function resolveBufferedTransactionEvents(
|
|
621
628
|
tx: unknown,
|
|
622
|
-
): Pick<BufferedDomainEventRecorder, "entries" | "clear"> {
|
|
629
|
+
): Pick<BufferedDomainEventRecorder, "entries" | "clear" | "flush"> {
|
|
623
630
|
const events =
|
|
624
631
|
tx && typeof tx === "object"
|
|
625
632
|
? (tx as { events?: unknown }).events
|
|
626
633
|
: undefined;
|
|
627
634
|
const recorder = events as
|
|
628
|
-
| Partial<Pick<BufferedDomainEventRecorder, "entries" | "clear">>
|
|
635
|
+
| Partial<Pick<BufferedDomainEventRecorder, "entries" | "clear" | "flush">>
|
|
629
636
|
| undefined;
|
|
630
637
|
if (
|
|
631
638
|
!recorder ||
|
|
632
639
|
typeof recorder.entries !== "function" ||
|
|
633
|
-
typeof recorder.clear !== "function"
|
|
640
|
+
typeof recorder.clear !== "function" ||
|
|
641
|
+
typeof recorder.flush !== "function"
|
|
634
642
|
) {
|
|
635
643
|
throw new Error(
|
|
636
644
|
"createTestPorts transaction.outbox requires tx.events to be a buffered domain event recorder. " +
|
|
@@ -638,7 +646,10 @@ function resolveBufferedTransactionEvents(
|
|
|
638
646
|
);
|
|
639
647
|
}
|
|
640
648
|
|
|
641
|
-
return recorder as Pick<
|
|
649
|
+
return recorder as Pick<
|
|
650
|
+
BufferedDomainEventRecorder,
|
|
651
|
+
"entries" | "clear" | "flush"
|
|
652
|
+
>;
|
|
642
653
|
}
|
|
643
654
|
|
|
644
655
|
/**
|