@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,114 @@
|
|
|
1
|
+
import type { ExecutionContext, ItemReader, ItemStream, ItemWriter, WriterResult } from '../core';
|
|
2
|
+
import type { JsonValue } from '../core/execution-context';
|
|
3
|
+
|
|
4
|
+
import { readCheckpointNumber, readCheckpointValue, writeCheckpoint } from './checkpoint';
|
|
5
|
+
|
|
6
|
+
export interface DatabasePagingItemReaderOptions<T> {
|
|
7
|
+
readonly pageSize: number;
|
|
8
|
+
readonly checkpointKey?: string;
|
|
9
|
+
fetchPage(args: { offset: number; limit: number }): Promise<readonly T[]>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class DatabasePagingItemReader<T> implements ItemReader<T>, ItemStream {
|
|
13
|
+
private offset = 0;
|
|
14
|
+
private buffer: readonly T[] = [];
|
|
15
|
+
private bufferIndex = 0;
|
|
16
|
+
|
|
17
|
+
constructor(private readonly options: DatabasePagingItemReaderOptions<T>) {}
|
|
18
|
+
|
|
19
|
+
open(context: ExecutionContext): void {
|
|
20
|
+
this.offset = readCheckpointNumber(context, this.checkpointKey, 'offset', 0);
|
|
21
|
+
this.buffer = [];
|
|
22
|
+
this.bufferIndex = 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async read(): Promise<T | null> {
|
|
26
|
+
if (this.bufferIndex >= this.buffer.length) {
|
|
27
|
+
this.buffer = await this.options.fetchPage({
|
|
28
|
+
offset: this.offset,
|
|
29
|
+
limit: this.options.pageSize,
|
|
30
|
+
});
|
|
31
|
+
this.bufferIndex = 0;
|
|
32
|
+
if (this.buffer.length === 0) return null;
|
|
33
|
+
}
|
|
34
|
+
const item = this.buffer[this.bufferIndex]!;
|
|
35
|
+
this.bufferIndex += 1;
|
|
36
|
+
this.offset += 1;
|
|
37
|
+
return item;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
update(context: ExecutionContext): ExecutionContext {
|
|
41
|
+
return writeCheckpoint(context, this.checkpointKey, { offset: this.offset });
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
close(): void {
|
|
45
|
+
this.buffer = [];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private get checkpointKey(): string {
|
|
49
|
+
return this.options.checkpointKey ?? 'database-paging-reader';
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface DatabaseCursorItemReaderOptions<T> {
|
|
54
|
+
readonly pageSize: number;
|
|
55
|
+
readonly checkpointKey?: string;
|
|
56
|
+
readonly initialCursor?: JsonValue;
|
|
57
|
+
fetchAfter(cursor: JsonValue, limit: number): Promise<readonly T[]>;
|
|
58
|
+
getCursor(item: T): JsonValue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export class DatabaseCursorItemReader<T> implements ItemReader<T>, ItemStream {
|
|
62
|
+
private cursor: JsonValue = null;
|
|
63
|
+
private buffer: readonly T[] = [];
|
|
64
|
+
private bufferIndex = 0;
|
|
65
|
+
|
|
66
|
+
constructor(private readonly options: DatabaseCursorItemReaderOptions<T>) {}
|
|
67
|
+
|
|
68
|
+
open(context: ExecutionContext): void {
|
|
69
|
+
this.cursor = readCheckpointValue(
|
|
70
|
+
context,
|
|
71
|
+
this.checkpointKey,
|
|
72
|
+
'cursor',
|
|
73
|
+
this.options.initialCursor ?? null,
|
|
74
|
+
);
|
|
75
|
+
this.buffer = [];
|
|
76
|
+
this.bufferIndex = 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async read(): Promise<T | null> {
|
|
80
|
+
if (this.bufferIndex >= this.buffer.length) {
|
|
81
|
+
this.buffer = await this.options.fetchAfter(this.cursor, this.options.pageSize);
|
|
82
|
+
this.bufferIndex = 0;
|
|
83
|
+
if (this.buffer.length === 0) return null;
|
|
84
|
+
}
|
|
85
|
+
const item = this.buffer[this.bufferIndex]!;
|
|
86
|
+
this.bufferIndex += 1;
|
|
87
|
+
this.cursor = this.options.getCursor(item);
|
|
88
|
+
return item;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
update(context: ExecutionContext): ExecutionContext {
|
|
92
|
+
return writeCheckpoint(context, this.checkpointKey, { cursor: this.cursor });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
close(): void {
|
|
96
|
+
this.buffer = [];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private get checkpointKey(): string {
|
|
100
|
+
return this.options.checkpointKey ?? 'database-cursor-reader';
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface DatabaseBatchItemWriterOptions<T> {
|
|
105
|
+
writeBatch(items: readonly T[]): Promise<WriterResult | void>;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export class DatabaseBatchItemWriter<T> implements ItemWriter<T> {
|
|
109
|
+
constructor(private readonly options: DatabaseBatchItemWriterOptions<T>) {}
|
|
110
|
+
|
|
111
|
+
write(items: T[]): Promise<WriterResult | void> {
|
|
112
|
+
return this.options.writeBatch(items);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
|
|
3
|
+
import type { ExecutionContext, ItemReader, ItemStream } from '../core';
|
|
4
|
+
|
|
5
|
+
import { readCheckpointNumber, writeCheckpoint } from './checkpoint';
|
|
6
|
+
|
|
7
|
+
export interface RestartableFileLineReaderOptions<T = string> {
|
|
8
|
+
readonly path: string;
|
|
9
|
+
readonly encoding?: BufferEncoding;
|
|
10
|
+
readonly checkpointKey?: string;
|
|
11
|
+
readonly skipLines?: number;
|
|
12
|
+
readonly skipBlankLines?: boolean;
|
|
13
|
+
readonly mapLine?: (line: string, lineIndex: number) => T | null | Promise<T | null>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class RestartableFileLineReader<T = string>
|
|
17
|
+
implements ItemReader<T>, ItemStream
|
|
18
|
+
{
|
|
19
|
+
private lines: string[] = [];
|
|
20
|
+
private index = 0;
|
|
21
|
+
|
|
22
|
+
constructor(private readonly options: RestartableFileLineReaderOptions<T>) {}
|
|
23
|
+
|
|
24
|
+
async open(context: ExecutionContext): Promise<void> {
|
|
25
|
+
const raw = await readFile(this.options.path, this.options.encoding ?? 'utf8');
|
|
26
|
+
this.lines = splitLines(raw);
|
|
27
|
+
this.index = readCheckpointNumber(
|
|
28
|
+
context,
|
|
29
|
+
this.checkpointKey,
|
|
30
|
+
'index',
|
|
31
|
+
this.options.skipLines ?? 0,
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async read(): Promise<T | null> {
|
|
36
|
+
while (this.index < this.lines.length) {
|
|
37
|
+
const lineIndex = this.index;
|
|
38
|
+
const line = this.lines[this.index]!;
|
|
39
|
+
this.index += 1;
|
|
40
|
+
if (this.options.skipBlankLines === true && line.trim().length === 0) continue;
|
|
41
|
+
if (this.options.mapLine !== undefined) {
|
|
42
|
+
const mapped = await this.options.mapLine(line, lineIndex);
|
|
43
|
+
if (mapped === null) continue;
|
|
44
|
+
return mapped;
|
|
45
|
+
}
|
|
46
|
+
return line as T;
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
update(context: ExecutionContext): ExecutionContext {
|
|
52
|
+
return writeCheckpoint(context, this.checkpointKey, { index: this.index });
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
close(): void {
|
|
56
|
+
this.lines = [];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private get checkpointKey(): string {
|
|
60
|
+
return this.options.checkpointKey ?? `file-line:${this.options.path}`;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface CsvFileItemReaderOptions<T extends Record<string, unknown>> {
|
|
65
|
+
readonly path: string;
|
|
66
|
+
readonly encoding?: BufferEncoding;
|
|
67
|
+
readonly checkpointKey?: string;
|
|
68
|
+
readonly delimiter?: string;
|
|
69
|
+
readonly headers?: readonly string[];
|
|
70
|
+
readonly hasHeader?: boolean;
|
|
71
|
+
readonly skipBlankLines?: boolean;
|
|
72
|
+
readonly mapRow?: (
|
|
73
|
+
row: Record<string, string>,
|
|
74
|
+
lineIndex: number,
|
|
75
|
+
) => T | null | Promise<T | null>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export class CsvFileItemReader<T extends Record<string, unknown> = Record<string, string>>
|
|
79
|
+
implements ItemReader<T>, ItemStream
|
|
80
|
+
{
|
|
81
|
+
private lines: string[] = [];
|
|
82
|
+
private headers: readonly string[] = [];
|
|
83
|
+
private index = 0;
|
|
84
|
+
|
|
85
|
+
constructor(private readonly options: CsvFileItemReaderOptions<T>) {}
|
|
86
|
+
|
|
87
|
+
async open(context: ExecutionContext): Promise<void> {
|
|
88
|
+
const raw = await readFile(this.options.path, this.options.encoding ?? 'utf8');
|
|
89
|
+
this.lines = splitLines(raw);
|
|
90
|
+
const hasHeader = this.options.hasHeader ?? this.options.headers === undefined;
|
|
91
|
+
this.headers =
|
|
92
|
+
this.options.headers ??
|
|
93
|
+
(this.lines.length > 0
|
|
94
|
+
? parseDelimitedLine(this.lines[0]!, this.delimiter)
|
|
95
|
+
: []);
|
|
96
|
+
const firstDataLine = hasHeader ? 1 : 0;
|
|
97
|
+
this.index = readCheckpointNumber(context, this.checkpointKey, 'index', firstDataLine);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async read(): Promise<T | null> {
|
|
101
|
+
while (this.index < this.lines.length) {
|
|
102
|
+
const lineIndex = this.index;
|
|
103
|
+
const line = this.lines[this.index]!;
|
|
104
|
+
this.index += 1;
|
|
105
|
+
if (this.options.skipBlankLines !== false && line.trim().length === 0) continue;
|
|
106
|
+
const fields = parseDelimitedLine(line, this.delimiter);
|
|
107
|
+
const row: Record<string, string> = {};
|
|
108
|
+
for (let i = 0; i < this.headers.length; i += 1) {
|
|
109
|
+
row[this.headers[i]!] = fields[i] ?? '';
|
|
110
|
+
}
|
|
111
|
+
if (this.options.mapRow !== undefined) {
|
|
112
|
+
const mapped = await this.options.mapRow(row, lineIndex);
|
|
113
|
+
if (mapped === null) continue;
|
|
114
|
+
return mapped;
|
|
115
|
+
}
|
|
116
|
+
return row as T;
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
update(context: ExecutionContext): ExecutionContext {
|
|
122
|
+
return writeCheckpoint(context, this.checkpointKey, { index: this.index });
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
close(): void {
|
|
126
|
+
this.lines = [];
|
|
127
|
+
this.headers = [];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private get checkpointKey(): string {
|
|
131
|
+
return this.options.checkpointKey ?? `csv:${this.options.path}`;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private get delimiter(): string {
|
|
135
|
+
return this.options.delimiter ?? ',';
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface JsonlFileItemReaderOptions<T> {
|
|
140
|
+
readonly path: string;
|
|
141
|
+
readonly encoding?: BufferEncoding;
|
|
142
|
+
readonly checkpointKey?: string;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export class JsonlFileItemReader<T = unknown> extends RestartableFileLineReader<T> {
|
|
146
|
+
constructor(options: JsonlFileItemReaderOptions<T>) {
|
|
147
|
+
super({
|
|
148
|
+
...options,
|
|
149
|
+
skipBlankLines: true,
|
|
150
|
+
mapLine: (line) => JSON.parse(line) as T,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function splitLines(raw: string | Buffer): string[] {
|
|
156
|
+
const text = typeof raw === 'string' ? raw : raw.toString('utf8');
|
|
157
|
+
const withoutBom = text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
|
|
158
|
+
return withoutBom.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function parseDelimitedLine(line: string, delimiter = ','): string[] {
|
|
162
|
+
const out: string[] = [];
|
|
163
|
+
let field = '';
|
|
164
|
+
let quoted = false;
|
|
165
|
+
for (let i = 0; i < line.length; i += 1) {
|
|
166
|
+
const ch = line[i]!;
|
|
167
|
+
if (quoted) {
|
|
168
|
+
if (ch === '"' && line[i + 1] === '"') {
|
|
169
|
+
field += '"';
|
|
170
|
+
i += 1;
|
|
171
|
+
} else if (ch === '"') {
|
|
172
|
+
quoted = false;
|
|
173
|
+
} else {
|
|
174
|
+
field += ch;
|
|
175
|
+
}
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (ch === '"') {
|
|
179
|
+
quoted = true;
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (ch === delimiter) {
|
|
183
|
+
out.push(field);
|
|
184
|
+
field = '';
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
field += ch;
|
|
188
|
+
}
|
|
189
|
+
out.push(field);
|
|
190
|
+
return out;
|
|
191
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { appendFile } from 'node:fs/promises';
|
|
2
|
+
|
|
3
|
+
import type { ExecutionContext, ItemStream, ItemWriter, WriterResult } from '../core';
|
|
4
|
+
|
|
5
|
+
import { readCheckpointValue, writeCheckpoint } from './checkpoint';
|
|
6
|
+
import { parseDelimitedLine } from './file-readers';
|
|
7
|
+
|
|
8
|
+
export interface JsonlFileItemWriterOptions {
|
|
9
|
+
readonly path: string;
|
|
10
|
+
readonly encoding?: BufferEncoding;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class JsonlFileItemWriter<T = unknown> implements ItemWriter<T> {
|
|
14
|
+
constructor(private readonly options: JsonlFileItemWriterOptions) {}
|
|
15
|
+
|
|
16
|
+
async write(items: T[]): Promise<void> {
|
|
17
|
+
if (items.length === 0) return;
|
|
18
|
+
const body = `${items.map((item) => JSON.stringify(item)).join('\n')}\n`;
|
|
19
|
+
await appendFile(this.options.path, body, this.options.encoding ?? 'utf8');
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface CsvFileItemWriterOptions<T extends Record<string, unknown>> {
|
|
24
|
+
readonly path: string;
|
|
25
|
+
readonly headers: readonly (keyof T & string)[];
|
|
26
|
+
readonly encoding?: BufferEncoding;
|
|
27
|
+
readonly delimiter?: string;
|
|
28
|
+
readonly checkpointKey?: string;
|
|
29
|
+
readonly writeHeader?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class CsvFileItemWriter<T extends Record<string, unknown>>
|
|
33
|
+
implements ItemWriter<T>, ItemStream
|
|
34
|
+
{
|
|
35
|
+
private headerWritten = false;
|
|
36
|
+
|
|
37
|
+
constructor(private readonly options: CsvFileItemWriterOptions<T>) {}
|
|
38
|
+
|
|
39
|
+
open(context: ExecutionContext): void {
|
|
40
|
+
this.headerWritten = readCheckpointValue(
|
|
41
|
+
context,
|
|
42
|
+
this.checkpointKey,
|
|
43
|
+
'headerWritten',
|
|
44
|
+
false,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async write(items: T[]): Promise<WriterResult | void> {
|
|
49
|
+
if (items.length === 0) return;
|
|
50
|
+
const lines: string[] = [];
|
|
51
|
+
if ((this.options.writeHeader ?? true) && !this.headerWritten) {
|
|
52
|
+
lines.push(this.options.headers.map((h) => escapeCsvValue(h, this.delimiter)).join(this.delimiter));
|
|
53
|
+
this.headerWritten = true;
|
|
54
|
+
}
|
|
55
|
+
for (const item of items) {
|
|
56
|
+
lines.push(
|
|
57
|
+
this.options.headers
|
|
58
|
+
.map((h) => escapeCsvValue(item[h], this.delimiter))
|
|
59
|
+
.join(this.delimiter),
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
await appendFile(
|
|
63
|
+
this.options.path,
|
|
64
|
+
`${lines.join('\n')}\n`,
|
|
65
|
+
this.options.encoding ?? 'utf8',
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
update(context: ExecutionContext): ExecutionContext {
|
|
70
|
+
return writeCheckpoint(context, this.checkpointKey, {
|
|
71
|
+
headerWritten: this.headerWritten,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
close(): void {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
private get checkpointKey(): string {
|
|
80
|
+
return this.options.checkpointKey ?? `csv-writer:${this.options.path}`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private get delimiter(): string {
|
|
84
|
+
return this.options.delimiter ?? ',';
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function escapeCsvValue(value: unknown, delimiter = ','): string {
|
|
89
|
+
const text = value === null || value === undefined ? '' : String(value);
|
|
90
|
+
if (
|
|
91
|
+
text.includes('"') ||
|
|
92
|
+
text.includes('\n') ||
|
|
93
|
+
text.includes('\r') ||
|
|
94
|
+
parseDelimitedLine(text, delimiter).length > 1
|
|
95
|
+
) {
|
|
96
|
+
return `"${text.replace(/"/g, '""')}"`;
|
|
97
|
+
}
|
|
98
|
+
return text;
|
|
99
|
+
}
|
package/src/io/index.ts
ADDED
package/src/io/s3.ts
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import type { ExecutionContext, ItemReader, ItemStream, ItemWriter } from '../core';
|
|
2
|
+
|
|
3
|
+
import { readCheckpointNumber, writeCheckpoint } from './checkpoint';
|
|
4
|
+
import { splitLines } from './file-readers';
|
|
5
|
+
|
|
6
|
+
export interface S3ObjectLocation {
|
|
7
|
+
readonly bucket: string;
|
|
8
|
+
readonly key: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface S3GetObjectInput extends S3ObjectLocation {}
|
|
12
|
+
|
|
13
|
+
export interface S3PutObjectInput extends S3ObjectLocation {
|
|
14
|
+
readonly body: string | Uint8Array;
|
|
15
|
+
readonly contentType?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface S3ObjectClient {
|
|
19
|
+
getObject(input: S3GetObjectInput): Promise<{ readonly body: string | Uint8Array }>;
|
|
20
|
+
putObject(input: S3PutObjectInput): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface S3JsonlItemReaderOptions<T> extends S3ObjectLocation {
|
|
24
|
+
readonly client: S3ObjectClient;
|
|
25
|
+
readonly checkpointKey?: string;
|
|
26
|
+
readonly mapItem?: (item: unknown, lineIndex: number) => T | Promise<T>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class S3JsonlItemReader<T = unknown> implements ItemReader<T>, ItemStream {
|
|
30
|
+
private lines: string[] = [];
|
|
31
|
+
private index = 0;
|
|
32
|
+
|
|
33
|
+
constructor(private readonly options: S3JsonlItemReaderOptions<T>) {}
|
|
34
|
+
|
|
35
|
+
async open(context: ExecutionContext): Promise<void> {
|
|
36
|
+
const object = await this.options.client.getObject({
|
|
37
|
+
bucket: this.options.bucket,
|
|
38
|
+
key: this.options.key,
|
|
39
|
+
});
|
|
40
|
+
this.lines = splitLines(bodyToString(object.body));
|
|
41
|
+
this.index = readCheckpointNumber(context, this.checkpointKey, 'index', 0);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async read(): Promise<T | null> {
|
|
45
|
+
while (this.index < this.lines.length) {
|
|
46
|
+
const lineIndex = this.index;
|
|
47
|
+
const line = this.lines[this.index]!;
|
|
48
|
+
this.index += 1;
|
|
49
|
+
if (line.trim().length === 0) continue;
|
|
50
|
+
const parsed = JSON.parse(line) as unknown;
|
|
51
|
+
return this.options.mapItem !== undefined
|
|
52
|
+
? this.options.mapItem(parsed, lineIndex)
|
|
53
|
+
: (parsed as T);
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
update(context: ExecutionContext): ExecutionContext {
|
|
59
|
+
return writeCheckpoint(context, this.checkpointKey, { index: this.index });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
close(): void {
|
|
63
|
+
this.lines = [];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
private get checkpointKey(): string {
|
|
67
|
+
return this.options.checkpointKey ?? `s3-jsonl:${this.options.bucket}/${this.options.key}`;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface S3ChunkJsonlItemWriterOptions {
|
|
72
|
+
readonly client: S3ObjectClient;
|
|
73
|
+
readonly bucket: string;
|
|
74
|
+
readonly keyPrefix: string;
|
|
75
|
+
readonly checkpointKey?: string;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export class S3ChunkJsonlItemWriter<T = unknown> implements ItemWriter<T>, ItemStream {
|
|
79
|
+
private chunkIndex = 0;
|
|
80
|
+
|
|
81
|
+
constructor(private readonly options: S3ChunkJsonlItemWriterOptions) {}
|
|
82
|
+
|
|
83
|
+
open(context: ExecutionContext): void {
|
|
84
|
+
this.chunkIndex = readCheckpointNumber(context, this.checkpointKey, 'chunkIndex', 0);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async write(items: T[]): Promise<void> {
|
|
88
|
+
if (items.length === 0) return;
|
|
89
|
+
const key = `${this.options.keyPrefix.replace(/\/+$/g, '')}/chunk-${String(this.chunkIndex).padStart(6, '0')}.jsonl`;
|
|
90
|
+
const body = `${items.map((item) => JSON.stringify(item)).join('\n')}\n`;
|
|
91
|
+
await this.options.client.putObject({
|
|
92
|
+
bucket: this.options.bucket,
|
|
93
|
+
key,
|
|
94
|
+
body,
|
|
95
|
+
contentType: 'application/x-ndjson',
|
|
96
|
+
});
|
|
97
|
+
this.chunkIndex += 1;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
update(context: ExecutionContext): ExecutionContext {
|
|
101
|
+
return writeCheckpoint(context, this.checkpointKey, {
|
|
102
|
+
chunkIndex: this.chunkIndex,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
close(): void {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private get checkpointKey(): string {
|
|
111
|
+
return this.options.checkpointKey ?? `s3-jsonl-writer:${this.options.bucket}/${this.options.keyPrefix}`;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function bodyToString(body: string | Uint8Array): string {
|
|
116
|
+
return typeof body === 'string' ? body : Buffer.from(body).toString('utf8');
|
|
117
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reference built-in listeners — drop-in `Injectable` classes that mirror common
|
|
3
|
+
* batch lifecycle conventions (logging, metrics, timing). Each listener exposes
|
|
4
|
+
* the lifecycle methods consumed by `ListenerInvoker` and uses the canonical
|
|
5
|
+
* `ResolverMap` key format (`${phase}:${kind}:${name}`), so a user can simply
|
|
6
|
+
* instantiate one of these classes and register the bound methods under the
|
|
7
|
+
* desired phase/kind pairs.
|
|
8
|
+
*
|
|
9
|
+
* The classes are intentionally framework-agnostic — they do not depend on
|
|
10
|
+
* the execution pipeline, the `ResolverMap` shape, or any module wiring. They
|
|
11
|
+
* only depend on `@nestjs/common` for `Logger` / `Injectable`.
|
|
12
|
+
*/
|
|
13
|
+
import { Injectable, Logger } from '@nestjs/common';
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// LoggingListener
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Emits a one-line `log` / `warn` entry for every lifecycle event. The method
|
|
21
|
+
* names match the 7 `LifecyclePhaseKind` values plus the 3 `SkipSubKind`
|
|
22
|
+
* variants, so callers can register any of them under a `ResolverMap` key
|
|
23
|
+
* like `before:step:LoggingListener` and the invoker will dispatch correctly.
|
|
24
|
+
*/
|
|
25
|
+
@Injectable()
|
|
26
|
+
export class LoggingListener {
|
|
27
|
+
private readonly logger = new Logger(LoggingListener.name);
|
|
28
|
+
|
|
29
|
+
// -- Job -----------------------------------------------------------------
|
|
30
|
+
async beforeJob(ctx: { jobExecutionId: string }): Promise<void> {
|
|
31
|
+
this.logger.log(`Job ${ctx.jobExecutionId} starting`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async afterJob(
|
|
35
|
+
ctx: { jobExecutionId: string },
|
|
36
|
+
result: { status: string },
|
|
37
|
+
): Promise<void> {
|
|
38
|
+
this.logger.log(`Job ${ctx.jobExecutionId} ${result.status}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// -- Step ----------------------------------------------------------------
|
|
42
|
+
async beforeStep(ctx: {
|
|
43
|
+
jobExecutionId: string;
|
|
44
|
+
stepExecutionId: string;
|
|
45
|
+
}): Promise<void> {
|
|
46
|
+
this.logger.log(`Step ${ctx.stepExecutionId} starting`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async afterStep(
|
|
50
|
+
ctx: { jobExecutionId: string; stepExecutionId: string },
|
|
51
|
+
result: { status: string; exitCode: string },
|
|
52
|
+
): Promise<void> {
|
|
53
|
+
this.logger.log(
|
|
54
|
+
`Step ${ctx.stepExecutionId} ${result.status} (${result.exitCode})`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// -- Skip ----------------------------------------------------------------
|
|
59
|
+
async onSkipInRead(err: unknown, item: unknown): Promise<void> {
|
|
60
|
+
this.logger.warn(
|
|
61
|
+
`Skipped read: ${(err as Error).message} (item=${String(item)})`,
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async onSkipInProcess(item: unknown, err: unknown): Promise<void> {
|
|
66
|
+
this.logger.warn(
|
|
67
|
+
`Skipped process: ${(err as Error).message} (item=${String(item)})`,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async onSkipInWrite(items: unknown[], err: unknown): Promise<void> {
|
|
72
|
+
this.logger.warn(
|
|
73
|
+
`Skipped write of ${items.length} items: ${(err as Error).message}`,
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
// MetricsListener
|
|
80
|
+
// ---------------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Per-step read / write / skip counters. The counts are keyed by
|
|
84
|
+
* `stepExecutionId`, so multiple step executions within the same job are
|
|
85
|
+
* tracked independently. Callers can read the latest counts via
|
|
86
|
+
* `getCounts(stepExecutionId)`.
|
|
87
|
+
*/
|
|
88
|
+
@Injectable()
|
|
89
|
+
export class MetricsListener {
|
|
90
|
+
private readonly stepCounts = new Map<
|
|
91
|
+
string,
|
|
92
|
+
{ read: number; write: number; skip: number }
|
|
93
|
+
>();
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Store the counts reported by the step result. Missing fields default to 0
|
|
97
|
+
* so a partial result (e.g. a tasklet step that has no read/write/skip
|
|
98
|
+
* counts) does not pollute the metric with `NaN`/`undefined`.
|
|
99
|
+
*/
|
|
100
|
+
async afterStep(
|
|
101
|
+
ctx: { stepExecutionId: string },
|
|
102
|
+
result: {
|
|
103
|
+
readCount?: number;
|
|
104
|
+
writeCount?: number;
|
|
105
|
+
skipCount?: number;
|
|
106
|
+
status: string;
|
|
107
|
+
},
|
|
108
|
+
): Promise<void> {
|
|
109
|
+
this.stepCounts.set(ctx.stepExecutionId, {
|
|
110
|
+
read: result.readCount ?? 0,
|
|
111
|
+
write: result.writeCount ?? 0,
|
|
112
|
+
skip: result.skipCount ?? 0,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Returns the latest recorded counts for the given step, or `undefined` if
|
|
117
|
+
* the step has not been observed yet. */
|
|
118
|
+
getCounts(
|
|
119
|
+
stepExecutionId: string,
|
|
120
|
+
): { read: number; write: number; skip: number } | undefined {
|
|
121
|
+
return this.stepCounts.get(stepExecutionId);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
// TimingListener
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Records the wall-clock duration of each step. `beforeStep` captures
|
|
131
|
+
* `Date.now()`; `afterStep` returns the elapsed milliseconds (or 0 if no
|
|
132
|
+
* matching `beforeStep` was observed — this keeps the listener idempotent
|
|
133
|
+
* even when invoked out of order, e.g. after a process restart that replayed
|
|
134
|
+
* a partial log).
|
|
135
|
+
*/
|
|
136
|
+
@Injectable()
|
|
137
|
+
export class TimingListener {
|
|
138
|
+
private readonly startTimes = new Map<string, number>();
|
|
139
|
+
|
|
140
|
+
async beforeStep(ctx: { stepExecutionId: string }): Promise<void> {
|
|
141
|
+
this.startTimes.set(ctx.stepExecutionId, Date.now());
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async afterStep(ctx: { stepExecutionId: string }): Promise<number> {
|
|
145
|
+
const start = this.startTimes.get(ctx.stepExecutionId);
|
|
146
|
+
if (start !== undefined) {
|
|
147
|
+
return Date.now() - start;
|
|
148
|
+
}
|
|
149
|
+
return 0;
|
|
150
|
+
}
|
|
151
|
+
}
|