@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,583 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
+
import type { OpenAPIOperationMeta } from "./openapi-meta";
|
|
3
|
+
import { parsePathTemplate } from "./path-template";
|
|
4
|
+
import type {
|
|
5
|
+
BodyHttpMethod,
|
|
6
|
+
ContractErrorResponses,
|
|
7
|
+
ContractHeaderSchemas,
|
|
8
|
+
ContractMeta,
|
|
9
|
+
ContractResponses,
|
|
10
|
+
HttpContractConfig,
|
|
11
|
+
HttpMethod,
|
|
12
|
+
ResponsesFromErrorDefinitions,
|
|
13
|
+
StandardSchema,
|
|
14
|
+
} from "./types";
|
|
15
|
+
import {
|
|
16
|
+
methodSupportsRequestBody,
|
|
17
|
+
STANDARD_ERROR_RESPONSE_SCHEMA,
|
|
18
|
+
} from "./types";
|
|
19
|
+
import { generateContractName } from "./utils";
|
|
20
|
+
|
|
21
|
+
function responsesFromErrors(
|
|
22
|
+
errors: ContractErrorResponses,
|
|
23
|
+
): ContractResponses {
|
|
24
|
+
const responses: ContractResponses = {};
|
|
25
|
+
for (const error of Object.values(errors)) {
|
|
26
|
+
responses[error.status] = STANDARD_ERROR_RESPONSE_SCHEMA;
|
|
27
|
+
}
|
|
28
|
+
return responses;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function assertNoResponseStatusConflicts(
|
|
32
|
+
current: ContractResponses,
|
|
33
|
+
next: ContractResponses,
|
|
34
|
+
): void {
|
|
35
|
+
const currentStatuses = new Set(Object.keys(current));
|
|
36
|
+
const conflicts = Object.keys(next).filter((status) =>
|
|
37
|
+
currentStatuses.has(status),
|
|
38
|
+
);
|
|
39
|
+
if (conflicts.length) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`.errors() cannot declare status ${conflicts.join(", ")} because a response schema for that status already exists. Use .responses() without .errors() when you need a custom error response body.`,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function assertNoCatalogErrorStatusConflicts(
|
|
47
|
+
metadata: ContractMeta,
|
|
48
|
+
next: ContractResponses,
|
|
49
|
+
): void {
|
|
50
|
+
const errors = metadata.errors;
|
|
51
|
+
if (typeof errors !== "object" || errors === null) return;
|
|
52
|
+
|
|
53
|
+
const errorStatuses = new Set(
|
|
54
|
+
Object.values(errors)
|
|
55
|
+
.map((error) =>
|
|
56
|
+
typeof error === "object" &&
|
|
57
|
+
error !== null &&
|
|
58
|
+
typeof (error as { status?: unknown }).status === "number"
|
|
59
|
+
? String((error as { status: number }).status)
|
|
60
|
+
: undefined,
|
|
61
|
+
)
|
|
62
|
+
.filter((status): status is string => status !== undefined),
|
|
63
|
+
);
|
|
64
|
+
const conflicts = Object.keys(next).filter((status) =>
|
|
65
|
+
errorStatuses.has(status),
|
|
66
|
+
);
|
|
67
|
+
if (conflicts.length) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
`.responses() cannot declare status ${conflicts.join(", ")} because that status is already declared by .errors(). Use .responses() without .errors() when you need a custom error response body.`,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Contract builder for constructing HTTP contracts in a fluent API style
|
|
76
|
+
* Schemas are accessible via the .schema property for introspection
|
|
77
|
+
*/
|
|
78
|
+
export class ContractBuilder<
|
|
79
|
+
TMethod extends HttpMethod,
|
|
80
|
+
TPathParams extends StandardSchema | null,
|
|
81
|
+
TQuery extends StandardSchema | null,
|
|
82
|
+
TBody extends StandardSchema | null,
|
|
83
|
+
THeaders extends ContractHeaderSchemas,
|
|
84
|
+
TResponses extends ContractResponses,
|
|
85
|
+
TMeta extends ContractMeta,
|
|
86
|
+
TPath extends string = string,
|
|
87
|
+
> {
|
|
88
|
+
readonly kind: "http";
|
|
89
|
+
readonly name: string;
|
|
90
|
+
readonly namespace?: string;
|
|
91
|
+
readonly localName: string;
|
|
92
|
+
readonly method: TMethod;
|
|
93
|
+
private readonly _path: TPath;
|
|
94
|
+
private readonly _pathParams: TPathParams;
|
|
95
|
+
private readonly _query: TQuery;
|
|
96
|
+
private readonly _body: TBody;
|
|
97
|
+
private readonly _headers: THeaders;
|
|
98
|
+
private readonly _responses: TResponses;
|
|
99
|
+
private readonly _meta: TMeta;
|
|
100
|
+
|
|
101
|
+
constructor(
|
|
102
|
+
config: HttpContractConfig<
|
|
103
|
+
TMethod,
|
|
104
|
+
TPathParams,
|
|
105
|
+
TQuery,
|
|
106
|
+
TBody,
|
|
107
|
+
TResponses,
|
|
108
|
+
TMeta,
|
|
109
|
+
TPath,
|
|
110
|
+
THeaders
|
|
111
|
+
>,
|
|
112
|
+
) {
|
|
113
|
+
if (config.body && !methodSupportsRequestBody(config.method)) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
`Request bodies are not supported for ${config.method} contracts. Use POST, PUT, or PATCH for contract request bodies.`,
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
this.kind = config.kind;
|
|
119
|
+
this.name = config.name;
|
|
120
|
+
this.namespace = config.namespace;
|
|
121
|
+
this.localName = config.localName ?? config.name;
|
|
122
|
+
this.method = config.method;
|
|
123
|
+
this._path = config.path;
|
|
124
|
+
this._pathParams = config.pathParams;
|
|
125
|
+
this._query = config.query;
|
|
126
|
+
this._body = config.body;
|
|
127
|
+
this._headers = config.headers ?? (null as THeaders);
|
|
128
|
+
this._responses = config.responses;
|
|
129
|
+
this._meta = config.metadata;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Access schemas for introspection
|
|
134
|
+
*/
|
|
135
|
+
get schema() {
|
|
136
|
+
return {
|
|
137
|
+
pathParams: this._pathParams,
|
|
138
|
+
query: this._query,
|
|
139
|
+
headers: this._headers,
|
|
140
|
+
body: this._body,
|
|
141
|
+
responses: this._responses,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get response schemas for introspection
|
|
147
|
+
*/
|
|
148
|
+
get responseSchemas(): TResponses {
|
|
149
|
+
return this._responses;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Get the URL path template
|
|
154
|
+
*/
|
|
155
|
+
get pathTemplate(): TPath {
|
|
156
|
+
return this._path;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Get the URL path (alias for pathTemplate)
|
|
161
|
+
*/
|
|
162
|
+
get path(): TPath {
|
|
163
|
+
return this._path;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Get contract metadata
|
|
168
|
+
*/
|
|
169
|
+
get metadata(): TMeta {
|
|
170
|
+
return this._meta;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Get the contract as an HttpContractConfig object
|
|
175
|
+
* Use this when passing to adapters that expect the plain config type
|
|
176
|
+
*/
|
|
177
|
+
get config(): HttpContractConfig<
|
|
178
|
+
TMethod,
|
|
179
|
+
TPathParams,
|
|
180
|
+
TQuery,
|
|
181
|
+
TBody,
|
|
182
|
+
TResponses,
|
|
183
|
+
TMeta,
|
|
184
|
+
TPath,
|
|
185
|
+
THeaders
|
|
186
|
+
> {
|
|
187
|
+
return {
|
|
188
|
+
kind: this.kind,
|
|
189
|
+
name: this.name,
|
|
190
|
+
namespace: this.namespace,
|
|
191
|
+
localName: this.localName,
|
|
192
|
+
method: this.method,
|
|
193
|
+
path: this._path,
|
|
194
|
+
pathParams: this._pathParams,
|
|
195
|
+
query: this._query,
|
|
196
|
+
headers: this._headers,
|
|
197
|
+
body: this._body,
|
|
198
|
+
responses: this._responses,
|
|
199
|
+
metadata: this._meta,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Set path parameters schema (chainable builder method)
|
|
205
|
+
*/
|
|
206
|
+
pathParams<TNewPathParams extends StandardSchemaV1>(
|
|
207
|
+
schema: TNewPathParams,
|
|
208
|
+
): ContractBuilder<
|
|
209
|
+
TMethod,
|
|
210
|
+
TNewPathParams,
|
|
211
|
+
TQuery,
|
|
212
|
+
TBody,
|
|
213
|
+
THeaders,
|
|
214
|
+
TResponses,
|
|
215
|
+
TMeta,
|
|
216
|
+
TPath
|
|
217
|
+
> {
|
|
218
|
+
return new ContractBuilder({
|
|
219
|
+
kind: this.kind,
|
|
220
|
+
name: this.name,
|
|
221
|
+
namespace: this.namespace,
|
|
222
|
+
localName: this.localName,
|
|
223
|
+
method: this.method,
|
|
224
|
+
path: this._path,
|
|
225
|
+
pathParams: schema as unknown as TNewPathParams,
|
|
226
|
+
query: this._query,
|
|
227
|
+
headers: this._headers,
|
|
228
|
+
body: this._body,
|
|
229
|
+
responses: this._responses,
|
|
230
|
+
metadata: this._meta,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Set query parameters schema (chainable builder method)
|
|
236
|
+
*/
|
|
237
|
+
query<TNewQuery extends StandardSchemaV1>(
|
|
238
|
+
schema: TNewQuery,
|
|
239
|
+
): ContractBuilder<
|
|
240
|
+
TMethod,
|
|
241
|
+
TPathParams,
|
|
242
|
+
TNewQuery,
|
|
243
|
+
TBody,
|
|
244
|
+
THeaders,
|
|
245
|
+
TResponses,
|
|
246
|
+
TMeta,
|
|
247
|
+
TPath
|
|
248
|
+
> {
|
|
249
|
+
return new ContractBuilder({
|
|
250
|
+
kind: this.kind,
|
|
251
|
+
name: this.name,
|
|
252
|
+
namespace: this.namespace,
|
|
253
|
+
localName: this.localName,
|
|
254
|
+
method: this.method,
|
|
255
|
+
path: this._path,
|
|
256
|
+
pathParams: this._pathParams,
|
|
257
|
+
query: schema as unknown as TNewQuery,
|
|
258
|
+
headers: this._headers,
|
|
259
|
+
body: this._body,
|
|
260
|
+
responses: this._responses,
|
|
261
|
+
metadata: this._meta,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Set request body schema (chainable builder method)
|
|
267
|
+
* Only available on POST, PUT, and PATCH contracts.
|
|
268
|
+
*/
|
|
269
|
+
body<TNewBody extends StandardSchemaV1>(
|
|
270
|
+
this: ContractBuilder<
|
|
271
|
+
BodyHttpMethod,
|
|
272
|
+
StandardSchema | null,
|
|
273
|
+
StandardSchema | null,
|
|
274
|
+
StandardSchema | null,
|
|
275
|
+
ContractHeaderSchemas,
|
|
276
|
+
ContractResponses,
|
|
277
|
+
ContractMeta,
|
|
278
|
+
TPath
|
|
279
|
+
>,
|
|
280
|
+
schema: TNewBody,
|
|
281
|
+
): ContractBuilder<
|
|
282
|
+
TMethod,
|
|
283
|
+
TPathParams,
|
|
284
|
+
TQuery,
|
|
285
|
+
TNewBody,
|
|
286
|
+
THeaders,
|
|
287
|
+
TResponses,
|
|
288
|
+
TMeta,
|
|
289
|
+
TPath
|
|
290
|
+
> {
|
|
291
|
+
const self = this as unknown as ContractBuilder<
|
|
292
|
+
TMethod,
|
|
293
|
+
TPathParams,
|
|
294
|
+
TQuery,
|
|
295
|
+
TBody,
|
|
296
|
+
THeaders,
|
|
297
|
+
TResponses,
|
|
298
|
+
TMeta,
|
|
299
|
+
TPath
|
|
300
|
+
>;
|
|
301
|
+
return new ContractBuilder({
|
|
302
|
+
kind: self.kind,
|
|
303
|
+
name: self.name,
|
|
304
|
+
namespace: self.namespace,
|
|
305
|
+
localName: self.localName,
|
|
306
|
+
method: self.method,
|
|
307
|
+
path: self._path,
|
|
308
|
+
pathParams: self._pathParams,
|
|
309
|
+
query: self._query,
|
|
310
|
+
headers: self._headers,
|
|
311
|
+
body: schema as unknown as TNewBody,
|
|
312
|
+
responses: self._responses,
|
|
313
|
+
metadata: self._meta,
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Add request header schema (chainable).
|
|
319
|
+
*
|
|
320
|
+
* Multiple schemas are evaluated in declaration order and their parsed
|
|
321
|
+
* outputs are merged. This keeps group-level headers library-agnostic.
|
|
322
|
+
*/
|
|
323
|
+
headers<TNewHeaders extends StandardSchemaV1>(
|
|
324
|
+
schema: TNewHeaders,
|
|
325
|
+
): ContractBuilder<
|
|
326
|
+
TMethod,
|
|
327
|
+
TPathParams,
|
|
328
|
+
TQuery,
|
|
329
|
+
TBody,
|
|
330
|
+
THeaders extends readonly StandardSchema[]
|
|
331
|
+
? readonly [...THeaders, TNewHeaders]
|
|
332
|
+
: THeaders extends StandardSchema
|
|
333
|
+
? readonly [THeaders, TNewHeaders]
|
|
334
|
+
: readonly [TNewHeaders],
|
|
335
|
+
TResponses,
|
|
336
|
+
TMeta,
|
|
337
|
+
TPath
|
|
338
|
+
> {
|
|
339
|
+
const existingHeaders = this._headers
|
|
340
|
+
? Array.isArray(this._headers)
|
|
341
|
+
? this._headers
|
|
342
|
+
: [this._headers]
|
|
343
|
+
: [];
|
|
344
|
+
|
|
345
|
+
return new ContractBuilder({
|
|
346
|
+
kind: this.kind,
|
|
347
|
+
name: this.name,
|
|
348
|
+
namespace: this.namespace,
|
|
349
|
+
localName: this.localName,
|
|
350
|
+
method: this.method,
|
|
351
|
+
path: this._path,
|
|
352
|
+
pathParams: this._pathParams,
|
|
353
|
+
query: this._query,
|
|
354
|
+
headers: [
|
|
355
|
+
...existingHeaders,
|
|
356
|
+
schema,
|
|
357
|
+
] as unknown as THeaders extends readonly StandardSchema[]
|
|
358
|
+
? readonly [...THeaders, TNewHeaders]
|
|
359
|
+
: THeaders extends StandardSchema
|
|
360
|
+
? readonly [THeaders, TNewHeaders]
|
|
361
|
+
: readonly [TNewHeaders],
|
|
362
|
+
body: this._body,
|
|
363
|
+
responses: this._responses,
|
|
364
|
+
metadata: this._meta,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Add or replace response schemas by status code (chainable).
|
|
370
|
+
* Use `null` for void/empty responses such as 204 No Content.
|
|
371
|
+
*/
|
|
372
|
+
responses<TNewResponses extends ContractResponses>(
|
|
373
|
+
responseSchemas: TNewResponses,
|
|
374
|
+
): ContractBuilder<
|
|
375
|
+
TMethod,
|
|
376
|
+
TPathParams,
|
|
377
|
+
TQuery,
|
|
378
|
+
TBody,
|
|
379
|
+
THeaders,
|
|
380
|
+
Omit<TResponses, keyof TNewResponses> & TNewResponses,
|
|
381
|
+
TMeta,
|
|
382
|
+
TPath
|
|
383
|
+
> {
|
|
384
|
+
assertNoCatalogErrorStatusConflicts(this._meta, responseSchemas);
|
|
385
|
+
return new ContractBuilder({
|
|
386
|
+
kind: this.kind,
|
|
387
|
+
name: this.name,
|
|
388
|
+
namespace: this.namespace,
|
|
389
|
+
localName: this.localName,
|
|
390
|
+
method: this.method,
|
|
391
|
+
path: this._path,
|
|
392
|
+
pathParams: this._pathParams,
|
|
393
|
+
query: this._query,
|
|
394
|
+
headers: this._headers,
|
|
395
|
+
body: this._body,
|
|
396
|
+
responses: {
|
|
397
|
+
...this._responses,
|
|
398
|
+
...responseSchemas,
|
|
399
|
+
} as unknown as Omit<TResponses, keyof TNewResponses> & TNewResponses,
|
|
400
|
+
metadata: this._meta,
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Declare route-owned application errors from an error catalog.
|
|
406
|
+
*
|
|
407
|
+
* Catalog errors use Beignet's standard error response envelope. Use
|
|
408
|
+
* `.responses()` when a route needs a custom error response body.
|
|
409
|
+
*/
|
|
410
|
+
errors<TErrorDefs extends ContractErrorResponses>(
|
|
411
|
+
errorDefs: TErrorDefs,
|
|
412
|
+
): ContractBuilder<
|
|
413
|
+
TMethod,
|
|
414
|
+
TPathParams,
|
|
415
|
+
TQuery,
|
|
416
|
+
TBody,
|
|
417
|
+
THeaders,
|
|
418
|
+
Omit<TResponses, keyof ResponsesFromErrorDefinitions<TErrorDefs>> &
|
|
419
|
+
ResponsesFromErrorDefinitions<TErrorDefs>,
|
|
420
|
+
Omit<TMeta, "errors"> & { errors: TErrorDefs },
|
|
421
|
+
TPath
|
|
422
|
+
> {
|
|
423
|
+
const errorResponses = responsesFromErrors(errorDefs);
|
|
424
|
+
assertNoResponseStatusConflicts(this._responses, errorResponses);
|
|
425
|
+
return new ContractBuilder({
|
|
426
|
+
kind: this.kind,
|
|
427
|
+
name: this.name,
|
|
428
|
+
namespace: this.namespace,
|
|
429
|
+
localName: this.localName,
|
|
430
|
+
method: this.method,
|
|
431
|
+
path: this._path,
|
|
432
|
+
pathParams: this._pathParams,
|
|
433
|
+
query: this._query,
|
|
434
|
+
headers: this._headers,
|
|
435
|
+
body: this._body,
|
|
436
|
+
responses: {
|
|
437
|
+
...this._responses,
|
|
438
|
+
...errorResponses,
|
|
439
|
+
} as unknown as Omit<
|
|
440
|
+
TResponses,
|
|
441
|
+
keyof ResponsesFromErrorDefinitions<TErrorDefs>
|
|
442
|
+
> &
|
|
443
|
+
ResponsesFromErrorDefinitions<TErrorDefs>,
|
|
444
|
+
metadata: {
|
|
445
|
+
...this._meta,
|
|
446
|
+
errors: errorDefs,
|
|
447
|
+
} as Omit<TMeta, "errors"> & { errors: TErrorDefs },
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Set or merge contract metadata (chainable builder method)
|
|
453
|
+
*/
|
|
454
|
+
meta<TNewMeta extends ContractMeta>(
|
|
455
|
+
newMeta: TNewMeta,
|
|
456
|
+
): ContractBuilder<
|
|
457
|
+
TMethod,
|
|
458
|
+
TPathParams,
|
|
459
|
+
TQuery,
|
|
460
|
+
TBody,
|
|
461
|
+
THeaders,
|
|
462
|
+
TResponses,
|
|
463
|
+
Omit<TMeta, keyof TNewMeta> & TNewMeta,
|
|
464
|
+
TPath
|
|
465
|
+
> {
|
|
466
|
+
return new ContractBuilder({
|
|
467
|
+
kind: this.kind,
|
|
468
|
+
name: this.name,
|
|
469
|
+
namespace: this.namespace,
|
|
470
|
+
localName: this.localName,
|
|
471
|
+
method: this.method,
|
|
472
|
+
path: this._path,
|
|
473
|
+
pathParams: this._pathParams,
|
|
474
|
+
query: this._query,
|
|
475
|
+
headers: this._headers,
|
|
476
|
+
body: this._body,
|
|
477
|
+
responses: this._responses,
|
|
478
|
+
metadata: {
|
|
479
|
+
...this._meta,
|
|
480
|
+
...newMeta,
|
|
481
|
+
} as unknown as Omit<TMeta, keyof TNewMeta> & TNewMeta,
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Set or merge OpenAPI metadata for this operation (chainable)
|
|
487
|
+
*/
|
|
488
|
+
openapi(
|
|
489
|
+
patch: Partial<OpenAPIOperationMeta>,
|
|
490
|
+
): ContractBuilder<
|
|
491
|
+
TMethod,
|
|
492
|
+
TPathParams,
|
|
493
|
+
TQuery,
|
|
494
|
+
TBody,
|
|
495
|
+
THeaders,
|
|
496
|
+
TResponses,
|
|
497
|
+
TMeta,
|
|
498
|
+
TPath
|
|
499
|
+
> {
|
|
500
|
+
const existingOpenApi = this._meta.openapi ?? {};
|
|
501
|
+
return new ContractBuilder({
|
|
502
|
+
kind: this.kind,
|
|
503
|
+
name: this.name,
|
|
504
|
+
namespace: this.namespace,
|
|
505
|
+
localName: this.localName,
|
|
506
|
+
method: this.method,
|
|
507
|
+
path: this._path,
|
|
508
|
+
pathParams: this._pathParams,
|
|
509
|
+
query: this._query,
|
|
510
|
+
headers: this._headers,
|
|
511
|
+
body: this._body,
|
|
512
|
+
responses: this._responses,
|
|
513
|
+
metadata: {
|
|
514
|
+
...this._meta,
|
|
515
|
+
openapi: {
|
|
516
|
+
...existingOpenApi,
|
|
517
|
+
...patch,
|
|
518
|
+
},
|
|
519
|
+
} as TMeta,
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Options for creating a contract with the factory function
|
|
526
|
+
*/
|
|
527
|
+
export type CreateContractOptions<
|
|
528
|
+
TMethod extends HttpMethod = HttpMethod,
|
|
529
|
+
TPath extends string = string,
|
|
530
|
+
> = {
|
|
531
|
+
/** HTTP method (GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS) */
|
|
532
|
+
method: TMethod;
|
|
533
|
+
/** URL path template (e.g., "/api/users/:id") */
|
|
534
|
+
path: TPath;
|
|
535
|
+
/** Optional contract name (auto-generated from method + path if not provided) */
|
|
536
|
+
name?: string;
|
|
537
|
+
};
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Create a new HTTP contract with the builder pattern.
|
|
541
|
+
*
|
|
542
|
+
* @example
|
|
543
|
+
* ```ts
|
|
544
|
+
* const getTodo = createContract({
|
|
545
|
+
* method: "GET",
|
|
546
|
+
* path: "/api/todos/:id",
|
|
547
|
+
* })
|
|
548
|
+
* .pathParams(z.object({ id: z.string() }))
|
|
549
|
+
* .responses({ 200: TodoSchema });
|
|
550
|
+
* ```
|
|
551
|
+
*/
|
|
552
|
+
export function createContract<
|
|
553
|
+
TMethod extends HttpMethod,
|
|
554
|
+
const TPath extends string,
|
|
555
|
+
>(
|
|
556
|
+
options: CreateContractOptions<TMethod, TPath>,
|
|
557
|
+
): ContractBuilder<
|
|
558
|
+
TMethod,
|
|
559
|
+
null,
|
|
560
|
+
null,
|
|
561
|
+
null,
|
|
562
|
+
null,
|
|
563
|
+
Record<never, never>,
|
|
564
|
+
ContractMeta,
|
|
565
|
+
TPath
|
|
566
|
+
> {
|
|
567
|
+
parsePathTemplate(options.path);
|
|
568
|
+
const name =
|
|
569
|
+
options.name || generateContractName(options.method, options.path);
|
|
570
|
+
return new ContractBuilder({
|
|
571
|
+
kind: "http",
|
|
572
|
+
name,
|
|
573
|
+
localName: name,
|
|
574
|
+
method: options.method,
|
|
575
|
+
path: options.path,
|
|
576
|
+
pathParams: null,
|
|
577
|
+
query: null,
|
|
578
|
+
headers: null,
|
|
579
|
+
body: null,
|
|
580
|
+
responses: {},
|
|
581
|
+
metadata: {},
|
|
582
|
+
});
|
|
583
|
+
}
|