@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
|
@@ -27,10 +27,7 @@ import {
|
|
|
27
27
|
import { FlowEvaluator } from '../flow/flow-evaluator';
|
|
28
28
|
import { BATCH_SCHEDULED_OPTIONS } from '../decorators/constants';
|
|
29
29
|
import type { BatchScheduledMetadata } from '../scheduling/batch-scheduled';
|
|
30
|
-
import {
|
|
31
|
-
BatchScheduleRegistry,
|
|
32
|
-
type BatchScheduleEntry,
|
|
33
|
-
} from './batch-schedule-registry';
|
|
30
|
+
import { BatchScheduleRegistry, type BatchScheduleEntry } from './batch-schedule-registry';
|
|
34
31
|
import {
|
|
35
32
|
BATCH_SCHEDULE_REGISTRY,
|
|
36
33
|
JOB_REPOSITORY_TOKEN,
|
|
@@ -127,9 +124,7 @@ export interface NestBatchModuleOptions {
|
|
|
127
124
|
*/
|
|
128
125
|
export interface NestBatchModuleAsyncOptions {
|
|
129
126
|
imports?: DynamicModule['imports'];
|
|
130
|
-
useFactory: (
|
|
131
|
-
...args: unknown[]
|
|
132
|
-
) => Promise<BatchAdaptersConfig> | BatchAdaptersConfig;
|
|
127
|
+
useFactory: (...args: unknown[]) => Promise<BatchAdaptersConfig> | BatchAdaptersConfig;
|
|
133
128
|
inject?: readonly unknown[];
|
|
134
129
|
}
|
|
135
130
|
|
|
@@ -167,9 +162,10 @@ const OPTIONS_FACTORY: symbol = Symbol.for('@nest-batch/core/OPTIONS_FACTORY');
|
|
|
167
162
|
*
|
|
168
163
|
* The bootstrapper also walks every discovered job for
|
|
169
164
|
* `@BatchScheduled` metadata and registers the corresponding entries
|
|
170
|
-
* into the `BatchScheduleRegistry` so
|
|
171
|
-
*
|
|
172
|
-
*
|
|
165
|
+
* into the `BatchScheduleRegistry` so scheduler adapters have a single,
|
|
166
|
+
* stable place to read them from. Core itself remains metadata-only —
|
|
167
|
+
* runtime adapters install timers and bridge schedule fires into job
|
|
168
|
+
* launches.
|
|
173
169
|
*/
|
|
174
170
|
@Injectable()
|
|
175
171
|
export class BatchBootstrapper implements OnApplicationBootstrap {
|
|
@@ -191,9 +187,7 @@ export class BatchBootstrapper implements OnApplicationBootstrap {
|
|
|
191
187
|
this.registry.register(def);
|
|
192
188
|
this.logger.log(`Registered job "${jobId}"`);
|
|
193
189
|
} catch (err) {
|
|
194
|
-
this.logger.error(
|
|
195
|
-
`Failed to register job "${jobId}": ${(err as Error).message}`,
|
|
196
|
-
);
|
|
190
|
+
this.logger.error(`Failed to register job "${jobId}": ${(err as Error).message}`);
|
|
197
191
|
throw err;
|
|
198
192
|
}
|
|
199
193
|
}
|
|
@@ -211,13 +205,13 @@ export class BatchBootstrapper implements OnApplicationBootstrap {
|
|
|
211
205
|
for (const name of this.allMethodNames(prototype)) {
|
|
212
206
|
const fn = prototype[name];
|
|
213
207
|
if (typeof fn !== 'function') continue;
|
|
214
|
-
const meta = Reflect.getMetadata(
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
) as BatchScheduledMetadata | undefined;
|
|
208
|
+
const meta = Reflect.getMetadata(BATCH_SCHEDULED_OPTIONS, fn) as
|
|
209
|
+
| BatchScheduledMetadata
|
|
210
|
+
| undefined;
|
|
218
211
|
if (!meta) continue;
|
|
219
212
|
const entry: BatchScheduleEntry = {
|
|
220
213
|
jobId,
|
|
214
|
+
scheduleName: meta.options.name,
|
|
221
215
|
methodName: name,
|
|
222
216
|
cron: meta.cron,
|
|
223
217
|
timezone: meta.options.timezone,
|
|
@@ -229,11 +223,12 @@ export class BatchBootstrapper implements OnApplicationBootstrap {
|
|
|
229
223
|
try {
|
|
230
224
|
this.scheduleRegistry.register(entry);
|
|
231
225
|
this.logger.log(
|
|
232
|
-
`Registered schedule for job "${jobId}"::${
|
|
226
|
+
`Registered schedule for job "${jobId}"::${meta.options.name} ` +
|
|
227
|
+
`(method="${name}", cron="${meta.cron}", tz="${meta.options.timezone}")`,
|
|
233
228
|
);
|
|
234
229
|
} catch (err) {
|
|
235
230
|
this.logger.error(
|
|
236
|
-
`Failed to register schedule for job "${jobId}"::${name}: ${
|
|
231
|
+
`Failed to register schedule for job "${jobId}"::${meta.options.name}: ${
|
|
237
232
|
(err as Error).message
|
|
238
233
|
}`,
|
|
239
234
|
);
|
|
@@ -303,19 +298,13 @@ export class NestBatchModule {
|
|
|
303
298
|
|
|
304
299
|
const hasJobRepositoryToken = providers.some(
|
|
305
300
|
(p) =>
|
|
306
|
-
typeof p === 'object' &&
|
|
307
|
-
p !== null &&
|
|
308
|
-
'provide' in p &&
|
|
309
|
-
p.provide === JOB_REPOSITORY_TOKEN,
|
|
301
|
+
typeof p === 'object' && p !== null && 'provide' in p && p.provide === JOB_REPOSITORY_TOKEN,
|
|
310
302
|
);
|
|
311
303
|
|
|
312
304
|
const hasJobRepositoryClass = providers.some(
|
|
313
305
|
(p) =>
|
|
314
306
|
p === JobRepository ||
|
|
315
|
-
(typeof p === 'object' &&
|
|
316
|
-
p !== null &&
|
|
317
|
-
'provide' in p &&
|
|
318
|
-
p.provide === JobRepository),
|
|
307
|
+
(typeof p === 'object' && p !== null && 'provide' in p && p.provide === JobRepository),
|
|
319
308
|
);
|
|
320
309
|
|
|
321
310
|
if (hasJobRepositoryToken && !hasJobRepositoryClass) {
|
|
@@ -336,10 +325,7 @@ export class NestBatchModule {
|
|
|
336
325
|
const hasTransactionManagerClass = providers.some(
|
|
337
326
|
(p) =>
|
|
338
327
|
p === TransactionManager ||
|
|
339
|
-
(typeof p === 'object' &&
|
|
340
|
-
p !== null &&
|
|
341
|
-
'provide' in p &&
|
|
342
|
-
p.provide === TransactionManager),
|
|
328
|
+
(typeof p === 'object' && p !== null && 'provide' in p && p.provide === TransactionManager),
|
|
343
329
|
);
|
|
344
330
|
|
|
345
331
|
if (hasTransactionManagerToken && !hasTransactionManagerClass) {
|
|
@@ -449,11 +435,7 @@ export class NestBatchModule {
|
|
|
449
435
|
return {
|
|
450
436
|
module: NestBatchModule,
|
|
451
437
|
global: true,
|
|
452
|
-
imports: [
|
|
453
|
-
adapters.persistence.module,
|
|
454
|
-
adapters.transport.module,
|
|
455
|
-
DiscoveryModule,
|
|
456
|
-
],
|
|
438
|
+
imports: [adapters.persistence.module, adapters.transport.module, DiscoveryModule],
|
|
457
439
|
providers: [
|
|
458
440
|
// Core classes (discovery + compile + register).
|
|
459
441
|
JobRegistry,
|
|
@@ -556,9 +538,7 @@ export class NestBatchModule {
|
|
|
556
538
|
const factoryResult = useFactory(...injectValues);
|
|
557
539
|
|
|
558
540
|
if (factoryResult instanceof Promise) {
|
|
559
|
-
return factoryResult.then((adapters) =>
|
|
560
|
-
NestBatchModule.buildAsyncModule(options, adapters),
|
|
561
|
-
);
|
|
541
|
+
return factoryResult.then((adapters) => NestBatchModule.buildAsyncModule(options, adapters));
|
|
562
542
|
}
|
|
563
543
|
|
|
564
544
|
return NestBatchModule.buildAsyncModule(options, factoryResult);
|
|
@@ -599,9 +579,8 @@ export class NestBatchModule {
|
|
|
599
579
|
// read the resolved config by its stable symbol.
|
|
600
580
|
const optionsProvider: Provider = {
|
|
601
581
|
provide: MODULE_OPTIONS_TOKEN,
|
|
602
|
-
useFactory: (
|
|
603
|
-
fromFactory
|
|
604
|
-
): BatchAdaptersConfig | undefined => fromFactory,
|
|
582
|
+
useFactory: (fromFactory: BatchAdaptersConfig | undefined): BatchAdaptersConfig | undefined =>
|
|
583
|
+
fromFactory,
|
|
605
584
|
inject: [OPTIONS_FACTORY],
|
|
606
585
|
};
|
|
607
586
|
|
package/src/module/tokens.ts
CHANGED
|
@@ -34,9 +34,7 @@ import { EXECUTION_STRATEGY } from '../execution/execution-strategy';
|
|
|
34
34
|
* does NOT ship a default binding because the choice of persistence
|
|
35
35
|
* backend is the host's decision.
|
|
36
36
|
*/
|
|
37
|
-
export const JOB_REPOSITORY_TOKEN: symbol = Symbol.for(
|
|
38
|
-
'@nest-batch/core/JOB_REPOSITORY',
|
|
39
|
-
);
|
|
37
|
+
export const JOB_REPOSITORY_TOKEN: symbol = Symbol.for('@nest-batch/core/JOB_REPOSITORY');
|
|
40
38
|
|
|
41
39
|
/**
|
|
42
40
|
* Injection token for the `TransactionManager` implementation.
|
|
@@ -45,20 +43,17 @@ export const JOB_REPOSITORY_TOKEN: symbol = Symbol.for(
|
|
|
45
43
|
* `JobRepository` implementation is expected to participate in the same
|
|
46
44
|
* transaction (e.g. share the same `EntityManager` / `DataSource`).
|
|
47
45
|
*/
|
|
48
|
-
export const TRANSACTION_MANAGER_TOKEN: symbol = Symbol.for(
|
|
49
|
-
'@nest-batch/core/TRANSACTION_MANAGER',
|
|
50
|
-
);
|
|
46
|
+
export const TRANSACTION_MANAGER_TOKEN: symbol = Symbol.for('@nest-batch/core/TRANSACTION_MANAGER');
|
|
51
47
|
|
|
52
48
|
/**
|
|
53
49
|
* Injection token for the `BatchScheduleRegistry` provider.
|
|
54
50
|
*
|
|
55
51
|
* The `BatchExplorer` populates this registry with `@BatchScheduled`
|
|
56
|
-
* metadata it discovers on `@Jobable` classes.
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* the explorer's internal state.
|
|
52
|
+
* metadata it discovers on `@Jobable` classes. Scheduler adapters read
|
|
53
|
+
* from this registry to install the actual timers or external schedules.
|
|
54
|
+
* Keeping the registry as a stable token means adapters can inject it
|
|
55
|
+
* (for introspection / health checks) without depending on the explorer's
|
|
56
|
+
* internal state.
|
|
62
57
|
*/
|
|
63
58
|
export const BATCH_SCHEDULE_REGISTRY: symbol = Symbol.for(
|
|
64
59
|
'@nest-batch/core/BATCH_SCHEDULE_REGISTRY',
|
|
@@ -78,9 +73,7 @@ export const BATCH_SCHEDULE_REGISTRY: symbol = Symbol.for(
|
|
|
78
73
|
* T1 type-contract refactor — hosts that need the options bag should
|
|
79
74
|
* inject `MODULE_OPTIONS_TOKEN` instead.
|
|
80
75
|
*/
|
|
81
|
-
export const MODULE_OPTIONS_TOKEN: symbol = Symbol.for(
|
|
82
|
-
'@nest-batch/core/MODULE_OPTIONS',
|
|
83
|
-
);
|
|
76
|
+
export const MODULE_OPTIONS_TOKEN: symbol = Symbol.for('@nest-batch/core/MODULE_OPTIONS');
|
|
84
77
|
|
|
85
78
|
/**
|
|
86
79
|
* Polymorphic execution strategy token.
|
package/src/partition-helpers.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* This module is the single source of truth for partition validation
|
|
5
5
|
* and the default partition-range shape. It is deliberately
|
|
6
6
|
* dependency-light: no `@nest-batch/bullmq`, no `@nest-batch/kafka`,
|
|
7
|
-
* no ORMs
|
|
7
|
+
* no ORMs — verified by
|
|
8
8
|
* `packages/core/tests/core/boundary/no-forbidden-imports.test.ts`.
|
|
9
9
|
*
|
|
10
10
|
* The helpers are consumed by:
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
* (cross-checks the resolved `JobDefinition`),
|
|
15
15
|
* - `packages/core/src/execution/in-process-execution-strategy.ts`
|
|
16
16
|
* (the in-process adapter's partition guard),
|
|
17
|
-
* - `packages/bullmq/src/bullmq-runtime.
|
|
17
|
+
* - `packages/bullmq/src/bullmq-runtime.ts` (the BullMQ
|
|
18
18
|
* strategy's enqueue fan-out + the worker's `partitionIndex`
|
|
19
19
|
* enforcement),
|
|
20
|
-
* - `packages/kafka/src/kafka-runtime.
|
|
20
|
+
* - `packages/kafka/src/kafka-runtime.ts` (the Kafka
|
|
21
21
|
* mirror — T9).
|
|
22
22
|
*
|
|
23
23
|
* Pinned by:
|
|
@@ -48,7 +48,10 @@ export const INVALID_PARTITION_INDEX = 'INVALID_PARTITION_INDEX';
|
|
|
48
48
|
*/
|
|
49
49
|
export class InvalidPartitionsError extends Error {
|
|
50
50
|
readonly code: string;
|
|
51
|
-
constructor(
|
|
51
|
+
constructor(
|
|
52
|
+
message: string,
|
|
53
|
+
public readonly details?: unknown,
|
|
54
|
+
) {
|
|
52
55
|
super(message);
|
|
53
56
|
this.name = 'InvalidPartitionsError';
|
|
54
57
|
this.code = 'INVALID_PARTITIONS';
|
|
@@ -109,10 +112,10 @@ export function defaultRange(
|
|
|
109
112
|
total: number,
|
|
110
113
|
): readonly [from: number, to: number] {
|
|
111
114
|
if (!Number.isInteger(i) || i < 0 || i >= n) {
|
|
112
|
-
throw new InvalidPartitionsError(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
);
|
|
115
|
+
throw new InvalidPartitionsError(`defaultRange: partition index ${i} out of range [0, ${n})`, {
|
|
116
|
+
i,
|
|
117
|
+
n,
|
|
118
|
+
});
|
|
116
119
|
}
|
|
117
120
|
if (!Number.isInteger(n) || n <= 0) {
|
|
118
121
|
throw new InvalidPartitionsError(`defaultRange: count ${n} must be a positive integer`, { n });
|
|
@@ -140,21 +143,14 @@ export function defaultRange(
|
|
|
140
143
|
* surfaces it as `FAILED` with the invariant violation in the
|
|
141
144
|
* `exitMessage`).
|
|
142
145
|
*/
|
|
143
|
-
export function enforcePartitionIndex(
|
|
144
|
-
partitionIndex: number | undefined,
|
|
145
|
-
count: number,
|
|
146
|
-
): void {
|
|
146
|
+
export function enforcePartitionIndex(partitionIndex: number | undefined, count: number): void {
|
|
147
147
|
if (partitionIndex === undefined) {
|
|
148
148
|
throw new InvalidPartitionsError(
|
|
149
149
|
`partitionIndex is required for a partitioned step (count=${count})`,
|
|
150
150
|
{ count },
|
|
151
151
|
);
|
|
152
152
|
}
|
|
153
|
-
if (
|
|
154
|
-
!Number.isInteger(partitionIndex) ||
|
|
155
|
-
partitionIndex < 0 ||
|
|
156
|
-
partitionIndex >= count
|
|
157
|
-
) {
|
|
153
|
+
if (!Number.isInteger(partitionIndex) || partitionIndex < 0 || partitionIndex >= count) {
|
|
158
154
|
throw new InvalidPartitionsError(
|
|
159
155
|
`partitionIndex ${partitionIndex} is out of range [0, ${count})`,
|
|
160
156
|
{ partitionIndex, count },
|
|
@@ -12,11 +12,10 @@ export const BATCH_SCHEDULED_OPTIONS = SCHEDULED_KEY;
|
|
|
12
12
|
* - `'queue'` — buffer the new tick and start it after the current one ends.
|
|
13
13
|
* - `'parallel'` — start the new tick alongside the current one.
|
|
14
14
|
*
|
|
15
|
-
* The
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* is the contract: the runtime applies the default.
|
|
15
|
+
* The active scheduler adapter reads this value off the stored metadata
|
|
16
|
+
* and applies the policy at dispatch time. The decorator itself MUST NOT
|
|
17
|
+
* silently default the policy to `'skip'` on the user's behalf — leaving
|
|
18
|
+
* it `undefined` here is the contract: the runtime applies the default.
|
|
20
19
|
*/
|
|
21
20
|
export type BatchOverlapPolicy = 'skip' | 'queue' | 'parallel';
|
|
22
21
|
|
|
@@ -45,9 +44,8 @@ export interface BatchScheduledOptions {
|
|
|
45
44
|
|
|
46
45
|
/**
|
|
47
46
|
* The shape stored under the `BATCH_SCHEDULED_OPTIONS` metadata key on
|
|
48
|
-
* the decorated method function.
|
|
49
|
-
*
|
|
50
|
-
* the job with the underlying scheduler.
|
|
47
|
+
* the decorated method function. Scheduler adapters read this verbatim
|
|
48
|
+
* to register the job with the underlying scheduler.
|
|
51
49
|
*
|
|
52
50
|
* Note: `inert` lives at the top level (not inside `options`) on
|
|
53
51
|
* purpose. It is a *runtime* flag captured at decoration time from
|
|
@@ -70,25 +68,17 @@ export interface BatchScheduledMetadata {
|
|
|
70
68
|
* to `descriptor.value`), so `Reflect.getMetadata(KEY, Job.prototype.run)`
|
|
71
69
|
* returns the stored shape.
|
|
72
70
|
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* read at decoration time and stamped onto the stored shape. The
|
|
85
|
-
* decorator does NOT install any timer, interval, or scheduler
|
|
86
|
-
* registration at decoration time; `inert` is a hint the future
|
|
87
|
-
* runtime scheduler honors when it later walks the class.
|
|
88
|
-
*
|
|
89
|
-
* The decorator is metadata-only by design — it does NOT depend on
|
|
90
|
-
* `cron` (the boundary test from Task 2 still passes — no `cron`
|
|
91
|
-
* import appears in core).
|
|
71
|
+
* The decorator validates cron/timezone inputs, stores metadata, and
|
|
72
|
+
* captures inert mode. `process.env.BATCH_SCHEDULED_DISABLE` is read at
|
|
73
|
+
* decoration time and stamped onto the stored shape. The decorator does
|
|
74
|
+
* NOT install any timer, interval, or scheduler registration at decoration
|
|
75
|
+
* time; runtime scheduler adapters honor the metadata when they later
|
|
76
|
+
* walk the `BatchScheduleRegistry`.
|
|
77
|
+
*
|
|
78
|
+
* The decorator is metadata-only by design — it does NOT install
|
|
79
|
+
* timers or import the runtime cron engine. The in-process transport
|
|
80
|
+
* owns that dependency because it is the layer that turns metadata
|
|
81
|
+
* into actual launches.
|
|
92
82
|
*/
|
|
93
83
|
// ---------------------------------------------------------------------------
|
|
94
84
|
// Validation helpers
|
|
@@ -117,8 +107,8 @@ const CRON_MAX_FIELDS = 6;
|
|
|
117
107
|
* a single whitespace run.
|
|
118
108
|
*
|
|
119
109
|
* This is a shape check, not a semantic one — `99 99 99 99 99` still
|
|
120
|
-
* passes the regex. The
|
|
121
|
-
*
|
|
110
|
+
* passes the regex. The active scheduler adapter is the layer that
|
|
111
|
+
* handles semantic validation with its scheduler backend.
|
|
122
112
|
* The shape check exists so the decorator fails fast on
|
|
123
113
|
* unambiguously-wrong input (empty string, too few / too many
|
|
124
114
|
* fields, literal English words).
|
|
@@ -189,9 +179,9 @@ function assertValidTimezone(tz: string): void {
|
|
|
189
179
|
* includes the invalid value and the failure reason so the error
|
|
190
180
|
* is greppable in logs and pinned in test assertions.
|
|
191
181
|
*
|
|
192
|
-
* Exported from the module barrel so adapter packages
|
|
193
|
-
*
|
|
194
|
-
*
|
|
182
|
+
* Exported from the module barrel so adapter packages can
|
|
183
|
+
* `instanceof`-check it without reaching into the decorator's internal
|
|
184
|
+
* helper.
|
|
195
185
|
*/
|
|
196
186
|
export class InvalidBatchScheduledCronError extends Error {
|
|
197
187
|
readonly cron: string;
|