@beignet/core 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (287) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +202 -8
  3. package/dist/application/index.d.ts +93 -9
  4. package/dist/application/index.d.ts.map +1 -1
  5. package/dist/application/index.js +11 -11
  6. package/dist/application/index.js.map +1 -1
  7. package/dist/client/client.d.ts +73 -12
  8. package/dist/client/client.d.ts.map +1 -1
  9. package/dist/client/client.js +37 -12
  10. package/dist/client/client.js.map +1 -1
  11. package/dist/client/index.d.ts +12 -0
  12. package/dist/client/index.d.ts.map +1 -1
  13. package/dist/client/index.js +6 -0
  14. package/dist/client/index.js.map +1 -1
  15. package/dist/client/types.d.ts +69 -8
  16. package/dist/client/types.d.ts.map +1 -1
  17. package/dist/config/index.d.ts +84 -0
  18. package/dist/config/index.d.ts.map +1 -1
  19. package/dist/config/index.js +36 -0
  20. package/dist/config/index.js.map +1 -1
  21. package/dist/contracts/contract-builder.d.ts +49 -22
  22. package/dist/contracts/contract-builder.d.ts.map +1 -1
  23. package/dist/contracts/contract-builder.js +48 -21
  24. package/dist/contracts/contract-builder.js.map +1 -1
  25. package/dist/contracts/contract-group.d.ts +35 -19
  26. package/dist/contracts/contract-group.d.ts.map +1 -1
  27. package/dist/contracts/contract-group.js +35 -19
  28. package/dist/contracts/contract-group.js.map +1 -1
  29. package/dist/contracts/contract-like.d.ts +4 -4
  30. package/dist/contracts/contract-like.d.ts.map +1 -1
  31. package/dist/contracts/contract-like.js +2 -1
  32. package/dist/contracts/contract-like.js.map +1 -1
  33. package/dist/contracts/index.d.ts +28 -0
  34. package/dist/contracts/index.d.ts.map +1 -1
  35. package/dist/contracts/index.js +12 -0
  36. package/dist/contracts/index.js.map +1 -1
  37. package/dist/contracts/openapi-meta.d.ts +8 -8
  38. package/dist/contracts/openapi-meta.d.ts.map +1 -1
  39. package/dist/contracts/path-template.d.ts +27 -0
  40. package/dist/contracts/path-template.d.ts.map +1 -1
  41. package/dist/contracts/path-template.js +6 -0
  42. package/dist/contracts/path-template.js.map +1 -1
  43. package/dist/contracts/types.d.ts +104 -10
  44. package/dist/contracts/types.d.ts.map +1 -1
  45. package/dist/contracts/types.js +15 -0
  46. package/dist/contracts/types.js.map +1 -1
  47. package/dist/contracts/utils.d.ts +6 -0
  48. package/dist/contracts/utils.d.ts.map +1 -1
  49. package/dist/contracts/utils.js +6 -0
  50. package/dist/contracts/utils.js.map +1 -1
  51. package/dist/domain/entity.d.ts +22 -11
  52. package/dist/domain/entity.d.ts.map +1 -1
  53. package/dist/domain/entity.js +5 -1
  54. package/dist/domain/entity.js.map +1 -1
  55. package/dist/domain/events.d.ts +5 -2
  56. package/dist/domain/events.d.ts.map +1 -1
  57. package/dist/domain/events.js +4 -1
  58. package/dist/domain/events.js.map +1 -1
  59. package/dist/domain/value-object.d.ts +19 -9
  60. package/dist/domain/value-object.d.ts.map +1 -1
  61. package/dist/domain/value-object.js +5 -1
  62. package/dist/domain/value-object.js.map +1 -1
  63. package/dist/errors/catalog.d.ts +40 -16
  64. package/dist/errors/catalog.d.ts.map +1 -1
  65. package/dist/errors/catalog.js +18 -7
  66. package/dist/errors/catalog.js.map +1 -1
  67. package/dist/errors/response.d.ts +16 -4
  68. package/dist/errors/response.d.ts.map +1 -1
  69. package/dist/errors/response.js +3 -3
  70. package/dist/errors/response.js.map +1 -1
  71. package/dist/errors/validation.d.ts +10 -1
  72. package/dist/errors/validation.d.ts.map +1 -1
  73. package/dist/errors/validation.js +3 -0
  74. package/dist/errors/validation.js.map +1 -1
  75. package/dist/events/index.d.ts +133 -0
  76. package/dist/events/index.d.ts.map +1 -1
  77. package/dist/events/index.js +30 -0
  78. package/dist/events/index.js.map +1 -1
  79. package/dist/idempotency/index.d.ts +355 -0
  80. package/dist/idempotency/index.d.ts.map +1 -0
  81. package/dist/idempotency/index.js +360 -0
  82. package/dist/idempotency/index.js.map +1 -0
  83. package/dist/jobs/index.d.ts +248 -4
  84. package/dist/jobs/index.d.ts.map +1 -1
  85. package/dist/jobs/index.js +183 -1
  86. package/dist/jobs/index.js.map +1 -1
  87. package/dist/mail/index.d.ts +149 -0
  88. package/dist/mail/index.d.ts.map +1 -1
  89. package/dist/mail/index.js +30 -0
  90. package/dist/mail/index.js.map +1 -1
  91. package/dist/notifications/index.d.ts +369 -0
  92. package/dist/notifications/index.d.ts.map +1 -0
  93. package/dist/notifications/index.js +310 -0
  94. package/dist/notifications/index.js.map +1 -0
  95. package/dist/openapi/index.d.ts +132 -16
  96. package/dist/openapi/index.d.ts.map +1 -1
  97. package/dist/openapi/index.js +1 -1
  98. package/dist/openapi/index.js.map +1 -1
  99. package/dist/outbox/index.d.ts +474 -0
  100. package/dist/outbox/index.d.ts.map +1 -0
  101. package/dist/outbox/index.js +538 -0
  102. package/dist/outbox/index.js.map +1 -0
  103. package/dist/pagination/index.d.ts +166 -0
  104. package/dist/pagination/index.d.ts.map +1 -0
  105. package/dist/pagination/index.js +96 -0
  106. package/dist/pagination/index.js.map +1 -0
  107. package/dist/ports/audit.d.ts +271 -0
  108. package/dist/ports/audit.d.ts.map +1 -1
  109. package/dist/ports/audit.js +128 -0
  110. package/dist/ports/audit.js.map +1 -1
  111. package/dist/ports/auth.d.ts +70 -0
  112. package/dist/ports/auth.d.ts.map +1 -1
  113. package/dist/ports/auth.js +30 -0
  114. package/dist/ports/auth.js.map +1 -1
  115. package/dist/ports/cache.d.ts +41 -0
  116. package/dist/ports/cache.d.ts.map +1 -1
  117. package/dist/ports/cache.js +10 -0
  118. package/dist/ports/cache.js.map +1 -1
  119. package/dist/ports/clock.d.ts +38 -0
  120. package/dist/ports/clock.d.ts.map +1 -1
  121. package/dist/ports/clock.js +20 -0
  122. package/dist/ports/clock.js.map +1 -1
  123. package/dist/ports/id-generator.d.ts +37 -0
  124. package/dist/ports/id-generator.d.ts.map +1 -1
  125. package/dist/ports/id-generator.js +22 -0
  126. package/dist/ports/id-generator.js.map +1 -1
  127. package/dist/ports/index.d.ts +83 -0
  128. package/dist/ports/index.d.ts.map +1 -1
  129. package/dist/ports/index.js +41 -5
  130. package/dist/ports/index.js.map +1 -1
  131. package/dist/ports/logger.d.ts +56 -0
  132. package/dist/ports/logger.d.ts.map +1 -1
  133. package/dist/ports/logger.js +17 -0
  134. package/dist/ports/logger.js.map +1 -1
  135. package/dist/ports/policy.d.ts +132 -0
  136. package/dist/ports/policy.d.ts.map +1 -1
  137. package/dist/ports/policy.js +45 -0
  138. package/dist/ports/policy.js.map +1 -1
  139. package/dist/ports/rate-limit.d.ts +25 -0
  140. package/dist/ports/rate-limit.d.ts.map +1 -1
  141. package/dist/ports/rate-limit.js +10 -0
  142. package/dist/ports/rate-limit.js.map +1 -1
  143. package/dist/ports/redaction.d.ts +101 -0
  144. package/dist/ports/redaction.d.ts.map +1 -1
  145. package/dist/ports/redaction.js +59 -0
  146. package/dist/ports/redaction.js.map +1 -1
  147. package/dist/ports/storage.d.ts +100 -0
  148. package/dist/ports/storage.d.ts.map +1 -1
  149. package/dist/ports/storage.js +10 -0
  150. package/dist/ports/storage.js.map +1 -1
  151. package/dist/ports/testing.d.ts +47 -0
  152. package/dist/ports/testing.d.ts.map +1 -1
  153. package/dist/ports/testing.js +23 -0
  154. package/dist/ports/testing.js.map +1 -1
  155. package/dist/ports/unit-of-work.d.ts +60 -3
  156. package/dist/ports/unit-of-work.d.ts.map +1 -1
  157. package/dist/ports/unit-of-work.js +11 -2
  158. package/dist/ports/unit-of-work.js.map +1 -1
  159. package/dist/providers/instrumentation.d.ts +205 -1
  160. package/dist/providers/instrumentation.d.ts.map +1 -1
  161. package/dist/providers/instrumentation.js +14 -0
  162. package/dist/providers/instrumentation.js.map +1 -1
  163. package/dist/providers/provider.d.ts +14 -1
  164. package/dist/providers/provider.d.ts.map +1 -1
  165. package/dist/providers/provider.js.map +1 -1
  166. package/dist/schedules/index.d.ts +246 -0
  167. package/dist/schedules/index.d.ts.map +1 -1
  168. package/dist/schedules/index.js +27 -0
  169. package/dist/schedules/index.js.map +1 -1
  170. package/dist/server/health.d.ts +14 -5
  171. package/dist/server/health.d.ts.map +1 -1
  172. package/dist/server/health.js +5 -2
  173. package/dist/server/health.js.map +1 -1
  174. package/dist/server/hooks/auth.d.ts +68 -26
  175. package/dist/server/hooks/auth.d.ts.map +1 -1
  176. package/dist/server/hooks/auth.js +44 -55
  177. package/dist/server/hooks/auth.js.map +1 -1
  178. package/dist/server/hooks/cors.d.ts +27 -0
  179. package/dist/server/hooks/cors.d.ts.map +1 -1
  180. package/dist/server/hooks/cors.js +12 -0
  181. package/dist/server/hooks/cors.js.map +1 -1
  182. package/dist/server/hooks/errors.d.ts +15 -6
  183. package/dist/server/hooks/errors.d.ts.map +1 -1
  184. package/dist/server/hooks/errors.js.map +1 -1
  185. package/dist/server/hooks/index.d.ts +4 -1
  186. package/dist/server/hooks/index.d.ts.map +1 -1
  187. package/dist/server/hooks/index.js +3 -0
  188. package/dist/server/hooks/index.js.map +1 -1
  189. package/dist/server/hooks/logging.d.ts +36 -0
  190. package/dist/server/hooks/logging.d.ts.map +1 -1
  191. package/dist/server/hooks/logging.js +6 -0
  192. package/dist/server/hooks/logging.js.map +1 -1
  193. package/dist/server/hooks/rate-limit.d.ts +33 -0
  194. package/dist/server/hooks/rate-limit.d.ts.map +1 -1
  195. package/dist/server/hooks/rate-limit.js +11 -0
  196. package/dist/server/hooks/rate-limit.js.map +1 -1
  197. package/dist/server/http.d.ts +222 -0
  198. package/dist/server/http.d.ts.map +1 -1
  199. package/dist/server/http.js +20 -1
  200. package/dist/server/http.js.map +1 -1
  201. package/dist/server/index.d.ts +19 -1
  202. package/dist/server/index.d.ts.map +1 -1
  203. package/dist/server/index.js +7 -1
  204. package/dist/server/index.js.map +1 -1
  205. package/dist/server/openapi.d.ts +5 -3
  206. package/dist/server/openapi.d.ts.map +1 -1
  207. package/dist/server/openapi.js +4 -2
  208. package/dist/server/openapi.js.map +1 -1
  209. package/dist/server/providers/loadProviderConfig.d.ts +9 -0
  210. package/dist/server/providers/loadProviderConfig.d.ts.map +1 -1
  211. package/dist/server/providers/loadProviderConfig.js +9 -0
  212. package/dist/server/providers/loadProviderConfig.js.map +1 -1
  213. package/dist/server/server.d.ts +159 -19
  214. package/dist/server/server.d.ts.map +1 -1
  215. package/dist/server/server.js +72 -31
  216. package/dist/server/server.js.map +1 -1
  217. package/dist/testing/index.d.ts +171 -0
  218. package/dist/testing/index.d.ts.map +1 -0
  219. package/dist/testing/index.js +127 -0
  220. package/dist/testing/index.js.map +1 -0
  221. package/dist/uploads/client.d.ts +278 -0
  222. package/dist/uploads/client.d.ts.map +1 -0
  223. package/dist/uploads/client.js +428 -0
  224. package/dist/uploads/client.js.map +1 -0
  225. package/dist/uploads/index.d.ts +361 -0
  226. package/dist/uploads/index.d.ts.map +1 -0
  227. package/dist/uploads/index.js +543 -0
  228. package/dist/uploads/index.js.map +1 -0
  229. package/package.json +31 -2
  230. package/src/application/index.ts +85 -22
  231. package/src/client/client.ts +73 -12
  232. package/src/client/index.ts +12 -0
  233. package/src/client/types.ts +70 -9
  234. package/src/config/index.ts +86 -0
  235. package/src/contracts/contract-builder.ts +49 -22
  236. package/src/contracts/contract-group.ts +35 -19
  237. package/src/contracts/contract-like.ts +4 -4
  238. package/src/contracts/index.ts +28 -1
  239. package/src/contracts/openapi-meta.ts +8 -8
  240. package/src/contracts/path-template.ts +27 -0
  241. package/src/contracts/types.ts +111 -10
  242. package/src/contracts/utils.ts +6 -0
  243. package/src/domain/entity.ts +22 -11
  244. package/src/domain/events.ts +5 -2
  245. package/src/domain/value-object.ts +19 -9
  246. package/src/errors/catalog.ts +40 -16
  247. package/src/errors/response.ts +16 -4
  248. package/src/errors/validation.ts +10 -1
  249. package/src/events/index.ts +134 -0
  250. package/src/idempotency/index.ts +767 -0
  251. package/src/jobs/index.ts +437 -5
  252. package/src/mail/index.ts +149 -0
  253. package/src/notifications/index.ts +771 -0
  254. package/src/openapi/index.ts +133 -16
  255. package/src/outbox/index.ts +1104 -0
  256. package/src/pagination/index.ts +278 -0
  257. package/src/ports/audit.ts +271 -0
  258. package/src/ports/auth.ts +70 -0
  259. package/src/ports/cache.ts +41 -0
  260. package/src/ports/clock.ts +38 -0
  261. package/src/ports/id-generator.ts +37 -0
  262. package/src/ports/index.ts +106 -11
  263. package/src/ports/logger.ts +56 -0
  264. package/src/ports/policy.ts +133 -0
  265. package/src/ports/rate-limit.ts +25 -0
  266. package/src/ports/redaction.ts +101 -0
  267. package/src/ports/storage.ts +100 -0
  268. package/src/ports/testing.ts +47 -0
  269. package/src/ports/unit-of-work.ts +60 -3
  270. package/src/providers/instrumentation.ts +211 -1
  271. package/src/providers/provider.ts +14 -1
  272. package/src/schedules/index.ts +247 -0
  273. package/src/server/health.ts +14 -5
  274. package/src/server/hooks/auth.ts +105 -120
  275. package/src/server/hooks/cors.ts +27 -0
  276. package/src/server/hooks/errors.ts +15 -6
  277. package/src/server/hooks/index.ts +4 -5
  278. package/src/server/hooks/logging.ts +36 -0
  279. package/src/server/hooks/rate-limit.ts +33 -0
  280. package/src/server/http.ts +249 -1
  281. package/src/server/index.ts +19 -1
  282. package/src/server/openapi.ts +5 -3
  283. package/src/server/providers/loadProviderConfig.ts +9 -0
  284. package/src/server/server.ts +296 -30
  285. package/src/testing/index.ts +348 -0
  286. package/src/uploads/client.ts +861 -0
  287. package/src/uploads/index.ts +1067 -0
package/src/jobs/index.ts CHANGED
@@ -1,82 +1,335 @@
1
1
  import type { StandardSchemaV1 } from "@standard-schema/spec";
2
2
 
3
+ /**
4
+ * Any Standard Schema compatible validator.
5
+ */
3
6
  export type StandardSchema = StandardSchemaV1<unknown, unknown>;
7
+
8
+ /**
9
+ * Value or promise of that value.
10
+ */
4
11
  export type MaybePromise<T> = T | Promise<T>;
5
12
 
13
+ /**
14
+ * Infer the parsed output type from a Standard Schema.
15
+ */
6
16
  export type InferSchemaOutput<T extends StandardSchemaV1> =
7
17
  StandardSchemaV1.InferOutput<T>;
8
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
+
61
+ /**
62
+ * Job definition created by `defineJob(...)`.
63
+ */
9
64
  export interface JobDef<
10
65
  Name extends string = string,
11
66
  Payload extends StandardSchema = StandardSchema,
12
67
  Ctx = unknown,
13
68
  > {
69
+ /**
70
+ * Discriminator for job definitions.
71
+ */
14
72
  readonly kind: "job";
73
+ /**
74
+ * Stable job name used by dispatchers and provider adapters.
75
+ */
15
76
  readonly name: Name;
77
+ /**
78
+ * Standard Schema payload validator.
79
+ */
16
80
  readonly payload: Payload;
81
+ /**
82
+ * Optional human-readable description for docs and tooling.
83
+ */
17
84
  readonly description?: string;
85
+ /**
86
+ * Retry metadata for durable job providers.
87
+ */
18
88
  readonly retry?: JobRetryOptions;
89
+ /**
90
+ * Handle a parsed job payload.
91
+ */
19
92
  handle(
20
93
  args: JobHandleArgs<JobDef<Name, Payload, Ctx>, Ctx>,
21
94
  ): MaybePromise<void>;
22
95
  }
23
96
 
97
+ /**
98
+ * Infer the parsed payload type for a job definition.
99
+ */
24
100
  export type InferJobPayload<J extends JobDef> =
25
101
  J["payload"] extends StandardSchemaV1<unknown, infer Output> ? Output : never;
26
102
 
103
+ /**
104
+ * Arguments passed to a job handler.
105
+ */
27
106
  export interface JobHandleArgs<J extends JobDef, Ctx> {
107
+ /**
108
+ * Job definition being handled.
109
+ */
28
110
  job: J;
111
+ /**
112
+ * Parsed job payload.
113
+ */
29
114
  payload: InferJobPayload<J>;
115
+ /**
116
+ * Handler context.
117
+ */
30
118
  ctx: Ctx;
31
119
  }
32
120
 
121
+ /**
122
+ * Retry metadata that durable job providers can map to their own retry model.
123
+ */
33
124
  export interface JobRetryOptions {
34
125
  /**
35
- * Maximum number of retry attempts a durable job adapter should request.
36
- *
37
- * Providers may impose their own range limits. For example, the Inngest
38
- * 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.
39
132
  */
40
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;
41
158
  }
42
159
 
160
+ /**
161
+ * Options for declaring a typed job.
162
+ */
43
163
  export interface DefineJobOptions<
44
164
  Name extends string,
45
165
  Payload extends StandardSchema,
46
166
  Ctx,
47
167
  > {
168
+ /**
169
+ * Standard Schema payload validator.
170
+ */
48
171
  payload: Payload;
172
+ /**
173
+ * Optional human-readable description for docs and tooling.
174
+ */
49
175
  description?: string;
176
+ /**
177
+ * Retry metadata for durable job providers.
178
+ */
50
179
  retry?: JobRetryOptions;
180
+ /**
181
+ * Handle a parsed job payload.
182
+ */
51
183
  handle(
52
184
  args: JobHandleArgs<JobDef<Name, Payload, Ctx>, Ctx>,
53
185
  ): MaybePromise<void>;
54
186
  }
55
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
+
278
+ /**
279
+ * Options for the inline job dispatcher.
280
+ */
56
281
  export interface InlineJobDispatcherOptions<Ctx> {
282
+ /**
283
+ * Static job context or factory evaluated for each dispatched job.
284
+ */
57
285
  ctx?: Ctx | (() => MaybePromise<Ctx>);
286
+ /**
287
+ * Called when a dispatched inline job fails. When omitted, errors are
288
+ * rethrown to the caller.
289
+ */
58
290
  onError?: (error: unknown, job: JobDef<string, StandardSchema, Ctx>) => void;
59
291
  }
60
292
 
293
+ /**
294
+ * Local/test job dispatcher that executes job handlers inline.
295
+ */
61
296
  export interface InlineJobDispatcher<Ctx = unknown> {
297
+ /**
298
+ * Validate a payload and run the job handler immediately.
299
+ */
62
300
  dispatch<J extends JobDef<string, StandardSchema, Ctx>>(
63
301
  job: J,
64
302
  payload: InferJobPayload<J>,
65
303
  ): Promise<void>;
66
304
  }
67
305
 
306
+ /**
307
+ * Context-bound job helper factory.
308
+ */
68
309
  export interface JobHandlers<Ctx> {
310
+ /**
311
+ * Define a job with the bound context type.
312
+ */
69
313
  defineJob<Name extends string, Payload extends StandardSchema>(
70
314
  name: Name,
71
315
  options: DefineJobOptions<Name, Payload, Ctx>,
72
316
  ): JobDef<Name, Payload, Ctx>;
73
317
 
318
+ /**
319
+ * Create an inline dispatcher with the bound context type.
320
+ */
74
321
  createInlineJobDispatcher(
75
322
  options?: InlineJobDispatcherOptions<Ctx>,
76
323
  ): InlineJobDispatcher<Ctx>;
77
324
  }
78
325
 
326
+ /**
327
+ * Error thrown when job payload validation fails.
328
+ */
79
329
  export class JobValidationError extends Error {
330
+ /**
331
+ * Raw Standard Schema validation issues.
332
+ */
80
333
  readonly issues: readonly StandardSchemaV1.Issue[];
81
334
 
82
335
  constructor(args: {
@@ -112,6 +365,165 @@ function formatIssues(issues: readonly StandardSchemaV1.Issue[]): string {
112
365
  .join("; ");
113
366
  }
114
367
 
368
+ function assertPositiveInteger(name: string, value: number): void {
369
+ if (!Number.isInteger(value) || value <= 0) {
370
+ throw new Error(`${name} must be a positive integer`);
371
+ }
372
+ }
373
+
374
+ function assertPositiveNumber(name: string, value: number): void {
375
+ if (!Number.isFinite(value) || value <= 0) {
376
+ throw new Error(`${name} must be a positive number`);
377
+ }
378
+ }
379
+
380
+ function durationToMs(name: string, value: JobRetryDuration): number {
381
+ if (typeof value === "number") {
382
+ assertPositiveInteger(name, value);
383
+ return value;
384
+ }
385
+
386
+ const match = /^(\d+)(ms|s|m|h)$/.exec(value);
387
+ if (!match) {
388
+ throw new Error(
389
+ `${name} must be a positive millisecond value or duration string like "500ms", "30s", "5m", or "1h".`,
390
+ );
391
+ }
392
+
393
+ const amount = Number(match[1]);
394
+ assertPositiveInteger(name, amount);
395
+
396
+ switch (match[2]) {
397
+ case "ms":
398
+ return amount;
399
+ case "s":
400
+ return amount * 1000;
401
+ case "m":
402
+ return amount * 60_000;
403
+ case "h":
404
+ return amount * 3_600_000;
405
+ default:
406
+ throw new Error(`${name} has an unsupported duration unit.`);
407
+ }
408
+ }
409
+
410
+ function validateJobRetryOptions(options: JobRetryOptions): JobRetryOptions {
411
+ const strategy = options.strategy ?? "exponential";
412
+
413
+ if (!["none", "fixed", "exponential"].includes(strategy)) {
414
+ throw new Error("retry.strategy must be none, fixed, or exponential");
415
+ }
416
+
417
+ const attempts = options.attempts ?? (strategy === "none" ? 1 : undefined);
418
+ if (attempts === undefined) {
419
+ throw new Error("retry.attempts is required");
420
+ }
421
+ assertPositiveInteger("retry.attempts", attempts);
422
+
423
+ if (strategy === "none" && attempts !== 1) {
424
+ throw new Error("retry.none() must use exactly one attempt");
425
+ }
426
+
427
+ if (strategy === "fixed") {
428
+ if (options.delay === undefined) {
429
+ throw new Error("retry.delay is required for fixed retry policies");
430
+ }
431
+ durationToMs("retry.delay", options.delay);
432
+ }
433
+
434
+ if (strategy === "exponential") {
435
+ if (options.initialDelay !== undefined) {
436
+ durationToMs("retry.initialDelay", options.initialDelay);
437
+ }
438
+ if (options.maxDelay !== undefined) {
439
+ durationToMs("retry.maxDelay", options.maxDelay);
440
+ }
441
+ if (options.factor !== undefined) {
442
+ assertPositiveNumber("retry.factor", options.factor);
443
+ }
444
+ }
445
+
446
+ return {
447
+ ...options,
448
+ strategy,
449
+ attempts,
450
+ };
451
+ }
452
+
453
+ /**
454
+ * Return the maximum total attempts configured by a retry policy.
455
+ */
456
+ export function getJobRetryMaxAttempts(
457
+ options: JobRetryOptions | undefined,
458
+ ): number | undefined {
459
+ return options ? validateJobRetryOptions(options).attempts : undefined;
460
+ }
461
+
462
+ /**
463
+ * Return whether a failed job attempt should be retried.
464
+ */
465
+ export function shouldRetryJob(
466
+ options: JobRetryOptions | undefined,
467
+ args: JobRetryPredicateArgs,
468
+ ): boolean {
469
+ if (!options) return args.attempt < args.maxAttempts;
470
+
471
+ const retryOptions = validateJobRetryOptions(options);
472
+ const maxAttempts = Math.min(
473
+ args.maxAttempts,
474
+ retryOptions.attempts ?? args.maxAttempts,
475
+ );
476
+ if (retryOptions.strategy === "none") return false;
477
+ if (args.attempt >= maxAttempts) return false;
478
+
479
+ return retryOptions.retryIf?.({ ...args, maxAttempts }) ?? true;
480
+ }
481
+
482
+ /**
483
+ * Compute the next retry delay in milliseconds for a failed job attempt.
484
+ */
485
+ export function getJobRetryDelayMs(
486
+ options: JobRetryOptions | undefined,
487
+ args: Pick<JobRetryPredicateArgs, "attempt" | "error" | "jobName">,
488
+ ): number {
489
+ const retryOptions = options
490
+ ? validateJobRetryOptions(options)
491
+ : retry.exponential({ attempts: 3 });
492
+
493
+ let delayMs: number;
494
+ if (retryOptions.strategy === "fixed") {
495
+ delayMs = durationToMs("retry.delay", retryOptions.delay ?? "1s");
496
+ } else if (retryOptions.strategy === "none") {
497
+ delayMs = 0;
498
+ } else {
499
+ const initialDelayMs = durationToMs(
500
+ "retry.initialDelay",
501
+ retryOptions.initialDelay ?? "1s",
502
+ );
503
+ const maxDelayMs = durationToMs(
504
+ "retry.maxDelay",
505
+ retryOptions.maxDelay ?? "1m",
506
+ );
507
+ const factor = retryOptions.factor ?? 2;
508
+ delayMs = Math.min(
509
+ maxDelayMs,
510
+ initialDelayMs * factor ** Math.max(0, args.attempt - 1),
511
+ );
512
+ }
513
+
514
+ if (retryOptions.jitter && delayMs > 0) {
515
+ delayMs = Math.ceil(delayMs * (0.5 + Math.random()));
516
+ if (retryOptions.strategy === "exponential") {
517
+ delayMs = Math.min(
518
+ delayMs,
519
+ durationToMs("retry.maxDelay", retryOptions.maxDelay ?? "1m"),
520
+ );
521
+ }
522
+ }
523
+
524
+ return delayMs;
525
+ }
526
+
115
527
  async function parsePayload<Schema extends StandardSchemaV1>(
116
528
  schema: Schema,
117
529
  input: unknown,
@@ -143,6 +555,13 @@ async function resolveCtx<Ctx>(
143
555
  return ctx as Ctx;
144
556
  }
145
557
 
558
+ /**
559
+ * Define a typed job.
560
+ *
561
+ * Retry options are provider hints. Inline dispatchers validate payloads and
562
+ * call `handle(...)` immediately; durable providers may enqueue or schedule the
563
+ * job according to their own runtime.
564
+ */
146
565
  export function defineJob<
147
566
  Name extends string,
148
567
  Payload extends StandardSchema,
@@ -151,16 +570,23 @@ export function defineJob<
151
570
  name: Name,
152
571
  options: DefineJobOptions<Name, Payload, Ctx>,
153
572
  ): JobDef<Name, Payload, Ctx> {
573
+ const retryOptions = options.retry
574
+ ? validateJobRetryOptions(options.retry)
575
+ : undefined;
576
+
154
577
  return {
155
578
  kind: "job",
156
579
  name,
157
580
  payload: options.payload,
158
581
  description: options.description,
159
- retry: options.retry,
582
+ retry: retryOptions,
160
583
  handle: options.handle as JobDef<Name, Payload, Ctx>["handle"],
161
584
  };
162
585
  }
163
586
 
587
+ /**
588
+ * Validate and parse a job payload with the job's Standard Schema.
589
+ */
164
590
  export async function parseJobPayload<J extends JobDef>(
165
591
  job: J,
166
592
  payload: unknown,
@@ -170,6 +596,9 @@ export async function parseJobPayload<J extends JobDef>(
170
596
  })) as InferJobPayload<J>;
171
597
  }
172
598
 
599
+ /**
600
+ * Create a local/test dispatcher that runs job handlers inline.
601
+ */
173
602
  export function createInlineJobDispatcher<Ctx>(
174
603
  options: InlineJobDispatcherOptions<Ctx> = {},
175
604
  ): InlineJobDispatcher<Ctx> {
@@ -193,6 +622,9 @@ export function createInlineJobDispatcher<Ctx>(
193
622
  };
194
623
  }
195
624
 
625
+ /**
626
+ * Create job helper methods bound to an application context type.
627
+ */
196
628
  export function createJobHandlers<Ctx>(): JobHandlers<Ctx> {
197
629
  return {
198
630
  defineJob<Name extends string, Payload extends StandardSchema>(