@nest-batch/core 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -5
- package/dist/src/adapters/in-process.adapter.d.ts +16 -13
- package/dist/src/adapters/in-process.adapter.d.ts.map +1 -1
- package/dist/src/adapters/in-process.adapter.js +2 -0
- package/dist/src/adapters/in-process.adapter.js.map +1 -1
- package/dist/src/compiler/definition-compiler.d.ts.map +1 -1
- package/dist/src/compiler/definition-compiler.js +3 -0
- package/dist/src/compiler/definition-compiler.js.map +1 -1
- package/dist/src/core/ir/listener-definition.d.ts +2 -0
- package/dist/src/core/ir/listener-definition.d.ts.map +1 -1
- package/dist/src/core/item/interfaces.d.ts +14 -4
- package/dist/src/core/item/interfaces.d.ts.map +1 -1
- package/dist/src/decorators/listener.decorators.d.ts +4 -3
- package/dist/src/decorators/listener.decorators.d.ts.map +1 -1
- package/dist/src/decorators/listener.decorators.js +6 -3
- package/dist/src/decorators/listener.decorators.js.map +1 -1
- package/dist/src/execution/chunk-step-executor.d.ts +7 -1
- package/dist/src/execution/chunk-step-executor.d.ts.map +1 -1
- package/dist/src/execution/chunk-step-executor.js +104 -13
- package/dist/src/execution/chunk-step-executor.js.map +1 -1
- package/dist/src/execution/in-process-schedule.d.ts +25 -0
- package/dist/src/execution/in-process-schedule.d.ts.map +1 -0
- package/dist/src/execution/in-process-schedule.js +129 -0
- package/dist/src/execution/in-process-schedule.js.map +1 -0
- package/dist/src/execution/index.d.ts +1 -0
- package/dist/src/execution/index.d.ts.map +1 -1
- package/dist/src/execution/index.js +1 -0
- package/dist/src/execution/index.js.map +1 -1
- package/dist/src/execution/job-executor.d.ts.map +1 -1
- package/dist/src/execution/job-executor.js +14 -8
- package/dist/src/execution/job-executor.js.map +1 -1
- package/dist/src/execution/listener-invoker.d.ts +25 -9
- package/dist/src/execution/listener-invoker.d.ts.map +1 -1
- package/dist/src/execution/listener-invoker.js +70 -14
- package/dist/src/execution/listener-invoker.js.map +1 -1
- package/dist/src/execution/tasklet-step-executor.d.ts +4 -1
- package/dist/src/execution/tasklet-step-executor.d.ts.map +1 -1
- package/dist/src/execution/tasklet-step-executor.js +20 -16
- package/dist/src/execution/tasklet-step-executor.js.map +1 -1
- package/dist/src/explorer/batch-explorer.d.ts +2 -1
- package/dist/src/explorer/batch-explorer.d.ts.map +1 -1
- package/dist/src/explorer/batch-explorer.js +3 -0
- package/dist/src/explorer/batch-explorer.js.map +1 -1
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/module/batch-schedule-registry.d.ts +13 -14
- package/dist/src/module/batch-schedule-registry.d.ts.map +1 -1
- package/dist/src/module/batch-schedule-registry.js +0 -0
- package/dist/src/module/batch-schedule-registry.js.map +1 -1
- package/dist/src/module/nest-batch.module.d.ts +4 -3
- package/dist/src/module/nest-batch.module.d.ts.map +1 -1
- package/dist/src/module/nest-batch.module.js +3 -2
- package/dist/src/module/nest-batch.module.js.map +1 -1
- package/dist/src/module/tokens.d.ts +5 -6
- package/dist/src/module/tokens.d.ts.map +1 -1
- package/dist/src/module/tokens.js.map +1 -1
- package/dist/src/partition-helpers.d.ts +3 -3
- package/dist/src/partition-helpers.d.ts.map +1 -1
- package/dist/src/partition-helpers.js +3 -3
- package/dist/src/partition-helpers.js.map +1 -1
- package/dist/src/scheduling/batch-scheduled.d.ts +9 -11
- package/dist/src/scheduling/batch-scheduled.d.ts.map +1 -1
- package/dist/src/scheduling/batch-scheduled.js +12 -20
- package/dist/src/scheduling/batch-scheduled.js.map +1 -1
- package/package.json +4 -1
- package/src/adapters/in-process.adapter.ts +18 -13
- package/src/compiler/definition-compiler.ts +12 -5
- package/src/core/ir/listener-definition.ts +2 -0
- package/src/core/item/interfaces.ts +15 -4
- package/src/decorators/listener.decorators.ts +11 -13
- package/src/execution/chunk-step-executor.ts +212 -18
- package/src/execution/in-process-schedule.ts +143 -0
- package/src/execution/index.ts +1 -0
- package/src/execution/job-executor.ts +30 -21
- package/src/execution/listener-invoker.ts +105 -27
- package/src/execution/tasklet-step-executor.ts +40 -16
- package/src/explorer/batch-explorer.ts +10 -4
- package/src/index.ts +1 -0
- package/src/module/batch-schedule-registry.ts +0 -0
- package/src/module/nest-batch.module.ts +21 -42
- package/src/module/tokens.ts +8 -15
- package/src/partition-helpers.ts +13 -17
- package/src/scheduling/batch-scheduled.ts +22 -32
package/README.md
CHANGED
|
@@ -306,7 +306,8 @@ The barrel re-exports:
|
|
|
306
306
|
- `./execution` — `JobLauncher`, `JobExecutor`, `InProcessExecutionStrategy`, `IExecutionStrategy`, `EXECUTION_STRATEGY`, `ChunkStepExecutor`, `TaskletStepExecutor`, `ListenerInvoker`, `RefResolver`.
|
|
307
307
|
- `./transaction` — `TransactionManager` token and contract.
|
|
308
308
|
- `./repository` — `JobRepository` token, contract, in-memory reference, ID generators.
|
|
309
|
-
- `./decorators` — under the `BatchDecorators` namespace (`@Jobable`, `@ItemReader`, `@ItemProcessor`, `@ItemWriter`, `@Tasklet`, listener decorators
|
|
309
|
+
- `./decorators` — under the `BatchDecorators` namespace (`@Jobable`, `@ItemReader`, `@ItemProcessor`, `@ItemWriter`, `@Tasklet`, listener decorators).
|
|
310
|
+
- `./scheduling/batch-scheduled` — `@BatchScheduled` and its schedule option/error types are also re-exported directly from the package root.
|
|
310
311
|
- `./module` — `NestBatchModule`, tokens, options.
|
|
311
312
|
- `./builder` — fluent `BatchBuilder`, `JobBuilder`, `StepBuilder`, `FlowBuilder`.
|
|
312
313
|
- `./explorer` — `BatchExplorer` (the metadata scanner).
|
|
@@ -329,8 +330,8 @@ injected at the DI boundary:
|
|
|
329
330
|
|
|
330
331
|
| Concern | Package | Why |
|
|
331
332
|
| ----------------------------- | ----------------------- | ----------------------------------------------------------------- |
|
|
332
|
-
| **Persistence (
|
|
333
|
-
| **Persistence (TypeORM 1.0)** | `@nest-batch/typeorm` |
|
|
333
|
+
| **Persistence (MikroORM)** | `@nest-batch/mikro-orm` | Exposes the batch meta entities; the host owns migrations. |
|
|
334
|
+
| **Persistence (TypeORM 1.0)** | `@nest-batch/typeorm` | Exposes the same table contract as TypeORM 1.0.0 entities. |
|
|
334
335
|
| **Transport (BullMQ)** | `@nest-batch/bullmq` | The Redis-backed execution strategy. Owns Queue/Worker lifecycle. |
|
|
335
336
|
| **Drizzle** | _not in this release_ | Explicitly excluded and deferred. See `MIGRATION.md`. |
|
|
336
337
|
|
|
@@ -364,5 +365,6 @@ pnpm --filter @nest-batch/core typecheck # tsc --noEmit
|
|
|
364
365
|
|
|
365
366
|
The boundary test (`tests/core/boundary/no-forbidden-imports.test.ts`)
|
|
366
367
|
guards core's dependency-light promise. It fails the build if any
|
|
367
|
-
forbidden package (`bullmq`, `mikro-orm`, `typeorm`,
|
|
368
|
-
`
|
|
368
|
+
forbidden integration package (`bullmq`, `mikro-orm`, `typeorm`,
|
|
369
|
+
`drizzle-orm`) shows up as a core import. The small `cron` dependency
|
|
370
|
+
is intentionally allowed for the built-in in-process scheduler bridge.
|
|
@@ -76,6 +76,16 @@ export declare class InProcessModule {
|
|
|
76
76
|
* some "config" into it, you almost certainly want a real transport
|
|
77
77
|
* adapter instead.
|
|
78
78
|
*
|
|
79
|
+
* ## Scheduling
|
|
80
|
+
*
|
|
81
|
+
* `InProcessSchedule` is registered as a transport global provider.
|
|
82
|
+
* It consumes `BatchScheduleRegistry` after discovery/bootstrap and
|
|
83
|
+
* turns non-inert `@BatchScheduled` cron ticks into
|
|
84
|
+
* `JobLauncher.launch(...)` calls in this same process. That gives
|
|
85
|
+
* single-process apps a real cron path without Redis or another
|
|
86
|
+
* queue. It is intentionally not a distributed lock: multiple app
|
|
87
|
+
* replicas will each run their own timer.
|
|
88
|
+
*
|
|
79
89
|
* ## DI scope
|
|
80
90
|
*
|
|
81
91
|
* The module is `global: true` and exports both the strategy class
|
|
@@ -94,23 +104,16 @@ export declare class InProcessModule {
|
|
|
94
104
|
* pattern uniform across the engine and its adapters — the host
|
|
95
105
|
* author only needs to learn one module-visibility model.
|
|
96
106
|
*
|
|
97
|
-
* ## Why `globalProviders` is
|
|
107
|
+
* ## Why `globalProviders` is used
|
|
98
108
|
*
|
|
99
109
|
* The `BatchAdapter` interface allows a `globalProviders` field for
|
|
100
110
|
* runtime classes (e.g. `JobExecutor`, `InProcessExecutionStrategy`)
|
|
101
111
|
* that the adapter's *own* module needs to inject but that the host
|
|
102
|
-
* should also be able to inject.
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
* works without the core module having to know which adapter is
|
|
108
|
-
* active.
|
|
109
|
-
*
|
|
110
|
-
* If a future in-process feature needs to expose a new provider to
|
|
111
|
-
* the host (e.g. an inline scheduler), prefer adding it to
|
|
112
|
-
* `exports` and updating the `BatchAdapter.globalProviders` decision
|
|
113
|
-
* — do not push it onto the host app's `providers` array.
|
|
112
|
+
* should also be able to inject. This adapter lists
|
|
113
|
+
* `InProcessExecutionStrategy`, `InProcessSchedule`, and the
|
|
114
|
+
* `EXECUTION_STRATEGY` binding there so `NestBatchModule` can merge
|
|
115
|
+
* them into the same provider graph as `JobLauncher`,
|
|
116
|
+
* `BatchScheduleRegistry`, and the executor subgraph.
|
|
114
117
|
*
|
|
115
118
|
* ## Concurrency
|
|
116
119
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"in-process.adapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/in-process.adapter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"in-process.adapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/in-process.adapter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAQtD;;;;;;;;;GASG;AACH,qBACa,eAAe;CAAG;AAE/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8GG;AACH,qBAAa,gBAAgB;IAC3B;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,OAAO,IAAI,YAAY;CAW/B"}
|
|
@@ -18,6 +18,7 @@ _export(exports, {
|
|
|
18
18
|
});
|
|
19
19
|
const _common = require("@nestjs/common");
|
|
20
20
|
const _inprocessexecutionstrategy = require("../execution/in-process-execution-strategy");
|
|
21
|
+
const _inprocessschedule = require("../execution/in-process-schedule");
|
|
21
22
|
function _ts_decorate(decorators, target, key, desc) {
|
|
22
23
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
23
24
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -50,6 +51,7 @@ let InProcessAdapter = class InProcessAdapter {
|
|
|
50
51
|
module: buildInProcessDynamicModule(),
|
|
51
52
|
globalProviders: [
|
|
52
53
|
_inprocessexecutionstrategy.InProcessExecutionStrategy,
|
|
54
|
+
_inprocessschedule.InProcessSchedule,
|
|
53
55
|
_inprocessexecutionstrategy.IN_PROCESS_EXECUTION_STRATEGY_PROVIDER
|
|
54
56
|
]
|
|
55
57
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/adapters/in-process.adapter.ts"],"sourcesContent":["import { Module, type DynamicModule } from '@nestjs/common';\n\nimport type { BatchAdapter } from '../module/adapter';\nimport { EXECUTION_STRATEGY } from '../execution/execution-strategy';\nimport {\n IN_PROCESS_EXECUTION_STRATEGY_PROVIDER,\n InProcessExecutionStrategy,\n} from '../execution/in-process-execution-strategy';\n\n/**\n * Empty Nest module class that owns the in-process execution-strategy\n * providers.\n *\n * The class has no body on purpose: it is purely a `DynamicModule`\n * carrier for the `forRoot()` factory below. Nest's module system\n * requires *some* class to identify the module — the empty class is\n * the minimum possible surface and keeps the runtime allocation at\n * one class (no decorators, no lifecycle hooks, no metadata).\n */\n@Module({})\nexport class InProcessModule {}\n\n/**\n * `InProcessAdapter` — the default transport adapter for\n * `@nest-batch/core`.\n *\n * This is the **no-Redis** transport: jobs run synchronously inside\n * the launching process via `JobExecutor.execute(...)`, on the same\n * event loop that called `JobLauncher.launch(...)`. There is no\n * queue, no worker, no Redis connection, no AOF / Lua scripts /\n * stream events. The whole point of this adapter is to be the\n * \"no transport runtime at all\" option.\n *\n * Use it when:\n *\n * - You do not need horizontal scale-out (one process, one\n * launcher, jobs run inline).\n * - You want the cheapest possible deployment — no extra\n * infrastructure, no extra process to supervise.\n * - You are building a library / dev-time harness and the queue\n * runtime would be in the way.\n * - You are migrating an existing batch app and want to validate\n * the engine end-to-end before turning on a real transport.\n *\n * Switch to `@nest-batch/bullmq` (or a future transport) when you\n * need cross-process work distribution, technical retry at the\n * transport layer, or a queue-backed backpressure model. The\n * `IExecutionStrategy` polymorphism means the application code does\n * not change — only the `transport` slot in `adapters: { ... }` does.\n *\n * ## Why a dedicated adapter (and not a built-in default)?\n *\n * The new factory-pattern API takes `adapters: { persistence,\n * transport }` and both slots are *required* (see\n * `BatchAdaptersConfig`). Shipping the in-process transport as a\n * dedicated `BatchAdapter` rather than a hidden implicit default\n * keeps the `AppModule` wiring explicit at the call site — you can\n * read the host's `imports` array and see exactly which transport is\n * active. That pays off the first time you debug a \"why is this\n * running inline?\" question and need to grep for the transport.\n *\n * ## Wiring\n *\n * ```ts\n * import { Module } from '@nestjs/common';\n * import { NestBatchModule, InProcessAdapter } from '@nest-batch/core';\n * import { MikroOrmAdapter } from '@nest-batch/mikro-orm';\n *\n * @Module({\n * imports: [\n * NestBatchModule.forRoot({\n * adapters: {\n * persistence: MikroOrmAdapter,\n * transport: InProcessAdapter,\n * },\n * }),\n * ],\n * })\n * class AppModule {}\n * ```\n *\n * `InProcessAdapter.forRoot()` takes no options — the in-process\n * transport has no connection params, no credentials, no knobs to\n * tune. If you find yourself reaching for a `useFactory` to plumb\n * some \"config\" into it, you almost certainly want a real transport\n * adapter instead.\n *\n * ## DI scope\n *\n * The module is `global: true` and exports both the strategy class\n * and the `EXECUTION_STRATEGY` token. Three reasons for the `global`\n * flag:\n *\n * 1. `JobLauncher` (registered by `NestBatchModule`) is `@Inject(\n * EXECUTION_STRATEGY )` — it needs the token visible at the\n * application level, not just inside the adapter's own module.\n * 2. The host application is the only place that may want to\n * inspect the strategy at runtime (e.g. for a `/healthz`\n * endpoint reporting which transport is active). The `global`\n * flag makes that work without forcing the host to re-import\n * this module from every sub-module.\n * 3. Mirroring `NestBatchModule`'s own `global: true` keeps the\n * pattern uniform across the engine and its adapters — the host\n * author only needs to learn one module-visibility model.\n *\n * ## Why `globalProviders` is
|
|
1
|
+
{"version":3,"sources":["../../../src/adapters/in-process.adapter.ts"],"sourcesContent":["import { Module, type DynamicModule } from '@nestjs/common';\n\nimport type { BatchAdapter } from '../module/adapter';\nimport { EXECUTION_STRATEGY } from '../execution/execution-strategy';\nimport {\n IN_PROCESS_EXECUTION_STRATEGY_PROVIDER,\n InProcessExecutionStrategy,\n} from '../execution/in-process-execution-strategy';\nimport { InProcessSchedule } from '../execution/in-process-schedule';\n\n/**\n * Empty Nest module class that owns the in-process execution-strategy\n * providers.\n *\n * The class has no body on purpose: it is purely a `DynamicModule`\n * carrier for the `forRoot()` factory below. Nest's module system\n * requires *some* class to identify the module — the empty class is\n * the minimum possible surface and keeps the runtime allocation at\n * one class (no decorators, no lifecycle hooks, no metadata).\n */\n@Module({})\nexport class InProcessModule {}\n\n/**\n * `InProcessAdapter` — the default transport adapter for\n * `@nest-batch/core`.\n *\n * This is the **no-Redis** transport: jobs run synchronously inside\n * the launching process via `JobExecutor.execute(...)`, on the same\n * event loop that called `JobLauncher.launch(...)`. There is no\n * queue, no worker, no Redis connection, no AOF / Lua scripts /\n * stream events. The whole point of this adapter is to be the\n * \"no transport runtime at all\" option.\n *\n * Use it when:\n *\n * - You do not need horizontal scale-out (one process, one\n * launcher, jobs run inline).\n * - You want the cheapest possible deployment — no extra\n * infrastructure, no extra process to supervise.\n * - You are building a library / dev-time harness and the queue\n * runtime would be in the way.\n * - You are migrating an existing batch app and want to validate\n * the engine end-to-end before turning on a real transport.\n *\n * Switch to `@nest-batch/bullmq` (or a future transport) when you\n * need cross-process work distribution, technical retry at the\n * transport layer, or a queue-backed backpressure model. The\n * `IExecutionStrategy` polymorphism means the application code does\n * not change — only the `transport` slot in `adapters: { ... }` does.\n *\n * ## Why a dedicated adapter (and not a built-in default)?\n *\n * The new factory-pattern API takes `adapters: { persistence,\n * transport }` and both slots are *required* (see\n * `BatchAdaptersConfig`). Shipping the in-process transport as a\n * dedicated `BatchAdapter` rather than a hidden implicit default\n * keeps the `AppModule` wiring explicit at the call site — you can\n * read the host's `imports` array and see exactly which transport is\n * active. That pays off the first time you debug a \"why is this\n * running inline?\" question and need to grep for the transport.\n *\n * ## Wiring\n *\n * ```ts\n * import { Module } from '@nestjs/common';\n * import { NestBatchModule, InProcessAdapter } from '@nest-batch/core';\n * import { MikroOrmAdapter } from '@nest-batch/mikro-orm';\n *\n * @Module({\n * imports: [\n * NestBatchModule.forRoot({\n * adapters: {\n * persistence: MikroOrmAdapter,\n * transport: InProcessAdapter,\n * },\n * }),\n * ],\n * })\n * class AppModule {}\n * ```\n *\n * `InProcessAdapter.forRoot()` takes no options — the in-process\n * transport has no connection params, no credentials, no knobs to\n * tune. If you find yourself reaching for a `useFactory` to plumb\n * some \"config\" into it, you almost certainly want a real transport\n * adapter instead.\n *\n * ## Scheduling\n *\n * `InProcessSchedule` is registered as a transport global provider.\n * It consumes `BatchScheduleRegistry` after discovery/bootstrap and\n * turns non-inert `@BatchScheduled` cron ticks into\n * `JobLauncher.launch(...)` calls in this same process. That gives\n * single-process apps a real cron path without Redis or another\n * queue. It is intentionally not a distributed lock: multiple app\n * replicas will each run their own timer.\n *\n * ## DI scope\n *\n * The module is `global: true` and exports both the strategy class\n * and the `EXECUTION_STRATEGY` token. Three reasons for the `global`\n * flag:\n *\n * 1. `JobLauncher` (registered by `NestBatchModule`) is `@Inject(\n * EXECUTION_STRATEGY )` — it needs the token visible at the\n * application level, not just inside the adapter's own module.\n * 2. The host application is the only place that may want to\n * inspect the strategy at runtime (e.g. for a `/healthz`\n * endpoint reporting which transport is active). The `global`\n * flag makes that work without forcing the host to re-import\n * this module from every sub-module.\n * 3. Mirroring `NestBatchModule`'s own `global: true` keeps the\n * pattern uniform across the engine and its adapters — the host\n * author only needs to learn one module-visibility model.\n *\n * ## Why `globalProviders` is used\n *\n * The `BatchAdapter` interface allows a `globalProviders` field for\n * runtime classes (e.g. `JobExecutor`, `InProcessExecutionStrategy`)\n * that the adapter's *own* module needs to inject but that the host\n * should also be able to inject. This adapter lists\n * `InProcessExecutionStrategy`, `InProcessSchedule`, and the\n * `EXECUTION_STRATEGY` binding there so `NestBatchModule` can merge\n * them into the same provider graph as `JobLauncher`,\n * `BatchScheduleRegistry`, and the executor subgraph.\n *\n * ## Concurrency\n *\n * The default in-process strategy runs jobs on the caller's event\n * loop. A long-running step will block the launching process. This\n * is the contract: no concurrency, no parallelism, no out-of-band\n * execution. If you need concurrency, switch transports.\n */\nexport class InProcessAdapter {\n /**\n * Build the `BatchAdapter` value the new factory-pattern\n * `NestBatchModule.forRoot({ adapters: { transport, ... } })`\n * expects.\n *\n * No options are accepted on purpose — the in-process transport\n * has nothing to configure. The method is static so the adapter\n * can be referenced as a value (`adapters: { transport:\n * InProcessAdapter }`) without needing an instance, mirroring\n * the shape of the sibling adapter packages' own factories.\n *\n * @returns A `BatchAdapter` whose `module` is a `global: true`\n * `DynamicModule` exposing `InProcessExecutionStrategy` and the\n * `EXECUTION_STRATEGY` token to the host application.\n */\n static forRoot(): BatchAdapter {\n return {\n name: 'in-process',\n module: buildInProcessDynamicModule(),\n globalProviders: [\n InProcessExecutionStrategy,\n InProcessSchedule,\n IN_PROCESS_EXECUTION_STRATEGY_PROVIDER,\n ],\n };\n }\n}\n\n/**\n * Build the `DynamicModule` payload for the in-process transport.\n *\n * Extracted from `InProcessAdapter.forRoot()` so the provider /\n * export list lives in one place — easier to read, easier to keep\n * the two arrays in sync if a new provider is ever added.\n *\n * The `EXECUTION_STRATEGY` token is exported (not just listed in\n * `providers`) so that host code can resolve the strategy directly\n * via `moduleRef.get(EXECUTION_STRATEGY)` — useful for `/healthz`\n * endpoints that need to report which transport is wired up.\n *\n * `InProcessExecutionStrategy` is also exported so host code can\n * inject the concrete class (not just the token) when type-strict\n * consumers prefer the class form.\n */\nfunction buildInProcessDynamicModule(): DynamicModule {\n return {\n module: InProcessModule,\n global: true,\n providers: [IN_PROCESS_EXECUTION_STRATEGY_PROVIDER],\n exports: [IN_PROCESS_EXECUTION_STRATEGY_PROVIDER],\n };\n}\n"],"names":["InProcessAdapter","InProcessModule","forRoot","name","module","buildInProcessDynamicModule","globalProviders","InProcessExecutionStrategy","InProcessSchedule","IN_PROCESS_EXECUTION_STRATEGY_PROVIDER","global","providers","exports"],"mappings":";;;;;;;;;;;QAsIaA;eAAAA;;QAjHAC;eAAAA;;;wBArB8B;4CAOpC;mCAC2B;;;;;;;AAa3B,IAAA,AAAMA,kBAAN,MAAMA;AAAiB;;;;AAiHvB,IAAA,AAAMD,mBAAN,MAAMA;IACX;;;;;;;;;;;;;;GAcC,GACD,OAAOE,UAAwB;QAC7B,OAAO;YACLC,MAAM;YACNC,QAAQC;YACRC,iBAAiB;gBACfC,sDAA0B;gBAC1BC,oCAAiB;gBACjBC,kEAAsC;aACvC;QACH;IACF;AACF;AAEA;;;;;;;;;;;;;;;CAeC,GACD,SAASJ;IACP,OAAO;QACLD,QAAQH;QACRS,QAAQ;QACRC,WAAW;YAACF,kEAAsC;SAAC;QACnDG,SAAS;YAACH,kEAAsC;SAAC;IACnD;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definition-compiler.d.ts","sourceRoot":"","sources":["../../../src/compiler/definition-compiler.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,OAAO,EAEL,KAAK,aAAa,EAWnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,UAAU,EAAyB,MAAM,gBAAgB,CAAC;AAGnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAMhE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAExD;;;;;;;;GAQG;AACH,qBAAa,qBAAsB,SAAQ,UAAU;IACnD,QAAQ,CAAC,IAAI,wBAAwB;gBACzB,KAAK,EAAE,MAAM;CAG1B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBACa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuC;IAC9D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6B;IAEvD;;;OAGG;IACH,qBAAqB,CAAC,UAAU,EAAE,aAAa,GAAG,aAAa;
|
|
1
|
+
{"version":3,"file":"definition-compiler.d.ts","sourceRoot":"","sources":["../../../src/compiler/definition-compiler.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,OAAO,EAEL,KAAK,aAAa,EAWnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,UAAU,EAAyB,MAAM,gBAAgB,CAAC;AAGnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAMhE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAExD;;;;;;;;GAQG;AACH,qBAAa,qBAAsB,SAAQ,UAAU;IACnD,QAAQ,CAAC,IAAI,wBAAwB;gBACzB,KAAK,EAAE,MAAM;CAG1B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBACa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuC;IAC9D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6B;IAEvD;;;OAGG;IACH,qBAAqB,CAAC,UAAU,EAAE,aAAa,GAAG,aAAa;IAkD/D;;;OAGG;IACH,wBAAwB,CAAC,MAAM,EAAE,gBAAgB,GAAG,aAAa;IAqBjE;;;;;;;;;;;;;;;;;OAiBG;IACH,OAAO,CAAC,gBAAgB;IAkBxB;;;;;;;OAOG;IACH,OAAO,CAAC,gBAAgB;IAoBxB;;;;;;OAMG;IACH,OAAO,CAAC,cAAc;IAqEtB,OAAO,CAAC,kBAAkB;IAsB1B;;;;;;;;OAQG;IACH,OAAO,CAAC,cAAc;CAavB"}
|
|
@@ -58,6 +58,9 @@ let DefinitionCompiler = class DefinitionCompiler {
|
|
|
58
58
|
const listenerDefs = discovered.listenerMethods.map((l)=>({
|
|
59
59
|
kind: l.kind,
|
|
60
60
|
phase: l.phase,
|
|
61
|
+
...l.skipKind !== undefined ? {
|
|
62
|
+
skipKind: l.skipKind
|
|
63
|
+
} : {},
|
|
61
64
|
nonCritical: l.nonCritical,
|
|
62
65
|
ref: this.buildListenerRef(discovered, classToken, l.methodName)
|
|
63
66
|
}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/compiler/definition-compiler.ts"],"sourcesContent":["import 'reflect-metadata';\nimport { Injectable, Logger } from '@nestjs/common';\nimport {\n RefKind,\n type JobDefinition,\n type StepDefinition,\n type ChunkStepDefinition,\n type TaskletStepDefinition,\n type ReaderRef,\n type ProcessorRef,\n type WriterRef,\n type TaskletRef,\n type ListenerRef,\n type ListenerDefinition,\n type TransitionDefinition,\n} from '../core/ir';\nimport { FlowExecutionStatus } from '../core/status';\nimport { BatchError, InvalidFlowGraphError } from '../core/errors';\nimport { DefinitionValidator } from '../core/validation/definition-validator';\nimport { validatePartitions, InvalidPartitionsError } from '../partition-helpers';\nimport type { DiscoveredJob } from '../explorer/batch-explorer';\nimport {\n BATCH_ITEM_READER_METADATA,\n BATCH_ITEM_PROCESSOR_METADATA,\n BATCH_ITEM_WRITER_METADATA,\n} from '../decorators/constants';\nimport type { JobBuilderConfig } from './builder-types';\n\n/**\n * Thrown when a chunk step on a discovered class cannot resolve a required\n * item handler method (`@ItemReader` or `@ItemWriter`) on the class prototype.\n *\n * Distinct from `InvalidFlowGraphError` because this is a *static* class-shape\n * problem (the class is missing a decorator), not a flow-graph problem\n * (transitions, start, reachability). The `code` is stable for callers\n * that want to switch on it.\n */\nexport class ProviderNotFoundError extends BatchError {\n readonly code = 'PROVIDER_NOT_FOUND';\n constructor(token: string) {\n super(`Provider not found: ${token}`, { token });\n }\n}\n\n/**\n * `DefinitionCompiler` is the bridge between metadata-rich sources\n * (decorator-discovered classes, fluent builder configs) and the\n * `JobDefinition` IR consumed by the rest of the runtime.\n *\n * Two compilation paths share the same output type:\n *\n * - `compileFromDiscovered(discovered)` walks the class prototype\n * (resolved by `BatchExplorer` in Task 7) and binds reader /\n * processor / writer / tasklet / listener methods into `Ref`s.\n * - `compileFromBuilderConfig(config)` accepts a plain-data config\n * from the builder API and copies it into a `JobDefinition`.\n *\n * Both paths run the same `DefinitionValidator` before returning, so\n * downstream consumers can assume the IR is structurally sound.\n *\n * The compiler does NOT register the job — that is `JobRegistry`'s job\n * (Task 9). The compiler is pure: it produces IR, nothing else.\n */\n@Injectable()\nexport class DefinitionCompiler {\n private readonly logger = new Logger(DefinitionCompiler.name);\n private readonly validator = new DefinitionValidator();\n\n /**\n * Compile from a discovered class. Resolves reader/processor/writer methods\n * on the class prototype and assembles StepDefinitions. Validates before returning.\n */\n compileFromDiscovered(discovered: DiscoveredJob): JobDefinition {\n const steps: Record<string, StepDefinition> = {};\n const startStepId = discovered.stepMethods[0]?.options.id ?? '';\n const classToken = discovered.classRef.name;\n\n for (const step of discovered.stepMethods) {\n if (step.isTasklet) {\n steps[step.options.id] = this.buildTaskletStep(discovered, classToken, step);\n } else {\n steps[step.options.id] = this.buildChunkStep(discovered, classToken, step);\n }\n }\n\n const listenerDefs: ListenerDefinition[] = discovered.listenerMethods.map((l) => ({\n kind: l.kind,\n phase: l.phase,\n nonCritical: l.nonCritical,\n ref: this.buildListenerRef(discovered, classToken, l.methodName),\n }));\n\n const transitions: TransitionDefinition[] = discovered.transitionMethods.map((t) => ({\n fromStepId: t.fromStep,\n onStatus:\n (FlowExecutionStatus as Record<string, FlowExecutionStatus>)[t.onStatus] ??\n FlowExecutionStatus.UNKNOWN,\n toStepId: t.toStep,\n }));\n\n const job: JobDefinition = {\n id: discovered.jobOptions.id,\n steps,\n startStepId,\n transitions,\n deciders: [],\n listeners: listenerDefs,\n restartable: discovered.jobOptions.restartable ?? false,\n allowDuplicateInstances: discovered.jobOptions.allowDuplicateInstances ?? false,\n };\n\n this.logger.log(\n `Compiled job \"${job.id}\" from \"${classToken}\": ${\n Object.keys(steps).length\n } step(s), ${listenerDefs.length} listener(s), ${transitions.length} transition(s)`,\n );\n\n this.validator.validate(job);\n return job;\n }\n\n /**\n * Compile from a builder config. Used by the fluent Builder API.\n * Same validation as `compileFromDiscovered`.\n */\n compileFromBuilderConfig(config: JobBuilderConfig): JobDefinition {\n const steps: Record<string, StepDefinition> = {};\n for (const s of config.steps) {\n steps[s.id] = s;\n }\n const job: JobDefinition = {\n id: config.id,\n steps,\n startStepId: config.startStepId,\n transitions: config.transitions,\n deciders: config.deciders ?? [],\n listeners: config.listeners,\n restartable: config.restartable,\n allowDuplicateInstances: config.allowDuplicateInstances,\n };\n this.validator.validate(job);\n return job;\n }\n\n // --- private helpers --------------------------------------------------\n\n /**\n * Resolve a listener method on a discovered class to a callable\n * `ListenerRef`. Mirrors the tasklet-ref resolution in\n * `buildTaskletStep`: if the DI container has already instantiated\n * the class (which is the case by the time the explorer walks\n * providers at `onModuleInit`), the method is pre-bound to the\n * instance and the returned ref is a `BuilderLambda` carrying the\n * bound function. This lets the runtime resolver map call the\n * listener directly without holding onto the instance or a\n * `ModuleRef`.\n *\n * When the instance is not yet available (factory providers that\n * have not been instantiated, late-bound providers, etc.) the ref\n * stays as a `Method` and the runtime resolver will throw a\n * deterministic error if it cannot resolve the class. The\n * pre-binding is a pure optimisation for the common\n * `providers: [MyClass]` case — the test suite exercises that path.\n */\n private buildListenerRef(\n discovered: DiscoveredJob,\n classToken: string,\n methodName: string,\n ): ListenerRef {\n const instance = discovered.instance as\n | Record<string, (...args: unknown[]) => unknown>\n | undefined;\n const method = instance?.[methodName];\n if (method) {\n return {\n kind: RefKind.BuilderLambda,\n fn: method.bind(discovered.instance) as (...args: any[]) => unknown,\n };\n }\n return { kind: RefKind.Method, classToken, methodName };\n }\n\n /**\n * Build a `TaskletStepDefinition` for a `@Stepable` + `@Tasklet` method.\n *\n * If a DI-resolved instance is available on the `DiscoveredJob`, the\n * tasklet ref is a `BuilderLambda` (bound function) so the executor\n * can call it directly. Otherwise we fall back to a `Method` ref that\n * the executor resolves at runtime against the DI container.\n */\n private buildTaskletStep(\n discovered: DiscoveredJob,\n classToken: string,\n step: DiscoveredJob['stepMethods'][number],\n ): TaskletStepDefinition {\n const instance = discovered.instance as\n | Record<string, (...args: unknown[]) => unknown>\n | undefined;\n const method = instance?.[step.methodName];\n const taskletRef: TaskletRef = method\n ? { kind: RefKind.BuilderLambda, fn: () => method.bind(discovered.instance) }\n : { kind: RefKind.Method, classToken, methodName: step.methodName };\n return {\n kind: 'tasklet',\n id: step.options.id,\n tasklet: taskletRef,\n listeners: [],\n };\n }\n\n /**\n * Build a `ChunkStepDefinition` for a `@Stepable` (no `@Tasklet`) method.\n * Requires `@ItemReader` and `@ItemWriter` on the class; `@ItemProcessor`\n * is optional.\n *\n * Throws `ProviderNotFoundError` if a required handler is missing.\n */\n private buildChunkStep(\n discovered: DiscoveredJob,\n classToken: string,\n step: DiscoveredJob['stepMethods'][number],\n ): ChunkStepDefinition {\n const reader = this.findItemMethod(discovered, BATCH_ITEM_READER_METADATA);\n const processor = this.findItemMethod(discovered, BATCH_ITEM_PROCESSOR_METADATA);\n const writer = this.findItemMethod(discovered, BATCH_ITEM_WRITER_METADATA);\n\n if (!reader) {\n throw new ProviderNotFoundError(\n `@ItemReader for job ${discovered.jobOptions.id} (step ${step.options.id})`,\n );\n }\n if (!writer) {\n throw new ProviderNotFoundError(\n `@ItemWriter for job ${discovered.jobOptions.id} (step ${step.options.id})`,\n );\n }\n\n const readerRef: ReaderRef = this.buildItemMethodRef(discovered, classToken, reader);\n const writerRef: WriterRef = this.buildItemMethodRef(discovered, classToken, writer);\n\n // Validate the partition config at compile time so a typo\n // (e.g. `count: 0`) fails at module load rather than at\n // runtime when the launcher pre-creates the execution.\n // `DefinitionValidator.validate` does the same check later\n // (the IR is the source of truth), but the compiler is the\n // first place we have the value in hand and we want the\n // earliest possible failure for a decorator-discovered job.\n try {\n validatePartitions(step.options.partitions);\n } catch (err) {\n if (err instanceof InvalidPartitionsError) {\n throw new InvalidFlowGraphError(\n 'INVALID_PARTITIONS',\n `Step \"${step.options.id}\" has invalid partitions: ${err.message}`,\n { jobId: discovered.jobOptions.id, stepId: step.options.id, partitions: step.options.partitions },\n );\n }\n throw err;\n }\n\n return {\n kind: 'chunk',\n id: step.options.id,\n chunkSize: step.options.chunkSize ?? 100,\n reader: readerRef,\n writer: writerRef,\n skipPolicy: step.options.skipPolicy,\n retryPolicy: step.options.retryPolicy,\n listeners: [],\n ...(step.options.partitions !== undefined\n ? { partitions: step.options.partitions }\n : {}),\n ...(processor\n ? {\n processor: this.buildItemMethodRef(discovered, classToken, processor) satisfies ProcessorRef,\n }\n : {}),\n };\n }\n\n private buildItemMethodRef(\n discovered: DiscoveredJob,\n classToken: string,\n methodName: string,\n ): ReaderRef | ProcessorRef | WriterRef {\n const instance = discovered.instance as\n | Record<string, (...args: unknown[]) => unknown>\n | undefined;\n const method = instance?.[methodName];\n if (method) {\n return {\n kind: RefKind.BuilderLambda,\n fn: () => method.bind(discovered.instance),\n };\n }\n return {\n kind: RefKind.Method,\n classToken,\n methodName,\n };\n }\n\n /**\n * Walks the prototype chain (including inherited prototypes, stopping\n * at `Object.prototype`) looking for a method carrying the given\n * `BATCH_ITEM_*` metadata key.\n *\n * Returns the method name or `undefined`. The explorer only records\n * `@Stepable` / listener / transition methods; item handlers are\n * resolved here at compile time.\n */\n private findItemMethod(discovered: DiscoveredJob, metadataKey: string): string | undefined {\n const proto = discovered.classRef.prototype as object | undefined;\n if (!proto) return undefined;\n let p: object | null = proto;\n while (p && p !== Object.prototype) {\n for (const name of Object.getOwnPropertyNames(p)) {\n if (name === 'constructor') continue;\n if (Reflect.getMetadata(metadataKey, p, name) === true) return name;\n }\n p = Object.getPrototypeOf(p);\n }\n return undefined;\n }\n}\n"],"names":["DefinitionCompiler","ProviderNotFoundError","BatchError","code","token","logger","Logger","name","validator","DefinitionValidator","compileFromDiscovered","discovered","steps","startStepId","stepMethods","options","id","classToken","classRef","step","isTasklet","buildTaskletStep","buildChunkStep","listenerDefs","listenerMethods","map","l","kind","phase","nonCritical","ref","buildListenerRef","methodName","transitions","transitionMethods","t","fromStepId","fromStep","onStatus","FlowExecutionStatus","UNKNOWN","toStepId","toStep","job","jobOptions","deciders","listeners","restartable","allowDuplicateInstances","log","Object","keys","length","validate","compileFromBuilderConfig","config","s","instance","method","RefKind","BuilderLambda","fn","bind","Method","taskletRef","tasklet","reader","findItemMethod","BATCH_ITEM_READER_METADATA","processor","BATCH_ITEM_PROCESSOR_METADATA","writer","BATCH_ITEM_WRITER_METADATA","readerRef","buildItemMethodRef","writerRef","validatePartitions","partitions","err","InvalidPartitionsError","InvalidFlowGraphError","message","jobId","stepId","chunkSize","skipPolicy","retryPolicy","undefined","metadataKey","proto","prototype","p","getOwnPropertyNames","Reflect","getMetadata","getPrototypeOf"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/compiler/definition-compiler.ts"],"sourcesContent":["import 'reflect-metadata';\nimport { Injectable, Logger } from '@nestjs/common';\nimport {\n RefKind,\n type JobDefinition,\n type StepDefinition,\n type ChunkStepDefinition,\n type TaskletStepDefinition,\n type ReaderRef,\n type ProcessorRef,\n type WriterRef,\n type TaskletRef,\n type ListenerRef,\n type ListenerDefinition,\n type TransitionDefinition,\n} from '../core/ir';\nimport { FlowExecutionStatus } from '../core/status';\nimport { BatchError, InvalidFlowGraphError } from '../core/errors';\nimport { DefinitionValidator } from '../core/validation/definition-validator';\nimport { validatePartitions, InvalidPartitionsError } from '../partition-helpers';\nimport type { DiscoveredJob } from '../explorer/batch-explorer';\nimport {\n BATCH_ITEM_READER_METADATA,\n BATCH_ITEM_PROCESSOR_METADATA,\n BATCH_ITEM_WRITER_METADATA,\n} from '../decorators/constants';\nimport type { JobBuilderConfig } from './builder-types';\n\n/**\n * Thrown when a chunk step on a discovered class cannot resolve a required\n * item handler method (`@ItemReader` or `@ItemWriter`) on the class prototype.\n *\n * Distinct from `InvalidFlowGraphError` because this is a *static* class-shape\n * problem (the class is missing a decorator), not a flow-graph problem\n * (transitions, start, reachability). The `code` is stable for callers\n * that want to switch on it.\n */\nexport class ProviderNotFoundError extends BatchError {\n readonly code = 'PROVIDER_NOT_FOUND';\n constructor(token: string) {\n super(`Provider not found: ${token}`, { token });\n }\n}\n\n/**\n * `DefinitionCompiler` is the bridge between metadata-rich sources\n * (decorator-discovered classes, fluent builder configs) and the\n * `JobDefinition` IR consumed by the rest of the runtime.\n *\n * Two compilation paths share the same output type:\n *\n * - `compileFromDiscovered(discovered)` walks the class prototype\n * (resolved by `BatchExplorer` in Task 7) and binds reader /\n * processor / writer / tasklet / listener methods into `Ref`s.\n * - `compileFromBuilderConfig(config)` accepts a plain-data config\n * from the builder API and copies it into a `JobDefinition`.\n *\n * Both paths run the same `DefinitionValidator` before returning, so\n * downstream consumers can assume the IR is structurally sound.\n *\n * The compiler does NOT register the job — that is `JobRegistry`'s job\n * (Task 9). The compiler is pure: it produces IR, nothing else.\n */\n@Injectable()\nexport class DefinitionCompiler {\n private readonly logger = new Logger(DefinitionCompiler.name);\n private readonly validator = new DefinitionValidator();\n\n /**\n * Compile from a discovered class. Resolves reader/processor/writer methods\n * on the class prototype and assembles StepDefinitions. Validates before returning.\n */\n compileFromDiscovered(discovered: DiscoveredJob): JobDefinition {\n const steps: Record<string, StepDefinition> = {};\n const startStepId = discovered.stepMethods[0]?.options.id ?? '';\n const classToken = discovered.classRef.name;\n\n for (const step of discovered.stepMethods) {\n if (step.isTasklet) {\n steps[step.options.id] = this.buildTaskletStep(discovered, classToken, step);\n } else {\n steps[step.options.id] = this.buildChunkStep(discovered, classToken, step);\n }\n }\n\n const listenerDefs: ListenerDefinition[] = discovered.listenerMethods.map((l) => ({\n kind: l.kind,\n phase: l.phase,\n ...(l.skipKind !== undefined ? { skipKind: l.skipKind } : {}),\n nonCritical: l.nonCritical,\n ref: this.buildListenerRef(discovered, classToken, l.methodName),\n }));\n\n const transitions: TransitionDefinition[] = discovered.transitionMethods.map((t) => ({\n fromStepId: t.fromStep,\n onStatus:\n (FlowExecutionStatus as Record<string, FlowExecutionStatus>)[t.onStatus] ??\n FlowExecutionStatus.UNKNOWN,\n toStepId: t.toStep,\n }));\n\n const job: JobDefinition = {\n id: discovered.jobOptions.id,\n steps,\n startStepId,\n transitions,\n deciders: [],\n listeners: listenerDefs,\n restartable: discovered.jobOptions.restartable ?? false,\n allowDuplicateInstances: discovered.jobOptions.allowDuplicateInstances ?? false,\n };\n\n this.logger.log(\n `Compiled job \"${job.id}\" from \"${classToken}\": ${\n Object.keys(steps).length\n } step(s), ${listenerDefs.length} listener(s), ${transitions.length} transition(s)`,\n );\n\n this.validator.validate(job);\n return job;\n }\n\n /**\n * Compile from a builder config. Used by the fluent Builder API.\n * Same validation as `compileFromDiscovered`.\n */\n compileFromBuilderConfig(config: JobBuilderConfig): JobDefinition {\n const steps: Record<string, StepDefinition> = {};\n for (const s of config.steps) {\n steps[s.id] = s;\n }\n const job: JobDefinition = {\n id: config.id,\n steps,\n startStepId: config.startStepId,\n transitions: config.transitions,\n deciders: config.deciders ?? [],\n listeners: config.listeners,\n restartable: config.restartable,\n allowDuplicateInstances: config.allowDuplicateInstances,\n };\n this.validator.validate(job);\n return job;\n }\n\n // --- private helpers --------------------------------------------------\n\n /**\n * Resolve a listener method on a discovered class to a callable\n * `ListenerRef`. Mirrors the tasklet-ref resolution in\n * `buildTaskletStep`: if the DI container has already instantiated\n * the class (which is the case by the time the explorer walks\n * providers at `onModuleInit`), the method is pre-bound to the\n * instance and the returned ref is a `BuilderLambda` carrying the\n * bound function. This lets the runtime resolver map call the\n * listener directly without holding onto the instance or a\n * `ModuleRef`.\n *\n * When the instance is not yet available (factory providers that\n * have not been instantiated, late-bound providers, etc.) the ref\n * stays as a `Method` and the runtime resolver will throw a\n * deterministic error if it cannot resolve the class. The\n * pre-binding is a pure optimisation for the common\n * `providers: [MyClass]` case — the test suite exercises that path.\n */\n private buildListenerRef(\n discovered: DiscoveredJob,\n classToken: string,\n methodName: string,\n ): ListenerRef {\n const instance = discovered.instance as\n | Record<string, (...args: unknown[]) => unknown>\n | undefined;\n const method = instance?.[methodName];\n if (method) {\n return {\n kind: RefKind.BuilderLambda,\n fn: method.bind(discovered.instance) as (...args: any[]) => unknown,\n };\n }\n return { kind: RefKind.Method, classToken, methodName };\n }\n\n /**\n * Build a `TaskletStepDefinition` for a `@Stepable` + `@Tasklet` method.\n *\n * If a DI-resolved instance is available on the `DiscoveredJob`, the\n * tasklet ref is a `BuilderLambda` (bound function) so the executor\n * can call it directly. Otherwise we fall back to a `Method` ref that\n * the executor resolves at runtime against the DI container.\n */\n private buildTaskletStep(\n discovered: DiscoveredJob,\n classToken: string,\n step: DiscoveredJob['stepMethods'][number],\n ): TaskletStepDefinition {\n const instance = discovered.instance as\n | Record<string, (...args: unknown[]) => unknown>\n | undefined;\n const method = instance?.[step.methodName];\n const taskletRef: TaskletRef = method\n ? { kind: RefKind.BuilderLambda, fn: () => method.bind(discovered.instance) }\n : { kind: RefKind.Method, classToken, methodName: step.methodName };\n return {\n kind: 'tasklet',\n id: step.options.id,\n tasklet: taskletRef,\n listeners: [],\n };\n }\n\n /**\n * Build a `ChunkStepDefinition` for a `@Stepable` (no `@Tasklet`) method.\n * Requires `@ItemReader` and `@ItemWriter` on the class; `@ItemProcessor`\n * is optional.\n *\n * Throws `ProviderNotFoundError` if a required handler is missing.\n */\n private buildChunkStep(\n discovered: DiscoveredJob,\n classToken: string,\n step: DiscoveredJob['stepMethods'][number],\n ): ChunkStepDefinition {\n const reader = this.findItemMethod(discovered, BATCH_ITEM_READER_METADATA);\n const processor = this.findItemMethod(discovered, BATCH_ITEM_PROCESSOR_METADATA);\n const writer = this.findItemMethod(discovered, BATCH_ITEM_WRITER_METADATA);\n\n if (!reader) {\n throw new ProviderNotFoundError(\n `@ItemReader for job ${discovered.jobOptions.id} (step ${step.options.id})`,\n );\n }\n if (!writer) {\n throw new ProviderNotFoundError(\n `@ItemWriter for job ${discovered.jobOptions.id} (step ${step.options.id})`,\n );\n }\n\n const readerRef: ReaderRef = this.buildItemMethodRef(discovered, classToken, reader);\n const writerRef: WriterRef = this.buildItemMethodRef(discovered, classToken, writer);\n\n // Validate the partition config at compile time so a typo\n // (e.g. `count: 0`) fails at module load rather than at\n // runtime when the launcher pre-creates the execution.\n // `DefinitionValidator.validate` does the same check later\n // (the IR is the source of truth), but the compiler is the\n // first place we have the value in hand and we want the\n // earliest possible failure for a decorator-discovered job.\n try {\n validatePartitions(step.options.partitions);\n } catch (err) {\n if (err instanceof InvalidPartitionsError) {\n throw new InvalidFlowGraphError(\n 'INVALID_PARTITIONS',\n `Step \"${step.options.id}\" has invalid partitions: ${err.message}`,\n {\n jobId: discovered.jobOptions.id,\n stepId: step.options.id,\n partitions: step.options.partitions,\n },\n );\n }\n throw err;\n }\n\n return {\n kind: 'chunk',\n id: step.options.id,\n chunkSize: step.options.chunkSize ?? 100,\n reader: readerRef,\n writer: writerRef,\n skipPolicy: step.options.skipPolicy,\n retryPolicy: step.options.retryPolicy,\n listeners: [],\n ...(step.options.partitions !== undefined ? { partitions: step.options.partitions } : {}),\n ...(processor\n ? {\n processor: this.buildItemMethodRef(\n discovered,\n classToken,\n processor,\n ) satisfies ProcessorRef,\n }\n : {}),\n };\n }\n\n private buildItemMethodRef(\n discovered: DiscoveredJob,\n classToken: string,\n methodName: string,\n ): ReaderRef | ProcessorRef | WriterRef {\n const instance = discovered.instance as\n | Record<string, (...args: unknown[]) => unknown>\n | undefined;\n const method = instance?.[methodName];\n if (method) {\n return {\n kind: RefKind.BuilderLambda,\n fn: () => method.bind(discovered.instance),\n };\n }\n return {\n kind: RefKind.Method,\n classToken,\n methodName,\n };\n }\n\n /**\n * Walks the prototype chain (including inherited prototypes, stopping\n * at `Object.prototype`) looking for a method carrying the given\n * `BATCH_ITEM_*` metadata key.\n *\n * Returns the method name or `undefined`. The explorer only records\n * `@Stepable` / listener / transition methods; item handlers are\n * resolved here at compile time.\n */\n private findItemMethod(discovered: DiscoveredJob, metadataKey: string): string | undefined {\n const proto = discovered.classRef.prototype as object | undefined;\n if (!proto) return undefined;\n let p: object | null = proto;\n while (p && p !== Object.prototype) {\n for (const name of Object.getOwnPropertyNames(p)) {\n if (name === 'constructor') continue;\n if (Reflect.getMetadata(metadataKey, p, name) === true) return name;\n }\n p = Object.getPrototypeOf(p);\n }\n return undefined;\n }\n}\n"],"names":["DefinitionCompiler","ProviderNotFoundError","BatchError","code","token","logger","Logger","name","validator","DefinitionValidator","compileFromDiscovered","discovered","steps","startStepId","stepMethods","options","id","classToken","classRef","step","isTasklet","buildTaskletStep","buildChunkStep","listenerDefs","listenerMethods","map","l","kind","phase","skipKind","undefined","nonCritical","ref","buildListenerRef","methodName","transitions","transitionMethods","t","fromStepId","fromStep","onStatus","FlowExecutionStatus","UNKNOWN","toStepId","toStep","job","jobOptions","deciders","listeners","restartable","allowDuplicateInstances","log","Object","keys","length","validate","compileFromBuilderConfig","config","s","instance","method","RefKind","BuilderLambda","fn","bind","Method","taskletRef","tasklet","reader","findItemMethod","BATCH_ITEM_READER_METADATA","processor","BATCH_ITEM_PROCESSOR_METADATA","writer","BATCH_ITEM_WRITER_METADATA","readerRef","buildItemMethodRef","writerRef","validatePartitions","partitions","err","InvalidPartitionsError","InvalidFlowGraphError","message","jobId","stepId","chunkSize","skipPolicy","retryPolicy","metadataKey","proto","prototype","p","getOwnPropertyNames","Reflect","getMetadata","getPrototypeOf"],"mappings":"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"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { ListenerRef } from './refs';
|
|
2
2
|
export type ListenerKind = 'job' | 'step' | 'chunk' | 'item-read' | 'item-process' | 'item-write' | 'skip' | 'transition';
|
|
3
3
|
export type ListenerPhase = 'before' | 'after' | 'on-error';
|
|
4
|
+
export type SkipSubKind = 'read' | 'process' | 'write';
|
|
4
5
|
export interface ListenerDefinition {
|
|
5
6
|
kind: ListenerKind;
|
|
6
7
|
ref: ListenerRef;
|
|
7
8
|
phase: ListenerPhase;
|
|
9
|
+
skipKind?: SkipSubKind;
|
|
8
10
|
nonCritical?: boolean;
|
|
9
11
|
}
|
|
10
12
|
//# sourceMappingURL=listener-definition.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"listener-definition.d.ts","sourceRoot":"","sources":["../../../../src/core/ir/listener-definition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAE1C,MAAM,MAAM,YAAY,GACpB,KAAK,GACL,MAAM,GACN,OAAO,GACP,WAAW,GACX,cAAc,GACd,YAAY,GACZ,MAAM,GACN,YAAY,CAAC;AACjB,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"listener-definition.d.ts","sourceRoot":"","sources":["../../../../src/core/ir/listener-definition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAE1C,MAAM,MAAM,YAAY,GACpB,KAAK,GACL,MAAM,GACN,OAAO,GACP,WAAW,GACX,cAAc,GACd,YAAY,GACZ,MAAM,GACN,YAAY,CAAC;AACjB,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,CAAC;AAC5D,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAEvD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,YAAY,CAAC;IACnB,GAAG,EAAE,WAAW,CAAC;IACjB,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB"}
|
|
@@ -1,18 +1,27 @@
|
|
|
1
|
-
import type { ExecutionContext } from '../repository/types';
|
|
1
|
+
import type { ExecutionContext, JobParameters } from '../repository/types';
|
|
2
2
|
type MaybePromise<T> = T | Promise<T>;
|
|
3
3
|
/**
|
|
4
4
|
* Reads one item at a time. Returns `null` to signal EOF.
|
|
5
5
|
* For async iteration, use `AsyncIterable.read()` (not yet supported in MVP).
|
|
6
6
|
*/
|
|
7
|
+
export interface ItemExecutionContext {
|
|
8
|
+
readonly jobExecutionId: string;
|
|
9
|
+
readonly stepExecutionId: string;
|
|
10
|
+
readonly stepName: string;
|
|
11
|
+
readonly jobParameters: JobParameters;
|
|
12
|
+
readonly chunkIndex?: number;
|
|
13
|
+
getExecutionContext(): Promise<ExecutionContext>;
|
|
14
|
+
saveExecutionContext(ctx: ExecutionContext): Promise<void>;
|
|
15
|
+
}
|
|
7
16
|
export interface ItemReader<T = unknown> {
|
|
8
|
-
read(): Promise<T | null>;
|
|
17
|
+
read(ctx?: ItemExecutionContext): Promise<T | null>;
|
|
9
18
|
}
|
|
10
19
|
/**
|
|
11
20
|
* Processes one item. Returns `null`/`undefined` to filter the item out of the chunk.
|
|
12
21
|
* Throws to indicate the item should be skipped (via SkipPolicy) or retried (via RetryPolicy).
|
|
13
22
|
*/
|
|
14
23
|
export interface ItemProcessor<I = unknown, O = unknown> {
|
|
15
|
-
process(item: I): Promise<O | null | undefined>;
|
|
24
|
+
process(item: I, ctx?: ItemExecutionContext): Promise<O | null | undefined>;
|
|
16
25
|
}
|
|
17
26
|
/**
|
|
18
27
|
* Writes a batch of items. Called once per chunk (after processing).
|
|
@@ -24,7 +33,7 @@ export interface ItemProcessor<I = unknown, O = unknown> {
|
|
|
24
33
|
* count and assumes no per-item skips.
|
|
25
34
|
*/
|
|
26
35
|
export interface ItemWriter<T = unknown> {
|
|
27
|
-
write(items: T[]): Promise<WriterResult | void>;
|
|
36
|
+
write(items: T[], ctx?: ItemExecutionContext): Promise<WriterResult | void>;
|
|
28
37
|
}
|
|
29
38
|
export interface WriterResult {
|
|
30
39
|
written: number;
|
|
@@ -50,6 +59,7 @@ export interface ItemStream {
|
|
|
50
59
|
export interface TaskletContext {
|
|
51
60
|
readonly jobExecutionId: string;
|
|
52
61
|
readonly stepExecutionId: string;
|
|
62
|
+
readonly jobParameters: JobParameters;
|
|
53
63
|
getExecutionContext(): Promise<ExecutionContext>;
|
|
54
64
|
saveExecutionContext(ctx: ExecutionContext): Promise<void>;
|
|
55
65
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../../../src/core/item/interfaces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAkB,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../../../src/core/item/interfaces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAkB,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAE3F,KAAK,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAEtC;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,mBAAmB,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACjD,oBAAoB,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D;AAED,MAAM,WAAW,UAAU,CAAC,CAAC,GAAG,OAAO;IACrC,IAAI,CAAC,GAAG,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;CACrD;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO;IACrD,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;CAC7E;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,UAAU,CAAC,CAAC,GAAG,OAAO;IACrC,KAAK,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;CAC7E;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,CAAC,OAAO,EAAE,gBAAgB,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IACpD,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,YAAY,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC;IACzE,KAAK,IAAI,YAAY,CAAC,IAAI,CAAC,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,mBAAmB,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACjD,oBAAoB,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5D;AAED;;;GAGG;AACH,MAAM,WAAW,OAAO;IACtB,OAAO,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAChD"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
|
-
import type { ListenerKind, ListenerPhase } from '../core/ir/listener-definition';
|
|
2
|
+
import type { ListenerKind, ListenerPhase, SkipSubKind } from '../core/ir/listener-definition';
|
|
3
3
|
/**
|
|
4
4
|
* Stored under `BATCH_LISTENER_METADATA` for each listener method.
|
|
5
5
|
* Mirrors `ListenerDefinition` minus the resolved `ref` (which the
|
|
@@ -7,12 +7,13 @@ import type { ListenerKind, ListenerPhase } from '../core/ir/listener-definition
|
|
|
7
7
|
*
|
|
8
8
|
* The `skip` kind is special: it has no before/after/on-error phase,
|
|
9
9
|
* it is a single fire-and-forget callback per skip event. We record
|
|
10
|
-
* `phase: 'after'` as a placeholder so the metadata shape stays uniform
|
|
11
|
-
*
|
|
10
|
+
* `phase: 'after'` as a placeholder so the metadata shape stays uniform,
|
|
11
|
+
* plus `skipKind` so read/process/write skip events remain distinguishable.
|
|
12
12
|
*/
|
|
13
13
|
export interface ListenerOptions {
|
|
14
14
|
kind: ListenerKind;
|
|
15
15
|
phase: ListenerPhase;
|
|
16
|
+
skipKind?: SkipSubKind;
|
|
16
17
|
nonCritical?: boolean;
|
|
17
18
|
}
|
|
18
19
|
/** Fires before a job execution starts. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"listener.decorators.d.ts","sourceRoot":"","sources":["../../../src/decorators/listener.decorators.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"listener.decorators.d.ts","sourceRoot":"","sources":["../../../src/decorators/listener.decorators.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAE/F;;;;;;;;;GASG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAoBD,2CAA2C;AAC3C,eAAO,MAAM,SAAS,QAAO,eAAsE,CAAC;AAEpG,mEAAmE;AACnE,eAAO,MAAM,QAAQ,QAAO,eAAqE,CAAC;AAMlG,4CAA4C;AAC5C,eAAO,MAAM,UAAU,QAAO,eACwB,CAAC;AAEvD,oEAAoE;AACpE,eAAO,MAAM,SAAS,QAAO,eAAsE,CAAC;AAMpG,iEAAiE;AACjE,eAAO,MAAM,WAAW,QAAO,eACwB,CAAC;AAExD,oDAAoD;AACpD,eAAO,MAAM,UAAU,QAAO,eACwB,CAAC;AAEvD,qEAAqE;AACrE,eAAO,MAAM,YAAY,QAAO,eACyB,CAAC;AAM1D,sCAAsC;AACtC,eAAO,MAAM,UAAU,QAAO,eAC6B,CAAC;AAE5D,kDAAkD;AAClD,eAAO,MAAM,SAAS,QAAO,eAC6B,CAAC;AAE3D,oCAAoC;AACpC,eAAO,MAAM,WAAW,QAAO,eAC8B,CAAC;AAM9D,2CAA2C;AAC3C,eAAO,MAAM,aAAa,QAAO,eAC6B,CAAC;AAE/D,uDAAuD;AACvD,eAAO,MAAM,YAAY,QAAO,eAC6B,CAAC;AAE9D,uCAAuC;AACvC,eAAO,MAAM,cAAc,QAAO,eAC8B,CAAC;AAMjE,gDAAgD;AAChD,eAAO,MAAM,WAAW,QAAO,eAC6B,CAAC;AAE7D,0DAA0D;AAC1D,eAAO,MAAM,UAAU,QAAO,eAC6B,CAAC;AAE5D,oCAAoC;AACpC,eAAO,MAAM,YAAY,QAAO,eAC8B,CAAC;AAW/D,4EAA4E;AAC5E,eAAO,MAAM,UAAU,QAAO,eACyC,CAAC;AAExE,8CAA8C;AAC9C,eAAO,MAAM,aAAa,QAAO,eACyC,CAAC;AAE3E,qCAAqC;AACrC,eAAO,MAAM,WAAW,QAAO,eACyC,CAAC"}
|
|
@@ -143,15 +143,18 @@ const OnWriteError = ()=>listenerDecorator({
|
|
|
143
143
|
});
|
|
144
144
|
const OnSkipRead = ()=>listenerDecorator({
|
|
145
145
|
kind: 'skip',
|
|
146
|
-
phase: 'after'
|
|
146
|
+
phase: 'after',
|
|
147
|
+
skipKind: 'read'
|
|
147
148
|
});
|
|
148
149
|
const OnSkipProcess = ()=>listenerDecorator({
|
|
149
150
|
kind: 'skip',
|
|
150
|
-
phase: 'after'
|
|
151
|
+
phase: 'after',
|
|
152
|
+
skipKind: 'process'
|
|
151
153
|
});
|
|
152
154
|
const OnSkipWrite = ()=>listenerDecorator({
|
|
153
155
|
kind: 'skip',
|
|
154
|
-
phase: 'after'
|
|
156
|
+
phase: 'after',
|
|
157
|
+
skipKind: 'write'
|
|
155
158
|
});
|
|
156
159
|
|
|
157
160
|
//# sourceMappingURL=listener.decorators.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/decorators/listener.decorators.ts"],"sourcesContent":["import 'reflect-metadata';\nimport { BATCH_LISTENER_METADATA } from './constants';\nimport type { ListenerKind, ListenerPhase } from '../core/ir/listener-definition';\n\n/**\n * Stored under `BATCH_LISTENER_METADATA` for each listener method.\n * Mirrors `ListenerDefinition` minus the resolved `ref` (which the\n * DefinitionCompiler fills in by walking the class).\n *\n * The `skip` kind is special: it has no before/after/on-error phase,\n * it is a single fire-and-forget callback per skip event. We record\n * `phase: 'after'` as a placeholder so the metadata shape stays uniform
|
|
1
|
+
{"version":3,"sources":["../../../src/decorators/listener.decorators.ts"],"sourcesContent":["import 'reflect-metadata';\nimport { BATCH_LISTENER_METADATA } from './constants';\nimport type { ListenerKind, ListenerPhase, SkipSubKind } from '../core/ir/listener-definition';\n\n/**\n * Stored under `BATCH_LISTENER_METADATA` for each listener method.\n * Mirrors `ListenerDefinition` minus the resolved `ref` (which the\n * DefinitionCompiler fills in by walking the class).\n *\n * The `skip` kind is special: it has no before/after/on-error phase,\n * it is a single fire-and-forget callback per skip event. We record\n * `phase: 'after'` as a placeholder so the metadata shape stays uniform,\n * plus `skipKind` so read/process/write skip events remain distinguishable.\n */\nexport interface ListenerOptions {\n kind: ListenerKind;\n phase: ListenerPhase;\n skipKind?: SkipSubKind;\n nonCritical?: boolean;\n}\n\nfunction defineListener(\n target: object,\n propertyKey: string | symbol,\n options: ListenerOptions,\n): void {\n Reflect.defineMetadata(BATCH_LISTENER_METADATA, options, target, propertyKey);\n}\n\nfunction listenerDecorator(options: ListenerOptions): MethodDecorator {\n return (target: object, propertyKey: string | symbol, _descriptor: PropertyDescriptor) => {\n defineListener(target, propertyKey, options);\n };\n}\n\n// ---------------------------------------------------------------------------\n// Job-level listeners (2)\n// ---------------------------------------------------------------------------\n\n/** Fires before a job execution starts. */\nexport const BeforeJob = (): MethodDecorator => listenerDecorator({ kind: 'job', phase: 'before' });\n\n/** Fires after a job execution finishes (regardless of status). */\nexport const AfterJob = (): MethodDecorator => listenerDecorator({ kind: 'job', phase: 'after' });\n\n// ---------------------------------------------------------------------------\n// Step-level listeners (2)\n// ---------------------------------------------------------------------------\n\n/** Fires before a step execution starts. */\nexport const BeforeStep = (): MethodDecorator =>\n listenerDecorator({ kind: 'step', phase: 'before' });\n\n/** Fires after a step execution finishes (regardless of status). */\nexport const AfterStep = (): MethodDecorator => listenerDecorator({ kind: 'step', phase: 'after' });\n\n// ---------------------------------------------------------------------------\n// Chunk-level listeners (3)\n// ---------------------------------------------------------------------------\n\n/** Fires before each chunk (read-process-write cycle) starts. */\nexport const BeforeChunk = (): MethodDecorator =>\n listenerDecorator({ kind: 'chunk', phase: 'before' });\n\n/** Fires after each chunk finishes successfully. */\nexport const AfterChunk = (): MethodDecorator =>\n listenerDecorator({ kind: 'chunk', phase: 'after' });\n\n/** Fires when a chunk throws (allows rollback hooks / telemetry). */\nexport const OnChunkError = (): MethodDecorator =>\n listenerDecorator({ kind: 'chunk', phase: 'on-error' });\n\n// ---------------------------------------------------------------------------\n// ItemRead listeners (3)\n// ---------------------------------------------------------------------------\n\n/** Fires before each item is read. */\nexport const BeforeRead = (): MethodDecorator =>\n listenerDecorator({ kind: 'item-read', phase: 'before' });\n\n/** Fires after each item is successfully read. */\nexport const AfterRead = (): MethodDecorator =>\n listenerDecorator({ kind: 'item-read', phase: 'after' });\n\n/** Fires when the reader throws. */\nexport const OnReadError = (): MethodDecorator =>\n listenerDecorator({ kind: 'item-read', phase: 'on-error' });\n\n// ---------------------------------------------------------------------------\n// ItemProcess listeners (3)\n// ---------------------------------------------------------------------------\n\n/** Fires before each item is processed. */\nexport const BeforeProcess = (): MethodDecorator =>\n listenerDecorator({ kind: 'item-process', phase: 'before' });\n\n/** Fires after each item is successfully processed. */\nexport const AfterProcess = (): MethodDecorator =>\n listenerDecorator({ kind: 'item-process', phase: 'after' });\n\n/** Fires when the processor throws. */\nexport const OnProcessError = (): MethodDecorator =>\n listenerDecorator({ kind: 'item-process', phase: 'on-error' });\n\n// ---------------------------------------------------------------------------\n// ItemWrite listeners (3)\n// ---------------------------------------------------------------------------\n\n/** Fires before the writer receives a chunk. */\nexport const BeforeWrite = (): MethodDecorator =>\n listenerDecorator({ kind: 'item-write', phase: 'before' });\n\n/** Fires after the writer successfully writes a chunk. */\nexport const AfterWrite = (): MethodDecorator =>\n listenerDecorator({ kind: 'item-write', phase: 'after' });\n\n/** Fires when the writer throws. */\nexport const OnWriteError = (): MethodDecorator =>\n listenerDecorator({ kind: 'item-write', phase: 'on-error' });\n\n// ---------------------------------------------------------------------------\n// Skip listeners (3)\n//\n// Skip listeners are not phase-based — each kind handles a distinct skip\n// event emitted by the corresponding read/process/write. We store them\n// under `kind: 'skip'` with `phase: 'after'` as a placeholder so the\n// metadata shape is uniform, plus `skipKind` for runtime dispatch.\n// ---------------------------------------------------------------------------\n\n/** Fires when a read is skipped (after the skip policy decides to skip). */\nexport const OnSkipRead = (): MethodDecorator =>\n listenerDecorator({ kind: 'skip', phase: 'after', skipKind: 'read' });\n\n/** Fires when a processed item is skipped. */\nexport const OnSkipProcess = (): MethodDecorator =>\n listenerDecorator({ kind: 'skip', phase: 'after', skipKind: 'process' });\n\n/** Fires when a write is skipped. */\nexport const OnSkipWrite = (): MethodDecorator =>\n listenerDecorator({ kind: 'skip', phase: 'after', skipKind: 'write' });\n"],"names":["AfterChunk","AfterJob","AfterProcess","AfterRead","AfterStep","AfterWrite","BeforeChunk","BeforeJob","BeforeProcess","BeforeRead","BeforeStep","BeforeWrite","OnChunkError","OnProcessError","OnReadError","OnSkipProcess","OnSkipRead","OnSkipWrite","OnWriteError","defineListener","target","propertyKey","options","Reflect","defineMetadata","BATCH_LISTENER_METADATA","listenerDecorator","_descriptor","kind","phase","skipKind"],"mappings":";;;;;;;;;;;QAiEaA;eAAAA;;QAtBAC;eAAAA;;QAsDAC;eAAAA;;QAhBAC;eAAAA;;QA3BAC;eAAAA;;QA2DAC;eAAAA;;QApDAC;eAAAA;;QArBAC;eAAAA;;QAqDAC;eAAAA;;QAhBAC;eAAAA;;QA3BAC;eAAAA;;QA2DAC;eAAAA;;QAxCAC;eAAAA;;QAgCAC;eAAAA;;QAhBAC;eAAAA;;QAiDAC;eAAAA;;QAJAC;eAAAA;;QAQAC;eAAAA;;QArBAC;eAAAA;;;QArHN;2BACiC;AAoBxC,SAASC,eACPC,MAAc,EACdC,WAA4B,EAC5BC,OAAwB;IAExBC,QAAQC,cAAc,CAACC,kCAAuB,EAAEH,SAASF,QAAQC;AACnE;AAEA,SAASK,kBAAkBJ,OAAwB;IACjD,OAAO,CAACF,QAAgBC,aAA8BM;QACpDR,eAAeC,QAAQC,aAAaC;IACtC;AACF;AAOO,MAAMf,YAAY,IAAuBmB,kBAAkB;QAAEE,MAAM;QAAOC,OAAO;IAAS;AAG1F,MAAM5B,WAAW,IAAuByB,kBAAkB;QAAEE,MAAM;QAAOC,OAAO;IAAQ;AAOxF,MAAMnB,aAAa,IACxBgB,kBAAkB;QAAEE,MAAM;QAAQC,OAAO;IAAS;AAG7C,MAAMzB,YAAY,IAAuBsB,kBAAkB;QAAEE,MAAM;QAAQC,OAAO;IAAQ;AAO1F,MAAMvB,cAAc,IACzBoB,kBAAkB;QAAEE,MAAM;QAASC,OAAO;IAAS;AAG9C,MAAM7B,aAAa,IACxB0B,kBAAkB;QAAEE,MAAM;QAASC,OAAO;IAAQ;AAG7C,MAAMjB,eAAe,IAC1Bc,kBAAkB;QAAEE,MAAM;QAASC,OAAO;IAAW;AAOhD,MAAMpB,aAAa,IACxBiB,kBAAkB;QAAEE,MAAM;QAAaC,OAAO;IAAS;AAGlD,MAAM1B,YAAY,IACvBuB,kBAAkB;QAAEE,MAAM;QAAaC,OAAO;IAAQ;AAGjD,MAAMf,cAAc,IACzBY,kBAAkB;QAAEE,MAAM;QAAaC,OAAO;IAAW;AAOpD,MAAMrB,gBAAgB,IAC3BkB,kBAAkB;QAAEE,MAAM;QAAgBC,OAAO;IAAS;AAGrD,MAAM3B,eAAe,IAC1BwB,kBAAkB;QAAEE,MAAM;QAAgBC,OAAO;IAAQ;AAGpD,MAAMhB,iBAAiB,IAC5Ba,kBAAkB;QAAEE,MAAM;QAAgBC,OAAO;IAAW;AAOvD,MAAMlB,cAAc,IACzBe,kBAAkB;QAAEE,MAAM;QAAcC,OAAO;IAAS;AAGnD,MAAMxB,aAAa,IACxBqB,kBAAkB;QAAEE,MAAM;QAAcC,OAAO;IAAQ;AAGlD,MAAMX,eAAe,IAC1BQ,kBAAkB;QAAEE,MAAM;QAAcC,OAAO;IAAW;AAYrD,MAAMb,aAAa,IACxBU,kBAAkB;QAAEE,MAAM;QAAQC,OAAO;QAASC,UAAU;IAAO;AAG9D,MAAMf,gBAAgB,IAC3BW,kBAAkB;QAAEE,MAAM;QAAQC,OAAO;QAASC,UAAU;IAAU;AAGjE,MAAMb,cAAc,IACzBS,kBAAkB;QAAEE,MAAM;QAAQC,OAAO;QAASC,UAAU;IAAQ"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ChunkStepDefinition } from '../core/ir';
|
|
2
|
-
import type { JobRepository } from '../core/repository';
|
|
2
|
+
import type { JobParameters, JobRepository } from '../core/repository';
|
|
3
3
|
import type { TransactionManager } from '../core/transaction';
|
|
4
4
|
import { StepStatus } from '../core/status';
|
|
5
5
|
import type { ResolverMap } from './listener-invoker';
|
|
@@ -10,9 +10,13 @@ export interface ChunkExecutionContext {
|
|
|
10
10
|
/** Step execution id, used to scope the chunk-progress checkpoint in the
|
|
11
11
|
* step's ExecutionContext (saved as `{ lastChunkIndex }`). */
|
|
12
12
|
stepExecutionId: string;
|
|
13
|
+
stepName?: string;
|
|
14
|
+
jobParameters?: JobParameters;
|
|
13
15
|
jobRepository: JobRepository;
|
|
14
16
|
transactionManager: TransactionManager;
|
|
15
17
|
listenerInvoker: ListenerInvoker;
|
|
18
|
+
/** Full listener resolver map keyed by `${phase}:${kind}:${name}`. */
|
|
19
|
+
listenerResolvers?: ResolverMap;
|
|
16
20
|
/** Map of resolved reader/processor/writer functions by name. */
|
|
17
21
|
resolvers: Map<string, (...args: unknown[]) => unknown | Promise<unknown>>;
|
|
18
22
|
jobExecutionId2: string;
|
|
@@ -63,6 +67,8 @@ export interface ChunkExecutionResult {
|
|
|
63
67
|
}
|
|
64
68
|
export declare class ChunkStepExecutor {
|
|
65
69
|
execute(step: ChunkStepDefinition, context: ChunkExecutionContext): Promise<ChunkExecutionResult>;
|
|
70
|
+
private buildListenerContext;
|
|
71
|
+
private buildItemContext;
|
|
66
72
|
private updateStreams;
|
|
67
73
|
private closeStreams;
|
|
68
74
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk-step-executor.d.ts","sourceRoot":"","sources":["../../../src/execution/chunk-step-executor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAsC,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"chunk-step-executor.d.ts","sourceRoot":"","sources":["../../../src/execution/chunk-step-executor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAsC,MAAM,YAAY,CAAC;AAS1F,OAAO,KAAK,EAAoB,aAAa,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACzF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAO5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAwB,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAE9E,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB;mEAC+D;IAC/D,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,aAAa,EAAE,aAAa,CAAC;IAC7B,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,eAAe,EAAE,eAAe,CAAC;IACjC,sEAAsE;IACtE,iBAAiB,CAAC,EAAE,WAAW,CAAC;IAChC,iEAAiE;IACjE,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IAC3E,eAAe,EAAE,MAAM,CAAC;IACxB,0FAA0F;IAC1F,qBAAqB,CAAC,EAAE,WAAW,CAAC;IACpC;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC;;;;;;;OAOG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AA2DD,qBACa,iBAAiB;IACtB,OAAO,CACX,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,oBAAoB,CAAC;IA+YhC,OAAO,CAAC,oBAAoB;IAoB5B,OAAO,CAAC,gBAAgB;YAsBV,aAAa;YAcb,YAAY;IAM1B;;;;;;;;;;;;OAYG;YACW,QAAQ;IAsFtB,OAAO,CAAC,aAAa;IA2BrB,OAAO,CAAC,gBAAgB;IA2BxB,OAAO,CAAC,aAAa;CA0BtB"}
|