@nest-batch/core 0.2.0
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.
- package/LICENSE +21 -0
- package/README.md +368 -0
- package/dist/src/adapters/in-process.adapter.d.ts +140 -0
- package/dist/src/adapters/in-process.adapter.d.ts.map +1 -0
- package/dist/src/adapters/in-process.adapter.js +86 -0
- package/dist/src/adapters/in-process.adapter.js.map +1 -0
- package/dist/src/adapters/index.d.ts +18 -0
- package/dist/src/adapters/index.d.ts.map +1 -0
- package/dist/src/adapters/index.js +35 -0
- package/dist/src/adapters/index.js.map +1 -0
- package/dist/src/builder/batch-builder.d.ts +26 -0
- package/dist/src/builder/batch-builder.d.ts.map +1 -0
- package/dist/src/builder/batch-builder.js +25 -0
- package/dist/src/builder/batch-builder.js.map +1 -0
- package/dist/src/builder/flow-builder.d.ts +59 -0
- package/dist/src/builder/flow-builder.d.ts.map +1 -0
- package/dist/src/builder/flow-builder.js +115 -0
- package/dist/src/builder/flow-builder.js.map +1 -0
- package/dist/src/builder/index.d.ts +5 -0
- package/dist/src/builder/index.d.ts.map +1 -0
- package/dist/src/builder/index.js +23 -0
- package/dist/src/builder/index.js.map +1 -0
- package/dist/src/builder/job-builder.d.ts +76 -0
- package/dist/src/builder/job-builder.d.ts.map +1 -0
- package/dist/src/builder/job-builder.js +166 -0
- package/dist/src/builder/job-builder.js.map +1 -0
- package/dist/src/builder/step-builder.d.ts +74 -0
- package/dist/src/builder/step-builder.d.ts.map +1 -0
- package/dist/src/builder/step-builder.js +144 -0
- package/dist/src/builder/step-builder.js.map +1 -0
- package/dist/src/compiler/builder-types.d.ts +20 -0
- package/dist/src/compiler/builder-types.d.ts.map +1 -0
- package/dist/src/compiler/builder-types.js +6 -0
- package/dist/src/compiler/builder-types.js.map +1 -0
- package/dist/src/compiler/definition-compiler.d.ts +99 -0
- package/dist/src/compiler/definition-compiler.d.ts.map +1 -0
- package/dist/src/compiler/definition-compiler.js +257 -0
- package/dist/src/compiler/definition-compiler.js.map +1 -0
- package/dist/src/compiler/index.d.ts +3 -0
- package/dist/src/compiler/index.d.ts.map +1 -0
- package/dist/src/compiler/index.js +21 -0
- package/dist/src/compiler/index.js.map +1 -0
- package/dist/src/core/errors.d.ts +77 -0
- package/dist/src/core/errors.d.ts.map +1 -0
- package/dist/src/core/errors.js +170 -0
- package/dist/src/core/errors.js.map +1 -0
- package/dist/src/core/execution-context/index.d.ts +4 -0
- package/dist/src/core/execution-context/index.d.ts.map +1 -0
- package/dist/src/core/execution-context/index.js +22 -0
- package/dist/src/core/execution-context/index.js.map +1 -0
- package/dist/src/core/execution-context/json-value.d.ts +5 -0
- package/dist/src/core/execution-context/json-value.d.ts.map +1 -0
- package/dist/src/core/execution-context/json-value.js +6 -0
- package/dist/src/core/execution-context/json-value.js.map +1 -0
- package/dist/src/core/execution-context/serializer.d.ts +4 -0
- package/dist/src/core/execution-context/serializer.d.ts.map +1 -0
- package/dist/src/core/execution-context/serializer.js +34 -0
- package/dist/src/core/execution-context/serializer.js.map +1 -0
- package/dist/src/core/execution-context/validator.d.ts +18 -0
- package/dist/src/core/execution-context/validator.d.ts.map +1 -0
- package/dist/src/core/execution-context/validator.js +90 -0
- package/dist/src/core/execution-context/validator.js.map +1 -0
- package/dist/src/core/index.d.ts +8 -0
- package/dist/src/core/index.d.ts.map +1 -0
- package/dist/src/core/index.js +26 -0
- package/dist/src/core/index.js.map +1 -0
- package/dist/src/core/ir/decider-definition.d.ts +20 -0
- package/dist/src/core/ir/decider-definition.d.ts.map +1 -0
- package/dist/src/core/ir/decider-definition.js +6 -0
- package/dist/src/core/ir/decider-definition.js.map +1 -0
- package/dist/src/core/ir/index.d.ts +8 -0
- package/dist/src/core/ir/index.d.ts.map +1 -0
- package/dist/src/core/ir/index.js +26 -0
- package/dist/src/core/ir/index.js.map +1 -0
- package/dist/src/core/ir/job-definition.d.ts +15 -0
- package/dist/src/core/ir/job-definition.d.ts.map +1 -0
- package/dist/src/core/ir/job-definition.js +6 -0
- package/dist/src/core/ir/job-definition.js.map +1 -0
- package/dist/src/core/ir/listener-definition.d.ts +10 -0
- package/dist/src/core/ir/listener-definition.d.ts.map +1 -0
- package/dist/src/core/ir/listener-definition.js +6 -0
- package/dist/src/core/ir/listener-definition.js.map +1 -0
- package/dist/src/core/ir/policy-config.d.ts +24 -0
- package/dist/src/core/ir/policy-config.d.ts.map +1 -0
- package/dist/src/core/ir/policy-config.js +6 -0
- package/dist/src/core/ir/policy-config.js.map +1 -0
- package/dist/src/core/ir/refs.d.ts +42 -0
- package/dist/src/core/ir/refs.d.ts.map +1 -0
- package/dist/src/core/ir/refs.js +18 -0
- package/dist/src/core/ir/refs.js.map +1 -0
- package/dist/src/core/ir/step-definition.d.ts +59 -0
- package/dist/src/core/ir/step-definition.d.ts.map +1 -0
- package/dist/src/core/ir/step-definition.js +6 -0
- package/dist/src/core/ir/step-definition.js.map +1 -0
- package/dist/src/core/ir/transition-definition.d.ts +8 -0
- package/dist/src/core/ir/transition-definition.d.ts.map +1 -0
- package/dist/src/core/ir/transition-definition.js +6 -0
- package/dist/src/core/ir/transition-definition.js.map +1 -0
- package/dist/src/core/item/index.d.ts +2 -0
- package/dist/src/core/item/index.d.ts.map +1 -0
- package/dist/src/core/item/index.js +20 -0
- package/dist/src/core/item/index.js.map +1 -0
- package/dist/src/core/item/interfaces.d.ts +64 -0
- package/dist/src/core/item/interfaces.d.ts.map +1 -0
- package/dist/src/core/item/interfaces.js +6 -0
- package/dist/src/core/item/interfaces.js.map +1 -0
- package/dist/src/core/repository/index.d.ts +3 -0
- package/dist/src/core/repository/index.d.ts.map +1 -0
- package/dist/src/core/repository/index.js +21 -0
- package/dist/src/core/repository/index.js.map +1 -0
- package/dist/src/core/repository/job-repository.d.ts +60 -0
- package/dist/src/core/repository/job-repository.d.ts.map +1 -0
- package/dist/src/core/repository/job-repository.js +27 -0
- package/dist/src/core/repository/job-repository.js.map +1 -0
- package/dist/src/core/repository/types.d.ts +84 -0
- package/dist/src/core/repository/types.d.ts.map +1 -0
- package/dist/src/core/repository/types.js +6 -0
- package/dist/src/core/repository/types.js.map +1 -0
- package/dist/src/core/status.d.ts +29 -0
- package/dist/src/core/status.d.ts.map +1 -0
- package/dist/src/core/status.js +58 -0
- package/dist/src/core/status.js.map +1 -0
- package/dist/src/core/transaction/index.d.ts +2 -0
- package/dist/src/core/transaction/index.d.ts.map +1 -0
- package/dist/src/core/transaction/index.js +20 -0
- package/dist/src/core/transaction/index.js.map +1 -0
- package/dist/src/core/transaction/transaction-manager.d.ts +8 -0
- package/dist/src/core/transaction/transaction-manager.d.ts.map +1 -0
- package/dist/src/core/transaction/transaction-manager.js +14 -0
- package/dist/src/core/transaction/transaction-manager.js.map +1 -0
- package/dist/src/core/validation/definition-validator.d.ts +46 -0
- package/dist/src/core/validation/definition-validator.d.ts.map +1 -0
- package/dist/src/core/validation/definition-validator.js +177 -0
- package/dist/src/core/validation/definition-validator.js.map +1 -0
- package/dist/src/core/validation/index.d.ts +2 -0
- package/dist/src/core/validation/index.d.ts.map +1 -0
- package/dist/src/core/validation/index.js +20 -0
- package/dist/src/core/validation/index.js.map +1 -0
- package/dist/src/decorators/constants.d.ts +10 -0
- package/dist/src/decorators/constants.d.ts.map +1 -0
- package/dist/src/decorators/constants.js +50 -0
- package/dist/src/decorators/constants.js.map +1 -0
- package/dist/src/decorators/flow.decorator.d.ts +25 -0
- package/dist/src/decorators/flow.decorator.d.ts.map +1 -0
- package/dist/src/decorators/flow.decorator.js +19 -0
- package/dist/src/decorators/flow.decorator.js.map +1 -0
- package/dist/src/decorators/index.d.ts +8 -0
- package/dist/src/decorators/index.d.ts.map +1 -0
- package/dist/src/decorators/index.js +26 -0
- package/dist/src/decorators/index.js.map +1 -0
- package/dist/src/decorators/item.decorators.d.ts +32 -0
- package/dist/src/decorators/item.decorators.d.ts.map +1 -0
- package/dist/src/decorators/item.decorators.js +40 -0
- package/dist/src/decorators/item.decorators.js.map +1 -0
- package/dist/src/decorators/job.decorator.d.ts +11 -0
- package/dist/src/decorators/job.decorator.d.ts.map +1 -0
- package/dist/src/decorators/job.decorator.js +17 -0
- package/dist/src/decorators/job.decorator.js.map +1 -0
- package/dist/src/decorators/listener.decorators.d.ts +56 -0
- package/dist/src/decorators/listener.decorators.d.ts.map +1 -0
- package/dist/src/decorators/listener.decorators.js +157 -0
- package/dist/src/decorators/listener.decorators.js.map +1 -0
- package/dist/src/decorators/step.decorator.d.ts +25 -0
- package/dist/src/decorators/step.decorator.d.ts.map +1 -0
- package/dist/src/decorators/step.decorator.js +21 -0
- package/dist/src/decorators/step.decorator.js.map +1 -0
- package/dist/src/decorators/tasklet.decorator.d.ts +7 -0
- package/dist/src/decorators/tasklet.decorator.d.ts.map +1 -0
- package/dist/src/decorators/tasklet.decorator.js +21 -0
- package/dist/src/decorators/tasklet.decorator.js.map +1 -0
- package/dist/src/execution/batch-worker-runner.d.ts +27 -0
- package/dist/src/execution/batch-worker-runner.d.ts.map +1 -0
- package/dist/src/execution/batch-worker-runner.js +147 -0
- package/dist/src/execution/batch-worker-runner.js.map +1 -0
- package/dist/src/execution/chunk-step-executor.d.ts +86 -0
- package/dist/src/execution/chunk-step-executor.d.ts.map +1 -0
- package/dist/src/execution/chunk-step-executor.js +482 -0
- package/dist/src/execution/chunk-step-executor.js.map +1 -0
- package/dist/src/execution/execution-strategy.d.ts +110 -0
- package/dist/src/execution/execution-strategy.d.ts.map +1 -0
- package/dist/src/execution/execution-strategy.js +13 -0
- package/dist/src/execution/execution-strategy.js.map +1 -0
- package/dist/src/execution/external-task-execution-strategy.d.ts +36 -0
- package/dist/src/execution/external-task-execution-strategy.d.ts.map +1 -0
- package/dist/src/execution/external-task-execution-strategy.js +97 -0
- package/dist/src/execution/external-task-execution-strategy.js.map +1 -0
- package/dist/src/execution/in-process-execution-strategy.d.ts +129 -0
- package/dist/src/execution/in-process-execution-strategy.d.ts.map +1 -0
- package/dist/src/execution/in-process-execution-strategy.js +141 -0
- package/dist/src/execution/in-process-execution-strategy.js.map +1 -0
- package/dist/src/execution/index.d.ts +14 -0
- package/dist/src/execution/index.d.ts.map +1 -0
- package/dist/src/execution/index.js +32 -0
- package/dist/src/execution/index.js.map +1 -0
- package/dist/src/execution/job-executor.d.ts +145 -0
- package/dist/src/execution/job-executor.d.ts.map +1 -0
- package/dist/src/execution/job-executor.js +475 -0
- package/dist/src/execution/job-executor.js.map +1 -0
- package/dist/src/execution/job-explorer.d.ts +15 -0
- package/dist/src/execution/job-explorer.d.ts.map +1 -0
- package/dist/src/execution/job-explorer.js +84 -0
- package/dist/src/execution/job-explorer.js.map +1 -0
- package/dist/src/execution/job-key.d.ts +3 -0
- package/dist/src/execution/job-key.d.ts.map +1 -0
- package/dist/src/execution/job-key.js +43 -0
- package/dist/src/execution/job-key.js.map +1 -0
- package/dist/src/execution/job-launcher.d.ts +75 -0
- package/dist/src/execution/job-launcher.d.ts.map +1 -0
- package/dist/src/execution/job-launcher.js +112 -0
- package/dist/src/execution/job-launcher.js.map +1 -0
- package/dist/src/execution/job-operator.d.ts +22 -0
- package/dist/src/execution/job-operator.d.ts.map +1 -0
- package/dist/src/execution/job-operator.js +125 -0
- package/dist/src/execution/job-operator.js.map +1 -0
- package/dist/src/execution/listener-invoker.d.ts +164 -0
- package/dist/src/execution/listener-invoker.d.ts.map +1 -0
- package/dist/src/execution/listener-invoker.js +246 -0
- package/dist/src/execution/listener-invoker.js.map +1 -0
- package/dist/src/execution/ref-resolver.d.ts +40 -0
- package/dist/src/execution/ref-resolver.d.ts.map +1 -0
- package/dist/src/execution/ref-resolver.js +41 -0
- package/dist/src/execution/ref-resolver.js.map +1 -0
- package/dist/src/execution/tasklet-step-executor.d.ts +79 -0
- package/dist/src/execution/tasklet-step-executor.d.ts.map +1 -0
- package/dist/src/execution/tasklet-step-executor.js +138 -0
- package/dist/src/execution/tasklet-step-executor.js.map +1 -0
- package/dist/src/explorer/batch-explorer.d.ts +138 -0
- package/dist/src/explorer/batch-explorer.d.ts.map +1 -0
- package/dist/src/explorer/batch-explorer.js +167 -0
- package/dist/src/explorer/batch-explorer.js.map +1 -0
- package/dist/src/explorer/index.d.ts +2 -0
- package/dist/src/explorer/index.d.ts.map +1 -0
- package/dist/src/explorer/index.js +20 -0
- package/dist/src/explorer/index.js.map +1 -0
- package/dist/src/flow/flow-evaluator.d.ts +30 -0
- package/dist/src/flow/flow-evaluator.d.ts.map +1 -0
- package/dist/src/flow/flow-evaluator.js +80 -0
- package/dist/src/flow/flow-evaluator.js.map +1 -0
- package/dist/src/flow/index.d.ts +2 -0
- package/dist/src/flow/index.d.ts.map +1 -0
- package/dist/src/flow/index.js +20 -0
- package/dist/src/flow/index.js.map +1 -0
- package/dist/src/index.d.ts +18 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +90 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/io/checkpoint.d.ts +7 -0
- package/dist/src/io/checkpoint.d.ts.map +1 -0
- package/dist/src/io/checkpoint.js +56 -0
- package/dist/src/io/checkpoint.js.map +1 -0
- package/dist/src/io/database.d.ts +50 -0
- package/dist/src/io/database.d.ts.map +1 -0
- package/dist/src/io/database.js +108 -0
- package/dist/src/io/database.js.map +1 -0
- package/dist/src/io/file-readers.d.ts +54 -0
- package/dist/src/io/file-readers.d.ts.map +1 -0
- package/dist/src/io/file-readers.js +167 -0
- package/dist/src/io/file-readers.js.map +1 -0
- package/dist/src/io/file-writers.d.ts +31 -0
- package/dist/src/io/file-writers.d.ts.map +1 -0
- package/dist/src/io/file-writers.js +80 -0
- package/dist/src/io/file-writers.js.map +1 -0
- package/dist/src/io/index.d.ts +6 -0
- package/dist/src/io/index.d.ts.map +1 -0
- package/dist/src/io/index.js +24 -0
- package/dist/src/io/index.js.map +1 -0
- package/dist/src/io/s3.d.ts +50 -0
- package/dist/src/io/s3.d.ts.map +1 -0
- package/dist/src/io/s3.js +96 -0
- package/dist/src/io/s3.js.map +1 -0
- package/dist/src/listeners/builtin-listeners.d.ts +77 -0
- package/dist/src/listeners/builtin-listeners.d.ts.map +1 -0
- package/dist/src/listeners/builtin-listeners.js +108 -0
- package/dist/src/listeners/builtin-listeners.js.map +1 -0
- package/dist/src/listeners/index.d.ts +8 -0
- package/dist/src/listeners/index.d.ts.map +1 -0
- package/dist/src/listeners/index.js +25 -0
- package/dist/src/listeners/index.js.map +1 -0
- package/dist/src/module/adapter-options.d.ts +39 -0
- package/dist/src/module/adapter-options.d.ts.map +1 -0
- package/dist/src/module/adapter-options.js +34 -0
- package/dist/src/module/adapter-options.js.map +1 -0
- package/dist/src/module/adapter.d.ts +157 -0
- package/dist/src/module/adapter.d.ts.map +1 -0
- package/dist/src/module/adapter.js +80 -0
- package/dist/src/module/adapter.js.map +1 -0
- package/dist/src/module/batch-schedule-registry.d.ts +110 -0
- package/dist/src/module/batch-schedule-registry.d.ts.map +1 -0
- package/dist/src/module/batch-schedule-registry.js +0 -0
- package/dist/src/module/batch-schedule-registry.js.map +1 -0
- package/dist/src/module/index.d.ts +14 -0
- package/dist/src/module/index.d.ts.map +1 -0
- package/dist/src/module/index.js +31 -0
- package/dist/src/module/index.js.map +1 -0
- package/dist/src/module/nest-batch.module.d.ts +236 -0
- package/dist/src/module/nest-batch.module.d.ts.map +1 -0
- package/dist/src/module/nest-batch.module.js +475 -0
- package/dist/src/module/nest-batch.module.js.map +1 -0
- package/dist/src/module/tokens.d.ts +83 -0
- package/dist/src/module/tokens.d.ts.map +1 -0
- package/dist/src/module/tokens.js +58 -0
- package/dist/src/module/tokens.js.map +1 -0
- package/dist/src/observability/event-types.d.ts +55 -0
- package/dist/src/observability/event-types.d.ts.map +1 -0
- package/dist/src/observability/event-types.js +36 -0
- package/dist/src/observability/event-types.js.map +1 -0
- package/dist/src/observability/exporters.d.ts +35 -0
- package/dist/src/observability/exporters.d.ts.map +1 -0
- package/dist/src/observability/exporters.js +93 -0
- package/dist/src/observability/exporters.js.map +1 -0
- package/dist/src/observability/index.d.ts +3 -0
- package/dist/src/observability/index.d.ts.map +1 -0
- package/dist/src/observability/index.js +21 -0
- package/dist/src/observability/index.js.map +1 -0
- package/dist/src/partition-helpers.d.ts +127 -0
- package/dist/src/partition-helpers.d.ts.map +1 -0
- package/dist/src/partition-helpers.js +136 -0
- package/dist/src/partition-helpers.js.map +1 -0
- package/dist/src/policies/backoff.d.ts +3 -0
- package/dist/src/policies/backoff.d.ts.map +1 -0
- package/dist/src/policies/backoff.js +34 -0
- package/dist/src/policies/backoff.js.map +1 -0
- package/dist/src/policies/index.d.ts +4 -0
- package/dist/src/policies/index.d.ts.map +1 -0
- package/dist/src/policies/index.js +22 -0
- package/dist/src/policies/index.js.map +1 -0
- package/dist/src/policies/retry-policy.d.ts +13 -0
- package/dist/src/policies/retry-policy.d.ts.map +1 -0
- package/dist/src/policies/retry-policy.js +55 -0
- package/dist/src/policies/retry-policy.js.map +1 -0
- package/dist/src/policies/skip-policy.d.ts +12 -0
- package/dist/src/policies/skip-policy.d.ts.map +1 -0
- package/dist/src/policies/skip-policy.js +44 -0
- package/dist/src/policies/skip-policy.js.map +1 -0
- package/dist/src/registry/index.d.ts +2 -0
- package/dist/src/registry/index.d.ts.map +1 -0
- package/dist/src/registry/index.js +20 -0
- package/dist/src/registry/index.js.map +1 -0
- package/dist/src/registry/job-registry.d.ts +16 -0
- package/dist/src/registry/job-registry.d.ts.map +1 -0
- package/dist/src/registry/job-registry.js +50 -0
- package/dist/src/registry/job-registry.js.map +1 -0
- package/dist/src/repository/id-generator.d.ts +18 -0
- package/dist/src/repository/id-generator.d.ts.map +1 -0
- package/dist/src/repository/id-generator.js +37 -0
- package/dist/src/repository/id-generator.js.map +1 -0
- package/dist/src/repository/in-memory/in-memory-job-repository.d.ts +49 -0
- package/dist/src/repository/in-memory/in-memory-job-repository.d.ts.map +1 -0
- package/dist/src/repository/in-memory/in-memory-job-repository.js +291 -0
- package/dist/src/repository/in-memory/in-memory-job-repository.js.map +1 -0
- package/dist/src/repository/in-memory/index.d.ts +2 -0
- package/dist/src/repository/in-memory/index.d.ts.map +1 -0
- package/dist/src/repository/in-memory/index.js +20 -0
- package/dist/src/repository/in-memory/index.js.map +1 -0
- package/dist/src/repository/index.d.ts +4 -0
- package/dist/src/repository/index.d.ts.map +1 -0
- package/dist/src/repository/index.js +22 -0
- package/dist/src/repository/index.js.map +1 -0
- package/dist/src/repository/uuid-v7.d.ts +20 -0
- package/dist/src/repository/uuid-v7.d.ts.map +1 -0
- package/dist/src/repository/uuid-v7.js +31 -0
- package/dist/src/repository/uuid-v7.js.map +1 -0
- package/dist/src/scheduling/batch-scheduled.d.ts +87 -0
- package/dist/src/scheduling/batch-scheduled.d.ts.map +1 -0
- package/dist/src/scheduling/batch-scheduled.js +170 -0
- package/dist/src/scheduling/batch-scheduled.js.map +1 -0
- package/dist/src/transaction/in-memory-transaction-manager.d.ts +16 -0
- package/dist/src/transaction/in-memory-transaction-manager.d.ts.map +1 -0
- package/dist/src/transaction/in-memory-transaction-manager.js +33 -0
- package/dist/src/transaction/in-memory-transaction-manager.js.map +1 -0
- package/dist/src/transaction/index.d.ts +2 -0
- package/dist/src/transaction/index.d.ts.map +1 -0
- package/dist/src/transaction/index.js +20 -0
- package/dist/src/transaction/index.js.map +1 -0
- package/dist/tests/contracts/index.d.ts +26 -0
- package/dist/tests/contracts/index.d.ts.map +1 -0
- package/dist/tests/contracts/index.js +37 -0
- package/dist/tests/contracts/index.js.map +1 -0
- package/dist/tests/contracts/job-repository.contract.d.ts +46 -0
- package/dist/tests/contracts/job-repository.contract.d.ts.map +1 -0
- package/dist/tests/contracts/job-repository.contract.js +644 -0
- package/dist/tests/contracts/job-repository.contract.js.map +1 -0
- package/package.json +80 -0
- package/src/adapters/in-process.adapter.ts +182 -0
- package/src/adapters/index.ts +17 -0
- package/src/builder/batch-builder.ts +32 -0
- package/src/builder/flow-builder.ts +141 -0
- package/src/builder/index.ts +4 -0
- package/src/builder/job-builder.ts +206 -0
- package/src/builder/step-builder.ts +190 -0
- package/src/compiler/builder-types.ts +27 -0
- package/src/compiler/definition-compiler.ts +325 -0
- package/src/compiler/index.ts +2 -0
- package/src/core/errors.ts +125 -0
- package/src/core/execution-context/index.ts +3 -0
- package/src/core/execution-context/json-value.ts +3 -0
- package/src/core/execution-context/serializer.ts +21 -0
- package/src/core/execution-context/validator.ts +103 -0
- package/src/core/index.ts +7 -0
- package/src/core/ir/decider-definition.ts +25 -0
- package/src/core/ir/index.ts +7 -0
- package/src/core/ir/job-definition.ts +15 -0
- package/src/core/ir/listener-definition.ts +19 -0
- package/src/core/ir/policy-config.ts +19 -0
- package/src/core/ir/refs.ts +42 -0
- package/src/core/ir/step-definition.ts +62 -0
- package/src/core/ir/transition-definition.ts +9 -0
- package/src/core/item/index.ts +1 -0
- package/src/core/item/interfaces.ts +70 -0
- package/src/core/repository/index.ts +2 -0
- package/src/core/repository/job-repository.ts +100 -0
- package/src/core/repository/types.ts +91 -0
- package/src/core/status.ts +31 -0
- package/src/core/transaction/index.ts +1 -0
- package/src/core/transaction/transaction-manager.ts +8 -0
- package/src/core/validation/definition-validator.ts +215 -0
- package/src/core/validation/index.ts +1 -0
- package/src/decorators/constants.ts +9 -0
- package/src/decorators/flow.decorator.ts +31 -0
- package/src/decorators/index.ts +7 -0
- package/src/decorators/item.decorators.ts +51 -0
- package/src/decorators/job.decorator.ts +16 -0
- package/src/decorators/listener.decorators.ts +142 -0
- package/src/decorators/step.decorator.ts +33 -0
- package/src/decorators/tasklet.decorator.ts +14 -0
- package/src/execution/batch-worker-runner.ts +142 -0
- package/src/execution/chunk-step-executor.ts +594 -0
- package/src/execution/execution-strategy.ts +115 -0
- package/src/execution/external-task-execution-strategy.ts +104 -0
- package/src/execution/in-process-execution-strategy.ts +207 -0
- package/src/execution/index.ts +13 -0
- package/src/execution/job-executor.ts +553 -0
- package/src/execution/job-explorer.ts +73 -0
- package/src/execution/job-key.ts +35 -0
- package/src/execution/job-launcher.ts +132 -0
- package/src/execution/job-operator.ts +127 -0
- package/src/execution/listener-invoker.ts +389 -0
- package/src/execution/ref-resolver.ts +64 -0
- package/src/execution/tasklet-step-executor.ts +182 -0
- package/src/explorer/batch-explorer.ts +251 -0
- package/src/explorer/index.ts +1 -0
- package/src/flow/flow-evaluator.ts +89 -0
- package/src/flow/index.ts +1 -0
- package/src/index.ts +24 -0
- package/src/io/checkpoint.ts +47 -0
- package/src/io/database.ts +114 -0
- package/src/io/file-readers.ts +191 -0
- package/src/io/file-writers.ts +99 -0
- package/src/io/index.ts +5 -0
- package/src/io/s3.ts +117 -0
- package/src/listeners/builtin-listeners.ts +151 -0
- package/src/listeners/index.ts +7 -0
- package/src/module/adapter-options.ts +38 -0
- package/src/module/adapter.ts +160 -0
- package/src/module/batch-schedule-registry.ts +0 -0
- package/src/module/index.ts +13 -0
- package/src/module/nest-batch.module.ts +674 -0
- package/src/module/tokens.ts +95 -0
- package/src/observability/event-types.ts +61 -0
- package/src/observability/exporters.ts +96 -0
- package/src/observability/index.ts +2 -0
- package/src/partition-helpers.ts +204 -0
- package/src/policies/backoff.ts +22 -0
- package/src/policies/index.ts +3 -0
- package/src/policies/retry-policy.ts +57 -0
- package/src/policies/skip-policy.ts +51 -0
- package/src/registry/index.ts +1 -0
- package/src/registry/job-registry.ts +42 -0
- package/src/repository/id-generator.ts +25 -0
- package/src/repository/in-memory/in-memory-job-repository.ts +334 -0
- package/src/repository/in-memory/index.ts +1 -0
- package/src/repository/index.ts +3 -0
- package/src/repository/uuid-v7.ts +40 -0
- package/src/scheduling/batch-scheduled.ts +257 -0
- package/src/transaction/in-memory-transaction-manager.ts +23 -0
- package/src/transaction/index.ts +1 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider-token ref resolver — shared helper used by both the chunk and
|
|
3
|
+
* tasklet step executors to resolve a `RefKind.ProviderToken` ref against
|
|
4
|
+
* a `Map<string, unknown>` of pre-resolved provider instances.
|
|
5
|
+
*
|
|
6
|
+
* The caller (typically the JobExecutor) builds the map by walking the
|
|
7
|
+
* `JobDefinition` and looking each ref's `token` up in the Nest DI
|
|
8
|
+
* container (`ModuleRef.get(token, { strict: false })`). The executors
|
|
9
|
+
* themselves never touch the container — they only consult this map.
|
|
10
|
+
*
|
|
11
|
+
* The error message format is centralized here so every role
|
|
12
|
+
* (`reader` / `processor` / `writer` / `tasklet` / `listener`) reports
|
|
13
|
+
* missing tokens identically. The tests assert on
|
|
14
|
+
* `exitMessage.includes(missingTokenId)`.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** Role label used to disambiguate error messages from the chunk/tasklet
|
|
18
|
+
* resolvers. Lowercase to match the surrounding executor code style. */
|
|
19
|
+
export type ProviderTokenRole = 'reader' | 'processor' | 'writer' | 'tasklet' | 'listener';
|
|
20
|
+
|
|
21
|
+
/** A provider instance bound to a string token. The runtime shape is
|
|
22
|
+
* determined by the role (e.g. `ItemReader` for `reader`), but the map
|
|
23
|
+
* itself is intentionally `unknown` so the resolver can serve every
|
|
24
|
+
* role from a single helper. */
|
|
25
|
+
export type ProviderResolvers = Map<string, unknown>;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Resolve a `RefKind.ProviderToken` ref to the bound provider instance.
|
|
29
|
+
*
|
|
30
|
+
* Throws a deterministic `Error` whose message contains BOTH the
|
|
31
|
+
* missing token id and the role label when the token is not bound.
|
|
32
|
+
* The chunk/tasklet executors' outer `try/catch` propagates this
|
|
33
|
+
* message into the `exitMessage` of the FAILED result.
|
|
34
|
+
*
|
|
35
|
+
* @param role The semantic role of the ref (used only in error messages).
|
|
36
|
+
* @param ref The `RefKind.ProviderToken` ref (must have a `token` field).
|
|
37
|
+
* @param map The provider-resolver map built by the caller.
|
|
38
|
+
* @returns The bound instance, narrowed to `T` at the call site.
|
|
39
|
+
*/
|
|
40
|
+
export function resolveProviderToken<T>(
|
|
41
|
+
role: ProviderTokenRole,
|
|
42
|
+
ref: { token?: string },
|
|
43
|
+
map: ProviderResolvers | undefined,
|
|
44
|
+
): T {
|
|
45
|
+
const token = ref.token;
|
|
46
|
+
if (!token) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`Missing token on ${role} ref: RefKind.ProviderToken requires a non-empty \`token\` field`,
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
if (!map) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`No provider resolvers available for ${role} ref (token=${token}); ` +
|
|
54
|
+
`the executor context did not receive a \`providerResolvers\` map`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
const instance = map.get(token);
|
|
58
|
+
if (instance === undefined) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
`Provider for ${role} token "${token}" was not found in the provider resolvers map`,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
return instance as T;
|
|
64
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { Injectable } from '@nestjs/common';
|
|
2
|
+
import type { TaskletStepDefinition, ListenerDefinition } from '../core/ir';
|
|
3
|
+
import { RefKind } from '../core/ir';
|
|
4
|
+
import type { Tasklet, TaskletContext } from '../core/item';
|
|
5
|
+
import type {
|
|
6
|
+
JobRepository,
|
|
7
|
+
ExecutionContext,
|
|
8
|
+
StepExecution,
|
|
9
|
+
ExecutionScope,
|
|
10
|
+
} from '../core/repository';
|
|
11
|
+
import type { TransactionManager } from '../core/transaction';
|
|
12
|
+
import { StepStatus, JobStatus } from '../core/status';
|
|
13
|
+
import { ListenerInvoker, type ListenerResolver } from './listener-invoker';
|
|
14
|
+
import { resolveProviderToken, type ProviderResolvers } from './ref-resolver';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Bundled dependencies + state for a single `TaskletStepExecutor.execute()` call.
|
|
18
|
+
* The caller (JobExecutor, Task 20) owns lifecycle and persistence.
|
|
19
|
+
*/
|
|
20
|
+
export interface TaskletExecutionContext {
|
|
21
|
+
jobExecutionId: string;
|
|
22
|
+
jobRepository: JobRepository;
|
|
23
|
+
transactionManager: TransactionManager;
|
|
24
|
+
listenerInvoker: ListenerInvoker;
|
|
25
|
+
/** Map from ListenerRef key (`phase:name`) to actual function. */
|
|
26
|
+
listenerResolvers: Map<string, ListenerResolver>;
|
|
27
|
+
/** Optional map of provider-token id → already-resolved provider instance
|
|
28
|
+
* for `RefKind.ProviderToken` tasklet refs. */
|
|
29
|
+
providerResolvers?: ProviderResolvers;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Result of a single tasklet step. Mirrors the subset of `StepExecutionPatch`
|
|
34
|
+
* that the executor can fill in BEFORE the JobExecutor persists it.
|
|
35
|
+
*
|
|
36
|
+
* - `status` — `COMPLETED` if the tasklet returned, `FAILED` if it threw
|
|
37
|
+
* - `exitCode` — short string label (`COMPLETED` / `FAILED`)
|
|
38
|
+
* - `exitMessage` — tasklet's return value (on success) or error message (on failure)
|
|
39
|
+
* - `readCount`/`writeCount`/`skipCount` — always 0 for tasklets (no chunk loop)
|
|
40
|
+
*/
|
|
41
|
+
export interface StepExecutionResult {
|
|
42
|
+
status: StepStatus;
|
|
43
|
+
exitCode: string;
|
|
44
|
+
exitMessage: string;
|
|
45
|
+
readCount: number;
|
|
46
|
+
writeCount: number;
|
|
47
|
+
skipCount: number;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* TaskletStepExecutor — runs a single tasklet step.
|
|
52
|
+
*
|
|
53
|
+
* Orchestration contract (in order):
|
|
54
|
+
* 1. `before-step:*` listeners (always run; failures bubble up)
|
|
55
|
+
* 2. `transactionManager.withTransaction(tasklet.execute, ctx)`
|
|
56
|
+
* 3a. On success → result `{ status: COMPLETED, exitCode: 'COMPLETED', exitMessage: <return> }`
|
|
57
|
+
* 3b. On error → `on-step-error:*` listeners → result `{ status: FAILED, exitCode: 'FAILED', exitMessage: <err> }`
|
|
58
|
+
* 4. `after-step:*` listeners (always run, receives the result)
|
|
59
|
+
*
|
|
60
|
+
* Persistence of the StepExecution is the caller's responsibility —
|
|
61
|
+
* this executor returns a `StepExecutionResult` and the JobExecutor (Task 20)
|
|
62
|
+
* applies it via `jobRepository.updateStepExecution()`.
|
|
63
|
+
*
|
|
64
|
+
* Read/write counts are always 0 for tasklets; the chunk executor (Task 18)
|
|
65
|
+
* produces non-zero counts.
|
|
66
|
+
*/
|
|
67
|
+
@Injectable()
|
|
68
|
+
export class TaskletStepExecutor {
|
|
69
|
+
/**
|
|
70
|
+
* Execute a tasklet step. Returns the step execution result.
|
|
71
|
+
* The caller (JobExecutor, Task 20) handles persistence of the StepExecution.
|
|
72
|
+
*/
|
|
73
|
+
async execute(
|
|
74
|
+
step: TaskletStepDefinition,
|
|
75
|
+
context: TaskletExecutionContext,
|
|
76
|
+
): Promise<StepExecutionResult> {
|
|
77
|
+
// Build the TaskletContext the tasklet will see.
|
|
78
|
+
//
|
|
79
|
+
// `stepExecutionId` is a placeholder here — the JobExecutor knows the real
|
|
80
|
+
// ID (it created the StepExecution) and will patch this object before the
|
|
81
|
+
// tasklet uses it. The placeholder keeps the contract explicit.
|
|
82
|
+
//
|
|
83
|
+
// `getExecutionContext` / `saveExecutionContext` are also stubbed here;
|
|
84
|
+
// they become real once the JobExecutor wires the stepExecutionId in.
|
|
85
|
+
// (For Wave 3 tests we do not exercise these methods.)
|
|
86
|
+
const taskletCtx: TaskletContext = {
|
|
87
|
+
jobExecutionId: context.jobExecutionId,
|
|
88
|
+
stepExecutionId: '<pending>',
|
|
89
|
+
getExecutionContext: async () => ({ data: null, version: 0 }),
|
|
90
|
+
saveExecutionContext: async (_ctx: ExecutionContext) => {
|
|
91
|
+
// wired by JobExecutor (Task 20) once stepExecutionId is known
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// 1. before-step listeners
|
|
96
|
+
await context.listenerInvoker.invokeBeforeStep(context.listenerResolvers, {
|
|
97
|
+
jobExecutionId: context.jobExecutionId,
|
|
98
|
+
stepExecutionId: taskletCtx.stepExecutionId,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
let result: StepExecutionResult;
|
|
102
|
+
try {
|
|
103
|
+
// 2. withTransaction wrap
|
|
104
|
+
const taskletInstance = this.resolveTasklet(step.tasklet, context);
|
|
105
|
+
const txResult = await context.transactionManager.withTransaction(async (_txCtx) => {
|
|
106
|
+
return taskletInstance.execute(taskletCtx);
|
|
107
|
+
});
|
|
108
|
+
result = {
|
|
109
|
+
status: StepStatus.COMPLETED,
|
|
110
|
+
exitCode: 'COMPLETED',
|
|
111
|
+
exitMessage: txResult === undefined ? '' : String(txResult),
|
|
112
|
+
readCount: 0,
|
|
113
|
+
writeCount: 0,
|
|
114
|
+
skipCount: 0,
|
|
115
|
+
};
|
|
116
|
+
} catch (err) {
|
|
117
|
+
// 3. on-step-error listeners (best-effort: rethrow their failures too)
|
|
118
|
+
await context.listenerInvoker.invokeOnErrorStep(
|
|
119
|
+
context.listenerResolvers,
|
|
120
|
+
{ jobExecutionId: context.jobExecutionId, stepExecutionId: taskletCtx.stepExecutionId },
|
|
121
|
+
err,
|
|
122
|
+
);
|
|
123
|
+
result = {
|
|
124
|
+
status: StepStatus.FAILED,
|
|
125
|
+
exitCode: 'FAILED',
|
|
126
|
+
exitMessage: err instanceof Error ? err.message : String(err),
|
|
127
|
+
readCount: 0,
|
|
128
|
+
writeCount: 0,
|
|
129
|
+
skipCount: 0,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 4. after-step listeners (always run, even on failure).
|
|
134
|
+
// Pass the LIVE `result` (not a snapshot) so an `after-step`
|
|
135
|
+
// listener can mutate `result.status` and steer the flow into a
|
|
136
|
+
// different branch — transition evaluation runs AFTER this call.
|
|
137
|
+
await context.listenerInvoker.invokeAfterStep(
|
|
138
|
+
context.listenerResolvers,
|
|
139
|
+
{ jobExecutionId: context.jobExecutionId, stepExecutionId: taskletCtx.stepExecutionId },
|
|
140
|
+
result,
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Resolve a `TaskletRef` to a `Tasklet` instance.
|
|
148
|
+
*
|
|
149
|
+
* Supported kinds:
|
|
150
|
+
* - `builder-lambda` — `taskletRef.fn` is the bound tasklet function.
|
|
151
|
+
* - `provider-token` — looked up in `context.providerResolvers` against
|
|
152
|
+
* `taskletRef.token`. The bound instance must
|
|
153
|
+
* expose an `execute(ctx)` method.
|
|
154
|
+
*
|
|
155
|
+
* Method refs are pre-resolved by the caller and wrapped in a
|
|
156
|
+
* `builder-lambda` ref before reaching this executor.
|
|
157
|
+
*/
|
|
158
|
+
private resolveTasklet(
|
|
159
|
+
taskletRef: { kind: string; token?: string; fn?: ListenerResolver; classToken?: string; methodName?: string },
|
|
160
|
+
context: TaskletExecutionContext,
|
|
161
|
+
): Tasklet {
|
|
162
|
+
if (taskletRef.kind === RefKind.BuilderLambda && taskletRef.fn) {
|
|
163
|
+
const result = taskletRef.fn();
|
|
164
|
+
if (typeof result === 'function') {
|
|
165
|
+
return { execute: result as Tasklet['execute'] };
|
|
166
|
+
}
|
|
167
|
+
if (result !== null && typeof result === 'object' && typeof (result as Tasklet).execute === 'function') {
|
|
168
|
+
return result as Tasklet;
|
|
169
|
+
}
|
|
170
|
+
return { execute: taskletRef.fn as Tasklet['execute'] };
|
|
171
|
+
}
|
|
172
|
+
if (taskletRef.kind === RefKind.ProviderToken) {
|
|
173
|
+
return resolveProviderToken<Tasklet>('tasklet', taskletRef, context.providerResolvers);
|
|
174
|
+
}
|
|
175
|
+
throw new Error(`Tasklet resolution not supported for ref kind: ${taskletRef.kind}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Re-exports for convenience — callers may import types from the executor module.
|
|
180
|
+
export type { Tasklet, TaskletContext } from '../core/item';
|
|
181
|
+
export type { StepExecution, ExecutionScope };
|
|
182
|
+
export { StepStatus, JobStatus };
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { Injectable, OnModuleInit, Logger } from '@nestjs/common';
|
|
2
|
+
import { DiscoveryService } from '@nestjs/core';
|
|
3
|
+
import type { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
|
|
4
|
+
import 'reflect-metadata';
|
|
5
|
+
import {
|
|
6
|
+
BATCH_JOB_METADATA,
|
|
7
|
+
BATCH_STEP_METADATA,
|
|
8
|
+
BATCH_TASKLET_METADATA,
|
|
9
|
+
BATCH_LISTENER_METADATA,
|
|
10
|
+
BATCH_TRANSITION_METADATA,
|
|
11
|
+
} from '../decorators/constants';
|
|
12
|
+
import type { JobableOptions, StepableOptions } from '../decorators';
|
|
13
|
+
import type { ListenerKind, ListenerPhase } from '../core/ir/listener-definition';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Raw shape of a discovered batch job, as it appears immediately after the
|
|
17
|
+
* explorer walks the Nest provider tree. The {@link DiscoveredJob} contains
|
|
18
|
+
* only metadata + class reference + (optionally) the resolved DI instance.
|
|
19
|
+
*
|
|
20
|
+
* It is intentionally NOT a `JobDefinition` yet — the {@link DefinitionCompiler}
|
|
21
|
+
* (Task 8) is responsible for resolving the prototype methods into concrete
|
|
22
|
+
* `ListenerRef` / `TaskletRef` / `ReaderRef` / `ProcessorRef` / `WriterRef` /
|
|
23
|
+
* `TransitionRef` records and for choosing a start step.
|
|
24
|
+
*/
|
|
25
|
+
export interface DiscoveredJob {
|
|
26
|
+
/** The @Jobable-decorated class reference. */
|
|
27
|
+
classRef: Function;
|
|
28
|
+
/** The Nest-resolved DI instance, when available. May be `undefined` for
|
|
29
|
+
* factories or providers that have not been instantiated yet. */
|
|
30
|
+
instance?: unknown;
|
|
31
|
+
/** Raw options passed to `@Jobable(...)`. */
|
|
32
|
+
jobOptions: JobableOptions;
|
|
33
|
+
/** Every `@Stepable` method on the class prototype, in declaration order. */
|
|
34
|
+
stepMethods: DiscoveredStep[];
|
|
35
|
+
/** Every listener-decorated method on the class prototype, in declaration order. */
|
|
36
|
+
listenerMethods: DiscoveredListener[];
|
|
37
|
+
/** Every `@OnTransition` method on the class prototype, in declaration order. */
|
|
38
|
+
transitionMethods: DiscoveredTransition[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** A `@Stepable` method. `isTasklet` is true if `@Tasklet` was also applied. */
|
|
42
|
+
export interface DiscoveredStep {
|
|
43
|
+
methodName: string;
|
|
44
|
+
options: StepableOptions;
|
|
45
|
+
isTasklet: boolean;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** A listener-decorated method (any of the 7 kinds). */
|
|
49
|
+
export interface DiscoveredListener {
|
|
50
|
+
methodName: string;
|
|
51
|
+
kind: ListenerKind;
|
|
52
|
+
phase: ListenerPhase;
|
|
53
|
+
nonCritical?: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* A `@OnTransition` method. `onStatus` is the string name of a
|
|
58
|
+
* `FlowExecutionStatus` (e.g. `'COMPLETED'`, `'FAILED'`, `'STOPPED'`).
|
|
59
|
+
* `toStep === null` means the transition ends the flow.
|
|
60
|
+
*/
|
|
61
|
+
export interface DiscoveredTransition {
|
|
62
|
+
methodName: string;
|
|
63
|
+
fromStep: string;
|
|
64
|
+
onStatus: string;
|
|
65
|
+
toStep: string | null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Minimal shape required from an `InstanceWrapper`-like object. We accept
|
|
70
|
+
* this loose shape (rather than requiring the full Nest `InstanceWrapper`
|
|
71
|
+
* class) so that:
|
|
72
|
+
* 1. tests can pass plain `{ metatype, instance }` objects without booting
|
|
73
|
+
* a Nest application, and
|
|
74
|
+
* 2. future Nest versions that change the wrapper's internal shape will
|
|
75
|
+
* still work as long as `metatype` + `instance` are preserved.
|
|
76
|
+
*/
|
|
77
|
+
export interface ProviderLike {
|
|
78
|
+
metatype?: Function;
|
|
79
|
+
instance?: unknown;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* `BatchExplorer` is a Nest `OnModuleInit` provider that walks every
|
|
84
|
+
* provider registered in the application, looks for classes carrying
|
|
85
|
+
* `@Jobable(...)` metadata, and records every `@Stepable` / `@Tasklet` /
|
|
86
|
+
* listener / `@OnTransition` method on each discovered class.
|
|
87
|
+
*
|
|
88
|
+
* The actual `JobDefinition` IR is produced downstream by the
|
|
89
|
+
* `DefinitionCompiler` (Task 8). The explorer only collects the raw
|
|
90
|
+
* metadata; it does not validate it, compile references, or register jobs.
|
|
91
|
+
*
|
|
92
|
+
* Mirrors the pattern used by `@nestjs/schedule` (`SchedulerExplorer`) and
|
|
93
|
+
* `@nestjs/cqrs` (`Explorer`).
|
|
94
|
+
*/
|
|
95
|
+
@Injectable()
|
|
96
|
+
export class BatchExplorer implements OnModuleInit {
|
|
97
|
+
private readonly logger = new Logger(BatchExplorer.name);
|
|
98
|
+
private discovered: DiscoveredJob[] = [];
|
|
99
|
+
|
|
100
|
+
constructor(private readonly discovery: DiscoveryService) {}
|
|
101
|
+
|
|
102
|
+
/** Hook called by Nest once the DI container is ready. */
|
|
103
|
+
onModuleInit(): void {
|
|
104
|
+
const providers = this.discovery.getProviders();
|
|
105
|
+
this.discovered = this.discoverFromProviders(providers as ProviderLike[]);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Returns the snapshot of jobs collected at `onModuleInit` time. The
|
|
110
|
+
* returned array is `readonly` — callers MUST NOT mutate it.
|
|
111
|
+
*/
|
|
112
|
+
getDiscovered(): readonly DiscoveredJob[] {
|
|
113
|
+
return this.discovered;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Pure provider-walk: given an array of `InstanceWrapper`-like objects,
|
|
118
|
+
* returns the list of `DiscoveredJob`s. Does not require a Nest container.
|
|
119
|
+
*
|
|
120
|
+
* The `onModuleInit` hook delegates here. Tests call this directly.
|
|
121
|
+
*/
|
|
122
|
+
discoverFromProviders(providers: ProviderLike[]): DiscoveredJob[] {
|
|
123
|
+
const out: DiscoveredJob[] = [];
|
|
124
|
+
for (const wrapper of providers) {
|
|
125
|
+
const metatype = wrapper.metatype;
|
|
126
|
+
if (!metatype) continue;
|
|
127
|
+
|
|
128
|
+
const jobOptions = Reflect.getMetadata(BATCH_JOB_METADATA, metatype) as
|
|
129
|
+
| JobableOptions
|
|
130
|
+
| undefined;
|
|
131
|
+
if (!jobOptions) continue;
|
|
132
|
+
|
|
133
|
+
out.push({
|
|
134
|
+
classRef: metatype,
|
|
135
|
+
instance: wrapper.instance,
|
|
136
|
+
jobOptions,
|
|
137
|
+
stepMethods: this.collectStepMethods(metatype.prototype),
|
|
138
|
+
listenerMethods: this.collectListenerMethods(metatype.prototype),
|
|
139
|
+
transitionMethods: this.collectTransitionMethods(metatype.prototype),
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
this.logger.log(`Discovered job: ${jobOptions.id}`);
|
|
143
|
+
}
|
|
144
|
+
return out;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// -------------------------------------------------------------------------
|
|
148
|
+
// Step methods
|
|
149
|
+
// -------------------------------------------------------------------------
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Walks the class prototype chain and returns every method that carries
|
|
153
|
+
* `BATCH_STEP_METADATA`. Each entry's `isTasklet` is true when the same
|
|
154
|
+
* method also carries `BATCH_TASKLET_METADATA`.
|
|
155
|
+
*
|
|
156
|
+
* Walks the full prototype chain (not just `Object.getOwnPropertyNames`
|
|
157
|
+
* on the top-level prototype) so that inherited `@Stepable` methods are
|
|
158
|
+
* also picked up.
|
|
159
|
+
*/
|
|
160
|
+
private collectStepMethods(prototype: object): DiscoveredStep[] {
|
|
161
|
+
const result: DiscoveredStep[] = [];
|
|
162
|
+
for (const name of this.allMethodNames(prototype)) {
|
|
163
|
+
const opts = Reflect.getMetadata(BATCH_STEP_METADATA, prototype, name) as
|
|
164
|
+
| StepableOptions
|
|
165
|
+
| undefined;
|
|
166
|
+
if (!opts) continue;
|
|
167
|
+
const isTasklet =
|
|
168
|
+
Reflect.getMetadata(BATCH_TASKLET_METADATA, prototype, name) === true;
|
|
169
|
+
result.push({ methodName: name, options: opts, isTasklet });
|
|
170
|
+
}
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// -------------------------------------------------------------------------
|
|
175
|
+
// Listener methods
|
|
176
|
+
// -------------------------------------------------------------------------
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Walks the prototype chain and returns every method decorated with one
|
|
180
|
+
* of the 7 listener kinds (job / step / chunk / item-read / item-process /
|
|
181
|
+
* item-write / skip). The metadata shape is uniform — `{ kind, phase,
|
|
182
|
+
* nonCritical? }` — because every listener decorator funnels through
|
|
183
|
+
* the same internal `defineListener` helper.
|
|
184
|
+
*/
|
|
185
|
+
private collectListenerMethods(prototype: object): DiscoveredListener[] {
|
|
186
|
+
const result: DiscoveredListener[] = [];
|
|
187
|
+
for (const name of this.allMethodNames(prototype)) {
|
|
188
|
+
const opts = Reflect.getMetadata(BATCH_LISTENER_METADATA, prototype, name) as
|
|
189
|
+
| { kind: ListenerKind; phase: ListenerPhase; nonCritical?: boolean }
|
|
190
|
+
| undefined;
|
|
191
|
+
if (!opts) continue;
|
|
192
|
+
result.push({
|
|
193
|
+
methodName: name,
|
|
194
|
+
kind: opts.kind,
|
|
195
|
+
phase: opts.phase,
|
|
196
|
+
nonCritical: opts.nonCritical,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
return result;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// -------------------------------------------------------------------------
|
|
203
|
+
// Transition methods
|
|
204
|
+
// -------------------------------------------------------------------------
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Walks the prototype chain and returns every method carrying
|
|
208
|
+
* `BATCH_TRANSITION_METADATA` (written by the `@OnTransition` decorator
|
|
209
|
+
* added in Task 31). Until Task 31 lands, this method simply returns
|
|
210
|
+
* an empty array.
|
|
211
|
+
*
|
|
212
|
+
* `onStatus` is the *string name* of a `FlowExecutionStatus` value
|
|
213
|
+
* (e.g. `'COMPLETED'`, `'FAILED'`, `'STOPPED'`). It is stored as a string
|
|
214
|
+
* to avoid a circular import from `core/ir` → `core/status` and to keep
|
|
215
|
+
* the metadata JSON-serializable.
|
|
216
|
+
*/
|
|
217
|
+
private collectTransitionMethods(prototype: object): DiscoveredTransition[] {
|
|
218
|
+
const result: DiscoveredTransition[] = [];
|
|
219
|
+
for (const name of this.allMethodNames(prototype)) {
|
|
220
|
+
const opts = Reflect.getMetadata(BATCH_TRANSITION_METADATA, prototype, name) as
|
|
221
|
+
| { fromStep: string; onStatus: string; toStep: string | null }
|
|
222
|
+
| undefined;
|
|
223
|
+
if (!opts) continue;
|
|
224
|
+
result.push({ methodName: name, ...opts });
|
|
225
|
+
}
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// -------------------------------------------------------------------------
|
|
230
|
+
// Prototype walker
|
|
231
|
+
// -------------------------------------------------------------------------
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Returns every own method name (excluding `constructor`) on the given
|
|
235
|
+
* prototype and all of its ancestors up to (but not including)
|
|
236
|
+
* `Object.prototype`. Order is undefined, so callers that need a stable
|
|
237
|
+
* order should sort afterwards.
|
|
238
|
+
*/
|
|
239
|
+
private allMethodNames(prototype: object): Set<string> {
|
|
240
|
+
const names = new Set<string>();
|
|
241
|
+
let proto: object | null = prototype;
|
|
242
|
+
while (proto && proto !== Object.prototype) {
|
|
243
|
+
for (const name of Object.getOwnPropertyNames(proto)) {
|
|
244
|
+
if (name === 'constructor') continue;
|
|
245
|
+
names.add(name);
|
|
246
|
+
}
|
|
247
|
+
proto = Object.getPrototypeOf(proto);
|
|
248
|
+
}
|
|
249
|
+
return names;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './batch-explorer';
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Injectable } from '@nestjs/common';
|
|
2
|
+
import type { TransitionDefinition } from '../core/ir';
|
|
3
|
+
import { InvalidFlowGraphError } from '../core/errors';
|
|
4
|
+
|
|
5
|
+
function escapeRegex(value: string): string {
|
|
6
|
+
return value.replace(/[|\\{}()[\]^$+.:]/g, '\\$&');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function matchesPattern(pattern: string, status: string): boolean {
|
|
10
|
+
if (!pattern.includes('*') && !pattern.includes('?')) {
|
|
11
|
+
return pattern === status;
|
|
12
|
+
}
|
|
13
|
+
const source = pattern
|
|
14
|
+
.split('')
|
|
15
|
+
.map((ch) => {
|
|
16
|
+
if (ch === '*') return '.*';
|
|
17
|
+
if (ch === '?') return '.';
|
|
18
|
+
return escapeRegex(ch);
|
|
19
|
+
})
|
|
20
|
+
.join('');
|
|
21
|
+
return new RegExp(`^${source}$`).test(status);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function patternSpecificity(pattern: string): number {
|
|
25
|
+
let score = 0;
|
|
26
|
+
for (const ch of pattern) {
|
|
27
|
+
if (ch !== '*' && ch !== '?') score += 1;
|
|
28
|
+
}
|
|
29
|
+
return pattern.includes('*') || pattern.includes('?') ? score : score + 1000;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* FlowEvaluator resolves the next step in a flow graph given the current step
|
|
34
|
+
* and its exit status. It is a pure, side-effect-free decision function:
|
|
35
|
+
* given the same inputs it always returns the same next step (or END).
|
|
36
|
+
*
|
|
37
|
+
* Per ORACLE verdict 3c, the API is uniformly async — even though the current
|
|
38
|
+
* implementation is synchronous internally, returning a Promise keeps the
|
|
39
|
+
* caller contract identical to future evaluators that may need to consult
|
|
40
|
+
* remote state (e.g. conditional / data-driven transitions).
|
|
41
|
+
*/
|
|
42
|
+
@Injectable()
|
|
43
|
+
export class FlowEvaluator {
|
|
44
|
+
/**
|
|
45
|
+
* Evaluate the next step ID given the current step and exit status.
|
|
46
|
+
*
|
|
47
|
+
* - Returns `null` if the job should END — either no transition matches
|
|
48
|
+
* the (fromStepId, onStatus) pair, or the matching transition's
|
|
49
|
+
* `toStepId` is `null` (explicit END).
|
|
50
|
+
* - Supports `*` and `?` wildcards in `onStatus`. Exact matches win
|
|
51
|
+
* over wildcard matches. If multiple matches have the same
|
|
52
|
+
* specificity, the graph is ambiguous and the caller must fix it.
|
|
53
|
+
*
|
|
54
|
+
* @param transitions All transitions in the job's flow graph.
|
|
55
|
+
* @param fromStepId The current step's ID.
|
|
56
|
+
* @param status The current step's exit status.
|
|
57
|
+
*/
|
|
58
|
+
async evaluate(
|
|
59
|
+
transitions: TransitionDefinition[],
|
|
60
|
+
fromStepId: string,
|
|
61
|
+
status: string,
|
|
62
|
+
): Promise<string | null> {
|
|
63
|
+
const matches = transitions.filter((t) => this.matches(t, fromStepId, status));
|
|
64
|
+
if (matches.length === 0) return null;
|
|
65
|
+
|
|
66
|
+
const ranked = matches.map((transition) => ({
|
|
67
|
+
transition,
|
|
68
|
+
specificity: patternSpecificity(transition.onStatus),
|
|
69
|
+
}));
|
|
70
|
+
const maxSpecificity = Math.max(...ranked.map((candidate) => candidate.specificity));
|
|
71
|
+
const best = ranked.filter((candidate) => candidate.specificity === maxSpecificity);
|
|
72
|
+
|
|
73
|
+
if (best.length > 1) {
|
|
74
|
+
throw new InvalidFlowGraphError(
|
|
75
|
+
'AMBIGUOUS_TRANSITION',
|
|
76
|
+
`Ambiguous transition from "${fromStepId}" on status "${status}": ${best.length} matches`,
|
|
77
|
+
{ fromStepId, status, count: best.length },
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
return best[0]!.transition.toStepId;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
matches(transition: TransitionDefinition, fromStepId: string, status: string): boolean {
|
|
84
|
+
return (
|
|
85
|
+
transition.fromStepId === fromStepId &&
|
|
86
|
+
matchesPattern(transition.onStatus, status)
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './flow-evaluator';
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Public API barrel for @nest-batch/core.
|
|
2
|
+
//
|
|
3
|
+
// Decorator functions (ItemReader, ItemProcessor, ItemWriter, Tasklet) live in
|
|
4
|
+
// ./decorators and share names with interfaces in ./core/item. To avoid name
|
|
5
|
+
// collisions in the public surface, decorators are re-exported under the
|
|
6
|
+
// `BatchDecorators` namespace; interfaces remain reachable as bare names from
|
|
7
|
+
// ./core/item (or via core/index).
|
|
8
|
+
export * from './core';
|
|
9
|
+
export * from './compiler';
|
|
10
|
+
export * from './partition-helpers';
|
|
11
|
+
export * from './registry';
|
|
12
|
+
export * from './execution';
|
|
13
|
+
export * from './transaction';
|
|
14
|
+
export * from './repository';
|
|
15
|
+
export * as BatchDecorators from './decorators';
|
|
16
|
+
export * from './module';
|
|
17
|
+
export * from './builder';
|
|
18
|
+
export * from './explorer';
|
|
19
|
+
export * from './listeners';
|
|
20
|
+
export * from './policies';
|
|
21
|
+
export * from './flow';
|
|
22
|
+
export * from './observability';
|
|
23
|
+
export * from './adapters';
|
|
24
|
+
export * from './io';
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ExecutionContext, JsonValue } from '../core';
|
|
2
|
+
|
|
3
|
+
export function readCheckpoint(
|
|
4
|
+
context: ExecutionContext,
|
|
5
|
+
key: string,
|
|
6
|
+
): Record<string, JsonValue> {
|
|
7
|
+
const root = asJsonRecord(context.data);
|
|
8
|
+
const value = root[key];
|
|
9
|
+
return asJsonRecord(value);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function readCheckpointNumber(
|
|
13
|
+
context: ExecutionContext,
|
|
14
|
+
key: string,
|
|
15
|
+
field: string,
|
|
16
|
+
fallback: number,
|
|
17
|
+
): number {
|
|
18
|
+
const value = readCheckpoint(context, key)[field];
|
|
19
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : fallback;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function readCheckpointValue<T extends JsonValue>(
|
|
23
|
+
context: ExecutionContext,
|
|
24
|
+
key: string,
|
|
25
|
+
field: string,
|
|
26
|
+
fallback: T,
|
|
27
|
+
): T {
|
|
28
|
+
const value = readCheckpoint(context, key)[field];
|
|
29
|
+
return value === undefined ? fallback : (value as T);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function writeCheckpoint(
|
|
33
|
+
context: ExecutionContext,
|
|
34
|
+
key: string,
|
|
35
|
+
state: Record<string, JsonValue>,
|
|
36
|
+
): ExecutionContext {
|
|
37
|
+
const root = { ...asJsonRecord(context.data), [key]: state };
|
|
38
|
+
context.data = root;
|
|
39
|
+
return context;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function asJsonRecord(value: JsonValue | undefined): Record<string, JsonValue> {
|
|
43
|
+
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
return {};
|
|
47
|
+
}
|