@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.
Files changed (331) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +288 -0
  3. package/dist/application/index.d.ts +260 -0
  4. package/dist/application/index.d.ts.map +1 -0
  5. package/dist/application/index.js +324 -0
  6. package/dist/application/index.js.map +1 -0
  7. package/dist/client/client.d.ts +241 -0
  8. package/dist/client/client.d.ts.map +1 -0
  9. package/dist/client/client.js +531 -0
  10. package/dist/client/client.js.map +1 -0
  11. package/dist/client/index.d.ts +10 -0
  12. package/dist/client/index.d.ts.map +1 -0
  13. package/dist/client/index.js +8 -0
  14. package/dist/client/index.js.map +1 -0
  15. package/dist/client/types.d.ts +139 -0
  16. package/dist/client/types.d.ts.map +1 -0
  17. package/dist/client/types.js +2 -0
  18. package/dist/client/types.js.map +1 -0
  19. package/dist/config/index.d.ts +122 -0
  20. package/dist/config/index.d.ts.map +1 -0
  21. package/dist/config/index.js +216 -0
  22. package/dist/config/index.js.map +1 -0
  23. package/dist/contracts/contract-builder.d.ts +121 -0
  24. package/dist/contracts/contract-builder.d.ts.map +1 -0
  25. package/dist/contracts/contract-builder.js +346 -0
  26. package/dist/contracts/contract-builder.js.map +1 -0
  27. package/dist/contracts/contract-group.d.ts +106 -0
  28. package/dist/contracts/contract-group.d.ts.map +1 -0
  29. package/dist/contracts/contract-group.js +240 -0
  30. package/dist/contracts/contract-group.js.map +1 -0
  31. package/dist/contracts/contract-like.d.ts +21 -0
  32. package/dist/contracts/contract-like.d.ts.map +1 -0
  33. package/dist/contracts/contract-like.js +9 -0
  34. package/dist/contracts/contract-like.js.map +1 -0
  35. package/dist/contracts/index.d.ts +15 -0
  36. package/dist/contracts/index.d.ts.map +1 -0
  37. package/dist/contracts/index.js +11 -0
  38. package/dist/contracts/index.js.map +1 -0
  39. package/dist/contracts/openapi-meta.d.ts +23 -0
  40. package/dist/contracts/openapi-meta.d.ts.map +1 -0
  41. package/dist/contracts/openapi-meta.js +2 -0
  42. package/dist/contracts/openapi-meta.js.map +1 -0
  43. package/dist/contracts/path-template.d.ts +17 -0
  44. package/dist/contracts/path-template.d.ts.map +1 -0
  45. package/dist/contracts/path-template.js +50 -0
  46. package/dist/contracts/path-template.js.map +1 -0
  47. package/dist/contracts/rate-limit.d.ts +50 -0
  48. package/dist/contracts/rate-limit.d.ts.map +1 -0
  49. package/dist/contracts/rate-limit.js +2 -0
  50. package/dist/contracts/rate-limit.js.map +1 -0
  51. package/dist/contracts/types.d.ts +97 -0
  52. package/dist/contracts/types.d.ts.map +1 -0
  53. package/dist/contracts/types.js +54 -0
  54. package/dist/contracts/types.js.map +1 -0
  55. package/dist/contracts/utils.d.ts +3 -0
  56. package/dist/contracts/utils.d.ts.map +1 -0
  57. package/dist/contracts/utils.js +44 -0
  58. package/dist/contracts/utils.js.map +1 -0
  59. package/dist/domain/entity.d.ts +87 -0
  60. package/dist/domain/entity.d.ts.map +1 -0
  61. package/dist/domain/entity.js +155 -0
  62. package/dist/domain/entity.js.map +1 -0
  63. package/dist/domain/events.d.ts +41 -0
  64. package/dist/domain/events.d.ts.map +1 -0
  65. package/dist/domain/events.js +21 -0
  66. package/dist/domain/events.js.map +1 -0
  67. package/dist/domain/index.d.ts +14 -0
  68. package/dist/domain/index.d.ts.map +1 -0
  69. package/dist/domain/index.js +14 -0
  70. package/dist/domain/index.js.map +1 -0
  71. package/dist/domain/value-object.d.ts +60 -0
  72. package/dist/domain/value-object.d.ts.map +1 -0
  73. package/dist/domain/value-object.js +87 -0
  74. package/dist/domain/value-object.js.map +1 -0
  75. package/dist/errors/catalog.d.ts +71 -0
  76. package/dist/errors/catalog.d.ts.map +1 -0
  77. package/dist/errors/catalog.js +71 -0
  78. package/dist/errors/catalog.js.map +1 -0
  79. package/dist/errors/http.d.ts +77 -0
  80. package/dist/errors/http.d.ts.map +1 -0
  81. package/dist/errors/http.js +74 -0
  82. package/dist/errors/http.js.map +1 -0
  83. package/dist/errors/index.d.ts +10 -0
  84. package/dist/errors/index.d.ts.map +1 -0
  85. package/dist/errors/index.js +14 -0
  86. package/dist/errors/index.js.map +1 -0
  87. package/dist/errors/response.d.ts +26 -0
  88. package/dist/errors/response.d.ts.map +1 -0
  89. package/dist/errors/response.js +34 -0
  90. package/dist/errors/response.js.map +1 -0
  91. package/dist/errors/validation.d.ts +18 -0
  92. package/dist/errors/validation.d.ts.map +1 -0
  93. package/dist/errors/validation.js +21 -0
  94. package/dist/errors/validation.js.map +1 -0
  95. package/dist/events/index.d.ts +58 -0
  96. package/dist/events/index.d.ts.map +1 -0
  97. package/dist/events/index.js +102 -0
  98. package/dist/events/index.js.map +1 -0
  99. package/dist/jobs/index.d.ts +56 -0
  100. package/dist/jobs/index.d.ts.map +1 -0
  101. package/dist/jobs/index.js +89 -0
  102. package/dist/jobs/index.js.map +1 -0
  103. package/dist/mail/index.d.ts +75 -0
  104. package/dist/mail/index.d.ts.map +1 -0
  105. package/dist/mail/index.js +84 -0
  106. package/dist/mail/index.js.map +1 -0
  107. package/dist/openapi/index.d.ts +207 -0
  108. package/dist/openapi/index.d.ts.map +1 -0
  109. package/dist/openapi/index.js +449 -0
  110. package/dist/openapi/index.js.map +1 -0
  111. package/dist/openapi/schema-introspector.d.ts +38 -0
  112. package/dist/openapi/schema-introspector.d.ts.map +1 -0
  113. package/dist/openapi/schema-introspector.js +67 -0
  114. package/dist/openapi/schema-introspector.js.map +1 -0
  115. package/dist/ports/audit.d.ts +58 -0
  116. package/dist/ports/audit.d.ts.map +1 -0
  117. package/dist/ports/audit.js +74 -0
  118. package/dist/ports/audit.js.map +1 -0
  119. package/dist/ports/auth.d.ts +23 -0
  120. package/dist/ports/auth.d.ts.map +1 -0
  121. package/dist/ports/auth.js +31 -0
  122. package/dist/ports/auth.js.map +1 -0
  123. package/dist/ports/builder.d.ts +61 -0
  124. package/dist/ports/builder.d.ts.map +1 -0
  125. package/dist/ports/builder.js +48 -0
  126. package/dist/ports/builder.js.map +1 -0
  127. package/dist/ports/cache.d.ts +15 -0
  128. package/dist/ports/cache.d.ts.map +1 -0
  129. package/dist/ports/cache.js +57 -0
  130. package/dist/ports/cache.js.map +1 -0
  131. package/dist/ports/clock.d.ts +10 -0
  132. package/dist/ports/clock.d.ts.map +1 -0
  133. package/dist/ports/clock.js +21 -0
  134. package/dist/ports/clock.js.map +1 -0
  135. package/dist/ports/events.d.ts +71 -0
  136. package/dist/ports/events.d.ts.map +1 -0
  137. package/dist/ports/events.js +2 -0
  138. package/dist/ports/events.js.map +1 -0
  139. package/dist/ports/id-generator.d.ts +12 -0
  140. package/dist/ports/id-generator.d.ts.map +1 -0
  141. package/dist/ports/id-generator.js +22 -0
  142. package/dist/ports/id-generator.js.map +1 -0
  143. package/dist/ports/index.d.ts +98 -0
  144. package/dist/ports/index.d.ts.map +1 -0
  145. package/dist/ports/index.js +67 -0
  146. package/dist/ports/index.js.map +1 -0
  147. package/dist/ports/logger.d.ts +22 -0
  148. package/dist/ports/logger.d.ts.map +1 -0
  149. package/dist/ports/logger.js +34 -0
  150. package/dist/ports/logger.js.map +1 -0
  151. package/dist/ports/mailer.d.ts +6 -0
  152. package/dist/ports/mailer.d.ts.map +1 -0
  153. package/dist/ports/mailer.js +2 -0
  154. package/dist/ports/mailer.js.map +1 -0
  155. package/dist/ports/policy.d.ts +53 -0
  156. package/dist/ports/policy.d.ts.map +1 -0
  157. package/dist/ports/policy.js +81 -0
  158. package/dist/ports/policy.js.map +1 -0
  159. package/dist/ports/rate-limit.d.ts +41 -0
  160. package/dist/ports/rate-limit.d.ts.map +1 -0
  161. package/dist/ports/rate-limit.js +37 -0
  162. package/dist/ports/rate-limit.js.map +1 -0
  163. package/dist/ports/redaction.d.ts +26 -0
  164. package/dist/ports/redaction.d.ts.map +1 -0
  165. package/dist/ports/redaction.js +126 -0
  166. package/dist/ports/redaction.js.map +1 -0
  167. package/dist/ports/schedules.d.ts +9 -0
  168. package/dist/ports/schedules.d.ts.map +1 -0
  169. package/dist/ports/schedules.js +2 -0
  170. package/dist/ports/schedules.js.map +1 -0
  171. package/dist/ports/storage.d.ts +47 -0
  172. package/dist/ports/storage.d.ts.map +1 -0
  173. package/dist/ports/storage.js +185 -0
  174. package/dist/ports/storage.js.map +1 -0
  175. package/dist/ports/testing.d.ts +73 -0
  176. package/dist/ports/testing.d.ts.map +1 -0
  177. package/dist/ports/testing.js +105 -0
  178. package/dist/ports/testing.js.map +1 -0
  179. package/dist/ports/unit-of-work.d.ts +56 -0
  180. package/dist/ports/unit-of-work.d.ts.map +1 -0
  181. package/dist/ports/unit-of-work.js +64 -0
  182. package/dist/ports/unit-of-work.js.map +1 -0
  183. package/dist/providers/index.d.ts +8 -0
  184. package/dist/providers/index.d.ts.map +1 -0
  185. package/dist/providers/index.js +8 -0
  186. package/dist/providers/index.js.map +1 -0
  187. package/dist/providers/instrumentation.d.ts +91 -0
  188. package/dist/providers/instrumentation.d.ts.map +1 -0
  189. package/dist/providers/instrumentation.js +93 -0
  190. package/dist/providers/instrumentation.js.map +1 -0
  191. package/dist/providers/provider.d.ts +146 -0
  192. package/dist/providers/provider.d.ts.map +1 -0
  193. package/dist/providers/provider.js +31 -0
  194. package/dist/providers/provider.js.map +1 -0
  195. package/dist/schedules/index.d.ts +105 -0
  196. package/dist/schedules/index.d.ts.map +1 -0
  197. package/dist/schedules/index.js +178 -0
  198. package/dist/schedules/index.js.map +1 -0
  199. package/dist/server/contract-like.d.ts +5 -0
  200. package/dist/server/contract-like.d.ts.map +1 -0
  201. package/dist/server/contract-like.js +5 -0
  202. package/dist/server/contract-like.js.map +1 -0
  203. package/dist/server/health.d.ts +41 -0
  204. package/dist/server/health.d.ts.map +1 -0
  205. package/dist/server/health.js +46 -0
  206. package/dist/server/health.js.map +1 -0
  207. package/dist/server/hooks/auth.d.ts +42 -0
  208. package/dist/server/hooks/auth.d.ts.map +1 -0
  209. package/dist/server/hooks/auth.js +61 -0
  210. package/dist/server/hooks/auth.js.map +1 -0
  211. package/dist/server/hooks/cors.d.ts +13 -0
  212. package/dist/server/hooks/cors.d.ts.map +1 -0
  213. package/dist/server/hooks/cors.js +70 -0
  214. package/dist/server/hooks/cors.js.map +1 -0
  215. package/dist/server/hooks/errors.d.ts +66 -0
  216. package/dist/server/hooks/errors.d.ts.map +1 -0
  217. package/dist/server/hooks/errors.js +83 -0
  218. package/dist/server/hooks/errors.js.map +1 -0
  219. package/dist/server/hooks/index.d.ts +12 -0
  220. package/dist/server/hooks/index.d.ts.map +1 -0
  221. package/dist/server/hooks/index.js +12 -0
  222. package/dist/server/hooks/index.js.map +1 -0
  223. package/dist/server/hooks/logging.d.ts +33 -0
  224. package/dist/server/hooks/logging.d.ts.map +1 -0
  225. package/dist/server/hooks/logging.js +90 -0
  226. package/dist/server/hooks/logging.js.map +1 -0
  227. package/dist/server/hooks/rate-limit.d.ts +29 -0
  228. package/dist/server/hooks/rate-limit.d.ts.map +1 -0
  229. package/dist/server/hooks/rate-limit.js +93 -0
  230. package/dist/server/hooks/rate-limit.js.map +1 -0
  231. package/dist/server/hooks/utils.d.ts +9 -0
  232. package/dist/server/hooks/utils.d.ts.map +1 -0
  233. package/dist/server/hooks/utils.js +16 -0
  234. package/dist/server/hooks/utils.js.map +1 -0
  235. package/dist/server/hooks.d.ts +2 -0
  236. package/dist/server/hooks.d.ts.map +1 -0
  237. package/dist/server/hooks.js +2 -0
  238. package/dist/server/hooks.js.map +1 -0
  239. package/dist/server/http.d.ts +124 -0
  240. package/dist/server/http.d.ts.map +1 -0
  241. package/dist/server/http.js +2 -0
  242. package/dist/server/http.js.map +1 -0
  243. package/dist/server/index.d.ts +19 -0
  244. package/dist/server/index.d.ts.map +1 -0
  245. package/dist/server/index.js +15 -0
  246. package/dist/server/index.js.map +1 -0
  247. package/dist/server/openapi.d.ts +32 -0
  248. package/dist/server/openapi.d.ts.map +1 -0
  249. package/dist/server/openapi.js +43 -0
  250. package/dist/server/openapi.js.map +1 -0
  251. package/dist/server/providers/index.d.ts +4 -0
  252. package/dist/server/providers/index.d.ts.map +1 -0
  253. package/dist/server/providers/index.js +4 -0
  254. package/dist/server/providers/index.js.map +1 -0
  255. package/dist/server/providers/loadProviderConfig.d.ts +7 -0
  256. package/dist/server/providers/loadProviderConfig.d.ts.map +1 -0
  257. package/dist/server/providers/loadProviderConfig.js +42 -0
  258. package/dist/server/providers/loadProviderConfig.js.map +1 -0
  259. package/dist/server/server.d.ts +86 -0
  260. package/dist/server/server.d.ts.map +1 -0
  261. package/dist/server/server.js +1031 -0
  262. package/dist/server/server.js.map +1 -0
  263. package/dist/server/types.d.ts +3 -0
  264. package/dist/server/types.d.ts.map +1 -0
  265. package/dist/server/types.js +3 -0
  266. package/dist/server/types.js.map +1 -0
  267. package/package.json +129 -0
  268. package/src/application/index.ts +747 -0
  269. package/src/client/client.ts +1105 -0
  270. package/src/client/index.ts +45 -0
  271. package/src/client/types.ts +305 -0
  272. package/src/config/index.ts +497 -0
  273. package/src/contracts/contract-builder.ts +583 -0
  274. package/src/contracts/contract-group.ts +502 -0
  275. package/src/contracts/contract-like.ts +29 -0
  276. package/src/contracts/index.ts +53 -0
  277. package/src/contracts/openapi-meta.ts +22 -0
  278. package/src/contracts/path-template.ts +91 -0
  279. package/src/contracts/rate-limit.ts +50 -0
  280. package/src/contracts/types.ts +207 -0
  281. package/src/contracts/utils.ts +56 -0
  282. package/src/domain/entity.ts +256 -0
  283. package/src/domain/events.ts +52 -0
  284. package/src/domain/index.ts +18 -0
  285. package/src/domain/value-object.ts +135 -0
  286. package/src/errors/catalog.ts +149 -0
  287. package/src/errors/http.ts +80 -0
  288. package/src/errors/index.ts +28 -0
  289. package/src/errors/response.ts +54 -0
  290. package/src/errors/validation.ts +35 -0
  291. package/src/events/index.ts +246 -0
  292. package/src/jobs/index.ts +211 -0
  293. package/src/mail/index.ts +177 -0
  294. package/src/openapi/index.ts +865 -0
  295. package/src/openapi/schema-introspector.ts +107 -0
  296. package/src/ports/audit.ts +176 -0
  297. package/src/ports/auth.ts +76 -0
  298. package/src/ports/builder.ts +97 -0
  299. package/src/ports/cache.ts +94 -0
  300. package/src/ports/clock.ts +34 -0
  301. package/src/ports/events.ts +100 -0
  302. package/src/ports/id-generator.ts +36 -0
  303. package/src/ports/index.ts +221 -0
  304. package/src/ports/logger.ts +67 -0
  305. package/src/ports/policy.ts +242 -0
  306. package/src/ports/rate-limit.ts +91 -0
  307. package/src/ports/redaction.ts +199 -0
  308. package/src/ports/storage.ts +282 -0
  309. package/src/ports/testing.ts +234 -0
  310. package/src/ports/unit-of-work.ts +134 -0
  311. package/src/providers/index.ts +40 -0
  312. package/src/providers/instrumentation.ts +248 -0
  313. package/src/providers/provider.ts +191 -0
  314. package/src/schedules/index.ts +442 -0
  315. package/src/server/contract-like.ts +8 -0
  316. package/src/server/health.ts +82 -0
  317. package/src/server/hooks/auth.ts +147 -0
  318. package/src/server/hooks/cors.ts +87 -0
  319. package/src/server/hooks/errors.ts +126 -0
  320. package/src/server/hooks/index.ts +43 -0
  321. package/src/server/hooks/logging.ts +121 -0
  322. package/src/server/hooks/rate-limit.ts +171 -0
  323. package/src/server/hooks/utils.ts +16 -0
  324. package/src/server/hooks.ts +1 -0
  325. package/src/server/http.ts +189 -0
  326. package/src/server/index.ts +35 -0
  327. package/src/server/openapi.ts +72 -0
  328. package/src/server/providers/index.ts +3 -0
  329. package/src/server/providers/loadProviderConfig.ts +72 -0
  330. package/src/server/server.ts +1521 -0
  331. 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
+ }