@beignet/core 0.0.1
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 +5 -0
- package/README.md +288 -0
- package/dist/application/index.d.ts +260 -0
- package/dist/application/index.d.ts.map +1 -0
- package/dist/application/index.js +324 -0
- package/dist/application/index.js.map +1 -0
- package/dist/client/client.d.ts +241 -0
- package/dist/client/client.d.ts.map +1 -0
- package/dist/client/client.js +531 -0
- package/dist/client/client.js.map +1 -0
- package/dist/client/index.d.ts +10 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +8 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/types.d.ts +139 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +2 -0
- package/dist/client/types.js.map +1 -0
- package/dist/config/index.d.ts +122 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +216 -0
- package/dist/config/index.js.map +1 -0
- package/dist/contracts/contract-builder.d.ts +121 -0
- package/dist/contracts/contract-builder.d.ts.map +1 -0
- package/dist/contracts/contract-builder.js +346 -0
- package/dist/contracts/contract-builder.js.map +1 -0
- package/dist/contracts/contract-group.d.ts +106 -0
- package/dist/contracts/contract-group.d.ts.map +1 -0
- package/dist/contracts/contract-group.js +240 -0
- package/dist/contracts/contract-group.js.map +1 -0
- package/dist/contracts/contract-like.d.ts +21 -0
- package/dist/contracts/contract-like.d.ts.map +1 -0
- package/dist/contracts/contract-like.js +9 -0
- package/dist/contracts/contract-like.js.map +1 -0
- package/dist/contracts/index.d.ts +15 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +11 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/openapi-meta.d.ts +23 -0
- package/dist/contracts/openapi-meta.d.ts.map +1 -0
- package/dist/contracts/openapi-meta.js +2 -0
- package/dist/contracts/openapi-meta.js.map +1 -0
- package/dist/contracts/path-template.d.ts +17 -0
- package/dist/contracts/path-template.d.ts.map +1 -0
- package/dist/contracts/path-template.js +50 -0
- package/dist/contracts/path-template.js.map +1 -0
- package/dist/contracts/rate-limit.d.ts +50 -0
- package/dist/contracts/rate-limit.d.ts.map +1 -0
- package/dist/contracts/rate-limit.js +2 -0
- package/dist/contracts/rate-limit.js.map +1 -0
- package/dist/contracts/types.d.ts +97 -0
- package/dist/contracts/types.d.ts.map +1 -0
- package/dist/contracts/types.js +54 -0
- package/dist/contracts/types.js.map +1 -0
- package/dist/contracts/utils.d.ts +3 -0
- package/dist/contracts/utils.d.ts.map +1 -0
- package/dist/contracts/utils.js +44 -0
- package/dist/contracts/utils.js.map +1 -0
- package/dist/domain/entity.d.ts +87 -0
- package/dist/domain/entity.d.ts.map +1 -0
- package/dist/domain/entity.js +155 -0
- package/dist/domain/entity.js.map +1 -0
- package/dist/domain/events.d.ts +41 -0
- package/dist/domain/events.d.ts.map +1 -0
- package/dist/domain/events.js +21 -0
- package/dist/domain/events.js.map +1 -0
- package/dist/domain/index.d.ts +14 -0
- package/dist/domain/index.d.ts.map +1 -0
- package/dist/domain/index.js +14 -0
- package/dist/domain/index.js.map +1 -0
- package/dist/domain/value-object.d.ts +60 -0
- package/dist/domain/value-object.d.ts.map +1 -0
- package/dist/domain/value-object.js +87 -0
- package/dist/domain/value-object.js.map +1 -0
- package/dist/errors/catalog.d.ts +71 -0
- package/dist/errors/catalog.d.ts.map +1 -0
- package/dist/errors/catalog.js +71 -0
- package/dist/errors/catalog.js.map +1 -0
- package/dist/errors/http.d.ts +77 -0
- package/dist/errors/http.d.ts.map +1 -0
- package/dist/errors/http.js +74 -0
- package/dist/errors/http.js.map +1 -0
- package/dist/errors/index.d.ts +10 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +14 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/errors/response.d.ts +26 -0
- package/dist/errors/response.d.ts.map +1 -0
- package/dist/errors/response.js +34 -0
- package/dist/errors/response.js.map +1 -0
- package/dist/errors/validation.d.ts +18 -0
- package/dist/errors/validation.d.ts.map +1 -0
- package/dist/errors/validation.js +21 -0
- package/dist/errors/validation.js.map +1 -0
- package/dist/events/index.d.ts +58 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +102 -0
- package/dist/events/index.js.map +1 -0
- package/dist/jobs/index.d.ts +56 -0
- package/dist/jobs/index.d.ts.map +1 -0
- package/dist/jobs/index.js +89 -0
- package/dist/jobs/index.js.map +1 -0
- package/dist/mail/index.d.ts +75 -0
- package/dist/mail/index.d.ts.map +1 -0
- package/dist/mail/index.js +84 -0
- package/dist/mail/index.js.map +1 -0
- package/dist/openapi/index.d.ts +207 -0
- package/dist/openapi/index.d.ts.map +1 -0
- package/dist/openapi/index.js +449 -0
- package/dist/openapi/index.js.map +1 -0
- package/dist/openapi/schema-introspector.d.ts +38 -0
- package/dist/openapi/schema-introspector.d.ts.map +1 -0
- package/dist/openapi/schema-introspector.js +67 -0
- package/dist/openapi/schema-introspector.js.map +1 -0
- package/dist/ports/audit.d.ts +58 -0
- package/dist/ports/audit.d.ts.map +1 -0
- package/dist/ports/audit.js +74 -0
- package/dist/ports/audit.js.map +1 -0
- package/dist/ports/auth.d.ts +23 -0
- package/dist/ports/auth.d.ts.map +1 -0
- package/dist/ports/auth.js +31 -0
- package/dist/ports/auth.js.map +1 -0
- package/dist/ports/builder.d.ts +61 -0
- package/dist/ports/builder.d.ts.map +1 -0
- package/dist/ports/builder.js +48 -0
- package/dist/ports/builder.js.map +1 -0
- package/dist/ports/cache.d.ts +15 -0
- package/dist/ports/cache.d.ts.map +1 -0
- package/dist/ports/cache.js +57 -0
- package/dist/ports/cache.js.map +1 -0
- package/dist/ports/clock.d.ts +10 -0
- package/dist/ports/clock.d.ts.map +1 -0
- package/dist/ports/clock.js +21 -0
- package/dist/ports/clock.js.map +1 -0
- package/dist/ports/events.d.ts +71 -0
- package/dist/ports/events.d.ts.map +1 -0
- package/dist/ports/events.js +2 -0
- package/dist/ports/events.js.map +1 -0
- package/dist/ports/id-generator.d.ts +12 -0
- package/dist/ports/id-generator.d.ts.map +1 -0
- package/dist/ports/id-generator.js +22 -0
- package/dist/ports/id-generator.js.map +1 -0
- package/dist/ports/index.d.ts +98 -0
- package/dist/ports/index.d.ts.map +1 -0
- package/dist/ports/index.js +67 -0
- package/dist/ports/index.js.map +1 -0
- package/dist/ports/logger.d.ts +22 -0
- package/dist/ports/logger.d.ts.map +1 -0
- package/dist/ports/logger.js +34 -0
- package/dist/ports/logger.js.map +1 -0
- package/dist/ports/mailer.d.ts +6 -0
- package/dist/ports/mailer.d.ts.map +1 -0
- package/dist/ports/mailer.js +2 -0
- package/dist/ports/mailer.js.map +1 -0
- package/dist/ports/policy.d.ts +53 -0
- package/dist/ports/policy.d.ts.map +1 -0
- package/dist/ports/policy.js +81 -0
- package/dist/ports/policy.js.map +1 -0
- package/dist/ports/rate-limit.d.ts +41 -0
- package/dist/ports/rate-limit.d.ts.map +1 -0
- package/dist/ports/rate-limit.js +37 -0
- package/dist/ports/rate-limit.js.map +1 -0
- package/dist/ports/redaction.d.ts +26 -0
- package/dist/ports/redaction.d.ts.map +1 -0
- package/dist/ports/redaction.js +126 -0
- package/dist/ports/redaction.js.map +1 -0
- package/dist/ports/schedules.d.ts +9 -0
- package/dist/ports/schedules.d.ts.map +1 -0
- package/dist/ports/schedules.js +2 -0
- package/dist/ports/schedules.js.map +1 -0
- package/dist/ports/storage.d.ts +47 -0
- package/dist/ports/storage.d.ts.map +1 -0
- package/dist/ports/storage.js +185 -0
- package/dist/ports/storage.js.map +1 -0
- package/dist/ports/testing.d.ts +73 -0
- package/dist/ports/testing.d.ts.map +1 -0
- package/dist/ports/testing.js +105 -0
- package/dist/ports/testing.js.map +1 -0
- package/dist/ports/unit-of-work.d.ts +56 -0
- package/dist/ports/unit-of-work.d.ts.map +1 -0
- package/dist/ports/unit-of-work.js +64 -0
- package/dist/ports/unit-of-work.js.map +1 -0
- package/dist/providers/index.d.ts +8 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +8 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/instrumentation.d.ts +91 -0
- package/dist/providers/instrumentation.d.ts.map +1 -0
- package/dist/providers/instrumentation.js +93 -0
- package/dist/providers/instrumentation.js.map +1 -0
- package/dist/providers/provider.d.ts +146 -0
- package/dist/providers/provider.d.ts.map +1 -0
- package/dist/providers/provider.js +31 -0
- package/dist/providers/provider.js.map +1 -0
- package/dist/schedules/index.d.ts +105 -0
- package/dist/schedules/index.d.ts.map +1 -0
- package/dist/schedules/index.js +178 -0
- package/dist/schedules/index.js.map +1 -0
- package/dist/server/contract-like.d.ts +5 -0
- package/dist/server/contract-like.d.ts.map +1 -0
- package/dist/server/contract-like.js +5 -0
- package/dist/server/contract-like.js.map +1 -0
- package/dist/server/health.d.ts +41 -0
- package/dist/server/health.d.ts.map +1 -0
- package/dist/server/health.js +46 -0
- package/dist/server/health.js.map +1 -0
- package/dist/server/hooks/auth.d.ts +42 -0
- package/dist/server/hooks/auth.d.ts.map +1 -0
- package/dist/server/hooks/auth.js +61 -0
- package/dist/server/hooks/auth.js.map +1 -0
- package/dist/server/hooks/cors.d.ts +13 -0
- package/dist/server/hooks/cors.d.ts.map +1 -0
- package/dist/server/hooks/cors.js +70 -0
- package/dist/server/hooks/cors.js.map +1 -0
- package/dist/server/hooks/errors.d.ts +66 -0
- package/dist/server/hooks/errors.d.ts.map +1 -0
- package/dist/server/hooks/errors.js +83 -0
- package/dist/server/hooks/errors.js.map +1 -0
- package/dist/server/hooks/index.d.ts +12 -0
- package/dist/server/hooks/index.d.ts.map +1 -0
- package/dist/server/hooks/index.js +12 -0
- package/dist/server/hooks/index.js.map +1 -0
- package/dist/server/hooks/logging.d.ts +33 -0
- package/dist/server/hooks/logging.d.ts.map +1 -0
- package/dist/server/hooks/logging.js +90 -0
- package/dist/server/hooks/logging.js.map +1 -0
- package/dist/server/hooks/rate-limit.d.ts +29 -0
- package/dist/server/hooks/rate-limit.d.ts.map +1 -0
- package/dist/server/hooks/rate-limit.js +93 -0
- package/dist/server/hooks/rate-limit.js.map +1 -0
- package/dist/server/hooks/utils.d.ts +9 -0
- package/dist/server/hooks/utils.d.ts.map +1 -0
- package/dist/server/hooks/utils.js +16 -0
- package/dist/server/hooks/utils.js.map +1 -0
- package/dist/server/hooks.d.ts +2 -0
- package/dist/server/hooks.d.ts.map +1 -0
- package/dist/server/hooks.js +2 -0
- package/dist/server/hooks.js.map +1 -0
- package/dist/server/http.d.ts +124 -0
- package/dist/server/http.d.ts.map +1 -0
- package/dist/server/http.js +2 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/index.d.ts +19 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +15 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/openapi.d.ts +32 -0
- package/dist/server/openapi.d.ts.map +1 -0
- package/dist/server/openapi.js +43 -0
- package/dist/server/openapi.js.map +1 -0
- package/dist/server/providers/index.d.ts +4 -0
- package/dist/server/providers/index.d.ts.map +1 -0
- package/dist/server/providers/index.js +4 -0
- package/dist/server/providers/index.js.map +1 -0
- package/dist/server/providers/loadProviderConfig.d.ts +7 -0
- package/dist/server/providers/loadProviderConfig.d.ts.map +1 -0
- package/dist/server/providers/loadProviderConfig.js +42 -0
- package/dist/server/providers/loadProviderConfig.js.map +1 -0
- package/dist/server/server.d.ts +86 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +1031 -0
- package/dist/server/server.js.map +1 -0
- package/dist/server/types.d.ts +3 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/types.js +3 -0
- package/dist/server/types.js.map +1 -0
- package/package.json +129 -0
- package/src/application/index.ts +747 -0
- package/src/client/client.ts +1105 -0
- package/src/client/index.ts +45 -0
- package/src/client/types.ts +305 -0
- package/src/config/index.ts +497 -0
- package/src/contracts/contract-builder.ts +583 -0
- package/src/contracts/contract-group.ts +502 -0
- package/src/contracts/contract-like.ts +29 -0
- package/src/contracts/index.ts +53 -0
- package/src/contracts/openapi-meta.ts +22 -0
- package/src/contracts/path-template.ts +91 -0
- package/src/contracts/rate-limit.ts +50 -0
- package/src/contracts/types.ts +207 -0
- package/src/contracts/utils.ts +56 -0
- package/src/domain/entity.ts +256 -0
- package/src/domain/events.ts +52 -0
- package/src/domain/index.ts +18 -0
- package/src/domain/value-object.ts +135 -0
- package/src/errors/catalog.ts +149 -0
- package/src/errors/http.ts +80 -0
- package/src/errors/index.ts +28 -0
- package/src/errors/response.ts +54 -0
- package/src/errors/validation.ts +35 -0
- package/src/events/index.ts +246 -0
- package/src/jobs/index.ts +211 -0
- package/src/mail/index.ts +177 -0
- package/src/openapi/index.ts +865 -0
- package/src/openapi/schema-introspector.ts +107 -0
- package/src/ports/audit.ts +176 -0
- package/src/ports/auth.ts +76 -0
- package/src/ports/builder.ts +97 -0
- package/src/ports/cache.ts +94 -0
- package/src/ports/clock.ts +34 -0
- package/src/ports/events.ts +100 -0
- package/src/ports/id-generator.ts +36 -0
- package/src/ports/index.ts +221 -0
- package/src/ports/logger.ts +67 -0
- package/src/ports/policy.ts +242 -0
- package/src/ports/rate-limit.ts +91 -0
- package/src/ports/redaction.ts +199 -0
- package/src/ports/storage.ts +282 -0
- package/src/ports/testing.ts +234 -0
- package/src/ports/unit-of-work.ts +134 -0
- package/src/providers/index.ts +40 -0
- package/src/providers/instrumentation.ts +248 -0
- package/src/providers/provider.ts +191 -0
- package/src/schedules/index.ts +442 -0
- package/src/server/contract-like.ts +8 -0
- package/src/server/health.ts +82 -0
- package/src/server/hooks/auth.ts +147 -0
- package/src/server/hooks/cors.ts +87 -0
- package/src/server/hooks/errors.ts +126 -0
- package/src/server/hooks/index.ts +43 -0
- package/src/server/hooks/logging.ts +121 -0
- package/src/server/hooks/rate-limit.ts +171 -0
- package/src/server/hooks/utils.ts +16 -0
- package/src/server/hooks.ts +1 -0
- package/src/server/http.ts +189 -0
- package/src/server/index.ts +35 -0
- package/src/server/openapi.ts +72 -0
- package/src/server/providers/index.ts +3 -0
- package/src/server/providers/loadProviderConfig.ts +72 -0
- package/src/server/server.ts +1521 -0
- package/src/server/types.ts +2 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rate limit scope for identifying the rate limit key.
|
|
3
|
+
*
|
|
4
|
+
* - "user": keyed by authenticated user id
|
|
5
|
+
* - "ip": keyed by client IP
|
|
6
|
+
* - "global": single global key
|
|
7
|
+
*/
|
|
8
|
+
export type RateLimitScope = "user" | "ip" | "global";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Rate limit configuration for a contract.
|
|
12
|
+
*
|
|
13
|
+
* This type is used in contract metadata to configure rate limiting behavior.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* const getTodoContract = c
|
|
18
|
+
* .get("/api/todos/:id", "getTodo")
|
|
19
|
+
* .pathParams(z.object({ id: z.string() }))
|
|
20
|
+
* .meta({
|
|
21
|
+
* auth: "required",
|
|
22
|
+
* rateLimit: {
|
|
23
|
+
* max: 60,
|
|
24
|
+
* windowSec: 60,
|
|
25
|
+
* scope: "user",
|
|
26
|
+
* },
|
|
27
|
+
* })
|
|
28
|
+
* .responses({ 200: z.object({ id: z.string(), name: z.string() }) });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export interface RateLimitMeta {
|
|
32
|
+
/**
|
|
33
|
+
* Maximum number of allowed hits within the window.
|
|
34
|
+
*/
|
|
35
|
+
max: number;
|
|
36
|
+
/**
|
|
37
|
+
* Length of the window in seconds.
|
|
38
|
+
*/
|
|
39
|
+
windowSec: number;
|
|
40
|
+
/**
|
|
41
|
+
* Scope for identifying the rate limit key.
|
|
42
|
+
*
|
|
43
|
+
* - "user": keyed by authenticated user id
|
|
44
|
+
* - "ip": keyed by client IP
|
|
45
|
+
* - "global": single global key
|
|
46
|
+
*
|
|
47
|
+
* Default: "global".
|
|
48
|
+
*/
|
|
49
|
+
scope?: RateLimitScope;
|
|
50
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
+
import type { OpenAPIOperationMeta } from "./openapi-meta";
|
|
3
|
+
import type { RateLimitMeta } from "./rate-limit";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Any Standard Schema validator type
|
|
7
|
+
* Replaces ZodTypeAny to support all Standard Schema compatible libraries
|
|
8
|
+
*/
|
|
9
|
+
export type StandardSchema = StandardSchemaV1<unknown, unknown>;
|
|
10
|
+
|
|
11
|
+
export type ContractResponseSchema = StandardSchema | null;
|
|
12
|
+
export type ContractResponses = Partial<Record<number, ContractResponseSchema>>;
|
|
13
|
+
export type StandardErrorResponseBody = {
|
|
14
|
+
code: string;
|
|
15
|
+
message: string;
|
|
16
|
+
details?: unknown;
|
|
17
|
+
requestId?: string;
|
|
18
|
+
};
|
|
19
|
+
export type StandardErrorResponseSchema = StandardSchemaV1<
|
|
20
|
+
unknown,
|
|
21
|
+
StandardErrorResponseBody
|
|
22
|
+
>;
|
|
23
|
+
export type ContractErrorDefinition = {
|
|
24
|
+
code: string;
|
|
25
|
+
status: number;
|
|
26
|
+
message: string;
|
|
27
|
+
details?: StandardSchema;
|
|
28
|
+
};
|
|
29
|
+
export type ContractErrorResponses = Record<string, ContractErrorDefinition>;
|
|
30
|
+
export type ResponsesFromErrorDefinitions<T extends ContractErrorResponses> = {
|
|
31
|
+
[K in keyof T as T[K]["status"]]: StandardErrorResponseSchema;
|
|
32
|
+
};
|
|
33
|
+
export type ContractHeaderSchemas =
|
|
34
|
+
| StandardSchema
|
|
35
|
+
| readonly StandardSchema[]
|
|
36
|
+
| null;
|
|
37
|
+
|
|
38
|
+
export const BEIGNET_ERROR_OWNER_HEADER = "x-beignet-error-owner";
|
|
39
|
+
|
|
40
|
+
export type InferHeaderSchemaInput<
|
|
41
|
+
T extends ContractHeaderSchemas | undefined,
|
|
42
|
+
> = T extends readonly StandardSchemaV1[]
|
|
43
|
+
? UnionToIntersection<InferInput<T[number]>>
|
|
44
|
+
: T extends StandardSchemaV1
|
|
45
|
+
? InferInput<T>
|
|
46
|
+
: undefined;
|
|
47
|
+
|
|
48
|
+
export type InferHeaderSchemaOutput<
|
|
49
|
+
T extends ContractHeaderSchemas | undefined,
|
|
50
|
+
> = T extends readonly StandardSchemaV1[]
|
|
51
|
+
? UnionToIntersection<InferOutput<T[number]>>
|
|
52
|
+
: T extends StandardSchemaV1
|
|
53
|
+
? InferOutput<T>
|
|
54
|
+
: undefined;
|
|
55
|
+
|
|
56
|
+
type UnionToIntersection<T> = (
|
|
57
|
+
T extends unknown
|
|
58
|
+
? (value: T) => void
|
|
59
|
+
: never
|
|
60
|
+
) extends (value: infer I) => void
|
|
61
|
+
? I
|
|
62
|
+
: never;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Infer the output type from a Standard Schema
|
|
66
|
+
*/
|
|
67
|
+
export type InferOutput<T extends StandardSchemaV1> =
|
|
68
|
+
StandardSchemaV1.InferOutput<T>;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Infer the input type from a Standard Schema
|
|
72
|
+
*/
|
|
73
|
+
export type InferInput<T extends StandardSchemaV1> =
|
|
74
|
+
StandardSchemaV1.InferInput<T>;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* HTTP methods supported by contracts
|
|
78
|
+
*/
|
|
79
|
+
export type HttpMethod =
|
|
80
|
+
| "GET"
|
|
81
|
+
| "POST"
|
|
82
|
+
| "PUT"
|
|
83
|
+
| "PATCH"
|
|
84
|
+
| "DELETE"
|
|
85
|
+
| "HEAD"
|
|
86
|
+
| "OPTIONS";
|
|
87
|
+
|
|
88
|
+
export type BodyHttpMethod = Extract<HttpMethod, "POST" | "PUT" | "PATCH">;
|
|
89
|
+
|
|
90
|
+
export const BODY_HTTP_METHODS = [
|
|
91
|
+
"POST",
|
|
92
|
+
"PUT",
|
|
93
|
+
"PATCH",
|
|
94
|
+
] as const satisfies readonly BodyHttpMethod[];
|
|
95
|
+
|
|
96
|
+
export function methodSupportsRequestBody(
|
|
97
|
+
method: HttpMethod,
|
|
98
|
+
): method is BodyHttpMethod {
|
|
99
|
+
return (BODY_HTTP_METHODS as readonly HttpMethod[]).includes(method);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export const STANDARD_ERROR_RESPONSE_SCHEMA: StandardErrorResponseSchema = {
|
|
103
|
+
"~standard": {
|
|
104
|
+
version: 1,
|
|
105
|
+
vendor: "beignet",
|
|
106
|
+
validate(value) {
|
|
107
|
+
if (typeof value !== "object" || value === null) {
|
|
108
|
+
return {
|
|
109
|
+
issues: [{ message: "Expected error response body object." }],
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const body = value as Record<string, unknown>;
|
|
114
|
+
const issues: StandardSchemaV1.Issue[] = [];
|
|
115
|
+
if (typeof body.code !== "string") {
|
|
116
|
+
issues.push({ message: 'Expected "code" to be a string.' });
|
|
117
|
+
}
|
|
118
|
+
if (typeof body.message !== "string") {
|
|
119
|
+
issues.push({ message: 'Expected "message" to be a string.' });
|
|
120
|
+
}
|
|
121
|
+
if (body.requestId !== undefined && typeof body.requestId !== "string") {
|
|
122
|
+
issues.push({ message: 'Expected "requestId" to be a string.' });
|
|
123
|
+
}
|
|
124
|
+
if (issues.length) {
|
|
125
|
+
return { issues };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
value: {
|
|
130
|
+
code: body.code as string,
|
|
131
|
+
message: body.message as string,
|
|
132
|
+
...(body.details !== undefined ? { details: body.details } : {}),
|
|
133
|
+
...(body.requestId !== undefined
|
|
134
|
+
? { requestId: body.requestId as string }
|
|
135
|
+
: {}),
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Contract metadata - freeform object for policies, auth, etc.
|
|
144
|
+
* May include an optional openapi namespace for OpenAPI-specific metadata,
|
|
145
|
+
* and an optional rateLimit namespace for rate limiting configuration.
|
|
146
|
+
*/
|
|
147
|
+
export type ContractMeta = {
|
|
148
|
+
openapi?: OpenAPIOperationMeta;
|
|
149
|
+
/**
|
|
150
|
+
* Optional rate limit configuration for this contract.
|
|
151
|
+
*
|
|
152
|
+
* If absent, no rate limiting is applied.
|
|
153
|
+
*/
|
|
154
|
+
rateLimit?: RateLimitMeta;
|
|
155
|
+
[namespace: string]: unknown;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* HTTP contract configuration
|
|
160
|
+
*/
|
|
161
|
+
export type HttpContractConfig<
|
|
162
|
+
TMethod extends HttpMethod = HttpMethod,
|
|
163
|
+
TPathParams extends StandardSchema | null = StandardSchema | null,
|
|
164
|
+
TQuery extends StandardSchema | null = StandardSchema | null,
|
|
165
|
+
TBody extends StandardSchema | null = StandardSchema | null,
|
|
166
|
+
TResponses extends ContractResponses = ContractResponses,
|
|
167
|
+
TMeta extends ContractMeta = ContractMeta,
|
|
168
|
+
TPath extends string = string,
|
|
169
|
+
THeaders extends ContractHeaderSchemas = ContractHeaderSchemas,
|
|
170
|
+
> = {
|
|
171
|
+
kind: "http";
|
|
172
|
+
/**
|
|
173
|
+
* Fully qualified contract name. Contracts created from a namespaced
|
|
174
|
+
* ContractGroup use `${namespace}.${localName}`.
|
|
175
|
+
*/
|
|
176
|
+
name: string;
|
|
177
|
+
/**
|
|
178
|
+
* Optional resource namespace for contracts created from a ContractGroup.
|
|
179
|
+
*/
|
|
180
|
+
namespace?: string;
|
|
181
|
+
/**
|
|
182
|
+
* Contract name without the resource namespace.
|
|
183
|
+
*/
|
|
184
|
+
localName?: string;
|
|
185
|
+
method: TMethod;
|
|
186
|
+
path: TPath;
|
|
187
|
+
pathParams: TPathParams;
|
|
188
|
+
query: TQuery;
|
|
189
|
+
headers?: THeaders;
|
|
190
|
+
body: TBody;
|
|
191
|
+
responses: TResponses;
|
|
192
|
+
metadata: TMeta;
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Any contract type
|
|
197
|
+
*/
|
|
198
|
+
export type AnyContract = HttpContractConfig;
|
|
199
|
+
|
|
200
|
+
export function getContractHeaderSchemas(
|
|
201
|
+
headers: ContractHeaderSchemas | undefined,
|
|
202
|
+
): readonly StandardSchema[] {
|
|
203
|
+
if (!headers) return [];
|
|
204
|
+
return Array.isArray(headers)
|
|
205
|
+
? (headers as readonly StandardSchema[])
|
|
206
|
+
: [headers as StandardSchema];
|
|
207
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type PathTemplateSegment, parsePathTemplate } from "./path-template";
|
|
2
|
+
import type { HttpMethod } from "./types";
|
|
3
|
+
|
|
4
|
+
const methodNameMap: Record<HttpMethod, string> = {
|
|
5
|
+
GET: "get",
|
|
6
|
+
POST: "create",
|
|
7
|
+
PUT: "update",
|
|
8
|
+
PATCH: "patch",
|
|
9
|
+
DELETE: "delete",
|
|
10
|
+
HEAD: "head",
|
|
11
|
+
OPTIONS: "options",
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function toPascalCase(value: string): string {
|
|
15
|
+
return value
|
|
16
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
17
|
+
.split(/[^a-zA-Z0-9]+/)
|
|
18
|
+
.filter(Boolean)
|
|
19
|
+
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
20
|
+
.join("");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function getMeaningfulPathSegments(path: string): PathTemplateSegment[] {
|
|
24
|
+
const segments = parsePathTemplate(path).segments;
|
|
25
|
+
|
|
26
|
+
if (
|
|
27
|
+
segments[0]?.kind === "static" &&
|
|
28
|
+
segments[0].value.toLowerCase() === "api"
|
|
29
|
+
) {
|
|
30
|
+
return segments.slice(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return segments;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function generateContractName(method: HttpMethod, path: string): string {
|
|
37
|
+
const methodPrefix = methodNameMap[method];
|
|
38
|
+
const segments = getMeaningfulPathSegments(path);
|
|
39
|
+
|
|
40
|
+
if (segments.length === 0) {
|
|
41
|
+
return `${methodPrefix}Root`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const nameSuffix = segments
|
|
45
|
+
.map((segment) => {
|
|
46
|
+
if (segment.kind === "dynamic") {
|
|
47
|
+
const paramName = toPascalCase(segment.name);
|
|
48
|
+
return `By${paramName || "Param"}`;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return toPascalCase(segment.value) || "Resource";
|
|
52
|
+
})
|
|
53
|
+
.join("");
|
|
54
|
+
|
|
55
|
+
return `${methodPrefix}${nameSuffix}`;
|
|
56
|
+
}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Any Standard Schema validator type
|
|
5
|
+
*/
|
|
6
|
+
export type StandardSchema = StandardSchemaV1<unknown, unknown>;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Infer the output type from a Standard Schema
|
|
10
|
+
*/
|
|
11
|
+
export type InferOutput<T extends StandardSchemaV1> =
|
|
12
|
+
StandardSchemaV1.InferOutput<T>;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Validate data using a Standard Schema validator synchronously if possible
|
|
16
|
+
*/
|
|
17
|
+
function validateSchemaSync<T>(
|
|
18
|
+
schema: StandardSchemaV1<unknown, T>,
|
|
19
|
+
data: unknown,
|
|
20
|
+
): T | Promise<T> {
|
|
21
|
+
const result = schema["~standard"].validate(data);
|
|
22
|
+
|
|
23
|
+
if (result instanceof Promise) {
|
|
24
|
+
return result.then((resolved) => {
|
|
25
|
+
if (resolved.issues?.length) {
|
|
26
|
+
const message = resolved.issues
|
|
27
|
+
.map((issue) => {
|
|
28
|
+
const path =
|
|
29
|
+
issue.path !== undefined
|
|
30
|
+
? Array.isArray(issue.path)
|
|
31
|
+
? issue.path.join(".")
|
|
32
|
+
: String(issue.path)
|
|
33
|
+
: "";
|
|
34
|
+
return path ? `${path}: ${issue.message}` : issue.message;
|
|
35
|
+
})
|
|
36
|
+
.join("; ");
|
|
37
|
+
throw new Error(`Validation failed: ${message}`);
|
|
38
|
+
}
|
|
39
|
+
if ("value" in resolved) {
|
|
40
|
+
return resolved.value;
|
|
41
|
+
}
|
|
42
|
+
throw new Error("Invalid Standard Schema result: missing value");
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (result.issues?.length) {
|
|
47
|
+
const message = result.issues
|
|
48
|
+
.map((issue) => {
|
|
49
|
+
const path =
|
|
50
|
+
issue.path !== undefined
|
|
51
|
+
? Array.isArray(issue.path)
|
|
52
|
+
? issue.path.join(".")
|
|
53
|
+
: String(issue.path)
|
|
54
|
+
: "";
|
|
55
|
+
return path ? `${path}: ${issue.message}` : issue.message;
|
|
56
|
+
})
|
|
57
|
+
.join("; ");
|
|
58
|
+
throw new Error(`Validation failed: ${message}`);
|
|
59
|
+
}
|
|
60
|
+
if ("value" in result) {
|
|
61
|
+
return result.value;
|
|
62
|
+
}
|
|
63
|
+
throw new Error("Invalid Standard Schema result: missing value");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// biome-ignore lint/suspicious/noExplicitAny: Base methods type for flexibility
|
|
67
|
+
type AnyMethods = Record<string, any>;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Represents an instance of an entity with props, methods, and immutable update capability.
|
|
71
|
+
*/
|
|
72
|
+
export type EntityInstance<
|
|
73
|
+
Name extends string,
|
|
74
|
+
Schema extends StandardSchema,
|
|
75
|
+
Methods extends AnyMethods,
|
|
76
|
+
> = InferOutput<Schema> & {
|
|
77
|
+
/**
|
|
78
|
+
* Create a new entity instance with updated properties (immutable update).
|
|
79
|
+
*/
|
|
80
|
+
with(
|
|
81
|
+
patch: Partial<InferOutput<Schema>>,
|
|
82
|
+
):
|
|
83
|
+
| EntityInstance<Name, Schema, Methods>
|
|
84
|
+
| Promise<EntityInstance<Name, Schema, Methods>>;
|
|
85
|
+
/**
|
|
86
|
+
* Convert the entity to a plain JSON object for persistence.
|
|
87
|
+
*/
|
|
88
|
+
toJSON(): InferOutput<Schema>;
|
|
89
|
+
} & Methods;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Represents a defined Entity with schema and factory methods.
|
|
93
|
+
*/
|
|
94
|
+
export interface EntityDef<
|
|
95
|
+
Name extends string,
|
|
96
|
+
Schema extends StandardSchema,
|
|
97
|
+
Methods extends AnyMethods,
|
|
98
|
+
> {
|
|
99
|
+
/** The name of this entity (for debugging/introspection) */
|
|
100
|
+
name: Name;
|
|
101
|
+
/** The Standard Schema for validating entity props */
|
|
102
|
+
schema: Schema;
|
|
103
|
+
/** Create a new entity instance from props */
|
|
104
|
+
create(
|
|
105
|
+
props: InferOutput<Schema>,
|
|
106
|
+
):
|
|
107
|
+
| EntityInstance<Name, Schema, Methods>
|
|
108
|
+
| Promise<EntityInstance<Name, Schema, Methods>>;
|
|
109
|
+
/** Reconstruct an entity instance from JSON (e.g., from persistence) */
|
|
110
|
+
fromJSON(
|
|
111
|
+
json: InferOutput<Schema>,
|
|
112
|
+
):
|
|
113
|
+
| EntityInstance<Name, Schema, Methods>
|
|
114
|
+
| Promise<EntityInstance<Name, Schema, Methods>>;
|
|
115
|
+
/** Type alias for the entity instance type */
|
|
116
|
+
Type: EntityInstance<Name, Schema, Methods>;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Builder class for creating Entities/Aggregates.
|
|
121
|
+
*/
|
|
122
|
+
class EntityBuilder<
|
|
123
|
+
Name extends string,
|
|
124
|
+
Schema extends StandardSchema,
|
|
125
|
+
Methods extends AnyMethods,
|
|
126
|
+
> {
|
|
127
|
+
constructor(
|
|
128
|
+
private readonly cfg: {
|
|
129
|
+
name: Name;
|
|
130
|
+
schema?: Schema;
|
|
131
|
+
// biome-ignore lint/suspicious/noExplicitAny: Methods builder receives base instance
|
|
132
|
+
methods?: (self: any) => Methods;
|
|
133
|
+
},
|
|
134
|
+
) {}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Define the schema for this entity using any Standard Schema compatible validator.
|
|
138
|
+
*/
|
|
139
|
+
props<S extends StandardSchema>(schema: S): EntityBuilder<Name, S, Methods> {
|
|
140
|
+
return new EntityBuilder<Name, S, Methods>({
|
|
141
|
+
name: this.cfg.name,
|
|
142
|
+
schema,
|
|
143
|
+
methods: this.cfg.methods,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Define methods to attach to entity instances.
|
|
149
|
+
* The method builder receives the base instance (with props, with, and toJSON).
|
|
150
|
+
*/
|
|
151
|
+
methods<M extends AnyMethods>(
|
|
152
|
+
// biome-ignore lint/suspicious/noExplicitAny: Methods builder receives base instance with props
|
|
153
|
+
build: (self: any) => M,
|
|
154
|
+
): EntityBuilder<Name, Schema, M> {
|
|
155
|
+
return new EntityBuilder<Name, Schema, M>({
|
|
156
|
+
name: this.cfg.name,
|
|
157
|
+
schema: this.cfg.schema,
|
|
158
|
+
methods: build,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Finalize and build the entity definition.
|
|
164
|
+
*/
|
|
165
|
+
build(): EntityDef<Name, Schema, Methods> {
|
|
166
|
+
if (!this.cfg.schema) {
|
|
167
|
+
throw new Error(`Entity "${this.cfg.name}" is missing props schema`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const schema = this.cfg.schema;
|
|
171
|
+
const methodsBuilder = this.cfg.methods;
|
|
172
|
+
|
|
173
|
+
const createInstance = (
|
|
174
|
+
raw: InferOutput<Schema>,
|
|
175
|
+
):
|
|
176
|
+
| EntityInstance<Name, Schema, Methods>
|
|
177
|
+
| Promise<EntityInstance<Name, Schema, Methods>> => {
|
|
178
|
+
const validationResult = validateSchemaSync(schema, raw);
|
|
179
|
+
|
|
180
|
+
const buildInstance = (
|
|
181
|
+
parsed: InferOutput<Schema>,
|
|
182
|
+
): EntityInstance<Name, Schema, Methods> => {
|
|
183
|
+
// Cast parsed to a plain object for spreading
|
|
184
|
+
const parsedObj = parsed as Record<string, unknown>;
|
|
185
|
+
|
|
186
|
+
// biome-ignore lint/suspicious/noExplicitAny: Dynamic object construction
|
|
187
|
+
const base: any = {
|
|
188
|
+
...parsedObj,
|
|
189
|
+
// Immutable update - creates a new validated instance
|
|
190
|
+
// Note: Re-validates the merged props for data integrity
|
|
191
|
+
with(patch: Partial<InferOutput<Schema>>) {
|
|
192
|
+
const patchObj = patch as Record<string, unknown>;
|
|
193
|
+
return createInstance({
|
|
194
|
+
...parsedObj,
|
|
195
|
+
...patchObj,
|
|
196
|
+
} as InferOutput<Schema>);
|
|
197
|
+
},
|
|
198
|
+
toJSON() {
|
|
199
|
+
return parsed;
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const methods = methodsBuilder?.(base) ?? {};
|
|
204
|
+
const instance = Object.freeze({
|
|
205
|
+
...base,
|
|
206
|
+
...methods,
|
|
207
|
+
}) as EntityInstance<Name, Schema, Methods>;
|
|
208
|
+
|
|
209
|
+
return instance;
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
if (validationResult instanceof Promise) {
|
|
213
|
+
return validationResult.then(buildInstance);
|
|
214
|
+
}
|
|
215
|
+
return buildInstance(validationResult);
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const def: EntityDef<Name, Schema, Methods> = {
|
|
219
|
+
name: this.cfg.name,
|
|
220
|
+
schema,
|
|
221
|
+
create: createInstance,
|
|
222
|
+
fromJSON: createInstance,
|
|
223
|
+
// Type is undefined at runtime - used only for TypeScript type inference via `typeof Entity.Type`
|
|
224
|
+
Type: undefined as unknown as EntityInstance<Name, Schema, Methods>,
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
return def;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Create a new Entity builder.
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* ```ts
|
|
236
|
+
* const Todo = defineEntity("Todo")
|
|
237
|
+
* .props(z.object({
|
|
238
|
+
* id: z.string(),
|
|
239
|
+
* title: z.string(),
|
|
240
|
+
* assigneeIds: z.array(z.string()).default([]),
|
|
241
|
+
* }))
|
|
242
|
+
* .methods((self) => ({
|
|
243
|
+
* addAssignee(id: string) {
|
|
244
|
+
* if (self.assigneeIds.includes(id)) return self;
|
|
245
|
+
* return self.with({ assigneeIds: [...self.assigneeIds, id] });
|
|
246
|
+
* },
|
|
247
|
+
* }))
|
|
248
|
+
* .build();
|
|
249
|
+
*
|
|
250
|
+
* type Todo = typeof Todo.Type;
|
|
251
|
+
* ```
|
|
252
|
+
*/
|
|
253
|
+
export function defineEntity<Name extends string>(name: Name) {
|
|
254
|
+
// biome-ignore lint/suspicious/noExplicitAny: Initial schema type will be set via .props()
|
|
255
|
+
return new EntityBuilder<Name, any, Record<string, never>>({ name } as any);
|
|
256
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { EventDef, InferEventPayload, StandardSchema } from "../events";
|
|
2
|
+
import { defineEvent } from "../events";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Represents a defined Domain Event with name and payload schema.
|
|
6
|
+
*/
|
|
7
|
+
export type DomainEventDef<
|
|
8
|
+
Name extends string = string,
|
|
9
|
+
Payload extends StandardSchema = StandardSchema,
|
|
10
|
+
> = EventDef<Name, Payload>;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Infer the payload type from a DomainEventDef.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* const UserRegistered = defineDomainEvent(
|
|
18
|
+
* "user.registered",
|
|
19
|
+
* z.object({
|
|
20
|
+
* userId: z.string(),
|
|
21
|
+
* email: z.string().email(),
|
|
22
|
+
* })
|
|
23
|
+
* );
|
|
24
|
+
*
|
|
25
|
+
* type Payload = InferEventPayload<typeof UserRegistered>;
|
|
26
|
+
* // { userId: string; email: string }
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export type { InferEventPayload };
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Create a new Domain Event definition.
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```ts
|
|
36
|
+
* const UserRegistered = defineDomainEvent(
|
|
37
|
+
* "user.registered",
|
|
38
|
+
* z.object({
|
|
39
|
+
* userId: z.string(),
|
|
40
|
+
* email: z.string().email(),
|
|
41
|
+
* })
|
|
42
|
+
* );
|
|
43
|
+
*
|
|
44
|
+
* type UserRegistered = typeof UserRegistered;
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export function defineDomainEvent<
|
|
48
|
+
Name extends string,
|
|
49
|
+
Payload extends StandardSchema,
|
|
50
|
+
>(name: Name, payload: Payload): DomainEventDef<Name, Payload> {
|
|
51
|
+
return defineEvent(name, { payload });
|
|
52
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @beignet/core/domain
|
|
3
|
+
*
|
|
4
|
+
* Domain modeling helpers for Beignet - value objects, entities, and domain events.
|
|
5
|
+
*
|
|
6
|
+
* This package provides small, framework-agnostic helpers for domain modeling:
|
|
7
|
+
* - A Value Object builder (defineValueObject)
|
|
8
|
+
* - An Entity/Aggregate builder (defineEntity)
|
|
9
|
+
* - A Domain Event helper (defineDomainEvent)
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
export { defineEntity, type EntityDef, type EntityInstance } from "./entity";
|
|
13
|
+
export {
|
|
14
|
+
type DomainEventDef,
|
|
15
|
+
defineDomainEvent,
|
|
16
|
+
type InferEventPayload,
|
|
17
|
+
} from "./events";
|
|
18
|
+
export { defineValueObject, type ValueObjectDef } from "./value-object";
|