@beignet/core 0.0.3 → 0.0.5

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 +159 -0
  2. package/README.md +792 -50
  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
package/src/ports/auth.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import type { ActivityTenant } from "./audit.js";
2
+
1
3
  /**
2
4
  * Minimal request shape consumed by Beignet auth ports.
3
5
  *
@@ -41,6 +43,136 @@ export class AuthUnauthorizedError extends Error {
41
43
  }
42
44
  }
43
45
 
46
+ /**
47
+ * Error thrown by tenant helpers when a workflow requires a tenant scope but
48
+ * the current context has none.
49
+ *
50
+ * The server maps this to a framework-owned 403 response, mirroring how
51
+ * `AuthUnauthorizedError` maps to a framework-owned 401.
52
+ */
53
+ export class TenantRequiredError extends Error {
54
+ readonly code = "TENANT_REQUIRED";
55
+ readonly status = 403;
56
+
57
+ constructor(message = "A tenant is required for this action.") {
58
+ super(message);
59
+ this.name = "TenantRequiredError";
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Options accepted by the `requireX(ctx)` context helpers.
65
+ */
66
+ export interface RequireOptions {
67
+ /**
68
+ * Create the error to throw instead of the framework default.
69
+ */
70
+ error?: () => unknown;
71
+ }
72
+
73
+ function throwRequired(
74
+ options: RequireOptions | undefined,
75
+ fallback: () => Error,
76
+ ): never {
77
+ throw options?.error ? options.error() : fallback();
78
+ }
79
+
80
+ /**
81
+ * Return the authenticated session from `ctx.auth` or throw.
82
+ *
83
+ * Throws `AuthUnauthorizedError` (a framework-owned 401) by default. Pass
84
+ * `options.error` to throw an app-owned error instead.
85
+ *
86
+ * @example
87
+ * ```ts
88
+ * const session = requireSession(ctx);
89
+ * ```
90
+ */
91
+ export function requireSession<Session extends AuthSession>(
92
+ ctx: { auth?: Session | null },
93
+ options?: RequireOptions,
94
+ ): Session {
95
+ if (!ctx.auth) {
96
+ throwRequired(options, () => new AuthUnauthorizedError());
97
+ }
98
+
99
+ return ctx.auth;
100
+ }
101
+
102
+ /**
103
+ * Return the authenticated user from `ctx.auth` or throw.
104
+ *
105
+ * The user type is inferred from the app's `ctx.auth` session. Throws
106
+ * `AuthUnauthorizedError` (a framework-owned 401) by default.
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * const user = requireUser(ctx);
111
+ * ```
112
+ */
113
+ export function requireUser<User>(
114
+ ctx: { auth?: AuthSession<User> | null },
115
+ options?: RequireOptions,
116
+ ): User {
117
+ return requireSession(ctx, options).user;
118
+ }
119
+
120
+ /**
121
+ * Return the authenticated user's ID from `ctx.auth` or throw.
122
+ *
123
+ * Throws `AuthUnauthorizedError` (a framework-owned 401) by default.
124
+ *
125
+ * @example
126
+ * ```ts
127
+ * const userId = requireUserId(ctx);
128
+ * ```
129
+ */
130
+ export function requireUserId(
131
+ ctx: { auth?: AuthSession<{ id: string }> | null },
132
+ options?: RequireOptions,
133
+ ): string {
134
+ return requireUser(ctx, options).id;
135
+ }
136
+
137
+ /**
138
+ * Return the tenant scope from `ctx.tenant` or throw.
139
+ *
140
+ * Throws `TenantRequiredError` (a framework-owned 403) by default. Pass
141
+ * `options.error` to throw an app-owned error instead.
142
+ *
143
+ * @example
144
+ * ```ts
145
+ * const tenant = requireTenant(ctx);
146
+ * ```
147
+ */
148
+ export function requireTenant(
149
+ ctx: { tenant?: ActivityTenant | null },
150
+ options?: RequireOptions,
151
+ ): ActivityTenant {
152
+ if (!ctx.tenant) {
153
+ throwRequired(options, () => new TenantRequiredError());
154
+ }
155
+
156
+ return ctx.tenant;
157
+ }
158
+
159
+ /**
160
+ * Return the tenant ID from `ctx.tenant` or throw.
161
+ *
162
+ * Throws `TenantRequiredError` (a framework-owned 403) by default.
163
+ *
164
+ * @example
165
+ * ```ts
166
+ * const tenantId = requireTenantId(ctx);
167
+ * ```
168
+ */
169
+ export function requireTenantId(
170
+ ctx: { tenant?: ActivityTenant | null },
171
+ options?: RequireOptions,
172
+ ): string {
173
+ return requireTenant(ctx, options).id;
174
+ }
175
+
44
176
  /**
45
177
  * App-facing authentication port.
46
178
  *
@@ -2,11 +2,11 @@ import type {
2
2
  EventPayloadDef,
3
3
  InferEventPayload as InferContractEventPayload,
4
4
  StandardSchema,
5
- } from "../events";
5
+ } from "../events/index.js";
6
6
  import type {
7
7
  JobDef as ContractJobDef,
8
8
  InferJobPayload as InferContractJobPayload,
9
- } from "../jobs";
9
+ } from "../jobs/index.js";
10
10
 
11
11
  /**
12
12
  * Represents a defined Domain Event with name and payload schema.
@@ -27,9 +27,11 @@
27
27
  * - `@beignet/core/mail` owns `MailerPort` and mail test adapters
28
28
  * - `@beignet/core/notifications` owns notification helpers and test adapters
29
29
  * - `@beignet/core/outbox` owns durable outbox helpers and test adapters
30
- * - `@beignet/core/schedules` owns scheduled task definitions and runners
30
+ * - `@beignet/core/schedules` owns schedule definitions and runners
31
31
  */
32
32
 
33
+ import { createUnboundPort } from "./unbound.js";
34
+
33
35
  /**
34
36
  * A generic map of named "ports" (outbound dependencies) that your
35
37
  * application depends on: db, mailer, cache, event bus, etc.
@@ -39,28 +41,78 @@
39
41
  */
40
42
  export type AnyPorts = Record<string, unknown>;
41
43
 
44
+ /**
45
+ * Definition accepted by the curried `definePorts<P>()(...)` form.
46
+ */
47
+ export type DeferredPortsDefinition<
48
+ P extends AnyPorts,
49
+ Deferred extends readonly (keyof P & string)[],
50
+ > = {
51
+ /**
52
+ * Ports the app binds directly. Optional keys of `P` may be omitted here
53
+ * and contributed by providers instead.
54
+ */
55
+ bound: Omit<P, Deferred[number]>;
56
+ /**
57
+ * Port keys that providers contribute during server startup. Deferred keys
58
+ * boot as throwing placeholders; `createServer(...)` fails startup if any
59
+ * are still unbound after providers have started (see `onUnboundPorts`).
60
+ */
61
+ deferred: Deferred;
62
+ };
63
+
42
64
  /**
43
65
  * Define the set of ports (outbound dependencies) for your application.
44
66
  *
45
- * This is essentially a typed identity function. It:
46
- * - captures the exact shape of the provided `ports` object
47
- * - allows you to export `type AppPorts = typeof appPorts` in your app
67
+ * The identity form captures the exact shape of the provided `ports` object
68
+ * so you can export `type AppPorts = typeof appPorts`.
69
+ *
70
+ * The curried form, `definePorts<AppPorts>()({ bound, deferred })`, declares
71
+ * which port keys providers contribute at server startup. Deferred keys boot
72
+ * as throwing placeholders instead of hand-written stubs, and
73
+ * `createServer(...)` validates after provider startup that nothing is left
74
+ * unbound. Optional keys of `AppPorts` may go in either bucket.
48
75
  *
49
76
  * @example
50
77
  * ```ts
78
+ * // Identity form: every port is bound directly.
51
79
  * const appPorts = definePorts({
52
80
  * db: dbAdapter,
53
81
  * mailer: mailerAdapter,
54
82
  * });
55
- *
56
83
  * export type AppPorts = typeof appPorts;
57
- * ```
58
84
  *
59
- * @param ports - The ports object to define
60
- * @returns The same ports object, unchanged
85
+ * // Deferred form: providers contribute the rest at startup.
86
+ * export const appPorts = definePorts<AppPorts>()({
87
+ * bound: { gate },
88
+ * deferred: ["db", "mailer", "storage"],
89
+ * });
90
+ * ```
61
91
  */
62
- export function definePorts<P extends AnyPorts>(ports: P): P {
63
- return ports;
92
+ export function definePorts<P extends AnyPorts>(ports: P): P;
93
+ export function definePorts<P extends AnyPorts>(): <
94
+ const Deferred extends readonly (keyof P & string)[],
95
+ >(
96
+ definition: DeferredPortsDefinition<P, Deferred>,
97
+ ) => P;
98
+ export function definePorts<P extends AnyPorts>(
99
+ ports?: P,
100
+ ):
101
+ | P
102
+ | ((
103
+ definition: DeferredPortsDefinition<P, readonly (keyof P & string)[]>,
104
+ ) => P) {
105
+ if (ports !== undefined) {
106
+ return ports;
107
+ }
108
+
109
+ return (definition) => {
110
+ const result: AnyPorts = { ...(definition.bound as AnyPorts) };
111
+ for (const key of definition.deferred) {
112
+ result[key] = createUnboundPort(key);
113
+ }
114
+ return result as P;
115
+ };
64
116
  }
65
117
 
66
118
  /**
@@ -93,7 +145,7 @@ export type {
93
145
  IdempotencyPort,
94
146
  IdempotencyReservation,
95
147
  IdempotencyReserveInput,
96
- } from "../idempotency";
148
+ } from "../idempotency/index.js";
97
149
  /**
98
150
  * Notification port exports.
99
151
  */
@@ -104,7 +156,7 @@ export type {
104
156
  NotificationPort,
105
157
  SendNotificationOptions,
106
158
  SendNotificationResult,
107
- } from "../notifications";
159
+ } from "../notifications/index.js";
108
160
  /**
109
161
  * Transactional outbox port exports.
110
162
  */
@@ -118,7 +170,7 @@ export type {
118
170
  OutboxMessageKind,
119
171
  OutboxMessageStatus,
120
172
  OutboxPort,
121
- } from "../outbox";
173
+ } from "../outbox/index.js";
122
174
  /**
123
175
  * Audit log port exports.
124
176
  */
@@ -134,13 +186,15 @@ export type {
134
186
  AuditLogOptions,
135
187
  AuditLogPort,
136
188
  AuditOutcome,
189
+ InstrumentedAuditLogOptions,
137
190
  MemoryAuditLogPort,
138
- } from "./audit";
191
+ } from "./audit.js";
139
192
  /**
140
193
  * Audit log helper exports.
141
194
  */
142
195
  export {
143
196
  createAnonymousActor,
197
+ createInstrumentedAuditLog,
144
198
  createMemoryAuditLog,
145
199
  createRedactedAuditLog,
146
200
  createServiceActor,
@@ -149,7 +203,7 @@ export {
149
203
  createUserActor,
150
204
  normalizeAuditLogEntry,
151
205
  redactAuditLogEntry,
152
- } from "./audit";
206
+ } from "./audit.js";
153
207
  /**
154
208
  * Auth port exports.
155
209
  */
@@ -157,8 +211,9 @@ export type {
157
211
  AuthPort,
158
212
  AuthRequestLike,
159
213
  AuthSession,
214
+ RequireOptions,
160
215
  StaticAuthSessionFactory,
161
- } from "./auth";
216
+ } from "./auth.js";
162
217
  /**
163
218
  * Auth helper exports.
164
219
  */
@@ -166,7 +221,13 @@ export {
166
221
  AuthUnauthorizedError,
167
222
  createAnonymousAuth,
168
223
  createStaticAuth,
169
- } from "./auth";
224
+ requireSession,
225
+ requireTenant,
226
+ requireTenantId,
227
+ requireUser,
228
+ requireUserId,
229
+ TenantRequiredError,
230
+ } from "./auth.js";
170
231
  /**
171
232
  * Ports builder exports.
172
233
  */
@@ -174,23 +235,23 @@ export {
174
235
  createPortsBuilder,
175
236
  type PortsBuilder,
176
237
  type PortsOf,
177
- } from "./builder";
238
+ } from "./builder.js";
178
239
  /**
179
240
  * Cache port exports.
180
241
  */
181
- export type { CachePort, CacheSetOptions } from "./cache";
242
+ export type { CachePort, CacheSetOptions } from "./cache.js";
182
243
  /**
183
244
  * Cache helper exports.
184
245
  */
185
- export { createMemoryCache } from "./cache";
246
+ export { createMemoryCache } from "./cache.js";
186
247
  /**
187
248
  * Clock port exports.
188
249
  */
189
- export type { ClockPort, FrozenClockPort } from "./clock";
250
+ export type { ClockPort, FrozenClockPort } from "./clock.js";
190
251
  /**
191
252
  * Clock helper exports.
192
253
  */
193
- export { createFrozenClock, createSystemClock } from "./clock";
254
+ export { createFrozenClock, createSystemClock } from "./clock.js";
194
255
  /**
195
256
  * Event bus and job dispatcher port exports.
196
257
  */
@@ -201,18 +262,21 @@ export type {
201
262
  InferJobPayload,
202
263
  JobDef,
203
264
  JobDispatcherPort,
204
- } from "./events";
265
+ } from "./events.js";
205
266
  /**
206
267
  * ID generator port exports.
207
268
  */
208
- export type { IdGeneratorPort, SequenceIdGeneratorPort } from "./id-generator";
269
+ export type {
270
+ IdGeneratorPort,
271
+ SequenceIdGeneratorPort,
272
+ } from "./id-generator.js";
209
273
  /**
210
274
  * ID generator helper exports.
211
275
  */
212
276
  export {
213
277
  createSequenceIdGenerator,
214
278
  createUuidIdGenerator,
215
- } from "./id-generator";
279
+ } from "./id-generator.js";
216
280
  /**
217
281
  * Logger port exports.
218
282
  */
@@ -221,11 +285,11 @@ export type {
221
285
  LogLevel,
222
286
  MemoryLogEntry,
223
287
  MemoryLoggerPort,
224
- } from "./logger";
288
+ } from "./logger.js";
225
289
  /**
226
290
  * Logger helper exports.
227
291
  */
228
- export { createMemoryLogger, createNoopLogger } from "./logger";
292
+ export { createMemoryLogger, createNoopLogger } from "./logger.js";
229
293
  /**
230
294
  * Policy and authorization gate type exports.
231
295
  */
@@ -233,6 +297,7 @@ export type {
233
297
  BoundGate,
234
298
  CreateGateOptions,
235
299
  GateAllowedDecision,
300
+ GateContext,
236
301
  GateDecision,
237
302
  GateDeniedDecision,
238
303
  GateDenyHandler,
@@ -243,7 +308,7 @@ export type {
243
308
  PolicyMapFromDefinitions,
244
309
  PolicyResolver,
245
310
  PolicySubjectArgs,
246
- } from "./policy";
311
+ } from "./policy.js";
247
312
  /**
248
313
  * Policy and authorization gate helper exports.
249
314
  */
@@ -253,7 +318,7 @@ export {
253
318
  definePolicy,
254
319
  deny,
255
320
  GateAuthorizationError,
256
- } from "./policy";
321
+ } from "./policy.js";
257
322
  /**
258
323
  * Rate limit port exports.
259
324
  */
@@ -261,11 +326,11 @@ export type {
261
326
  RateLimitHitOptions,
262
327
  RateLimitPort,
263
328
  RateLimitResult,
264
- } from "./rate-limit";
329
+ } from "./rate-limit.js";
265
330
  /**
266
331
  * Rate limit helper exports.
267
332
  */
268
- export { createMemoryRateLimiter } from "./rate-limit";
333
+ export { createMemoryRateLimiter } from "./rate-limit.js";
269
334
  /**
270
335
  * Redaction helper exports.
271
336
  */
@@ -283,7 +348,7 @@ export {
283
348
  type Redactor,
284
349
  redactHeaders,
285
350
  redactValue,
286
- } from "./redaction";
351
+ } from "./redaction.js";
287
352
  /**
288
353
  * Storage port exports.
289
354
  */
@@ -296,11 +361,15 @@ export type {
296
361
  StoragePort,
297
362
  StoragePutOptions,
298
363
  StorageVisibility,
299
- } from "./storage";
364
+ } from "./storage.js";
300
365
  /**
301
366
  * Storage helper exports.
302
367
  */
303
- export { createMemoryStorage } from "./storage";
368
+ export { createMemoryStorage } from "./storage.js";
369
+ /**
370
+ * Unbound (deferred) port helper exports.
371
+ */
372
+ export { createUnboundPort, isUnboundPort } from "./unbound.js";
304
373
  /**
305
374
  * Unit of Work port exports.
306
375
  */
@@ -313,4 +382,4 @@ export {
313
382
  type RecordedDomainEvent,
314
383
  type UnitOfWorkCallback,
315
384
  type UnitOfWorkPort,
316
- } from "./unit-of-work";
385
+ } from "./unit-of-work.js";
@@ -113,6 +113,12 @@ export type PolicyContextFromDefinitions<
113
113
  * into every authorization call.
114
114
  */
115
115
  export type BoundGate<TPolicies extends readonly PolicyDefinition[]> = {
116
+ /**
117
+ * Type-only marker that keeps `TPolicies` inferable from a bound gate.
118
+ * Runtime bound gates never implement this method. Method syntax keeps the
119
+ * marker bivariant so readonly and mutable policy tuples stay compatible.
120
+ */
121
+ __policies?(policies: TPolicies): void;
116
122
  /**
117
123
  * Return only whether the ability is allowed.
118
124
  */
@@ -138,6 +144,18 @@ export type BoundGate<TPolicies extends readonly PolicyDefinition[]> = {
138
144
  ): Promise<GateAllowedDecision>;
139
145
  };
140
146
 
147
+ /**
148
+ * Context shape contributed by `gate.attach(...)`.
149
+ */
150
+ export type GateContext<
151
+ TPolicies extends readonly PolicyDefinition[] = readonly PolicyDefinition[],
152
+ > = {
153
+ /**
154
+ * Gate bound to the context that carries it.
155
+ */
156
+ gate: BoundGate<TPolicies>;
157
+ };
158
+
141
159
  /**
142
160
  * App-facing authorization gate.
143
161
  *
@@ -150,9 +168,23 @@ export type GatePort<
150
168
  TPolicies extends readonly PolicyDefinition[] = readonly PolicyDefinition[],
151
169
  > = {
152
170
  /**
153
- * Bind this gate to a context, usually during `createContext`.
171
+ * Bind this gate to a fixed context snapshot.
172
+ *
173
+ * This is the low-level primitive: the returned gate keeps evaluating
174
+ * against the exact object it was bound to. Prefer `attach(...)` for app
175
+ * context assembly so identity changes can never go stale.
154
176
  */
155
177
  bind(ctx: TContext): BoundGate<TPolicies>;
178
+ /**
179
+ * Attach a live `gate` property to a context object.
180
+ *
181
+ * The gate is exposed through a getter that re-binds against the receiving
182
+ * object on every access, so in-place updates to fields such as `actor` or
183
+ * `tenant` are always observed. The property is non-enumerable on purpose:
184
+ * spreading the context (`{ ...ctx }`) drops the gate instead of silently
185
+ * carrying a stale identity, and the next `ctx.gate` access fails loudly.
186
+ */
187
+ attach<C extends TContext & object>(ctx: C): C & GateContext<TPolicies>;
156
188
  /**
157
189
  * Return only whether the ability is allowed for a context.
158
190
  */
@@ -284,8 +316,8 @@ export function definePolicy<
284
316
  /**
285
317
  * Create an authorization gate from app-owned policy definitions.
286
318
  *
287
- * Register the gate as a port, then bind it to the request/background context:
288
- * `gate: ports.gate.bind(context)`. Use cases can then call
319
+ * Register the gate as a port, then let the server context blueprint attach
320
+ * it: `context: { gate: (ports) => ports.gate, ... }`. Use cases can then call
289
321
  * `ctx.gate.authorize(...)` for business authorization.
290
322
  *
291
323
  * @param options - Policy definitions and optional denial mapper.
@@ -348,8 +380,23 @@ export function createGate<
348
380
  };
349
381
  }
350
382
 
383
+ function attach<C extends TContext & object>(
384
+ ctx: C,
385
+ ): C & GateContext<TPolicies> {
386
+ Object.defineProperty(ctx, "gate", {
387
+ configurable: true,
388
+ enumerable: false,
389
+ get(this: TContext) {
390
+ return bind(this);
391
+ },
392
+ });
393
+
394
+ return ctx as C & GateContext<TPolicies>;
395
+ }
396
+
351
397
  const gate: GatePort<TContext, TPolicies> = {
352
398
  bind,
399
+ attach,
353
400
  async can(ctx, ability, ...subject) {
354
401
  return (await inspect(ctx, ability, firstSubject(subject))).allowed;
355
402
  },