@nest-batch/core 0.2.0 → 0.2.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 (85) hide show
  1. package/README.md +7 -5
  2. package/dist/src/adapters/in-process.adapter.d.ts +16 -13
  3. package/dist/src/adapters/in-process.adapter.d.ts.map +1 -1
  4. package/dist/src/adapters/in-process.adapter.js +2 -0
  5. package/dist/src/adapters/in-process.adapter.js.map +1 -1
  6. package/dist/src/compiler/definition-compiler.d.ts.map +1 -1
  7. package/dist/src/compiler/definition-compiler.js +3 -0
  8. package/dist/src/compiler/definition-compiler.js.map +1 -1
  9. package/dist/src/core/ir/listener-definition.d.ts +2 -0
  10. package/dist/src/core/ir/listener-definition.d.ts.map +1 -1
  11. package/dist/src/core/item/interfaces.d.ts +14 -4
  12. package/dist/src/core/item/interfaces.d.ts.map +1 -1
  13. package/dist/src/decorators/listener.decorators.d.ts +4 -3
  14. package/dist/src/decorators/listener.decorators.d.ts.map +1 -1
  15. package/dist/src/decorators/listener.decorators.js +6 -3
  16. package/dist/src/decorators/listener.decorators.js.map +1 -1
  17. package/dist/src/execution/chunk-step-executor.d.ts +7 -1
  18. package/dist/src/execution/chunk-step-executor.d.ts.map +1 -1
  19. package/dist/src/execution/chunk-step-executor.js +104 -13
  20. package/dist/src/execution/chunk-step-executor.js.map +1 -1
  21. package/dist/src/execution/in-process-schedule.d.ts +25 -0
  22. package/dist/src/execution/in-process-schedule.d.ts.map +1 -0
  23. package/dist/src/execution/in-process-schedule.js +129 -0
  24. package/dist/src/execution/in-process-schedule.js.map +1 -0
  25. package/dist/src/execution/index.d.ts +1 -0
  26. package/dist/src/execution/index.d.ts.map +1 -1
  27. package/dist/src/execution/index.js +1 -0
  28. package/dist/src/execution/index.js.map +1 -1
  29. package/dist/src/execution/job-executor.d.ts.map +1 -1
  30. package/dist/src/execution/job-executor.js +14 -8
  31. package/dist/src/execution/job-executor.js.map +1 -1
  32. package/dist/src/execution/listener-invoker.d.ts +25 -9
  33. package/dist/src/execution/listener-invoker.d.ts.map +1 -1
  34. package/dist/src/execution/listener-invoker.js +70 -14
  35. package/dist/src/execution/listener-invoker.js.map +1 -1
  36. package/dist/src/execution/tasklet-step-executor.d.ts +4 -1
  37. package/dist/src/execution/tasklet-step-executor.d.ts.map +1 -1
  38. package/dist/src/execution/tasklet-step-executor.js +20 -16
  39. package/dist/src/execution/tasklet-step-executor.js.map +1 -1
  40. package/dist/src/explorer/batch-explorer.d.ts +2 -1
  41. package/dist/src/explorer/batch-explorer.d.ts.map +1 -1
  42. package/dist/src/explorer/batch-explorer.js +3 -0
  43. package/dist/src/explorer/batch-explorer.js.map +1 -1
  44. package/dist/src/index.d.ts +1 -0
  45. package/dist/src/index.d.ts.map +1 -1
  46. package/dist/src/index.js +1 -0
  47. package/dist/src/index.js.map +1 -1
  48. package/dist/src/module/batch-schedule-registry.d.ts +13 -14
  49. package/dist/src/module/batch-schedule-registry.d.ts.map +1 -1
  50. package/dist/src/module/batch-schedule-registry.js +0 -0
  51. package/dist/src/module/batch-schedule-registry.js.map +1 -1
  52. package/dist/src/module/nest-batch.module.d.ts +4 -3
  53. package/dist/src/module/nest-batch.module.d.ts.map +1 -1
  54. package/dist/src/module/nest-batch.module.js +3 -2
  55. package/dist/src/module/nest-batch.module.js.map +1 -1
  56. package/dist/src/module/tokens.d.ts +5 -6
  57. package/dist/src/module/tokens.d.ts.map +1 -1
  58. package/dist/src/module/tokens.js.map +1 -1
  59. package/dist/src/partition-helpers.d.ts +3 -3
  60. package/dist/src/partition-helpers.d.ts.map +1 -1
  61. package/dist/src/partition-helpers.js +3 -3
  62. package/dist/src/partition-helpers.js.map +1 -1
  63. package/dist/src/scheduling/batch-scheduled.d.ts +9 -11
  64. package/dist/src/scheduling/batch-scheduled.d.ts.map +1 -1
  65. package/dist/src/scheduling/batch-scheduled.js +12 -20
  66. package/dist/src/scheduling/batch-scheduled.js.map +1 -1
  67. package/package.json +4 -1
  68. package/src/adapters/in-process.adapter.ts +18 -13
  69. package/src/compiler/definition-compiler.ts +12 -5
  70. package/src/core/ir/listener-definition.ts +2 -0
  71. package/src/core/item/interfaces.ts +15 -4
  72. package/src/decorators/listener.decorators.ts +11 -13
  73. package/src/execution/chunk-step-executor.ts +212 -18
  74. package/src/execution/in-process-schedule.ts +143 -0
  75. package/src/execution/index.ts +1 -0
  76. package/src/execution/job-executor.ts +30 -21
  77. package/src/execution/listener-invoker.ts +105 -27
  78. package/src/execution/tasklet-step-executor.ts +40 -16
  79. package/src/explorer/batch-explorer.ts +10 -4
  80. package/src/index.ts +1 -0
  81. package/src/module/batch-schedule-registry.ts +0 -0
  82. package/src/module/nest-batch.module.ts +21 -42
  83. package/src/module/tokens.ts +8 -15
  84. package/src/partition-helpers.ts +13 -17
  85. package/src/scheduling/batch-scheduled.ts +22 -32
@@ -44,7 +44,8 @@ let ChunkStepExecutor = class ChunkStepExecutor {
44
44
  async execute(step, context) {
45
45
  const skipPolicy = step.skipPolicy ? (0, _skippolicy.compileSkipPolicy)(step.skipPolicy) : null;
46
46
  const retryPolicy = step.retryPolicy ? (0, _retrypolicy.compileRetryPolicy)(step.retryPolicy) : null;
47
- const skipResolvers = context.skipListenerResolvers ?? new Map();
47
+ const listenerResolvers = context.listenerResolvers ?? new Map();
48
+ const skipResolvers = context.skipListenerResolvers ?? listenerResolvers;
48
49
  const skipLimit = step.skipPolicy?.limit ?? 0;
49
50
  const retryLimit = step.retryPolicy?.limit ?? 0;
50
51
  // Partition routing (T8): when the transport attached a
@@ -95,6 +96,7 @@ let ChunkStepExecutor = class ChunkStepExecutor {
95
96
  let chunkIndex = 0;
96
97
  let openedStreams = [];
97
98
  let streamContext = null;
99
+ await context.listenerInvoker.invokeBefore(listenerResolvers, 'step', this.buildListenerContext(step, context));
98
100
  try {
99
101
  // Resolve inside the try block so a missing provider-token ref
100
102
  // surfaces as FAILED/{exitMessage: <err>}, matching the tasklet
@@ -130,8 +132,9 @@ let ChunkStepExecutor = class ChunkStepExecutor {
130
132
  // slice on the next iteration.
131
133
  const skipCap = partition !== null ? Math.max(0, partition.to - partition.from - readCount) : step.chunkSize;
132
134
  if (skipCap === 0) break;
135
+ const itemContext = this.buildItemContext(step, context, chunkIndex);
133
136
  for(let i = 0; i < skipCap; i++){
134
- const item = await reader.read();
137
+ const item = await reader.read(itemContext);
135
138
  if (item == null) break;
136
139
  drained += 1;
137
140
  }
@@ -153,9 +156,14 @@ let ChunkStepExecutor = class ChunkStepExecutor {
153
156
  if (remaining === 0) {
154
157
  break;
155
158
  }
159
+ const itemContext = this.buildItemContext(step, context, chunkIndex);
160
+ const listenerContext = this.buildListenerContext(step, context, {
161
+ chunkIndex
162
+ });
163
+ await context.listenerInvoker.invokeBefore(listenerResolvers, 'chunk', listenerContext);
156
164
  // ---- READ PHASE: per-item retry+skip ----
157
165
  for(let i = 0; i < remaining && !eof; i++){
158
- const r = await this.runPhase(()=>reader.read(), {
166
+ const r = await this.runPhase(()=>reader.read(itemContext), {
159
167
  phase: 'read',
160
168
  item: null,
161
169
  skipPolicy,
@@ -166,6 +174,17 @@ let ChunkStepExecutor = class ChunkStepExecutor {
166
174
  onSkip: async (err)=>{
167
175
  skipCount += 1;
168
176
  await context.listenerInvoker.invokeOnSkipRead(skipResolvers, err, null);
177
+ },
178
+ before: async ()=>{
179
+ await context.listenerInvoker.invokeBeforeRead(listenerResolvers, listenerContext);
180
+ },
181
+ after: async (item)=>{
182
+ if (item !== null && item !== undefined) {
183
+ await context.listenerInvoker.invokeAfterRead(listenerResolvers, item, listenerContext);
184
+ }
185
+ },
186
+ onError: async (err)=>{
187
+ await context.listenerInvoker.invokeOnReadError(listenerResolvers, err, listenerContext);
169
188
  }
170
189
  });
171
190
  if (r.kind === 'skipped') continue;
@@ -177,7 +196,16 @@ let ChunkStepExecutor = class ChunkStepExecutor {
177
196
  items.push(r.value);
178
197
  readCount += 1;
179
198
  }
180
- if (items.length === 0) break; // EOF (either before first read or after skips)
199
+ if (items.length === 0) {
200
+ await context.listenerInvoker.invokeAfter(listenerResolvers, 'chunk', listenerContext, {
201
+ status: _status.StepStatus.COMPLETED,
202
+ readCount,
203
+ writeCount,
204
+ skipCount,
205
+ commitCount
206
+ });
207
+ break; // EOF (either before first read or after skips)
208
+ }
181
209
  // ---- PROCESS PHASE: per-item retry+skip ----
182
210
  const processed = [];
183
211
  for (const item of items){
@@ -185,7 +213,7 @@ let ChunkStepExecutor = class ChunkStepExecutor {
185
213
  processed.push(item);
186
214
  continue;
187
215
  }
188
- const r = await this.runPhase(()=>processor.process(item), {
216
+ const r = await this.runPhase(()=>processor.process(item, itemContext), {
189
217
  phase: 'process',
190
218
  item,
191
219
  skipPolicy,
@@ -196,6 +224,15 @@ let ChunkStepExecutor = class ChunkStepExecutor {
196
224
  onSkip: async (err)=>{
197
225
  skipCount += 1;
198
226
  await context.listenerInvoker.invokeOnSkipProcess(skipResolvers, item, err);
227
+ },
228
+ before: async ()=>{
229
+ await context.listenerInvoker.invokeBeforeProcess(listenerResolvers, item, listenerContext);
230
+ },
231
+ after: async (value)=>{
232
+ await context.listenerInvoker.invokeAfterProcess(listenerResolvers, item, value, listenerContext);
233
+ },
234
+ onError: async (err)=>{
235
+ await context.listenerInvoker.invokeOnProcessError(listenerResolvers, item, err, listenerContext);
199
236
  }
200
237
  });
201
238
  if (r.kind === 'skipped') continue;
@@ -210,7 +247,7 @@ let ChunkStepExecutor = class ChunkStepExecutor {
210
247
  // ---- WRITE PHASE: per-chunk retry+skip, wrapped in transaction ----
211
248
  if (processed.length > 0) {
212
249
  const r = await this.runPhase(()=>context.transactionManager.withTransaction(async ()=>{
213
- return writer.write(processed);
250
+ return writer.write(processed, itemContext);
214
251
  }), {
215
252
  phase: 'write',
216
253
  item: processed,
@@ -222,6 +259,15 @@ let ChunkStepExecutor = class ChunkStepExecutor {
222
259
  onSkip: async (err)=>{
223
260
  skipCount += 1;
224
261
  await context.listenerInvoker.invokeOnSkipWrite(skipResolvers, processed, err);
262
+ },
263
+ before: async ()=>{
264
+ await context.listenerInvoker.invokeBeforeWrite(listenerResolvers, processed, listenerContext);
265
+ },
266
+ after: async (value)=>{
267
+ await context.listenerInvoker.invokeAfterWrite(listenerResolvers, processed, value, listenerContext);
268
+ },
269
+ onError: async (err)=>{
270
+ await context.listenerInvoker.invokeOnWriteError(listenerResolvers, processed, err, listenerContext);
225
271
  }
226
272
  });
227
273
  if (r.kind === 'ok') {
@@ -251,13 +297,20 @@ let ChunkStepExecutor = class ChunkStepExecutor {
251
297
  streamContext = await context.jobRepository.getExecutionContext({
252
298
  stepExecutionId: context.stepExecutionId
253
299
  });
300
+ await context.listenerInvoker.invokeAfter(listenerResolvers, 'chunk', listenerContext, {
301
+ status: _status.StepStatus.COMPLETED,
302
+ readCount,
303
+ writeCount,
304
+ skipCount,
305
+ commitCount: commitCount + 1
306
+ });
254
307
  commitCount += 1;
255
308
  chunkIndex += 1;
256
309
  }
257
310
  const streamsToClose = openedStreams;
258
311
  openedStreams = [];
259
312
  await this.closeStreams(streamsToClose);
260
- return {
313
+ const result = {
261
314
  status: _status.StepStatus.COMPLETED,
262
315
  exitCode: 'COMPLETED',
263
316
  exitMessage: '',
@@ -266,6 +319,8 @@ let ChunkStepExecutor = class ChunkStepExecutor {
266
319
  skipCount,
267
320
  commitCount
268
321
  };
322
+ await context.listenerInvoker.invokeAfter(listenerResolvers, 'step', this.buildListenerContext(step, context), result);
323
+ return result;
269
324
  } catch (err) {
270
325
  let finalError = err;
271
326
  const streamsToClose = openedStreams;
@@ -275,7 +330,10 @@ let ChunkStepExecutor = class ChunkStepExecutor {
275
330
  } catch (closeErr) {
276
331
  finalError = closeErr;
277
332
  }
278
- return {
333
+ await context.listenerInvoker.invokeOnError(listenerResolvers, 'chunk', this.buildListenerContext(step, context, {
334
+ chunkIndex
335
+ }), finalError);
336
+ const result = {
279
337
  status: _status.StepStatus.FAILED,
280
338
  exitCode: 'FAILED',
281
339
  exitMessage: finalError instanceof Error ? finalError.message : String(finalError),
@@ -284,8 +342,37 @@ let ChunkStepExecutor = class ChunkStepExecutor {
284
342
  skipCount,
285
343
  commitCount
286
344
  };
345
+ await context.listenerInvoker.invokeOnError(listenerResolvers, 'step', this.buildListenerContext(step, context), finalError);
346
+ await context.listenerInvoker.invokeAfter(listenerResolvers, 'step', this.buildListenerContext(step, context), result);
347
+ return result;
287
348
  }
288
349
  }
350
+ buildListenerContext(step, context, extra = {}) {
351
+ return {
352
+ jobExecutionId: context.jobExecutionId,
353
+ stepExecutionId: context.stepExecutionId,
354
+ stepName: context.stepName ?? step.id,
355
+ jobParameters: context.jobParameters ?? {},
356
+ ...extra
357
+ };
358
+ }
359
+ buildItemContext(step, context, chunkIndex) {
360
+ return {
361
+ jobExecutionId: context.jobExecutionId,
362
+ stepExecutionId: context.stepExecutionId,
363
+ stepName: context.stepName ?? step.id,
364
+ jobParameters: context.jobParameters ?? {},
365
+ chunkIndex,
366
+ getExecutionContext: async ()=>context.jobRepository.getExecutionContext({
367
+ stepExecutionId: context.stepExecutionId
368
+ }),
369
+ saveExecutionContext: async (ctx)=>{
370
+ await context.jobRepository.saveExecutionContext({
371
+ stepExecutionId: context.stepExecutionId
372
+ }, ctx);
373
+ }
374
+ };
375
+ }
289
376
  async updateStreams(streams, ctx) {
290
377
  let next = ctx;
291
378
  for (const stream of streams){
@@ -320,13 +407,12 @@ let ChunkStepExecutor = class ChunkStepExecutor {
320
407
  // exactly one attempt.
321
408
  const outerCap = options.retryPolicy ? 999 : 1;
322
409
  while(attempt <= outerCap){
410
+ if (options.before) await options.before();
411
+ let value;
323
412
  try {
324
- const value = await op();
325
- return {
326
- kind: 'ok',
327
- value
328
- };
413
+ value = await op();
329
414
  } catch (err) {
415
+ if (options.onError) await options.onError(err);
330
416
  // 1) Skip consultation: is this error skippable, and is there budget?
331
417
  if (options.skipPolicy) {
332
418
  // Use the policy's `shouldSkip` with `skipCount: 0` to get a pure
@@ -384,6 +470,11 @@ let ChunkStepExecutor = class ChunkStepExecutor {
384
470
  // 3) Neither skippable nor retryable: re-throw the original error.
385
471
  throw err;
386
472
  }
473
+ if (options.after) await options.after(value);
474
+ return {
475
+ kind: 'ok',
476
+ value
477
+ };
387
478
  }
388
479
  // Defensive: the outer cap should never be reached when a retry policy
389
480
  // gates us, but if no retry policy exists and the very first attempt
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/execution/chunk-step-executor.ts"],"sourcesContent":["import { Injectable } from '@nestjs/common';\nimport type { ChunkStepDefinition, ReaderRef, ProcessorRef, WriterRef } from '../core/ir';\nimport { RefKind } from '../core/ir';\nimport type { ItemReader, ItemProcessor, ItemStream, ItemWriter } from '../core/item';\nimport type { ExecutionContext, JobRepository } from '../core/repository';\nimport type { TransactionManager } from '../core/transaction';\nimport { StepStatus } from '../core/status';\nimport type { SkipPolicy, SkipContext } from '../policies/skip-policy';\nimport type { RetryPolicy, RetryContext } from '../policies/retry-policy';\nimport { compileSkipPolicy } from '../policies/skip-policy';\nimport { compileRetryPolicy } from '../policies/retry-policy';\nimport { SkipLimitExceededError, RetryLimitExceededError } from '../core/errors';\nimport { enforcePartitionIndex } from '../partition-helpers';\nimport type { ResolverMap } from './listener-invoker';\nimport { ListenerInvoker } from './listener-invoker';\nimport { resolveProviderToken, type ProviderResolvers } from './ref-resolver';\n\nexport interface ChunkExecutionContext {\n jobExecutionId: string;\n /** Step execution id, used to scope the chunk-progress checkpoint in the\n * step's ExecutionContext (saved as `{ lastChunkIndex }`). */\n stepExecutionId: string;\n jobRepository: JobRepository;\n transactionManager: TransactionManager;\n listenerInvoker: ListenerInvoker;\n /** Map of resolved reader/processor/writer functions by name. */\n resolvers: Map<string, (...args: unknown[]) => unknown | Promise<unknown>>;\n jobExecutionId2: string; // unique key for listener resolver namespacing\n /** Optional skip listener resolver map. Keys follow the `on-skip:<kind>:<name>` shape. */\n skipListenerResolvers?: ResolverMap;\n /**\n * Optional map of provider-token id → already-resolved provider instance.\n * Populated by the JobExecutor (or test fixtures) so that\n * `RefKind.ProviderToken` refs on the reader/processor/writer slots can\n * be looked up without coupling the executor to the Nest DI container.\n */\n providerResolvers?: ProviderResolvers;\n /**\n * When set, the executor skips any chunk whose 0-based index is less\n * than or equal to this value. The reader is still advanced by\n * `chunkSize` calls per skipped chunk (so the data stream position\n * stays correct), but no read/process/write happens and `commitCount`\n * is not incremented. The checkpoint value is loaded from the prior\n * FAILED step execution's ExecutionContext by `JobExecutor`.\n */\n resumeFromChunkIndex?: number;\n /**\n * Partition ordinal for this chunk execution. Set by the transport\n * (BullMQ / Kafka) when the step declares `partitions.count >= 2`;\n * `undefined` means \"non-partitioned\" (the 0.1.0 path). The executor\n * uses this together with `partitionCount` and the step's\n * `partitions.range` to bound the read loop to the partition's\n * `[from, to)` range — see `packages/core/src/partition-helpers.ts`\n * and `docs/RELEASE-0.2.0.md §6`.\n */\n partitionIndex?: number;\n /**\n * Mirror of the step's `partitions.count`. Carried on the context\n * (rather than read from the step) so the executor does not have to\n * reason about which step is being executed when it consults the\n * partition range.\n */\n partitionCount?: number;\n}\n\nexport interface ChunkExecutionResult {\n status: StepStatus;\n exitCode: string;\n exitMessage: string;\n readCount: number;\n writeCount: number;\n skipCount: number;\n commitCount: number;\n}\n\n/** Phase tag used by skip/retry policies. Mirrors the union in policy modules. */\ntype Phase = 'read' | 'process' | 'write';\n\n/**\n * Outcome of a single per-phase attempt after skip/retry consultation.\n * - `ok` — the operation succeeded; `value` is the op's return value.\n * - `skipped` — the error was skippable (and within budget); the op was\n * abandoned, the on-skip listener was invoked, and `value` is\n * `undefined`.\n */\ntype PhaseResult<T> = { kind: 'ok'; value: T } | { kind: 'skipped' };\n\n/**\n * Options for `runPhase`. `getSkipCount` is a closure over the executor's\n * live `skipCount` accumulator so the policy's budget check is consistent\n * with the accounting in the outer loop.\n */\ninterface RunPhaseOptions {\n phase: Phase;\n item: unknown;\n skipPolicy: SkipPolicy | null;\n retryPolicy: RetryPolicy | null;\n skipLimit: number;\n retryLimit: number;\n /** Live read of the executor's skipCount accumulator. */\n getSkipCount: () => number;\n /** Invoked when an error is actually skipped (within budget). The caller\n * is responsible for incrementing its own skipCount here. */\n onSkip: (err: unknown) => Promise<void>;\n}\n\nfunction isItemStream(value: unknown): value is ItemStream {\n if (value === null || typeof value !== 'object') return false;\n const stream = value as Partial<ItemStream>;\n return (\n typeof stream.open === 'function' &&\n typeof stream.update === 'function' &&\n typeof stream.close === 'function'\n );\n}\n\nfunction withLastChunkIndex(ctx: ExecutionContext, chunkIndex: number): ExecutionContext {\n const current = ctx.data;\n const data =\n current !== null && typeof current === 'object' && !Array.isArray(current)\n ? { ...(current as Record<string, unknown>), lastChunkIndex: chunkIndex }\n : { lastChunkIndex: chunkIndex };\n\n return {\n data: data as ExecutionContext['data'],\n version: ctx.version,\n };\n}\n\n@Injectable()\nexport class ChunkStepExecutor {\n async execute(\n step: ChunkStepDefinition,\n context: ChunkExecutionContext,\n ): Promise<ChunkExecutionResult> {\n const skipPolicy = step.skipPolicy ? compileSkipPolicy(step.skipPolicy) : null;\n const retryPolicy = step.retryPolicy ? compileRetryPolicy(step.retryPolicy) : null;\n const skipResolvers: ResolverMap = context.skipListenerResolvers ?? new Map();\n\n const skipLimit = step.skipPolicy?.limit ?? 0;\n const retryLimit = step.retryPolicy?.limit ?? 0;\n\n // Partition routing (T8): when the transport attached a\n // `partitionIndex` / `partitionCount` pair to this execution, the\n // step's `partitions.range` (or, when absent, the runtime\n // \"read until EOF\" default) bounds the chunk loop. We resolve the\n // range once up-front so the hot loop does not re-invoke the\n // user's resolver.\n //\n // The resolve helper throws when the index is out of range; we\n // surface the violation as a FAILED step (caught by the outer\n // try/catch) rather than a propagated throw, matching the\n // chunk executor's \"errors become FAILED step\" contract.\n let partition: { from: number; to: number } | null = null;\n if (context.partitionIndex !== undefined && context.partitionCount !== undefined) {\n // Defensive: enforce the index range here too. The runtime\n // service validates at enqueue time, but the chunk executor is\n // also a public surface (tests drive it directly), so a\n // double-check costs almost nothing and pins the contract.\n enforcePartitionIndex(context.partitionIndex, context.partitionCount);\n if (step.partitions?.range === undefined) {\n // No `range` resolver: each partition reads from the start of\n // the input. This is the documented \"default\" behaviour in\n // `docs/RELEASE-0.2.0.md §6.1` — the host's reader is\n // responsible for not over-processing. We still cap the read\n // loop to the partition's \"share\" via a sentinel that the\n // hot loop checks.\n partition = null;\n } else {\n const range = step.partitions.range(context.partitionIndex, context.partitionCount);\n const [from, to] = range;\n if (from < 0 || to < from) {\n throw new Error(\n `[ChunkStepExecutor] partitions.range(${context.partitionIndex}, ` +\n `${context.partitionCount}) returned invalid [${from}, ${to})`,\n );\n }\n partition = { from, to };\n }\n }\n\n let readCount = 0;\n let writeCount = 0;\n let skipCount = 0;\n let commitCount = 0;\n // 0-based index of the chunk currently being assembled. Used by the\n // restart path to decide which chunks to skip and where to record\n // the last-committed checkpoint.\n let chunkIndex = 0;\n let openedStreams: ItemStream[] = [];\n let streamContext: ExecutionContext | null = null;\n\n try {\n // Resolve inside the try block so a missing provider-token ref\n // surfaces as FAILED/{exitMessage: <err>}, matching the tasklet\n // executor's contract — not as a propagated throw.\n const reader = this.resolveReader(step.reader, context);\n const processor = step.processor ? this.resolveProcessor(step.processor, context) : null;\n const writer = this.resolveWriter(step.writer, context);\n\n const streams: ItemStream[] = [];\n if (isItemStream(reader)) streams.push(reader);\n if (isItemStream(writer)) streams.push(writer);\n streamContext = await context.jobRepository.getExecutionContext({\n stepExecutionId: context.stepExecutionId,\n });\n for (const stream of streams) {\n await stream.open(streamContext);\n openedStreams.push(stream);\n }\n\n // Outer loop: keep reading chunks until reader returns null\n // eslint-disable-next-line no-constant-condition\n while (true) {\n // ---- SKIP PHASE (restart-only): drain up to chunkSize items so\n // the reader advances past already-committed chunks, but skip\n // the read/process/write pipeline entirely. commitCount is not\n // incremented (those chunks were already counted in the prior\n // run) and no checkpoint is written (the prior run owns it).\n if (\n context.resumeFromChunkIndex !== undefined &&\n chunkIndex <= context.resumeFromChunkIndex\n ) {\n let drained = 0;\n // Cap the restart skip-drain by the partition's remaining\n // budget too (T8). A restart on a partitioned step is rare\n // but possible — without the cap, the skip-drain could\n // pull items the partition's range is not allowed to\n // touch, leaving the chunk pipeline reading the wrong\n // slice on the next iteration.\n const skipCap =\n partition !== null\n ? Math.max(0, partition.to - partition.from - readCount)\n : step.chunkSize;\n if (skipCap === 0) break;\n for (let i = 0; i < skipCap; i++) {\n const item = await reader.read();\n if (item == null) break;\n drained += 1;\n }\n if (drained === 0) break; // EOF reached while skipping\n chunkIndex += 1;\n continue;\n }\n\n const items: unknown[] = [];\n let eof = false;\n\n // ---- PARTITION CAP (T8) ----\n // When the transport attached a partition's [from, to) range,\n // bound this chunk's read loop so the partition never\n // overshoots. `readCount` is the running count of items the\n // partition has read so far; `partition.to - partition.from`\n // is the partition's total budget. The cap is `min(chunkSize,\n // remaining)` and `0` is a valid \"no more items\" cap (we\n // exit the outer loop on the next iteration).\n const remaining =\n partition !== null ? Math.max(0, partition.to - partition.from - readCount) : step.chunkSize;\n if (remaining === 0) {\n // Partition is fully drained. Exit the outer loop so the\n // chunk executor returns COMPLETED with the partition's\n // commitCount + readCount + writeCount.\n break;\n }\n\n // ---- READ PHASE: per-item retry+skip ----\n for (let i = 0; i < remaining && !eof; i++) {\n const r = await this.runPhase<unknown>(() => reader.read(), {\n phase: 'read',\n item: null,\n skipPolicy,\n retryPolicy,\n skipLimit,\n retryLimit,\n getSkipCount: () => skipCount,\n onSkip: async (err) => {\n skipCount += 1;\n await context.listenerInvoker.invokeOnSkipRead(skipResolvers, err, null);\n },\n });\n if (r.kind === 'skipped') continue;\n if (r.value == null) {\n // Natural EOF from the reader\n eof = true;\n break;\n }\n items.push(r.value);\n readCount += 1;\n }\n if (items.length === 0) break; // EOF (either before first read or after skips)\n\n // ---- PROCESS PHASE: per-item retry+skip ----\n const processed: unknown[] = [];\n for (const item of items) {\n if (!processor) {\n processed.push(item);\n continue;\n }\n const r = await this.runPhase<unknown>(() => processor.process(item), {\n phase: 'process',\n item,\n skipPolicy,\n retryPolicy,\n skipLimit,\n retryLimit,\n getSkipCount: () => skipCount,\n onSkip: async (err) => {\n skipCount += 1;\n await context.listenerInvoker.invokeOnSkipProcess(skipResolvers, item, err);\n },\n });\n if (r.kind === 'skipped') continue;\n if (r.value !== null && r.value !== undefined) {\n processed.push(r.value);\n } else {\n // Processor filtered the item out (returned null/undefined) — not a\n // skip-policy skip, but counts as a skip for accounting parity.\n skipCount += 1;\n }\n }\n\n // ---- WRITE PHASE: per-chunk retry+skip, wrapped in transaction ----\n if (processed.length > 0) {\n const r = await this.runPhase<{ written: number; skipped: number } | void>(\n () =>\n context.transactionManager.withTransaction(async () => {\n return writer.write(processed);\n }),\n {\n phase: 'write',\n item: processed,\n skipPolicy,\n retryPolicy,\n skipLimit,\n retryLimit,\n getSkipCount: () => skipCount,\n onSkip: async (err) => {\n skipCount += 1;\n await context.listenerInvoker.invokeOnSkipWrite(skipResolvers, processed, err);\n },\n },\n );\n if (r.kind === 'ok') {\n if (r.value) {\n writeCount += r.value.written;\n skipCount += r.value.skipped;\n } else {\n writeCount += processed.length;\n }\n }\n // If the write was skipped, writeCount is not incremented and the\n // chunk is still considered \"committed\" (commitCount++) so that\n // progress tracking reflects that we moved past it.\n }\n\n // Persist the last-committed-chunk checkpoint in the step's\n // ExecutionContext. The next restart will read this value and\n // skip every chunk with index ≤ `lastChunkIndex`. The save is\n // intentionally outside the per-chunk transaction: the\n // checkpoint reflects \"we successfully moved past this chunk\",\n // and we only reach this line after the write completed\n // (either OK or skipped — both are forward progress).\n streamContext = withLastChunkIndex(streamContext, chunkIndex);\n streamContext = await this.updateStreams(openedStreams, streamContext);\n await context.jobRepository.saveExecutionContext(\n { stepExecutionId: context.stepExecutionId },\n streamContext,\n );\n streamContext = await context.jobRepository.getExecutionContext({\n stepExecutionId: context.stepExecutionId,\n });\n\n commitCount += 1;\n chunkIndex += 1;\n }\n\n const streamsToClose = openedStreams;\n openedStreams = [];\n await this.closeStreams(streamsToClose);\n\n return {\n status: StepStatus.COMPLETED,\n exitCode: 'COMPLETED',\n exitMessage: '',\n readCount,\n writeCount,\n skipCount,\n commitCount,\n };\n } catch (err) {\n let finalError = err;\n const streamsToClose = openedStreams;\n openedStreams = [];\n try {\n await this.closeStreams(streamsToClose);\n } catch (closeErr) {\n finalError = closeErr;\n }\n return {\n status: StepStatus.FAILED,\n exitCode: 'FAILED',\n exitMessage: finalError instanceof Error ? finalError.message : String(finalError),\n readCount,\n writeCount,\n skipCount,\n commitCount,\n };\n }\n }\n\n private async updateStreams(\n streams: readonly ItemStream[],\n ctx: ExecutionContext,\n ): Promise<ExecutionContext> {\n let next = ctx;\n for (const stream of streams) {\n const updated = await stream.update(next);\n if (updated !== undefined) {\n next = updated;\n }\n }\n return next;\n }\n\n private async closeStreams(streams: readonly ItemStream[]): Promise<void> {\n for (let i = streams.length - 1; i >= 0; i--) {\n await streams[i]!.close();\n }\n }\n\n /**\n * Run a single per-phase operation (one read, one process, or one write) with\n * skip/retry policy consultation. Throws when the operation cannot be\n * skipped (either non-skippable error or skip-limit exceeded) or when\n * retries are exhausted.\n *\n * The contract on budget accounting:\n * - The caller's `skipCount` accumulator is consulted *before* a skip is\n * honored. If the post-skip count would exceed the limit, the error\n * becomes a `SkipLimitExceededError` instead of a skip.\n * - The caller's `onSkip` callback is responsible for incrementing the\n * skipCount; it runs only when a skip is actually honored.\n */\n private async runPhase<T>(\n op: () => Promise<T>,\n options: RunPhaseOptions,\n ): Promise<PhaseResult<T>> {\n let attempt = 1;\n // Outer safety cap: when a retry policy exists, allow many iterations;\n // the policy's `canRetry` is the actual gate. When no retry policy,\n // exactly one attempt.\n const outerCap = options.retryPolicy ? 999 : 1;\n\n while (attempt <= outerCap) {\n try {\n const value = await op();\n return { kind: 'ok', value };\n } catch (err) {\n // 1) Skip consultation: is this error skippable, and is there budget?\n if (options.skipPolicy) {\n // Use the policy's `shouldSkip` with `skipCount: 0` to get a pure\n // membership check (the policy's own budget gate is bypassed).\n // We apply the budget ourselves with the caller's live accounting.\n const membership: SkipContext = {\n item: options.item,\n phase: options.phase,\n skipCount: 0,\n skipLimit: options.skipLimit,\n };\n if (options.skipPolicy.shouldSkip(err, membership)) {\n // Error is in the skippable list. Now check the budget: would\n // honoring this skip exceed the limit?\n const projected = options.getSkipCount() + 1;\n if (projected > options.skipLimit) {\n throw new SkipLimitExceededError(options.skipLimit);\n }\n await options.onSkip(err);\n return { kind: 'skipped' };\n }\n // Not in the skippable list — fall through to retry/throw.\n }\n\n // 2) Retry consultation\n if (options.retryPolicy) {\n const retryCtx: RetryContext = {\n item: options.item,\n phase: options.phase,\n attempt,\n retryLimit: options.retryLimit,\n };\n if (options.retryPolicy.canRetry(err, retryCtx)) {\n const ms = options.retryPolicy.backoffMs(attempt);\n if (ms > 0) await new Promise((r) => setTimeout(r, ms));\n attempt += 1;\n continue;\n }\n // canRetry returned false. Distinguish \"exhausted\" from \"not\n // retryable\" by re-checking membership with an effectively\n // infinite budget.\n if (attempt > options.retryLimit) {\n const isRetryable = options.retryPolicy.canRetry(err, {\n item: options.item,\n phase: options.phase,\n attempt: 1,\n retryLimit: Number.POSITIVE_INFINITY,\n });\n if (isRetryable) {\n throw new RetryLimitExceededError(options.retryLimit);\n }\n }\n }\n\n // 3) Neither skippable nor retryable: re-throw the original error.\n throw err;\n }\n }\n\n // Defensive: the outer cap should never be reached when a retry policy\n // gates us, but if no retry policy exists and the very first attempt\n // somehow re-entered the loop, fall through with a clear failure.\n throw new Error(\n `ChunkStepExecutor: phase \"${options.phase}\" exhausted attempts without a retry policy`,\n );\n }\n\n private resolveReader(ref: ReaderRef, context: ChunkExecutionContext): ItemReader {\n if (ref.kind === RefKind.BuilderLambda && ref.fn) {\n const result = ref.fn();\n if (typeof result === 'function') {\n return { read: result as ItemReader['read'] };\n }\n if (result !== null && typeof result === 'object' && typeof (result as ItemReader).read === 'function') {\n return result as ItemReader;\n }\n return { read: ref.fn as ItemReader['read'] };\n }\n if (ref.kind === RefKind.ProviderToken) {\n return resolveProviderToken<ItemReader>('reader', ref, context.providerResolvers);\n }\n if (ref.kind === RefKind.Method && ref.classToken && ref.methodName) {\n const key = `${context.jobExecutionId2}::reader::${ref.classToken}::${ref.methodName}`;\n const fn = context.resolvers.get(key);\n if (!fn) throw new Error(`Reader not resolved: ${ref.classToken}.${ref.methodName}`);\n return { read: fn as ItemReader['read'] };\n }\n throw new Error(`Unsupported reader ref kind: ${ref.kind}`);\n }\n\n private resolveProcessor(ref: ProcessorRef, context: ChunkExecutionContext): ItemProcessor {\n if (ref.kind === RefKind.BuilderLambda && ref.fn) {\n const result = ref.fn();\n if (typeof result === 'function') {\n return { process: result as ItemProcessor['process'] };\n }\n if (result !== null && typeof result === 'object' && typeof (result as ItemProcessor).process === 'function') {\n return result as ItemProcessor;\n }\n return { process: ref.fn as ItemProcessor['process'] };\n }\n if (ref.kind === RefKind.ProviderToken) {\n return resolveProviderToken<ItemProcessor>('processor', ref, context.providerResolvers);\n }\n if (ref.kind === RefKind.Method && ref.classToken && ref.methodName) {\n const key = `${context.jobExecutionId2}::processor::${ref.classToken}::${ref.methodName}`;\n const fn = context.resolvers.get(key);\n if (!fn) throw new Error(`Processor not resolved: ${ref.classToken}.${ref.methodName}`);\n return { process: fn as ItemProcessor['process'] };\n }\n throw new Error(`Unsupported processor ref kind: ${ref.kind}`);\n }\n\n private resolveWriter(ref: WriterRef, context: ChunkExecutionContext): ItemWriter {\n if (ref.kind === RefKind.BuilderLambda && ref.fn) {\n const result = ref.fn();\n if (typeof result === 'function') {\n return { write: result as ItemWriter['write'] };\n }\n if (result !== null && typeof result === 'object' && typeof (result as ItemWriter).write === 'function') {\n return result as ItemWriter;\n }\n return { write: ref.fn as ItemWriter['write'] };\n }\n if (ref.kind === RefKind.ProviderToken) {\n return resolveProviderToken<ItemWriter>('writer', ref, context.providerResolvers);\n }\n if (ref.kind === RefKind.Method && ref.classToken && ref.methodName) {\n const key = `${context.jobExecutionId2}::writer::${ref.classToken}::${ref.methodName}`;\n const fn = context.resolvers.get(key);\n if (!fn) throw new Error(`Writer not resolved: ${ref.classToken}.${ref.methodName}`);\n return { write: fn as ItemWriter['write'] };\n }\n throw new Error(`Unsupported writer ref kind: ${ref.kind}`);\n }\n}\n"],"names":["ChunkStepExecutor","isItemStream","value","stream","open","update","close","withLastChunkIndex","ctx","chunkIndex","current","data","Array","isArray","lastChunkIndex","version","execute","step","context","skipPolicy","compileSkipPolicy","retryPolicy","compileRetryPolicy","skipResolvers","skipListenerResolvers","Map","skipLimit","limit","retryLimit","partition","partitionIndex","undefined","partitionCount","enforcePartitionIndex","partitions","range","from","to","Error","readCount","writeCount","skipCount","commitCount","openedStreams","streamContext","reader","resolveReader","processor","resolveProcessor","writer","resolveWriter","streams","push","jobRepository","getExecutionContext","stepExecutionId","resumeFromChunkIndex","drained","skipCap","Math","max","chunkSize","i","item","read","items","eof","remaining","r","runPhase","phase","getSkipCount","onSkip","err","listenerInvoker","invokeOnSkipRead","kind","length","processed","process","invokeOnSkipProcess","transactionManager","withTransaction","write","invokeOnSkipWrite","written","skipped","updateStreams","saveExecutionContext","streamsToClose","closeStreams","status","StepStatus","COMPLETED","exitCode","exitMessage","finalError","closeErr","FAILED","message","String","next","updated","op","options","attempt","outerCap","membership","shouldSkip","projected","SkipLimitExceededError","retryCtx","canRetry","ms","backoffMs","Promise","setTimeout","isRetryable","Number","POSITIVE_INFINITY","RetryLimitExceededError","ref","RefKind","BuilderLambda","fn","result","ProviderToken","resolveProviderToken","providerResolvers","Method","classToken","methodName","key","jobExecutionId2","resolvers","get"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/execution/chunk-step-executor.ts"],"sourcesContent":["import { Injectable } from '@nestjs/common';\nimport type { ChunkStepDefinition, ReaderRef, ProcessorRef, WriterRef } from '../core/ir';\nimport { RefKind } from '../core/ir';\nimport type {\n ItemExecutionContext,\n ItemReader,\n ItemProcessor,\n ItemStream,\n ItemWriter,\n} from '../core/item';\nimport type { ExecutionContext, JobParameters, JobRepository } from '../core/repository';\nimport type { TransactionManager } from '../core/transaction';\nimport { StepStatus } from '../core/status';\nimport type { SkipPolicy, SkipContext } from '../policies/skip-policy';\nimport type { RetryPolicy, RetryContext } from '../policies/retry-policy';\nimport { compileSkipPolicy } from '../policies/skip-policy';\nimport { compileRetryPolicy } from '../policies/retry-policy';\nimport { SkipLimitExceededError, RetryLimitExceededError } from '../core/errors';\nimport { enforcePartitionIndex } from '../partition-helpers';\nimport type { ResolverMap } from './listener-invoker';\nimport { ListenerInvoker } from './listener-invoker';\nimport { resolveProviderToken, type ProviderResolvers } from './ref-resolver';\n\nexport interface ChunkExecutionContext {\n jobExecutionId: string;\n /** Step execution id, used to scope the chunk-progress checkpoint in the\n * step's ExecutionContext (saved as `{ lastChunkIndex }`). */\n stepExecutionId: string;\n stepName?: string;\n jobParameters?: JobParameters;\n jobRepository: JobRepository;\n transactionManager: TransactionManager;\n listenerInvoker: ListenerInvoker;\n /** Full listener resolver map keyed by `${phase}:${kind}:${name}`. */\n listenerResolvers?: ResolverMap;\n /** Map of resolved reader/processor/writer functions by name. */\n resolvers: Map<string, (...args: unknown[]) => unknown | Promise<unknown>>;\n jobExecutionId2: string; // unique key for listener resolver namespacing\n /** Optional skip listener resolver map. Keys follow the `on-skip:<kind>:<name>` shape. */\n skipListenerResolvers?: ResolverMap;\n /**\n * Optional map of provider-token id → already-resolved provider instance.\n * Populated by the JobExecutor (or test fixtures) so that\n * `RefKind.ProviderToken` refs on the reader/processor/writer slots can\n * be looked up without coupling the executor to the Nest DI container.\n */\n providerResolvers?: ProviderResolvers;\n /**\n * When set, the executor skips any chunk whose 0-based index is less\n * than or equal to this value. The reader is still advanced by\n * `chunkSize` calls per skipped chunk (so the data stream position\n * stays correct), but no read/process/write happens and `commitCount`\n * is not incremented. The checkpoint value is loaded from the prior\n * FAILED step execution's ExecutionContext by `JobExecutor`.\n */\n resumeFromChunkIndex?: number;\n /**\n * Partition ordinal for this chunk execution. Set by the transport\n * (BullMQ / Kafka) when the step declares `partitions.count >= 2`;\n * `undefined` means \"non-partitioned\" (the 0.1.0 path). The executor\n * uses this together with `partitionCount` and the step's\n * `partitions.range` to bound the read loop to the partition's\n * `[from, to)` range — see `packages/core/src/partition-helpers.ts`\n * and `docs/RELEASE-0.2.0.md §6`.\n */\n partitionIndex?: number;\n /**\n * Mirror of the step's `partitions.count`. Carried on the context\n * (rather than read from the step) so the executor does not have to\n * reason about which step is being executed when it consults the\n * partition range.\n */\n partitionCount?: number;\n}\n\nexport interface ChunkExecutionResult {\n status: StepStatus;\n exitCode: string;\n exitMessage: string;\n readCount: number;\n writeCount: number;\n skipCount: number;\n commitCount: number;\n}\n\n/** Phase tag used by skip/retry policies. Mirrors the union in policy modules. */\ntype Phase = 'read' | 'process' | 'write';\n\n/**\n * Outcome of a single per-phase attempt after skip/retry consultation.\n * - `ok` — the operation succeeded; `value` is the op's return value.\n * - `skipped` — the error was skippable (and within budget); the op was\n * abandoned, the on-skip listener was invoked, and `value` is\n * `undefined`.\n */\ntype PhaseResult<T> = { kind: 'ok'; value: T } | { kind: 'skipped' };\n\n/**\n * Options for `runPhase`. `getSkipCount` is a closure over the executor's\n * live `skipCount` accumulator so the policy's budget check is consistent\n * with the accounting in the outer loop.\n */\ninterface RunPhaseOptions<T = unknown> {\n phase: Phase;\n item: unknown;\n skipPolicy: SkipPolicy | null;\n retryPolicy: RetryPolicy | null;\n skipLimit: number;\n retryLimit: number;\n /** Live read of the executor's skipCount accumulator. */\n getSkipCount: () => number;\n /** Invoked when an error is actually skipped (within budget). The caller\n * is responsible for incrementing its own skipCount here. */\n onSkip: (err: unknown) => Promise<void>;\n before?: () => Promise<void>;\n after?: (value: T) => Promise<void>;\n onError?: (err: unknown) => Promise<void>;\n}\n\nfunction isItemStream(value: unknown): value is ItemStream {\n if (value === null || typeof value !== 'object') return false;\n const stream = value as Partial<ItemStream>;\n return (\n typeof stream.open === 'function' &&\n typeof stream.update === 'function' &&\n typeof stream.close === 'function'\n );\n}\n\nfunction withLastChunkIndex(ctx: ExecutionContext, chunkIndex: number): ExecutionContext {\n const current = ctx.data;\n const data =\n current !== null && typeof current === 'object' && !Array.isArray(current)\n ? { ...(current as Record<string, unknown>), lastChunkIndex: chunkIndex }\n : { lastChunkIndex: chunkIndex };\n\n return {\n data: data as ExecutionContext['data'],\n version: ctx.version,\n };\n}\n\n@Injectable()\nexport class ChunkStepExecutor {\n async execute(\n step: ChunkStepDefinition,\n context: ChunkExecutionContext,\n ): Promise<ChunkExecutionResult> {\n const skipPolicy = step.skipPolicy ? compileSkipPolicy(step.skipPolicy) : null;\n const retryPolicy = step.retryPolicy ? compileRetryPolicy(step.retryPolicy) : null;\n const listenerResolvers: ResolverMap = context.listenerResolvers ?? new Map();\n const skipResolvers: ResolverMap = context.skipListenerResolvers ?? listenerResolvers;\n\n const skipLimit = step.skipPolicy?.limit ?? 0;\n const retryLimit = step.retryPolicy?.limit ?? 0;\n\n // Partition routing (T8): when the transport attached a\n // `partitionIndex` / `partitionCount` pair to this execution, the\n // step's `partitions.range` (or, when absent, the runtime\n // \"read until EOF\" default) bounds the chunk loop. We resolve the\n // range once up-front so the hot loop does not re-invoke the\n // user's resolver.\n //\n // The resolve helper throws when the index is out of range; we\n // surface the violation as a FAILED step (caught by the outer\n // try/catch) rather than a propagated throw, matching the\n // chunk executor's \"errors become FAILED step\" contract.\n let partition: { from: number; to: number } | null = null;\n if (context.partitionIndex !== undefined && context.partitionCount !== undefined) {\n // Defensive: enforce the index range here too. The runtime\n // service validates at enqueue time, but the chunk executor is\n // also a public surface (tests drive it directly), so a\n // double-check costs almost nothing and pins the contract.\n enforcePartitionIndex(context.partitionIndex, context.partitionCount);\n if (step.partitions?.range === undefined) {\n // No `range` resolver: each partition reads from the start of\n // the input. This is the documented \"default\" behaviour in\n // `docs/RELEASE-0.2.0.md §6.1` — the host's reader is\n // responsible for not over-processing. We still cap the read\n // loop to the partition's \"share\" via a sentinel that the\n // hot loop checks.\n partition = null;\n } else {\n const range = step.partitions.range(context.partitionIndex, context.partitionCount);\n const [from, to] = range;\n if (from < 0 || to < from) {\n throw new Error(\n `[ChunkStepExecutor] partitions.range(${context.partitionIndex}, ` +\n `${context.partitionCount}) returned invalid [${from}, ${to})`,\n );\n }\n partition = { from, to };\n }\n }\n\n let readCount = 0;\n let writeCount = 0;\n let skipCount = 0;\n let commitCount = 0;\n // 0-based index of the chunk currently being assembled. Used by the\n // restart path to decide which chunks to skip and where to record\n // the last-committed checkpoint.\n let chunkIndex = 0;\n let openedStreams: ItemStream[] = [];\n let streamContext: ExecutionContext | null = null;\n\n await context.listenerInvoker.invokeBefore(\n listenerResolvers,\n 'step',\n this.buildListenerContext(step, context),\n );\n\n try {\n // Resolve inside the try block so a missing provider-token ref\n // surfaces as FAILED/{exitMessage: <err>}, matching the tasklet\n // executor's contract — not as a propagated throw.\n const reader = this.resolveReader(step.reader, context);\n const processor = step.processor ? this.resolveProcessor(step.processor, context) : null;\n const writer = this.resolveWriter(step.writer, context);\n\n const streams: ItemStream[] = [];\n if (isItemStream(reader)) streams.push(reader);\n if (isItemStream(writer)) streams.push(writer);\n streamContext = await context.jobRepository.getExecutionContext({\n stepExecutionId: context.stepExecutionId,\n });\n for (const stream of streams) {\n await stream.open(streamContext);\n openedStreams.push(stream);\n }\n\n // Outer loop: keep reading chunks until reader returns null\n // eslint-disable-next-line no-constant-condition\n while (true) {\n // ---- SKIP PHASE (restart-only): drain up to chunkSize items so\n // the reader advances past already-committed chunks, but skip\n // the read/process/write pipeline entirely. commitCount is not\n // incremented (those chunks were already counted in the prior\n // run) and no checkpoint is written (the prior run owns it).\n if (\n context.resumeFromChunkIndex !== undefined &&\n chunkIndex <= context.resumeFromChunkIndex\n ) {\n let drained = 0;\n // Cap the restart skip-drain by the partition's remaining\n // budget too (T8). A restart on a partitioned step is rare\n // but possible — without the cap, the skip-drain could\n // pull items the partition's range is not allowed to\n // touch, leaving the chunk pipeline reading the wrong\n // slice on the next iteration.\n const skipCap =\n partition !== null\n ? Math.max(0, partition.to - partition.from - readCount)\n : step.chunkSize;\n if (skipCap === 0) break;\n const itemContext = this.buildItemContext(step, context, chunkIndex);\n for (let i = 0; i < skipCap; i++) {\n const item = await reader.read(itemContext);\n if (item == null) break;\n drained += 1;\n }\n if (drained === 0) break; // EOF reached while skipping\n chunkIndex += 1;\n continue;\n }\n\n const items: unknown[] = [];\n let eof = false;\n\n // ---- PARTITION CAP (T8) ----\n // When the transport attached a partition's [from, to) range,\n // bound this chunk's read loop so the partition never\n // overshoots. `readCount` is the running count of items the\n // partition has read so far; `partition.to - partition.from`\n // is the partition's total budget. The cap is `min(chunkSize,\n // remaining)` and `0` is a valid \"no more items\" cap (we\n // exit the outer loop on the next iteration).\n const remaining =\n partition !== null\n ? Math.max(0, partition.to - partition.from - readCount)\n : step.chunkSize;\n if (remaining === 0) {\n // Partition is fully drained. Exit the outer loop so the\n // chunk executor returns COMPLETED with the partition's\n // commitCount + readCount + writeCount.\n break;\n }\n\n const itemContext = this.buildItemContext(step, context, chunkIndex);\n const listenerContext = this.buildListenerContext(step, context, { chunkIndex });\n\n await context.listenerInvoker.invokeBefore(listenerResolvers, 'chunk', listenerContext);\n\n // ---- READ PHASE: per-item retry+skip ----\n for (let i = 0; i < remaining && !eof; i++) {\n const r = await this.runPhase<unknown>(() => reader.read(itemContext), {\n phase: 'read',\n item: null,\n skipPolicy,\n retryPolicy,\n skipLimit,\n retryLimit,\n getSkipCount: () => skipCount,\n onSkip: async (err) => {\n skipCount += 1;\n await context.listenerInvoker.invokeOnSkipRead(skipResolvers, err, null);\n },\n before: async () => {\n await context.listenerInvoker.invokeBeforeRead(listenerResolvers, listenerContext);\n },\n after: async (item) => {\n if (item !== null && item !== undefined) {\n await context.listenerInvoker.invokeAfterRead(\n listenerResolvers,\n item,\n listenerContext,\n );\n }\n },\n onError: async (err) => {\n await context.listenerInvoker.invokeOnReadError(\n listenerResolvers,\n err,\n listenerContext,\n );\n },\n });\n if (r.kind === 'skipped') continue;\n if (r.value == null) {\n // Natural EOF from the reader\n eof = true;\n break;\n }\n items.push(r.value);\n readCount += 1;\n }\n if (items.length === 0) {\n await context.listenerInvoker.invokeAfter(listenerResolvers, 'chunk', listenerContext, {\n status: StepStatus.COMPLETED,\n readCount,\n writeCount,\n skipCount,\n commitCount,\n });\n break; // EOF (either before first read or after skips)\n }\n\n // ---- PROCESS PHASE: per-item retry+skip ----\n const processed: unknown[] = [];\n for (const item of items) {\n if (!processor) {\n processed.push(item);\n continue;\n }\n const r = await this.runPhase<unknown>(() => processor.process(item, itemContext), {\n phase: 'process',\n item,\n skipPolicy,\n retryPolicy,\n skipLimit,\n retryLimit,\n getSkipCount: () => skipCount,\n onSkip: async (err) => {\n skipCount += 1;\n await context.listenerInvoker.invokeOnSkipProcess(skipResolvers, item, err);\n },\n before: async () => {\n await context.listenerInvoker.invokeBeforeProcess(\n listenerResolvers,\n item,\n listenerContext,\n );\n },\n after: async (value) => {\n await context.listenerInvoker.invokeAfterProcess(\n listenerResolvers,\n item,\n value,\n listenerContext,\n );\n },\n onError: async (err) => {\n await context.listenerInvoker.invokeOnProcessError(\n listenerResolvers,\n item,\n err,\n listenerContext,\n );\n },\n });\n if (r.kind === 'skipped') continue;\n if (r.value !== null && r.value !== undefined) {\n processed.push(r.value);\n } else {\n // Processor filtered the item out (returned null/undefined) — not a\n // skip-policy skip, but counts as a skip for accounting parity.\n skipCount += 1;\n }\n }\n\n // ---- WRITE PHASE: per-chunk retry+skip, wrapped in transaction ----\n if (processed.length > 0) {\n const r = await this.runPhase<{ written: number; skipped: number } | void>(\n () =>\n context.transactionManager.withTransaction(async () => {\n return writer.write(processed, itemContext);\n }),\n {\n phase: 'write',\n item: processed,\n skipPolicy,\n retryPolicy,\n skipLimit,\n retryLimit,\n getSkipCount: () => skipCount,\n onSkip: async (err) => {\n skipCount += 1;\n await context.listenerInvoker.invokeOnSkipWrite(skipResolvers, processed, err);\n },\n before: async () => {\n await context.listenerInvoker.invokeBeforeWrite(\n listenerResolvers,\n processed,\n listenerContext,\n );\n },\n after: async (value) => {\n await context.listenerInvoker.invokeAfterWrite(\n listenerResolvers,\n processed,\n value,\n listenerContext,\n );\n },\n onError: async (err) => {\n await context.listenerInvoker.invokeOnWriteError(\n listenerResolvers,\n processed,\n err,\n listenerContext,\n );\n },\n },\n );\n if (r.kind === 'ok') {\n if (r.value) {\n writeCount += r.value.written;\n skipCount += r.value.skipped;\n } else {\n writeCount += processed.length;\n }\n }\n // If the write was skipped, writeCount is not incremented and the\n // chunk is still considered \"committed\" (commitCount++) so that\n // progress tracking reflects that we moved past it.\n }\n\n // Persist the last-committed-chunk checkpoint in the step's\n // ExecutionContext. The next restart will read this value and\n // skip every chunk with index ≤ `lastChunkIndex`. The save is\n // intentionally outside the per-chunk transaction: the\n // checkpoint reflects \"we successfully moved past this chunk\",\n // and we only reach this line after the write completed\n // (either OK or skipped — both are forward progress).\n streamContext = withLastChunkIndex(streamContext, chunkIndex);\n streamContext = await this.updateStreams(openedStreams, streamContext);\n await context.jobRepository.saveExecutionContext(\n { stepExecutionId: context.stepExecutionId },\n streamContext,\n );\n streamContext = await context.jobRepository.getExecutionContext({\n stepExecutionId: context.stepExecutionId,\n });\n\n await context.listenerInvoker.invokeAfter(listenerResolvers, 'chunk', listenerContext, {\n status: StepStatus.COMPLETED,\n readCount,\n writeCount,\n skipCount,\n commitCount: commitCount + 1,\n });\n\n commitCount += 1;\n chunkIndex += 1;\n }\n\n const streamsToClose = openedStreams;\n openedStreams = [];\n await this.closeStreams(streamsToClose);\n\n const result: ChunkExecutionResult = {\n status: StepStatus.COMPLETED,\n exitCode: 'COMPLETED',\n exitMessage: '',\n readCount,\n writeCount,\n skipCount,\n commitCount,\n };\n await context.listenerInvoker.invokeAfter(\n listenerResolvers,\n 'step',\n this.buildListenerContext(step, context),\n result,\n );\n return result;\n } catch (err) {\n let finalError = err;\n const streamsToClose = openedStreams;\n openedStreams = [];\n try {\n await this.closeStreams(streamsToClose);\n } catch (closeErr) {\n finalError = closeErr;\n }\n await context.listenerInvoker.invokeOnError(\n listenerResolvers,\n 'chunk',\n this.buildListenerContext(step, context, { chunkIndex }),\n finalError,\n );\n const result: ChunkExecutionResult = {\n status: StepStatus.FAILED,\n exitCode: 'FAILED',\n exitMessage: finalError instanceof Error ? finalError.message : String(finalError),\n readCount,\n writeCount,\n skipCount,\n commitCount,\n };\n await context.listenerInvoker.invokeOnError(\n listenerResolvers,\n 'step',\n this.buildListenerContext(step, context),\n finalError,\n );\n await context.listenerInvoker.invokeAfter(\n listenerResolvers,\n 'step',\n this.buildListenerContext(step, context),\n result,\n );\n return result;\n }\n }\n\n private buildListenerContext(\n step: ChunkStepDefinition,\n context: ChunkExecutionContext,\n extra: Record<string, unknown> = {},\n ): {\n jobExecutionId: string;\n stepExecutionId: string;\n stepName: string;\n jobParameters: JobParameters;\n [extra: string]: unknown;\n } {\n return {\n jobExecutionId: context.jobExecutionId,\n stepExecutionId: context.stepExecutionId,\n stepName: context.stepName ?? step.id,\n jobParameters: context.jobParameters ?? {},\n ...extra,\n };\n }\n\n private buildItemContext(\n step: ChunkStepDefinition,\n context: ChunkExecutionContext,\n chunkIndex: number,\n ): ItemExecutionContext {\n return {\n jobExecutionId: context.jobExecutionId,\n stepExecutionId: context.stepExecutionId,\n stepName: context.stepName ?? step.id,\n jobParameters: context.jobParameters ?? {},\n chunkIndex,\n getExecutionContext: async () =>\n context.jobRepository.getExecutionContext({ stepExecutionId: context.stepExecutionId }),\n saveExecutionContext: async (ctx: ExecutionContext) => {\n await context.jobRepository.saveExecutionContext(\n { stepExecutionId: context.stepExecutionId },\n ctx,\n );\n },\n };\n }\n\n private async updateStreams(\n streams: readonly ItemStream[],\n ctx: ExecutionContext,\n ): Promise<ExecutionContext> {\n let next = ctx;\n for (const stream of streams) {\n const updated = await stream.update(next);\n if (updated !== undefined) {\n next = updated;\n }\n }\n return next;\n }\n\n private async closeStreams(streams: readonly ItemStream[]): Promise<void> {\n for (let i = streams.length - 1; i >= 0; i--) {\n await streams[i]!.close();\n }\n }\n\n /**\n * Run a single per-phase operation (one read, one process, or one write) with\n * skip/retry policy consultation. Throws when the operation cannot be\n * skipped (either non-skippable error or skip-limit exceeded) or when\n * retries are exhausted.\n *\n * The contract on budget accounting:\n * - The caller's `skipCount` accumulator is consulted *before* a skip is\n * honored. If the post-skip count would exceed the limit, the error\n * becomes a `SkipLimitExceededError` instead of a skip.\n * - The caller's `onSkip` callback is responsible for incrementing the\n * skipCount; it runs only when a skip is actually honored.\n */\n private async runPhase<T>(\n op: () => Promise<T>,\n options: RunPhaseOptions<T>,\n ): Promise<PhaseResult<T>> {\n let attempt = 1;\n // Outer safety cap: when a retry policy exists, allow many iterations;\n // the policy's `canRetry` is the actual gate. When no retry policy,\n // exactly one attempt.\n const outerCap = options.retryPolicy ? 999 : 1;\n\n while (attempt <= outerCap) {\n if (options.before) await options.before();\n let value: T;\n try {\n value = await op();\n } catch (err) {\n if (options.onError) await options.onError(err);\n // 1) Skip consultation: is this error skippable, and is there budget?\n if (options.skipPolicy) {\n // Use the policy's `shouldSkip` with `skipCount: 0` to get a pure\n // membership check (the policy's own budget gate is bypassed).\n // We apply the budget ourselves with the caller's live accounting.\n const membership: SkipContext = {\n item: options.item,\n phase: options.phase,\n skipCount: 0,\n skipLimit: options.skipLimit,\n };\n if (options.skipPolicy.shouldSkip(err, membership)) {\n // Error is in the skippable list. Now check the budget: would\n // honoring this skip exceed the limit?\n const projected = options.getSkipCount() + 1;\n if (projected > options.skipLimit) {\n throw new SkipLimitExceededError(options.skipLimit);\n }\n await options.onSkip(err);\n return { kind: 'skipped' };\n }\n // Not in the skippable list — fall through to retry/throw.\n }\n\n // 2) Retry consultation\n if (options.retryPolicy) {\n const retryCtx: RetryContext = {\n item: options.item,\n phase: options.phase,\n attempt,\n retryLimit: options.retryLimit,\n };\n if (options.retryPolicy.canRetry(err, retryCtx)) {\n const ms = options.retryPolicy.backoffMs(attempt);\n if (ms > 0) await new Promise((r) => setTimeout(r, ms));\n attempt += 1;\n continue;\n }\n // canRetry returned false. Distinguish \"exhausted\" from \"not\n // retryable\" by re-checking membership with an effectively\n // infinite budget.\n if (attempt > options.retryLimit) {\n const isRetryable = options.retryPolicy.canRetry(err, {\n item: options.item,\n phase: options.phase,\n attempt: 1,\n retryLimit: Number.POSITIVE_INFINITY,\n });\n if (isRetryable) {\n throw new RetryLimitExceededError(options.retryLimit);\n }\n }\n }\n\n // 3) Neither skippable nor retryable: re-throw the original error.\n throw err;\n }\n if (options.after) await options.after(value);\n return { kind: 'ok', value };\n }\n\n // Defensive: the outer cap should never be reached when a retry policy\n // gates us, but if no retry policy exists and the very first attempt\n // somehow re-entered the loop, fall through with a clear failure.\n throw new Error(\n `ChunkStepExecutor: phase \"${options.phase}\" exhausted attempts without a retry policy`,\n );\n }\n\n private resolveReader(ref: ReaderRef, context: ChunkExecutionContext): ItemReader {\n if (ref.kind === RefKind.BuilderLambda && ref.fn) {\n const result = ref.fn();\n if (typeof result === 'function') {\n return { read: result as ItemReader['read'] };\n }\n if (\n result !== null &&\n typeof result === 'object' &&\n typeof (result as ItemReader).read === 'function'\n ) {\n return result as ItemReader;\n }\n return { read: ref.fn as ItemReader['read'] };\n }\n if (ref.kind === RefKind.ProviderToken) {\n return resolveProviderToken<ItemReader>('reader', ref, context.providerResolvers);\n }\n if (ref.kind === RefKind.Method && ref.classToken && ref.methodName) {\n const key = `${context.jobExecutionId2}::reader::${ref.classToken}::${ref.methodName}`;\n const fn = context.resolvers.get(key);\n if (!fn) throw new Error(`Reader not resolved: ${ref.classToken}.${ref.methodName}`);\n return { read: fn as ItemReader['read'] };\n }\n throw new Error(`Unsupported reader ref kind: ${ref.kind}`);\n }\n\n private resolveProcessor(ref: ProcessorRef, context: ChunkExecutionContext): ItemProcessor {\n if (ref.kind === RefKind.BuilderLambda && ref.fn) {\n const result = ref.fn();\n if (typeof result === 'function') {\n return { process: result as ItemProcessor['process'] };\n }\n if (\n result !== null &&\n typeof result === 'object' &&\n typeof (result as ItemProcessor).process === 'function'\n ) {\n return result as ItemProcessor;\n }\n return { process: ref.fn as ItemProcessor['process'] };\n }\n if (ref.kind === RefKind.ProviderToken) {\n return resolveProviderToken<ItemProcessor>('processor', ref, context.providerResolvers);\n }\n if (ref.kind === RefKind.Method && ref.classToken && ref.methodName) {\n const key = `${context.jobExecutionId2}::processor::${ref.classToken}::${ref.methodName}`;\n const fn = context.resolvers.get(key);\n if (!fn) throw new Error(`Processor not resolved: ${ref.classToken}.${ref.methodName}`);\n return { process: fn as ItemProcessor['process'] };\n }\n throw new Error(`Unsupported processor ref kind: ${ref.kind}`);\n }\n\n private resolveWriter(ref: WriterRef, context: ChunkExecutionContext): ItemWriter {\n if (ref.kind === RefKind.BuilderLambda && ref.fn) {\n const result = ref.fn();\n if (typeof result === 'function') {\n return { write: result as ItemWriter['write'] };\n }\n if (\n result !== null &&\n typeof result === 'object' &&\n typeof (result as ItemWriter).write === 'function'\n ) {\n return result as ItemWriter;\n }\n return { write: ref.fn as ItemWriter['write'] };\n }\n if (ref.kind === RefKind.ProviderToken) {\n return resolveProviderToken<ItemWriter>('writer', ref, context.providerResolvers);\n }\n if (ref.kind === RefKind.Method && ref.classToken && ref.methodName) {\n const key = `${context.jobExecutionId2}::writer::${ref.classToken}::${ref.methodName}`;\n const fn = context.resolvers.get(key);\n if (!fn) throw new Error(`Writer not resolved: ${ref.classToken}.${ref.methodName}`);\n return { write: fn as ItemWriter['write'] };\n }\n throw new Error(`Unsupported writer ref kind: ${ref.kind}`);\n }\n}\n"],"names":["ChunkStepExecutor","isItemStream","value","stream","open","update","close","withLastChunkIndex","ctx","chunkIndex","current","data","Array","isArray","lastChunkIndex","version","execute","step","context","skipPolicy","compileSkipPolicy","retryPolicy","compileRetryPolicy","listenerResolvers","Map","skipResolvers","skipListenerResolvers","skipLimit","limit","retryLimit","partition","partitionIndex","undefined","partitionCount","enforcePartitionIndex","partitions","range","from","to","Error","readCount","writeCount","skipCount","commitCount","openedStreams","streamContext","listenerInvoker","invokeBefore","buildListenerContext","reader","resolveReader","processor","resolveProcessor","writer","resolveWriter","streams","push","jobRepository","getExecutionContext","stepExecutionId","resumeFromChunkIndex","drained","skipCap","Math","max","chunkSize","itemContext","buildItemContext","i","item","read","items","eof","remaining","listenerContext","r","runPhase","phase","getSkipCount","onSkip","err","invokeOnSkipRead","before","invokeBeforeRead","after","invokeAfterRead","onError","invokeOnReadError","kind","length","invokeAfter","status","StepStatus","COMPLETED","processed","process","invokeOnSkipProcess","invokeBeforeProcess","invokeAfterProcess","invokeOnProcessError","transactionManager","withTransaction","write","invokeOnSkipWrite","invokeBeforeWrite","invokeAfterWrite","invokeOnWriteError","written","skipped","updateStreams","saveExecutionContext","streamsToClose","closeStreams","result","exitCode","exitMessage","finalError","closeErr","invokeOnError","FAILED","message","String","extra","jobExecutionId","stepName","id","jobParameters","next","updated","op","options","attempt","outerCap","membership","shouldSkip","projected","SkipLimitExceededError","retryCtx","canRetry","ms","backoffMs","Promise","setTimeout","isRetryable","Number","POSITIVE_INFINITY","RetryLimitExceededError","ref","RefKind","BuilderLambda","fn","ProviderToken","resolveProviderToken","providerResolvers","Method","classToken","methodName","key","jobExecutionId2","resolvers","get"],"mappings":";;;;+BA+IaA;;;eAAAA;;;wBA/Ic;oBAEH;wBAUG;4BAGO;6BACC;wBAC6B;kCAC1B;6BAGuB;;;;;;;AAkG7D,SAASC,aAAaC,KAAc;IAClC,IAAIA,UAAU,QAAQ,OAAOA,UAAU,UAAU,OAAO;IACxD,MAAMC,SAASD;IACf,OACE,OAAOC,OAAOC,IAAI,KAAK,cACvB,OAAOD,OAAOE,MAAM,KAAK,cACzB,OAAOF,OAAOG,KAAK,KAAK;AAE5B;AAEA,SAASC,mBAAmBC,GAAqB,EAAEC,UAAkB;IACnE,MAAMC,UAAUF,IAAIG,IAAI;IACxB,MAAMA,OACJD,YAAY,QAAQ,OAAOA,YAAY,YAAY,CAACE,MAAMC,OAAO,CAACH,WAC9D;QAAE,GAAIA,OAAO;QAA8BI,gBAAgBL;IAAW,IACtE;QAAEK,gBAAgBL;IAAW;IAEnC,OAAO;QACLE,MAAMA;QACNI,SAASP,IAAIO,OAAO;IACtB;AACF;AAGO,IAAA,AAAMf,oBAAN,MAAMA;IACX,MAAMgB,QACJC,IAAyB,EACzBC,OAA8B,EACC;QAC/B,MAAMC,aAAaF,KAAKE,UAAU,GAAGC,IAAAA,6BAAiB,EAACH,KAAKE,UAAU,IAAI;QAC1E,MAAME,cAAcJ,KAAKI,WAAW,GAAGC,IAAAA,+BAAkB,EAACL,KAAKI,WAAW,IAAI;QAC9E,MAAME,oBAAiCL,QAAQK,iBAAiB,IAAI,IAAIC;QACxE,MAAMC,gBAA6BP,QAAQQ,qBAAqB,IAAIH;QAEpE,MAAMI,YAAYV,KAAKE,UAAU,EAAES,SAAS;QAC5C,MAAMC,aAAaZ,KAAKI,WAAW,EAAEO,SAAS;QAE9C,wDAAwD;QACxD,kEAAkE;QAClE,0DAA0D;QAC1D,kEAAkE;QAClE,6DAA6D;QAC7D,mBAAmB;QACnB,EAAE;QACF,+DAA+D;QAC/D,8DAA8D;QAC9D,0DAA0D;QAC1D,yDAAyD;QACzD,IAAIE,YAAiD;QACrD,IAAIZ,QAAQa,cAAc,KAAKC,aAAad,QAAQe,cAAc,KAAKD,WAAW;YAChF,2DAA2D;YAC3D,+DAA+D;YAC/D,wDAAwD;YACxD,2DAA2D;YAC3DE,IAAAA,uCAAqB,EAAChB,QAAQa,cAAc,EAAEb,QAAQe,cAAc;YACpE,IAAIhB,KAAKkB,UAAU,EAAEC,UAAUJ,WAAW;gBACxC,8DAA8D;gBAC9D,2DAA2D;gBAC3D,sDAAsD;gBACtD,6DAA6D;gBAC7D,0DAA0D;gBAC1D,mBAAmB;gBACnBF,YAAY;YACd,OAAO;gBACL,MAAMM,QAAQnB,KAAKkB,UAAU,CAACC,KAAK,CAAClB,QAAQa,cAAc,EAAEb,QAAQe,cAAc;gBAClF,MAAM,CAACI,MAAMC,GAAG,GAAGF;gBACnB,IAAIC,OAAO,KAAKC,KAAKD,MAAM;oBACzB,MAAM,IAAIE,MACR,CAAC,qCAAqC,EAAErB,QAAQa,cAAc,CAAC,EAAE,CAAC,GAChE,GAAGb,QAAQe,cAAc,CAAC,oBAAoB,EAAEI,KAAK,EAAE,EAAEC,GAAG,CAAC,CAAC;gBAEpE;gBACAR,YAAY;oBAAEO;oBAAMC;gBAAG;YACzB;QACF;QAEA,IAAIE,YAAY;QAChB,IAAIC,aAAa;QACjB,IAAIC,YAAY;QAChB,IAAIC,cAAc;QAClB,oEAAoE;QACpE,kEAAkE;QAClE,iCAAiC;QACjC,IAAIlC,aAAa;QACjB,IAAImC,gBAA8B,EAAE;QACpC,IAAIC,gBAAyC;QAE7C,MAAM3B,QAAQ4B,eAAe,CAACC,YAAY,CACxCxB,mBACA,QACA,IAAI,CAACyB,oBAAoB,CAAC/B,MAAMC;QAGlC,IAAI;YACF,+DAA+D;YAC/D,gEAAgE;YAChE,mDAAmD;YACnD,MAAM+B,SAAS,IAAI,CAACC,aAAa,CAACjC,KAAKgC,MAAM,EAAE/B;YAC/C,MAAMiC,YAAYlC,KAAKkC,SAAS,GAAG,IAAI,CAACC,gBAAgB,CAACnC,KAAKkC,SAAS,EAAEjC,WAAW;YACpF,MAAMmC,SAAS,IAAI,CAACC,aAAa,CAACrC,KAAKoC,MAAM,EAAEnC;YAE/C,MAAMqC,UAAwB,EAAE;YAChC,IAAItD,aAAagD,SAASM,QAAQC,IAAI,CAACP;YACvC,IAAIhD,aAAaoD,SAASE,QAAQC,IAAI,CAACH;YACvCR,gBAAgB,MAAM3B,QAAQuC,aAAa,CAACC,mBAAmB,CAAC;gBAC9DC,iBAAiBzC,QAAQyC,eAAe;YAC1C;YACA,KAAK,MAAMxD,UAAUoD,QAAS;gBAC5B,MAAMpD,OAAOC,IAAI,CAACyC;gBAClBD,cAAcY,IAAI,CAACrD;YACrB;YAEA,4DAA4D;YAC5D,iDAAiD;YACjD,MAAO,KAAM;gBACX,iEAAiE;gBACjE,8DAA8D;gBAC9D,+DAA+D;gBAC/D,8DAA8D;gBAC9D,6DAA6D;gBAC7D,IACEe,QAAQ0C,oBAAoB,KAAK5B,aACjCvB,cAAcS,QAAQ0C,oBAAoB,EAC1C;oBACA,IAAIC,UAAU;oBACd,0DAA0D;oBAC1D,2DAA2D;oBAC3D,uDAAuD;oBACvD,qDAAqD;oBACrD,sDAAsD;oBACtD,+BAA+B;oBAC/B,MAAMC,UACJhC,cAAc,OACViC,KAAKC,GAAG,CAAC,GAAGlC,UAAUQ,EAAE,GAAGR,UAAUO,IAAI,GAAGG,aAC5CvB,KAAKgD,SAAS;oBACpB,IAAIH,YAAY,GAAG;oBACnB,MAAMI,cAAc,IAAI,CAACC,gBAAgB,CAAClD,MAAMC,SAAST;oBACzD,IAAK,IAAI2D,IAAI,GAAGA,IAAIN,SAASM,IAAK;wBAChC,MAAMC,OAAO,MAAMpB,OAAOqB,IAAI,CAACJ;wBAC/B,IAAIG,QAAQ,MAAM;wBAClBR,WAAW;oBACb;oBACA,IAAIA,YAAY,GAAG,OAAO,6BAA6B;oBACvDpD,cAAc;oBACd;gBACF;gBAEA,MAAM8D,QAAmB,EAAE;gBAC3B,IAAIC,MAAM;gBAEV,+BAA+B;gBAC/B,8DAA8D;gBAC9D,sDAAsD;gBACtD,4DAA4D;gBAC5D,6DAA6D;gBAC7D,8DAA8D;gBAC9D,yDAAyD;gBACzD,8CAA8C;gBAC9C,MAAMC,YACJ3C,cAAc,OACViC,KAAKC,GAAG,CAAC,GAAGlC,UAAUQ,EAAE,GAAGR,UAAUO,IAAI,GAAGG,aAC5CvB,KAAKgD,SAAS;gBACpB,IAAIQ,cAAc,GAAG;oBAInB;gBACF;gBAEA,MAAMP,cAAc,IAAI,CAACC,gBAAgB,CAAClD,MAAMC,SAAST;gBACzD,MAAMiE,kBAAkB,IAAI,CAAC1B,oBAAoB,CAAC/B,MAAMC,SAAS;oBAAET;gBAAW;gBAE9E,MAAMS,QAAQ4B,eAAe,CAACC,YAAY,CAACxB,mBAAmB,SAASmD;gBAEvE,4CAA4C;gBAC5C,IAAK,IAAIN,IAAI,GAAGA,IAAIK,aAAa,CAACD,KAAKJ,IAAK;oBAC1C,MAAMO,IAAI,MAAM,IAAI,CAACC,QAAQ,CAAU,IAAM3B,OAAOqB,IAAI,CAACJ,cAAc;wBACrEW,OAAO;wBACPR,MAAM;wBACNlD;wBACAE;wBACAM;wBACAE;wBACAiD,cAAc,IAAMpC;wBACpBqC,QAAQ,OAAOC;4BACbtC,aAAa;4BACb,MAAMxB,QAAQ4B,eAAe,CAACmC,gBAAgB,CAACxD,eAAeuD,KAAK;wBACrE;wBACAE,QAAQ;4BACN,MAAMhE,QAAQ4B,eAAe,CAACqC,gBAAgB,CAAC5D,mBAAmBmD;wBACpE;wBACAU,OAAO,OAAOf;4BACZ,IAAIA,SAAS,QAAQA,SAASrC,WAAW;gCACvC,MAAMd,QAAQ4B,eAAe,CAACuC,eAAe,CAC3C9D,mBACA8C,MACAK;4BAEJ;wBACF;wBACAY,SAAS,OAAON;4BACd,MAAM9D,QAAQ4B,eAAe,CAACyC,iBAAiB,CAC7ChE,mBACAyD,KACAN;wBAEJ;oBACF;oBACA,IAAIC,EAAEa,IAAI,KAAK,WAAW;oBAC1B,IAAIb,EAAEzE,KAAK,IAAI,MAAM;wBACnB,8BAA8B;wBAC9BsE,MAAM;wBACN;oBACF;oBACAD,MAAMf,IAAI,CAACmB,EAAEzE,KAAK;oBAClBsC,aAAa;gBACf;gBACA,IAAI+B,MAAMkB,MAAM,KAAK,GAAG;oBACtB,MAAMvE,QAAQ4B,eAAe,CAAC4C,WAAW,CAACnE,mBAAmB,SAASmD,iBAAiB;wBACrFiB,QAAQC,kBAAU,CAACC,SAAS;wBAC5BrD;wBACAC;wBACAC;wBACAC;oBACF;oBACA,OAAO,gDAAgD;gBACzD;gBAEA,+CAA+C;gBAC/C,MAAMmD,YAAuB,EAAE;gBAC/B,KAAK,MAAMzB,QAAQE,MAAO;oBACxB,IAAI,CAACpB,WAAW;wBACd2C,UAAUtC,IAAI,CAACa;wBACf;oBACF;oBACA,MAAMM,IAAI,MAAM,IAAI,CAACC,QAAQ,CAAU,IAAMzB,UAAU4C,OAAO,CAAC1B,MAAMH,cAAc;wBACjFW,OAAO;wBACPR;wBACAlD;wBACAE;wBACAM;wBACAE;wBACAiD,cAAc,IAAMpC;wBACpBqC,QAAQ,OAAOC;4BACbtC,aAAa;4BACb,MAAMxB,QAAQ4B,eAAe,CAACkD,mBAAmB,CAACvE,eAAe4C,MAAMW;wBACzE;wBACAE,QAAQ;4BACN,MAAMhE,QAAQ4B,eAAe,CAACmD,mBAAmB,CAC/C1E,mBACA8C,MACAK;wBAEJ;wBACAU,OAAO,OAAOlF;4BACZ,MAAMgB,QAAQ4B,eAAe,CAACoD,kBAAkB,CAC9C3E,mBACA8C,MACAnE,OACAwE;wBAEJ;wBACAY,SAAS,OAAON;4BACd,MAAM9D,QAAQ4B,eAAe,CAACqD,oBAAoB,CAChD5E,mBACA8C,MACAW,KACAN;wBAEJ;oBACF;oBACA,IAAIC,EAAEa,IAAI,KAAK,WAAW;oBAC1B,IAAIb,EAAEzE,KAAK,KAAK,QAAQyE,EAAEzE,KAAK,KAAK8B,WAAW;wBAC7C8D,UAAUtC,IAAI,CAACmB,EAAEzE,KAAK;oBACxB,OAAO;wBACL,oEAAoE;wBACpE,gEAAgE;wBAChEwC,aAAa;oBACf;gBACF;gBAEA,sEAAsE;gBACtE,IAAIoD,UAAUL,MAAM,GAAG,GAAG;oBACxB,MAAMd,IAAI,MAAM,IAAI,CAACC,QAAQ,CAC3B,IACE1D,QAAQkF,kBAAkB,CAACC,eAAe,CAAC;4BACzC,OAAOhD,OAAOiD,KAAK,CAACR,WAAW5B;wBACjC,IACF;wBACEW,OAAO;wBACPR,MAAMyB;wBACN3E;wBACAE;wBACAM;wBACAE;wBACAiD,cAAc,IAAMpC;wBACpBqC,QAAQ,OAAOC;4BACbtC,aAAa;4BACb,MAAMxB,QAAQ4B,eAAe,CAACyD,iBAAiB,CAAC9E,eAAeqE,WAAWd;wBAC5E;wBACAE,QAAQ;4BACN,MAAMhE,QAAQ4B,eAAe,CAAC0D,iBAAiB,CAC7CjF,mBACAuE,WACApB;wBAEJ;wBACAU,OAAO,OAAOlF;4BACZ,MAAMgB,QAAQ4B,eAAe,CAAC2D,gBAAgB,CAC5ClF,mBACAuE,WACA5F,OACAwE;wBAEJ;wBACAY,SAAS,OAAON;4BACd,MAAM9D,QAAQ4B,eAAe,CAAC4D,kBAAkB,CAC9CnF,mBACAuE,WACAd,KACAN;wBAEJ;oBACF;oBAEF,IAAIC,EAAEa,IAAI,KAAK,MAAM;wBACnB,IAAIb,EAAEzE,KAAK,EAAE;4BACXuC,cAAckC,EAAEzE,KAAK,CAACyG,OAAO;4BAC7BjE,aAAaiC,EAAEzE,KAAK,CAAC0G,OAAO;wBAC9B,OAAO;4BACLnE,cAAcqD,UAAUL,MAAM;wBAChC;oBACF;gBACA,kEAAkE;gBAClE,gEAAgE;gBAChE,oDAAoD;gBACtD;gBAEA,4DAA4D;gBAC5D,8DAA8D;gBAC9D,8DAA8D;gBAC9D,uDAAuD;gBACvD,+DAA+D;gBAC/D,wDAAwD;gBACxD,sDAAsD;gBACtD5C,gBAAgBtC,mBAAmBsC,eAAepC;gBAClDoC,gBAAgB,MAAM,IAAI,CAACgE,aAAa,CAACjE,eAAeC;gBACxD,MAAM3B,QAAQuC,aAAa,CAACqD,oBAAoB,CAC9C;oBAAEnD,iBAAiBzC,QAAQyC,eAAe;gBAAC,GAC3Cd;gBAEFA,gBAAgB,MAAM3B,QAAQuC,aAAa,CAACC,mBAAmB,CAAC;oBAC9DC,iBAAiBzC,QAAQyC,eAAe;gBAC1C;gBAEA,MAAMzC,QAAQ4B,eAAe,CAAC4C,WAAW,CAACnE,mBAAmB,SAASmD,iBAAiB;oBACrFiB,QAAQC,kBAAU,CAACC,SAAS;oBAC5BrD;oBACAC;oBACAC;oBACAC,aAAaA,cAAc;gBAC7B;gBAEAA,eAAe;gBACflC,cAAc;YAChB;YAEA,MAAMsG,iBAAiBnE;YACvBA,gBAAgB,EAAE;YAClB,MAAM,IAAI,CAACoE,YAAY,CAACD;YAExB,MAAME,SAA+B;gBACnCtB,QAAQC,kBAAU,CAACC,SAAS;gBAC5BqB,UAAU;gBACVC,aAAa;gBACb3E;gBACAC;gBACAC;gBACAC;YACF;YACA,MAAMzB,QAAQ4B,eAAe,CAAC4C,WAAW,CACvCnE,mBACA,QACA,IAAI,CAACyB,oBAAoB,CAAC/B,MAAMC,UAChC+F;YAEF,OAAOA;QACT,EAAE,OAAOjC,KAAK;YACZ,IAAIoC,aAAapC;YACjB,MAAM+B,iBAAiBnE;YACvBA,gBAAgB,EAAE;YAClB,IAAI;gBACF,MAAM,IAAI,CAACoE,YAAY,CAACD;YAC1B,EAAE,OAAOM,UAAU;gBACjBD,aAAaC;YACf;YACA,MAAMnG,QAAQ4B,eAAe,CAACwE,aAAa,CACzC/F,mBACA,SACA,IAAI,CAACyB,oBAAoB,CAAC/B,MAAMC,SAAS;gBAAET;YAAW,IACtD2G;YAEF,MAAMH,SAA+B;gBACnCtB,QAAQC,kBAAU,CAAC2B,MAAM;gBACzBL,UAAU;gBACVC,aAAaC,sBAAsB7E,QAAQ6E,WAAWI,OAAO,GAAGC,OAAOL;gBACvE5E;gBACAC;gBACAC;gBACAC;YACF;YACA,MAAMzB,QAAQ4B,eAAe,CAACwE,aAAa,CACzC/F,mBACA,QACA,IAAI,CAACyB,oBAAoB,CAAC/B,MAAMC,UAChCkG;YAEF,MAAMlG,QAAQ4B,eAAe,CAAC4C,WAAW,CACvCnE,mBACA,QACA,IAAI,CAACyB,oBAAoB,CAAC/B,MAAMC,UAChC+F;YAEF,OAAOA;QACT;IACF;IAEQjE,qBACN/B,IAAyB,EACzBC,OAA8B,EAC9BwG,QAAiC,CAAC,CAAC,EAOnC;QACA,OAAO;YACLC,gBAAgBzG,QAAQyG,cAAc;YACtChE,iBAAiBzC,QAAQyC,eAAe;YACxCiE,UAAU1G,QAAQ0G,QAAQ,IAAI3G,KAAK4G,EAAE;YACrCC,eAAe5G,QAAQ4G,aAAa,IAAI,CAAC;YACzC,GAAGJ,KAAK;QACV;IACF;IAEQvD,iBACNlD,IAAyB,EACzBC,OAA8B,EAC9BT,UAAkB,EACI;QACtB,OAAO;YACLkH,gBAAgBzG,QAAQyG,cAAc;YACtChE,iBAAiBzC,QAAQyC,eAAe;YACxCiE,UAAU1G,QAAQ0G,QAAQ,IAAI3G,KAAK4G,EAAE;YACrCC,eAAe5G,QAAQ4G,aAAa,IAAI,CAAC;YACzCrH;YACAiD,qBAAqB,UACnBxC,QAAQuC,aAAa,CAACC,mBAAmB,CAAC;oBAAEC,iBAAiBzC,QAAQyC,eAAe;gBAAC;YACvFmD,sBAAsB,OAAOtG;gBAC3B,MAAMU,QAAQuC,aAAa,CAACqD,oBAAoB,CAC9C;oBAAEnD,iBAAiBzC,QAAQyC,eAAe;gBAAC,GAC3CnD;YAEJ;QACF;IACF;IAEA,MAAcqG,cACZtD,OAA8B,EAC9B/C,GAAqB,EACM;QAC3B,IAAIuH,OAAOvH;QACX,KAAK,MAAML,UAAUoD,QAAS;YAC5B,MAAMyE,UAAU,MAAM7H,OAAOE,MAAM,CAAC0H;YACpC,IAAIC,YAAYhG,WAAW;gBACzB+F,OAAOC;YACT;QACF;QACA,OAAOD;IACT;IAEA,MAAcf,aAAazD,OAA8B,EAAiB;QACxE,IAAK,IAAIa,IAAIb,QAAQkC,MAAM,GAAG,GAAGrB,KAAK,GAAGA,IAAK;YAC5C,MAAMb,OAAO,CAACa,EAAE,CAAE9D,KAAK;QACzB;IACF;IAEA;;;;;;;;;;;;GAYC,GACD,MAAcsE,SACZqD,EAAoB,EACpBC,OAA2B,EACF;QACzB,IAAIC,UAAU;QACd,uEAAuE;QACvE,oEAAoE;QACpE,uBAAuB;QACvB,MAAMC,WAAWF,QAAQ7G,WAAW,GAAG,MAAM;QAE7C,MAAO8G,WAAWC,SAAU;YAC1B,IAAIF,QAAQhD,MAAM,EAAE,MAAMgD,QAAQhD,MAAM;YACxC,IAAIhF;YACJ,IAAI;gBACFA,QAAQ,MAAM+H;YAChB,EAAE,OAAOjD,KAAK;gBACZ,IAAIkD,QAAQ5C,OAAO,EAAE,MAAM4C,QAAQ5C,OAAO,CAACN;gBAC3C,sEAAsE;gBACtE,IAAIkD,QAAQ/G,UAAU,EAAE;oBACtB,kEAAkE;oBAClE,+DAA+D;oBAC/D,mEAAmE;oBACnE,MAAMkH,aAA0B;wBAC9BhE,MAAM6D,QAAQ7D,IAAI;wBAClBQ,OAAOqD,QAAQrD,KAAK;wBACpBnC,WAAW;wBACXf,WAAWuG,QAAQvG,SAAS;oBAC9B;oBACA,IAAIuG,QAAQ/G,UAAU,CAACmH,UAAU,CAACtD,KAAKqD,aAAa;wBAClD,8DAA8D;wBAC9D,uCAAuC;wBACvC,MAAME,YAAYL,QAAQpD,YAAY,KAAK;wBAC3C,IAAIyD,YAAYL,QAAQvG,SAAS,EAAE;4BACjC,MAAM,IAAI6G,8BAAsB,CAACN,QAAQvG,SAAS;wBACpD;wBACA,MAAMuG,QAAQnD,MAAM,CAACC;wBACrB,OAAO;4BAAEQ,MAAM;wBAAU;oBAC3B;gBACA,2DAA2D;gBAC7D;gBAEA,wBAAwB;gBACxB,IAAI0C,QAAQ7G,WAAW,EAAE;oBACvB,MAAMoH,WAAyB;wBAC7BpE,MAAM6D,QAAQ7D,IAAI;wBAClBQ,OAAOqD,QAAQrD,KAAK;wBACpBsD;wBACAtG,YAAYqG,QAAQrG,UAAU;oBAChC;oBACA,IAAIqG,QAAQ7G,WAAW,CAACqH,QAAQ,CAAC1D,KAAKyD,WAAW;wBAC/C,MAAME,KAAKT,QAAQ7G,WAAW,CAACuH,SAAS,CAACT;wBACzC,IAAIQ,KAAK,GAAG,MAAM,IAAIE,QAAQ,CAAClE,IAAMmE,WAAWnE,GAAGgE;wBACnDR,WAAW;wBACX;oBACF;oBACA,6DAA6D;oBAC7D,2DAA2D;oBAC3D,mBAAmB;oBACnB,IAAIA,UAAUD,QAAQrG,UAAU,EAAE;wBAChC,MAAMkH,cAAcb,QAAQ7G,WAAW,CAACqH,QAAQ,CAAC1D,KAAK;4BACpDX,MAAM6D,QAAQ7D,IAAI;4BAClBQ,OAAOqD,QAAQrD,KAAK;4BACpBsD,SAAS;4BACTtG,YAAYmH,OAAOC,iBAAiB;wBACtC;wBACA,IAAIF,aAAa;4BACf,MAAM,IAAIG,+BAAuB,CAAChB,QAAQrG,UAAU;wBACtD;oBACF;gBACF;gBAEA,mEAAmE;gBACnE,MAAMmD;YACR;YACA,IAAIkD,QAAQ9C,KAAK,EAAE,MAAM8C,QAAQ9C,KAAK,CAAClF;YACvC,OAAO;gBAAEsF,MAAM;gBAAMtF;YAAM;QAC7B;QAEA,uEAAuE;QACvE,qEAAqE;QACrE,kEAAkE;QAClE,MAAM,IAAIqC,MACR,CAAC,0BAA0B,EAAE2F,QAAQrD,KAAK,CAAC,2CAA2C,CAAC;IAE3F;IAEQ3B,cAAciG,GAAc,EAAEjI,OAA8B,EAAc;QAChF,IAAIiI,IAAI3D,IAAI,KAAK4D,WAAO,CAACC,aAAa,IAAIF,IAAIG,EAAE,EAAE;YAChD,MAAMrC,SAASkC,IAAIG,EAAE;YACrB,IAAI,OAAOrC,WAAW,YAAY;gBAChC,OAAO;oBAAE3C,MAAM2C;gBAA6B;YAC9C;YACA,IACEA,WAAW,QACX,OAAOA,WAAW,YAClB,OAAO,AAACA,OAAsB3C,IAAI,KAAK,YACvC;gBACA,OAAO2C;YACT;YACA,OAAO;gBAAE3C,MAAM6E,IAAIG,EAAE;YAAuB;QAC9C;QACA,IAAIH,IAAI3D,IAAI,KAAK4D,WAAO,CAACG,aAAa,EAAE;YACtC,OAAOC,IAAAA,iCAAoB,EAAa,UAAUL,KAAKjI,QAAQuI,iBAAiB;QAClF;QACA,IAAIN,IAAI3D,IAAI,KAAK4D,WAAO,CAACM,MAAM,IAAIP,IAAIQ,UAAU,IAAIR,IAAIS,UAAU,EAAE;YACnE,MAAMC,MAAM,GAAG3I,QAAQ4I,eAAe,CAAC,UAAU,EAAEX,IAAIQ,UAAU,CAAC,EAAE,EAAER,IAAIS,UAAU,EAAE;YACtF,MAAMN,KAAKpI,QAAQ6I,SAAS,CAACC,GAAG,CAACH;YACjC,IAAI,CAACP,IAAI,MAAM,IAAI/G,MAAM,CAAC,qBAAqB,EAAE4G,IAAIQ,UAAU,CAAC,CAAC,EAAER,IAAIS,UAAU,EAAE;YACnF,OAAO;gBAAEtF,MAAMgF;YAAyB;QAC1C;QACA,MAAM,IAAI/G,MAAM,CAAC,6BAA6B,EAAE4G,IAAI3D,IAAI,EAAE;IAC5D;IAEQpC,iBAAiB+F,GAAiB,EAAEjI,OAA8B,EAAiB;QACzF,IAAIiI,IAAI3D,IAAI,KAAK4D,WAAO,CAACC,aAAa,IAAIF,IAAIG,EAAE,EAAE;YAChD,MAAMrC,SAASkC,IAAIG,EAAE;YACrB,IAAI,OAAOrC,WAAW,YAAY;gBAChC,OAAO;oBAAElB,SAASkB;gBAAmC;YACvD;YACA,IACEA,WAAW,QACX,OAAOA,WAAW,YAClB,OAAO,AAACA,OAAyBlB,OAAO,KAAK,YAC7C;gBACA,OAAOkB;YACT;YACA,OAAO;gBAAElB,SAASoD,IAAIG,EAAE;YAA6B;QACvD;QACA,IAAIH,IAAI3D,IAAI,KAAK4D,WAAO,CAACG,aAAa,EAAE;YACtC,OAAOC,IAAAA,iCAAoB,EAAgB,aAAaL,KAAKjI,QAAQuI,iBAAiB;QACxF;QACA,IAAIN,IAAI3D,IAAI,KAAK4D,WAAO,CAACM,MAAM,IAAIP,IAAIQ,UAAU,IAAIR,IAAIS,UAAU,EAAE;YACnE,MAAMC,MAAM,GAAG3I,QAAQ4I,eAAe,CAAC,aAAa,EAAEX,IAAIQ,UAAU,CAAC,EAAE,EAAER,IAAIS,UAAU,EAAE;YACzF,MAAMN,KAAKpI,QAAQ6I,SAAS,CAACC,GAAG,CAACH;YACjC,IAAI,CAACP,IAAI,MAAM,IAAI/G,MAAM,CAAC,wBAAwB,EAAE4G,IAAIQ,UAAU,CAAC,CAAC,EAAER,IAAIS,UAAU,EAAE;YACtF,OAAO;gBAAE7D,SAASuD;YAA+B;QACnD;QACA,MAAM,IAAI/G,MAAM,CAAC,gCAAgC,EAAE4G,IAAI3D,IAAI,EAAE;IAC/D;IAEQlC,cAAc6F,GAAc,EAAEjI,OAA8B,EAAc;QAChF,IAAIiI,IAAI3D,IAAI,KAAK4D,WAAO,CAACC,aAAa,IAAIF,IAAIG,EAAE,EAAE;YAChD,MAAMrC,SAASkC,IAAIG,EAAE;YACrB,IAAI,OAAOrC,WAAW,YAAY;gBAChC,OAAO;oBAAEX,OAAOW;gBAA8B;YAChD;YACA,IACEA,WAAW,QACX,OAAOA,WAAW,YAClB,OAAO,AAACA,OAAsBX,KAAK,KAAK,YACxC;gBACA,OAAOW;YACT;YACA,OAAO;gBAAEX,OAAO6C,IAAIG,EAAE;YAAwB;QAChD;QACA,IAAIH,IAAI3D,IAAI,KAAK4D,WAAO,CAACG,aAAa,EAAE;YACtC,OAAOC,IAAAA,iCAAoB,EAAa,UAAUL,KAAKjI,QAAQuI,iBAAiB;QAClF;QACA,IAAIN,IAAI3D,IAAI,KAAK4D,WAAO,CAACM,MAAM,IAAIP,IAAIQ,UAAU,IAAIR,IAAIS,UAAU,EAAE;YACnE,MAAMC,MAAM,GAAG3I,QAAQ4I,eAAe,CAAC,UAAU,EAAEX,IAAIQ,UAAU,CAAC,EAAE,EAAER,IAAIS,UAAU,EAAE;YACtF,MAAMN,KAAKpI,QAAQ6I,SAAS,CAACC,GAAG,CAACH;YACjC,IAAI,CAACP,IAAI,MAAM,IAAI/G,MAAM,CAAC,qBAAqB,EAAE4G,IAAIQ,UAAU,CAAC,CAAC,EAAER,IAAIS,UAAU,EAAE;YACnF,OAAO;gBAAEtD,OAAOgD;YAA0B;QAC5C;QACA,MAAM,IAAI/G,MAAM,CAAC,6BAA6B,EAAE4G,IAAI3D,IAAI,EAAE;IAC5D;AACF"}
@@ -0,0 +1,25 @@
1
+ import { OnApplicationBootstrap, OnApplicationShutdown } from '@nestjs/common';
2
+ import { BatchScheduleRegistry } from '../module/batch-schedule-registry';
3
+ import { JobLauncher } from './job-launcher';
4
+ /**
5
+ * In-process scheduler for `@BatchScheduled` jobs.
6
+ *
7
+ * This is deliberately part of the in-process transport, not the
8
+ * `@BatchScheduled` decorator. The decorator remains metadata-only;
9
+ * this provider consumes the `BatchScheduleRegistry` and turns matching
10
+ * cron ticks into `JobLauncher.launch(...)` calls inside the same server
11
+ * process.
12
+ */
13
+ export declare class InProcessSchedule implements OnApplicationBootstrap, OnApplicationShutdown {
14
+ private readonly scheduleRegistry;
15
+ private readonly launcher;
16
+ private readonly logger;
17
+ private readonly states;
18
+ private stopped;
19
+ constructor(scheduleRegistry: BatchScheduleRegistry, launcher: JobLauncher);
20
+ onApplicationBootstrap(): void;
21
+ onApplicationShutdown(): void;
22
+ private dispatch;
23
+ private launch;
24
+ }
25
+ //# sourceMappingURL=in-process-schedule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"in-process-schedule.d.ts","sourceRoot":"","sources":["../../../src/execution/in-process-schedule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,sBAAsB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAGnG,OAAO,EAA2B,qBAAqB,EAAE,MAAM,mCAAmC,CAAC;AACnG,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAc7C;;;;;;;;GAQG;AACH,qBACa,iBAAkB,YAAW,sBAAsB,EAAE,qBAAqB;IAMnF,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAN3B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsC;IAC7D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoC;IAC3D,OAAO,CAAC,OAAO,CAAQ;gBAGJ,gBAAgB,EAAE,qBAAqB,EACvC,QAAQ,EAAE,WAAW;IAGxC,sBAAsB,IAAI,IAAI;IAmD9B,qBAAqB,IAAI,IAAI;IAQ7B,OAAO,CAAC,QAAQ;YAyBF,MAAM;CAoBrB"}
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "InProcessSchedule", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return InProcessSchedule;
9
+ }
10
+ });
11
+ const _common = require("@nestjs/common");
12
+ const _cron = require("cron");
13
+ const _batchscheduleregistry = require("../module/batch-schedule-registry");
14
+ const _joblauncher = require("./job-launcher");
15
+ function _ts_decorate(decorators, target, key, desc) {
16
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
17
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
18
+ else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
19
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
20
+ }
21
+ function _ts_metadata(k, v) {
22
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
23
+ }
24
+ function scheduleKey(entry) {
25
+ return `${entry.jobId}::${entry.scheduleName}`;
26
+ }
27
+ let InProcessSchedule = class InProcessSchedule {
28
+ scheduleRegistry;
29
+ launcher;
30
+ logger = new _common.Logger(InProcessSchedule.name);
31
+ states = new Map();
32
+ stopped = true;
33
+ constructor(scheduleRegistry, launcher){
34
+ this.scheduleRegistry = scheduleRegistry;
35
+ this.launcher = launcher;
36
+ }
37
+ onApplicationBootstrap() {
38
+ this.stopped = false;
39
+ for (const entry of this.scheduleRegistry.getAll()){
40
+ if (entry.inert) {
41
+ this.logger.log(`Skipping inert schedule: ${entry.jobId}::${entry.scheduleName}`);
42
+ continue;
43
+ }
44
+ try {
45
+ let state;
46
+ const job = _cron.CronJob.from({
47
+ cronTime: entry.cron,
48
+ timeZone: entry.timezone,
49
+ start: false,
50
+ unrefTimeout: true,
51
+ name: scheduleKey(entry),
52
+ errorHandler: (err)=>{
53
+ this.logger.warn(`In-process schedule ${scheduleKey(entry)} callback failed: ${err instanceof Error ? err.message : String(err)}`);
54
+ },
55
+ onTick: ()=>{
56
+ if (this.stopped) return;
57
+ this.dispatch(state, state.job.lastDate() ?? new Date());
58
+ }
59
+ });
60
+ state = {
61
+ entry,
62
+ job,
63
+ runningCount: 0,
64
+ queued: false,
65
+ queuedAt: null
66
+ };
67
+ this.states.set(scheduleKey(entry), state);
68
+ job.start();
69
+ } catch (err) {
70
+ this.logger.warn(`Failed to install in-process schedule ${entry.jobId}::${entry.scheduleName}: ${err instanceof Error ? err.message : String(err)}`);
71
+ }
72
+ }
73
+ if (this.states.size === 0) return;
74
+ this.logger.log(`InProcessSchedule started: schedules=${this.states.size}`);
75
+ }
76
+ onApplicationShutdown() {
77
+ this.stopped = true;
78
+ for (const state of this.states.values()){
79
+ void state.job.stop();
80
+ }
81
+ this.states.clear();
82
+ }
83
+ dispatch(state, scheduledAt) {
84
+ if (this.stopped) return;
85
+ const overlap = state.entry.overlap ?? 'skip';
86
+ if (state.runningCount > 0) {
87
+ if (overlap === 'skip') return;
88
+ if (overlap === 'queue') {
89
+ state.queued = true;
90
+ state.queuedAt ??= scheduledAt;
91
+ return;
92
+ }
93
+ }
94
+ state.runningCount += 1;
95
+ void this.launch(state, scheduledAt).finally(()=>{
96
+ state.runningCount -= 1;
97
+ if (this.stopped) return;
98
+ if (state.runningCount === 0 && state.queued) {
99
+ state.queued = false;
100
+ const queuedAt = state.queuedAt ?? new Date();
101
+ state.queuedAt = null;
102
+ this.dispatch(state, queuedAt);
103
+ }
104
+ });
105
+ }
106
+ async launch(state, scheduledAt) {
107
+ const { entry } = state;
108
+ try {
109
+ const execution = await this.launcher.launch(entry.jobId, {
110
+ scheduled: true,
111
+ scheduleName: entry.scheduleName,
112
+ scheduledAt: scheduledAt.toISOString()
113
+ });
114
+ this.logger.log(`Fired schedule ${entry.jobId}::${entry.scheduleName} -> ` + `execution=${execution.id} status=${execution.status}`);
115
+ } catch (err) {
116
+ this.logger.warn(`Failed to fire schedule ${entry.jobId}::${entry.scheduleName}: ${err instanceof Error ? err.message : String(err)}`);
117
+ }
118
+ }
119
+ };
120
+ InProcessSchedule = _ts_decorate([
121
+ (0, _common.Injectable)(),
122
+ _ts_metadata("design:type", Function),
123
+ _ts_metadata("design:paramtypes", [
124
+ typeof _batchscheduleregistry.BatchScheduleRegistry === "undefined" ? Object : _batchscheduleregistry.BatchScheduleRegistry,
125
+ typeof _joblauncher.JobLauncher === "undefined" ? Object : _joblauncher.JobLauncher
126
+ ])
127
+ ], InProcessSchedule);
128
+
129
+ //# sourceMappingURL=in-process-schedule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/execution/in-process-schedule.ts"],"sourcesContent":["import { Injectable, Logger, OnApplicationBootstrap, OnApplicationShutdown } from '@nestjs/common';\nimport { CronJob } from 'cron';\n\nimport { type BatchScheduleEntry, BatchScheduleRegistry } from '../module/batch-schedule-registry';\nimport { JobLauncher } from './job-launcher';\n\ninterface ScheduleState {\n readonly entry: BatchScheduleEntry;\n readonly job: CronJob;\n runningCount: number;\n queued: boolean;\n queuedAt: Date | null;\n}\n\nfunction scheduleKey(entry: BatchScheduleEntry): string {\n return `${entry.jobId}::${entry.scheduleName}`;\n}\n\n/**\n * In-process scheduler for `@BatchScheduled` jobs.\n *\n * This is deliberately part of the in-process transport, not the\n * `@BatchScheduled` decorator. The decorator remains metadata-only;\n * this provider consumes the `BatchScheduleRegistry` and turns matching\n * cron ticks into `JobLauncher.launch(...)` calls inside the same server\n * process.\n */\n@Injectable()\nexport class InProcessSchedule implements OnApplicationBootstrap, OnApplicationShutdown {\n private readonly logger = new Logger(InProcessSchedule.name);\n private readonly states = new Map<string, ScheduleState>();\n private stopped = true;\n\n constructor(\n private readonly scheduleRegistry: BatchScheduleRegistry,\n private readonly launcher: JobLauncher,\n ) {}\n\n onApplicationBootstrap(): void {\n this.stopped = false;\n for (const entry of this.scheduleRegistry.getAll()) {\n if (entry.inert) {\n this.logger.log(`Skipping inert schedule: ${entry.jobId}::${entry.scheduleName}`);\n continue;\n }\n\n try {\n let state!: ScheduleState;\n const job = CronJob.from({\n cronTime: entry.cron,\n timeZone: entry.timezone,\n start: false,\n unrefTimeout: true,\n name: scheduleKey(entry),\n errorHandler: (err) => {\n this.logger.warn(\n `In-process schedule ${scheduleKey(entry)} callback failed: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n },\n onTick: () => {\n if (this.stopped) return;\n this.dispatch(state, state.job.lastDate() ?? new Date());\n },\n });\n state = {\n entry,\n job,\n runningCount: 0,\n queued: false,\n queuedAt: null,\n };\n this.states.set(scheduleKey(entry), state);\n job.start();\n } catch (err) {\n this.logger.warn(\n `Failed to install in-process schedule ${entry.jobId}::${entry.scheduleName}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n }\n\n if (this.states.size === 0) return;\n\n this.logger.log(`InProcessSchedule started: schedules=${this.states.size}`);\n }\n\n onApplicationShutdown(): void {\n this.stopped = true;\n for (const state of this.states.values()) {\n void state.job.stop();\n }\n this.states.clear();\n }\n\n private dispatch(state: ScheduleState, scheduledAt: Date): void {\n if (this.stopped) return;\n const overlap = state.entry.overlap ?? 'skip';\n if (state.runningCount > 0) {\n if (overlap === 'skip') return;\n if (overlap === 'queue') {\n state.queued = true;\n state.queuedAt ??= scheduledAt;\n return;\n }\n }\n\n state.runningCount += 1;\n void this.launch(state, scheduledAt).finally(() => {\n state.runningCount -= 1;\n if (this.stopped) return;\n if (state.runningCount === 0 && state.queued) {\n state.queued = false;\n const queuedAt = state.queuedAt ?? new Date();\n state.queuedAt = null;\n this.dispatch(state, queuedAt);\n }\n });\n }\n\n private async launch(state: ScheduleState, scheduledAt: Date): Promise<void> {\n const { entry } = state;\n try {\n const execution = await this.launcher.launch(entry.jobId, {\n scheduled: true,\n scheduleName: entry.scheduleName,\n scheduledAt: scheduledAt.toISOString(),\n });\n this.logger.log(\n `Fired schedule ${entry.jobId}::${entry.scheduleName} -> ` +\n `execution=${execution.id} status=${execution.status}`,\n );\n } catch (err) {\n this.logger.warn(\n `Failed to fire schedule ${entry.jobId}::${entry.scheduleName}: ${\n err instanceof Error ? err.message : String(err)\n }`,\n );\n }\n }\n}\n"],"names":["InProcessSchedule","scheduleKey","entry","jobId","scheduleName","logger","Logger","name","states","Map","stopped","scheduleRegistry","launcher","onApplicationBootstrap","getAll","inert","log","state","job","CronJob","from","cronTime","cron","timeZone","timezone","start","unrefTimeout","errorHandler","err","warn","Error","message","String","onTick","dispatch","lastDate","Date","runningCount","queued","queuedAt","set","size","onApplicationShutdown","values","stop","clear","scheduledAt","overlap","launch","finally","execution","scheduled","toISOString","id","status"],"mappings":"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"}
@@ -11,4 +11,5 @@ export * from './job-key';
11
11
  export * from './execution-strategy';
12
12
  export * from './ref-resolver';
13
13
  export * from './in-process-execution-strategy';
14
+ export * from './in-process-schedule';
14
15
  //# sourceMappingURL=index.d.ts.map