@beignet/core 0.0.3 → 0.0.4

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 (360) hide show
  1. package/CHANGELOG.md +157 -0
  2. package/README.md +785 -43
  3. package/dist/application/index.d.ts +28 -2
  4. package/dist/application/index.d.ts.map +1 -1
  5. package/dist/application/index.js +140 -12
  6. package/dist/application/index.js.map +1 -1
  7. package/dist/client/client.d.ts +2 -2
  8. package/dist/client/client.d.ts.map +1 -1
  9. package/dist/client/client.js +136 -48
  10. package/dist/client/client.js.map +1 -1
  11. package/dist/client/error-messages.d.ts +14 -0
  12. package/dist/client/error-messages.d.ts.map +1 -0
  13. package/dist/client/error-messages.js +23 -0
  14. package/dist/client/error-messages.js.map +1 -0
  15. package/dist/client/index.d.ts +8 -4
  16. package/dist/client/index.d.ts.map +1 -1
  17. package/dist/client/index.js +6 -2
  18. package/dist/client/index.js.map +1 -1
  19. package/dist/client/types.d.ts +35 -5
  20. package/dist/client/types.d.ts.map +1 -1
  21. package/dist/client-only.d.ts +8 -0
  22. package/dist/client-only.d.ts.map +1 -0
  23. package/dist/client-only.js +8 -0
  24. package/dist/client-only.js.map +1 -0
  25. package/dist/config/index.d.ts +5 -5
  26. package/dist/config/index.d.ts.map +1 -1
  27. package/dist/config/index.js +2 -2
  28. package/dist/config/index.js.map +1 -1
  29. package/dist/contracts/catalog-errors.d.ts +27 -0
  30. package/dist/contracts/catalog-errors.d.ts.map +1 -0
  31. package/dist/contracts/catalog-errors.js +69 -0
  32. package/dist/contracts/catalog-errors.js.map +1 -0
  33. package/dist/contracts/contract-builder.d.ts +15 -12
  34. package/dist/contracts/contract-builder.d.ts.map +1 -1
  35. package/dist/contracts/contract-builder.js +15 -41
  36. package/dist/contracts/contract-builder.js.map +1 -1
  37. package/dist/contracts/contract-group.d.ts +11 -8
  38. package/dist/contracts/contract-group.d.ts.map +1 -1
  39. package/dist/contracts/contract-group.js +13 -40
  40. package/dist/contracts/contract-group.js.map +1 -1
  41. package/dist/contracts/contract-like.d.ts +1 -1
  42. package/dist/contracts/contract-like.d.ts.map +1 -1
  43. package/dist/contracts/index.d.ts +13 -9
  44. package/dist/contracts/index.d.ts.map +1 -1
  45. package/dist/contracts/index.js +9 -5
  46. package/dist/contracts/index.js.map +1 -1
  47. package/dist/contracts/openapi-meta.d.ts +48 -0
  48. package/dist/contracts/openapi-meta.d.ts.map +1 -1
  49. package/dist/contracts/openapi-meta.js +3 -0
  50. package/dist/contracts/openapi-meta.js.map +1 -1
  51. package/dist/contracts/path-template.d.ts +1 -1
  52. package/dist/contracts/path-template.js +2 -2
  53. package/dist/contracts/path-template.js.map +1 -1
  54. package/dist/contracts/schema-shape.d.ts +37 -0
  55. package/dist/contracts/schema-shape.d.ts.map +1 -0
  56. package/dist/contracts/schema-shape.js +61 -0
  57. package/dist/contracts/schema-shape.js.map +1 -0
  58. package/dist/contracts/success-status.d.ts +32 -0
  59. package/dist/contracts/success-status.d.ts.map +1 -0
  60. package/dist/contracts/success-status.js +18 -0
  61. package/dist/contracts/success-status.js.map +1 -0
  62. package/dist/contracts/types.d.ts +25 -5
  63. package/dist/contracts/types.d.ts.map +1 -1
  64. package/dist/contracts/types.js.map +1 -1
  65. package/dist/contracts/utils.d.ts +1 -1
  66. package/dist/contracts/utils.d.ts.map +1 -1
  67. package/dist/contracts/utils.js +1 -1
  68. package/dist/contracts/utils.js.map +1 -1
  69. package/dist/domain/events.d.ts +1 -1
  70. package/dist/domain/events.d.ts.map +1 -1
  71. package/dist/domain/events.js +1 -1
  72. package/dist/domain/events.js.map +1 -1
  73. package/dist/domain/index.d.ts +3 -3
  74. package/dist/domain/index.d.ts.map +1 -1
  75. package/dist/domain/index.js +3 -3
  76. package/dist/domain/index.js.map +1 -1
  77. package/dist/errors/catalog.d.ts +9 -1
  78. package/dist/errors/catalog.d.ts.map +1 -1
  79. package/dist/errors/catalog.js +7 -1
  80. package/dist/errors/catalog.js.map +1 -1
  81. package/dist/errors/http.d.ts +10 -0
  82. package/dist/errors/http.d.ts.map +1 -1
  83. package/dist/errors/http.js +11 -1
  84. package/dist/errors/http.js.map +1 -1
  85. package/dist/errors/index.d.ts +4 -4
  86. package/dist/errors/index.d.ts.map +1 -1
  87. package/dist/errors/index.js +4 -4
  88. package/dist/errors/index.js.map +1 -1
  89. package/dist/errors/response.d.ts +4 -1
  90. package/dist/errors/response.d.ts.map +1 -1
  91. package/dist/errors/response.js.map +1 -1
  92. package/dist/events/index.d.ts +10 -12
  93. package/dist/events/index.d.ts.map +1 -1
  94. package/dist/events/index.js +10 -10
  95. package/dist/events/index.js.map +1 -1
  96. package/dist/idempotency/index.d.ts +5 -3
  97. package/dist/idempotency/index.d.ts.map +1 -1
  98. package/dist/idempotency/index.js.map +1 -1
  99. package/dist/jobs/index.d.ts +12 -14
  100. package/dist/jobs/index.d.ts.map +1 -1
  101. package/dist/jobs/index.js +13 -13
  102. package/dist/jobs/index.js.map +1 -1
  103. package/dist/notifications/index.d.ts +14 -16
  104. package/dist/notifications/index.d.ts.map +1 -1
  105. package/dist/notifications/index.js +14 -14
  106. package/dist/notifications/index.js.map +1 -1
  107. package/dist/openapi/index.d.ts +8 -3
  108. package/dist/openapi/index.d.ts.map +1 -1
  109. package/dist/openapi/index.js +41 -29
  110. package/dist/openapi/index.js.map +1 -1
  111. package/dist/openapi/schema-introspector.d.ts +37 -0
  112. package/dist/openapi/schema-introspector.d.ts.map +1 -1
  113. package/dist/openapi/schema-introspector.js +23 -17
  114. package/dist/openapi/schema-introspector.js.map +1 -1
  115. package/dist/outbox/index.d.ts +15 -6
  116. package/dist/outbox/index.d.ts.map +1 -1
  117. package/dist/outbox/index.js +60 -16
  118. package/dist/outbox/index.js.map +1 -1
  119. package/dist/ports/audit.d.ts +56 -10
  120. package/dist/ports/audit.d.ts.map +1 -1
  121. package/dist/ports/audit.js +71 -3
  122. package/dist/ports/audit.js.map +1 -1
  123. package/dist/ports/auth.d.ts +92 -0
  124. package/dist/ports/auth.d.ts.map +1 -1
  125. package/dist/ports/auth.js +92 -0
  126. package/dist/ports/auth.js.map +1 -1
  127. package/dist/ports/events.d.ts +2 -2
  128. package/dist/ports/events.d.ts.map +1 -1
  129. package/dist/ports/index.d.ts +62 -33
  130. package/dist/ports/index.d.ts.map +1 -1
  131. package/dist/ports/index.js +28 -34
  132. package/dist/ports/index.js.map +1 -1
  133. package/dist/ports/policy.d.ts +32 -3
  134. package/dist/ports/policy.d.ts.map +1 -1
  135. package/dist/ports/policy.js +13 -2
  136. package/dist/ports/policy.js.map +1 -1
  137. package/dist/ports/testing.d.ts +1030 -2
  138. package/dist/ports/testing.d.ts.map +1 -1
  139. package/dist/ports/testing.js +1031 -1
  140. package/dist/ports/testing.js.map +1 -1
  141. package/dist/ports/unbound.d.ts +21 -0
  142. package/dist/ports/unbound.d.ts.map +1 -0
  143. package/dist/ports/unbound.js +57 -0
  144. package/dist/ports/unbound.js.map +1 -0
  145. package/dist/ports/unit-of-work.d.ts +1 -1
  146. package/dist/ports/unit-of-work.d.ts.map +1 -1
  147. package/dist/ports/unit-of-work.js +1 -1
  148. package/dist/ports/unit-of-work.js.map +1 -1
  149. package/dist/providers/index.d.ts +3 -2
  150. package/dist/providers/index.d.ts.map +1 -1
  151. package/dist/providers/index.js +3 -2
  152. package/dist/providers/index.js.map +1 -1
  153. package/dist/providers/instrumentation.d.ts +45 -4
  154. package/dist/providers/instrumentation.d.ts.map +1 -1
  155. package/dist/providers/instrumentation.js +25 -6
  156. package/dist/providers/instrumentation.js.map +1 -1
  157. package/dist/providers/metadata.d.ts +39 -0
  158. package/dist/providers/metadata.d.ts.map +1 -0
  159. package/dist/providers/metadata.js +169 -0
  160. package/dist/providers/metadata.js.map +1 -0
  161. package/dist/providers/provider.d.ts +114 -9
  162. package/dist/providers/provider.d.ts.map +1 -1
  163. package/dist/providers/provider.js +3 -20
  164. package/dist/providers/provider.js.map +1 -1
  165. package/dist/schedules/index.d.ts +94 -13
  166. package/dist/schedules/index.d.ts.map +1 -1
  167. package/dist/schedules/index.js +66 -12
  168. package/dist/schedules/index.js.map +1 -1
  169. package/dist/server/audit-context.d.ts +29 -0
  170. package/dist/server/audit-context.d.ts.map +1 -0
  171. package/dist/server/audit-context.js +44 -0
  172. package/dist/server/audit-context.js.map +1 -0
  173. package/dist/server/context.d.ts +141 -0
  174. package/dist/server/context.d.ts.map +1 -0
  175. package/dist/server/context.js +39 -0
  176. package/dist/server/context.js.map +1 -0
  177. package/dist/server/contract-like.d.ts +1 -1
  178. package/dist/server/contract-like.d.ts.map +1 -1
  179. package/dist/server/contract-like.js +1 -1
  180. package/dist/server/contract-like.js.map +1 -1
  181. package/dist/server/health.d.ts +2 -2
  182. package/dist/server/health.d.ts.map +1 -1
  183. package/dist/server/hooks/auth.d.ts +49 -10
  184. package/dist/server/hooks/auth.d.ts.map +1 -1
  185. package/dist/server/hooks/auth.js +77 -37
  186. package/dist/server/hooks/auth.js.map +1 -1
  187. package/dist/server/hooks/cors.d.ts +1 -1
  188. package/dist/server/hooks/cors.d.ts.map +1 -1
  189. package/dist/server/hooks/errors.d.ts +2 -2
  190. package/dist/server/hooks/errors.d.ts.map +1 -1
  191. package/dist/server/hooks/errors.js +2 -2
  192. package/dist/server/hooks/errors.js.map +1 -1
  193. package/dist/server/hooks/idempotency.d.ts +78 -0
  194. package/dist/server/hooks/idempotency.d.ts.map +1 -0
  195. package/dist/server/hooks/idempotency.js +154 -0
  196. package/dist/server/hooks/idempotency.js.map +1 -0
  197. package/dist/server/hooks/index.d.ts +8 -7
  198. package/dist/server/hooks/index.d.ts.map +1 -1
  199. package/dist/server/hooks/index.js +6 -5
  200. package/dist/server/hooks/index.js.map +1 -1
  201. package/dist/server/hooks/logging.d.ts +2 -2
  202. package/dist/server/hooks/logging.d.ts.map +1 -1
  203. package/dist/server/hooks/logging.js +1 -1
  204. package/dist/server/hooks/logging.js.map +1 -1
  205. package/dist/server/hooks/rate-limit.d.ts +25 -7
  206. package/dist/server/hooks/rate-limit.d.ts.map +1 -1
  207. package/dist/server/hooks/rate-limit.js +47 -12
  208. package/dist/server/hooks/rate-limit.js.map +1 -1
  209. package/dist/server/hooks.d.ts +1 -1
  210. package/dist/server/hooks.d.ts.map +1 -1
  211. package/dist/server/hooks.js +1 -1
  212. package/dist/server/hooks.js.map +1 -1
  213. package/dist/server/http.d.ts +61 -35
  214. package/dist/server/http.d.ts.map +1 -1
  215. package/dist/server/http.js +1 -20
  216. package/dist/server/http.js.map +1 -1
  217. package/dist/server/index.d.ts +36 -12
  218. package/dist/server/index.d.ts.map +1 -1
  219. package/dist/server/index.js +24 -8
  220. package/dist/server/index.js.map +1 -1
  221. package/dist/server/instrumentation.d.ts +108 -0
  222. package/dist/server/instrumentation.d.ts.map +1 -0
  223. package/dist/server/instrumentation.js +297 -0
  224. package/dist/server/instrumentation.js.map +1 -0
  225. package/dist/server/openapi.d.ts +3 -3
  226. package/dist/server/openapi.d.ts.map +1 -1
  227. package/dist/server/openapi.js +1 -1
  228. package/dist/server/openapi.js.map +1 -1
  229. package/dist/server/providers/index.d.ts +3 -3
  230. package/dist/server/providers/index.d.ts.map +1 -1
  231. package/dist/server/providers/index.js +3 -3
  232. package/dist/server/providers/index.js.map +1 -1
  233. package/dist/server/providers/loadProviderConfig.d.ts +2 -2
  234. package/dist/server/providers/loadProviderConfig.d.ts.map +1 -1
  235. package/dist/server/providers/loadProviderConfig.js +2 -2
  236. package/dist/server/providers/loadProviderConfig.js.map +1 -1
  237. package/dist/server/request-context.d.ts +67 -0
  238. package/dist/server/request-context.d.ts.map +1 -0
  239. package/dist/server/request-context.js +79 -0
  240. package/dist/server/request-context.js.map +1 -0
  241. package/dist/server/server-context.d.ts +38 -0
  242. package/dist/server/server-context.d.ts.map +1 -0
  243. package/dist/server/server-context.js +38 -0
  244. package/dist/server/server-context.js.map +1 -0
  245. package/dist/server/server.d.ts +105 -33
  246. package/dist/server/server.d.ts.map +1 -1
  247. package/dist/server/server.js +434 -118
  248. package/dist/server/server.js.map +1 -1
  249. package/dist/server/types.d.ts +2 -2
  250. package/dist/server/types.d.ts.map +1 -1
  251. package/dist/server/types.js +2 -2
  252. package/dist/server/types.js.map +1 -1
  253. package/dist/server/use-case-route.d.ts +263 -0
  254. package/dist/server/use-case-route.d.ts.map +1 -0
  255. package/dist/server/use-case-route.js +77 -0
  256. package/dist/server/use-case-route.js.map +1 -0
  257. package/dist/server-only.d.ts +8 -0
  258. package/dist/server-only.d.ts.map +1 -0
  259. package/dist/server-only.js +8 -0
  260. package/dist/server-only.js.map +1 -0
  261. package/dist/tasks/index.d.ts +139 -0
  262. package/dist/tasks/index.d.ts.map +1 -0
  263. package/dist/tasks/index.js +98 -0
  264. package/dist/tasks/index.js.map +1 -0
  265. package/dist/testing/index.d.ts +607 -5
  266. package/dist/testing/index.d.ts.map +1 -1
  267. package/dist/testing/index.js +426 -4
  268. package/dist/testing/index.js.map +1 -1
  269. package/dist/tracing/index.d.ts +89 -0
  270. package/dist/tracing/index.d.ts.map +1 -0
  271. package/dist/tracing/index.js +101 -0
  272. package/dist/tracing/index.js.map +1 -0
  273. package/dist/uploads/client.d.ts +1 -1
  274. package/dist/uploads/client.d.ts.map +1 -1
  275. package/dist/uploads/index.d.ts +2 -2
  276. package/dist/uploads/index.d.ts.map +1 -1
  277. package/dist/uploads/index.js +1 -1
  278. package/dist/uploads/index.js.map +1 -1
  279. package/package.json +24 -2
  280. package/src/application/index.ts +193 -10
  281. package/src/client/client.ts +148 -150
  282. package/src/client/error-messages.ts +35 -0
  283. package/src/client/index.ts +12 -4
  284. package/src/client/types.ts +44 -5
  285. package/src/client-only.ts +7 -0
  286. package/src/config/index.ts +6 -6
  287. package/src/contracts/catalog-errors.ts +115 -0
  288. package/src/contracts/contract-builder.ts +39 -76
  289. package/src/contracts/contract-group.ts +33 -68
  290. package/src/contracts/contract-like.ts +1 -1
  291. package/src/contracts/index.ts +24 -11
  292. package/src/contracts/openapi-meta.ts +55 -0
  293. package/src/contracts/path-template.ts +2 -2
  294. package/src/contracts/schema-shape.ts +75 -0
  295. package/src/contracts/success-status.ts +68 -0
  296. package/src/contracts/types.ts +32 -5
  297. package/src/contracts/utils.ts +5 -2
  298. package/src/domain/events.ts +6 -2
  299. package/src/domain/index.ts +3 -3
  300. package/src/errors/catalog.ts +9 -1
  301. package/src/errors/http.ts +11 -1
  302. package/src/errors/index.ts +4 -4
  303. package/src/errors/response.ts +4 -1
  304. package/src/events/index.ts +12 -26
  305. package/src/idempotency/index.ts +5 -3
  306. package/src/jobs/index.ts +14 -24
  307. package/src/notifications/index.ts +17 -27
  308. package/src/openapi/index.ts +73 -38
  309. package/src/openapi/schema-introspector.ts +68 -17
  310. package/src/outbox/index.ts +84 -19
  311. package/src/ports/audit.ts +120 -11
  312. package/src/ports/auth.ts +132 -0
  313. package/src/ports/events.ts +2 -2
  314. package/src/ports/index.ts +104 -35
  315. package/src/ports/policy.ts +50 -3
  316. package/src/ports/testing.ts +2220 -33
  317. package/src/ports/unbound.ts +64 -0
  318. package/src/ports/unit-of-work.ts +6 -2
  319. package/src/providers/index.ts +16 -3
  320. package/src/providers/instrumentation.ts +86 -7
  321. package/src/providers/metadata.ts +234 -0
  322. package/src/providers/provider.ts +168 -9
  323. package/src/schedules/index.ts +173 -23
  324. package/src/server/audit-context.ts +45 -0
  325. package/src/server/context.ts +224 -0
  326. package/src/server/contract-like.ts +1 -1
  327. package/src/server/health.ts +2 -2
  328. package/src/server/hooks/auth.ts +141 -51
  329. package/src/server/hooks/cors.ts +1 -1
  330. package/src/server/hooks/errors.ts +7 -4
  331. package/src/server/hooks/idempotency.ts +263 -0
  332. package/src/server/hooks/index.ts +14 -7
  333. package/src/server/hooks/logging.ts +3 -3
  334. package/src/server/hooks/rate-limit.ts +85 -17
  335. package/src/server/hooks.ts +1 -1
  336. package/src/server/http.ts +78 -51
  337. package/src/server/index.ts +62 -12
  338. package/src/server/instrumentation.ts +470 -0
  339. package/src/server/openapi.ts +4 -4
  340. package/src/server/providers/index.ts +6 -3
  341. package/src/server/providers/loadProviderConfig.ts +4 -4
  342. package/src/server/request-context.ts +116 -0
  343. package/src/server/server-context.ts +44 -0
  344. package/src/server/server.ts +886 -238
  345. package/src/server/types.ts +2 -2
  346. package/src/server/use-case-route.ts +430 -0
  347. package/src/server-only.ts +7 -0
  348. package/src/tasks/index.ts +275 -0
  349. package/src/testing/index.ts +1142 -6
  350. package/src/tracing/index.ts +176 -0
  351. package/src/uploads/client.ts +1 -1
  352. package/src/uploads/index.ts +7 -3
  353. package/dist/ports/mailer.d.ts +0 -6
  354. package/dist/ports/mailer.d.ts.map +0 -1
  355. package/dist/ports/mailer.js +0 -2
  356. package/dist/ports/mailer.js.map +0 -1
  357. package/dist/ports/schedules.d.ts +0 -9
  358. package/dist/ports/schedules.d.ts.map +0 -1
  359. package/dist/ports/schedules.js +0 -2
  360. package/dist/ports/schedules.js.map +0 -1
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Internal schema shape helpers shared by server route registration and
3
+ * OpenAPI generation.
4
+ *
5
+ * These helpers are intentionally zod-free: zod is an optional peer
6
+ * dependency, so shape extraction duck-types Zod's `_def.shape` internals and
7
+ * returns undefined for schemas it cannot introspect. This module is not
8
+ * exported from the package index.
9
+ */
10
+
11
+ /**
12
+ * Extract the field shape (field name → field schema) from an object schema.
13
+ *
14
+ * Returns undefined when the schema is not an introspectable object schema,
15
+ * for example a non-Zod Standard Schema.
16
+ */
17
+ export function getObjectSchemaShape(
18
+ schema: unknown,
19
+ ): Record<string, unknown> | undefined {
20
+ if (!schema || typeof schema !== "object") return undefined;
21
+
22
+ const schemaDef = (schema as Record<string, unknown>)?._def;
23
+ if (!schemaDef || typeof schemaDef !== "object") return undefined;
24
+
25
+ if (!("shape" in schemaDef)) return undefined;
26
+
27
+ let shape: unknown;
28
+ if (typeof schemaDef.shape === "function") {
29
+ shape = schemaDef.shape();
30
+ } else if (typeof schemaDef.shape === "object") {
31
+ shape = schemaDef.shape;
32
+ }
33
+
34
+ if (!shape || typeof shape !== "object") return undefined;
35
+
36
+ return shape as Record<string, unknown>;
37
+ }
38
+
39
+ /**
40
+ * Compare a path template's dynamic keys against a pathParams schema shape.
41
+ */
42
+ export function comparePathParamsToTemplate(args: {
43
+ pathKeys: readonly string[];
44
+ shapeKeys: readonly string[];
45
+ }): { missingKeys: string[]; extraKeys: string[] } {
46
+ const missingKeys = args.pathKeys.filter(
47
+ (key) => !args.shapeKeys.includes(key),
48
+ );
49
+ const extraKeys = args.shapeKeys.filter(
50
+ (key) => !args.pathKeys.includes(key),
51
+ );
52
+ return { missingKeys, extraKeys };
53
+ }
54
+
55
+ /**
56
+ * Format a pathParams/template mismatch for error messages.
57
+ *
58
+ * Shared so server registration and OpenAPI generation report the same
59
+ * diagnostic details.
60
+ */
61
+ export function formatPathParamsMismatch(args: {
62
+ missingKeys: readonly string[];
63
+ extraKeys: readonly string[];
64
+ }): string {
65
+ return [
66
+ args.missingKeys.length > 0
67
+ ? `missing pathParams keys: ${args.missingKeys.join(", ")}`
68
+ : undefined,
69
+ args.extraKeys.length > 0
70
+ ? `extra pathParams keys: ${args.extraKeys.join(", ")}`
71
+ : undefined,
72
+ ]
73
+ .filter(Boolean)
74
+ .join("; ");
75
+ }
@@ -0,0 +1,68 @@
1
+ import type { HttpContractConfig } from "./types.js";
2
+
3
+ type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
4
+ type ToNumber<S extends string> = S extends `${infer N extends number}`
5
+ ? N
6
+ : never;
7
+ type Any2xxCode = ToNumber<`2${Digit}${Digit}`>;
8
+ type NumericKey<K> = K extends number
9
+ ? K
10
+ : K extends `${infer N extends number}`
11
+ ? N
12
+ : never;
13
+
14
+ type UnionToIntersection<T> = (
15
+ T extends unknown
16
+ ? (value: T) => void
17
+ : never
18
+ ) extends (value: infer I) => void
19
+ ? I
20
+ : never;
21
+
22
+ /**
23
+ * All declared 2xx status codes from a contract `responses` record, as
24
+ * numeric literal keys.
25
+ */
26
+ export type Success2xxKeys<TResponses extends object> = {
27
+ [K in keyof TResponses]: NumericKey<K> extends Any2xxCode
28
+ ? NumericKey<K>
29
+ : never;
30
+ }[keyof TResponses];
31
+
32
+ /**
33
+ * Whether a contract `responses` record declares exactly one 2xx status.
34
+ */
35
+ export type IsSingle2xx<TResponses extends object> = [
36
+ Success2xxKeys<TResponses>,
37
+ ] extends [never]
38
+ ? false
39
+ : [Success2xxKeys<TResponses>] extends [
40
+ UnionToIntersection<Success2xxKeys<TResponses>>,
41
+ ]
42
+ ? true
43
+ : false;
44
+
45
+ /**
46
+ * List the declared 2xx statuses of a contract at runtime.
47
+ */
48
+ export function getSuccess2xxStatuses(
49
+ contract: Pick<HttpContractConfig, "responses">,
50
+ ): number[] {
51
+ return Object.keys(contract.responses)
52
+ .map((status) => Number(status))
53
+ .filter(
54
+ (status) => Number.isInteger(status) && status >= 200 && status < 300,
55
+ )
56
+ .sort((a, b) => a - b);
57
+ }
58
+
59
+ /**
60
+ * Resolve the sole declared 2xx status of a contract, or `undefined` when the
61
+ * contract declares zero or multiple 2xx statuses.
62
+ */
63
+ export function inferSoleSuccessStatus(
64
+ contract: Pick<HttpContractConfig, "responses">,
65
+ ): number | undefined {
66
+ const statuses = getSuccess2xxStatuses(contract);
67
+ return statuses.length === 1 ? statuses[0] : undefined;
68
+ }
@@ -1,7 +1,7 @@
1
1
  import type { StandardSchemaV1 } from "@standard-schema/spec";
2
- import type { IdempotencyMeta } from "../idempotency";
3
- import type { OpenAPIOperationMeta } from "./openapi-meta";
4
- import type { RateLimitMeta } from "./rate-limit";
2
+ import type { IdempotencyMeta } from "../idempotency/index.js";
3
+ import type { OpenAPIOperationMeta } from "./openapi-meta.js";
4
+ import type { RateLimitMeta } from "./rate-limit.js";
5
5
 
6
6
  /**
7
7
  * Any Standard Schema compatible validator.
@@ -72,6 +72,21 @@ export type ResponsesFromErrorDefinitions<T extends ContractErrorResponses> = {
72
72
  [K in keyof T as T[K]["status"]]: StandardErrorResponseSchema;
73
73
  };
74
74
 
75
+ /**
76
+ * Merge catalog errors already declared in contract metadata with newly
77
+ * declared error definitions.
78
+ *
79
+ * `.errors()` declarations compose: contracts keep the union of group-declared
80
+ * and route-declared catalog errors. Later declarations win when the same
81
+ * catalog key is declared twice.
82
+ */
83
+ export type MergedContractErrorResponses<
84
+ TMeta extends ContractMeta,
85
+ TErrorDefs extends ContractErrorResponses,
86
+ > = TMeta["errors"] extends ContractErrorResponses
87
+ ? Omit<TMeta["errors"], keyof TErrorDefs> & TErrorDefs
88
+ : TErrorDefs;
89
+
75
90
  /**
76
91
  * Header schema declaration for a contract.
77
92
  */
@@ -222,13 +237,25 @@ export type ContractMeta = {
222
237
  /**
223
238
  * Optional idempotency configuration for this contract.
224
239
  *
225
- * This metadata is intended for hooks, docs, and OpenAPI. Use cases should
226
- * still call `runIdempotently(...)` for workflows that must be retry-safe.
240
+ * Enforced at the HTTP boundary by `createIdempotencyHooks(...)`.
241
+ * `runIdempotently(...)` remains the primitive for non-HTTP workflows.
227
242
  */
228
243
  idempotency?: IdempotencyMeta;
229
244
  [namespace: string]: unknown;
230
245
  };
231
246
 
247
+ /**
248
+ * Omit metadata keys while preserving literal property types.
249
+ *
250
+ * `ContractMeta` carries an index signature, so the built-in `Omit` collapses
251
+ * `keyof` to `string` and erases literal metadata such as
252
+ * `idempotency: { header: "idempotency-key" }` that typed clients rely on.
253
+ * Key remapping keeps known properties and the index signature intact.
254
+ */
255
+ export type OmitMetaKeys<TMeta, K extends PropertyKey> = {
256
+ [P in keyof TMeta as P extends K ? never : P]: TMeta[P];
257
+ };
258
+
232
259
  /**
233
260
  * Plain HTTP contract configuration consumed by server, client, and tooling.
234
261
  */
@@ -1,5 +1,8 @@
1
- import { type PathTemplateSegment, parsePathTemplate } from "./path-template";
2
- import type { HttpMethod } from "./types";
1
+ import {
2
+ type PathTemplateSegment,
3
+ parsePathTemplate,
4
+ } from "./path-template.js";
5
+ import type { HttpMethod } from "./types.js";
3
6
 
4
7
  const methodNameMap: Record<HttpMethod, string> = {
5
8
  GET: "get",
@@ -1,5 +1,9 @@
1
- import type { EventDef, InferEventPayload, StandardSchema } from "../events";
2
- import { defineEvent } from "../events";
1
+ import type {
2
+ EventDef,
3
+ InferEventPayload,
4
+ StandardSchema,
5
+ } from "../events/index.js";
6
+ import { defineEvent } from "../events/index.js";
3
7
 
4
8
  /**
5
9
  * Domain event definition with a stable name and payload schema.
@@ -9,10 +9,10 @@
9
9
  * - A Domain Event helper (defineDomainEvent)
10
10
  */
11
11
 
12
- export { defineEntity, type EntityDef, type EntityInstance } from "./entity";
12
+ export { defineEntity, type EntityDef, type EntityInstance } from "./entity.js";
13
13
  export {
14
14
  type DomainEventDef,
15
15
  defineDomainEvent,
16
16
  type InferEventPayload,
17
- } from "./events";
18
- export { defineValueObject, type ValueObjectDef } from "./value-object";
17
+ } from "./events.js";
18
+ export { defineValueObject, type ValueObjectDef } from "./value-object.js";
@@ -26,6 +26,8 @@ export interface ErrorDef<
26
26
  *
27
27
  * Beignet uses this for TypeScript inference. Details are not runtime
28
28
  * validated by `AppError`; validate before constructing errors when needed.
29
+ * Details become public response data when the error crosses the HTTP
30
+ * boundary, so use app-owned safe fields instead of raw diagnostics.
29
31
  */
30
32
  details?: TDetails;
31
33
  }
@@ -64,7 +66,13 @@ export class AppError<TDef extends ErrorDef = ErrorDef> extends Error {
64
66
  readonly [APP_ERROR_BRAND] = true;
65
67
  /** Error definition from the catalog. */
66
68
  readonly def: TDef;
67
- /** Optional structured details for clients, logs, or UI. */
69
+ /**
70
+ * Optional structured details for clients or UI.
71
+ *
72
+ * Beignet does not automatically redact route-owned error details. Keep
73
+ * provider errors, stack traces, secrets, and private content in `cause`,
74
+ * logs, or error reporting instead.
75
+ */
68
76
  readonly details?: InferErrorDetails<TDef>;
69
77
 
70
78
  constructor(
@@ -4,7 +4,7 @@
4
4
  * A base catalog of common HTTP-related errors that apps can reuse and extend.
5
5
  */
6
6
 
7
- import { defineErrors } from "./catalog";
7
+ import { defineErrors } from "./catalog.js";
8
8
 
9
9
  /**
10
10
  * A base catalog of common HTTP-related errors.
@@ -52,6 +52,16 @@ export const httpErrors = defineErrors({
52
52
  status: 409,
53
53
  message: "Conflict",
54
54
  },
55
+ IdempotencyConflict: {
56
+ code: "IDEMPOTENCY_CONFLICT",
57
+ status: 409,
58
+ message: "Idempotency key was already used with a different request",
59
+ },
60
+ IdempotencyInProgress: {
61
+ code: "IDEMPOTENCY_IN_PROGRESS",
62
+ status: 409,
63
+ message: "Idempotency key is already being processed",
64
+ },
55
65
  TooManyRequests: {
56
66
  code: "TOO_MANY_REQUESTS",
57
67
  status: 429,
@@ -14,15 +14,15 @@ export {
14
14
  type ErrorDef,
15
15
  type InferErrorDetails,
16
16
  isAppError,
17
- } from "./catalog";
17
+ } from "./catalog.js";
18
18
  // HTTP error helpers
19
- export { type HttpErrorCatalog, httpErrors } from "./http";
19
+ export { type HttpErrorCatalog, httpErrors } from "./http.js";
20
20
  // Error response utilities
21
21
  export {
22
22
  createErrorResponseBody,
23
23
  type ErrorResponseBody,
24
24
  isErrorResponseBody,
25
25
  toErrorResponseBody,
26
- } from "./response";
26
+ } from "./response.js";
27
27
  // Schema validation error (shared across client/core/server)
28
- export { SchemaValidationError, type ValidationIssue } from "./validation";
28
+ export { SchemaValidationError, type ValidationIssue } from "./validation.js";
@@ -2,7 +2,7 @@
2
2
  * Standard error response schema and utilities for Beignet
3
3
  */
4
4
 
5
- import type { AppError, ErrorDef } from "./catalog";
5
+ import type { AppError, ErrorDef } from "./catalog.js";
6
6
 
7
7
  /**
8
8
  * Standard Beignet error response body.
@@ -18,6 +18,9 @@ export interface ErrorResponseBody {
18
18
  message: string;
19
19
  /**
20
20
  * Optional structured details.
21
+ *
22
+ * These details are sent to clients. Beignet does not automatically redact
23
+ * app-owned response details, so only include values that are safe to expose.
21
24
  */
22
25
  details?: unknown;
23
26
  /**
@@ -168,9 +168,9 @@ export interface RegisterListenersOptions<Ctx> {
168
168
  }
169
169
 
170
170
  /**
171
- * Context-bound event helper factory.
171
+ * Context-bound listener helper factory.
172
172
  */
173
- export interface EventHandlers<Ctx> {
173
+ export interface Listeners<Ctx> {
174
174
  /**
175
175
  * Define a listener with the bound context type.
176
176
  */
@@ -178,15 +178,6 @@ export interface EventHandlers<Ctx> {
178
178
  event: E,
179
179
  options: DefineListenerOptions<E, Ctx, Name>,
180
180
  ): ListenerDef<E, Ctx, Name>;
181
-
182
- /**
183
- * Register listeners with an event bus.
184
- */
185
- registerListeners(
186
- eventBus: EventBusLike,
187
- listeners: readonly ListenerDef<EventDef, Ctx>[],
188
- options?: RegisterListenersOptions<Ctx>,
189
- ): () => void;
190
181
  }
191
182
 
192
183
  /**
@@ -280,10 +271,7 @@ export function defineEvent<
280
271
  };
281
272
  }
282
273
 
283
- /**
284
- * Define a listener for one event.
285
- */
286
- export function defineListener<
274
+ function defineListenerImpl<
287
275
  E extends EventDef,
288
276
  Ctx = unknown,
289
277
  Name extends string = string,
@@ -358,23 +346,21 @@ export function registerListeners<Ctx>(
358
346
  }
359
347
 
360
348
  /**
361
- * Create event helper methods bound to an application context type.
349
+ * Create listener helper methods bound to an application context type.
350
+ *
351
+ * Call it once in `lib/listeners.ts`:
352
+ *
353
+ * ```ts
354
+ * export const { defineListener } = createListeners<AppContext>();
355
+ * ```
362
356
  */
363
- export function createEventHandlers<Ctx>(): EventHandlers<Ctx> {
357
+ export function createListeners<Ctx>(): Listeners<Ctx> {
364
358
  return {
365
359
  defineListener<E extends EventDef, Name extends string = string>(
366
360
  event: E,
367
361
  options: DefineListenerOptions<E, Ctx, Name>,
368
362
  ): ListenerDef<E, Ctx, Name> {
369
- return defineListener(event, options);
370
- },
371
-
372
- registerListeners(
373
- eventBus: EventBusLike,
374
- listeners: readonly ListenerDef<EventDef, Ctx>[],
375
- options: RegisterListenersOptions<Ctx> = {},
376
- ): () => void {
377
- return registerListeners(eventBus, listeners, options);
363
+ return defineListenerImpl(event, options);
378
364
  },
379
365
  };
380
366
  }
@@ -38,13 +38,15 @@ export type IdempotencyScopeMode =
38
38
 
39
39
  /**
40
40
  * Contract metadata for idempotency-aware routes.
41
+ *
42
+ * This metadata is enforced at the HTTP boundary by
43
+ * `createIdempotencyHooks(...)` from `@beignet/core/server`.
44
+ * `runIdempotently(...)` remains the primitive for non-HTTP workflows such as
45
+ * jobs, listeners, webhooks, and schedules.
41
46
  */
42
47
  export interface IdempotencyMeta {
43
48
  /**
44
49
  * Whether this operation requires an idempotency key at the HTTP boundary.
45
- *
46
- * This is metadata for hooks, docs, and generated OpenAPI. Use cases should
47
- * still call `runIdempotently(...)` for workflows that must be retry-safe.
48
50
  */
49
51
  required?: boolean;
50
52
 
package/src/jobs/index.ts CHANGED
@@ -306,7 +306,7 @@ export interface InlineJobDispatcher<Ctx = unknown> {
306
306
  /**
307
307
  * Context-bound job helper factory.
308
308
  */
309
- export interface JobHandlers<Ctx> {
309
+ export interface Jobs<Ctx> {
310
310
  /**
311
311
  * Define a job with the bound context type.
312
312
  */
@@ -314,13 +314,6 @@ export interface JobHandlers<Ctx> {
314
314
  name: Name,
315
315
  options: DefineJobOptions<Name, Payload, Ctx>,
316
316
  ): JobDef<Name, Payload, Ctx>;
317
-
318
- /**
319
- * Create an inline dispatcher with the bound context type.
320
- */
321
- createInlineJobDispatcher(
322
- options?: InlineJobDispatcherOptions<Ctx>,
323
- ): InlineJobDispatcher<Ctx>;
324
317
  }
325
318
 
326
319
  /**
@@ -555,14 +548,7 @@ async function resolveCtx<Ctx>(
555
548
  return ctx as Ctx;
556
549
  }
557
550
 
558
- /**
559
- * Define a typed job.
560
- *
561
- * Retry options are provider hints. Inline dispatchers validate payloads and
562
- * call `handle(...)` immediately; durable providers may enqueue or schedule the
563
- * job according to their own runtime.
564
- */
565
- export function defineJob<
551
+ function defineJobImpl<
566
552
  Name extends string,
567
553
  Payload extends StandardSchema,
568
554
  Ctx = unknown,
@@ -624,20 +610,24 @@ export function createInlineJobDispatcher<Ctx>(
624
610
 
625
611
  /**
626
612
  * Create job helper methods bound to an application context type.
613
+ *
614
+ * Call it once in `lib/jobs.ts`:
615
+ *
616
+ * ```ts
617
+ * export const { defineJob } = createJobs<AppContext>();
618
+ * ```
619
+ *
620
+ * Retry options are provider hints. Inline dispatchers validate payloads and
621
+ * call `handle(...)` immediately; durable providers may enqueue or schedule the
622
+ * job according to their own runtime.
627
623
  */
628
- export function createJobHandlers<Ctx>(): JobHandlers<Ctx> {
624
+ export function createJobs<Ctx>(): Jobs<Ctx> {
629
625
  return {
630
626
  defineJob<Name extends string, Payload extends StandardSchema>(
631
627
  name: Name,
632
628
  options: DefineJobOptions<Name, Payload, Ctx>,
633
629
  ): JobDef<Name, Payload, Ctx> {
634
- return defineJob(name, options);
635
- },
636
-
637
- createInlineJobDispatcher(
638
- options: InlineJobDispatcherOptions<Ctx> = {},
639
- ): InlineJobDispatcher<Ctx> {
640
- return createInlineJobDispatcher(options);
630
+ return defineJobImpl(name, options);
641
631
  },
642
632
  };
643
633
  }
@@ -1,7 +1,7 @@
1
1
  import type { StandardSchemaV1 } from "@standard-schema/spec";
2
- import type { SendMailOptions } from "../mail";
3
- import type { ProviderInstrumentationTarget } from "../providers";
4
- import { createProviderInstrumentation } from "../providers";
2
+ import type { SendMailOptions } from "../mail/index.js";
3
+ import type { ProviderInstrumentationTarget } from "../providers/index.js";
4
+ import { createProviderInstrumentation } from "../providers/index.js";
5
5
 
6
6
  /**
7
7
  * Any Standard Schema compatible validator.
@@ -336,7 +336,7 @@ export type MailNotificationRenderer<
336
336
  /**
337
337
  * Context-bound notification helper factory.
338
338
  */
339
- export interface NotificationHandlers<Ctx> {
339
+ export interface Notifications<Ctx> {
340
340
  /**
341
341
  * Define a notification with the bound context type.
342
342
  */
@@ -344,13 +344,6 @@ export interface NotificationHandlers<Ctx> {
344
344
  name: Name,
345
345
  options: DefineNotificationOptions<Payload, Ctx>,
346
346
  ): NotificationDef<Name, Payload, Ctx>;
347
-
348
- /**
349
- * Create an inline dispatcher with the bound context type.
350
- */
351
- createInlineNotificationDispatcher(
352
- options?: InlineNotificationDispatcherOptions<Ctx>,
353
- ): NotificationPort;
354
347
  }
355
348
 
356
349
  /**
@@ -495,14 +488,7 @@ function summarizeResults(results: readonly NotificationChannelResult[]) {
495
488
  return { sent, skipped, failed };
496
489
  }
497
490
 
498
- /**
499
- * Define a typed notification.
500
- *
501
- * Notifications represent user-facing communication intent. Channel handlers
502
- * decide how that intent becomes mail, SMS, push, in-app delivery, or another
503
- * app-owned channel.
504
- */
505
- export function defineNotification<
491
+ function defineNotificationImpl<
506
492
  Name extends string,
507
493
  Payload extends StandardSchema,
508
494
  Ctx = unknown,
@@ -752,20 +738,24 @@ export function createMemoryNotificationPort(
752
738
 
753
739
  /**
754
740
  * Create notification helper methods bound to an application context type.
741
+ *
742
+ * Call it once in `lib/notifications.ts`:
743
+ *
744
+ * ```ts
745
+ * export const { defineNotification } = createNotifications<AppContext>();
746
+ * ```
747
+ *
748
+ * Notifications represent user-facing communication intent. Channel handlers
749
+ * decide how that intent becomes mail, SMS, push, in-app delivery, or another
750
+ * app-owned channel.
755
751
  */
756
- export function createNotificationHandlers<Ctx>(): NotificationHandlers<Ctx> {
752
+ export function createNotifications<Ctx>(): Notifications<Ctx> {
757
753
  return {
758
754
  defineNotification<Name extends string, Payload extends StandardSchema>(
759
755
  name: Name,
760
756
  options: DefineNotificationOptions<Payload, Ctx>,
761
757
  ): NotificationDef<Name, Payload, Ctx> {
762
- return defineNotification(name, options);
763
- },
764
-
765
- createInlineNotificationDispatcher(
766
- options: InlineNotificationDispatcherOptions<Ctx> = {},
767
- ): NotificationPort {
768
- return createInlineNotificationDispatcher(options);
758
+ return defineNotificationImpl(name, options);
769
759
  },
770
760
  };
771
761
  }