@beignet/core 0.0.2 → 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 +173 -0
  2. package/README.md +821 -30
  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 +148 -16
  100. package/dist/jobs/index.d.ts.map +1 -1
  101. package/dist/jobs/index.js +174 -14
  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 +18 -4
  116. package/dist/outbox/index.d.ts.map +1 -1
  117. package/dist/outbox/index.js +104 -4
  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 +46 -5
  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 +89 -65
  184. package/dist/server/hooks/auth.d.ts.map +1 -1
  185. package/dist/server/hooks/auth.js +84 -55
  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 +84 -6
  214. package/dist/server/http.d.ts.map +1 -1
  215. package/dist/server/index.d.ts +36 -12
  216. package/dist/server/index.d.ts.map +1 -1
  217. package/dist/server/index.js +24 -8
  218. package/dist/server/index.js.map +1 -1
  219. package/dist/server/instrumentation.d.ts +108 -0
  220. package/dist/server/instrumentation.d.ts.map +1 -0
  221. package/dist/server/instrumentation.js +297 -0
  222. package/dist/server/instrumentation.js.map +1 -0
  223. package/dist/server/openapi.d.ts +3 -3
  224. package/dist/server/openapi.d.ts.map +1 -1
  225. package/dist/server/openapi.js +1 -1
  226. package/dist/server/openapi.js.map +1 -1
  227. package/dist/server/providers/index.d.ts +3 -3
  228. package/dist/server/providers/index.d.ts.map +1 -1
  229. package/dist/server/providers/index.js +3 -3
  230. package/dist/server/providers/index.js.map +1 -1
  231. package/dist/server/providers/loadProviderConfig.d.ts +2 -2
  232. package/dist/server/providers/loadProviderConfig.d.ts.map +1 -1
  233. package/dist/server/providers/loadProviderConfig.js +2 -2
  234. package/dist/server/providers/loadProviderConfig.js.map +1 -1
  235. package/dist/server/request-context.d.ts +67 -0
  236. package/dist/server/request-context.d.ts.map +1 -0
  237. package/dist/server/request-context.js +79 -0
  238. package/dist/server/request-context.js.map +1 -0
  239. package/dist/server/server-context.d.ts +38 -0
  240. package/dist/server/server-context.d.ts.map +1 -0
  241. package/dist/server/server-context.js +38 -0
  242. package/dist/server/server-context.js.map +1 -0
  243. package/dist/server/server.d.ts +148 -35
  244. package/dist/server/server.d.ts.map +1 -1
  245. package/dist/server/server.js +482 -145
  246. package/dist/server/server.js.map +1 -1
  247. package/dist/server/types.d.ts +2 -2
  248. package/dist/server/types.d.ts.map +1 -1
  249. package/dist/server/types.js +2 -2
  250. package/dist/server/types.js.map +1 -1
  251. package/dist/server/use-case-route.d.ts +263 -0
  252. package/dist/server/use-case-route.d.ts.map +1 -0
  253. package/dist/server/use-case-route.js +77 -0
  254. package/dist/server/use-case-route.js.map +1 -0
  255. package/dist/server-only.d.ts +8 -0
  256. package/dist/server-only.d.ts.map +1 -0
  257. package/dist/server-only.js +8 -0
  258. package/dist/server-only.js.map +1 -0
  259. package/dist/tasks/index.d.ts +139 -0
  260. package/dist/tasks/index.d.ts.map +1 -0
  261. package/dist/tasks/index.js +98 -0
  262. package/dist/tasks/index.js.map +1 -0
  263. package/dist/testing/index.d.ts +611 -5
  264. package/dist/testing/index.d.ts.map +1 -1
  265. package/dist/testing/index.js +434 -4
  266. package/dist/testing/index.js.map +1 -1
  267. package/dist/tracing/index.d.ts +89 -0
  268. package/dist/tracing/index.d.ts.map +1 -0
  269. package/dist/tracing/index.js +101 -0
  270. package/dist/tracing/index.js.map +1 -0
  271. package/dist/uploads/client.d.ts +278 -0
  272. package/dist/uploads/client.d.ts.map +1 -0
  273. package/dist/uploads/client.js +428 -0
  274. package/dist/uploads/client.js.map +1 -0
  275. package/dist/uploads/index.d.ts +361 -0
  276. package/dist/uploads/index.d.ts.map +1 -0
  277. package/dist/uploads/index.js +543 -0
  278. package/dist/uploads/index.js.map +1 -0
  279. package/package.json +34 -3
  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 +340 -29
  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 +151 -6
  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 +93 -8
  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 +175 -158
  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 +15 -12
  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 +112 -6
  337. package/src/server/index.ts +63 -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 +1045 -229
  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 +1153 -6
  350. package/src/tracing/index.ts +176 -0
  351. package/src/uploads/client.ts +861 -0
  352. package/src/uploads/index.ts +1071 -0
  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
@@ -43,20 +43,50 @@ export interface ProviderConfigDef<CfgSchema extends StandardSchemaV1> {
43
43
  */
44
44
  export type MaybePromise<T> = T | Promise<T>;
45
45
 
46
+ /**
47
+ * Late-bound service context factory exposed to providers.
48
+ *
49
+ * Calling it before all providers have started throws, so providers should
50
+ * only invoke it from runtime entrypoints such as job dispatch, listeners, or
51
+ * scheduled work.
52
+ *
53
+ * App-local providers can type the factory by declaring `Context` and
54
+ * `ServiceInput` through the curried `createProvider<Requires, Context,
55
+ * ServiceInput>()` form. Untyped providers see `(input: void) =>
56
+ * Promise<unknown>`.
57
+ */
58
+ export type ProviderServiceContextFactory<
59
+ Context = unknown,
60
+ ServiceInput = void,
61
+ > = (input: ServiceInput) => Promise<Context>;
62
+
46
63
  /**
47
64
  * Context passed to provider lifecycle hooks.
48
65
  */
49
- export type ProviderLifecycleContext = {
66
+ export type ProviderLifecycleContext<
67
+ Ports = ProviderPorts,
68
+ Context = unknown,
69
+ ServiceInput = void,
70
+ > = {
50
71
  /**
51
72
  * Final app ports after provider setup.
52
73
  */
53
- ports: Readonly<ProviderPorts>;
74
+ ports: Readonly<Ports>;
75
+ /**
76
+ * Build an app service context through the server context blueprint.
77
+ */
78
+ createServiceContext: ProviderServiceContextFactory<Context, ServiceInput>;
54
79
  };
55
80
 
56
81
  /**
57
82
  * Result returned from provider setup.
58
83
  */
59
- export type ProviderSetupResult<ProvidedPorts extends ProviderPorts> = {
84
+ export type ProviderSetupResult<
85
+ ProvidedPorts extends ProviderPorts,
86
+ Ports = ProviderPorts,
87
+ Context = unknown,
88
+ ServiceInput = void,
89
+ > = {
60
90
  /**
61
91
  * Ports contributed by this provider.
62
92
  * Keys overwrite earlier ports with the same name at runtime. Prefer unique
@@ -66,15 +96,60 @@ export type ProviderSetupResult<ProvidedPorts extends ProviderPorts> = {
66
96
 
67
97
  /**
68
98
  * Optional hook called after all providers have contributed their ports.
99
+ *
100
+ * Declared as a method so typed providers stay assignable to loosely typed
101
+ * provider lists. Hooks that take `ctx` with an unannotated parameter keep
102
+ * TypeScript from inferring `ProvidedPorts` from the returned `ports`.
103
+ * Prefer closing over setup locals, or annotate `ctx` with
104
+ * `ProviderLifecycleContext<...>`.
69
105
  */
70
- start?: (ctx: ProviderLifecycleContext) => MaybePromise<void>;
106
+ start?(
107
+ ctx: ProviderLifecycleContext<Ports & ProvidedPorts, Context, ServiceInput>,
108
+ ): MaybePromise<void>;
71
109
 
72
110
  /**
73
111
  * Optional hook called when the server is stopped.
74
112
  */
75
- stop?: (ctx: ProviderLifecycleContext) => MaybePromise<void>;
113
+ stop?(
114
+ ctx: ProviderLifecycleContext<Ports & ProvidedPorts, Context, ServiceInput>,
115
+ ): MaybePromise<void>;
76
116
  };
77
117
 
118
+ /**
119
+ * Static provider metadata used by docs and app-local tooling.
120
+ *
121
+ * Metadata is descriptive. It does not change provider setup, ordering, or
122
+ * runtime port merging behavior.
123
+ *
124
+ * Reusable provider packages should also declare package-owned
125
+ * `beignet.provider` metadata in package.json so external tooling can inspect
126
+ * provider facts without importing runtime code.
127
+ */
128
+ export interface ServiceProviderMetadata {
129
+ /**
130
+ * Package that exports this provider, when it comes from a reusable package.
131
+ */
132
+ packageName?: string;
133
+ /**
134
+ * App port keys this provider contributes or replaces.
135
+ */
136
+ ports?: readonly string[];
137
+ /**
138
+ * App port keys this provider expects previous providers or base app ports to
139
+ * have installed before setup runs.
140
+ */
141
+ requires?: readonly string[];
142
+ /**
143
+ * Environment variables this provider reads directly or via config loading.
144
+ */
145
+ env?: readonly string[];
146
+ /**
147
+ * Devtools watcher names this provider can emit through provider
148
+ * instrumentation.
149
+ */
150
+ watchers?: readonly string[];
151
+ }
152
+
78
153
  /**
79
154
  * A service provider that can extend or replace ports during app initialization.
80
155
  *
@@ -111,12 +186,19 @@ export interface ServiceProvider<
111
186
  Ports,
112
187
  CfgSchema extends StandardSchemaV1 = StandardSchemaV1<void, void>,
113
188
  ProvidedPorts extends ProviderPorts = NoProvidedPorts,
189
+ Context = unknown,
190
+ ServiceInput = void,
114
191
  > {
115
192
  /**
116
193
  * Unique name for this provider (used for logging/debugging)
117
194
  */
118
195
  name: string;
119
196
 
197
+ /**
198
+ * Optional static metadata for docs and diagnostics.
199
+ */
200
+ metadata?: ServiceProviderMetadata;
201
+
120
202
  /**
121
203
  * Optional configuration definition.
122
204
  * If provided, the config will be loaded and validated before calling setup.
@@ -130,11 +212,17 @@ export interface ServiceProvider<
130
212
  *
131
213
  * @param ctx.ports - Ports contributed by previous providers
132
214
  * @param ctx.config - Validated config (if config was defined), or undefined
215
+ * @param ctx.createServiceContext - Late-bound service context factory.
216
+ * Throws until all providers have started, so call it lazily from runtime
217
+ * entrypoints such as job dispatchers and event listeners.
133
218
  */
134
219
  setup(ctx: {
135
220
  ports: Readonly<Ports>;
136
221
  config: InferOutput<CfgSchema> | undefined;
137
- }): MaybePromise<ProviderSetupResult<ProvidedPorts>>;
222
+ createServiceContext: ProviderServiceContextFactory<Context, ServiceInput>;
223
+ }): MaybePromise<
224
+ ProviderSetupResult<ProvidedPorts, Ports, Context, ServiceInput>
225
+ >;
138
226
 
139
227
  /**
140
228
  * Type-only marker for ports this provider contributes.
@@ -143,6 +231,25 @@ export interface ServiceProvider<
143
231
  readonly __providedPorts?: ProvidedPorts;
144
232
  }
145
233
 
234
+ /**
235
+ * Loosely typed service provider.
236
+ *
237
+ * Required-port, config, app-context, and service-input generics are erased
238
+ * here so any provider created with `createProvider(...)` — including the
239
+ * typed curried form — stays assignable. Use this for code that works across
240
+ * arbitrary providers, such as provider lists and test helpers.
241
+ */
242
+ export type AnyServiceProvider = ServiceProvider<
243
+ unknown,
244
+ // biome-ignore lint/suspicious/noExplicitAny: provider config types are erased at this level
245
+ StandardSchemaV1<any, any>,
246
+ ProviderPorts,
247
+ // biome-ignore lint/suspicious/noExplicitAny: provider context types are erased at this level
248
+ any,
249
+ // biome-ignore lint/suspicious/noExplicitAny: provider service-input types are erased at this level
250
+ any
251
+ >;
252
+
146
253
  /**
147
254
  * Extract the ports a provider contributes.
148
255
  */
@@ -150,7 +257,9 @@ export type ProvidedPortsOf<TProvider> =
150
257
  TProvider extends ServiceProvider<
151
258
  infer _Ports,
152
259
  infer _CfgSchema,
153
- infer ProvidedPorts
260
+ infer ProvidedPorts,
261
+ infer _Context,
262
+ infer _ServiceInput
154
263
  >
155
264
  ? ProvidedPorts
156
265
  : NoProvidedPorts;
@@ -165,8 +274,20 @@ type UnionToIntersection<T> = (
165
274
 
166
275
  /**
167
276
  * Extract and merge the ports contributed by a provider list.
277
+ *
278
+ * Use this with `typeof providers` to type provider-contributed ports in app
279
+ * code without hand-written casts:
280
+ *
281
+ * @example
282
+ * ```ts
283
+ * import type { InferProviderPorts } from "@beignet/core/providers";
284
+ * import type { providers } from "@/server/providers";
285
+ * import type { AppPorts } from "@/ports";
286
+ *
287
+ * export type AppRuntimePorts = AppPorts & InferProviderPorts<typeof providers>;
288
+ * ```
168
289
  */
169
- export type ProvidedPortsOfList<TProviders> =
290
+ export type InferProviderPorts<TProviders> =
170
291
  TProviders extends readonly unknown[]
171
292
  ? [TProviders[number]] extends [never]
172
293
  ? NoProvidedPorts
@@ -179,6 +300,11 @@ export type ProvidedPortsOfList<TProviders> =
179
300
  * This is a simple identity function that helps TypeScript infer the correct types
180
301
  * for the provider definition.
181
302
  *
303
+ * App-local providers can use the curried zero-argument form to declare the
304
+ * ports they require from earlier providers plus their app context and
305
+ * service-context input. The required ports, `ctx.ports`, and
306
+ * `ctx.createServiceContext` are then fully typed with no casts.
307
+ *
182
308
  * @example
183
309
  * ```ts
184
310
  * export const myProvider = createProvider({
@@ -191,14 +317,47 @@ export type ProvidedPortsOfList<TProviders> =
191
317
  * return { ports: { myService: createMyService(config) } };
192
318
  * },
193
319
  * });
320
+ *
321
+ * // Typed app-local provider:
322
+ * export const appDatabaseProvider = createProvider<
323
+ * { db: DbPort<typeof schema>; devtools?: DevtoolsPort },
324
+ * AppContext,
325
+ * AppServiceContextInput
326
+ * >()({
327
+ * name: "app-database",
328
+ * async setup({ ports, createServiceContext }) {
329
+ * const repositories = createRepositories(ports.db.db);
330
+ * return { ports: repositories };
331
+ * },
332
+ * });
194
333
  * ```
195
334
  */
335
+ export function createProvider<
336
+ Requires = unknown,
337
+ Context = unknown,
338
+ ServiceInput = void,
339
+ >(): <
340
+ CfgSchema extends StandardSchemaV1 = StandardSchemaV1<void, void>,
341
+ Provided extends ProviderPorts = NoProvidedPorts,
342
+ >(
343
+ def: ServiceProvider<Requires, CfgSchema, Provided, Context, ServiceInput>,
344
+ ) => ServiceProvider<Requires, CfgSchema, Provided, Context, ServiceInput>;
196
345
  export function createProvider<
197
346
  Ports = unknown,
198
347
  CfgSchema extends StandardSchemaV1 = StandardSchemaV1<void, void>,
199
348
  ProvidedPorts extends ProviderPorts = NoProvidedPorts,
200
349
  >(
201
350
  def: ServiceProvider<Ports, CfgSchema, ProvidedPorts>,
202
- ): ServiceProvider<Ports, CfgSchema, ProvidedPorts> {
351
+ ): ServiceProvider<Ports, CfgSchema, ProvidedPorts>;
352
+ export function createProvider(
353
+ def?: ServiceProvider<unknown, StandardSchemaV1, ProviderPorts>,
354
+ ):
355
+ | ServiceProvider<unknown, StandardSchemaV1, ProviderPorts>
356
+ | ((
357
+ definition: ServiceProvider<unknown, StandardSchemaV1, ProviderPorts>,
358
+ ) => ServiceProvider<unknown, StandardSchemaV1, ProviderPorts>) {
359
+ if (def === undefined) {
360
+ return (definition) => definition;
361
+ }
203
362
  return def;
204
363
  }
@@ -29,6 +29,10 @@ export interface ScheduleRunContext {
29
29
  * Optional provider run ID.
30
30
  */
31
31
  readonly id?: string;
32
+ /**
33
+ * One-based provider attempt number for this run, when available.
34
+ */
35
+ readonly attempt?: number;
32
36
  /**
33
37
  * Time the provider planned the run.
34
38
  */
@@ -190,6 +194,10 @@ export interface ScheduleRunOptions<Payload = unknown> {
190
194
  * Optional provider run ID.
191
195
  */
192
196
  id?: string;
197
+ /**
198
+ * One-based provider attempt number for this run, when available.
199
+ */
200
+ attempt?: number;
193
201
  /**
194
202
  * Time the provider planned the run.
195
203
  */
@@ -262,6 +270,72 @@ export interface ScheduleErrorArgs<S extends ScheduleDef = ScheduleDef> {
262
270
  */
263
271
  export type ScheduleHookName = "start" | "success" | "error";
264
272
 
273
+ /**
274
+ * Devtools event recorded by the inline schedule runner for each run.
275
+ */
276
+ export interface ScheduleDevtoolsEvent {
277
+ /**
278
+ * Devtools event type.
279
+ */
280
+ type: "schedule";
281
+ /**
282
+ * Watcher category used by devtools.
283
+ */
284
+ watcher: "schedules";
285
+ /**
286
+ * Stable schedule name.
287
+ */
288
+ scheduleName: string;
289
+ /**
290
+ * Schedule run lifecycle status.
291
+ */
292
+ status: "started" | "completed" | "failed";
293
+ /**
294
+ * Cron expression for the schedule.
295
+ */
296
+ cron: string;
297
+ /**
298
+ * IANA timezone for the schedule, when declared.
299
+ */
300
+ timezone?: string;
301
+ /**
302
+ * Request correlation ID, when the trigger ran inside a request.
303
+ */
304
+ requestId?: string;
305
+ /**
306
+ * Trace identifier for distributed tracing integrations.
307
+ */
308
+ traceId?: string;
309
+ /**
310
+ * Structured run details such as `source`, `scheduledAt`, and `error`.
311
+ */
312
+ details?: Record<string, unknown>;
313
+ }
314
+
315
+ /**
316
+ * Sink for schedule devtools events, usually `ctx.ports.devtools`.
317
+ */
318
+ export interface ScheduleInstrumentation {
319
+ /**
320
+ * Record one schedule devtools event.
321
+ */
322
+ record(event: ScheduleDevtoolsEvent): unknown;
323
+ }
324
+
325
+ /**
326
+ * Correlation fields attached to schedule instrumentation events.
327
+ */
328
+ export interface ScheduleInstrumentationContext {
329
+ /**
330
+ * Request correlation ID for the triggering invocation.
331
+ */
332
+ requestId?: string;
333
+ /**
334
+ * Trace identifier for the triggering invocation.
335
+ */
336
+ traceId?: string;
337
+ }
338
+
265
339
  /**
266
340
  * Arguments passed when a schedule lifecycle hook itself fails.
267
341
  */
@@ -304,6 +378,18 @@ export interface InlineScheduleRunnerOptions<Ctx> {
304
378
  * Clock used when run timestamps are not provided.
305
379
  */
306
380
  now?: () => Date;
381
+ /**
382
+ * Devtools-compatible sink that receives `schedule` events for each run.
383
+ *
384
+ * The runner records `started`, `completed`, and `failed` events. Recording
385
+ * failures are isolated from schedule execution and reported to
386
+ * `onHookError` when provided.
387
+ */
388
+ instrumentation?: ScheduleInstrumentation;
389
+ /**
390
+ * Correlation fields attached to recorded schedule events.
391
+ */
392
+ instrumentationContext?: ScheduleInstrumentationContext;
307
393
  /**
308
394
  * Called after payload validation and before the schedule handler.
309
395
  */
@@ -352,7 +438,7 @@ export interface InlineScheduleRunner<Ctx = unknown>
352
438
  /**
353
439
  * Context-bound schedule helper factory.
354
440
  */
355
- export interface ScheduleHandlers<Ctx> {
441
+ export interface Schedules<Ctx> {
356
442
  /**
357
443
  * Define a schedule with the bound context type.
358
444
  */
@@ -360,13 +446,6 @@ export interface ScheduleHandlers<Ctx> {
360
446
  name: Name,
361
447
  options: DefineScheduleOptions<Name, Payload, Ctx>,
362
448
  ): ScheduleDef<Name, Payload, Ctx>;
363
-
364
- /**
365
- * Create an inline schedule runner with the bound context type.
366
- */
367
- createInlineScheduleRunner(
368
- options?: InlineScheduleRunnerOptions<Ctx>,
369
- ): InlineScheduleRunner<Ctx>;
370
449
  }
371
450
 
372
451
  /**
@@ -469,12 +548,27 @@ function normalizeOptionalDate(
469
548
  return normalizeDate(value, field, () => new Date());
470
549
  }
471
550
 
551
+ function normalizeOptionalAttempt(
552
+ value: number | undefined,
553
+ ): number | undefined {
554
+ if (value === undefined) return undefined;
555
+
556
+ if (!Number.isInteger(value) || value < 1) {
557
+ throw new ScheduleRunContextError(
558
+ "Schedule run attempt must be a positive integer.",
559
+ );
560
+ }
561
+
562
+ return value;
563
+ }
564
+
472
565
  function createRunContext(
473
566
  options: ScheduleRunOptions<unknown>,
474
567
  now: () => Date,
475
568
  ): ScheduleRunContext {
476
569
  return {
477
570
  id: options.id,
571
+ attempt: normalizeOptionalAttempt(options.attempt),
478
572
  scheduledAt: normalizeOptionalDate(options.scheduledAt, "scheduledAt"),
479
573
  triggeredAt: normalizeDate(options.triggeredAt, "triggeredAt", now),
480
574
  source: options.source,
@@ -556,13 +650,52 @@ async function runErrorHook<
556
650
  }
557
651
  }
558
652
 
559
- /**
560
- * Define a typed schedule.
561
- *
562
- * Cron and timezone are metadata for schedule providers. The inline runner only
563
- * runs schedules when its `run(...)` method is called.
564
- */
565
- export function defineSchedule<
653
+ async function recordScheduleEvent<
654
+ Ctx,
655
+ S extends ScheduleDef<string, StandardSchema, Ctx>,
656
+ >(
657
+ options: InlineScheduleRunnerOptions<Ctx>,
658
+ schedule: S,
659
+ status: ScheduleDevtoolsEvent["status"],
660
+ run: ScheduleRunContext,
661
+ args: {
662
+ payload?: InferSchedulePayload<S>;
663
+ hook: ScheduleHookName;
664
+ scheduleError?: unknown;
665
+ details?: Record<string, unknown>;
666
+ },
667
+ ): Promise<void> {
668
+ if (!options.instrumentation) return;
669
+
670
+ try {
671
+ options.instrumentation.record({
672
+ type: "schedule",
673
+ watcher: "schedules",
674
+ requestId: options.instrumentationContext?.requestId,
675
+ traceId: options.instrumentationContext?.traceId,
676
+ scheduleName: schedule.name,
677
+ status,
678
+ cron: schedule.cron,
679
+ timezone: schedule.timezone,
680
+ details: {
681
+ source: run.source,
682
+ scheduledAt: run.scheduledAt?.toISOString(),
683
+ ...args.details,
684
+ },
685
+ });
686
+ } catch (error) {
687
+ await reportHookError(options.onHookError, {
688
+ schedule,
689
+ payload: args.payload,
690
+ run,
691
+ hook: args.hook,
692
+ error,
693
+ scheduleError: args.scheduleError,
694
+ });
695
+ }
696
+ }
697
+
698
+ function defineScheduleImpl<
566
699
  Name extends string,
567
700
  Payload extends StandardSchema,
568
701
  Ctx = unknown,
@@ -637,6 +770,10 @@ export function createInlineScheduleRunner<Ctx>(
637
770
  payload = await resolveSchedulePayload(schedule, runOptions, run);
638
771
 
639
772
  const lifecycleArgs = { schedule, payload, run };
773
+ await recordScheduleEvent(options, schedule, "started", run, {
774
+ payload,
775
+ hook: "start",
776
+ });
640
777
  await runLifecycleHook(
641
778
  "start",
642
779
  options.onStart,
@@ -649,6 +786,10 @@ export function createInlineScheduleRunner<Ctx>(
649
786
  ctx: await resolveCtx(options.ctx),
650
787
  run,
651
788
  });
789
+ await recordScheduleEvent(options, schedule, "completed", run, {
790
+ payload,
791
+ hook: "success",
792
+ });
652
793
  await runLifecycleHook(
653
794
  "success",
654
795
  options.onSuccess,
@@ -656,6 +797,12 @@ export function createInlineScheduleRunner<Ctx>(
656
797
  lifecycleArgs,
657
798
  );
658
799
  } catch (error) {
800
+ await recordScheduleEvent(options, schedule, "failed", run, {
801
+ payload,
802
+ hook: "error",
803
+ scheduleError: error,
804
+ details: { error },
805
+ });
659
806
  await runErrorHook(options.onError, options.onHookError, {
660
807
  error,
661
808
  schedule,
@@ -670,20 +817,23 @@ export function createInlineScheduleRunner<Ctx>(
670
817
 
671
818
  /**
672
819
  * Create schedule helper methods bound to an application context type.
820
+ *
821
+ * Call it once in `lib/schedules.ts`:
822
+ *
823
+ * ```ts
824
+ * export const { defineSchedule } = createSchedules<AppContext>();
825
+ * ```
826
+ *
827
+ * Cron and timezone are metadata for schedule providers. The inline runner only
828
+ * runs schedules when its `run(...)` method is called.
673
829
  */
674
- export function createScheduleHandlers<Ctx>(): ScheduleHandlers<Ctx> {
830
+ export function createSchedules<Ctx>(): Schedules<Ctx> {
675
831
  return {
676
832
  defineSchedule<Name extends string, Payload extends StandardSchema>(
677
833
  name: Name,
678
834
  options: DefineScheduleOptions<Name, Payload, Ctx>,
679
835
  ): ScheduleDef<Name, Payload, Ctx> {
680
- return defineSchedule(name, options);
681
- },
682
-
683
- createInlineScheduleRunner(
684
- options: InlineScheduleRunnerOptions<Ctx> = {},
685
- ): InlineScheduleRunner<Ctx> {
686
- return createInlineScheduleRunner(options);
836
+ return defineScheduleImpl(name, options);
687
837
  },
688
838
  };
689
839
  }
@@ -0,0 +1,45 @@
1
+ import type { AuditLogPort } from "../ports/index.js";
2
+ import { getActiveRequestContext } from "./request-context.js";
3
+
4
+ /**
5
+ * Wrap an audit log port so missing `actor`, `tenant`, `requestId`, and
6
+ * `traceId` fields are filled from the ambient request context at record time.
7
+ *
8
+ * The server keeps the ambient context current for requests (including
9
+ * identity elevated by hooks) and for service contexts created for jobs,
10
+ * listeners, schedules, and tasks. Because enrichment happens when
11
+ * `record(...)` runs, the wrapper also works for ports rebuilt per
12
+ * transaction inside a unit of work — wrap both the top-level audit port and
13
+ * the per-transaction rebuild.
14
+ *
15
+ * Fields provided on the entry always win; only missing fields are filled.
16
+ * When no ambient context is active (for example on runtimes without
17
+ * `AsyncLocalStorage` propagation), entries pass through unchanged.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * const audit = createAmbientAuditLog(
22
+ * createInstrumentedAuditLog({ audit: createDrizzleAuditLog(db), instrumentation: ports }),
23
+ * );
24
+ * await audit.record({ action: "posts.publish", resource: { type: "post", id } });
25
+ * ```
26
+ *
27
+ * @param audit - Underlying audit log port to write enriched entries to.
28
+ * @returns An audit log port that fills missing context fields before writing.
29
+ */
30
+ export function createAmbientAuditLog(audit: AuditLogPort): AuditLogPort {
31
+ return {
32
+ record(entry) {
33
+ const context = getActiveRequestContext();
34
+ if (!context) return audit.record(entry);
35
+
36
+ return audit.record({
37
+ ...entry,
38
+ actor: entry.actor ?? context.actor,
39
+ tenant: entry.tenant ?? context.tenant,
40
+ requestId: entry.requestId ?? context.requestId,
41
+ traceId: entry.traceId ?? context.traceId,
42
+ });
43
+ },
44
+ };
45
+ }