@beignet/core 0.0.1 → 0.0.3

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 (287) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +202 -8
  3. package/dist/application/index.d.ts +93 -9
  4. package/dist/application/index.d.ts.map +1 -1
  5. package/dist/application/index.js +11 -11
  6. package/dist/application/index.js.map +1 -1
  7. package/dist/client/client.d.ts +73 -12
  8. package/dist/client/client.d.ts.map +1 -1
  9. package/dist/client/client.js +37 -12
  10. package/dist/client/client.js.map +1 -1
  11. package/dist/client/index.d.ts +12 -0
  12. package/dist/client/index.d.ts.map +1 -1
  13. package/dist/client/index.js +6 -0
  14. package/dist/client/index.js.map +1 -1
  15. package/dist/client/types.d.ts +69 -8
  16. package/dist/client/types.d.ts.map +1 -1
  17. package/dist/config/index.d.ts +84 -0
  18. package/dist/config/index.d.ts.map +1 -1
  19. package/dist/config/index.js +36 -0
  20. package/dist/config/index.js.map +1 -1
  21. package/dist/contracts/contract-builder.d.ts +49 -22
  22. package/dist/contracts/contract-builder.d.ts.map +1 -1
  23. package/dist/contracts/contract-builder.js +48 -21
  24. package/dist/contracts/contract-builder.js.map +1 -1
  25. package/dist/contracts/contract-group.d.ts +35 -19
  26. package/dist/contracts/contract-group.d.ts.map +1 -1
  27. package/dist/contracts/contract-group.js +35 -19
  28. package/dist/contracts/contract-group.js.map +1 -1
  29. package/dist/contracts/contract-like.d.ts +4 -4
  30. package/dist/contracts/contract-like.d.ts.map +1 -1
  31. package/dist/contracts/contract-like.js +2 -1
  32. package/dist/contracts/contract-like.js.map +1 -1
  33. package/dist/contracts/index.d.ts +28 -0
  34. package/dist/contracts/index.d.ts.map +1 -1
  35. package/dist/contracts/index.js +12 -0
  36. package/dist/contracts/index.js.map +1 -1
  37. package/dist/contracts/openapi-meta.d.ts +8 -8
  38. package/dist/contracts/openapi-meta.d.ts.map +1 -1
  39. package/dist/contracts/path-template.d.ts +27 -0
  40. package/dist/contracts/path-template.d.ts.map +1 -1
  41. package/dist/contracts/path-template.js +6 -0
  42. package/dist/contracts/path-template.js.map +1 -1
  43. package/dist/contracts/types.d.ts +104 -10
  44. package/dist/contracts/types.d.ts.map +1 -1
  45. package/dist/contracts/types.js +15 -0
  46. package/dist/contracts/types.js.map +1 -1
  47. package/dist/contracts/utils.d.ts +6 -0
  48. package/dist/contracts/utils.d.ts.map +1 -1
  49. package/dist/contracts/utils.js +6 -0
  50. package/dist/contracts/utils.js.map +1 -1
  51. package/dist/domain/entity.d.ts +22 -11
  52. package/dist/domain/entity.d.ts.map +1 -1
  53. package/dist/domain/entity.js +5 -1
  54. package/dist/domain/entity.js.map +1 -1
  55. package/dist/domain/events.d.ts +5 -2
  56. package/dist/domain/events.d.ts.map +1 -1
  57. package/dist/domain/events.js +4 -1
  58. package/dist/domain/events.js.map +1 -1
  59. package/dist/domain/value-object.d.ts +19 -9
  60. package/dist/domain/value-object.d.ts.map +1 -1
  61. package/dist/domain/value-object.js +5 -1
  62. package/dist/domain/value-object.js.map +1 -1
  63. package/dist/errors/catalog.d.ts +40 -16
  64. package/dist/errors/catalog.d.ts.map +1 -1
  65. package/dist/errors/catalog.js +18 -7
  66. package/dist/errors/catalog.js.map +1 -1
  67. package/dist/errors/response.d.ts +16 -4
  68. package/dist/errors/response.d.ts.map +1 -1
  69. package/dist/errors/response.js +3 -3
  70. package/dist/errors/response.js.map +1 -1
  71. package/dist/errors/validation.d.ts +10 -1
  72. package/dist/errors/validation.d.ts.map +1 -1
  73. package/dist/errors/validation.js +3 -0
  74. package/dist/errors/validation.js.map +1 -1
  75. package/dist/events/index.d.ts +133 -0
  76. package/dist/events/index.d.ts.map +1 -1
  77. package/dist/events/index.js +30 -0
  78. package/dist/events/index.js.map +1 -1
  79. package/dist/idempotency/index.d.ts +355 -0
  80. package/dist/idempotency/index.d.ts.map +1 -0
  81. package/dist/idempotency/index.js +360 -0
  82. package/dist/idempotency/index.js.map +1 -0
  83. package/dist/jobs/index.d.ts +248 -4
  84. package/dist/jobs/index.d.ts.map +1 -1
  85. package/dist/jobs/index.js +183 -1
  86. package/dist/jobs/index.js.map +1 -1
  87. package/dist/mail/index.d.ts +149 -0
  88. package/dist/mail/index.d.ts.map +1 -1
  89. package/dist/mail/index.js +30 -0
  90. package/dist/mail/index.js.map +1 -1
  91. package/dist/notifications/index.d.ts +369 -0
  92. package/dist/notifications/index.d.ts.map +1 -0
  93. package/dist/notifications/index.js +310 -0
  94. package/dist/notifications/index.js.map +1 -0
  95. package/dist/openapi/index.d.ts +132 -16
  96. package/dist/openapi/index.d.ts.map +1 -1
  97. package/dist/openapi/index.js +1 -1
  98. package/dist/openapi/index.js.map +1 -1
  99. package/dist/outbox/index.d.ts +474 -0
  100. package/dist/outbox/index.d.ts.map +1 -0
  101. package/dist/outbox/index.js +538 -0
  102. package/dist/outbox/index.js.map +1 -0
  103. package/dist/pagination/index.d.ts +166 -0
  104. package/dist/pagination/index.d.ts.map +1 -0
  105. package/dist/pagination/index.js +96 -0
  106. package/dist/pagination/index.js.map +1 -0
  107. package/dist/ports/audit.d.ts +271 -0
  108. package/dist/ports/audit.d.ts.map +1 -1
  109. package/dist/ports/audit.js +128 -0
  110. package/dist/ports/audit.js.map +1 -1
  111. package/dist/ports/auth.d.ts +70 -0
  112. package/dist/ports/auth.d.ts.map +1 -1
  113. package/dist/ports/auth.js +30 -0
  114. package/dist/ports/auth.js.map +1 -1
  115. package/dist/ports/cache.d.ts +41 -0
  116. package/dist/ports/cache.d.ts.map +1 -1
  117. package/dist/ports/cache.js +10 -0
  118. package/dist/ports/cache.js.map +1 -1
  119. package/dist/ports/clock.d.ts +38 -0
  120. package/dist/ports/clock.d.ts.map +1 -1
  121. package/dist/ports/clock.js +20 -0
  122. package/dist/ports/clock.js.map +1 -1
  123. package/dist/ports/id-generator.d.ts +37 -0
  124. package/dist/ports/id-generator.d.ts.map +1 -1
  125. package/dist/ports/id-generator.js +22 -0
  126. package/dist/ports/id-generator.js.map +1 -1
  127. package/dist/ports/index.d.ts +83 -0
  128. package/dist/ports/index.d.ts.map +1 -1
  129. package/dist/ports/index.js +41 -5
  130. package/dist/ports/index.js.map +1 -1
  131. package/dist/ports/logger.d.ts +56 -0
  132. package/dist/ports/logger.d.ts.map +1 -1
  133. package/dist/ports/logger.js +17 -0
  134. package/dist/ports/logger.js.map +1 -1
  135. package/dist/ports/policy.d.ts +132 -0
  136. package/dist/ports/policy.d.ts.map +1 -1
  137. package/dist/ports/policy.js +45 -0
  138. package/dist/ports/policy.js.map +1 -1
  139. package/dist/ports/rate-limit.d.ts +25 -0
  140. package/dist/ports/rate-limit.d.ts.map +1 -1
  141. package/dist/ports/rate-limit.js +10 -0
  142. package/dist/ports/rate-limit.js.map +1 -1
  143. package/dist/ports/redaction.d.ts +101 -0
  144. package/dist/ports/redaction.d.ts.map +1 -1
  145. package/dist/ports/redaction.js +59 -0
  146. package/dist/ports/redaction.js.map +1 -1
  147. package/dist/ports/storage.d.ts +100 -0
  148. package/dist/ports/storage.d.ts.map +1 -1
  149. package/dist/ports/storage.js +10 -0
  150. package/dist/ports/storage.js.map +1 -1
  151. package/dist/ports/testing.d.ts +47 -0
  152. package/dist/ports/testing.d.ts.map +1 -1
  153. package/dist/ports/testing.js +23 -0
  154. package/dist/ports/testing.js.map +1 -1
  155. package/dist/ports/unit-of-work.d.ts +60 -3
  156. package/dist/ports/unit-of-work.d.ts.map +1 -1
  157. package/dist/ports/unit-of-work.js +11 -2
  158. package/dist/ports/unit-of-work.js.map +1 -1
  159. package/dist/providers/instrumentation.d.ts +205 -1
  160. package/dist/providers/instrumentation.d.ts.map +1 -1
  161. package/dist/providers/instrumentation.js +14 -0
  162. package/dist/providers/instrumentation.js.map +1 -1
  163. package/dist/providers/provider.d.ts +14 -1
  164. package/dist/providers/provider.d.ts.map +1 -1
  165. package/dist/providers/provider.js.map +1 -1
  166. package/dist/schedules/index.d.ts +246 -0
  167. package/dist/schedules/index.d.ts.map +1 -1
  168. package/dist/schedules/index.js +27 -0
  169. package/dist/schedules/index.js.map +1 -1
  170. package/dist/server/health.d.ts +14 -5
  171. package/dist/server/health.d.ts.map +1 -1
  172. package/dist/server/health.js +5 -2
  173. package/dist/server/health.js.map +1 -1
  174. package/dist/server/hooks/auth.d.ts +68 -26
  175. package/dist/server/hooks/auth.d.ts.map +1 -1
  176. package/dist/server/hooks/auth.js +44 -55
  177. package/dist/server/hooks/auth.js.map +1 -1
  178. package/dist/server/hooks/cors.d.ts +27 -0
  179. package/dist/server/hooks/cors.d.ts.map +1 -1
  180. package/dist/server/hooks/cors.js +12 -0
  181. package/dist/server/hooks/cors.js.map +1 -1
  182. package/dist/server/hooks/errors.d.ts +15 -6
  183. package/dist/server/hooks/errors.d.ts.map +1 -1
  184. package/dist/server/hooks/errors.js.map +1 -1
  185. package/dist/server/hooks/index.d.ts +4 -1
  186. package/dist/server/hooks/index.d.ts.map +1 -1
  187. package/dist/server/hooks/index.js +3 -0
  188. package/dist/server/hooks/index.js.map +1 -1
  189. package/dist/server/hooks/logging.d.ts +36 -0
  190. package/dist/server/hooks/logging.d.ts.map +1 -1
  191. package/dist/server/hooks/logging.js +6 -0
  192. package/dist/server/hooks/logging.js.map +1 -1
  193. package/dist/server/hooks/rate-limit.d.ts +33 -0
  194. package/dist/server/hooks/rate-limit.d.ts.map +1 -1
  195. package/dist/server/hooks/rate-limit.js +11 -0
  196. package/dist/server/hooks/rate-limit.js.map +1 -1
  197. package/dist/server/http.d.ts +222 -0
  198. package/dist/server/http.d.ts.map +1 -1
  199. package/dist/server/http.js +20 -1
  200. package/dist/server/http.js.map +1 -1
  201. package/dist/server/index.d.ts +19 -1
  202. package/dist/server/index.d.ts.map +1 -1
  203. package/dist/server/index.js +7 -1
  204. package/dist/server/index.js.map +1 -1
  205. package/dist/server/openapi.d.ts +5 -3
  206. package/dist/server/openapi.d.ts.map +1 -1
  207. package/dist/server/openapi.js +4 -2
  208. package/dist/server/openapi.js.map +1 -1
  209. package/dist/server/providers/loadProviderConfig.d.ts +9 -0
  210. package/dist/server/providers/loadProviderConfig.d.ts.map +1 -1
  211. package/dist/server/providers/loadProviderConfig.js +9 -0
  212. package/dist/server/providers/loadProviderConfig.js.map +1 -1
  213. package/dist/server/server.d.ts +159 -19
  214. package/dist/server/server.d.ts.map +1 -1
  215. package/dist/server/server.js +72 -31
  216. package/dist/server/server.js.map +1 -1
  217. package/dist/testing/index.d.ts +171 -0
  218. package/dist/testing/index.d.ts.map +1 -0
  219. package/dist/testing/index.js +127 -0
  220. package/dist/testing/index.js.map +1 -0
  221. package/dist/uploads/client.d.ts +278 -0
  222. package/dist/uploads/client.d.ts.map +1 -0
  223. package/dist/uploads/client.js +428 -0
  224. package/dist/uploads/client.js.map +1 -0
  225. package/dist/uploads/index.d.ts +361 -0
  226. package/dist/uploads/index.d.ts.map +1 -0
  227. package/dist/uploads/index.js +543 -0
  228. package/dist/uploads/index.js.map +1 -0
  229. package/package.json +31 -2
  230. package/src/application/index.ts +85 -22
  231. package/src/client/client.ts +73 -12
  232. package/src/client/index.ts +12 -0
  233. package/src/client/types.ts +70 -9
  234. package/src/config/index.ts +86 -0
  235. package/src/contracts/contract-builder.ts +49 -22
  236. package/src/contracts/contract-group.ts +35 -19
  237. package/src/contracts/contract-like.ts +4 -4
  238. package/src/contracts/index.ts +28 -1
  239. package/src/contracts/openapi-meta.ts +8 -8
  240. package/src/contracts/path-template.ts +27 -0
  241. package/src/contracts/types.ts +111 -10
  242. package/src/contracts/utils.ts +6 -0
  243. package/src/domain/entity.ts +22 -11
  244. package/src/domain/events.ts +5 -2
  245. package/src/domain/value-object.ts +19 -9
  246. package/src/errors/catalog.ts +40 -16
  247. package/src/errors/response.ts +16 -4
  248. package/src/errors/validation.ts +10 -1
  249. package/src/events/index.ts +134 -0
  250. package/src/idempotency/index.ts +767 -0
  251. package/src/jobs/index.ts +437 -5
  252. package/src/mail/index.ts +149 -0
  253. package/src/notifications/index.ts +771 -0
  254. package/src/openapi/index.ts +133 -16
  255. package/src/outbox/index.ts +1104 -0
  256. package/src/pagination/index.ts +278 -0
  257. package/src/ports/audit.ts +271 -0
  258. package/src/ports/auth.ts +70 -0
  259. package/src/ports/cache.ts +41 -0
  260. package/src/ports/clock.ts +38 -0
  261. package/src/ports/id-generator.ts +37 -0
  262. package/src/ports/index.ts +106 -11
  263. package/src/ports/logger.ts +56 -0
  264. package/src/ports/policy.ts +133 -0
  265. package/src/ports/rate-limit.ts +25 -0
  266. package/src/ports/redaction.ts +101 -0
  267. package/src/ports/storage.ts +100 -0
  268. package/src/ports/testing.ts +47 -0
  269. package/src/ports/unit-of-work.ts +60 -3
  270. package/src/providers/instrumentation.ts +211 -1
  271. package/src/providers/provider.ts +14 -1
  272. package/src/schedules/index.ts +247 -0
  273. package/src/server/health.ts +14 -5
  274. package/src/server/hooks/auth.ts +105 -120
  275. package/src/server/hooks/cors.ts +27 -0
  276. package/src/server/hooks/errors.ts +15 -6
  277. package/src/server/hooks/index.ts +4 -5
  278. package/src/server/hooks/logging.ts +36 -0
  279. package/src/server/hooks/rate-limit.ts +33 -0
  280. package/src/server/http.ts +249 -1
  281. package/src/server/index.ts +19 -1
  282. package/src/server/openapi.ts +5 -3
  283. package/src/server/providers/loadProviderConfig.ts +9 -0
  284. package/src/server/server.ts +296 -30
  285. package/src/testing/index.ts +348 -0
  286. package/src/uploads/client.ts +861 -0
  287. package/src/uploads/index.ts +1067 -0
@@ -31,6 +31,7 @@ import type {
31
31
  HttpResponse,
32
32
  HttpResponseLike,
33
33
  ResolvedRoute,
34
+ RouteHook,
34
35
  ServerCaughtErrorHook,
35
36
  ServerHook,
36
37
  ServerUnhandledErrorMapper,
@@ -42,44 +43,130 @@ import {
42
43
  } from "./providers";
43
44
 
44
45
  /**
45
- * Route definition
46
+ * Route registration for one contract.
47
+ *
48
+ * Route definitions connect a contract to the handler that implements it. Most
49
+ * apps keep these in `features/<feature>/routes.ts` and compose them with
50
+ * `defineRoutes(...)`.
46
51
  */
47
- export type RouteDef<Ctx, CLike extends ContractLike = ContractLike> = {
52
+ export type RouteDef<
53
+ Ctx,
54
+ CLike extends ContractLike = ContractLike,
55
+ Hooks extends readonly RouteHook<Ctx, object>[] = readonly RouteHook<
56
+ Ctx,
57
+ object
58
+ >[],
59
+ > = {
60
+ /**
61
+ * Contract builder or plain contract config for this route.
62
+ */
48
63
  contract: CLike;
49
- handle: Handler<Ctx, ResolveContract<CLike>>;
64
+ /**
65
+ * Route-scoped hooks that run after group hooks and before the handler.
66
+ */
67
+ hooks?: Hooks;
68
+ /**
69
+ * Handler that implements the contract.
70
+ */
71
+ handle: Handler<Ctx & AddedCtxFromHooks<Hooks>, ResolveContract<CLike>>;
50
72
  };
51
73
 
74
+ type AddedCtxFromHook<Hook> =
75
+ Hook extends RouteHook<infer _Ctx, infer AddedCtx> ? AddedCtx : unknown;
76
+
77
+ type UnionToIntersection<Union> = (
78
+ Union extends unknown
79
+ ? (value: Union) => void
80
+ : never
81
+ ) extends (value: infer Intersection) => void
82
+ ? Intersection
83
+ : never;
84
+
85
+ type AddedCtxFromHooks<Hooks extends readonly unknown[]> =
86
+ Hooks extends readonly []
87
+ ? unknown
88
+ : UnionToIntersection<AddedCtxFromHook<Hooks[number]>>;
89
+
90
+ // biome-ignore lint/suspicious/noExplicitAny: route contract types are erased at collection boundaries
91
+ type AnyRouteDef = RouteDef<any, any>;
92
+
93
+ // biome-ignore lint/suspicious/noExplicitAny: route contract types are erased at collection boundaries
94
+ type PlainRouteDef<Ctx> = RouteDef<Ctx, any, readonly []>;
95
+
96
+ // biome-ignore lint/suspicious/noExplicitAny: route contract types are erased at collection boundaries
97
+ type AnyContractRouteDef<Ctx> = RouteDef<Ctx, any>;
98
+
52
99
  const ROUTE_GROUP_KIND = "beignet.route-group";
53
100
 
101
+ /**
102
+ * Named collection of related route registrations.
103
+ *
104
+ * Route groups colocate feature routes and can apply scoped route hooks to
105
+ * every route in the group. `defineRoutes(...)` flattens them before server
106
+ * registration while preserving those hooks.
107
+ */
54
108
  export type RouteGroup<
55
109
  Ctx,
56
- // biome-ignore lint/suspicious/noExplicitAny: route contract types are erased at this level
57
- Routes extends readonly RouteDef<Ctx, any>[] = readonly RouteDef<Ctx, any>[],
110
+ Routes extends readonly AnyRouteDef[] = readonly AnyRouteDef[],
58
111
  > = {
112
+ /**
113
+ * Internal marker used by `defineRoutes(...)`.
114
+ */
59
115
  kind: typeof ROUTE_GROUP_KIND;
116
+ /**
117
+ * Human-readable group name.
118
+ */
60
119
  name: string;
120
+ /**
121
+ * Hooks applied to every route in this group.
122
+ */
123
+ hooks?: readonly RouteHook<Ctx, object>[];
124
+ /**
125
+ * Route definitions in this group.
126
+ */
61
127
  routes: Routes;
62
128
  };
63
129
 
64
130
  type RouteInput<Ctx> =
65
- // biome-ignore lint/suspicious/noExplicitAny: route contract types are erased at this level
66
- | RouteDef<Ctx, any>
67
- // biome-ignore lint/suspicious/noExplicitAny: route contract types are erased at this level
68
- | RouteGroup<Ctx, readonly RouteDef<Ctx, any>[]>;
131
+ | AnyContractRouteDef<Ctx>
132
+ | AnyRouteDef
133
+ | RouteGroup<Ctx, readonly AnyRouteDef[]>;
134
+
135
+ type ContextualRouteInput<Ctx> =
136
+ | PlainRouteDef<Ctx>
137
+ | RouteGroup<Ctx, readonly AnyRouteDef[]>;
138
+
139
+ type RouteGroupBuilder<Ctx> = {
140
+ <
141
+ const GroupHooks extends readonly RouteHook<Ctx, object>[] = readonly [],
142
+ const R extends readonly PlainRouteDef<
143
+ Ctx & AddedCtxFromHooks<GroupHooks>
144
+ >[] = readonly PlainRouteDef<Ctx & AddedCtxFromHooks<GroupHooks>>[],
145
+ >(group: {
146
+ name: string;
147
+ hooks?: GroupHooks;
148
+ routes: R;
149
+ }): RouteGroup<Ctx, R>;
150
+ <
151
+ const GroupHooks extends readonly RouteHook<Ctx, object>[] = readonly [],
152
+ const R extends readonly AnyRouteDef[] = readonly AnyRouteDef[],
153
+ >(group: {
154
+ name: string;
155
+ hooks?: GroupHooks;
156
+ routes: R;
157
+ }): RouteGroup<Ctx, R>;
158
+ };
69
159
 
70
160
  type RoutesFromInput<Input> =
71
- // biome-ignore lint/suspicious/noExplicitAny: route contract types are erased at this level
72
- Input extends RouteGroup<any, infer Routes>
161
+ Input extends RouteGroup<infer _Ctx, infer Routes>
73
162
  ? Routes
74
- : // biome-ignore lint/suspicious/noExplicitAny: route contract types are erased at this level
75
- Input extends RouteDef<any, any>
163
+ : Input extends AnyRouteDef
76
164
  ? readonly [Input]
77
165
  : readonly [];
78
166
 
79
167
  type FlattenRouteInputs<Inputs extends readonly unknown[]> =
80
168
  number extends Inputs["length"]
81
- ? // biome-ignore lint/suspicious/noExplicitAny: route contract types are erased at this level
82
- readonly RouteDef<any, any>[]
169
+ ? readonly AnyRouteDef[]
83
170
  : Inputs extends readonly [infer First, ...infer Rest]
84
171
  ? readonly [...RoutesFromInput<First>, ...FlattenRouteInputs<Rest>]
85
172
  : readonly [];
@@ -97,11 +184,30 @@ type ContractsFromRouteList<
97
184
  : never;
98
185
  };
99
186
 
187
+ /**
188
+ * Define one route registration with hook-aware handler typing.
189
+ *
190
+ * Direct route objects are still supported. Use this helper when route-scoped
191
+ * hooks enrich `ctx` for a single handler and you want TypeScript to infer the
192
+ * added fields.
193
+ */
194
+ export function defineRoute<Ctx>() {
195
+ return <
196
+ CLike extends ContractLike,
197
+ const Hooks extends readonly RouteHook<Ctx, object>[] = readonly [],
198
+ >(
199
+ route: RouteDef<Ctx, CLike, Hooks>,
200
+ ): RouteDef<Ctx, CLike, Hooks> => route;
201
+ }
202
+
203
+ /**
204
+ * Options for creating a Beignet server instance.
205
+ */
100
206
  export type CreateServerOptions<
101
207
  Ctx,
102
208
  Ports extends AnyPorts,
103
209
  // biome-ignore lint/suspicious/noExplicitAny: route contract types are erased at this level
104
- Routes extends readonly RouteDef<Ctx, any>[] = readonly RouteDef<Ctx, any>[],
210
+ Routes extends readonly RouteDef<any, any>[] = readonly RouteDef<any, any>[],
105
211
  Providers extends readonly ServiceProvider<
106
212
  unknown,
107
213
  // biome-ignore lint/suspicious/noExplicitAny: provider config types are erased at this level
@@ -109,21 +215,53 @@ export type CreateServerOptions<
109
215
  AnyPorts
110
216
  >[] = readonly [],
111
217
  > = {
218
+ /**
219
+ * App-owned ports available to context creation, hooks, and handlers.
220
+ */
112
221
  ports: Ports;
222
+ /**
223
+ * Providers installed during server startup.
224
+ *
225
+ * Provider ports are merged into `ports` before request handling and are
226
+ * stopped in reverse setup order when `server.stop()` runs.
227
+ */
113
228
  providers?: Providers;
114
229
 
115
- // config sources
230
+ /**
231
+ * Runtime env used by providers. Defaults to `process.env`.
232
+ */
116
233
  providerEnv?: Record<string, string | undefined>;
234
+ /**
235
+ * Provider config overrides keyed by provider name.
236
+ */
117
237
  providerConfig?: Record<string, unknown>;
118
238
 
239
+ /**
240
+ * Create request context after a route is matched.
241
+ *
242
+ * The `ports` argument includes app ports plus ports provided during server
243
+ * startup.
244
+ */
119
245
  createContext: (args: {
120
246
  req: HttpRequestLike;
121
247
  ports: Ports & ProvidedPortsOfList<Providers>;
122
248
  contract?: HttpContractConfig;
123
249
  }) => Ctx | Promise<Ctx>;
250
+ /**
251
+ * Server hooks that wrap every registered route.
252
+ */
124
253
  hooks?: ServerHook<Ctx, Ports & ProvidedPortsOfList<Providers>>[];
254
+ /**
255
+ * Route list to register up front.
256
+ */
125
257
  routes?: Routes;
258
+ /**
259
+ * Global caught-error observer.
260
+ */
126
261
  onCaughtError?: ServerCaughtErrorHook<Ctx>;
262
+ /**
263
+ * Global mapper for unexpected errors not handled by app error catalogs.
264
+ */
127
265
  mapUnhandledError?: ServerUnhandledErrorMapper<Ctx>;
128
266
  };
129
267
 
@@ -133,13 +271,31 @@ interface RouteBuilder<Ctx, C extends HttpContractConfig> {
133
271
  ) => (req: HttpRequestLike) => Promise<HttpResponse>;
134
272
  }
135
273
 
274
+ /**
275
+ * Runtime server object returned by `createServer(...)`.
276
+ */
136
277
  export interface ServerInstance<Ctx, Ports extends AnyPorts = AnyPorts> {
278
+ /**
279
+ * Catch-all request handler for platform adapters.
280
+ */
137
281
  api: (req: HttpRequestLike) => Promise<HttpResponse>;
282
+ /**
283
+ * Register and build a single route handler imperatively.
284
+ */
138
285
  route: <CLike extends ContractLike>(
139
286
  contractLike: CLike,
140
287
  ) => RouteBuilder<Ctx, ResolveContract<CLike>>;
288
+ /**
289
+ * Contract configs registered through the `routes` option.
290
+ */
141
291
  contracts: readonly HttpContractConfig[];
292
+ /**
293
+ * Stop installed providers in reverse setup order.
294
+ */
142
295
  stop: () => Promise<void>;
296
+ /**
297
+ * Final app ports after provider setup.
298
+ */
143
299
  ports: Ports;
144
300
  }
145
301
 
@@ -559,6 +715,7 @@ function buildHandler<
559
715
  contract: C,
560
716
  userHandler: Handler<Ctx, C>,
561
717
  hooks: ServerHook<Ctx, FinalPorts>[],
718
+ routeHooks: readonly RouteHook<unknown, object>[] = [],
562
719
  optionsOverrides?: {
563
720
  skipRoutePreparation?: boolean;
564
721
  },
@@ -1130,6 +1287,39 @@ function buildHandler<
1130
1287
  }
1131
1288
  }
1132
1289
 
1290
+ if (!result) {
1291
+ for (const hook of routeHooks) {
1292
+ try {
1293
+ const additions = await hook.resolve({
1294
+ req,
1295
+ ctx: currentCtx,
1296
+ contract,
1297
+ path,
1298
+ query,
1299
+ headers,
1300
+ body,
1301
+ });
1302
+ if (additions && typeof additions === "object") {
1303
+ currentCtx = {
1304
+ ...(currentCtx as object),
1305
+ ...additions,
1306
+ } as Ctx;
1307
+ }
1308
+ } catch (error) {
1309
+ result = await resolveErrorResult(
1310
+ error,
1311
+ currentCtx,
1312
+ pathValue,
1313
+ queryValue,
1314
+ headersValue,
1315
+ bodyValue,
1316
+ { owner: "framework" },
1317
+ );
1318
+ break;
1319
+ }
1320
+ }
1321
+ }
1322
+
1133
1323
  if (!result) {
1134
1324
  try {
1135
1325
  result = {
@@ -1234,11 +1424,23 @@ function buildHandler<
1234
1424
  };
1235
1425
  }
1236
1426
 
1427
+ /**
1428
+ * Create a Beignet server instance.
1429
+ *
1430
+ * The server owns route registration, provider setup/startup, request
1431
+ * validation, hook execution, response validation, and framework error mapping.
1432
+ * Use adapter packages such as `@beignet/next` to expose `server.api` to a
1433
+ * specific runtime.
1434
+ *
1435
+ * @param options - Ports, providers, routes, hooks, context factory, and error
1436
+ * mapping hooks for the server.
1437
+ * @returns A started server instance with final ports and a catch-all handler.
1438
+ */
1237
1439
  export async function createServer<
1238
1440
  Ctx,
1239
1441
  Ports extends AnyPorts,
1240
1442
  // biome-ignore lint/suspicious/noExplicitAny: route contract types are erased at this level
1241
- Routes extends readonly RouteDef<Ctx, any>[] = readonly RouteDef<Ctx, any>[],
1443
+ Routes extends readonly RouteDef<any, any>[] = readonly RouteDef<any, any>[],
1242
1444
  Providers extends readonly ServiceProvider<
1243
1445
  unknown,
1244
1446
  // biome-ignore lint/suspicious/noExplicitAny: provider config types are erased at this level
@@ -1292,6 +1494,7 @@ export async function createServer<
1292
1494
  const registerRoute = <C extends HttpContractConfig>(
1293
1495
  contract: C,
1294
1496
  handler: Handler<Ctx, C>,
1497
+ routeHooks: readonly RouteHook<unknown, object>[] = [],
1295
1498
  ): void => {
1296
1499
  if (contract.body && !methodSupportsRequestBody(contract.method)) {
1297
1500
  throw new Error(
@@ -1322,6 +1525,7 @@ export async function createServer<
1322
1525
  contract,
1323
1526
  handler,
1324
1527
  hooks,
1528
+ routeHooks,
1325
1529
  );
1326
1530
  registry.push({
1327
1531
  contract,
@@ -1354,7 +1558,11 @@ export async function createServer<
1354
1558
  try {
1355
1559
  for (const route of options.routes) {
1356
1560
  const contract = resolveContract(route.contract);
1357
- registerRoute(contract, route.handle);
1561
+ registerRoute(
1562
+ contract,
1563
+ route.handle as Handler<Ctx, typeof contract>,
1564
+ route.hooks as readonly RouteHook<unknown, object>[] | undefined,
1565
+ );
1358
1566
  }
1359
1567
  } catch (error) {
1360
1568
  try {
@@ -1428,6 +1636,7 @@ export async function createServer<
1428
1636
  notFoundContract,
1429
1637
  async () => errorResponse(404, "NOT_FOUND", "Not found"),
1430
1638
  hooks,
1639
+ [],
1431
1640
  { skipRoutePreparation: true },
1432
1641
  );
1433
1642
 
@@ -1447,14 +1656,27 @@ export async function createServer<
1447
1656
  }
1448
1657
 
1449
1658
  /**
1450
- * Helper function to define routes with proper type inference.
1451
- * Eliminates the need for type assertions when passing routes to createServer.
1659
+ * Define and flatten route registrations with strong type inference.
1660
+ *
1661
+ * Pass route definitions and route groups here before `createServer(...)`.
1662
+ * Group entries are flattened so downstream tooling receives one route list.
1452
1663
  *
1453
1664
  * @example
1665
+ * ```ts
1454
1666
  * const routes = defineRoutes<AppContext>([
1455
- * { contract: myContract, handle: async ({ query }) => { ... } }
1667
+ * { contract: listPosts, handle: async ({ ctx }) => ctx.posts.list() },
1456
1668
  * ]);
1669
+ * ```
1457
1670
  */
1671
+ export function defineRoutes<
1672
+ Ctx,
1673
+ const R extends
1674
+ readonly ContextualRouteInput<Ctx>[] = readonly ContextualRouteInput<Ctx>[],
1675
+ >(routes: R): FlattenRouteInputs<R>;
1676
+ export function defineRoutes<
1677
+ Ctx,
1678
+ const R extends readonly RouteInput<Ctx>[] = readonly RouteInput<Ctx>[],
1679
+ >(routes: R): FlattenRouteInputs<R>;
1458
1680
  export function defineRoutes<
1459
1681
  Ctx,
1460
1682
  const R extends readonly RouteInput<Ctx>[] = readonly RouteInput<Ctx>[],
@@ -1463,7 +1685,12 @@ export function defineRoutes<
1463
1685
 
1464
1686
  for (const route of routes) {
1465
1687
  if (isRouteGroup(route)) {
1466
- flattened.push(...route.routes);
1688
+ for (const groupRoute of route.routes) {
1689
+ flattened.push({
1690
+ ...groupRoute,
1691
+ hooks: [...(route.hooks ?? []), ...(groupRoute.hooks ?? [])],
1692
+ });
1693
+ }
1467
1694
  } else {
1468
1695
  flattened.push(route);
1469
1696
  }
@@ -1473,8 +1700,10 @@ export function defineRoutes<
1473
1700
  }
1474
1701
 
1475
1702
  /**
1476
- * Extract contract configs from a route list. Use this to drive OpenAPI from
1477
- * the same route registration list that createServer receives.
1703
+ * Extract contract configs from a route list.
1704
+ *
1705
+ * Use this to drive clients, OpenAPI, and docs from the same route list passed
1706
+ * to `createServer(...)`.
1478
1707
  */
1479
1708
  export function contractsFromRoutes<
1480
1709
  // biome-ignore lint/suspicious/noExplicitAny: route contract types are erased at this level
@@ -1486,27 +1715,64 @@ export function contractsFromRoutes<
1486
1715
  }
1487
1716
 
1488
1717
  /**
1489
- * Helper function to group related route registrations.
1718
+ * Define a named group of related route registrations.
1490
1719
  *
1491
1720
  * Route groups are flattened by defineRoutes, so createServer still receives
1492
- * a regular route list while app code can keep feature route wiring colocated.
1721
+ * a regular route list while app code can keep feature route wiring and scoped
1722
+ * hooks colocated.
1493
1723
  *
1494
1724
  * @example
1495
- * const todoRoutes = defineRouteGroup<AppContext>({
1725
+ * ```ts
1726
+ * const todoRoutes = defineRouteGroup<AppContext>()({
1496
1727
  * name: "todos",
1728
+ * hooks: [auth.optional()],
1497
1729
  * routes: [
1498
- * { contract: listTodos, handle: async ({ ctx }) => { ... } }
1730
+ * { contract: listTodos, handle: async ({ ctx }) => ctx.todos.list() },
1499
1731
  * ]
1500
1732
  * });
1733
+ * ```
1501
1734
  */
1735
+ export function defineRouteGroup<Ctx>(): RouteGroupBuilder<Ctx>;
1502
1736
  export function defineRouteGroup<
1503
1737
  Ctx,
1504
1738
  // biome-ignore lint/suspicious/noExplicitAny: route contract types are erased at this level
1505
1739
  const R extends readonly RouteDef<Ctx, any>[] = readonly RouteDef<Ctx, any>[],
1506
- >(group: { name: string; routes: R }): RouteGroup<Ctx, R> {
1740
+ >(group: {
1741
+ name: string;
1742
+ hooks?: readonly RouteHook<Ctx, object>[];
1743
+ routes: R;
1744
+ }): RouteGroup<Ctx, R>;
1745
+ export function defineRouteGroup<
1746
+ Ctx,
1747
+ // biome-ignore lint/suspicious/noExplicitAny: route contract types are erased at this level
1748
+ const R extends readonly RouteDef<any, any>[] = readonly RouteDef<any, any>[],
1749
+ >(group?: {
1750
+ name: string;
1751
+ hooks?: readonly RouteHook<Ctx, object>[];
1752
+ routes: R;
1753
+ }): RouteGroup<Ctx, R> | RouteGroupBuilder<Ctx> {
1754
+ const createGroup = <
1755
+ const GroupHooks extends readonly RouteHook<Ctx, object>[] = readonly [],
1756
+ const GroupRoutes extends readonly AnyRouteDef[] = readonly AnyRouteDef[],
1757
+ >(input: {
1758
+ name: string;
1759
+ hooks?: GroupHooks;
1760
+ routes: GroupRoutes;
1761
+ }): RouteGroup<Ctx, GroupRoutes> => ({
1762
+ kind: ROUTE_GROUP_KIND,
1763
+ name: input.name,
1764
+ hooks: input.hooks,
1765
+ routes: input.routes,
1766
+ });
1767
+
1768
+ if (!group) {
1769
+ return createGroup;
1770
+ }
1771
+
1507
1772
  return {
1508
1773
  kind: ROUTE_GROUP_KIND,
1509
1774
  name: group.name,
1775
+ hooks: group.hooks,
1510
1776
  routes: group.routes,
1511
1777
  };
1512
1778
  }