nmtjs 0.16.1 → 0.17.0-beta.2

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 (224) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +41 -1
  3. package/dist/index.d.ts +22 -161
  4. package/dist/index.js +24 -53
  5. package/dist/index.js.map +1 -1
  6. package/package.json +11 -63
  7. package/src/index.ts +71 -91
  8. package/bin/cli.js +0 -2
  9. package/dist/cli.d.ts +0 -1
  10. package/dist/cli.js +0 -137
  11. package/dist/cli.js.map +0 -1
  12. package/dist/codegen.d.ts +0 -5
  13. package/dist/codegen.js +0 -18
  14. package/dist/codegen.js.map +0 -1
  15. package/dist/config.d.ts +0 -47
  16. package/dist/config.js +0 -16
  17. package/dist/config.js.map +0 -1
  18. package/dist/entrypoints/cli.d.ts +0 -1
  19. package/dist/entrypoints/cli.js +0 -12
  20. package/dist/entrypoints/cli.js.map +0 -1
  21. package/dist/entrypoints/main.d.ts +0 -5
  22. package/dist/entrypoints/main.js +0 -248
  23. package/dist/entrypoints/main.js.map +0 -1
  24. package/dist/entrypoints/thread.d.ts +0 -16
  25. package/dist/entrypoints/thread.js +0 -182
  26. package/dist/entrypoints/thread.js.map +0 -1
  27. package/dist/entrypoints/worker.d.ts +0 -9
  28. package/dist/entrypoints/worker.js +0 -143
  29. package/dist/entrypoints/worker.js.map +0 -1
  30. package/dist/resolver.d.ts +0 -2
  31. package/dist/resolver.js +0 -7
  32. package/dist/resolver.js.map +0 -1
  33. package/dist/runtime/application/index.d.ts +0 -1
  34. package/dist/runtime/application/index.js +0 -2
  35. package/dist/runtime/application/index.js.map +0 -1
  36. package/dist/runtime/constants.d.ts +0 -8
  37. package/dist/runtime/constants.js +0 -5
  38. package/dist/runtime/constants.js.map +0 -1
  39. package/dist/runtime/enums.d.ts +0 -10
  40. package/dist/runtime/enums.js +0 -13
  41. package/dist/runtime/enums.js.map +0 -1
  42. package/dist/runtime/hooks.d.ts +0 -1
  43. package/dist/runtime/hooks.js +0 -2
  44. package/dist/runtime/hooks.js.map +0 -1
  45. package/dist/runtime/index.d.ts +0 -28
  46. package/dist/runtime/index.js +0 -29
  47. package/dist/runtime/index.js.map +0 -1
  48. package/dist/runtime/injectables.d.ts +0 -69
  49. package/dist/runtime/injectables.js +0 -40
  50. package/dist/runtime/injectables.js.map +0 -1
  51. package/dist/runtime/jobs/job.d.ts +0 -146
  52. package/dist/runtime/jobs/job.js +0 -89
  53. package/dist/runtime/jobs/job.js.map +0 -1
  54. package/dist/runtime/jobs/manager.d.ts +0 -131
  55. package/dist/runtime/jobs/manager.js +0 -309
  56. package/dist/runtime/jobs/manager.js.map +0 -1
  57. package/dist/runtime/jobs/router.d.ts +0 -296
  58. package/dist/runtime/jobs/router.js +0 -458
  59. package/dist/runtime/jobs/router.js.map +0 -1
  60. package/dist/runtime/jobs/runner.d.ts +0 -103
  61. package/dist/runtime/jobs/runner.js +0 -425
  62. package/dist/runtime/jobs/runner.js.map +0 -1
  63. package/dist/runtime/jobs/step.d.ts +0 -26
  64. package/dist/runtime/jobs/step.js +0 -18
  65. package/dist/runtime/jobs/step.js.map +0 -1
  66. package/dist/runtime/jobs/types.d.ts +0 -86
  67. package/dist/runtime/jobs/types.js +0 -5
  68. package/dist/runtime/jobs/types.js.map +0 -1
  69. package/dist/runtime/metrics/metric.d.ts +0 -8
  70. package/dist/runtime/metrics/metric.js +0 -11
  71. package/dist/runtime/metrics/metric.js.map +0 -1
  72. package/dist/runtime/metrics/registry.d.ts +0 -3
  73. package/dist/runtime/metrics/registry.js +0 -5
  74. package/dist/runtime/metrics/registry.js.map +0 -1
  75. package/dist/runtime/metrics/server.d.ts +0 -7
  76. package/dist/runtime/metrics/server.js +0 -53
  77. package/dist/runtime/metrics/server.js.map +0 -1
  78. package/dist/runtime/plugin.d.ts +0 -2
  79. package/dist/runtime/plugin.js +0 -2
  80. package/dist/runtime/plugin.js.map +0 -1
  81. package/dist/runtime/runtime.d.ts +0 -27
  82. package/dist/runtime/runtime.js +0 -77
  83. package/dist/runtime/runtime.js.map +0 -1
  84. package/dist/runtime/scheduler/index.d.ts +0 -22
  85. package/dist/runtime/scheduler/index.js +0 -20
  86. package/dist/runtime/scheduler/index.js.map +0 -1
  87. package/dist/runtime/server/applications.d.ts +0 -91
  88. package/dist/runtime/server/applications.js +0 -171
  89. package/dist/runtime/server/applications.js.map +0 -1
  90. package/dist/runtime/server/config.d.ts +0 -148
  91. package/dist/runtime/server/config.js +0 -33
  92. package/dist/runtime/server/config.js.map +0 -1
  93. package/dist/runtime/server/environment.d.ts +0 -43
  94. package/dist/runtime/server/environment.js +0 -33
  95. package/dist/runtime/server/environment.js.map +0 -1
  96. package/dist/runtime/server/error-policy.d.ts +0 -62
  97. package/dist/runtime/server/error-policy.js +0 -73
  98. package/dist/runtime/server/error-policy.js.map +0 -1
  99. package/dist/runtime/server/hmr-coordinator.d.ts +0 -37
  100. package/dist/runtime/server/hmr-coordinator.js +0 -94
  101. package/dist/runtime/server/hmr-coordinator.js.map +0 -1
  102. package/dist/runtime/server/jobs.d.ts +0 -54
  103. package/dist/runtime/server/jobs.js +0 -176
  104. package/dist/runtime/server/jobs.js.map +0 -1
  105. package/dist/runtime/server/lifecycle.d.ts +0 -85
  106. package/dist/runtime/server/lifecycle.js +0 -177
  107. package/dist/runtime/server/lifecycle.js.map +0 -1
  108. package/dist/runtime/server/managed-worker.d.ts +0 -158
  109. package/dist/runtime/server/managed-worker.js +0 -420
  110. package/dist/runtime/server/managed-worker.js.map +0 -1
  111. package/dist/runtime/server/pool-manager.d.ts +0 -85
  112. package/dist/runtime/server/pool-manager.js +0 -129
  113. package/dist/runtime/server/pool-manager.js.map +0 -1
  114. package/dist/runtime/server/proxy.d.ts +0 -21
  115. package/dist/runtime/server/proxy.js +0 -76
  116. package/dist/runtime/server/proxy.js.map +0 -1
  117. package/dist/runtime/server/server.d.ts +0 -60
  118. package/dist/runtime/server/server.js +0 -163
  119. package/dist/runtime/server/server.js.map +0 -1
  120. package/dist/runtime/server/types.d.ts +0 -31
  121. package/dist/runtime/server/types.js +0 -2
  122. package/dist/runtime/server/types.js.map +0 -1
  123. package/dist/runtime/server/worker-pool.d.ts +0 -121
  124. package/dist/runtime/server/worker-pool.js +0 -171
  125. package/dist/runtime/server/worker-pool.js.map +0 -1
  126. package/dist/runtime/store/index.d.ts +0 -3
  127. package/dist/runtime/store/index.js +0 -23
  128. package/dist/runtime/store/index.js.map +0 -1
  129. package/dist/runtime/subscription/manager.d.ts +0 -49
  130. package/dist/runtime/subscription/manager.js +0 -170
  131. package/dist/runtime/subscription/manager.js.map +0 -1
  132. package/dist/runtime/subscription/redis.d.ts +0 -18
  133. package/dist/runtime/subscription/redis.js +0 -119
  134. package/dist/runtime/subscription/redis.js.map +0 -1
  135. package/dist/runtime/types.d.ts +0 -95
  136. package/dist/runtime/types.js +0 -2
  137. package/dist/runtime/types.js.map +0 -1
  138. package/dist/runtime/workers/application.d.ts +0 -29
  139. package/dist/runtime/workers/application.js +0 -77
  140. package/dist/runtime/workers/application.js.map +0 -1
  141. package/dist/runtime/workers/base.d.ts +0 -14
  142. package/dist/runtime/workers/base.js +0 -41
  143. package/dist/runtime/workers/base.js.map +0 -1
  144. package/dist/runtime/workers/cli.d.ts +0 -1
  145. package/dist/runtime/workers/cli.js +0 -2
  146. package/dist/runtime/workers/cli.js.map +0 -1
  147. package/dist/runtime/workers/job.d.ts +0 -20
  148. package/dist/runtime/workers/job.js +0 -170
  149. package/dist/runtime/workers/job.js.map +0 -1
  150. package/dist/vite/builder.d.ts +0 -5
  151. package/dist/vite/builder.js +0 -79
  152. package/dist/vite/builder.js.map +0 -1
  153. package/dist/vite/config.d.ts +0 -28
  154. package/dist/vite/config.js +0 -22
  155. package/dist/vite/config.js.map +0 -1
  156. package/dist/vite/plugins.d.ts +0 -2
  157. package/dist/vite/plugins.js +0 -43
  158. package/dist/vite/plugins.js.map +0 -1
  159. package/dist/vite/runners/worker.d.ts +0 -3
  160. package/dist/vite/runners/worker.js +0 -34
  161. package/dist/vite/runners/worker.js.map +0 -1
  162. package/dist/vite/server.d.ts +0 -3
  163. package/dist/vite/server.js +0 -36
  164. package/dist/vite/server.js.map +0 -1
  165. package/dist/vite/servers/main.d.ts +0 -8
  166. package/dist/vite/servers/main.js +0 -24
  167. package/dist/vite/servers/main.js.map +0 -1
  168. package/dist/vite/servers/worker.d.ts +0 -13
  169. package/dist/vite/servers/worker.js +0 -124
  170. package/dist/vite/servers/worker.js.map +0 -1
  171. package/src/cli.ts +0 -162
  172. package/src/codegen.ts +0 -30
  173. package/src/config.ts +0 -69
  174. package/src/entrypoints/cli.ts +0 -13
  175. package/src/entrypoints/main.ts +0 -317
  176. package/src/entrypoints/thread.ts +0 -223
  177. package/src/entrypoints/worker.ts +0 -174
  178. package/src/resolver.ts +0 -7
  179. package/src/runtime/application/index.ts +0 -1
  180. package/src/runtime/constants.ts +0 -13
  181. package/src/runtime/enums.ts +0 -12
  182. package/src/runtime/hooks.ts +0 -1
  183. package/src/runtime/index.ts +0 -29
  184. package/src/runtime/injectables.ts +0 -101
  185. package/src/runtime/jobs/job.ts +0 -467
  186. package/src/runtime/jobs/manager.ts +0 -454
  187. package/src/runtime/jobs/router.ts +0 -967
  188. package/src/runtime/jobs/runner.ts +0 -583
  189. package/src/runtime/jobs/step.ts +0 -66
  190. package/src/runtime/jobs/types.ts +0 -116
  191. package/src/runtime/metrics/metric.ts +0 -18
  192. package/src/runtime/metrics/registry.ts +0 -11
  193. package/src/runtime/metrics/server.ts +0 -69
  194. package/src/runtime/plugin.ts +0 -2
  195. package/src/runtime/runtime.ts +0 -103
  196. package/src/runtime/scheduler/index.ts +0 -39
  197. package/src/runtime/server/applications.ts +0 -264
  198. package/src/runtime/server/config.ts +0 -199
  199. package/src/runtime/server/environment.ts +0 -71
  200. package/src/runtime/server/error-policy.ts +0 -132
  201. package/src/runtime/server/hmr-coordinator.ts +0 -116
  202. package/src/runtime/server/jobs.ts +0 -266
  203. package/src/runtime/server/lifecycle.ts +0 -234
  204. package/src/runtime/server/managed-worker.ts +0 -546
  205. package/src/runtime/server/pool-manager.ts +0 -187
  206. package/src/runtime/server/proxy.ts +0 -119
  207. package/src/runtime/server/server.ts +0 -218
  208. package/src/runtime/server/types.ts +0 -32
  209. package/src/runtime/server/worker-pool.ts +0 -275
  210. package/src/runtime/store/index.ts +0 -30
  211. package/src/runtime/subscription/manager.ts +0 -307
  212. package/src/runtime/subscription/redis.ts +0 -159
  213. package/src/runtime/types.ts +0 -84
  214. package/src/runtime/workers/application.ts +0 -133
  215. package/src/runtime/workers/base.ts +0 -65
  216. package/src/runtime/workers/cli.ts +0 -0
  217. package/src/runtime/workers/job.ts +0 -142
  218. package/src/vite/builder.ts +0 -94
  219. package/src/vite/config.ts +0 -45
  220. package/src/vite/plugins.ts +0 -53
  221. package/src/vite/runners/worker.ts +0 -55
  222. package/src/vite/server.ts +0 -55
  223. package/src/vite/servers/main.ts +0 -41
  224. package/src/vite/servers/worker.ts +0 -177
@@ -1,967 +0,0 @@
1
- import type {
2
- AnyGuard,
3
- AnyMiddleware,
4
- AnyProcedure,
5
- AnyRouter,
6
- AnyRouterMetaBinding,
7
- ProcedureMetaBinding,
8
- Router,
9
- } from '@nmtjs/application'
10
- import type { MaybePromise } from '@nmtjs/common'
11
- import type { TProcedureContract, TRouterContract } from '@nmtjs/contract'
12
- import type { Dependencies, DependencyContext } from '@nmtjs/core'
13
- import type { NullableType, OptionalType } from '@nmtjs/type'
14
- import type { NeverType } from '@nmtjs/type/never'
15
- import { createProcedure, createRouter } from '@nmtjs/application'
16
- import { CoreInjectables } from '@nmtjs/core'
17
- import { t } from '@nmtjs/type'
18
-
19
- import type { AnyJob } from './job.ts'
20
- import type { JobStatus } from './types.ts'
21
- import { jobManager } from '../injectables.ts'
22
-
23
- // ============================================================================
24
- // Configuration Types
25
- // ============================================================================
26
-
27
- type ListOperationInput = { page?: number; limit?: number; status?: string[] }
28
-
29
- type IdOperationInput = { id: string }
30
-
31
- type RetryOperationInput = { id: string; clearState?: boolean }
32
-
33
- type AddOperationInput<T extends AnyJob> = {
34
- data: T['_']['input']
35
- jobId?: string
36
- priority?: number
37
- delay?: number
38
- }
39
-
40
- /** Base operation config shared by all operations */
41
- export type BaseOperationConfig<
42
- Input = unknown,
43
- Deps extends Dependencies = {},
44
- > = {
45
- dependencies?: Deps
46
- guards?: AnyGuard[]
47
- middlewares?: AnyMiddleware[]
48
- meta?: ProcedureMetaBinding<Input>[]
49
- timeout?: number
50
- }
51
-
52
- /** List operation config (read-only, no hooks) */
53
- export type ListOperationConfig<Deps extends Dependencies = {}> =
54
- BaseOperationConfig<ListOperationInput, Deps>
55
-
56
- /** Get operation config (read-only, no hooks) */
57
- export type GetOperationConfig<Deps extends Dependencies = {}> =
58
- BaseOperationConfig<IdOperationInput, Deps>
59
-
60
- /** Info operation config (read-only, no hooks) */
61
- export type InfoOperationConfig<Deps extends Dependencies = {}> =
62
- BaseOperationConfig<unknown, Deps>
63
-
64
- /** Add queue options */
65
- export type AddQueueOptions = {
66
- priority?: number
67
- attempts?: number
68
- delay?: number
69
- }
70
-
71
- /** Add operation config with before/after hooks */
72
- export type AddOperationConfig<
73
- T extends AnyJob = AnyJob,
74
- Deps extends Dependencies = {},
75
- > = BaseOperationConfig<AddOperationInput<T>, Deps> & {
76
- beforeAdd?: (
77
- ctx: DependencyContext<Deps>,
78
- input: T['_']['input'],
79
- ) => MaybePromise<T['_']['input']>
80
- afterAdd?: (
81
- ctx: DependencyContext<Deps>,
82
- result: { id: string; name: string },
83
- input: T['_']['input'],
84
- ) => MaybePromise<void>
85
- options?: AddQueueOptions
86
- }
87
-
88
- /** Remove operation config with before/after hooks */
89
- export type RemoveOperationConfig<Deps extends Dependencies = {}> =
90
- BaseOperationConfig<IdOperationInput, Deps> & {
91
- beforeRemove?: (
92
- ctx: DependencyContext<Deps>,
93
- params: { id: string },
94
- ) => MaybePromise<void>
95
- afterRemove?: (
96
- ctx: DependencyContext<Deps>,
97
- params: { id: string },
98
- ) => MaybePromise<void>
99
- }
100
-
101
- /** Retry operation config with before/after hooks */
102
- export type RetryOperationConfig<Deps extends Dependencies = {}> =
103
- BaseOperationConfig<RetryOperationInput, Deps> & {
104
- clearState?: boolean
105
- beforeRetry?: (
106
- ctx: DependencyContext<Deps>,
107
- params: { id: string; clearState?: boolean },
108
- ) => MaybePromise<void>
109
- afterRetry?: (
110
- ctx: DependencyContext<Deps>,
111
- params: { id: string; clearState?: boolean },
112
- ) => MaybePromise<void>
113
- }
114
-
115
- /** Cancel operation config with before/after hooks */
116
- export type CancelOperationConfig<Deps extends Dependencies = {}> =
117
- BaseOperationConfig<IdOperationInput, Deps> & {
118
- beforeCancel?: (
119
- ctx: DependencyContext<Deps>,
120
- params: { id: string },
121
- ) => MaybePromise<void>
122
- afterCancel?: (
123
- ctx: DependencyContext<Deps>,
124
- params: { id: string },
125
- ) => MaybePromise<void>
126
- }
127
-
128
- /** All operations for a job (false = disabled) */
129
- export type JobOperations<T extends AnyJob = AnyJob> = {
130
- info?: InfoOperationConfig<any> | false
131
- list?: ListOperationConfig<any> | false
132
- get?: GetOperationConfig<any> | false
133
- add?: AddOperationConfig<T, any> | false
134
- retry?: RetryOperationConfig<any> | false
135
- cancel?: CancelOperationConfig<any> | false
136
- remove?: RemoveOperationConfig<any> | false
137
- }
138
-
139
- /** Default operations config */
140
- export type DefaultOperations = {
141
- info?: InfoOperationConfig<any> | false
142
- list?: ListOperationConfig<any> | false
143
- get?: GetOperationConfig<any> | false
144
- add?: AddOperationConfig<AnyJob, any> | false
145
- retry?: RetryOperationConfig<any> | false
146
- cancel?: CancelOperationConfig<any> | false
147
- remove?: RemoveOperationConfig<any> | false
148
- }
149
-
150
- /** Options for createJobsRouter */
151
- export type CreateJobsRouterOptions<Jobs extends Record<string, AnyJob>> = {
152
- jobs: Jobs
153
- guards?: AnyGuard[]
154
- middlewares?: AnyMiddleware[]
155
- meta?: JobsRouterMetaBinding[]
156
- defaults?: DefaultOperations
157
- overrides?: {
158
- [K in keyof Jobs]?: JobOperations<Jobs[K]>
159
- }
160
- }
161
-
162
- // ============================================================================
163
- // Router Contract Types
164
- // ============================================================================
165
-
166
- /** Type-level schema for JobProgressCheckpoint - typed per job */
167
- type JobProgressCheckpointSchemaType<T extends AnyJob> = t.ObjectType<{
168
- stepIndex: t.NumberType
169
- stepLabel: OptionalType<t.StringType>
170
- result: t.RecordType<t.StringType, t.AnyType>
171
- stepResults: t.ArrayType<
172
- NullableType<
173
- t.ObjectType<{
174
- data: NullableType<t.RecordType<t.StringType, t.AnyType>>
175
- duration: t.NumberType
176
- }>
177
- >
178
- >
179
- progress: T['progress']
180
- }>
181
-
182
- /** Type-level representation of createJobItemSchema output */
183
- type JobItemSchemaType<T extends AnyJob> = t.ObjectType<{
184
- id: t.StringType
185
- name: t.StringType
186
- queue: t.StringType
187
- data: T['input']
188
- output: OptionalType<NullableType<T['output']>>
189
- status: t.StringType
190
- priority: OptionalType<t.NumberType>
191
- progress: OptionalType<JobProgressCheckpointSchemaType<T>>
192
- attempts: t.NumberType
193
- startedAt: OptionalType<t.NumberType>
194
- completedAt: OptionalType<t.NumberType>
195
- error: OptionalType<t.StringType>
196
- stacktrace: NullableType<t.ArrayType<t.StringType>>
197
- }>
198
-
199
- /** Type-level representation of createListOutputSchema output */
200
- type ListOutputSchemaType<T extends AnyJob> = t.ObjectType<{
201
- items: t.ArrayType<JobItemSchemaType<T>>
202
- page: t.NumberType
203
- limit: t.NumberType
204
- pages: t.NumberType
205
- total: t.NumberType
206
- }>
207
-
208
- /** Type-level representation of createGetOutputSchema output */
209
- type GetOutputSchemaType<T extends AnyJob> = NullableType<JobItemSchemaType<T>>
210
-
211
- /** Type-level representation of infoOutputSchema */
212
- type InfoOutputSchemaType = typeof infoOutputSchema
213
-
214
- /** Type-level representation of createAddInputSchema output */
215
- type AddInputSchemaType<T extends AnyJob> = t.ObjectType<{
216
- data: T['input']
217
- jobId: OptionalType<t.StringType>
218
- priority: OptionalType<t.NumberType>
219
- delay: OptionalType<t.NumberType>
220
- }>
221
-
222
- /** Operations contract for a single job - now properly typed per job */
223
- type JobOperationsProcedures<T extends AnyJob> = {
224
- info: TProcedureContract<NeverType, InfoOutputSchemaType>
225
- list: TProcedureContract<typeof listInputSchema, ListOutputSchemaType<T>>
226
- get: TProcedureContract<typeof getInputSchema, GetOutputSchemaType<T>>
227
- add: TProcedureContract<AddInputSchemaType<T>, typeof addOutputSchema>
228
- retry: TProcedureContract<typeof retryInputSchema, NeverType>
229
- cancel: TProcedureContract<typeof idInputSchema, NeverType>
230
- remove: TProcedureContract<typeof idInputSchema, NeverType>
231
- }
232
-
233
- /** Router contract for a single job's operations */
234
- type JobRouterContract<T extends AnyJob> = TRouterContract<
235
- JobOperationsProcedures<T>
236
- >
237
-
238
- /** Full jobs router contract mapping job names to their operation routers */
239
- type JobsRouterContract<Jobs extends Record<string, AnyJob>> = TRouterContract<{
240
- [K in keyof Jobs]: JobRouterContract<Jobs[K]>
241
- }>
242
-
243
- export type JobsRouterMetaBinding = AnyRouterMetaBinding
244
-
245
- /** Return type for createJobsRouter */
246
- export type JobsRouter<Jobs extends Record<string, AnyJob>> = Router<
247
- JobsRouterContract<Jobs>
248
- >
249
-
250
- // ============================================================================
251
- // Schemas
252
- // ============================================================================
253
-
254
- /** Input schema for list operation */
255
- const listInputSchema = t.object({
256
- page: t.number().optional(),
257
- limit: t.number().optional(),
258
- status: t.array(t.string()).optional(),
259
- })
260
-
261
- /** Input schema for get operation */
262
- const getInputSchema = t.object({ id: t.string() })
263
-
264
- /** Output schema for add operation */
265
- const addOutputSchema = t.object({ id: t.string(), name: t.string() })
266
-
267
- /** Input schema for retry operation */
268
- const retryInputSchema = t.object({
269
- id: t.string(),
270
- clearState: t.boolean().optional(),
271
- })
272
-
273
- /** Input schema for cancel/remove operations */
274
- const idInputSchema = t.object({ id: t.string() })
275
-
276
- /** Output schema for info operation */
277
- const infoOutputSchema = t.object({
278
- name: t.string(),
279
- steps: t.array(
280
- t.object({
281
- label: t.string().optional(),
282
- conditional: t.boolean(),
283
- parallel: t.boolean(),
284
- }),
285
- ),
286
- })
287
-
288
- /** Schema for step result entry */
289
- const stepResultEntrySchema = t
290
- .object({
291
- data: t.record(t.string(), t.any()).nullable(),
292
- startedAt: t.number(),
293
- completedAt: t.number(),
294
- duration: t.number(),
295
- })
296
- .nullable()
297
-
298
- /** Creates JobProgressCheckpoint schema for a specific job */
299
- function createJobProgressCheckpointSchema<T extends AnyJob>(
300
- job: T,
301
- ): JobProgressCheckpointSchemaType<T> {
302
- return t.object({
303
- stepIndex: t.number(),
304
- stepLabel: t.string().optional(),
305
- result: t.record(t.string(), t.any()),
306
- stepResults: t.array(stepResultEntrySchema),
307
- progress: job.progress,
308
- })
309
- }
310
-
311
- /** JobItem schema for list/get responses - typed per job */
312
- function createJobItemSchema<T extends AnyJob>(job: T): JobItemSchemaType<T> {
313
- return t.object({
314
- id: t.string(),
315
- name: t.string(),
316
- queue: t.string(),
317
- data: job.input,
318
- output: job.output.nullish(),
319
- status: t.string(),
320
- priority: t.number().optional(),
321
- progress: createJobProgressCheckpointSchema(job).optional(),
322
- attempts: t.number(),
323
- startedAt: t.number().optional(),
324
- completedAt: t.number().optional(),
325
- error: t.string().optional(),
326
- stacktrace: t.array(t.string()).nullable(),
327
- })
328
- }
329
-
330
- /** Creates list output schema for a specific job */
331
- function createListOutputSchema<T extends AnyJob>(
332
- job: T,
333
- ): ListOutputSchemaType<T> {
334
- return t.object({
335
- items: t.array(createJobItemSchema(job)),
336
- page: t.number(),
337
- limit: t.number(),
338
- pages: t.number(),
339
- total: t.number(),
340
- })
341
- }
342
-
343
- /** Creates get output schema for a specific job */
344
- function createGetOutputSchema<T extends AnyJob>(
345
- job: T,
346
- ): GetOutputSchemaType<T> {
347
- return createJobItemSchema(job).nullable()
348
- }
349
-
350
- /** Creates add input schema for a specific job */
351
- function createAddInputSchema<T extends AnyJob>(job: T): AddInputSchemaType<T> {
352
- return t.object({
353
- data: job.input,
354
- jobId: t.string().optional(),
355
- priority: t.number().optional(),
356
- delay: t.number().optional(),
357
- })
358
- }
359
-
360
- // ============================================================================
361
- // Helpers
362
- // ============================================================================
363
-
364
- /**
365
- * Helper function to create a type-safe operation config with dependencies.
366
- * Use this when you need custom dependencies for hooks.
367
- *
368
- * @example
369
- * ```ts
370
- * add: jobOperation({
371
- * dependencies: { userService, auditLog },
372
- * beforeAdd: async (ctx, input) => {
373
- * return { ...input, createdBy: ctx.userService.getCurrentId() }
374
- * },
375
- * })
376
- * ```
377
- */
378
- export function createJobRouterOperation<
379
- Deps extends Dependencies,
380
- T extends AnyJob = AnyJob,
381
- >(
382
- config: BaseOperationConfig<Deps> & {
383
- // Add hooks
384
- beforeAdd?: (
385
- ctx: DependencyContext<Deps>,
386
- input: T['_']['input'],
387
- ) => MaybePromise<T['_']['input']>
388
- afterAdd?: (
389
- ctx: DependencyContext<Deps>,
390
- result: { id: string; name: string },
391
- input: T['_']['input'],
392
- ) => MaybePromise<void>
393
- options?: AddQueueOptions
394
-
395
- // Remove hooks
396
- beforeRemove?: (
397
- ctx: DependencyContext<Deps>,
398
- params: { id: string },
399
- ) => MaybePromise<void>
400
- afterRemove?: (
401
- ctx: DependencyContext<Deps>,
402
- params: { id: string },
403
- ) => MaybePromise<void>
404
-
405
- // Retry hooks
406
- clearState?: boolean
407
- beforeRetry?: (
408
- ctx: DependencyContext<Deps>,
409
- params: { id: string; clearState?: boolean },
410
- ) => MaybePromise<void>
411
- afterRetry?: (
412
- ctx: DependencyContext<Deps>,
413
- params: { id: string; clearState?: boolean },
414
- ) => MaybePromise<void>
415
-
416
- // Cancel hooks
417
- beforeCancel?: (
418
- ctx: DependencyContext<Deps>,
419
- params: { id: string },
420
- ) => MaybePromise<void>
421
- afterCancel?: (
422
- ctx: DependencyContext<Deps>,
423
- params: { id: string },
424
- ) => MaybePromise<void>
425
- },
426
- ): typeof config {
427
- return config
428
- }
429
-
430
- // ============================================================================
431
- // Implementation
432
- // ============================================================================
433
-
434
- type JobManagerDeps = {
435
- jobManager: typeof jobManager
436
- logger: typeof CoreInjectables.logger
437
- }
438
-
439
- function createInfoProcedure(
440
- job: AnyJob,
441
- config: InfoOperationConfig<any> = {},
442
- shared: { guards?: AnyGuard[]; middlewares?: AnyMiddleware[] },
443
- ): AnyProcedure {
444
- const allGuards = [...(shared.guards ?? []), ...(config.guards ?? [])]
445
- const allMiddlewares = [
446
- ...(shared.middlewares ?? []),
447
- ...(config.middlewares ?? []),
448
- ]
449
-
450
- const deps: JobManagerDeps = { jobManager, logger: CoreInjectables.logger }
451
-
452
- return createProcedure({
453
- output: infoOutputSchema,
454
- dependencies: { ...deps, ...(config.dependencies ?? {}) },
455
- guards: allGuards,
456
- middlewares: allMiddlewares,
457
- meta: config.meta,
458
- timeout: config.timeout,
459
- handler: (ctx: DependencyContext<JobManagerDeps>) => {
460
- ctx.logger.trace({ jobName: job.options.name }, 'Getting job info')
461
- return ctx.jobManager.getInfo(job)
462
- },
463
- })
464
- }
465
-
466
- function createListProcedure(
467
- job: AnyJob,
468
- config: ListOperationConfig<any> = {},
469
- shared: { guards?: AnyGuard[]; middlewares?: AnyMiddleware[] },
470
- ): AnyProcedure {
471
- const allGuards = [...(shared.guards ?? []), ...(config.guards ?? [])]
472
- const allMiddlewares = [
473
- ...(shared.middlewares ?? []),
474
- ...(config.middlewares ?? []),
475
- ]
476
-
477
- const deps: JobManagerDeps = { jobManager, logger: CoreInjectables.logger }
478
-
479
- return createProcedure({
480
- input: listInputSchema,
481
- output: createListOutputSchema(job),
482
- dependencies: { ...deps, ...(config.dependencies ?? {}) },
483
- guards: allGuards,
484
- middlewares: allMiddlewares,
485
- meta: config.meta,
486
- timeout: config.timeout,
487
- handler: async (ctx: DependencyContext<JobManagerDeps>, input) => {
488
- ctx.logger.trace(
489
- {
490
- jobName: job.options.name,
491
- page: input.page,
492
- limit: input.limit,
493
- status: input.status,
494
- },
495
- 'Listing jobs',
496
- )
497
- const result = await ctx.jobManager.list(job, {
498
- page: input.page,
499
- limit: input.limit,
500
- status: input.status as JobStatus[],
501
- })
502
- ctx.logger.trace(
503
- { jobName: job.options.name, total: result.total, pages: result.pages },
504
- 'Jobs listed',
505
- )
506
- return result
507
- },
508
- })
509
- }
510
-
511
- function createGetProcedure(
512
- job: AnyJob,
513
- config: GetOperationConfig<any> = {},
514
- shared: { guards?: AnyGuard[]; middlewares?: AnyMiddleware[] },
515
- ): AnyProcedure {
516
- const allGuards = [...(shared.guards ?? []), ...(config.guards ?? [])]
517
- const allMiddlewares = [
518
- ...(shared.middlewares ?? []),
519
- ...(config.middlewares ?? []),
520
- ]
521
-
522
- const deps: JobManagerDeps = { jobManager, logger: CoreInjectables.logger }
523
-
524
- return createProcedure({
525
- input: getInputSchema,
526
- output: createGetOutputSchema(job),
527
- dependencies: { ...deps, ...(config.dependencies ?? {}) },
528
- guards: allGuards,
529
- middlewares: allMiddlewares,
530
- meta: config.meta,
531
- timeout: config.timeout,
532
- handler: async (ctx: DependencyContext<JobManagerDeps>, input) => {
533
- ctx.logger.trace(
534
- { jobName: job.options.name, id: input.id },
535
- 'Getting job',
536
- )
537
- const result = await ctx.jobManager.get(job, input.id)
538
- ctx.logger.trace(
539
- { jobName: job.options.name, id: input.id, found: result !== null },
540
- 'Job retrieved',
541
- )
542
- return result
543
- },
544
- })
545
- }
546
-
547
- function createAddProcedure(
548
- job: AnyJob,
549
- config: AddOperationConfig<AnyJob, any> = {},
550
- shared: { guards?: AnyGuard[]; middlewares?: AnyMiddleware[] },
551
- ): AnyProcedure {
552
- const allGuards = [...(shared.guards ?? []), ...(config.guards ?? [])]
553
- const allMiddlewares = [
554
- ...(shared.middlewares ?? []),
555
- ...(config.middlewares ?? []),
556
- ]
557
-
558
- const deps: JobManagerDeps = { jobManager, logger: CoreInjectables.logger }
559
-
560
- return createProcedure({
561
- input: createAddInputSchema(job),
562
- output: addOutputSchema,
563
- dependencies: { ...deps, ...(config.dependencies ?? {}) },
564
- guards: allGuards,
565
- middlewares: allMiddlewares,
566
- meta: config.meta,
567
- timeout: config.timeout,
568
- handler: async (ctx: DependencyContext<JobManagerDeps>, input) => {
569
- let jobData = input.data
570
-
571
- ctx.logger.debug(
572
- {
573
- jobName: job.options.name,
574
- jobId: input.jobId,
575
- priority: input.priority,
576
- },
577
- 'Adding job',
578
- )
579
-
580
- // Call beforeAdd hook if provided
581
- if (config.beforeAdd) {
582
- ctx.logger.trace(
583
- { jobName: job.options.name },
584
- 'Running beforeAdd hook',
585
- )
586
- jobData = await config.beforeAdd(ctx as any, jobData)
587
- }
588
-
589
- const queueResult = await ctx.jobManager.add(job, jobData, {
590
- jobId: input.jobId,
591
- priority: input.priority ?? config.options?.priority,
592
- delay: input.delay ?? config.options?.delay,
593
- attempts: config.options?.attempts,
594
- })
595
-
596
- const result = { id: queueResult.id!, name: queueResult.name }
597
-
598
- ctx.logger.info({ jobName: job.options.name, id: result.id }, 'Job added')
599
-
600
- // Call afterAdd hook if provided
601
- if (config.afterAdd) {
602
- ctx.logger.trace(
603
- { jobName: job.options.name, id: result.id },
604
- 'Running afterAdd hook',
605
- )
606
- await config.afterAdd(ctx as any, result, jobData)
607
- }
608
-
609
- return result
610
- },
611
- })
612
- }
613
-
614
- function createRetryProcedure(
615
- job: AnyJob,
616
- config: RetryOperationConfig<any> = {},
617
- shared: { guards?: AnyGuard[]; middlewares?: AnyMiddleware[] },
618
- ): AnyProcedure {
619
- const allGuards = [...(shared.guards ?? []), ...(config.guards ?? [])]
620
- const allMiddlewares = [
621
- ...(shared.middlewares ?? []),
622
- ...(config.middlewares ?? []),
623
- ]
624
-
625
- const deps: JobManagerDeps = { jobManager, logger: CoreInjectables.logger }
626
-
627
- return createProcedure({
628
- input: retryInputSchema,
629
- dependencies: { ...deps, ...(config.dependencies ?? {}) },
630
- guards: allGuards,
631
- middlewares: allMiddlewares,
632
- meta: config.meta,
633
- timeout: config.timeout,
634
- handler: async (ctx: DependencyContext<JobManagerDeps>, input) => {
635
- const clearState = input.clearState ?? config.clearState
636
-
637
- ctx.logger.debug(
638
- { jobName: job.options.name, id: input.id, clearState },
639
- 'Retrying job',
640
- )
641
-
642
- // Call beforeRetry hook if provided
643
- if (config.beforeRetry) {
644
- ctx.logger.trace(
645
- { jobName: job.options.name, id: input.id },
646
- 'Running beforeRetry hook',
647
- )
648
- await config.beforeRetry(ctx as any, { id: input.id, clearState })
649
- }
650
-
651
- await ctx.jobManager.retry(job, input.id, { clearState })
652
-
653
- ctx.logger.trace(
654
- { jobName: job.options.name, id: input.id },
655
- 'Job retried',
656
- )
657
-
658
- // Call afterRetry hook if provided
659
- if (config.afterRetry) {
660
- ctx.logger.trace(
661
- { jobName: job.options.name, id: input.id },
662
- 'Running afterRetry hook',
663
- )
664
- await config.afterRetry(ctx as any, { id: input.id, clearState })
665
- }
666
- },
667
- })
668
- }
669
-
670
- function createCancelProcedure(
671
- job: AnyJob,
672
- config: CancelOperationConfig<any> = {},
673
- shared: { guards?: AnyGuard[]; middlewares?: AnyMiddleware[] },
674
- ): AnyProcedure {
675
- const allGuards = [...(shared.guards ?? []), ...(config.guards ?? [])]
676
- const allMiddlewares = [
677
- ...(shared.middlewares ?? []),
678
- ...(config.middlewares ?? []),
679
- ]
680
-
681
- const deps: JobManagerDeps = { jobManager, logger: CoreInjectables.logger }
682
-
683
- return createProcedure({
684
- input: idInputSchema,
685
- dependencies: { ...deps, ...(config.dependencies ?? {}) },
686
- guards: allGuards,
687
- middlewares: allMiddlewares,
688
- meta: config.meta,
689
- timeout: config.timeout,
690
- handler: async (ctx: DependencyContext<JobManagerDeps>, input) => {
691
- ctx.logger.debug(
692
- { jobName: job.options.name, id: input.id },
693
- 'Canceling job',
694
- )
695
-
696
- // Call beforeCancel hook if provided
697
- if (config.beforeCancel) {
698
- ctx.logger.trace(
699
- { jobName: job.options.name, id: input.id },
700
- 'Running beforeCancel hook',
701
- )
702
- await config.beforeCancel(ctx as any, { id: input.id })
703
- }
704
-
705
- await ctx.jobManager.cancel(job, input.id)
706
-
707
- ctx.logger.info(
708
- { jobName: job.options.name, id: input.id },
709
- 'Job canceled',
710
- )
711
-
712
- // Call afterCancel hook if provided
713
- if (config.afterCancel) {
714
- ctx.logger.trace(
715
- { jobName: job.options.name, id: input.id },
716
- 'Running afterCancel hook',
717
- )
718
- await config.afterCancel(ctx as any, { id: input.id })
719
- }
720
- },
721
- })
722
- }
723
-
724
- function createRemoveProcedure(
725
- job: AnyJob,
726
- config: RemoveOperationConfig<any> = {},
727
- shared: { guards?: AnyGuard[]; middlewares?: AnyMiddleware[] },
728
- ): AnyProcedure {
729
- const allGuards = [...(shared.guards ?? []), ...(config.guards ?? [])]
730
- const allMiddlewares = [
731
- ...(shared.middlewares ?? []),
732
- ...(config.middlewares ?? []),
733
- ]
734
-
735
- const deps: JobManagerDeps = { jobManager, logger: CoreInjectables.logger }
736
-
737
- return createProcedure({
738
- input: idInputSchema,
739
- dependencies: { ...deps, ...(config.dependencies ?? {}) },
740
- guards: allGuards,
741
- middlewares: allMiddlewares,
742
- meta: config.meta,
743
- timeout: config.timeout,
744
- handler: async (ctx: DependencyContext<JobManagerDeps>, input) => {
745
- ctx.logger.trace(
746
- { jobName: job.options.name, id: input.id },
747
- 'Removing job',
748
- )
749
-
750
- // Call beforeRemove hook if provided
751
- if (config.beforeRemove) {
752
- ctx.logger.trace(
753
- { jobName: job.options.name, id: input.id },
754
- 'Running beforeRemove hook',
755
- )
756
- await config.beforeRemove(ctx as any, { id: input.id })
757
- }
758
-
759
- await ctx.jobManager.remove(job, input.id)
760
-
761
- ctx.logger.info(
762
- { jobName: job.options.name, id: input.id },
763
- 'Job removed',
764
- )
765
-
766
- // Call afterRemove hook if provided
767
- if (config.afterRemove) {
768
- ctx.logger.trace(
769
- { jobName: job.options.name, id: input.id },
770
- 'Running afterRemove hook',
771
- )
772
- await config.afterRemove(ctx as any, { id: input.id })
773
- }
774
- },
775
- })
776
- }
777
-
778
- // ============================================================================
779
- // Main router factory
780
- // ============================================================================
781
-
782
- /**
783
- * Merge default operations with job-specific overrides
784
- */
785
- function mergeOperations(
786
- defaults: DefaultOperations = {},
787
- overrides: JobOperations = {},
788
- ): JobOperations {
789
- const result: JobOperations = {}
790
-
791
- const ops = [
792
- 'info',
793
- 'list',
794
- 'get',
795
- 'add',
796
- 'retry',
797
- 'cancel',
798
- 'remove',
799
- ] as const
800
-
801
- for (const op of ops) {
802
- const override = overrides[op]
803
- const defaultOp = defaults[op]
804
-
805
- if (override === false) {
806
- result[op] = false
807
- } else if (override !== undefined) {
808
- // Override provided - use it (merged with default base config if both are objects)
809
- if (
810
- defaultOp &&
811
- (defaultOp as unknown) !== false &&
812
- typeof override === 'object'
813
- ) {
814
- result[op] = {
815
- ...(defaultOp as object),
816
- ...(override as object),
817
- guards: [
818
- ...((defaultOp as any).guards ?? []),
819
- ...((override as any).guards ?? []),
820
- ],
821
- middlewares: [
822
- ...((defaultOp as any).middlewares ?? []),
823
- ...((override as any).middlewares ?? []),
824
- ],
825
- meta: [
826
- ...((defaultOp as any).meta ?? []),
827
- ...((override as any).meta ?? []),
828
- ],
829
- } as any
830
- } else {
831
- result[op] = override as any
832
- }
833
- } else if (defaultOp !== undefined) {
834
- result[op] = defaultOp as any
835
- }
836
- // else: undefined = use default behavior (enabled with empty config)
837
- }
838
-
839
- return result
840
- }
841
-
842
- /**
843
- * Creates a router with CRUD-like operations for multiple jobs.
844
- *
845
- * @example
846
- * ```ts
847
- * const jobsRouter = createJobsRouter({
848
- * jobs: [userJob, emailJob] as const,
849
- * guards: [authGuard],
850
- * defaults: {
851
- * list: {},
852
- * get: {},
853
- * add: {},
854
- * retry: { guards: [adminGuard] },
855
- * cancel: { guards: [adminGuard] },
856
- * remove: false,
857
- * },
858
- * overrides: {
859
- * userProcessing: {
860
- * add: jobOperation({
861
- * dependencies: { userService },
862
- * beforeAdd: async (ctx, input) => ({
863
- * ...input,
864
- * userId: ctx.userService.getCurrentId(),
865
- * }),
866
- * }),
867
- * },
868
- * },
869
- * })
870
- *
871
- * // Use in your router
872
- * createRouter({
873
- * routes: {
874
- * jobs: jobsRouter,
875
- * }
876
- * })
877
- * ```
878
- */
879
- export function createJobsRouter<const Jobs extends Record<string, AnyJob>>(
880
- options: CreateJobsRouterOptions<Jobs>,
881
- ): JobsRouter<Jobs> {
882
- const {
883
- jobs,
884
- guards: sharedGuards = [],
885
- middlewares: sharedMiddlewares = [],
886
- meta = [],
887
- defaults = {},
888
- overrides = {},
889
- } = options
890
-
891
- const routes: Record<string, AnyRouter> = {}
892
- const shared = { guards: sharedGuards, middlewares: sharedMiddlewares }
893
-
894
- for (const jobName in jobs) {
895
- const job = jobs[jobName]
896
- const jobOverrides =
897
- (overrides as Record<string, JobOperations>)[jobName] ?? {}
898
-
899
- // Merge defaults with job-specific overrides
900
- const operations = mergeOperations(defaults, jobOverrides)
901
-
902
- const jobRoutes: Record<string, AnyProcedure> = {}
903
-
904
- // Generate each enabled operation
905
- if (operations.info !== false) {
906
- jobRoutes.info = createInfoProcedure(
907
- job,
908
- operations.info as InfoOperationConfig,
909
- shared,
910
- )
911
- }
912
-
913
- if (operations.list !== false) {
914
- jobRoutes.list = createListProcedure(
915
- job,
916
- operations.list as ListOperationConfig,
917
- shared,
918
- )
919
- }
920
-
921
- if (operations.get !== false) {
922
- jobRoutes.get = createGetProcedure(
923
- job,
924
- operations.get as GetOperationConfig,
925
- shared,
926
- )
927
- }
928
-
929
- if (operations.add !== false) {
930
- jobRoutes.add = createAddProcedure(
931
- job,
932
- operations.add as AddOperationConfig,
933
- shared,
934
- )
935
- }
936
-
937
- if (operations.retry !== false) {
938
- jobRoutes.retry = createRetryProcedure(
939
- job,
940
- operations.retry as RetryOperationConfig,
941
- shared,
942
- )
943
- }
944
-
945
- if (operations.cancel !== false) {
946
- jobRoutes.cancel = createCancelProcedure(
947
- job,
948
- operations.cancel as CancelOperationConfig,
949
- shared,
950
- )
951
- }
952
-
953
- if (operations.remove !== false) {
954
- jobRoutes.remove = createRemoveProcedure(
955
- job,
956
- operations.remove as RemoveOperationConfig,
957
- shared,
958
- )
959
- }
960
-
961
- // Create router for this job (named by job name)
962
- routes[jobName] = createRouter({ routes: jobRoutes })
963
- }
964
-
965
- // Return router containing all job routers
966
- return createRouter({ routes, meta }) as JobsRouter<Jobs>
967
- }