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,583 +0,0 @@
1
- import type { LifecycleHooks } from '@nmtjs/application'
2
- import type { Container, Logger, LoggingOptions } from '@nmtjs/core'
3
- import type { Job } from 'bullmq'
4
- import { LifecycleHook } from '@nmtjs/application'
5
- import { anyAbortSignal } from '@nmtjs/common'
6
- import { Scope } from '@nmtjs/core'
7
- import { UnrecoverableError } from 'bullmq'
8
-
9
- import type { AnyJob } from './job.ts'
10
- import type { AnyJobStep } from './step.ts'
11
- import type {
12
- JobExecutionContext,
13
- JobProgressCheckpoint,
14
- StepResultEntry,
15
- } from './types.ts'
16
- import {
17
- currentJobInfo,
18
- jobAbortSignal,
19
- saveJobProgress,
20
- } from '../injectables.ts'
21
-
22
- export type JobRunnerOptions = { logging?: LoggingOptions }
23
-
24
- export type { StepResultEntry } from './types.ts'
25
-
26
- export interface JobRunnerRunOptions {
27
- signal: AbortSignal
28
- result: Record<string, unknown>
29
- stepResults: StepResultEntry[]
30
- currentStepIndex: number
31
- progress: Record<string, unknown>
32
- }
33
-
34
- export interface JobRunnerRunBeforeStepParams<
35
- Options extends JobRunnerRunOptions,
36
- > {
37
- job: AnyJob
38
- step: AnyJobStep
39
- stepIndex: number
40
- result: Record<string, unknown>
41
- stepResults: StepResultEntry[]
42
- options: Options
43
- }
44
-
45
- export interface JobRunnerRunAfterStepParams<
46
- Options extends JobRunnerRunOptions,
47
- > extends JobRunnerRunBeforeStepParams<Options> {
48
- stepResult: StepResultEntry
49
- }
50
-
51
- /** Context for saveProgress function - contains mutable state references */
52
- export interface SaveProgressContext<
53
- Options extends JobRunnerRunOptions = JobRunnerRunOptions,
54
- > {
55
- job: AnyJob
56
- progress: Record<string, unknown>
57
- result: Record<string, unknown>
58
- stepResults: StepResultEntry[] | null
59
- currentStepIndex: number
60
- options: Options | null
61
- }
62
-
63
- export class JobRunner<
64
- RunOptions extends JobRunnerRunOptions = JobRunnerRunOptions,
65
- > {
66
- logger: Logger
67
-
68
- constructor(
69
- protected runtime: {
70
- logger: Logger
71
- container: Container
72
- lifecycleHooks: LifecycleHooks
73
- },
74
- ) {
75
- this.logger = runtime.logger.child({ $group: JobRunner.name })
76
- }
77
-
78
- get container() {
79
- return this.runtime.container
80
- }
81
-
82
- /**
83
- * Creates a function that saves the current job progress state.
84
- * Override in subclasses to implement actual persistence.
85
- */
86
- protected createSaveProgressFn(
87
- _context: SaveProgressContext<RunOptions>,
88
- ): () => Promise<void> {
89
- // No-op in base runner - subclasses implement actual persistence
90
- return async () => {}
91
- }
92
-
93
- /**
94
- * Creates the current job info object.
95
- * Override in subclasses to include additional info (e.g., BullMQ job ID).
96
- */
97
- protected createJobInfo(
98
- job: AnyJob,
99
- _options: Partial<RunOptions>,
100
- ): JobExecutionContext {
101
- return { name: job.options.name }
102
- }
103
-
104
- async runJob<T extends AnyJob>(
105
- job: T,
106
- data: any,
107
- options: Partial<RunOptions> = {},
108
- ): Promise<T['_']['output']> {
109
- const {
110
- signal: runSignal,
111
- result: runResult = {},
112
- stepResults: runStepResults = [] as RunOptions['stepResults'],
113
- progress: runProgress = {},
114
- currentStepIndex = 0,
115
- ...rest
116
- } = options
117
-
118
- const { input, output, jobSteps: steps } = job
119
-
120
- const result: Record<string, unknown> = { ...runResult }
121
- const decodedInput = input.decode(data)
122
-
123
- // Initialize progress: decode from checkpoint or start fresh
124
- const progress: Record<string, unknown> = job.progress
125
- ? job.progress.decode(runProgress)
126
- : { ...runProgress }
127
-
128
- using stopListener = this.runtime.lifecycleHooks.once(
129
- LifecycleHook.BeforeDispose,
130
- )
131
- const signal = anyAbortSignal(runSignal, stopListener.signal)
132
- await using container = this.container.fork(Scope.Global)
133
- container.provide(jobAbortSignal, signal)
134
- container.provide(currentJobInfo, this.createJobInfo(job, options))
135
-
136
- // Create mutable state context for saveProgress
137
- const progressContext = {
138
- job,
139
- progress,
140
- result,
141
- stepResults: null as StepResultEntry[] | null, // Will be set below
142
- currentStepIndex,
143
- options: null as RunOptions | null, // Will be set below
144
- }
145
-
146
- // Provide saveProgress injectable
147
- container.provide(
148
- saveJobProgress,
149
- this.createSaveProgressFn(progressContext),
150
- )
151
-
152
- const jobDependencyContext = await container.createContext(job.dependencies)
153
- const jobData = job.options.data
154
- ? await job.options.data(jobDependencyContext, decodedInput, progress)
155
- : undefined
156
-
157
- const stepResults: StepResultEntry[] = Array.from({ length: steps.length })
158
-
159
- // Restore previous step results and reconstruct accumulated result
160
- for (let stepIndex = 0; stepIndex < runStepResults.length; stepIndex++) {
161
- const entry = runStepResults[stepIndex]
162
- stepResults[stepIndex] = entry
163
- if (entry?.data) Object.assign(result, entry.data)
164
- }
165
-
166
- // @ts-expect-error
167
- const runOptions: RunOptions = {
168
- signal,
169
- result,
170
- stepResults,
171
- currentStepIndex: currentStepIndex,
172
- progress,
173
- ...rest,
174
- } satisfies JobRunnerRunOptions
175
-
176
- // Update mutable context references
177
- progressContext.stepResults = stepResults
178
- progressContext.options = runOptions
179
-
180
- for (let stepIndex = currentStepIndex; stepIndex < steps.length; ) {
181
- if (signal.aborted) {
182
- const { reason } = signal
183
- if (reason instanceof UnrecoverableError) throw reason
184
- throw new UnrecoverableError('Job cancelled')
185
- }
186
-
187
- const groupStart = job.parallelGroupByStepIndex.get(stepIndex)
188
-
189
- if (groupStart !== undefined) {
190
- const groupEnd = job.parallelGroups.get(groupStart) ?? groupStart + 1
191
- const resultSnapshot = Object.freeze(
192
- Object.assign({}, decodedInput, result),
193
- )
194
-
195
- const pending = [] as number[]
196
- for (let index = groupStart; index < groupEnd; index++) {
197
- if (!stepResults[index]) pending.push(index)
198
- }
199
-
200
- if (pending.length > 0) {
201
- const settled = await Promise.all(
202
- pending.map(async (index) => {
203
- const step = steps[index]
204
- try {
205
- const produced = await this.runStep({
206
- job,
207
- step,
208
- stepIndex: index,
209
- result,
210
- resultSnapshot,
211
- decodedInput,
212
- progress,
213
- stepResults,
214
- options: runOptions,
215
- jobDependencyContext,
216
- jobData,
217
- container,
218
- applyResult: false,
219
- })
220
- return { index, produced }
221
- } catch (error) {
222
- return { index, error }
223
- }
224
- }),
225
- )
226
-
227
- const errors = settled.filter(
228
- (entry): entry is { index: number; error: unknown } =>
229
- 'error' in entry,
230
- )
231
-
232
- if (errors.length > 0) {
233
- throw new Error(
234
- `Error during parallel step group [${groupStart}-${groupEnd - 1}]`,
235
- { cause: errors[0].error },
236
- )
237
- }
238
-
239
- const keyOwners = new Map<string, number>()
240
- for (const entry of settled) {
241
- if (!('produced' in entry) || !entry.produced) continue
242
- for (const key of Object.keys(entry.produced)) {
243
- const owner = keyOwners.get(key)
244
- if (owner !== undefined && owner !== entry.index) {
245
- throw new Error(
246
- `Parallel step key conflict for "${key}" between steps [${owner}] and [${entry.index}]`,
247
- )
248
- }
249
- keyOwners.set(key, entry.index)
250
- }
251
- }
252
-
253
- for (let index = groupStart; index < groupEnd; index++) {
254
- const entry = settled.find(
255
- (
256
- item,
257
- ): item is {
258
- index: number
259
- produced: Record<string, unknown> | null
260
- } => item.index === index && 'produced' in item,
261
- )
262
- if (entry?.produced) {
263
- Object.assign(result, entry.produced)
264
- }
265
- }
266
- }
267
-
268
- stepIndex = groupEnd
269
- continue
270
- }
271
-
272
- const step = steps[stepIndex]
273
- const resultSnapshot = Object.freeze(
274
- Object.assign({}, decodedInput, result),
275
- )
276
-
277
- await this.runStep({
278
- job,
279
- step,
280
- stepIndex,
281
- result,
282
- resultSnapshot,
283
- decodedInput,
284
- progress,
285
- stepResults,
286
- options: runOptions,
287
- jobDependencyContext,
288
- jobData,
289
- container,
290
- applyResult: true,
291
- })
292
-
293
- stepIndex++
294
- }
295
-
296
- const finalPayload = await job.returnHandler!({
297
- context: jobDependencyContext,
298
- data: jobData,
299
- input: decodedInput,
300
- result,
301
- progress,
302
- })
303
-
304
- return output.encode(finalPayload)
305
- }
306
-
307
- protected async runStep(params: {
308
- job: AnyJob
309
- step: AnyJobStep
310
- stepIndex: number
311
- result: Record<string, unknown>
312
- resultSnapshot: Record<string, unknown>
313
- decodedInput: Record<string, unknown>
314
- progress: Record<string, unknown>
315
- stepResults: StepResultEntry[]
316
- options: RunOptions
317
- jobDependencyContext: any
318
- jobData: unknown
319
- container: Container
320
- applyResult: boolean
321
- }): Promise<Record<string, unknown> | null> {
322
- const {
323
- job,
324
- step,
325
- stepIndex,
326
- result,
327
- resultSnapshot,
328
- decodedInput,
329
- progress,
330
- stepResults,
331
- options,
332
- jobDependencyContext,
333
- jobData,
334
- container,
335
- applyResult,
336
- } = params
337
-
338
- try {
339
- const condition = job.conditions.get(stepIndex)
340
- if (condition) {
341
- const shouldRun = await condition({
342
- context: jobDependencyContext,
343
- data: jobData,
344
- input: decodedInput,
345
- result: resultSnapshot,
346
- progress,
347
- })
348
- if (!shouldRun) {
349
- const now = Date.now()
350
- const skippedResult = {
351
- data: null,
352
- startedAt: now,
353
- completedAt: now,
354
- duration: 0,
355
- }
356
- stepResults[stepIndex] = skippedResult
357
- await this.afterStep({
358
- job,
359
- step,
360
- stepIndex,
361
- result,
362
- stepResult: skippedResult,
363
- stepResults,
364
- options,
365
- })
366
- return null
367
- }
368
- }
369
-
370
- const stepStartedAt = Date.now()
371
-
372
- await this.beforeStep({
373
- job,
374
- step,
375
- stepIndex,
376
- result,
377
- options,
378
- stepResults,
379
- })
380
-
381
- const stepContext = await container.createContext(step.dependencies)
382
- const stepInput = step.input.decode(resultSnapshot)
383
-
384
- await job.beforeEachHandler?.({
385
- context: jobDependencyContext,
386
- data: jobData,
387
- input: decodedInput,
388
- result: resultSnapshot,
389
- progress,
390
- step,
391
- stepIndex,
392
- })
393
-
394
- const handlerReturn = await step.handler(stepContext, stepInput, jobData)
395
-
396
- const produced = step.output.encode(handlerReturn ?? {})
397
- const stepCompletedAt = Date.now()
398
- const stepResult: StepResultEntry = {
399
- data: produced,
400
- startedAt: stepStartedAt,
401
- completedAt: stepCompletedAt,
402
- duration: stepCompletedAt - stepStartedAt,
403
- }
404
-
405
- stepResults[stepIndex] = stepResult
406
-
407
- if (applyResult) {
408
- Object.assign(result, produced)
409
- }
410
-
411
- await job.afterEachHandler?.({
412
- context: jobDependencyContext,
413
- data: jobData,
414
- input: decodedInput,
415
- result: applyResult ? result : Object.assign({}, result, produced),
416
- progress,
417
- step,
418
- stepIndex,
419
- })
420
-
421
- await this.afterStep({
422
- job,
423
- step,
424
- stepIndex,
425
- result,
426
- stepResult,
427
- stepResults,
428
- options,
429
- })
430
-
431
- return produced
432
- } catch (error) {
433
- const wrapped = new Error(`Error during step [${stepIndex}]`, {
434
- cause: error,
435
- })
436
- this.logger.error(wrapped)
437
-
438
- const allowRetry = await job.onErrorHandler?.({
439
- context: jobDependencyContext,
440
- data: jobData,
441
- input: decodedInput,
442
- result,
443
- progress,
444
- step,
445
- stepIndex,
446
- error,
447
- })
448
-
449
- if (allowRetry === false) {
450
- throw new UnrecoverableError('Job failed (unrecoverable)')
451
- }
452
-
453
- throw wrapped
454
- }
455
- }
456
-
457
- protected nextStepIndex(stepResults: StepResultEntry[]) {
458
- const missingIndex = stepResults.findIndex((entry) => !entry)
459
- return missingIndex === -1 ? stepResults.length : missingIndex
460
- }
461
-
462
- protected async beforeStep(
463
- params: JobRunnerRunBeforeStepParams<RunOptions>,
464
- ): Promise<void> {
465
- this.logger.debug(
466
- {
467
- job: params.job.name,
468
- step: params.step.label || params.stepIndex + 1,
469
- stepIndex: params.stepIndex,
470
- },
471
- 'Executing job step',
472
- )
473
- }
474
-
475
- protected async afterStep(
476
- params: JobRunnerRunAfterStepParams<RunOptions>,
477
- ): Promise<void> {
478
- this.logger.debug(
479
- {
480
- job: params.job.name,
481
- step: params.step.label || params.stepIndex + 1,
482
- stepIndex: params.stepIndex,
483
- },
484
- 'Completed job step',
485
- )
486
- }
487
- }
488
-
489
- export class ApplicationWorkerJobRunner extends JobRunner<
490
- JobRunnerRunOptions & { queueJob: Job }
491
- > {
492
- constructor(
493
- protected runtime: {
494
- logger: Logger
495
- container: Container
496
- lifecycleHooks: LifecycleHooks
497
- },
498
- ) {
499
- super(runtime)
500
- }
501
-
502
- protected createJobInfo(
503
- job: AnyJob,
504
- options: Partial<JobRunnerRunOptions & { queueJob: Job }>,
505
- ): JobExecutionContext {
506
- const { queueJob } = options
507
- return {
508
- name: job.options.name,
509
- id: queueJob?.id,
510
- queue: queueJob?.queueName,
511
- attempts: queueJob?.attemptsMade,
512
- stepIndex: options.stepResults
513
- ? this.nextStepIndex(options.stepResults)
514
- : options.currentStepIndex,
515
- }
516
- }
517
-
518
- protected createSaveProgressFn(
519
- context: SaveProgressContext<JobRunnerRunOptions & { queueJob: Job }>,
520
- ): () => Promise<void> {
521
- return async () => {
522
- const { job, progress, result, stepResults, options } = context
523
- if (!options || !stepResults) return
524
-
525
- const { queueJob } = options
526
-
527
- const currentStepIndex = this.nextStepIndex(stepResults)
528
-
529
- // Encode progress before persisting if schema is defined
530
- const encodedProgress = job.progress
531
- ? job.progress.encode(progress)
532
- : progress
533
-
534
- const checkpoint: JobProgressCheckpoint = {
535
- stepIndex: currentStepIndex,
536
- stepLabel: job.jobSteps[currentStepIndex]?.label,
537
- result,
538
- stepResults,
539
- progress: encodedProgress,
540
- }
541
-
542
- await queueJob.updateProgress(checkpoint)
543
- }
544
- }
545
-
546
- protected async afterStep(
547
- params: JobRunnerRunAfterStepParams<
548
- JobRunnerRunOptions & { queueJob: Job }
549
- >,
550
- ): Promise<void> {
551
- await super.afterStep(params)
552
- const {
553
- job,
554
- step,
555
- result,
556
- stepResult,
557
- stepResults,
558
- stepIndex,
559
- options: { queueJob, progress },
560
- } = params
561
- const nextStepIndex = this.nextStepIndex(stepResults)
562
-
563
- // Encode progress before persisting if schema is defined
564
- const encodedProgress = job.progress
565
- ? job.progress.encode(progress)
566
- : progress
567
-
568
- const checkpoint: JobProgressCheckpoint = {
569
- stepIndex: nextStepIndex,
570
- stepLabel: job.jobSteps[nextStepIndex]?.label,
571
- result,
572
- stepResults,
573
- progress: encodedProgress,
574
- }
575
-
576
- await Promise.all([
577
- queueJob.log(
578
- `Step ${step.label || stepIndex + 1} completed in ${(stepResult.duration / 1000).toFixed(3)}s`,
579
- ),
580
- queueJob.updateProgress(checkpoint),
581
- ])
582
- }
583
- }
@@ -1,66 +0,0 @@
1
- import type { MaybePromise } from '@nmtjs/common'
2
- import type { Dependant, Dependencies, DependencyContext } from '@nmtjs/core'
3
- import type { AnyObjectLikeType, ObjectType } from '@nmtjs/type/object'
4
- import { tryCaptureStackTrace } from '@nmtjs/common'
5
- import { t } from '@nmtjs/type'
6
-
7
- import { kJobStepKey } from '../constants.ts'
8
-
9
- export type AnyJobStep = JobStep<any, any, any, any, any>
10
-
11
- export type JobStepHandler<
12
- Deps extends Dependencies,
13
- Input extends AnyObjectLikeType,
14
- Output extends AnyObjectLikeType,
15
- Data = any,
16
- > = (
17
- context: DependencyContext<Deps>,
18
- input: t.infer.decode.output<Input>,
19
- data: Data,
20
- ) => MaybePromise<t.infer.encode.input<Output>>
21
-
22
- export interface JobStep<
23
- Input extends AnyObjectLikeType = AnyObjectLikeType,
24
- Output extends AnyObjectLikeType = AnyObjectLikeType,
25
- Deps extends Dependencies = Dependencies,
26
- Return = unknown,
27
- Data = any,
28
- > extends Dependant {
29
- [kJobStepKey]: any
30
- _?: { return: Return }
31
- label?: string
32
- input: Input
33
- output: Output
34
- dependencies: Deps
35
- handler: JobStepHandler<Deps, Input, Output, Data>
36
- }
37
-
38
- export function createStep<
39
- Input extends AnyObjectLikeType,
40
- Output extends AnyObjectLikeType = ObjectType<{}>,
41
- Deps extends Dependencies = {},
42
- Return = unknown,
43
- Data = any,
44
- >(step: {
45
- label?: string
46
- input: Input
47
- output?: Output
48
- dependencies?: Deps
49
- handler: JobStepHandler<Deps, Input, Output, Data>
50
- }): JobStep<Input, Output, Deps, Return, Data> {
51
- return Object.freeze({
52
- [kJobStepKey]: true,
53
- output: t.object({}) as unknown as Output,
54
- dependencies: {} as Deps,
55
- stack: tryCaptureStackTrace(),
56
- ...step,
57
- })
58
- }
59
-
60
- export function isJobStep(value: unknown): value is AnyJobStep {
61
- return (
62
- typeof value === 'object' &&
63
- value !== null &&
64
- (value as AnyJobStep)[kJobStepKey] === true
65
- )
66
- }