@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
package/src/jobs/index.ts CHANGED
@@ -16,6 +16,48 @@ export type MaybePromise<T> = T | Promise<T>;
16
16
  export type InferSchemaOutput<T extends StandardSchemaV1> =
17
17
  StandardSchemaV1.InferOutput<T>;
18
18
 
19
+ /**
20
+ * Duration accepted by retry helpers. Numbers are milliseconds.
21
+ */
22
+ export type JobRetryDuration =
23
+ | number
24
+ | `${number}ms`
25
+ | `${number}s`
26
+ | `${number}m`
27
+ | `${number}h`;
28
+
29
+ /**
30
+ * Retry strategy understood by Beignet job adapters.
31
+ */
32
+ export type JobRetryStrategy = "none" | "fixed" | "exponential";
33
+
34
+ /**
35
+ * Arguments passed to a retry predicate.
36
+ */
37
+ export interface JobRetryPredicateArgs {
38
+ /**
39
+ * Error thrown by the previous attempt.
40
+ */
41
+ error: unknown;
42
+ /**
43
+ * One-based attempt number that just failed.
44
+ */
45
+ attempt: number;
46
+ /**
47
+ * Maximum attempts allowed for this delivery.
48
+ */
49
+ maxAttempts: number;
50
+ /**
51
+ * Job name when the retry decision is for a job.
52
+ */
53
+ jobName?: string;
54
+ }
55
+
56
+ /**
57
+ * Return whether a failed attempt should be retried.
58
+ */
59
+ export type JobRetryPredicate = (args: JobRetryPredicateArgs) => boolean;
60
+
19
61
  /**
20
62
  * Job definition created by `defineJob(...)`.
21
63
  */
@@ -81,12 +123,38 @@ export interface JobHandleArgs<J extends JobDef, Ctx> {
81
123
  */
82
124
  export interface JobRetryOptions {
83
125
  /**
84
- * Maximum number of retry attempts a durable job adapter should request.
85
- *
86
- * Providers may impose their own range limits. For example, the Inngest
87
- * adapter validates this as a function-level `retries` value.
126
+ * Retry strategy. Raw objects without a strategy default to exponential
127
+ * backoff so existing `{ attempts }` style definitions stay meaningful.
128
+ */
129
+ strategy?: JobRetryStrategy;
130
+ /**
131
+ * Maximum total attempts, including the first attempt.
88
132
  */
89
133
  attempts?: number;
134
+ /**
135
+ * Delay between attempts for fixed retry policies.
136
+ */
137
+ delay?: JobRetryDuration;
138
+ /**
139
+ * Initial delay for exponential retry policies.
140
+ */
141
+ initialDelay?: JobRetryDuration;
142
+ /**
143
+ * Maximum delay for exponential retry policies.
144
+ */
145
+ maxDelay?: JobRetryDuration;
146
+ /**
147
+ * Exponential multiplier. Defaults to `2`.
148
+ */
149
+ factor?: number;
150
+ /**
151
+ * Whether adapters that compute delays should add jitter.
152
+ */
153
+ jitter?: boolean;
154
+ /**
155
+ * Optional app-owned retry classifier.
156
+ */
157
+ retryIf?: JobRetryPredicate;
90
158
  }
91
159
 
92
160
  /**
@@ -117,6 +185,96 @@ export interface DefineJobOptions<
117
185
  ): MaybePromise<void>;
118
186
  }
119
187
 
188
+ /**
189
+ * Options for a fixed job retry policy.
190
+ */
191
+ export interface FixedJobRetryOptions {
192
+ /**
193
+ * Maximum total attempts, including the first attempt.
194
+ */
195
+ attempts: number;
196
+ /**
197
+ * Delay between attempts.
198
+ */
199
+ delay: JobRetryDuration;
200
+ /**
201
+ * Optional app-owned retry classifier.
202
+ */
203
+ retryIf?: JobRetryPredicate;
204
+ }
205
+
206
+ /**
207
+ * Options for an exponential job retry policy.
208
+ */
209
+ export interface ExponentialJobRetryOptions {
210
+ /**
211
+ * Maximum total attempts, including the first attempt.
212
+ */
213
+ attempts: number;
214
+ /**
215
+ * Initial delay. Defaults to `1s`.
216
+ */
217
+ initialDelay?: JobRetryDuration;
218
+ /**
219
+ * Maximum delay. Defaults to `1m`.
220
+ */
221
+ maxDelay?: JobRetryDuration;
222
+ /**
223
+ * Exponential multiplier. Defaults to `2`.
224
+ */
225
+ factor?: number;
226
+ /**
227
+ * Whether computed delays should include jitter.
228
+ */
229
+ jitter?: boolean;
230
+ /**
231
+ * Optional app-owned retry classifier.
232
+ */
233
+ retryIf?: JobRetryPredicate;
234
+ }
235
+
236
+ /**
237
+ * Retry helper namespace for job definitions.
238
+ */
239
+ export const retry = {
240
+ /**
241
+ * Disable retries. The first failure is terminal.
242
+ */
243
+ none(): JobRetryOptions {
244
+ return {
245
+ strategy: "none",
246
+ attempts: 1,
247
+ };
248
+ },
249
+
250
+ /**
251
+ * Retry with the same delay between attempts.
252
+ */
253
+ fixed(options: FixedJobRetryOptions): JobRetryOptions {
254
+ return validateJobRetryOptions({
255
+ strategy: "fixed",
256
+ attempts: options.attempts,
257
+ delay: options.delay,
258
+ retryIf: options.retryIf,
259
+ });
260
+ },
261
+
262
+ /**
263
+ * Retry with exponential backoff.
264
+ */
265
+ exponential(options: ExponentialJobRetryOptions): JobRetryOptions {
266
+ return validateJobRetryOptions({
267
+ strategy: "exponential",
268
+ attempts: options.attempts,
269
+ initialDelay: options.initialDelay,
270
+ maxDelay: options.maxDelay,
271
+ factor: options.factor,
272
+ jitter: options.jitter,
273
+ retryIf: options.retryIf,
274
+ });
275
+ },
276
+ } as const;
277
+
120
278
  /**
121
279
  * Options for the inline job dispatcher.
122
280
  */
@@ -148,7 +306,7 @@ export interface InlineJobDispatcher<Ctx = unknown> {
148
306
  /**
149
307
  * Context-bound job helper factory.
150
308
  */
151
- export interface JobHandlers<Ctx> {
309
+ export interface Jobs<Ctx> {
152
310
  /**
153
311
  * Define a job with the bound context type.
154
312
  */
@@ -156,13 +314,6 @@ export interface JobHandlers<Ctx> {
156
314
  name: Name,
157
315
  options: DefineJobOptions<Name, Payload, Ctx>,
158
316
  ): JobDef<Name, Payload, Ctx>;
159
-
160
- /**
161
- * Create an inline dispatcher with the bound context type.
162
- */
163
- createInlineJobDispatcher(
164
- options?: InlineJobDispatcherOptions<Ctx>,
165
- ): InlineJobDispatcher<Ctx>;
166
317
  }
167
318
 
168
319
  /**
@@ -207,6 +358,165 @@ function formatIssues(issues: readonly StandardSchemaV1.Issue[]): string {
207
358
  .join("; ");
208
359
  }
209
360
 
361
+ function assertPositiveInteger(name: string, value: number): void {
362
+ if (!Number.isInteger(value) || value <= 0) {
363
+ throw new Error(`${name} must be a positive integer`);
364
+ }
365
+ }
366
+
367
+ function assertPositiveNumber(name: string, value: number): void {
368
+ if (!Number.isFinite(value) || value <= 0) {
369
+ throw new Error(`${name} must be a positive number`);
370
+ }
371
+ }
372
+
373
+ function durationToMs(name: string, value: JobRetryDuration): number {
374
+ if (typeof value === "number") {
375
+ assertPositiveInteger(name, value);
376
+ return value;
377
+ }
378
+
379
+ const match = /^(\d+)(ms|s|m|h)$/.exec(value);
380
+ if (!match) {
381
+ throw new Error(
382
+ `${name} must be a positive millisecond value or duration string like "500ms", "30s", "5m", or "1h".`,
383
+ );
384
+ }
385
+
386
+ const amount = Number(match[1]);
387
+ assertPositiveInteger(name, amount);
388
+
389
+ switch (match[2]) {
390
+ case "ms":
391
+ return amount;
392
+ case "s":
393
+ return amount * 1000;
394
+ case "m":
395
+ return amount * 60_000;
396
+ case "h":
397
+ return amount * 3_600_000;
398
+ default:
399
+ throw new Error(`${name} has an unsupported duration unit.`);
400
+ }
401
+ }
402
+
403
+ function validateJobRetryOptions(options: JobRetryOptions): JobRetryOptions {
404
+ const strategy = options.strategy ?? "exponential";
405
+
406
+ if (!["none", "fixed", "exponential"].includes(strategy)) {
407
+ throw new Error("retry.strategy must be none, fixed, or exponential");
408
+ }
409
+
410
+ const attempts = options.attempts ?? (strategy === "none" ? 1 : undefined);
411
+ if (attempts === undefined) {
412
+ throw new Error("retry.attempts is required");
413
+ }
414
+ assertPositiveInteger("retry.attempts", attempts);
415
+
416
+ if (strategy === "none" && attempts !== 1) {
417
+ throw new Error("retry.none() must use exactly one attempt");
418
+ }
419
+
420
+ if (strategy === "fixed") {
421
+ if (options.delay === undefined) {
422
+ throw new Error("retry.delay is required for fixed retry policies");
423
+ }
424
+ durationToMs("retry.delay", options.delay);
425
+ }
426
+
427
+ if (strategy === "exponential") {
428
+ if (options.initialDelay !== undefined) {
429
+ durationToMs("retry.initialDelay", options.initialDelay);
430
+ }
431
+ if (options.maxDelay !== undefined) {
432
+ durationToMs("retry.maxDelay", options.maxDelay);
433
+ }
434
+ if (options.factor !== undefined) {
435
+ assertPositiveNumber("retry.factor", options.factor);
436
+ }
437
+ }
438
+
439
+ return {
440
+ ...options,
441
+ strategy,
442
+ attempts,
443
+ };
444
+ }
445
+
446
+ /**
447
+ * Return the maximum total attempts configured by a retry policy.
448
+ */
449
+ export function getJobRetryMaxAttempts(
450
+ options: JobRetryOptions | undefined,
451
+ ): number | undefined {
452
+ return options ? validateJobRetryOptions(options).attempts : undefined;
453
+ }
454
+
455
+ /**
456
+ * Return whether a failed job attempt should be retried.
457
+ */
458
+ export function shouldRetryJob(
459
+ options: JobRetryOptions | undefined,
460
+ args: JobRetryPredicateArgs,
461
+ ): boolean {
462
+ if (!options) return args.attempt < args.maxAttempts;
463
+
464
+ const retryOptions = validateJobRetryOptions(options);
465
+ const maxAttempts = Math.min(
466
+ args.maxAttempts,
467
+ retryOptions.attempts ?? args.maxAttempts,
468
+ );
469
+ if (retryOptions.strategy === "none") return false;
470
+ if (args.attempt >= maxAttempts) return false;
471
+
472
+ return retryOptions.retryIf?.({ ...args, maxAttempts }) ?? true;
473
+ }
474
+
475
+ /**
476
+ * Compute the next retry delay in milliseconds for a failed job attempt.
477
+ */
478
+ export function getJobRetryDelayMs(
479
+ options: JobRetryOptions | undefined,
480
+ args: Pick<JobRetryPredicateArgs, "attempt" | "error" | "jobName">,
481
+ ): number {
482
+ const retryOptions = options
483
+ ? validateJobRetryOptions(options)
484
+ : retry.exponential({ attempts: 3 });
485
+
486
+ let delayMs: number;
487
+ if (retryOptions.strategy === "fixed") {
488
+ delayMs = durationToMs("retry.delay", retryOptions.delay ?? "1s");
489
+ } else if (retryOptions.strategy === "none") {
490
+ delayMs = 0;
491
+ } else {
492
+ const initialDelayMs = durationToMs(
493
+ "retry.initialDelay",
494
+ retryOptions.initialDelay ?? "1s",
495
+ );
496
+ const maxDelayMs = durationToMs(
497
+ "retry.maxDelay",
498
+ retryOptions.maxDelay ?? "1m",
499
+ );
500
+ const factor = retryOptions.factor ?? 2;
501
+ delayMs = Math.min(
502
+ maxDelayMs,
503
+ initialDelayMs * factor ** Math.max(0, args.attempt - 1),
504
+ );
505
+ }
506
+
507
+ if (retryOptions.jitter && delayMs > 0) {
508
+ delayMs = Math.ceil(delayMs * (0.5 + Math.random()));
509
+ if (retryOptions.strategy === "exponential") {
510
+ delayMs = Math.min(
511
+ delayMs,
512
+ durationToMs("retry.maxDelay", retryOptions.maxDelay ?? "1m"),
513
+ );
514
+ }
515
+ }
516
+
517
+ return delayMs;
518
+ }
519
+
210
520
  async function parsePayload<Schema extends StandardSchemaV1>(
211
521
  schema: Schema,
212
522
  input: unknown,
@@ -238,14 +548,7 @@ async function resolveCtx<Ctx>(
238
548
  return ctx as Ctx;
239
549
  }
240
550
 
241
- /**
242
- * Define a typed job.
243
- *
244
- * Retry options are provider hints. Inline dispatchers validate payloads and
245
- * call `handle(...)` immediately; durable providers may enqueue or schedule the
246
- * job according to their own runtime.
247
- */
248
- export function defineJob<
551
+ function defineJobImpl<
249
552
  Name extends string,
250
553
  Payload extends StandardSchema,
251
554
  Ctx = unknown,
@@ -253,12 +556,16 @@ export function defineJob<
253
556
  name: Name,
254
557
  options: DefineJobOptions<Name, Payload, Ctx>,
255
558
  ): JobDef<Name, Payload, Ctx> {
559
+ const retryOptions = options.retry
560
+ ? validateJobRetryOptions(options.retry)
561
+ : undefined;
562
+
256
563
  return {
257
564
  kind: "job",
258
565
  name,
259
566
  payload: options.payload,
260
567
  description: options.description,
261
- retry: options.retry,
568
+ retry: retryOptions,
262
569
  handle: options.handle as JobDef<Name, Payload, Ctx>["handle"],
263
570
  };
264
571
  }
@@ -303,20 +610,24 @@ export function createInlineJobDispatcher<Ctx>(
303
610
 
304
611
  /**
305
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.
306
623
  */
307
- export function createJobHandlers<Ctx>(): JobHandlers<Ctx> {
624
+ export function createJobs<Ctx>(): Jobs<Ctx> {
308
625
  return {
309
626
  defineJob<Name extends string, Payload extends StandardSchema>(
310
627
  name: Name,
311
628
  options: DefineJobOptions<Name, Payload, Ctx>,
312
629
  ): JobDef<Name, Payload, Ctx> {
313
- return defineJob(name, options);
314
- },
315
-
316
- createInlineJobDispatcher(
317
- options: InlineJobDispatcherOptions<Ctx> = {},
318
- ): InlineJobDispatcher<Ctx> {
319
- return createInlineJobDispatcher(options);
630
+ return defineJobImpl(name, options);
320
631
  },
321
632
  };
322
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
  }