@beignet/core 0.0.1 → 0.0.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/CHANGELOG.md +11 -0
- package/README.md +149 -4
- package/dist/application/index.d.ts +93 -9
- package/dist/application/index.d.ts.map +1 -1
- package/dist/application/index.js +11 -11
- package/dist/application/index.js.map +1 -1
- package/dist/client/client.d.ts +73 -12
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +37 -12
- package/dist/client/client.js.map +1 -1
- package/dist/client/index.d.ts +12 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +6 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/types.d.ts +69 -8
- package/dist/client/types.d.ts.map +1 -1
- package/dist/config/index.d.ts +84 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +36 -0
- package/dist/config/index.js.map +1 -1
- package/dist/contracts/contract-builder.d.ts +49 -22
- package/dist/contracts/contract-builder.d.ts.map +1 -1
- package/dist/contracts/contract-builder.js +48 -21
- package/dist/contracts/contract-builder.js.map +1 -1
- package/dist/contracts/contract-group.d.ts +35 -19
- package/dist/contracts/contract-group.d.ts.map +1 -1
- package/dist/contracts/contract-group.js +35 -19
- package/dist/contracts/contract-group.js.map +1 -1
- package/dist/contracts/contract-like.d.ts +4 -4
- package/dist/contracts/contract-like.d.ts.map +1 -1
- package/dist/contracts/contract-like.js +2 -1
- package/dist/contracts/contract-like.js.map +1 -1
- package/dist/contracts/index.d.ts +28 -0
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/index.js +12 -0
- package/dist/contracts/index.js.map +1 -1
- package/dist/contracts/openapi-meta.d.ts +8 -8
- package/dist/contracts/openapi-meta.d.ts.map +1 -1
- package/dist/contracts/path-template.d.ts +27 -0
- package/dist/contracts/path-template.d.ts.map +1 -1
- package/dist/contracts/path-template.js +6 -0
- package/dist/contracts/path-template.js.map +1 -1
- package/dist/contracts/types.d.ts +104 -10
- package/dist/contracts/types.d.ts.map +1 -1
- package/dist/contracts/types.js +15 -0
- package/dist/contracts/types.js.map +1 -1
- package/dist/contracts/utils.d.ts +6 -0
- package/dist/contracts/utils.d.ts.map +1 -1
- package/dist/contracts/utils.js +6 -0
- package/dist/contracts/utils.js.map +1 -1
- package/dist/domain/entity.d.ts +22 -11
- package/dist/domain/entity.d.ts.map +1 -1
- package/dist/domain/entity.js +5 -1
- package/dist/domain/entity.js.map +1 -1
- package/dist/domain/events.d.ts +5 -2
- package/dist/domain/events.d.ts.map +1 -1
- package/dist/domain/events.js +4 -1
- package/dist/domain/events.js.map +1 -1
- package/dist/domain/value-object.d.ts +19 -9
- package/dist/domain/value-object.d.ts.map +1 -1
- package/dist/domain/value-object.js +5 -1
- package/dist/domain/value-object.js.map +1 -1
- package/dist/errors/catalog.d.ts +40 -16
- package/dist/errors/catalog.d.ts.map +1 -1
- package/dist/errors/catalog.js +18 -7
- package/dist/errors/catalog.js.map +1 -1
- package/dist/errors/response.d.ts +16 -4
- package/dist/errors/response.d.ts.map +1 -1
- package/dist/errors/response.js +3 -3
- package/dist/errors/response.js.map +1 -1
- package/dist/errors/validation.d.ts +10 -1
- package/dist/errors/validation.d.ts.map +1 -1
- package/dist/errors/validation.js +3 -0
- package/dist/errors/validation.js.map +1 -1
- package/dist/events/index.d.ts +133 -0
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +30 -0
- package/dist/events/index.js.map +1 -1
- package/dist/idempotency/index.d.ts +355 -0
- package/dist/idempotency/index.d.ts.map +1 -0
- package/dist/idempotency/index.js +360 -0
- package/dist/idempotency/index.js.map +1 -0
- package/dist/jobs/index.d.ts +110 -0
- package/dist/jobs/index.d.ts.map +1 -1
- package/dist/jobs/index.js +22 -0
- package/dist/jobs/index.js.map +1 -1
- package/dist/mail/index.d.ts +149 -0
- package/dist/mail/index.d.ts.map +1 -1
- package/dist/mail/index.js +30 -0
- package/dist/mail/index.js.map +1 -1
- package/dist/notifications/index.d.ts +369 -0
- package/dist/notifications/index.d.ts.map +1 -0
- package/dist/notifications/index.js +310 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/openapi/index.d.ts +132 -16
- package/dist/openapi/index.d.ts.map +1 -1
- package/dist/openapi/index.js +1 -1
- package/dist/openapi/index.js.map +1 -1
- package/dist/outbox/index.d.ts +469 -0
- package/dist/outbox/index.d.ts.map +1 -0
- package/dist/outbox/index.js +482 -0
- package/dist/outbox/index.js.map +1 -0
- package/dist/pagination/index.d.ts +166 -0
- package/dist/pagination/index.d.ts.map +1 -0
- package/dist/pagination/index.js +96 -0
- package/dist/pagination/index.js.map +1 -0
- package/dist/ports/audit.d.ts +271 -0
- package/dist/ports/audit.d.ts.map +1 -1
- package/dist/ports/audit.js +128 -0
- package/dist/ports/audit.js.map +1 -1
- package/dist/ports/auth.d.ts +70 -0
- package/dist/ports/auth.d.ts.map +1 -1
- package/dist/ports/auth.js +30 -0
- package/dist/ports/auth.js.map +1 -1
- package/dist/ports/cache.d.ts +41 -0
- package/dist/ports/cache.d.ts.map +1 -1
- package/dist/ports/cache.js +10 -0
- package/dist/ports/cache.js.map +1 -1
- package/dist/ports/clock.d.ts +38 -0
- package/dist/ports/clock.d.ts.map +1 -1
- package/dist/ports/clock.js +20 -0
- package/dist/ports/clock.js.map +1 -1
- package/dist/ports/id-generator.d.ts +37 -0
- package/dist/ports/id-generator.d.ts.map +1 -1
- package/dist/ports/id-generator.js +22 -0
- package/dist/ports/id-generator.js.map +1 -1
- package/dist/ports/index.d.ts +83 -0
- package/dist/ports/index.d.ts.map +1 -1
- package/dist/ports/index.js +41 -5
- package/dist/ports/index.js.map +1 -1
- package/dist/ports/logger.d.ts +56 -0
- package/dist/ports/logger.d.ts.map +1 -1
- package/dist/ports/logger.js +17 -0
- package/dist/ports/logger.js.map +1 -1
- package/dist/ports/policy.d.ts +132 -0
- package/dist/ports/policy.d.ts.map +1 -1
- package/dist/ports/policy.js +45 -0
- package/dist/ports/policy.js.map +1 -1
- package/dist/ports/rate-limit.d.ts +25 -0
- package/dist/ports/rate-limit.d.ts.map +1 -1
- package/dist/ports/rate-limit.js +10 -0
- package/dist/ports/rate-limit.js.map +1 -1
- package/dist/ports/redaction.d.ts +101 -0
- package/dist/ports/redaction.d.ts.map +1 -1
- package/dist/ports/redaction.js +59 -0
- package/dist/ports/redaction.js.map +1 -1
- package/dist/ports/storage.d.ts +100 -0
- package/dist/ports/storage.d.ts.map +1 -1
- package/dist/ports/storage.js +10 -0
- package/dist/ports/storage.js.map +1 -1
- package/dist/ports/testing.d.ts +47 -0
- package/dist/ports/testing.d.ts.map +1 -1
- package/dist/ports/testing.js +23 -0
- package/dist/ports/testing.js.map +1 -1
- package/dist/ports/unit-of-work.d.ts +60 -3
- package/dist/ports/unit-of-work.d.ts.map +1 -1
- package/dist/ports/unit-of-work.js +11 -2
- package/dist/ports/unit-of-work.js.map +1 -1
- package/dist/providers/instrumentation.d.ts +204 -0
- package/dist/providers/instrumentation.d.ts.map +1 -1
- package/dist/providers/instrumentation.js +14 -0
- package/dist/providers/instrumentation.js.map +1 -1
- package/dist/providers/provider.d.ts +14 -1
- package/dist/providers/provider.d.ts.map +1 -1
- package/dist/providers/provider.js.map +1 -1
- package/dist/schedules/index.d.ts +246 -0
- package/dist/schedules/index.d.ts.map +1 -1
- package/dist/schedules/index.js +27 -0
- package/dist/schedules/index.js.map +1 -1
- package/dist/server/health.d.ts +14 -5
- package/dist/server/health.d.ts.map +1 -1
- package/dist/server/health.js +5 -2
- package/dist/server/health.js.map +1 -1
- package/dist/server/hooks/auth.d.ts +57 -0
- package/dist/server/hooks/auth.d.ts.map +1 -1
- package/dist/server/hooks/auth.js.map +1 -1
- package/dist/server/hooks/cors.d.ts +27 -0
- package/dist/server/hooks/cors.d.ts.map +1 -1
- package/dist/server/hooks/cors.js +12 -0
- package/dist/server/hooks/cors.js.map +1 -1
- package/dist/server/hooks/errors.d.ts +15 -6
- package/dist/server/hooks/errors.d.ts.map +1 -1
- package/dist/server/hooks/errors.js.map +1 -1
- package/dist/server/hooks/index.d.ts +3 -0
- package/dist/server/hooks/index.d.ts.map +1 -1
- package/dist/server/hooks/index.js +3 -0
- package/dist/server/hooks/index.js.map +1 -1
- package/dist/server/hooks/logging.d.ts +36 -0
- package/dist/server/hooks/logging.d.ts.map +1 -1
- package/dist/server/hooks/logging.js +6 -0
- package/dist/server/hooks/logging.js.map +1 -1
- package/dist/server/hooks/rate-limit.d.ts +33 -0
- package/dist/server/hooks/rate-limit.d.ts.map +1 -1
- package/dist/server/hooks/rate-limit.js +11 -0
- package/dist/server/hooks/rate-limit.js.map +1 -1
- package/dist/server/http.d.ts +170 -0
- package/dist/server/http.d.ts.map +1 -1
- package/dist/server/index.d.ts +18 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +6 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/openapi.d.ts +5 -3
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/server/openapi.js +4 -2
- package/dist/server/openapi.js.map +1 -1
- package/dist/server/providers/loadProviderConfig.d.ts +9 -0
- package/dist/server/providers/loadProviderConfig.d.ts.map +1 -1
- package/dist/server/providers/loadProviderConfig.js +9 -0
- package/dist/server/providers/loadProviderConfig.js.map +1 -1
- package/dist/server/server.d.ts +107 -8
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/server.js +27 -7
- package/dist/server/server.js.map +1 -1
- package/dist/testing/index.d.ts +167 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +119 -0
- package/dist/testing/index.js.map +1 -0
- package/package.json +21 -1
- package/src/application/index.ts +85 -22
- package/src/client/client.ts +73 -12
- package/src/client/index.ts +12 -0
- package/src/client/types.ts +70 -9
- package/src/config/index.ts +86 -0
- package/src/contracts/contract-builder.ts +49 -22
- package/src/contracts/contract-group.ts +35 -19
- package/src/contracts/contract-like.ts +4 -4
- package/src/contracts/index.ts +28 -1
- package/src/contracts/openapi-meta.ts +8 -8
- package/src/contracts/path-template.ts +27 -0
- package/src/contracts/types.ts +111 -10
- package/src/contracts/utils.ts +6 -0
- package/src/domain/entity.ts +22 -11
- package/src/domain/events.ts +5 -2
- package/src/domain/value-object.ts +19 -9
- package/src/errors/catalog.ts +40 -16
- package/src/errors/response.ts +16 -4
- package/src/errors/validation.ts +10 -1
- package/src/events/index.ts +134 -0
- package/src/idempotency/index.ts +767 -0
- package/src/jobs/index.ts +111 -0
- package/src/mail/index.ts +149 -0
- package/src/notifications/index.ts +771 -0
- package/src/openapi/index.ts +133 -16
- package/src/outbox/index.ts +1024 -0
- package/src/pagination/index.ts +278 -0
- package/src/ports/audit.ts +271 -0
- package/src/ports/auth.ts +70 -0
- package/src/ports/cache.ts +41 -0
- package/src/ports/clock.ts +38 -0
- package/src/ports/id-generator.ts +37 -0
- package/src/ports/index.ts +106 -11
- package/src/ports/logger.ts +56 -0
- package/src/ports/policy.ts +133 -0
- package/src/ports/rate-limit.ts +25 -0
- package/src/ports/redaction.ts +101 -0
- package/src/ports/storage.ts +100 -0
- package/src/ports/testing.ts +47 -0
- package/src/ports/unit-of-work.ts +60 -3
- package/src/providers/instrumentation.ts +204 -0
- package/src/providers/provider.ts +14 -1
- package/src/schedules/index.ts +247 -0
- package/src/server/health.ts +14 -5
- package/src/server/hooks/auth.ts +58 -0
- package/src/server/hooks/cors.ts +27 -0
- package/src/server/hooks/errors.ts +15 -6
- package/src/server/hooks/index.ts +3 -0
- package/src/server/hooks/logging.ts +36 -0
- package/src/server/hooks/rate-limit.ts +33 -0
- package/src/server/http.ts +170 -1
- package/src/server/index.ts +18 -1
- package/src/server/openapi.ts +5 -3
- package/src/server/providers/loadProviderConfig.ts +9 -0
- package/src/server/server.ts +107 -9
- package/src/testing/index.ts +337 -0
package/src/application/index.ts
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Any Standard Schema validator
|
|
4
|
+
* Any Standard Schema compatible validator.
|
|
5
5
|
*/
|
|
6
6
|
export type StandardSchema = StandardSchemaV1<unknown, unknown>;
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* Infer the output type from a Standard Schema
|
|
9
|
+
* Infer the parsed output type from a Standard Schema.
|
|
10
10
|
*/
|
|
11
11
|
export type InferOutput<T extends StandardSchemaV1> =
|
|
12
12
|
StandardSchemaV1.InferOutput<T>;
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* Infer the input type accepted by a Standard Schema
|
|
15
|
+
* Infer the input type accepted by a Standard Schema.
|
|
16
16
|
*/
|
|
17
17
|
export type InferInput<T extends StandardSchemaV1> =
|
|
18
18
|
StandardSchemaV1.InferInput<T>;
|
|
@@ -20,6 +20,9 @@ export type InferInput<T extends StandardSchemaV1> =
|
|
|
20
20
|
type SchemaInput<T> = T extends StandardSchemaV1 ? InferInput<T> : never;
|
|
21
21
|
type SchemaOutput<T> = T extends StandardSchemaV1 ? InferOutput<T> : never;
|
|
22
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Boundary phase that failed use-case schema validation.
|
|
25
|
+
*/
|
|
23
26
|
export type UseCaseValidationPhase = "input" | "output";
|
|
24
27
|
|
|
25
28
|
/**
|
|
@@ -139,11 +142,20 @@ async function parseSchema<TSchema extends StandardSchemaV1>(
|
|
|
139
142
|
}
|
|
140
143
|
|
|
141
144
|
/**
|
|
142
|
-
*
|
|
143
|
-
*
|
|
145
|
+
* Minimal domain event definition accepted by use-case event helpers.
|
|
146
|
+
*
|
|
147
|
+
* This matches events from `@beignet/core/events` and compatible app-owned
|
|
148
|
+
* definitions without forcing the application builder to depend on one event
|
|
149
|
+
* implementation.
|
|
144
150
|
*/
|
|
145
151
|
export interface DomainEventLike {
|
|
152
|
+
/**
|
|
153
|
+
* Stable event name.
|
|
154
|
+
*/
|
|
146
155
|
name: string;
|
|
156
|
+
/**
|
|
157
|
+
* Standard Schema payload validator.
|
|
158
|
+
*/
|
|
147
159
|
payload: StandardSchema;
|
|
148
160
|
}
|
|
149
161
|
|
|
@@ -157,16 +169,22 @@ export type InferUseCaseEventPayload<E extends DomainEventLike> =
|
|
|
157
169
|
* Minimal recorder shape accepted by use-case event helpers.
|
|
158
170
|
*/
|
|
159
171
|
export interface UseCaseEventRecorderTarget {
|
|
172
|
+
/**
|
|
173
|
+
* Record a domain event payload.
|
|
174
|
+
*/
|
|
160
175
|
record<E extends DomainEventLike>(
|
|
161
176
|
event: E,
|
|
162
177
|
payload: InferUseCaseEventPayload<E>,
|
|
163
|
-
): void;
|
|
178
|
+
): Promise<void> | void;
|
|
164
179
|
}
|
|
165
180
|
|
|
166
181
|
/**
|
|
167
182
|
* Minimal event-bus shape accepted by use-case event helpers.
|
|
168
183
|
*/
|
|
169
184
|
export interface UseCaseEventBusTarget {
|
|
185
|
+
/**
|
|
186
|
+
* Publish a domain event payload.
|
|
187
|
+
*/
|
|
170
188
|
publish<E extends DomainEventLike>(
|
|
171
189
|
event: E,
|
|
172
190
|
payload: InferUseCaseEventPayload<E>,
|
|
@@ -278,7 +296,7 @@ function createUseCaseEventHelpers<Emits extends readonly DomainEventLike[]>(
|
|
|
278
296
|
payload,
|
|
279
297
|
useCaseName,
|
|
280
298
|
);
|
|
281
|
-
recorder.record(declaredEvent, parsed);
|
|
299
|
+
await recorder.record(declaredEvent, parsed);
|
|
282
300
|
},
|
|
283
301
|
async publish(eventBus, event, payload) {
|
|
284
302
|
const declaredEvent = resolveDeclared(event) as typeof event;
|
|
@@ -298,7 +316,10 @@ function createUseCaseEventHelpers<Emits extends readonly DomainEventLike[]>(
|
|
|
298
316
|
export type UseCaseKind = "command" | "query";
|
|
299
317
|
|
|
300
318
|
/**
|
|
301
|
-
*
|
|
319
|
+
* Finalized use case definition.
|
|
320
|
+
*
|
|
321
|
+
* Use cases validate their input before `run(...)` executes and validate their
|
|
322
|
+
* output before returning, unless validation is disabled on the builder.
|
|
302
323
|
*/
|
|
303
324
|
export interface UseCaseDef<
|
|
304
325
|
Ctx,
|
|
@@ -308,13 +329,26 @@ export interface UseCaseDef<
|
|
|
308
329
|
OutputSchema extends StandardSchemaV1,
|
|
309
330
|
Emits extends readonly DomainEventLike[] = readonly [],
|
|
310
331
|
> {
|
|
332
|
+
/**
|
|
333
|
+
* Stable use-case name, usually namespaced by feature.
|
|
334
|
+
*/
|
|
311
335
|
name: Name;
|
|
336
|
+
/**
|
|
337
|
+
* Whether this use case is a command or query.
|
|
338
|
+
*/
|
|
312
339
|
kind: Kind;
|
|
313
340
|
/** Input schema, suitable for reuse in HTTP contracts and forms. */
|
|
314
341
|
inputSchema: InputSchema;
|
|
315
342
|
/** Output schema, suitable for reuse in HTTP contracts and clients. */
|
|
316
343
|
outputSchema: OutputSchema;
|
|
344
|
+
/**
|
|
345
|
+
* Domain events this use case is allowed to record or publish through the
|
|
346
|
+
* scoped `events` helper.
|
|
347
|
+
*/
|
|
317
348
|
emits: Emits;
|
|
349
|
+
/**
|
|
350
|
+
* Execute the use case with application context and typed input.
|
|
351
|
+
*/
|
|
318
352
|
run: (args: {
|
|
319
353
|
ctx: Ctx;
|
|
320
354
|
input: InferInput<InputSchema>;
|
|
@@ -322,19 +356,37 @@ export interface UseCaseDef<
|
|
|
322
356
|
}
|
|
323
357
|
|
|
324
358
|
/**
|
|
325
|
-
* Event passed to the onRun hook for instrumentation
|
|
359
|
+
* Event passed to the `onRun` hook for instrumentation.
|
|
326
360
|
*/
|
|
327
361
|
export interface UseCaseRunEvent<Ctx> {
|
|
362
|
+
/**
|
|
363
|
+
* Use-case name.
|
|
364
|
+
*/
|
|
328
365
|
name: string;
|
|
366
|
+
/**
|
|
367
|
+
* Use-case kind.
|
|
368
|
+
*/
|
|
329
369
|
kind: UseCaseKind;
|
|
370
|
+
/**
|
|
371
|
+
* Execution phase being observed.
|
|
372
|
+
*/
|
|
330
373
|
phase: "start" | "end" | "error";
|
|
374
|
+
/**
|
|
375
|
+
* Elapsed time for end/error events.
|
|
376
|
+
*/
|
|
331
377
|
durationMs?: number;
|
|
378
|
+
/**
|
|
379
|
+
* Error captured for error events.
|
|
380
|
+
*/
|
|
332
381
|
error?: unknown;
|
|
382
|
+
/**
|
|
383
|
+
* Application context used for the run.
|
|
384
|
+
*/
|
|
333
385
|
ctx: Ctx;
|
|
334
386
|
}
|
|
335
387
|
|
|
336
388
|
/**
|
|
337
|
-
* Options for createUseCase
|
|
389
|
+
* Options for `createUseCase(...)`.
|
|
338
390
|
*/
|
|
339
391
|
export interface CreateUseCaseOptions<Ctx> {
|
|
340
392
|
/**
|
|
@@ -599,6 +651,9 @@ export interface UseCaseBuilderRoot<Ctx> {
|
|
|
599
651
|
): UseCaseBuilder<Ctx, Name, "query", undefined, undefined, readonly []>;
|
|
600
652
|
}
|
|
601
653
|
|
|
654
|
+
/**
|
|
655
|
+
* Infer the application context type from a finalized use case.
|
|
656
|
+
*/
|
|
602
657
|
export type UseCaseContext<TUseCase> = TUseCase extends {
|
|
603
658
|
run: (args: {
|
|
604
659
|
ctx: infer Ctx;
|
|
@@ -608,6 +663,9 @@ export type UseCaseContext<TUseCase> = TUseCase extends {
|
|
|
608
663
|
? Ctx
|
|
609
664
|
: never;
|
|
610
665
|
|
|
666
|
+
/**
|
|
667
|
+
* Infer the public input type accepted by a finalized use case.
|
|
668
|
+
*/
|
|
611
669
|
export type UseCaseInput<TUseCase> = TUseCase extends {
|
|
612
670
|
run: (args: {
|
|
613
671
|
ctx: infer _Ctx;
|
|
@@ -617,6 +675,9 @@ export type UseCaseInput<TUseCase> = TUseCase extends {
|
|
|
617
675
|
? Input
|
|
618
676
|
: never;
|
|
619
677
|
|
|
678
|
+
/**
|
|
679
|
+
* Infer the public output type returned by a finalized use case.
|
|
680
|
+
*/
|
|
620
681
|
export type UseCaseOutput<TUseCase> = TUseCase extends {
|
|
621
682
|
run: (args: {
|
|
622
683
|
ctx: infer _Ctx;
|
|
@@ -628,6 +689,9 @@ export type UseCaseOutput<TUseCase> = TUseCase extends {
|
|
|
628
689
|
|
|
629
690
|
type MaybePromise<T> = T | Promise<T>;
|
|
630
691
|
|
|
692
|
+
/**
|
|
693
|
+
* Small test harness for running use cases with typed inputs.
|
|
694
|
+
*/
|
|
631
695
|
export interface UseCaseTester<Ctx> {
|
|
632
696
|
/**
|
|
633
697
|
* Create a fresh test context.
|
|
@@ -677,30 +741,29 @@ export function createUseCaseTester<Ctx>(
|
|
|
677
741
|
};
|
|
678
742
|
}
|
|
679
743
|
|
|
744
|
+
/** Empty emits array used as default. */
|
|
745
|
+
const EMPTY_EMITS = [] as const;
|
|
746
|
+
|
|
680
747
|
/**
|
|
681
748
|
* Create a use case builder with a specific context type.
|
|
682
749
|
*
|
|
750
|
+
* Create this once in app code, usually in `lib/use-case.ts`, then import that
|
|
751
|
+
* configured builder from feature use-case modules.
|
|
752
|
+
*
|
|
683
753
|
* @example
|
|
684
754
|
* ```ts
|
|
685
|
-
*
|
|
686
|
-
* import type { AppCtx } from "./ctx";
|
|
687
|
-
*
|
|
688
|
-
* export const useCase = createUseCase<AppCtx>();
|
|
755
|
+
* export const useCase = createUseCase<AppContext>();
|
|
689
756
|
*
|
|
690
757
|
* export const createTodo = useCase
|
|
691
758
|
* .command("todos.create")
|
|
692
759
|
* .input(CreateTodoInput)
|
|
693
760
|
* .output(CreateTodoOutput)
|
|
694
|
-
* .run(async ({ ctx, input }) =>
|
|
695
|
-
* const todo = await ctx.ports.db.todos.create(input);
|
|
696
|
-
* return { id: todo.id, title: todo.title };
|
|
697
|
-
* });
|
|
761
|
+
* .run(async ({ ctx, input }) => ctx.ports.todos.create(input));
|
|
698
762
|
* ```
|
|
763
|
+
*
|
|
764
|
+
* @param options - Optional instrumentation and validation configuration.
|
|
765
|
+
* @returns A root builder for command and query use cases.
|
|
699
766
|
*/
|
|
700
|
-
|
|
701
|
-
/** Empty emits array used as default */
|
|
702
|
-
const EMPTY_EMITS = [] as const;
|
|
703
|
-
|
|
704
767
|
export function createUseCase<Ctx>(
|
|
705
768
|
options?: CreateUseCaseOptions<Ctx>,
|
|
706
769
|
): UseCaseBuilderRoot<Ctx> {
|
package/src/client/client.ts
CHANGED
|
@@ -23,23 +23,38 @@ import type {
|
|
|
23
23
|
InferSuccessResponse,
|
|
24
24
|
} from "./types";
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Source category for a `ContractError`.
|
|
28
|
+
*/
|
|
26
29
|
export type ContractErrorSource = "http" | "client" | "network" | "contract";
|
|
27
30
|
|
|
31
|
+
/**
|
|
32
|
+
* Narrow a contract error union by source.
|
|
33
|
+
*/
|
|
28
34
|
export type ContractErrorWithSource<
|
|
29
35
|
TError,
|
|
30
36
|
TSource extends ContractErrorSource,
|
|
31
37
|
> = Extract<TError, { readonly source: TSource }>;
|
|
32
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Narrow a contract error union by HTTP status.
|
|
41
|
+
*/
|
|
33
42
|
export type ContractErrorWithStatus<TError, TStatus extends number> = Extract<
|
|
34
43
|
TError,
|
|
35
44
|
{ readonly status: TStatus }
|
|
36
45
|
>;
|
|
37
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Narrow a contract error union by Beignet error code.
|
|
49
|
+
*/
|
|
38
50
|
export type ContractErrorWithCode<TError, TCode extends string> = Extract<
|
|
39
51
|
TError,
|
|
40
52
|
{ readonly code: TCode }
|
|
41
53
|
>;
|
|
42
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Error for a non-2xx HTTP response.
|
|
57
|
+
*/
|
|
43
58
|
export type HttpContractError<
|
|
44
59
|
TBody = unknown,
|
|
45
60
|
TStatus extends number = number,
|
|
@@ -49,6 +64,9 @@ export type HttpContractError<
|
|
|
49
64
|
readonly response: Response;
|
|
50
65
|
};
|
|
51
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Error created by the client before a network request is made.
|
|
69
|
+
*/
|
|
52
70
|
export type ClientContractError = ContractError<
|
|
53
71
|
undefined,
|
|
54
72
|
undefined,
|
|
@@ -59,6 +77,9 @@ export type ClientContractError = ContractError<
|
|
|
59
77
|
readonly response: undefined;
|
|
60
78
|
};
|
|
61
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Error created when the network request itself fails.
|
|
82
|
+
*/
|
|
62
83
|
export type NetworkContractError = ContractError<
|
|
63
84
|
undefined,
|
|
64
85
|
undefined,
|
|
@@ -69,6 +90,9 @@ export type NetworkContractError = ContractError<
|
|
|
69
90
|
readonly response: undefined;
|
|
70
91
|
};
|
|
71
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Error created when a response violates the contract.
|
|
95
|
+
*/
|
|
72
96
|
export type ResponseContractError<
|
|
73
97
|
TBody = unknown,
|
|
74
98
|
TStatus extends number | undefined = number | undefined,
|
|
@@ -77,6 +101,9 @@ export type ResponseContractError<
|
|
|
77
101
|
readonly status: TStatus;
|
|
78
102
|
};
|
|
79
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Union of all Beignet client error variants.
|
|
106
|
+
*/
|
|
80
107
|
export type AnyContractError =
|
|
81
108
|
| HttpContractError
|
|
82
109
|
| ClientContractError
|
|
@@ -118,9 +145,15 @@ type EndpointCatalogContractError<TContract extends HttpContractConfig> =
|
|
|
118
145
|
: never
|
|
119
146
|
: never;
|
|
120
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Infer route-owned error catalog codes declared by a contract.
|
|
150
|
+
*/
|
|
121
151
|
export type InferEndpointErrorCode<TContract extends HttpContractConfig> =
|
|
122
152
|
EndpointCatalogErrorDefinition<TContract>["code"];
|
|
123
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Infer the full typed error union for a contract endpoint.
|
|
156
|
+
*/
|
|
124
157
|
export type InferEndpointContractError<TContract extends HttpContractConfig> =
|
|
125
158
|
| EndpointCatalogContractError<TContract>
|
|
126
159
|
| {
|
|
@@ -138,18 +171,39 @@ export type InferEndpointContractError<TContract extends HttpContractConfig> =
|
|
|
138
171
|
>;
|
|
139
172
|
|
|
140
173
|
/**
|
|
141
|
-
*
|
|
174
|
+
* Error thrown by Beignet contract clients.
|
|
175
|
+
*
|
|
176
|
+
* `source` distinguishes HTTP error responses, client-side request mistakes,
|
|
177
|
+
* network failures, and contract drift such as response validation failures.
|
|
142
178
|
*/
|
|
143
179
|
export class ContractError<
|
|
144
180
|
TBody = unknown,
|
|
145
181
|
TStatus extends number | undefined = number | undefined,
|
|
146
182
|
TSource extends ContractErrorSource = ContractErrorSource,
|
|
147
183
|
> extends Error {
|
|
184
|
+
/**
|
|
185
|
+
* Error source category.
|
|
186
|
+
*/
|
|
148
187
|
readonly source: TSource;
|
|
188
|
+
/**
|
|
189
|
+
* HTTP status when a response was available.
|
|
190
|
+
*/
|
|
149
191
|
readonly status: TStatus;
|
|
192
|
+
/**
|
|
193
|
+
* Stable error code.
|
|
194
|
+
*/
|
|
150
195
|
readonly code?: string;
|
|
196
|
+
/**
|
|
197
|
+
* Parsed response body when available.
|
|
198
|
+
*/
|
|
151
199
|
readonly body?: TBody;
|
|
200
|
+
/**
|
|
201
|
+
* Structured error details when available.
|
|
202
|
+
*/
|
|
152
203
|
readonly details?: unknown;
|
|
204
|
+
/**
|
|
205
|
+
* Native fetch response when available.
|
|
206
|
+
*/
|
|
153
207
|
readonly response?: Response;
|
|
154
208
|
override cause?: unknown;
|
|
155
209
|
|
|
@@ -175,7 +229,7 @@ export class ContractError<
|
|
|
175
229
|
}
|
|
176
230
|
|
|
177
231
|
/**
|
|
178
|
-
* Check
|
|
232
|
+
* Check whether this error has a specific HTTP status code.
|
|
179
233
|
*/
|
|
180
234
|
hasStatus<S extends number>(
|
|
181
235
|
status: S,
|
|
@@ -184,7 +238,7 @@ export class ContractError<
|
|
|
184
238
|
}
|
|
185
239
|
|
|
186
240
|
/**
|
|
187
|
-
* Check
|
|
241
|
+
* Check whether this error came from a specific source.
|
|
188
242
|
*/
|
|
189
243
|
hasSource<S extends ContractErrorSource>(
|
|
190
244
|
source: S,
|
|
@@ -193,7 +247,7 @@ export class ContractError<
|
|
|
193
247
|
}
|
|
194
248
|
|
|
195
249
|
/**
|
|
196
|
-
* Check
|
|
250
|
+
* Check whether this error has a specific error code.
|
|
197
251
|
*/
|
|
198
252
|
hasCode<C extends string>(code: C): this is this & { code: C } {
|
|
199
253
|
return this.code === code;
|
|
@@ -407,7 +461,7 @@ type PathParams = Record<string, PrimitiveParam>;
|
|
|
407
461
|
type QueryParams = Record<string, QueryParamValue>;
|
|
408
462
|
|
|
409
463
|
/**
|
|
410
|
-
*
|
|
464
|
+
* Typed client endpoint for one contract.
|
|
411
465
|
*/
|
|
412
466
|
export class Endpoint<
|
|
413
467
|
TContract extends HttpContractConfig,
|
|
@@ -418,6 +472,9 @@ export class Endpoint<
|
|
|
418
472
|
private config: ClientConfig<TProvidedHeaders>,
|
|
419
473
|
) {}
|
|
420
474
|
|
|
475
|
+
/**
|
|
476
|
+
* Check whether an unknown error is a `ContractError` for this endpoint.
|
|
477
|
+
*/
|
|
421
478
|
isError(err: unknown): err is InferEndpointContractError<TContract>;
|
|
422
479
|
isError<S extends InferEndpointErrorStatus<TContract>>(
|
|
423
480
|
err: unknown,
|
|
@@ -497,7 +554,10 @@ export class Endpoint<
|
|
|
497
554
|
}
|
|
498
555
|
|
|
499
556
|
/**
|
|
500
|
-
* Call the endpoint
|
|
557
|
+
* Call the endpoint and return the parsed success body.
|
|
558
|
+
*
|
|
559
|
+
* Throws `ContractError` when the request fails or the response violates the
|
|
560
|
+
* contract.
|
|
501
561
|
*/
|
|
502
562
|
async call(
|
|
503
563
|
...callArgs: CallArgs<TContract, TProvidedHeaders>
|
|
@@ -510,7 +570,8 @@ export class Endpoint<
|
|
|
510
570
|
}
|
|
511
571
|
|
|
512
572
|
/**
|
|
513
|
-
* Call the endpoint and return a typed result instead of throwing
|
|
573
|
+
* Call the endpoint and return a typed result instead of throwing
|
|
574
|
+
* `ContractError`.
|
|
514
575
|
*/
|
|
515
576
|
async safeCall(
|
|
516
577
|
...callArgs: CallArgs<TContract, TProvidedHeaders>
|
|
@@ -951,16 +1012,16 @@ export class Endpoint<
|
|
|
951
1012
|
}
|
|
952
1013
|
|
|
953
1014
|
/**
|
|
954
|
-
* Client for making contract-based requests
|
|
1015
|
+
* Client for making contract-based requests.
|
|
955
1016
|
*/
|
|
956
1017
|
export class Client<TProvidedHeaders extends string = never> {
|
|
957
1018
|
constructor(private config: ClientConfig<TProvidedHeaders>) {}
|
|
958
1019
|
|
|
959
1020
|
/**
|
|
960
|
-
* Create an endpoint wrapper for a contract
|
|
1021
|
+
* Create an endpoint wrapper for a contract.
|
|
961
1022
|
*
|
|
962
|
-
* Accepts either
|
|
963
|
-
*
|
|
1023
|
+
* Accepts either a plain `HttpContractConfig` or a builder with a `.config`
|
|
1024
|
+
* property.
|
|
964
1025
|
*/
|
|
965
1026
|
endpoint<TContractLike extends ContractLike>(
|
|
966
1027
|
contract: TContractLike,
|
|
@@ -971,7 +1032,7 @@ export class Client<TProvidedHeaders extends string = never> {
|
|
|
971
1032
|
}
|
|
972
1033
|
|
|
973
1034
|
/**
|
|
974
|
-
* Create a configured client
|
|
1035
|
+
* Create a configured Beignet client.
|
|
975
1036
|
*/
|
|
976
1037
|
export function createClient<const TProvidedHeaders extends string = never>(
|
|
977
1038
|
config: ClientConfig<TProvidedHeaders> = {},
|
package/src/client/index.ts
CHANGED
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
* Base HTTP client for making contract-based requests
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Schema validation error re-exported for client users.
|
|
9
|
+
*/
|
|
7
10
|
export { SchemaValidationError } from "../errors";
|
|
11
|
+
/**
|
|
12
|
+
* Contract client error types.
|
|
13
|
+
*/
|
|
8
14
|
export type {
|
|
9
15
|
AnyContractError,
|
|
10
16
|
ClientContractError,
|
|
@@ -18,6 +24,9 @@ export type {
|
|
|
18
24
|
NetworkContractError,
|
|
19
25
|
ResponseContractError,
|
|
20
26
|
} from "./client";
|
|
27
|
+
/**
|
|
28
|
+
* Contract client runtime helpers.
|
|
29
|
+
*/
|
|
21
30
|
export {
|
|
22
31
|
Client,
|
|
23
32
|
ContractError,
|
|
@@ -25,6 +34,9 @@ export {
|
|
|
25
34
|
Endpoint,
|
|
26
35
|
isContractError,
|
|
27
36
|
} from "./client";
|
|
37
|
+
/**
|
|
38
|
+
* Contract client inference and call types.
|
|
39
|
+
*/
|
|
28
40
|
export type {
|
|
29
41
|
CallArgs,
|
|
30
42
|
ClientConfig,
|
package/src/client/types.ts
CHANGED
|
@@ -10,12 +10,24 @@ import type {
|
|
|
10
10
|
import type { ErrorResponseBody } from "../errors";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
13
|
+
* Configuration for a Beignet contract client.
|
|
14
14
|
*/
|
|
15
15
|
export type ClientConfig<TProvidedHeaders extends string = never> = {
|
|
16
|
+
/**
|
|
17
|
+
* Base URL prepended to all endpoint paths.
|
|
18
|
+
*/
|
|
16
19
|
baseUrl?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Static or async headers applied to every request.
|
|
22
|
+
*/
|
|
17
23
|
headers?: () => Promise<Record<string, string>> | Record<string, string>;
|
|
24
|
+
/**
|
|
25
|
+
* Header keys supplied by global `headers`, making them optional per call.
|
|
26
|
+
*/
|
|
18
27
|
providedHeaders?: readonly TProvidedHeaders[];
|
|
28
|
+
/**
|
|
29
|
+
* Fetch implementation. Defaults to global `fetch`.
|
|
30
|
+
*/
|
|
19
31
|
fetch?: typeof fetch;
|
|
20
32
|
/** Enable client-side input validation for path, query, and body. Default: false. */
|
|
21
33
|
validate?: boolean;
|
|
@@ -56,7 +68,7 @@ type InferPathParamsFromTemplate<TPath extends string> = [
|
|
|
56
68
|
: { [K in PathParamNames<TPath>]: PathParamPrimitive };
|
|
57
69
|
|
|
58
70
|
/**
|
|
59
|
-
* Infer path params from contract
|
|
71
|
+
* Infer path params accepted by a client call from a contract.
|
|
60
72
|
*/
|
|
61
73
|
export type InferPathParams<TContract extends HttpContractConfig> =
|
|
62
74
|
TContract["pathParams"] extends StandardSchemaV1
|
|
@@ -64,13 +76,16 @@ export type InferPathParams<TContract extends HttpContractConfig> =
|
|
|
64
76
|
: InferPathParamsFromTemplate<TContract["path"]>;
|
|
65
77
|
|
|
66
78
|
/**
|
|
67
|
-
* Infer query params from contract
|
|
79
|
+
* Infer query params accepted by a client call from a contract.
|
|
68
80
|
*/
|
|
69
81
|
export type InferQuery<TContract extends HttpContractConfig> =
|
|
70
82
|
TContract["query"] extends StandardSchemaV1
|
|
71
83
|
? InferInput<TContract["query"]>
|
|
72
84
|
: undefined;
|
|
73
85
|
|
|
86
|
+
/**
|
|
87
|
+
* Infer request headers accepted by a client call from a contract.
|
|
88
|
+
*/
|
|
74
89
|
export type InferHeaders<TContract extends HttpContractConfig> =
|
|
75
90
|
InferHeaderSchemaInput<
|
|
76
91
|
Exclude<TContract["headers"], undefined>
|
|
@@ -79,7 +94,7 @@ export type InferHeaders<TContract extends HttpContractConfig> =
|
|
|
79
94
|
: InferHeaderSchemaInput<Exclude<TContract["headers"], undefined>>;
|
|
80
95
|
|
|
81
96
|
/**
|
|
82
|
-
* Infer body from contract
|
|
97
|
+
* Infer JSON request body accepted by a client call from a contract.
|
|
83
98
|
*/
|
|
84
99
|
export type InferBody<TContract extends HttpContractConfig> =
|
|
85
100
|
TContract["method"] extends BodyHttpMethod
|
|
@@ -135,7 +150,9 @@ type InferResponseType<T> = T extends null
|
|
|
135
150
|
: unknown;
|
|
136
151
|
|
|
137
152
|
/**
|
|
138
|
-
* Infer success response from contract
|
|
153
|
+
* Infer success response body from a contract.
|
|
154
|
+
*
|
|
155
|
+
* This is the union of all declared 2xx response schemas.
|
|
139
156
|
*/
|
|
140
157
|
export type InferSuccessResponse<TContract extends HttpContractConfig> =
|
|
141
158
|
TContract["responses"] extends Partial<Record<number, StandardSchema | null>>
|
|
@@ -147,7 +164,9 @@ export type InferSuccessResponse<TContract extends HttpContractConfig> =
|
|
|
147
164
|
: unknown;
|
|
148
165
|
|
|
149
166
|
/**
|
|
150
|
-
* Infer error response from contract
|
|
167
|
+
* Infer declared route-owned error response bodies from a contract.
|
|
168
|
+
*
|
|
169
|
+
* This is the union of all declared non-2xx response schemas.
|
|
151
170
|
*/
|
|
152
171
|
export type InferErrorResponse<TContract extends HttpContractConfig> =
|
|
153
172
|
TContract["responses"] extends Partial<Record<number, StandardSchema | null>>
|
|
@@ -201,27 +220,57 @@ export type InferEndpointErrorResponse<TContract extends HttpContractConfig> =
|
|
|
201
220
|
| string
|
|
202
221
|
| undefined;
|
|
203
222
|
|
|
223
|
+
/**
|
|
224
|
+
* Successful endpoint result returned by `safeCall(...)`.
|
|
225
|
+
*/
|
|
204
226
|
export type EndpointSuccessResult<TContract extends HttpContractConfig> = {
|
|
227
|
+
/**
|
|
228
|
+
* Success discriminator.
|
|
229
|
+
*/
|
|
205
230
|
ok: true;
|
|
231
|
+
/**
|
|
232
|
+
* HTTP status code.
|
|
233
|
+
*/
|
|
206
234
|
status: number;
|
|
235
|
+
/**
|
|
236
|
+
* Parsed response body.
|
|
237
|
+
*/
|
|
207
238
|
data: InferSuccessResponse<TContract>;
|
|
239
|
+
/**
|
|
240
|
+
* Native fetch response.
|
|
241
|
+
*/
|
|
208
242
|
response: Response;
|
|
209
243
|
};
|
|
210
244
|
|
|
245
|
+
/**
|
|
246
|
+
* Failed endpoint result returned by `safeCall(...)`.
|
|
247
|
+
*/
|
|
211
248
|
export type EndpointErrorResult<TError> = {
|
|
249
|
+
/**
|
|
250
|
+
* Failure discriminator.
|
|
251
|
+
*/
|
|
212
252
|
ok: false;
|
|
253
|
+
/**
|
|
254
|
+
* HTTP status code when a response was available.
|
|
255
|
+
*/
|
|
213
256
|
status?: number;
|
|
257
|
+
/**
|
|
258
|
+
* Contract client error.
|
|
259
|
+
*/
|
|
214
260
|
error: TError;
|
|
261
|
+
/**
|
|
262
|
+
* Native fetch response when one was available.
|
|
263
|
+
*/
|
|
215
264
|
response?: Response;
|
|
216
265
|
};
|
|
217
266
|
|
|
267
|
+
/**
|
|
268
|
+
* Result union returned by `safeCall(...)`.
|
|
269
|
+
*/
|
|
218
270
|
export type EndpointResult<TContract extends HttpContractConfig, TError> =
|
|
219
271
|
| EndpointSuccessResult<TContract>
|
|
220
272
|
| EndpointErrorResult<TError>;
|
|
221
273
|
|
|
222
|
-
/**
|
|
223
|
-
* Call arguments for an endpoint
|
|
224
|
-
*/
|
|
225
274
|
type RequestArg<
|
|
226
275
|
Name extends "path" | "query" | "body",
|
|
227
276
|
Value,
|
|
@@ -275,6 +324,9 @@ type HeadersArgs<
|
|
|
275
324
|
? { headers?: HeaderInput<TContract, TProvidedHeaders> }
|
|
276
325
|
: { headers: HeaderInput<TContract, TProvidedHeaders> };
|
|
277
326
|
|
|
327
|
+
/**
|
|
328
|
+
* Arguments accepted by an endpoint call for a specific contract.
|
|
329
|
+
*/
|
|
278
330
|
export type EndpointCallArgs<
|
|
279
331
|
TContract extends HttpContractConfig,
|
|
280
332
|
TProvidedHeaders extends string = never,
|
|
@@ -289,6 +341,9 @@ export type EndpointCallArgs<
|
|
|
289
341
|
* and is not validated or JSON-serialized.
|
|
290
342
|
*/
|
|
291
343
|
rawBody?: TContract["method"] extends BodyHttpMethod ? BodyInit : never;
|
|
344
|
+
/**
|
|
345
|
+
* Abort signal passed to fetch.
|
|
346
|
+
*/
|
|
292
347
|
signal?: AbortSignal;
|
|
293
348
|
} & HeadersArgs<TContract, TProvidedHeaders>;
|
|
294
349
|
|
|
@@ -296,6 +351,12 @@ type HasRequiredKeys<T> = {
|
|
|
296
351
|
[K in keyof T]-?: Record<string, never> extends Pick<T, K> ? never : K;
|
|
297
352
|
}[keyof T];
|
|
298
353
|
|
|
354
|
+
/**
|
|
355
|
+
* Tuple type used by endpoint calls.
|
|
356
|
+
*
|
|
357
|
+
* The args object is optional only when the contract has no required path,
|
|
358
|
+
* query, body, or header input.
|
|
359
|
+
*/
|
|
299
360
|
export type CallArgs<
|
|
300
361
|
TContract extends HttpContractConfig,
|
|
301
362
|
TProvidedHeaders extends string = never,
|