@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,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";