@nest-batch/core 0.2.0 → 0.2.1
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 +5 -3
- 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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/module/nest-batch.module.ts"],"sourcesContent":["import {\n DynamicModule,\n Injectable,\n Logger,\n Module,\n OnApplicationBootstrap,\n Provider,\n} from '@nestjs/common';\nimport { DiscoveryModule } from '@nestjs/core';\n\nimport type { BatchAdaptersConfig } from './adapter';\nimport { DefinitionCompiler } from '../compiler/definition-compiler';\nimport { BatchExplorer } from '../explorer/batch-explorer';\nimport { JobRegistry } from '../registry/job-registry';\nimport { JobExecutor } from '../execution/job-executor';\nimport { ChunkStepExecutor } from '../execution/chunk-step-executor';\nimport { TaskletStepExecutor } from '../execution/tasklet-step-executor';\nimport { ListenerInvoker } from '../execution/listener-invoker';\nimport { JobLauncher } from '../execution/job-launcher';\nimport { JobExplorer } from '../execution/job-explorer';\nimport { JobOperator } from '../execution/job-operator';\nimport { BatchWorkerRunner } from '../execution/batch-worker-runner';\nimport {\n InProcessExecutionStrategy,\n IN_PROCESS_EXECUTION_STRATEGY_PROVIDER,\n} from '../execution/in-process-execution-strategy';\nimport { FlowEvaluator } from '../flow/flow-evaluator';\nimport { BATCH_SCHEDULED_OPTIONS } from '../decorators/constants';\nimport type { BatchScheduledMetadata } from '../scheduling/batch-scheduled';\nimport {\n BatchScheduleRegistry,\n type BatchScheduleEntry,\n} from './batch-schedule-registry';\nimport {\n BATCH_SCHEDULE_REGISTRY,\n JOB_REPOSITORY_TOKEN,\n MODULE_OPTIONS_TOKEN,\n TRANSACTION_MANAGER_TOKEN,\n} from './tokens';\nimport { JobRepository } from '../core/repository/job-repository';\nimport { TransactionManager } from '../core/transaction/transaction-manager';\n\n/**\n * Re-export the default in-process strategy and its token binding so\n * the host app can wire them up alongside the rest of the batch\n * engine. The strategy is *not* auto-registered by\n * `NestBatchModule.forRoot()` because its constructor requires\n * `JobRepository` and `JobExecutor` — runtime deps the host\n * supplies. The T4 `InProcessAdapter` factory does the wiring through\n * the adapter's own `DynamicModule.exports` so the runtime\n * resolution chain works without the core module having to know which\n * adapter is active.\n *\n * ```ts\n * import { InProcessAdapter, MikroOrmAdapter, NestBatchModule } from '@nest-batch/core';\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 */\nexport { InProcessExecutionStrategy, IN_PROCESS_EXECUTION_STRATEGY_PROVIDER };\n\n/**\n * Options for `NestBatchModule.forRoot()`.\n *\n * The whole configuration is the pair of adapters (persistence +\n * transport) the host picked. Both are required — the compiler\n * will reject an `adapters` bag that is missing one — and each\n * adapter is a self-contained `DynamicModule` core will import.\n * Sibling packages can no longer extend the options shape via\n * interface merging: the old `AdapterOptions` extension point is\n * gone because every adapter now owns its own `DynamicModule`\n * (and therefore its own config). Adapter authors that want\n * type-safe factory arguments should expose them on their own\n * adapter factory (e.g. `MikroOrmAdapter.forRoot({ ... })`).\n *\n * - `adapters.persistence` — the adapter that owns the\n * `JobRepository` + `TransactionManager` bindings (e.g.\n * `MikroOrmAdapter`, `TypeOrmAdapter`).\n * - `adapters.transport` — the adapter that owns the\n * `IExecutionStrategy` binding (e.g. `InProcessAdapter`,\n * `BullmqAdapter`).\n */\nexport interface NestBatchModuleOptions {\n readonly adapters: BatchAdaptersConfig;\n}\n\n/**\n * Options for `NestBatchModule.forRootAsync()`.\n *\n * `imports` + `inject` + `useFactory` mirror the standard\n * `ConfigurableModuleBuilder` shape. The factory is registered as a\n * sentinel provider under `OPTIONS_FACTORY` (a `Symbol.for` token\n * stable across module boundaries), and `MODULE_OPTIONS_TOKEN` is\n * bound to its resolved `BatchAdaptersConfig` via a follow-up\n * `useFactory` provider.\n *\n * **Note on adapter module merging.** NestJS cannot dynamically\n * import a `DynamicModule` at module-build time, so the\n * `forRootAsync` path does NOT auto-merge the adapter modules'\n * `globalProviders` into the core module's `providers` list the way\n * `forRoot` does. Two consequences for the async path:\n *\n * 1. The adapter `DynamicModule`s must be passed in the caller's\n * `imports` array directly (e.g.\n * `imports: [MikroOrmAdapter.module, InProcessAdapter.module]`)\n * so Nest sees them in the module graph.\n * 2. The factory's return value is used only for the\n * `MODULE_OPTIONS_TOKEN` binding (adapters introspection);\n * sibling packages and the host can read the resolved config\n * via `@Inject(MODULE_OPTIONS_TOKEN)`.\n *\n * For the full auto-merge (adapter modules + `globalProviders`\n * registered into core's own DI scope), prefer `forRoot` with a\n * pre-resolved `BatchAdaptersConfig`. The async path is for\n * adapters whose factory needs to consult a config service or\n * another async provider to decide which adapter to plug in.\n */\nexport interface NestBatchModuleAsyncOptions {\n imports?: DynamicModule['imports'];\n useFactory: (\n ...args: unknown[]\n ) => Promise<BatchAdaptersConfig> | BatchAdaptersConfig;\n inject?: readonly unknown[];\n}\n\n/**\n * Sentinel provider token used by `forRootAsync` to plumb the user's\n * `useFactory` through DI. The factory is registered under this\n * token, and `MODULE_OPTIONS_TOKEN` resolves to its output via a\n * follow-up `useFactory` provider.\n *\n * `Symbol.for(...)` makes the token stable across module boundaries:\n * tooling or sibling packages that know the description string can\n * resolve the same symbol without importing this file. Matches the\n * convention used by `BATCH_SCHEDULE_REGISTRY`, `MODULE_OPTIONS_TOKEN`,\n * `JOB_REPOSITORY_TOKEN`, etc. in `./tokens.ts`.\n */\nconst OPTIONS_FACTORY: symbol = Symbol.for('@nest-batch/core/OPTIONS_FACTORY');\n\n/**\n * Hook that runs on `OnApplicationBootstrap` to wire together the\n * discovery → compile → register pipeline.\n *\n * Why a separate provider and not a method on `JobRegistry` or\n * `DefinitionCompiler`?\n * - `BatchExplorer.onModuleInit` populates the discovered\n * list once the DI container is ready. Compilation needs every\n * `@Jobable` provider to be instantiated, so it must run *after*\n * `onModuleInit`.\n * - `OnApplicationBootstrap` is the latest point in Nest's lifecycle\n * before the app actually starts handling requests, so all of:\n * `forRoot` / `forRootAsync` providers, custom `useFactory` results,\n * and user-supplied job classes, are guaranteed to be live.\n * - Keeping the wire-up in a dedicated `BatchBootstrapper` means the\n * explorer/compiler/registry stay pure (no `onApplicationBootstrap`\n * coupling) and are independently testable.\n *\n * The bootstrapper also walks every discovered job for\n * `@BatchScheduled` metadata and registers the corresponding entries\n * into the `BatchScheduleRegistry` so the (future) runtime scheduler\n * has a single, stable place to read them from. Today, the registry is\n * metadata-only — no timers are installed.\n */\n@Injectable()\nexport class BatchBootstrapper implements OnApplicationBootstrap {\n private readonly logger = new Logger(BatchBootstrapper.name);\n\n constructor(\n private readonly explorer: BatchExplorer,\n private readonly compiler: DefinitionCompiler,\n private readonly registry: JobRegistry,\n private readonly scheduleRegistry: BatchScheduleRegistry,\n ) {}\n\n onApplicationBootstrap(): void {\n // 1. Compile + register every discovered job.\n for (const discovered of this.explorer.getDiscovered()) {\n const jobId = discovered.jobOptions.id;\n try {\n const def = this.compiler.compileFromDiscovered(discovered);\n this.registry.register(def);\n this.logger.log(`Registered job \"${jobId}\"`);\n } catch (err) {\n this.logger.error(\n `Failed to register job \"${jobId}\": ${(err as Error).message}`,\n );\n throw err;\n }\n }\n\n // 2. Walk the same discovered set for @BatchScheduled metadata\n // and populate BatchScheduleRegistry. The metadata is stamped\n // by the decorator via `SetMetadata(KEY, value)`, which Nest\n // writes to the *function reference* of the decorated method\n // (not to the prototype+name slot). We therefore read it from\n // `prototype[name]` (the function), not from the (proto, name)\n // tuple.\n for (const discovered of this.explorer.getDiscovered()) {\n const jobId = discovered.jobOptions.id;\n const prototype = discovered.classRef.prototype as Record<string, unknown>;\n for (const name of this.allMethodNames(prototype)) {\n const fn = prototype[name];\n if (typeof fn !== 'function') continue;\n const meta = Reflect.getMetadata(\n BATCH_SCHEDULED_OPTIONS,\n fn,\n ) as BatchScheduledMetadata | undefined;\n if (!meta) continue;\n const entry: BatchScheduleEntry = {\n jobId,\n methodName: name,\n cron: meta.cron,\n timezone: meta.options.timezone,\n overlap: meta.options.overlap,\n startAt: meta.options.startAt,\n endAt: meta.options.endAt,\n inert: meta.inert,\n };\n try {\n this.scheduleRegistry.register(entry);\n this.logger.log(\n `Registered schedule for job \"${jobId}\"::${name} (cron=\"${meta.cron}\", tz=\"${meta.options.timezone}\")`,\n );\n } catch (err) {\n this.logger.error(\n `Failed to register schedule for job \"${jobId}\"::${name}: ${\n (err as Error).message\n }`,\n );\n throw err;\n }\n }\n }\n }\n\n /**\n * Walk the prototype chain and return every own method name\n * (excluding `constructor`) up to (but not including)\n * `Object.prototype`. Same shape as `BatchExplorer.allMethodNames` —\n * we duplicate the walker here so the bootstrapper remains\n * independent of the explorer's internals.\n */\n private allMethodNames(prototype: object): Set<string> {\n const names = new Set<string>();\n let proto: object | null = prototype;\n while (proto && proto !== Object.prototype) {\n for (const name of Object.getOwnPropertyNames(proto)) {\n if (name === 'constructor') continue;\n names.add(name);\n }\n proto = Object.getPrototypeOf(proto);\n }\n return names;\n }\n}\n\n/**\n * Public Nest module that wires up the @nest-batch/core library.\n *\n * The module is a `global: true` `DynamicModule` whose `imports`,\n * `providers`, and `exports` are assembled at the call site by\n * `forRoot` (synchronous) or `forRootAsync` (sentinel-factory\n * pattern). In both paths the core providers and the executor\n * subgraph are auto-registered so the host does not have to wire\n * them by hand; adapter modules are imported as part of the same\n * `DynamicModule` so Nest's discovery phase sees every job class.\n *\n * @see {@link NestBatchModuleOptions} for the synchronous options shape\n * @see {@link NestBatchModuleAsyncOptions} for the async options shape\n * @see {@link BatchAdaptersConfig} for the adapter contract\n */\n@Module({})\nexport class NestBatchModule {\n /**\n * Build a provider list from an adapter's `globalProviders`,\n * automatically aliasing the abstract `JobRepository` class to\n * `JOB_REPOSITORY_TOKEN` when the symbol is present but the class\n * itself is not directly provided.\n *\n * This fixes the NestJS DI resolution gap: `JobExecutor`,\n * `JobLauncher`, and `InProcessExecutionStrategy` all inject the\n * abstract `JobRepository`, but adapters typically bind their\n * concrete implementation to `JOB_REPOSITORY_TOKEN`. NestJS cannot\n * resolve abstract class → concrete subclass automatically, so we\n * inject the alias here.\n *\n * Idempotent: if `JobRepository` is already directly provided (e.g.\n * `{ provide: JobRepository, useClass: MyRepo }`), no extra alias is\n * added.\n */\n private static buildProviders(adapterGlobalProviders: readonly Provider[]): Provider[] {\n const providers = [...adapterGlobalProviders];\n\n const hasJobRepositoryToken = providers.some(\n (p) =>\n typeof p === 'object' &&\n p !== null &&\n 'provide' in p &&\n p.provide === JOB_REPOSITORY_TOKEN,\n );\n\n const hasJobRepositoryClass = providers.some(\n (p) =>\n p === JobRepository ||\n (typeof p === 'object' &&\n p !== null &&\n 'provide' in p &&\n p.provide === JobRepository),\n );\n\n if (hasJobRepositoryToken && !hasJobRepositoryClass) {\n providers.push({\n provide: JobRepository,\n useExisting: JOB_REPOSITORY_TOKEN,\n });\n }\n\n const hasTransactionManagerToken = providers.some(\n (p) =>\n typeof p === 'object' &&\n p !== null &&\n 'provide' in p &&\n p.provide === TRANSACTION_MANAGER_TOKEN,\n );\n\n const hasTransactionManagerClass = providers.some(\n (p) =>\n p === TransactionManager ||\n (typeof p === 'object' &&\n p !== null &&\n 'provide' in p &&\n p.provide === TransactionManager),\n );\n\n if (hasTransactionManagerToken && !hasTransactionManagerClass) {\n providers.push({\n provide: TransactionManager,\n useExisting: TRANSACTION_MANAGER_TOKEN,\n });\n }\n\n return providers;\n }\n\n /**\n * Best-effort resolution of `inject` tokens by reading the\n * `providers` metadata of modules listed in `imports`. This lets\n * `forRootAsync` evaluate a synchronous `useFactory` even when it\n * declares `inject` deps, provided those deps are `useValue`\n * providers exported by an imported module.\n *\n * Only `useValue` providers can be resolved this way — `useClass`\n * and `useFactory` providers need Nest's DI container and are\n * skipped.\n */\n private static resolveInjectValues(\n imports: NestBatchModuleAsyncOptions['imports'],\n inject: readonly unknown[],\n ): unknown[] {\n if (!inject || inject.length === 0) return [];\n\n const values: unknown[] = [];\n for (const token of inject) {\n let resolved: unknown = undefined;\n for (const mod of imports ?? []) {\n if (!mod) continue;\n\n let providers: Provider[] | undefined;\n\n if (typeof mod === 'object' && 'module' in mod) {\n const dynamicMod = mod as DynamicModule;\n providers = dynamicMod.providers;\n if (!providers && dynamicMod.module) {\n try {\n providers = Reflect.getMetadata('providers', dynamicMod.module);\n } catch {\n /* ignore metadata read failures */\n }\n }\n } else if (typeof mod === 'function') {\n try {\n providers = Reflect.getMetadata('providers', mod);\n } catch {\n /* ignore metadata read failures */\n }\n }\n\n for (const p of providers ?? []) {\n if (\n typeof p === 'object' &&\n p !== null &&\n 'provide' in p &&\n p.provide === token &&\n 'useValue' in p\n ) {\n resolved = p.useValue;\n break;\n }\n }\n if (resolved !== undefined) break;\n }\n values.push(resolved);\n }\n return values;\n }\n\n /**\n * Static (synchronous) configuration.\n *\n * Takes the resolved `BatchAdaptersConfig` and builds a\n * `global: true` `DynamicModule` that:\n *\n * 1. imports each adapter's `DynamicModule`\n * (`adapters.persistence.module` and `adapters.transport.module`);\n * 2. imports `DiscoveryModule` from `@nestjs/core` so the explorer\n * can use Nest's `DiscoveryService`;\n * 3. registers core's own providers — `JobRegistry`,\n * `DefinitionCompiler`, `BatchExplorer`, `FlowEvaluator`,\n * `BatchScheduleRegistry`, `BatchBootstrapper` — and the\n * executor subgraph (`JobExecutor`, `ChunkStepExecutor`,\n * `TaskletStepExecutor`, `ListenerInvoker`) so the host\n * does not have to wire them by hand;\n * 4. registers each adapter's `globalProviders` (e.g. the\n * `JobRepository` / `TransactionManager` implementations\n * from a persistence adapter) so the host can inject them\n * too;\n * 5. binds the `BatchAdaptersConfig` to `MODULE_OPTIONS_TOKEN`\n * via a value provider for adapter introspection.\n */\n static forRoot(options: NestBatchModuleOptions): DynamicModule {\n const { adapters } = options;\n const persistenceProviders = NestBatchModule.buildProviders(\n adapters.persistence.globalProviders ?? [],\n );\n const transportProviders = NestBatchModule.buildProviders(\n adapters.transport.globalProviders ?? [],\n );\n\n return {\n module: NestBatchModule,\n global: true,\n imports: [\n adapters.persistence.module,\n adapters.transport.module,\n DiscoveryModule,\n ],\n providers: [\n // Core classes (discovery + compile + register).\n JobRegistry,\n DefinitionCompiler,\n BatchExplorer,\n FlowEvaluator,\n BatchScheduleRegistry,\n BatchBootstrapper,\n // Executor subgraph (JobExecutor → Chunk/Tasklet/Listener).\n JobExecutor,\n ChunkStepExecutor,\n TaskletStepExecutor,\n ListenerInvoker,\n JobLauncher,\n JobExplorer,\n JobOperator,\n BatchWorkerRunner,\n // Resolved options bag for adapter introspection.\n {\n provide: MODULE_OPTIONS_TOKEN,\n useValue: adapters,\n },\n // Schedule registry symbol alias — the symbol itself\n // must be a provider (not just a class export) so the\n // `exports` entry below resolves through Nest's DI\n // validation.\n {\n provide: BATCH_SCHEDULE_REGISTRY,\n useExisting: BatchScheduleRegistry,\n },\n // Adapter-supplied global providers (e.g. JobRepository\n // / TransactionManager implementations).\n ...persistenceProviders,\n ...transportProviders,\n ],\n exports: [\n // Core classes — exported so sibling packages and the\n // host app can resolve them from the global module chain.\n JobRegistry,\n DefinitionCompiler,\n BatchExplorer,\n FlowEvaluator,\n BatchScheduleRegistry,\n BatchBootstrapper,\n // Tokens — exported so adapters can bind to them via\n // `@Inject(MODULE_OPTIONS_TOKEN)` and host code can read\n // the schedule registry by its stable symbol.\n BATCH_SCHEDULE_REGISTRY,\n MODULE_OPTIONS_TOKEN,\n // Executor subgraph — exported so adapters (e.g. the\n // `InProcessExecutionStrategy`) and host code can inject\n // them.\n JobExecutor,\n ChunkStepExecutor,\n TaskletStepExecutor,\n ListenerInvoker,\n JobLauncher,\n JobExplorer,\n JobOperator,\n BatchWorkerRunner,\n // Adapter-supplied global providers — re-exported so the\n // host can resolve the persistence + transport bindings\n // from the global module chain.\n ...persistenceProviders,\n ...transportProviders,\n ],\n };\n }\n\n /**\n * Async configuration — useful when the adapter set comes from a\n * config service or another async source.\n *\n * Mirrors the `BullmqBatchModule.forRootAsync` pattern: the user's\n * `useFactory` is registered as a sentinel provider under\n * `OPTIONS_FACTORY`, and `MODULE_OPTIONS_TOKEN` is bound to its\n * resolved `BatchAdaptersConfig` via a follow-up `useFactory`\n * provider. The user's `imports` + `inject` are forwarded as-is\n * so the factory can pull from `ConfigService` or any other\n * DI-bound dependency.\n *\n * See the `NestBatchModuleAsyncOptions` JSDoc for the adapter\n * module merging caveat — the async path does not auto-merge\n * the adapter modules' `globalProviders`. The adapter\n * `DynamicModule`s must be passed in the caller's `imports`\n * array so Nest sees them in the module graph; the factory's\n * return value is used only for `MODULE_OPTIONS_TOKEN`.\n */\n static forRootAsync(\n options: NestBatchModuleAsyncOptions,\n ): DynamicModule | Promise<DynamicModule> {\n const { imports = [], inject = [], useFactory } = options;\n\n // Try to evaluate the factory synchronously so we can merge\n // adapter `globalProviders` at module-build time (mirroring\n // what `forRoot` does). If the factory is async or needs\n // injected deps we can't resolve, fall back to the pure\n // sentinel-factory path.\n const injectValues = NestBatchModule.resolveInjectValues(imports, inject);\n const factoryResult = useFactory(...injectValues);\n\n if (factoryResult instanceof Promise) {\n return factoryResult.then((adapters) =>\n NestBatchModule.buildAsyncModule(options, adapters),\n );\n }\n\n return NestBatchModule.buildAsyncModule(options, factoryResult);\n }\n\n /**\n * Shared module builder used by both the sync and async branches\n * of `forRootAsync`. When the factory result is available\n * synchronously we merge adapter `globalProviders` (plus the\n * `JobRepository` alias) into core's own provider list, exactly\n * like `forRoot` does.\n */\n private static buildAsyncModule(\n options: NestBatchModuleAsyncOptions,\n adapters: BatchAdaptersConfig,\n ): DynamicModule {\n const { imports = [], inject = [], useFactory } = options;\n\n const persistenceProviders = NestBatchModule.buildProviders(\n adapters.persistence.globalProviders ?? [],\n );\n const transportProviders = NestBatchModule.buildProviders(\n adapters.transport.globalProviders ?? [],\n );\n\n // Sentinel factory provider: holds the user's `useFactory` and\n // any `inject` deps. Other providers can pull the resolved\n // `BatchAdaptersConfig` via `@Inject(OPTIONS_FACTORY)` if they\n // need to.\n const factoryProvider: Provider = {\n provide: OPTIONS_FACTORY,\n useFactory: useFactory as (...args: unknown[]) => unknown,\n inject: [...inject] as Array<string | symbol | Function>,\n };\n\n // Options provider: bridges the sentinel factory to the\n // canonical `MODULE_OPTIONS_TOKEN` so adapters + host code can\n // read the resolved config by its stable symbol.\n const optionsProvider: Provider = {\n provide: MODULE_OPTIONS_TOKEN,\n useFactory: (\n fromFactory: BatchAdaptersConfig | undefined,\n ): BatchAdaptersConfig | undefined => fromFactory,\n inject: [OPTIONS_FACTORY],\n };\n\n return {\n module: NestBatchModule,\n global: true,\n imports: [\n ...imports,\n adapters.persistence.module,\n adapters.transport.module,\n DiscoveryModule,\n ],\n providers: [\n // Core classes (discovery + compile + register).\n JobRegistry,\n DefinitionCompiler,\n BatchExplorer,\n FlowEvaluator,\n BatchScheduleRegistry,\n BatchBootstrapper,\n // Executor subgraph (JobExecutor → Chunk/Tasklet/Listener).\n JobExecutor,\n ChunkStepExecutor,\n TaskletStepExecutor,\n ListenerInvoker,\n JobLauncher,\n JobExplorer,\n JobOperator,\n BatchWorkerRunner,\n {\n provide: BATCH_SCHEDULE_REGISTRY,\n useExisting: BatchScheduleRegistry,\n },\n // Adapter-supplied global providers (e.g. JobRepository\n // / TransactionManager implementations).\n ...persistenceProviders,\n ...transportProviders,\n // Sentinel factory + options provider (the async path).\n factoryProvider,\n optionsProvider,\n ],\n exports: [\n // Core classes.\n JobRegistry,\n DefinitionCompiler,\n BatchExplorer,\n FlowEvaluator,\n BatchScheduleRegistry,\n BatchBootstrapper,\n // Tokens.\n BATCH_SCHEDULE_REGISTRY,\n MODULE_OPTIONS_TOKEN,\n // Executor subgraph.\n JobExecutor,\n ChunkStepExecutor,\n TaskletStepExecutor,\n ListenerInvoker,\n JobLauncher,\n JobExplorer,\n JobOperator,\n BatchWorkerRunner,\n // Adapter-supplied global providers — re-exported so the\n // host can resolve the persistence + transport bindings\n // from the global module chain.\n ...persistenceProviders,\n ...transportProviders,\n ],\n };\n }\n}\n"],"names":["BatchBootstrapper","IN_PROCESS_EXECUTION_STRATEGY_PROVIDER","InProcessExecutionStrategy","NestBatchModule","OPTIONS_FACTORY","Symbol","for","logger","Logger","name","explorer","compiler","registry","scheduleRegistry","onApplicationBootstrap","discovered","getDiscovered","jobId","jobOptions","id","def","compileFromDiscovered","register","log","err","error","message","prototype","classRef","allMethodNames","fn","meta","Reflect","getMetadata","BATCH_SCHEDULED_OPTIONS","entry","methodName","cron","timezone","options","overlap","startAt","endAt","inert","names","Set","proto","Object","getOwnPropertyNames","add","getPrototypeOf","buildProviders","adapterGlobalProviders","providers","hasJobRepositoryToken","some","p","provide","JOB_REPOSITORY_TOKEN","hasJobRepositoryClass","JobRepository","push","useExisting","hasTransactionManagerToken","TRANSACTION_MANAGER_TOKEN","hasTransactionManagerClass","TransactionManager","resolveInjectValues","imports","inject","length","values","token","resolved","undefined","mod","dynamicMod","module","useValue","forRoot","adapters","persistenceProviders","persistence","globalProviders","transportProviders","transport","global","DiscoveryModule","JobRegistry","DefinitionCompiler","BatchExplorer","FlowEvaluator","BatchScheduleRegistry","JobExecutor","ChunkStepExecutor","TaskletStepExecutor","ListenerInvoker","JobLauncher","JobExplorer","JobOperator","BatchWorkerRunner","MODULE_OPTIONS_TOKEN","BATCH_SCHEDULE_REGISTRY","exports","forRootAsync","useFactory","injectValues","factoryResult","Promise","then","buildAsyncModule","factoryProvider","optionsProvider","fromFactory"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/module/nest-batch.module.ts"],"sourcesContent":["import {\n DynamicModule,\n Injectable,\n Logger,\n Module,\n OnApplicationBootstrap,\n Provider,\n} from '@nestjs/common';\nimport { DiscoveryModule } from '@nestjs/core';\n\nimport type { BatchAdaptersConfig } from './adapter';\nimport { DefinitionCompiler } from '../compiler/definition-compiler';\nimport { BatchExplorer } from '../explorer/batch-explorer';\nimport { JobRegistry } from '../registry/job-registry';\nimport { JobExecutor } from '../execution/job-executor';\nimport { ChunkStepExecutor } from '../execution/chunk-step-executor';\nimport { TaskletStepExecutor } from '../execution/tasklet-step-executor';\nimport { ListenerInvoker } from '../execution/listener-invoker';\nimport { JobLauncher } from '../execution/job-launcher';\nimport { JobExplorer } from '../execution/job-explorer';\nimport { JobOperator } from '../execution/job-operator';\nimport { BatchWorkerRunner } from '../execution/batch-worker-runner';\nimport {\n InProcessExecutionStrategy,\n IN_PROCESS_EXECUTION_STRATEGY_PROVIDER,\n} from '../execution/in-process-execution-strategy';\nimport { FlowEvaluator } from '../flow/flow-evaluator';\nimport { BATCH_SCHEDULED_OPTIONS } from '../decorators/constants';\nimport type { BatchScheduledMetadata } from '../scheduling/batch-scheduled';\nimport { BatchScheduleRegistry, type BatchScheduleEntry } from './batch-schedule-registry';\nimport {\n BATCH_SCHEDULE_REGISTRY,\n JOB_REPOSITORY_TOKEN,\n MODULE_OPTIONS_TOKEN,\n TRANSACTION_MANAGER_TOKEN,\n} from './tokens';\nimport { JobRepository } from '../core/repository/job-repository';\nimport { TransactionManager } from '../core/transaction/transaction-manager';\n\n/**\n * Re-export the default in-process strategy and its token binding so\n * the host app can wire them up alongside the rest of the batch\n * engine. The strategy is *not* auto-registered by\n * `NestBatchModule.forRoot()` because its constructor requires\n * `JobRepository` and `JobExecutor` — runtime deps the host\n * supplies. The T4 `InProcessAdapter` factory does the wiring through\n * the adapter's own `DynamicModule.exports` so the runtime\n * resolution chain works without the core module having to know which\n * adapter is active.\n *\n * ```ts\n * import { InProcessAdapter, MikroOrmAdapter, NestBatchModule } from '@nest-batch/core';\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 */\nexport { InProcessExecutionStrategy, IN_PROCESS_EXECUTION_STRATEGY_PROVIDER };\n\n/**\n * Options for `NestBatchModule.forRoot()`.\n *\n * The whole configuration is the pair of adapters (persistence +\n * transport) the host picked. Both are required — the compiler\n * will reject an `adapters` bag that is missing one — and each\n * adapter is a self-contained `DynamicModule` core will import.\n * Sibling packages can no longer extend the options shape via\n * interface merging: the old `AdapterOptions` extension point is\n * gone because every adapter now owns its own `DynamicModule`\n * (and therefore its own config). Adapter authors that want\n * type-safe factory arguments should expose them on their own\n * adapter factory (e.g. `MikroOrmAdapter.forRoot({ ... })`).\n *\n * - `adapters.persistence` — the adapter that owns the\n * `JobRepository` + `TransactionManager` bindings (e.g.\n * `MikroOrmAdapter`, `TypeOrmAdapter`).\n * - `adapters.transport` — the adapter that owns the\n * `IExecutionStrategy` binding (e.g. `InProcessAdapter`,\n * `BullmqAdapter`).\n */\nexport interface NestBatchModuleOptions {\n readonly adapters: BatchAdaptersConfig;\n}\n\n/**\n * Options for `NestBatchModule.forRootAsync()`.\n *\n * `imports` + `inject` + `useFactory` mirror the standard\n * `ConfigurableModuleBuilder` shape. The factory is registered as a\n * sentinel provider under `OPTIONS_FACTORY` (a `Symbol.for` token\n * stable across module boundaries), and `MODULE_OPTIONS_TOKEN` is\n * bound to its resolved `BatchAdaptersConfig` via a follow-up\n * `useFactory` provider.\n *\n * **Note on adapter module merging.** NestJS cannot dynamically\n * import a `DynamicModule` at module-build time, so the\n * `forRootAsync` path does NOT auto-merge the adapter modules'\n * `globalProviders` into the core module's `providers` list the way\n * `forRoot` does. Two consequences for the async path:\n *\n * 1. The adapter `DynamicModule`s must be passed in the caller's\n * `imports` array directly (e.g.\n * `imports: [MikroOrmAdapter.module, InProcessAdapter.module]`)\n * so Nest sees them in the module graph.\n * 2. The factory's return value is used only for the\n * `MODULE_OPTIONS_TOKEN` binding (adapters introspection);\n * sibling packages and the host can read the resolved config\n * via `@Inject(MODULE_OPTIONS_TOKEN)`.\n *\n * For the full auto-merge (adapter modules + `globalProviders`\n * registered into core's own DI scope), prefer `forRoot` with a\n * pre-resolved `BatchAdaptersConfig`. The async path is for\n * adapters whose factory needs to consult a config service or\n * another async provider to decide which adapter to plug in.\n */\nexport interface NestBatchModuleAsyncOptions {\n imports?: DynamicModule['imports'];\n useFactory: (...args: unknown[]) => Promise<BatchAdaptersConfig> | BatchAdaptersConfig;\n inject?: readonly unknown[];\n}\n\n/**\n * Sentinel provider token used by `forRootAsync` to plumb the user's\n * `useFactory` through DI. The factory is registered under this\n * token, and `MODULE_OPTIONS_TOKEN` resolves to its output via a\n * follow-up `useFactory` provider.\n *\n * `Symbol.for(...)` makes the token stable across module boundaries:\n * tooling or sibling packages that know the description string can\n * resolve the same symbol without importing this file. Matches the\n * convention used by `BATCH_SCHEDULE_REGISTRY`, `MODULE_OPTIONS_TOKEN`,\n * `JOB_REPOSITORY_TOKEN`, etc. in `./tokens.ts`.\n */\nconst OPTIONS_FACTORY: symbol = Symbol.for('@nest-batch/core/OPTIONS_FACTORY');\n\n/**\n * Hook that runs on `OnApplicationBootstrap` to wire together the\n * discovery → compile → register pipeline.\n *\n * Why a separate provider and not a method on `JobRegistry` or\n * `DefinitionCompiler`?\n * - `BatchExplorer.onModuleInit` populates the discovered\n * list once the DI container is ready. Compilation needs every\n * `@Jobable` provider to be instantiated, so it must run *after*\n * `onModuleInit`.\n * - `OnApplicationBootstrap` is the latest point in Nest's lifecycle\n * before the app actually starts handling requests, so all of:\n * `forRoot` / `forRootAsync` providers, custom `useFactory` results,\n * and user-supplied job classes, are guaranteed to be live.\n * - Keeping the wire-up in a dedicated `BatchBootstrapper` means the\n * explorer/compiler/registry stay pure (no `onApplicationBootstrap`\n * coupling) and are independently testable.\n *\n * The bootstrapper also walks every discovered job for\n * `@BatchScheduled` metadata and registers the corresponding entries\n * into the `BatchScheduleRegistry` so scheduler adapters have a single,\n * stable place to read them from. Core itself remains metadata-only —\n * runtime adapters install timers and bridge schedule fires into job\n * launches.\n */\n@Injectable()\nexport class BatchBootstrapper implements OnApplicationBootstrap {\n private readonly logger = new Logger(BatchBootstrapper.name);\n\n constructor(\n private readonly explorer: BatchExplorer,\n private readonly compiler: DefinitionCompiler,\n private readonly registry: JobRegistry,\n private readonly scheduleRegistry: BatchScheduleRegistry,\n ) {}\n\n onApplicationBootstrap(): void {\n // 1. Compile + register every discovered job.\n for (const discovered of this.explorer.getDiscovered()) {\n const jobId = discovered.jobOptions.id;\n try {\n const def = this.compiler.compileFromDiscovered(discovered);\n this.registry.register(def);\n this.logger.log(`Registered job \"${jobId}\"`);\n } catch (err) {\n this.logger.error(`Failed to register job \"${jobId}\": ${(err as Error).message}`);\n throw err;\n }\n }\n\n // 2. Walk the same discovered set for @BatchScheduled metadata\n // and populate BatchScheduleRegistry. The metadata is stamped\n // by the decorator via `SetMetadata(KEY, value)`, which Nest\n // writes to the *function reference* of the decorated method\n // (not to the prototype+name slot). We therefore read it from\n // `prototype[name]` (the function), not from the (proto, name)\n // tuple.\n for (const discovered of this.explorer.getDiscovered()) {\n const jobId = discovered.jobOptions.id;\n const prototype = discovered.classRef.prototype as Record<string, unknown>;\n for (const name of this.allMethodNames(prototype)) {\n const fn = prototype[name];\n if (typeof fn !== 'function') continue;\n const meta = Reflect.getMetadata(BATCH_SCHEDULED_OPTIONS, fn) as\n | BatchScheduledMetadata\n | undefined;\n if (!meta) continue;\n const entry: BatchScheduleEntry = {\n jobId,\n scheduleName: meta.options.name,\n methodName: name,\n cron: meta.cron,\n timezone: meta.options.timezone,\n overlap: meta.options.overlap,\n startAt: meta.options.startAt,\n endAt: meta.options.endAt,\n inert: meta.inert,\n };\n try {\n this.scheduleRegistry.register(entry);\n this.logger.log(\n `Registered schedule for job \"${jobId}\"::${meta.options.name} ` +\n `(method=\"${name}\", cron=\"${meta.cron}\", tz=\"${meta.options.timezone}\")`,\n );\n } catch (err) {\n this.logger.error(\n `Failed to register schedule for job \"${jobId}\"::${meta.options.name}: ${\n (err as Error).message\n }`,\n );\n throw err;\n }\n }\n }\n }\n\n /**\n * Walk the prototype chain and return every own method name\n * (excluding `constructor`) up to (but not including)\n * `Object.prototype`. Same shape as `BatchExplorer.allMethodNames` —\n * we duplicate the walker here so the bootstrapper remains\n * independent of the explorer's internals.\n */\n private allMethodNames(prototype: object): Set<string> {\n const names = new Set<string>();\n let proto: object | null = prototype;\n while (proto && proto !== Object.prototype) {\n for (const name of Object.getOwnPropertyNames(proto)) {\n if (name === 'constructor') continue;\n names.add(name);\n }\n proto = Object.getPrototypeOf(proto);\n }\n return names;\n }\n}\n\n/**\n * Public Nest module that wires up the @nest-batch/core library.\n *\n * The module is a `global: true` `DynamicModule` whose `imports`,\n * `providers`, and `exports` are assembled at the call site by\n * `forRoot` (synchronous) or `forRootAsync` (sentinel-factory\n * pattern). In both paths the core providers and the executor\n * subgraph are auto-registered so the host does not have to wire\n * them by hand; adapter modules are imported as part of the same\n * `DynamicModule` so Nest's discovery phase sees every job class.\n *\n * @see {@link NestBatchModuleOptions} for the synchronous options shape\n * @see {@link NestBatchModuleAsyncOptions} for the async options shape\n * @see {@link BatchAdaptersConfig} for the adapter contract\n */\n@Module({})\nexport class NestBatchModule {\n /**\n * Build a provider list from an adapter's `globalProviders`,\n * automatically aliasing the abstract `JobRepository` class to\n * `JOB_REPOSITORY_TOKEN` when the symbol is present but the class\n * itself is not directly provided.\n *\n * This fixes the NestJS DI resolution gap: `JobExecutor`,\n * `JobLauncher`, and `InProcessExecutionStrategy` all inject the\n * abstract `JobRepository`, but adapters typically bind their\n * concrete implementation to `JOB_REPOSITORY_TOKEN`. NestJS cannot\n * resolve abstract class → concrete subclass automatically, so we\n * inject the alias here.\n *\n * Idempotent: if `JobRepository` is already directly provided (e.g.\n * `{ provide: JobRepository, useClass: MyRepo }`), no extra alias is\n * added.\n */\n private static buildProviders(adapterGlobalProviders: readonly Provider[]): Provider[] {\n const providers = [...adapterGlobalProviders];\n\n const hasJobRepositoryToken = providers.some(\n (p) =>\n typeof p === 'object' && p !== null && 'provide' in p && p.provide === JOB_REPOSITORY_TOKEN,\n );\n\n const hasJobRepositoryClass = providers.some(\n (p) =>\n p === JobRepository ||\n (typeof p === 'object' && p !== null && 'provide' in p && p.provide === JobRepository),\n );\n\n if (hasJobRepositoryToken && !hasJobRepositoryClass) {\n providers.push({\n provide: JobRepository,\n useExisting: JOB_REPOSITORY_TOKEN,\n });\n }\n\n const hasTransactionManagerToken = providers.some(\n (p) =>\n typeof p === 'object' &&\n p !== null &&\n 'provide' in p &&\n p.provide === TRANSACTION_MANAGER_TOKEN,\n );\n\n const hasTransactionManagerClass = providers.some(\n (p) =>\n p === TransactionManager ||\n (typeof p === 'object' && p !== null && 'provide' in p && p.provide === TransactionManager),\n );\n\n if (hasTransactionManagerToken && !hasTransactionManagerClass) {\n providers.push({\n provide: TransactionManager,\n useExisting: TRANSACTION_MANAGER_TOKEN,\n });\n }\n\n return providers;\n }\n\n /**\n * Best-effort resolution of `inject` tokens by reading the\n * `providers` metadata of modules listed in `imports`. This lets\n * `forRootAsync` evaluate a synchronous `useFactory` even when it\n * declares `inject` deps, provided those deps are `useValue`\n * providers exported by an imported module.\n *\n * Only `useValue` providers can be resolved this way — `useClass`\n * and `useFactory` providers need Nest's DI container and are\n * skipped.\n */\n private static resolveInjectValues(\n imports: NestBatchModuleAsyncOptions['imports'],\n inject: readonly unknown[],\n ): unknown[] {\n if (!inject || inject.length === 0) return [];\n\n const values: unknown[] = [];\n for (const token of inject) {\n let resolved: unknown = undefined;\n for (const mod of imports ?? []) {\n if (!mod) continue;\n\n let providers: Provider[] | undefined;\n\n if (typeof mod === 'object' && 'module' in mod) {\n const dynamicMod = mod as DynamicModule;\n providers = dynamicMod.providers;\n if (!providers && dynamicMod.module) {\n try {\n providers = Reflect.getMetadata('providers', dynamicMod.module);\n } catch {\n /* ignore metadata read failures */\n }\n }\n } else if (typeof mod === 'function') {\n try {\n providers = Reflect.getMetadata('providers', mod);\n } catch {\n /* ignore metadata read failures */\n }\n }\n\n for (const p of providers ?? []) {\n if (\n typeof p === 'object' &&\n p !== null &&\n 'provide' in p &&\n p.provide === token &&\n 'useValue' in p\n ) {\n resolved = p.useValue;\n break;\n }\n }\n if (resolved !== undefined) break;\n }\n values.push(resolved);\n }\n return values;\n }\n\n /**\n * Static (synchronous) configuration.\n *\n * Takes the resolved `BatchAdaptersConfig` and builds a\n * `global: true` `DynamicModule` that:\n *\n * 1. imports each adapter's `DynamicModule`\n * (`adapters.persistence.module` and `adapters.transport.module`);\n * 2. imports `DiscoveryModule` from `@nestjs/core` so the explorer\n * can use Nest's `DiscoveryService`;\n * 3. registers core's own providers — `JobRegistry`,\n * `DefinitionCompiler`, `BatchExplorer`, `FlowEvaluator`,\n * `BatchScheduleRegistry`, `BatchBootstrapper` — and the\n * executor subgraph (`JobExecutor`, `ChunkStepExecutor`,\n * `TaskletStepExecutor`, `ListenerInvoker`) so the host\n * does not have to wire them by hand;\n * 4. registers each adapter's `globalProviders` (e.g. the\n * `JobRepository` / `TransactionManager` implementations\n * from a persistence adapter) so the host can inject them\n * too;\n * 5. binds the `BatchAdaptersConfig` to `MODULE_OPTIONS_TOKEN`\n * via a value provider for adapter introspection.\n */\n static forRoot(options: NestBatchModuleOptions): DynamicModule {\n const { adapters } = options;\n const persistenceProviders = NestBatchModule.buildProviders(\n adapters.persistence.globalProviders ?? [],\n );\n const transportProviders = NestBatchModule.buildProviders(\n adapters.transport.globalProviders ?? [],\n );\n\n return {\n module: NestBatchModule,\n global: true,\n imports: [adapters.persistence.module, adapters.transport.module, DiscoveryModule],\n providers: [\n // Core classes (discovery + compile + register).\n JobRegistry,\n DefinitionCompiler,\n BatchExplorer,\n FlowEvaluator,\n BatchScheduleRegistry,\n BatchBootstrapper,\n // Executor subgraph (JobExecutor → Chunk/Tasklet/Listener).\n JobExecutor,\n ChunkStepExecutor,\n TaskletStepExecutor,\n ListenerInvoker,\n JobLauncher,\n JobExplorer,\n JobOperator,\n BatchWorkerRunner,\n // Resolved options bag for adapter introspection.\n {\n provide: MODULE_OPTIONS_TOKEN,\n useValue: adapters,\n },\n // Schedule registry symbol alias — the symbol itself\n // must be a provider (not just a class export) so the\n // `exports` entry below resolves through Nest's DI\n // validation.\n {\n provide: BATCH_SCHEDULE_REGISTRY,\n useExisting: BatchScheduleRegistry,\n },\n // Adapter-supplied global providers (e.g. JobRepository\n // / TransactionManager implementations).\n ...persistenceProviders,\n ...transportProviders,\n ],\n exports: [\n // Core classes — exported so sibling packages and the\n // host app can resolve them from the global module chain.\n JobRegistry,\n DefinitionCompiler,\n BatchExplorer,\n FlowEvaluator,\n BatchScheduleRegistry,\n BatchBootstrapper,\n // Tokens — exported so adapters can bind to them via\n // `@Inject(MODULE_OPTIONS_TOKEN)` and host code can read\n // the schedule registry by its stable symbol.\n BATCH_SCHEDULE_REGISTRY,\n MODULE_OPTIONS_TOKEN,\n // Executor subgraph — exported so adapters (e.g. the\n // `InProcessExecutionStrategy`) and host code can inject\n // them.\n JobExecutor,\n ChunkStepExecutor,\n TaskletStepExecutor,\n ListenerInvoker,\n JobLauncher,\n JobExplorer,\n JobOperator,\n BatchWorkerRunner,\n // Adapter-supplied global providers — re-exported so the\n // host can resolve the persistence + transport bindings\n // from the global module chain.\n ...persistenceProviders,\n ...transportProviders,\n ],\n };\n }\n\n /**\n * Async configuration — useful when the adapter set comes from a\n * config service or another async source.\n *\n * Mirrors the `BullmqBatchModule.forRootAsync` pattern: the user's\n * `useFactory` is registered as a sentinel provider under\n * `OPTIONS_FACTORY`, and `MODULE_OPTIONS_TOKEN` is bound to its\n * resolved `BatchAdaptersConfig` via a follow-up `useFactory`\n * provider. The user's `imports` + `inject` are forwarded as-is\n * so the factory can pull from `ConfigService` or any other\n * DI-bound dependency.\n *\n * See the `NestBatchModuleAsyncOptions` JSDoc for the adapter\n * module merging caveat — the async path does not auto-merge\n * the adapter modules' `globalProviders`. The adapter\n * `DynamicModule`s must be passed in the caller's `imports`\n * array so Nest sees them in the module graph; the factory's\n * return value is used only for `MODULE_OPTIONS_TOKEN`.\n */\n static forRootAsync(\n options: NestBatchModuleAsyncOptions,\n ): DynamicModule | Promise<DynamicModule> {\n const { imports = [], inject = [], useFactory } = options;\n\n // Try to evaluate the factory synchronously so we can merge\n // adapter `globalProviders` at module-build time (mirroring\n // what `forRoot` does). If the factory is async or needs\n // injected deps we can't resolve, fall back to the pure\n // sentinel-factory path.\n const injectValues = NestBatchModule.resolveInjectValues(imports, inject);\n const factoryResult = useFactory(...injectValues);\n\n if (factoryResult instanceof Promise) {\n return factoryResult.then((adapters) => NestBatchModule.buildAsyncModule(options, adapters));\n }\n\n return NestBatchModule.buildAsyncModule(options, factoryResult);\n }\n\n /**\n * Shared module builder used by both the sync and async branches\n * of `forRootAsync`. When the factory result is available\n * synchronously we merge adapter `globalProviders` (plus the\n * `JobRepository` alias) into core's own provider list, exactly\n * like `forRoot` does.\n */\n private static buildAsyncModule(\n options: NestBatchModuleAsyncOptions,\n adapters: BatchAdaptersConfig,\n ): DynamicModule {\n const { imports = [], inject = [], useFactory } = options;\n\n const persistenceProviders = NestBatchModule.buildProviders(\n adapters.persistence.globalProviders ?? [],\n );\n const transportProviders = NestBatchModule.buildProviders(\n adapters.transport.globalProviders ?? [],\n );\n\n // Sentinel factory provider: holds the user's `useFactory` and\n // any `inject` deps. Other providers can pull the resolved\n // `BatchAdaptersConfig` via `@Inject(OPTIONS_FACTORY)` if they\n // need to.\n const factoryProvider: Provider = {\n provide: OPTIONS_FACTORY,\n useFactory: useFactory as (...args: unknown[]) => unknown,\n inject: [...inject] as Array<string | symbol | Function>,\n };\n\n // Options provider: bridges the sentinel factory to the\n // canonical `MODULE_OPTIONS_TOKEN` so adapters + host code can\n // read the resolved config by its stable symbol.\n const optionsProvider: Provider = {\n provide: MODULE_OPTIONS_TOKEN,\n useFactory: (fromFactory: BatchAdaptersConfig | undefined): BatchAdaptersConfig | undefined =>\n fromFactory,\n inject: [OPTIONS_FACTORY],\n };\n\n return {\n module: NestBatchModule,\n global: true,\n imports: [\n ...imports,\n adapters.persistence.module,\n adapters.transport.module,\n DiscoveryModule,\n ],\n providers: [\n // Core classes (discovery + compile + register).\n JobRegistry,\n DefinitionCompiler,\n BatchExplorer,\n FlowEvaluator,\n BatchScheduleRegistry,\n BatchBootstrapper,\n // Executor subgraph (JobExecutor → Chunk/Tasklet/Listener).\n JobExecutor,\n ChunkStepExecutor,\n TaskletStepExecutor,\n ListenerInvoker,\n JobLauncher,\n JobExplorer,\n JobOperator,\n BatchWorkerRunner,\n {\n provide: BATCH_SCHEDULE_REGISTRY,\n useExisting: BatchScheduleRegistry,\n },\n // Adapter-supplied global providers (e.g. JobRepository\n // / TransactionManager implementations).\n ...persistenceProviders,\n ...transportProviders,\n // Sentinel factory + options provider (the async path).\n factoryProvider,\n optionsProvider,\n ],\n exports: [\n // Core classes.\n JobRegistry,\n DefinitionCompiler,\n BatchExplorer,\n FlowEvaluator,\n BatchScheduleRegistry,\n BatchBootstrapper,\n // Tokens.\n BATCH_SCHEDULE_REGISTRY,\n MODULE_OPTIONS_TOKEN,\n // Executor subgraph.\n JobExecutor,\n ChunkStepExecutor,\n TaskletStepExecutor,\n ListenerInvoker,\n JobLauncher,\n JobExplorer,\n JobOperator,\n BatchWorkerRunner,\n // Adapter-supplied global providers — re-exported so the\n // host can resolve the persistence + transport bindings\n // from the global module chain.\n ...persistenceProviders,\n ...transportProviders,\n ],\n };\n }\n}\n"],"names":["BatchBootstrapper","IN_PROCESS_EXECUTION_STRATEGY_PROVIDER","InProcessExecutionStrategy","NestBatchModule","OPTIONS_FACTORY","Symbol","for","logger","Logger","name","explorer","compiler","registry","scheduleRegistry","onApplicationBootstrap","discovered","getDiscovered","jobId","jobOptions","id","def","compileFromDiscovered","register","log","err","error","message","prototype","classRef","allMethodNames","fn","meta","Reflect","getMetadata","BATCH_SCHEDULED_OPTIONS","entry","scheduleName","options","methodName","cron","timezone","overlap","startAt","endAt","inert","names","Set","proto","Object","getOwnPropertyNames","add","getPrototypeOf","buildProviders","adapterGlobalProviders","providers","hasJobRepositoryToken","some","p","provide","JOB_REPOSITORY_TOKEN","hasJobRepositoryClass","JobRepository","push","useExisting","hasTransactionManagerToken","TRANSACTION_MANAGER_TOKEN","hasTransactionManagerClass","TransactionManager","resolveInjectValues","imports","inject","length","values","token","resolved","undefined","mod","dynamicMod","module","useValue","forRoot","adapters","persistenceProviders","persistence","globalProviders","transportProviders","transport","global","DiscoveryModule","JobRegistry","DefinitionCompiler","BatchExplorer","FlowEvaluator","BatchScheduleRegistry","JobExecutor","ChunkStepExecutor","TaskletStepExecutor","ListenerInvoker","JobLauncher","JobExplorer","JobOperator","BatchWorkerRunner","MODULE_OPTIONS_TOKEN","BATCH_SCHEDULE_REGISTRY","exports","forRootAsync","useFactory","injectValues","factoryResult","Promise","then","buildAsyncModule","factoryProvider","optionsProvider","fromFactory"],"mappings":"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"}
|
|
@@ -46,12 +46,11 @@ export declare const TRANSACTION_MANAGER_TOKEN: symbol;
|
|
|
46
46
|
* Injection token for the `BatchScheduleRegistry` provider.
|
|
47
47
|
*
|
|
48
48
|
* The `BatchExplorer` populates this registry with `@BatchScheduled`
|
|
49
|
-
* metadata it discovers on `@Jobable` classes.
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
* the explorer's internal state.
|
|
49
|
+
* metadata it discovers on `@Jobable` classes. Scheduler adapters read
|
|
50
|
+
* from this registry to install the actual timers or external schedules.
|
|
51
|
+
* Keeping the registry as a stable token means adapters can inject it
|
|
52
|
+
* (for introspection / health checks) without depending on the explorer's
|
|
53
|
+
* internal state.
|
|
55
54
|
*/
|
|
56
55
|
export declare const BATCH_SCHEDULE_REGISTRY: symbol;
|
|
57
56
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../../src/module/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAErE;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,EAAE,
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../../src/module/tokens.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAErE;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,EAAE,MAAsD,CAAC;AAE1F;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,EAAE,MAA2D,CAAC;AAEpG;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAErC,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,oBAAoB,EAAE,MAAsD,CAAC;AAE1F;;;;;;;;;GASG;AACH,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/module/tokens.ts"],"sourcesContent":["/**\n * Public injection tokens for the `@nest-batch/core` module surface.\n *\n * These tokens are the stable, package-scoped identifiers sibling packages\n * (e.g. `@nest-batch/mikro-orm`, `@nest-batch/typeorm`, `@nest-batch/bullmq`)\n * use to bind their own providers into the core DI graph. They are\n * registered in the global `Symbol.for` registry under stable, package-\n * scoped keys so they are unique across the host process even if the\n * package is loaded multiple times.\n *\n * Why symbols and not string tokens?\n * - Symbols cannot collide with a user string by accident.\n * - `Symbol.for(key)` gives us cross-realm uniqueness without the\n * caller having to thread the token through `import` chains — a\n * host can resolve any of these tokens by reaching into\n * `Symbol.for('...description...')` and getting the same value.\n * - Symbols are erased from emitted JavaScript, so they do not\n * pollute production bundles with debug strings.\n *\n * Why a stable description (not `Symbol(description)`)?\n * - `Symbol.for('k')` only works if the *same* string is passed both\n * times. Hard-coding a `description` lets future sibling packages\n * resolve the token without importing this file (useful for tooling\n * and for ad-hoc cross-package debugging).\n */\nimport { EXECUTION_STRATEGY } from '../execution/execution-strategy';\n\n/**\n * Injection token for the `JobRepository` implementation.\n *\n * Adapter packages (`@nest-batch/mikro-orm`, `@nest-batch/typeorm`, ...)\n * bind their `JobRepository` subclass to this token. By default the host\n * app is expected to register its own `JobRepository` provider — core\n * does NOT ship a default binding because the choice of persistence\n * backend is the host's decision.\n */\nexport const JOB_REPOSITORY_TOKEN: symbol = Symbol.for(
|
|
1
|
+
{"version":3,"sources":["../../../src/module/tokens.ts"],"sourcesContent":["/**\n * Public injection tokens for the `@nest-batch/core` module surface.\n *\n * These tokens are the stable, package-scoped identifiers sibling packages\n * (e.g. `@nest-batch/mikro-orm`, `@nest-batch/typeorm`, `@nest-batch/bullmq`)\n * use to bind their own providers into the core DI graph. They are\n * registered in the global `Symbol.for` registry under stable, package-\n * scoped keys so they are unique across the host process even if the\n * package is loaded multiple times.\n *\n * Why symbols and not string tokens?\n * - Symbols cannot collide with a user string by accident.\n * - `Symbol.for(key)` gives us cross-realm uniqueness without the\n * caller having to thread the token through `import` chains — a\n * host can resolve any of these tokens by reaching into\n * `Symbol.for('...description...')` and getting the same value.\n * - Symbols are erased from emitted JavaScript, so they do not\n * pollute production bundles with debug strings.\n *\n * Why a stable description (not `Symbol(description)`)?\n * - `Symbol.for('k')` only works if the *same* string is passed both\n * times. Hard-coding a `description` lets future sibling packages\n * resolve the token without importing this file (useful for tooling\n * and for ad-hoc cross-package debugging).\n */\nimport { EXECUTION_STRATEGY } from '../execution/execution-strategy';\n\n/**\n * Injection token for the `JobRepository` implementation.\n *\n * Adapter packages (`@nest-batch/mikro-orm`, `@nest-batch/typeorm`, ...)\n * bind their `JobRepository` subclass to this token. By default the host\n * app is expected to register its own `JobRepository` provider — core\n * does NOT ship a default binding because the choice of persistence\n * backend is the host's decision.\n */\nexport const JOB_REPOSITORY_TOKEN: symbol = Symbol.for('@nest-batch/core/JOB_REPOSITORY');\n\n/**\n * Injection token for the `TransactionManager` implementation.\n *\n * Adapter packages bind their transaction manager to this token. The\n * `JobRepository` implementation is expected to participate in the same\n * transaction (e.g. share the same `EntityManager` / `DataSource`).\n */\nexport const TRANSACTION_MANAGER_TOKEN: symbol = Symbol.for('@nest-batch/core/TRANSACTION_MANAGER');\n\n/**\n * Injection token for the `BatchScheduleRegistry` provider.\n *\n * The `BatchExplorer` populates this registry with `@BatchScheduled`\n * metadata it discovers on `@Jobable` classes. Scheduler adapters read\n * from this registry to install the actual timers or external schedules.\n * Keeping the registry as a stable token means adapters can inject it\n * (for introspection / health checks) without depending on the explorer's\n * internal state.\n */\nexport const BATCH_SCHEDULE_REGISTRY: symbol = Symbol.for(\n '@nest-batch/core/BATCH_SCHEDULE_REGISTRY',\n);\n\n/**\n * Injection token for the module's resolved options bag.\n *\n * Backs the post-`useFactory` options read (T2 will wire the async\n * factory provider to write into this slot). Sibling packages and the\n * host app can read the resolved options by injecting this token. The\n * shape is the union of `NestBatchModuleOptions` plus whatever an\n * adapter's own config contributed, so the value is a\n * `Record<string, unknown>` at runtime.\n *\n * The previous `'BATCH_OPTIONS'` string alias was removed in the\n * T1 type-contract refactor — hosts that need the options bag should\n * inject `MODULE_OPTIONS_TOKEN` instead.\n */\nexport const MODULE_OPTIONS_TOKEN: symbol = Symbol.for('@nest-batch/core/MODULE_OPTIONS');\n\n/**\n * Polymorphic execution strategy token.\n *\n * Re-exported here from `execution/execution-strategy.ts` so that the\n * module surface is the single import path for downstream packages.\n * Apps that want the default in-process strategy wire up\n * `IN_PROCESS_EXECUTION_STRATEGY_PROVIDER`; sibling packages (e.g.\n * `@nest-batch/bullmq`) provide a custom binding under this same\n * token.\n */\nexport { EXECUTION_STRATEGY };\n"],"names":["BATCH_SCHEDULE_REGISTRY","EXECUTION_STRATEGY","JOB_REPOSITORY_TOKEN","MODULE_OPTIONS_TOKEN","TRANSACTION_MANAGER_TOKEN","Symbol","for"],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;CAwBC;;;;;;;;;;;QAiCYA;eAAAA;;QA8BJC;eAAAA,qCAAkB;;QAnDdC;eAAAA;;QAuCAC;eAAAA;;QA9BAC;eAAAA;;;mCApBsB;AAW5B,MAAMF,uBAA+BG,OAAOC,GAAG,CAAC;AAShD,MAAMF,4BAAoCC,OAAOC,GAAG,CAAC;AAYrD,MAAMN,0BAAkCK,OAAOC,GAAG,CACvD;AAiBK,MAAMH,uBAA+BE,OAAOC,GAAG,CAAC"}
|
|
@@ -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:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"partition-helpers.d.ts","sourceRoot":"","sources":["../../src/partition-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEtE;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,4BAA4B,CAAC;AAEjE;;;;;;;GAOG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;
|
|
1
|
+
{"version":3,"file":"partition-helpers.d.ts","sourceRoot":"","sources":["../../src/partition-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAEtE;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,4BAA4B,CAAC;AAEjE;;;;;;;GAOG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;aAI7B,OAAO,CAAC,EAAE,OAAO;IAHnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEpB,OAAO,EAAE,MAAM,EACC,OAAO,CAAC,EAAE,OAAO,YAAA;CAMpC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,oBAAoB,GAAG,SAAS,GAAG,IAAI,CAgBrF;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAC1B,CAAC,EAAE,MAAM,EACT,CAAC,EAAE,MAAM,EACT,KAAK,EAAE,MAAM,GACZ,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,CAmBrC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAa7F;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;0CAEsC;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;CACtD;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,UAAU,EAAE,oBAAoB,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC,GAAG,iBAAiB,CAQpB"}
|
|
@@ -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:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/partition-helpers.ts"],"sourcesContent":["/**\n * Pure partition helpers for chunked steps.\n *\n * This module is the single source of truth for partition validation\n * and the default partition-range shape. It is deliberately\n * dependency-light: no `@nest-batch/bullmq`, no `@nest-batch/kafka`,\n * no ORMs, no cron — verified by\n * `packages/core/tests/core/boundary/no-forbidden-imports.test.ts`.\n *\n * The helpers are consumed by:\n * - `packages/core/src/compiler/definition-compiler.ts` (validation\n * at compile time),\n * - `packages/core/src/core/validation/definition-validator.ts`\n * (cross-checks the resolved `JobDefinition`),\n * - `packages/core/src/execution/in-process-execution-strategy.ts`\n * (the in-process adapter's partition guard),\n * - `packages/bullmq/src/bullmq-runtime.service.ts` (the BullMQ\n * strategy's enqueue fan-out + the worker's `partitionIndex`\n * enforcement),\n * - `packages/kafka/src/kafka-runtime.service.ts` (the Kafka\n * mirror — T9).\n *\n * Pinned by:\n * - `docs/RELEASE-0.2.0.md §6` — the v1 partition contract.\n * - `packages/bullmq/tests/partition-invariant.test.ts` — T-AC-3\n * first half (the BullMQ side).\n * - `packages/kafka/tests/partition-invariant.test.ts` — T-AC-3\n * second half (the Kafka side, T9).\n */\n\nimport type { ChunkPartitionConfig } from './core/ir/step-definition';\n\n/**\n * Error code returned / thrown by the partition helpers. Stable for\n * callers that want to switch on it (the BullMQ / Kafka workers\n * surface this in their `exitMessage` when a partition payload is\n * out of range).\n */\nexport const INVALID_PARTITION_INDEX = 'INVALID_PARTITION_INDEX';\n\n/**\n * Thrown when a partition's `count` is not a positive integer, or\n * when a runtime-resolved `partitionIndex` falls outside `[0, count)`.\n *\n * Distinct from `InvalidFlowGraphError` because this is a *value*\n * problem (the input is out of range) rather than a *graph*\n * problem (the step graph is malformed). The `code` is stable.\n */\nexport class InvalidPartitionsError extends Error {\n readonly code: string;\n constructor(message: string, public readonly details?: unknown) {\n super(message);\n this.name = 'InvalidPartitionsError';\n this.code = 'INVALID_PARTITIONS';\n }\n}\n\n/**\n * Validate a `ChunkPartitionConfig` at compile time. Throws\n * `InvalidPartitionsError` when the config is structurally invalid:\n *\n * - `count` is not a finite integer\n * - `count <= 0` (a partition count of zero is meaningless)\n * - `range` is set but is not a function\n *\n * `count === 1` is allowed but the runtime short-circuits it to\n * the non-partitioned path; we still validate it here so a typo\n * (`count: 0` vs `count: 1`) fails loudly at the compiler.\n */\nexport function validatePartitions(partitions: ChunkPartitionConfig | undefined): void {\n if (partitions === undefined) return;\n if (!Number.isInteger(partitions.count) || partitions.count <= 0) {\n throw new InvalidPartitionsError(\n `ChunkStepDefinition.partitions.count must be a positive integer, got ${String(\n partitions.count,\n )}`,\n { count: partitions.count },\n );\n }\n if (partitions.range !== undefined && typeof partitions.range !== 'function') {\n throw new InvalidPartitionsError(\n `ChunkStepDefinition.partitions.range must be a function when present, got ${typeof partitions.range}`,\n { rangeType: typeof partitions.range },\n );\n }\n}\n\n/**\n * The default even-split partition range, given a known total item\n * count. The formula mirrors the v1 contract in\n * `docs/RELEASE-0.2.0.md §6.1`:\n *\n * partition `i` of `n` over `total` items:\n * [floor(i * total / n), floor((i+1) * total / n))\n *\n * Pure function. Exported so hosts that want the \"even split\"\n * behaviour without re-implementing the math can reuse it:\n *\n * const r = defaultRange(i, n, total);\n * partitions: { count: n, range: (i, n) => defaultRange(i, n, total) }\n *\n * `total` is required because the runtime has no generic way to\n * count the input — only the host's reader knows. The math is\n * robust to `total === 0` (returns `[0, 0)` for every partition).\n */\nexport function defaultRange(\n i: number,\n n: number,\n total: number,\n): readonly [from: number, to: number] {\n if (!Number.isInteger(i) || i < 0 || i >= n) {\n throw new InvalidPartitionsError(\n `defaultRange: partition index ${i} out of range [0, ${n})`,\n { i, n },\n );\n }\n if (!Number.isInteger(n) || n <= 0) {\n throw new InvalidPartitionsError(`defaultRange: count ${n} must be a positive integer`, { n });\n }\n if (!Number.isFinite(total) || total < 0) {\n throw new InvalidPartitionsError(\n `defaultRange: total ${total} must be a non-negative finite number`,\n { total },\n );\n }\n const from = Math.floor((i * total) / n);\n const to = Math.floor(((i + 1) * total) / n);\n return [from, to] as const;\n}\n\n/**\n * Runtime check that a `partitionIndex` on a job payload is in\n * range for the configured `count`. Throws\n * `InvalidPartitionsError` when the index is undefined, not an\n * integer, negative, or `>= count`.\n *\n * The BullMQ / Kafka worker's `processJob` calls this with the\n * payload's `partitionIndex` and the step's `partitions.count` so\n * an out-of-range index becomes a hard step failure (the runtime\n * surfaces it as `FAILED` with the invariant violation in the\n * `exitMessage`).\n */\nexport function enforcePartitionIndex(\n partitionIndex: number | undefined,\n count: number,\n): void {\n if (partitionIndex === undefined) {\n throw new InvalidPartitionsError(\n `partitionIndex is required for a partitioned step (count=${count})`,\n { count },\n );\n }\n if (\n !Number.isInteger(partitionIndex) ||\n partitionIndex < 0 ||\n partitionIndex >= count\n ) {\n throw new InvalidPartitionsError(\n `partitionIndex ${partitionIndex} is out of range [0, ${count})`,\n { partitionIndex, count },\n );\n }\n}\n\n/**\n * Resolved partition info passed through the runtime. Built from\n * the step's `partitions` config and the BullMQ / Kafka payload's\n * `partitionIndex`. The runtime uses this to:\n * - bound the chunk executor's read loop (when the host provided\n * a `range` resolver),\n * - tag the persisted `StepExecution` for diagnostics (a future\n * task; the v1 contract only requires the chunk executor to\n * honour the range).\n */\nexport interface ResolvedPartition {\n readonly count: number;\n readonly index: number;\n /** The partition's resolved `[from, to)` range, or `undefined`\n * when the step did not provide a `range` resolver (the default\n * behaviour is \"read until EOF\"). */\n readonly range?: readonly [from: number, to: number];\n}\n\n/**\n * Resolve a partition's metadata from the step's `partitions`\n * config and the transport's `partitionIndex`. Calls\n * `enforcePartitionIndex` to fail loudly on out-of-range indices.\n *\n * The `range` is computed only when the step provides a resolver;\n * otherwise the returned `ResolvedPartition` is `count` + `index`\n * with no `range`, and the chunk executor reads until EOF.\n */\nexport function resolvePartition(args: {\n partitions: ChunkPartitionConfig;\n partitionIndex: number | undefined;\n}): ResolvedPartition {\n enforcePartitionIndex(args.partitionIndex, args.partitions.count);\n const index = args.partitionIndex as number;\n if (args.partitions.range === undefined) {\n return { count: args.partitions.count, index };\n }\n const range = args.partitions.range(index, args.partitions.count);\n return { count: args.partitions.count, index, range };\n}\n"],"names":["INVALID_PARTITION_INDEX","InvalidPartitionsError","defaultRange","enforcePartitionIndex","resolvePartition","validatePartitions","Error","code","message","details","name","partitions","undefined","Number","isInteger","count","String","range","rangeType","i","n","total","isFinite","from","Math","floor","to","partitionIndex","args","index"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/partition-helpers.ts"],"sourcesContent":["/**\n * Pure partition helpers for chunked steps.\n *\n * This module is the single source of truth for partition validation\n * and the default partition-range shape. It is deliberately\n * dependency-light: no `@nest-batch/bullmq`, no `@nest-batch/kafka`,\n * no ORMs — verified by\n * `packages/core/tests/core/boundary/no-forbidden-imports.test.ts`.\n *\n * The helpers are consumed by:\n * - `packages/core/src/compiler/definition-compiler.ts` (validation\n * at compile time),\n * - `packages/core/src/core/validation/definition-validator.ts`\n * (cross-checks the resolved `JobDefinition`),\n * - `packages/core/src/execution/in-process-execution-strategy.ts`\n * (the in-process adapter's partition guard),\n * - `packages/bullmq/src/bullmq-runtime.ts` (the BullMQ\n * strategy's enqueue fan-out + the worker's `partitionIndex`\n * enforcement),\n * - `packages/kafka/src/kafka-runtime.ts` (the Kafka\n * mirror — T9).\n *\n * Pinned by:\n * - `docs/RELEASE-0.2.0.md §6` — the v1 partition contract.\n * - `packages/bullmq/tests/partition-invariant.test.ts` — T-AC-3\n * first half (the BullMQ side).\n * - `packages/kafka/tests/partition-invariant.test.ts` — T-AC-3\n * second half (the Kafka side, T9).\n */\n\nimport type { ChunkPartitionConfig } from './core/ir/step-definition';\n\n/**\n * Error code returned / thrown by the partition helpers. Stable for\n * callers that want to switch on it (the BullMQ / Kafka workers\n * surface this in their `exitMessage` when a partition payload is\n * out of range).\n */\nexport const INVALID_PARTITION_INDEX = 'INVALID_PARTITION_INDEX';\n\n/**\n * Thrown when a partition's `count` is not a positive integer, or\n * when a runtime-resolved `partitionIndex` falls outside `[0, count)`.\n *\n * Distinct from `InvalidFlowGraphError` because this is a *value*\n * problem (the input is out of range) rather than a *graph*\n * problem (the step graph is malformed). The `code` is stable.\n */\nexport class InvalidPartitionsError extends Error {\n readonly code: string;\n constructor(\n message: string,\n public readonly details?: unknown,\n ) {\n super(message);\n this.name = 'InvalidPartitionsError';\n this.code = 'INVALID_PARTITIONS';\n }\n}\n\n/**\n * Validate a `ChunkPartitionConfig` at compile time. Throws\n * `InvalidPartitionsError` when the config is structurally invalid:\n *\n * - `count` is not a finite integer\n * - `count <= 0` (a partition count of zero is meaningless)\n * - `range` is set but is not a function\n *\n * `count === 1` is allowed but the runtime short-circuits it to\n * the non-partitioned path; we still validate it here so a typo\n * (`count: 0` vs `count: 1`) fails loudly at the compiler.\n */\nexport function validatePartitions(partitions: ChunkPartitionConfig | undefined): void {\n if (partitions === undefined) return;\n if (!Number.isInteger(partitions.count) || partitions.count <= 0) {\n throw new InvalidPartitionsError(\n `ChunkStepDefinition.partitions.count must be a positive integer, got ${String(\n partitions.count,\n )}`,\n { count: partitions.count },\n );\n }\n if (partitions.range !== undefined && typeof partitions.range !== 'function') {\n throw new InvalidPartitionsError(\n `ChunkStepDefinition.partitions.range must be a function when present, got ${typeof partitions.range}`,\n { rangeType: typeof partitions.range },\n );\n }\n}\n\n/**\n * The default even-split partition range, given a known total item\n * count. The formula mirrors the v1 contract in\n * `docs/RELEASE-0.2.0.md §6.1`:\n *\n * partition `i` of `n` over `total` items:\n * [floor(i * total / n), floor((i+1) * total / n))\n *\n * Pure function. Exported so hosts that want the \"even split\"\n * behaviour without re-implementing the math can reuse it:\n *\n * const r = defaultRange(i, n, total);\n * partitions: { count: n, range: (i, n) => defaultRange(i, n, total) }\n *\n * `total` is required because the runtime has no generic way to\n * count the input — only the host's reader knows. The math is\n * robust to `total === 0` (returns `[0, 0)` for every partition).\n */\nexport function defaultRange(\n i: number,\n n: number,\n total: number,\n): readonly [from: number, to: number] {\n if (!Number.isInteger(i) || i < 0 || i >= n) {\n throw new InvalidPartitionsError(`defaultRange: partition index ${i} out of range [0, ${n})`, {\n i,\n n,\n });\n }\n if (!Number.isInteger(n) || n <= 0) {\n throw new InvalidPartitionsError(`defaultRange: count ${n} must be a positive integer`, { n });\n }\n if (!Number.isFinite(total) || total < 0) {\n throw new InvalidPartitionsError(\n `defaultRange: total ${total} must be a non-negative finite number`,\n { total },\n );\n }\n const from = Math.floor((i * total) / n);\n const to = Math.floor(((i + 1) * total) / n);\n return [from, to] as const;\n}\n\n/**\n * Runtime check that a `partitionIndex` on a job payload is in\n * range for the configured `count`. Throws\n * `InvalidPartitionsError` when the index is undefined, not an\n * integer, negative, or `>= count`.\n *\n * The BullMQ / Kafka worker's `processJob` calls this with the\n * payload's `partitionIndex` and the step's `partitions.count` so\n * an out-of-range index becomes a hard step failure (the runtime\n * surfaces it as `FAILED` with the invariant violation in the\n * `exitMessage`).\n */\nexport function enforcePartitionIndex(partitionIndex: number | undefined, count: number): void {\n if (partitionIndex === undefined) {\n throw new InvalidPartitionsError(\n `partitionIndex is required for a partitioned step (count=${count})`,\n { count },\n );\n }\n if (!Number.isInteger(partitionIndex) || partitionIndex < 0 || partitionIndex >= count) {\n throw new InvalidPartitionsError(\n `partitionIndex ${partitionIndex} is out of range [0, ${count})`,\n { partitionIndex, count },\n );\n }\n}\n\n/**\n * Resolved partition info passed through the runtime. Built from\n * the step's `partitions` config and the BullMQ / Kafka payload's\n * `partitionIndex`. The runtime uses this to:\n * - bound the chunk executor's read loop (when the host provided\n * a `range` resolver),\n * - tag the persisted `StepExecution` for diagnostics (a future\n * task; the v1 contract only requires the chunk executor to\n * honour the range).\n */\nexport interface ResolvedPartition {\n readonly count: number;\n readonly index: number;\n /** The partition's resolved `[from, to)` range, or `undefined`\n * when the step did not provide a `range` resolver (the default\n * behaviour is \"read until EOF\"). */\n readonly range?: readonly [from: number, to: number];\n}\n\n/**\n * Resolve a partition's metadata from the step's `partitions`\n * config and the transport's `partitionIndex`. Calls\n * `enforcePartitionIndex` to fail loudly on out-of-range indices.\n *\n * The `range` is computed only when the step provides a resolver;\n * otherwise the returned `ResolvedPartition` is `count` + `index`\n * with no `range`, and the chunk executor reads until EOF.\n */\nexport function resolvePartition(args: {\n partitions: ChunkPartitionConfig;\n partitionIndex: number | undefined;\n}): ResolvedPartition {\n enforcePartitionIndex(args.partitionIndex, args.partitions.count);\n const index = args.partitionIndex as number;\n if (args.partitions.range === undefined) {\n return { count: args.partitions.count, index };\n }\n const range = args.partitions.range(index, args.partitions.count);\n return { count: args.partitions.count, index, range };\n}\n"],"names":["INVALID_PARTITION_INDEX","InvalidPartitionsError","defaultRange","enforcePartitionIndex","resolvePartition","validatePartitions","Error","code","message","details","name","partitions","undefined","Number","isInteger","count","String","range","rangeType","i","n","total","isFinite","from","Math","floor","to","partitionIndex","args","index"],"mappings":"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"}
|
|
@@ -7,11 +7,10 @@ export declare const BATCH_SCHEDULED_OPTIONS = "nest-batch:scheduled";
|
|
|
7
7
|
* - `'queue'` — buffer the new tick and start it after the current one ends.
|
|
8
8
|
* - `'parallel'` — start the new tick alongside the current one.
|
|
9
9
|
*
|
|
10
|
-
* The
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* is the contract: the runtime applies the default.
|
|
10
|
+
* The active scheduler adapter reads this value off the stored metadata
|
|
11
|
+
* and applies the policy at dispatch time. The decorator itself MUST NOT
|
|
12
|
+
* silently default the policy to `'skip'` on the user's behalf — leaving
|
|
13
|
+
* it `undefined` here is the contract: the runtime applies the default.
|
|
15
14
|
*/
|
|
16
15
|
export type BatchOverlapPolicy = 'skip' | 'queue' | 'parallel';
|
|
17
16
|
/**
|
|
@@ -38,9 +37,8 @@ export interface BatchScheduledOptions {
|
|
|
38
37
|
}
|
|
39
38
|
/**
|
|
40
39
|
* The shape stored under the `BATCH_SCHEDULED_OPTIONS` metadata key on
|
|
41
|
-
* the decorated method function.
|
|
42
|
-
*
|
|
43
|
-
* the job with the underlying scheduler.
|
|
40
|
+
* the decorated method function. Scheduler adapters read this verbatim
|
|
41
|
+
* to register the job with the underlying scheduler.
|
|
44
42
|
*
|
|
45
43
|
* Note: `inert` lives at the top level (not inside `options`) on
|
|
46
44
|
* purpose. It is a *runtime* flag captured at decoration time from
|
|
@@ -60,9 +58,9 @@ export interface BatchScheduledMetadata {
|
|
|
60
58
|
* includes the invalid value and the failure reason so the error
|
|
61
59
|
* is greppable in logs and pinned in test assertions.
|
|
62
60
|
*
|
|
63
|
-
* Exported from the module barrel so adapter packages
|
|
64
|
-
*
|
|
65
|
-
*
|
|
61
|
+
* Exported from the module barrel so adapter packages can
|
|
62
|
+
* `instanceof`-check it without reaching into the decorator's internal
|
|
63
|
+
* helper.
|
|
66
64
|
*/
|
|
67
65
|
export declare class InvalidBatchScheduledCronError extends Error {
|
|
68
66
|
readonly cron: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batch-scheduled.d.ts","sourceRoot":"","sources":["../../../src/scheduling/batch-scheduled.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAK1B,eAAO,MAAM,uBAAuB,yBAAgB,CAAC;AAErD
|
|
1
|
+
{"version":3,"file":"batch-scheduled.d.ts","sourceRoot":"","sources":["../../../src/scheduling/batch-scheduled.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAK1B,eAAO,MAAM,uBAAuB,yBAAgB,CAAC;AAErD;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,CAAC;AAE/D;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,OAAO,CAAC,EAAE,IAAI,CAAC;IACf,KAAK,CAAC,EAAE,IAAI,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,qBAAqB,CAAC;IAC/B,KAAK,EAAE,OAAO,CAAC;CAChB;AAmHD;;;;;;;;;GASG;AACH,qBAAa,8BAA+B,SAAQ,KAAK;IACvD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAUzC;AAED;;;;;;;;GAQG;AACH,qBAAa,kCAAmC,SAAQ,KAAK;IAC3D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAS7C;AAMD,wBAAgB,cAAc,CAC5B,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,qBAAqB,GAC7B,eAAe,CAcjB"}
|
|
@@ -34,25 +34,17 @@ const BATCH_SCHEDULED_OPTIONS = _constants.BATCH_SCHEDULED_OPTIONS;
|
|
|
34
34
|
* to `descriptor.value`), so `Reflect.getMetadata(KEY, Job.prototype.run)`
|
|
35
35
|
* returns the stored shape.
|
|
36
36
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
37
|
+
* The decorator validates cron/timezone inputs, stores metadata, and
|
|
38
|
+
* captures inert mode. `process.env.BATCH_SCHEDULED_DISABLE` is read at
|
|
39
|
+
* decoration time and stamped onto the stored shape. The decorator does
|
|
40
|
+
* NOT install any timer, interval, or scheduler registration at decoration
|
|
41
|
+
* time; runtime scheduler adapters honor the metadata when they later
|
|
42
|
+
* walk the `BatchScheduleRegistry`.
|
|
42
43
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* a pure metadata capture — `process.env.BATCH_SCHEDULED_DISABLE` is
|
|
48
|
-
* read at decoration time and stamped onto the stored shape. The
|
|
49
|
-
* decorator does NOT install any timer, interval, or scheduler
|
|
50
|
-
* registration at decoration time; `inert` is a hint the future
|
|
51
|
-
* runtime scheduler honors when it later walks the class.
|
|
52
|
-
*
|
|
53
|
-
* The decorator is metadata-only by design — it does NOT depend on
|
|
54
|
-
* `cron` (the boundary test from Task 2 still passes — no `cron`
|
|
55
|
-
* import appears in core).
|
|
44
|
+
* The decorator is metadata-only by design — it does NOT install
|
|
45
|
+
* timers or import the runtime cron engine. The in-process transport
|
|
46
|
+
* owns that dependency because it is the layer that turns metadata
|
|
47
|
+
* into actual launches.
|
|
56
48
|
*/ // ---------------------------------------------------------------------------
|
|
57
49
|
// Validation helpers
|
|
58
50
|
// ---------------------------------------------------------------------------
|
|
@@ -77,8 +69,8 @@ const CRON_MAX_FIELDS = 6;
|
|
|
77
69
|
* a single whitespace run.
|
|
78
70
|
*
|
|
79
71
|
* This is a shape check, not a semantic one — `99 99 99 99 99` still
|
|
80
|
-
* passes the regex. The
|
|
81
|
-
*
|
|
72
|
+
* passes the regex. The active scheduler adapter is the layer that
|
|
73
|
+
* handles semantic validation with its scheduler backend.
|
|
82
74
|
* The shape check exists so the decorator fails fast on
|
|
83
75
|
* unambiguously-wrong input (empty string, too few / too many
|
|
84
76
|
* fields, literal English words).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/scheduling/batch-scheduled.ts"],"sourcesContent":["import 'reflect-metadata';\nimport { SetMetadata } from '@nestjs/common';\n\nimport { BATCH_SCHEDULED_OPTIONS as SCHEDULED_KEY } from '../decorators/constants';\n\nexport const BATCH_SCHEDULED_OPTIONS = SCHEDULED_KEY;\n\n/**\n * Overlap policies for cron-scheduled jobs:\n *\n * - `'skip'` — drop the new tick if the previous run is still in flight.\n * - `'queue'` — buffer the new tick and start it after the current one ends.\n * - `'parallel'` — start the new tick alongside the current one.\n *\n * The runtime scheduler (the future `@nest-batch/bullmq` cron strategy)\n * reads this value off the stored metadata and applies the policy at\n * dispatch time. The decorator itself MUST NOT silently default the\n * policy to `'skip'` on the user's behalf — leaving it `undefined` here\n * is the contract: the runtime applies the default.\n */\nexport type BatchOverlapPolicy = 'skip' | 'queue' | 'parallel';\n\n/**\n * Decorator-facing options for `@BatchScheduled`.\n *\n * - `name` — required, unique per job (used as the scheduling key).\n * - `timezone` — required, IANA zone (e.g. `'UTC'`, `'Asia/Seoul'`).\n * - `overlap` — optional, see `BatchOverlapPolicy`. Default applied at\n * runtime, never silently here.\n * - `startAt` — optional, absolute lower bound. Preserved by reference.\n * - `endAt` — optional, absolute upper bound. Preserved by reference.\n * - `inert` — optional, hints the runtime scheduler to skip actual\n * registration. The decorator stamps this by reading\n * `process.env.BATCH_SCHEDULED_DISABLE` at decoration time\n * so the runtime never has to re-evaluate the env.\n */\nexport interface BatchScheduledOptions {\n name: string;\n timezone: string;\n overlap?: BatchOverlapPolicy;\n startAt?: Date;\n endAt?: Date;\n inert?: boolean;\n}\n\n/**\n * The shape stored under the `BATCH_SCHEDULED_OPTIONS` metadata key on\n * the decorated method function. The runtime scheduler (the future\n * `@nest-batch/bullmq` cron strategy) reads this verbatim to register\n * the job with the underlying scheduler.\n *\n * Note: `inert` lives at the top level (not inside `options`) on\n * purpose. It is a *runtime* flag captured at decoration time from\n * `process.env.BATCH_SCHEDULED_DISABLE`; it is not a user-facing\n * knob in `BatchScheduledOptions`. (The `inert?: boolean` slot on\n * `BatchScheduledOptions` is the user-facing counterpart — see the\n * GREEN half of Task 13 to wire it up.)\n */\nexport interface BatchScheduledMetadata {\n cron: string;\n options: BatchScheduledOptions;\n inert: boolean;\n}\n\n/**\n * `@BatchScheduled` — cron decorator.\n *\n * Stamps `BATCH_SCHEDULED_OPTIONS` metadata onto the decorated method's\n * function reference (via `@nestjs/common`'s `SetMetadata`, which writes\n * to `descriptor.value`), so `Reflect.getMetadata(KEY, Job.prototype.run)`\n * returns the stored shape.\n *\n * This is the TDD-RED half of Task 13. It deliberately implements only\n * the metadata-storing contract — i.e. the 10 happy-path assertions in\n * `tests/scheduling/batch-scheduled.test.ts` (sections A + B). The\n * 7 negative-path assertions in sections C + D are the GREEN half\n * of the contract and land in the next task:\n *\n * 1. Cron expression shape validation (5/6 fields, no literal words).\n * 2. IANA timezone validation (rejects unknown / empty / whitespace).\n *\n * The inert-mode flag (section E) is wired up here because it is also\n * a pure metadata capture — `process.env.BATCH_SCHEDULED_DISABLE` is\n * read at decoration time and stamped onto the stored shape. The\n * decorator does NOT install any timer, interval, or scheduler\n * registration at decoration time; `inert` is a hint the future\n * runtime scheduler honors when it later walks the class.\n *\n * The decorator is metadata-only by design — it does NOT depend on\n * `cron` (the boundary test from Task 2 still passes — no `cron`\n * import appears in core).\n */\n// ---------------------------------------------------------------------------\n// Validation helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Minimum + maximum number of whitespace-separated fields a valid\n * cron expression can have. `cronstrue` / Linux\n * `crontab(5)` all agree on 5 (minute, hour, day-of-month, month,\n * day-of-week); Quartz-style extensions add a leading seconds field\n * for 6.\n */\nconst CRON_MIN_FIELDS = 5;\nconst CRON_MAX_FIELDS = 6;\n\n/**\n * Cron-shape check.\n *\n * Accepts:\n * - 5 fields: `minute hour dom month dow` (Linux crontab style)\n * - 6 fields: `second minute hour dom month dow` (extended cron style)\n *\n * Each field is `\\S+` (one or more non-whitespace tokens, no empty\n * fields). The trailing `\\S+$` is the final field; the leading\n * `(\\S+\\s+){4,5}` captures the preceding 4 or 5 fields separated by\n * a single whitespace run.\n *\n * This is a shape check, not a semantic one — `99 99 99 99 99` still\n * passes the regex. The runtime scheduler (in `@nest-batch/bullmq`)\n * is the layer that handles semantic validation via `cron-parser`.\n * The shape check exists so the decorator fails fast on\n * unambiguously-wrong input (empty string, too few / too many\n * fields, literal English words).\n */\nconst CRON_SHAPE = /^(\\S+\\s+){4,5}\\S+$/;\n\n/**\n * IANA timezone validation.\n *\n * The platform's `Intl.DateTimeFormat` is the canonical, no-dep\n * source of truth for valid IANA zone identifiers: it throws a\n * `RangeError` for unknown zones. We use it inside a `try/catch` and\n * normalise the result to a boolean so the decorator's failure mode\n * is a deterministic `Error` (not a `RangeError` leaking out).\n *\n * Reference: https://tc39.es/ecma402/#sec-intl-datetimeformat-constructor\n */\nfunction isValidIanaTimezone(tz: string): boolean {\n try {\n new Intl.DateTimeFormat(undefined, { timeZone: tz });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Validate a cron expression. Throws a deterministic\n * `InvalidBatchScheduledCronError` whose `.message` embeds the\n * invalid value verbatim so log lines and test assertions can pin\n * the error to a specific input.\n */\nfunction assertValidCron(cronExpression: string): void {\n // Type guard: a non-string value would have already broken\n // SetMetadata, but checking here keeps the helper standalone.\n if (typeof cronExpression !== 'string' || cronExpression.length === 0) {\n throw new InvalidBatchScheduledCronError(cronExpression, 'empty');\n }\n if (!CRON_SHAPE.test(cronExpression.trim())) {\n const fieldCount = cronExpression.trim().split(/\\s+/).length;\n const reason =\n fieldCount < CRON_MIN_FIELDS\n ? `fewer than ${CRON_MIN_FIELDS} fields`\n : fieldCount > CRON_MAX_FIELDS\n ? `more than ${CRON_MAX_FIELDS} fields`\n : 'not a valid cron expression';\n throw new InvalidBatchScheduledCronError(cronExpression, reason);\n }\n}\n\n/**\n * Validate a timezone string. Throws a deterministic\n * `InvalidBatchScheduledTimezoneError` whose `.message` embeds the\n * invalid value verbatim.\n */\nfunction assertValidTimezone(tz: string): void {\n if (typeof tz !== 'string' || tz.trim().length === 0) {\n throw new InvalidBatchScheduledTimezoneError(tz, 'empty');\n }\n if (!isValidIanaTimezone(tz)) {\n throw new InvalidBatchScheduledTimezoneError(tz, 'not a valid IANA timezone');\n }\n}\n\n/**\n * Thrown by `@BatchScheduled` when the cron expression fails the\n * shape check (5/6 fields, non-whitespace tokens). The `.message`\n * includes the invalid value and the failure reason so the error\n * is greppable in logs and pinned in test assertions.\n *\n * Exported from the module barrel so adapter packages (e.g. the\n * BullMQ runtime) can `instanceof`-check it without reaching into\n * the decorator's internal helper.\n */\nexport class InvalidBatchScheduledCronError extends Error {\n readonly cron: string;\n readonly reason: string;\n\n constructor(cron: string, reason: string) {\n super(\n `[BatchScheduled] invalid cron expression \"${cron}\": ${reason}. ` +\n `Expected ${CRON_MIN_FIELDS} (Linux crontab) or ${CRON_MAX_FIELDS} (extended cron) ` +\n `whitespace-separated fields.`,\n );\n this.name = 'InvalidBatchScheduledCronError';\n this.cron = cron;\n this.reason = reason;\n }\n}\n\n/**\n * Thrown by `@BatchScheduled` when the IANA timezone fails the\n * platform check. The `.message` embeds the invalid value and the\n * failure reason.\n *\n * Exported from the module barrel so adapter packages can\n * `instanceof`-check it without reaching into the decorator's\n * internal helper.\n */\nexport class InvalidBatchScheduledTimezoneError extends Error {\n readonly timezone: string;\n readonly reason: string;\n\n constructor(timezone: string, reason: string) {\n super(\n `[BatchScheduled] invalid timezone \"${timezone}\": ${reason}. ` +\n `Expected a valid IANA zone identifier (e.g. \"UTC\", \"Asia/Seoul\", \"America/New_York\").`,\n );\n this.name = 'InvalidBatchScheduledTimezoneError';\n this.timezone = timezone;\n this.reason = reason;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Decorator\n// ---------------------------------------------------------------------------\n\nexport function BatchScheduled(\n cronExpression: string,\n options: BatchScheduledOptions,\n): MethodDecorator {\n // 1. Validate inputs at decoration time. The decorator is\n // synchronous and metadata-only; the validation runs BEFORE\n // `SetMetadata(...)` is called so a bad input never reaches\n // the registry.\n assertValidCron(cronExpression);\n assertValidTimezone(options.timezone);\n\n const meta: BatchScheduledMetadata = {\n cron: cronExpression,\n options,\n inert: process.env.BATCH_SCHEDULED_DISABLE === '1',\n };\n return SetMetadata(BATCH_SCHEDULED_OPTIONS, meta);\n}\n"],"names":["BATCH_SCHEDULED_OPTIONS","BatchScheduled","InvalidBatchScheduledCronError","InvalidBatchScheduledTimezoneError","SCHEDULED_KEY","CRON_MIN_FIELDS","CRON_MAX_FIELDS","CRON_SHAPE","isValidIanaTimezone","tz","Intl","DateTimeFormat","undefined","timeZone","assertValidCron","cronExpression","length","test","trim","fieldCount","split","reason","assertValidTimezone","Error","cron","name","timezone","options","meta","inert","process","env","BATCH_SCHEDULED_DISABLE","SetMetadata"],"mappings":";;;;;;;;;;;QAKaA;eAAAA;;QA0OGC;eAAAA;;QA5CHC;eAAAA;;QAyBAC;eAAAA;;;QA5NN;wBACqB;2BAE6B;AAElD,MAAMH,0BAA0BI,kCAAa;AA2DpD;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BC,GACD,8EAA8E;AAC9E,qBAAqB;AACrB,8EAA8E;AAE9E;;;;;;CAMC,GACD,MAAMC,kBAAkB;AACxB,MAAMC,kBAAkB;AAExB;;;;;;;;;;;;;;;;;;CAkBC,GACD,MAAMC,aAAa;AAEnB;;;;;;;;;;CAUC,GACD,SAASC,oBAAoBC,EAAU;IACrC,IAAI;QACF,IAAIC,KAAKC,cAAc,CAACC,WAAW;YAAEC,UAAUJ;QAAG;QAClD,OAAO;IACT,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAEA;;;;;CAKC,GACD,SAASK,gBAAgBC,cAAsB;IAC7C,2DAA2D;IAC3D,8DAA8D;IAC9D,IAAI,OAAOA,mBAAmB,YAAYA,eAAeC,MAAM,KAAK,GAAG;QACrE,MAAM,IAAId,+BAA+Ba,gBAAgB;IAC3D;IACA,IAAI,CAACR,WAAWU,IAAI,CAACF,eAAeG,IAAI,KAAK;QAC3C,MAAMC,aAAaJ,eAAeG,IAAI,GAAGE,KAAK,CAAC,OAAOJ,MAAM;QAC5D,MAAMK,SACJF,aAAad,kBACT,CAAC,WAAW,EAAEA,gBAAgB,OAAO,CAAC,GACtCc,aAAab,kBACX,CAAC,UAAU,EAAEA,gBAAgB,OAAO,CAAC,GACrC;QACR,MAAM,IAAIJ,+BAA+Ba,gBAAgBM;IAC3D;AACF;AAEA;;;;CAIC,GACD,SAASC,oBAAoBb,EAAU;IACrC,IAAI,OAAOA,OAAO,YAAYA,GAAGS,IAAI,GAAGF,MAAM,KAAK,GAAG;QACpD,MAAM,IAAIb,mCAAmCM,IAAI;IACnD;IACA,IAAI,CAACD,oBAAoBC,KAAK;QAC5B,MAAM,IAAIN,mCAAmCM,IAAI;IACnD;AACF;AAYO,IAAA,AAAMP,iCAAN,MAAMA,uCAAuCqB;IACzCC,KAAa;IACbH,OAAe;IAExB,YAAYG,IAAY,EAAEH,MAAc,CAAE;QACxC,KAAK,CACH,CAAC,0CAA0C,EAAEG,KAAK,GAAG,EAAEH,OAAO,EAAE,CAAC,GAC/D,CAAC,SAAS,EAAEhB,gBAAgB,oBAAoB,EAAEC,gBAAgB,iBAAiB,CAAC,GACpF,CAAC,4BAA4B,CAAC;QAElC,IAAI,CAACmB,IAAI,GAAG;QACZ,IAAI,CAACD,IAAI,GAAGA;QACZ,IAAI,CAACH,MAAM,GAAGA;IAChB;AACF;AAWO,IAAA,AAAMlB,qCAAN,MAAMA,2CAA2CoB;IAC7CG,SAAiB;IACjBL,OAAe;IAExB,YAAYK,QAAgB,EAAEL,MAAc,CAAE;QAC5C,KAAK,CACH,CAAC,mCAAmC,EAAEK,SAAS,GAAG,EAAEL,OAAO,EAAE,CAAC,GAC5D,CAAC,qFAAqF,CAAC;QAE3F,IAAI,CAACI,IAAI,GAAG;QACZ,IAAI,CAACC,QAAQ,GAAGA;QAChB,IAAI,CAACL,MAAM,GAAGA;IAChB;AACF;AAMO,SAASpB,eACdc,cAAsB,EACtBY,OAA8B;IAE9B,0DAA0D;IAC1D,+DAA+D;IAC/D,+DAA+D;IAC/D,mBAAmB;IACnBb,gBAAgBC;IAChBO,oBAAoBK,QAAQD,QAAQ;IAEpC,MAAME,OAA+B;QACnCJ,MAAMT;QACNY;QACAE,OAAOC,QAAQC,GAAG,CAACC,uBAAuB,KAAK;IACjD;IACA,OAAOC,IAAAA,mBAAW,EAACjC,yBAAyB4B;AAC9C"}
|
|
1
|
+
{"version":3,"sources":["../../../src/scheduling/batch-scheduled.ts"],"sourcesContent":["import 'reflect-metadata';\nimport { SetMetadata } from '@nestjs/common';\n\nimport { BATCH_SCHEDULED_OPTIONS as SCHEDULED_KEY } from '../decorators/constants';\n\nexport const BATCH_SCHEDULED_OPTIONS = SCHEDULED_KEY;\n\n/**\n * Overlap policies for cron-scheduled jobs:\n *\n * - `'skip'` — drop the new tick if the previous run is still in flight.\n * - `'queue'` — buffer the new tick and start it after the current one ends.\n * - `'parallel'` — start the new tick alongside the current one.\n *\n * The active scheduler adapter reads this value off the stored metadata\n * and applies the policy at dispatch time. The decorator itself MUST NOT\n * silently default the policy to `'skip'` on the user's behalf — leaving\n * it `undefined` here is the contract: the runtime applies the default.\n */\nexport type BatchOverlapPolicy = 'skip' | 'queue' | 'parallel';\n\n/**\n * Decorator-facing options for `@BatchScheduled`.\n *\n * - `name` — required, unique per job (used as the scheduling key).\n * - `timezone` — required, IANA zone (e.g. `'UTC'`, `'Asia/Seoul'`).\n * - `overlap` — optional, see `BatchOverlapPolicy`. Default applied at\n * runtime, never silently here.\n * - `startAt` — optional, absolute lower bound. Preserved by reference.\n * - `endAt` — optional, absolute upper bound. Preserved by reference.\n * - `inert` — optional, hints the runtime scheduler to skip actual\n * registration. The decorator stamps this by reading\n * `process.env.BATCH_SCHEDULED_DISABLE` at decoration time\n * so the runtime never has to re-evaluate the env.\n */\nexport interface BatchScheduledOptions {\n name: string;\n timezone: string;\n overlap?: BatchOverlapPolicy;\n startAt?: Date;\n endAt?: Date;\n inert?: boolean;\n}\n\n/**\n * The shape stored under the `BATCH_SCHEDULED_OPTIONS` metadata key on\n * the decorated method function. Scheduler adapters read this verbatim\n * to register the job with the underlying scheduler.\n *\n * Note: `inert` lives at the top level (not inside `options`) on\n * purpose. It is a *runtime* flag captured at decoration time from\n * `process.env.BATCH_SCHEDULED_DISABLE`; it is not a user-facing\n * knob in `BatchScheduledOptions`. (The `inert?: boolean` slot on\n * `BatchScheduledOptions` is the user-facing counterpart — see the\n * GREEN half of Task 13 to wire it up.)\n */\nexport interface BatchScheduledMetadata {\n cron: string;\n options: BatchScheduledOptions;\n inert: boolean;\n}\n\n/**\n * `@BatchScheduled` — cron decorator.\n *\n * Stamps `BATCH_SCHEDULED_OPTIONS` metadata onto the decorated method's\n * function reference (via `@nestjs/common`'s `SetMetadata`, which writes\n * to `descriptor.value`), so `Reflect.getMetadata(KEY, Job.prototype.run)`\n * returns the stored shape.\n *\n * The decorator validates cron/timezone inputs, stores metadata, and\n * captures inert mode. `process.env.BATCH_SCHEDULED_DISABLE` is read at\n * decoration time and stamped onto the stored shape. The decorator does\n * NOT install any timer, interval, or scheduler registration at decoration\n * time; runtime scheduler adapters honor the metadata when they later\n * walk the `BatchScheduleRegistry`.\n *\n * The decorator is metadata-only by design — it does NOT install\n * timers or import the runtime cron engine. The in-process transport\n * owns that dependency because it is the layer that turns metadata\n * into actual launches.\n */\n// ---------------------------------------------------------------------------\n// Validation helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Minimum + maximum number of whitespace-separated fields a valid\n * cron expression can have. `cronstrue` / Linux\n * `crontab(5)` all agree on 5 (minute, hour, day-of-month, month,\n * day-of-week); Quartz-style extensions add a leading seconds field\n * for 6.\n */\nconst CRON_MIN_FIELDS = 5;\nconst CRON_MAX_FIELDS = 6;\n\n/**\n * Cron-shape check.\n *\n * Accepts:\n * - 5 fields: `minute hour dom month dow` (Linux crontab style)\n * - 6 fields: `second minute hour dom month dow` (extended cron style)\n *\n * Each field is `\\S+` (one or more non-whitespace tokens, no empty\n * fields). The trailing `\\S+$` is the final field; the leading\n * `(\\S+\\s+){4,5}` captures the preceding 4 or 5 fields separated by\n * a single whitespace run.\n *\n * This is a shape check, not a semantic one — `99 99 99 99 99` still\n * passes the regex. The active scheduler adapter is the layer that\n * handles semantic validation with its scheduler backend.\n * The shape check exists so the decorator fails fast on\n * unambiguously-wrong input (empty string, too few / too many\n * fields, literal English words).\n */\nconst CRON_SHAPE = /^(\\S+\\s+){4,5}\\S+$/;\n\n/**\n * IANA timezone validation.\n *\n * The platform's `Intl.DateTimeFormat` is the canonical, no-dep\n * source of truth for valid IANA zone identifiers: it throws a\n * `RangeError` for unknown zones. We use it inside a `try/catch` and\n * normalise the result to a boolean so the decorator's failure mode\n * is a deterministic `Error` (not a `RangeError` leaking out).\n *\n * Reference: https://tc39.es/ecma402/#sec-intl-datetimeformat-constructor\n */\nfunction isValidIanaTimezone(tz: string): boolean {\n try {\n new Intl.DateTimeFormat(undefined, { timeZone: tz });\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Validate a cron expression. Throws a deterministic\n * `InvalidBatchScheduledCronError` whose `.message` embeds the\n * invalid value verbatim so log lines and test assertions can pin\n * the error to a specific input.\n */\nfunction assertValidCron(cronExpression: string): void {\n // Type guard: a non-string value would have already broken\n // SetMetadata, but checking here keeps the helper standalone.\n if (typeof cronExpression !== 'string' || cronExpression.length === 0) {\n throw new InvalidBatchScheduledCronError(cronExpression, 'empty');\n }\n if (!CRON_SHAPE.test(cronExpression.trim())) {\n const fieldCount = cronExpression.trim().split(/\\s+/).length;\n const reason =\n fieldCount < CRON_MIN_FIELDS\n ? `fewer than ${CRON_MIN_FIELDS} fields`\n : fieldCount > CRON_MAX_FIELDS\n ? `more than ${CRON_MAX_FIELDS} fields`\n : 'not a valid cron expression';\n throw new InvalidBatchScheduledCronError(cronExpression, reason);\n }\n}\n\n/**\n * Validate a timezone string. Throws a deterministic\n * `InvalidBatchScheduledTimezoneError` whose `.message` embeds the\n * invalid value verbatim.\n */\nfunction assertValidTimezone(tz: string): void {\n if (typeof tz !== 'string' || tz.trim().length === 0) {\n throw new InvalidBatchScheduledTimezoneError(tz, 'empty');\n }\n if (!isValidIanaTimezone(tz)) {\n throw new InvalidBatchScheduledTimezoneError(tz, 'not a valid IANA timezone');\n }\n}\n\n/**\n * Thrown by `@BatchScheduled` when the cron expression fails the\n * shape check (5/6 fields, non-whitespace tokens). The `.message`\n * includes the invalid value and the failure reason so the error\n * is greppable in logs and pinned in test assertions.\n *\n * Exported from the module barrel so adapter packages can\n * `instanceof`-check it without reaching into the decorator's internal\n * helper.\n */\nexport class InvalidBatchScheduledCronError extends Error {\n readonly cron: string;\n readonly reason: string;\n\n constructor(cron: string, reason: string) {\n super(\n `[BatchScheduled] invalid cron expression \"${cron}\": ${reason}. ` +\n `Expected ${CRON_MIN_FIELDS} (Linux crontab) or ${CRON_MAX_FIELDS} (extended cron) ` +\n `whitespace-separated fields.`,\n );\n this.name = 'InvalidBatchScheduledCronError';\n this.cron = cron;\n this.reason = reason;\n }\n}\n\n/**\n * Thrown by `@BatchScheduled` when the IANA timezone fails the\n * platform check. The `.message` embeds the invalid value and the\n * failure reason.\n *\n * Exported from the module barrel so adapter packages can\n * `instanceof`-check it without reaching into the decorator's\n * internal helper.\n */\nexport class InvalidBatchScheduledTimezoneError extends Error {\n readonly timezone: string;\n readonly reason: string;\n\n constructor(timezone: string, reason: string) {\n super(\n `[BatchScheduled] invalid timezone \"${timezone}\": ${reason}. ` +\n `Expected a valid IANA zone identifier (e.g. \"UTC\", \"Asia/Seoul\", \"America/New_York\").`,\n );\n this.name = 'InvalidBatchScheduledTimezoneError';\n this.timezone = timezone;\n this.reason = reason;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Decorator\n// ---------------------------------------------------------------------------\n\nexport function BatchScheduled(\n cronExpression: string,\n options: BatchScheduledOptions,\n): MethodDecorator {\n // 1. Validate inputs at decoration time. The decorator is\n // synchronous and metadata-only; the validation runs BEFORE\n // `SetMetadata(...)` is called so a bad input never reaches\n // the registry.\n assertValidCron(cronExpression);\n assertValidTimezone(options.timezone);\n\n const meta: BatchScheduledMetadata = {\n cron: cronExpression,\n options,\n inert: process.env.BATCH_SCHEDULED_DISABLE === '1',\n };\n return SetMetadata(BATCH_SCHEDULED_OPTIONS, meta);\n}\n"],"names":["BATCH_SCHEDULED_OPTIONS","BatchScheduled","InvalidBatchScheduledCronError","InvalidBatchScheduledTimezoneError","SCHEDULED_KEY","CRON_MIN_FIELDS","CRON_MAX_FIELDS","CRON_SHAPE","isValidIanaTimezone","tz","Intl","DateTimeFormat","undefined","timeZone","assertValidCron","cronExpression","length","test","trim","fieldCount","split","reason","assertValidTimezone","Error","cron","name","timezone","options","meta","inert","process","env","BATCH_SCHEDULED_DISABLE","SetMetadata"],"mappings":";;;;;;;;;;;QAKaA;eAAAA;;QAgOGC;eAAAA;;QA5CHC;eAAAA;;QAyBAC;eAAAA;;;QAlNN;wBACqB;2BAE6B;AAElD,MAAMH,0BAA0BI,kCAAa;AAyDpD;;;;;;;;;;;;;;;;;;;CAmBC,GACD,8EAA8E;AAC9E,qBAAqB;AACrB,8EAA8E;AAE9E;;;;;;CAMC,GACD,MAAMC,kBAAkB;AACxB,MAAMC,kBAAkB;AAExB;;;;;;;;;;;;;;;;;;CAkBC,GACD,MAAMC,aAAa;AAEnB;;;;;;;;;;CAUC,GACD,SAASC,oBAAoBC,EAAU;IACrC,IAAI;QACF,IAAIC,KAAKC,cAAc,CAACC,WAAW;YAAEC,UAAUJ;QAAG;QAClD,OAAO;IACT,EAAE,OAAM;QACN,OAAO;IACT;AACF;AAEA;;;;;CAKC,GACD,SAASK,gBAAgBC,cAAsB;IAC7C,2DAA2D;IAC3D,8DAA8D;IAC9D,IAAI,OAAOA,mBAAmB,YAAYA,eAAeC,MAAM,KAAK,GAAG;QACrE,MAAM,IAAId,+BAA+Ba,gBAAgB;IAC3D;IACA,IAAI,CAACR,WAAWU,IAAI,CAACF,eAAeG,IAAI,KAAK;QAC3C,MAAMC,aAAaJ,eAAeG,IAAI,GAAGE,KAAK,CAAC,OAAOJ,MAAM;QAC5D,MAAMK,SACJF,aAAad,kBACT,CAAC,WAAW,EAAEA,gBAAgB,OAAO,CAAC,GACtCc,aAAab,kBACX,CAAC,UAAU,EAAEA,gBAAgB,OAAO,CAAC,GACrC;QACR,MAAM,IAAIJ,+BAA+Ba,gBAAgBM;IAC3D;AACF;AAEA;;;;CAIC,GACD,SAASC,oBAAoBb,EAAU;IACrC,IAAI,OAAOA,OAAO,YAAYA,GAAGS,IAAI,GAAGF,MAAM,KAAK,GAAG;QACpD,MAAM,IAAIb,mCAAmCM,IAAI;IACnD;IACA,IAAI,CAACD,oBAAoBC,KAAK;QAC5B,MAAM,IAAIN,mCAAmCM,IAAI;IACnD;AACF;AAYO,IAAA,AAAMP,iCAAN,MAAMA,uCAAuCqB;IACzCC,KAAa;IACbH,OAAe;IAExB,YAAYG,IAAY,EAAEH,MAAc,CAAE;QACxC,KAAK,CACH,CAAC,0CAA0C,EAAEG,KAAK,GAAG,EAAEH,OAAO,EAAE,CAAC,GAC/D,CAAC,SAAS,EAAEhB,gBAAgB,oBAAoB,EAAEC,gBAAgB,iBAAiB,CAAC,GACpF,CAAC,4BAA4B,CAAC;QAElC,IAAI,CAACmB,IAAI,GAAG;QACZ,IAAI,CAACD,IAAI,GAAGA;QACZ,IAAI,CAACH,MAAM,GAAGA;IAChB;AACF;AAWO,IAAA,AAAMlB,qCAAN,MAAMA,2CAA2CoB;IAC7CG,SAAiB;IACjBL,OAAe;IAExB,YAAYK,QAAgB,EAAEL,MAAc,CAAE;QAC5C,KAAK,CACH,CAAC,mCAAmC,EAAEK,SAAS,GAAG,EAAEL,OAAO,EAAE,CAAC,GAC5D,CAAC,qFAAqF,CAAC;QAE3F,IAAI,CAACI,IAAI,GAAG;QACZ,IAAI,CAACC,QAAQ,GAAGA;QAChB,IAAI,CAACL,MAAM,GAAGA;IAChB;AACF;AAMO,SAASpB,eACdc,cAAsB,EACtBY,OAA8B;IAE9B,0DAA0D;IAC1D,+DAA+D;IAC/D,+DAA+D;IAC/D,mBAAmB;IACnBb,gBAAgBC;IAChBO,oBAAoBK,QAAQD,QAAQ;IAEpC,MAAME,OAA+B;QACnCJ,MAAMT;QACNY;QACAE,OAAOC,QAAQC,GAAG,CAACC,uBAAuB,KAAK;IACjD;IACA,OAAOC,IAAAA,mBAAW,EAACjC,yBAAyB4B;AAC9C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nest-batch/core",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Batch processing engine for NestJS — jobs, steps, chunk-oriented processing, and the persistence/transport contracts the @nest-batch adapters implement.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "easdkr",
|
|
@@ -70,6 +70,9 @@
|
|
|
70
70
|
"typescript": "^5.5.0",
|
|
71
71
|
"vitest": "^2.0.0"
|
|
72
72
|
},
|
|
73
|
+
"dependencies": {
|
|
74
|
+
"cron": "^4.4.0"
|
|
75
|
+
},
|
|
73
76
|
"scripts": {
|
|
74
77
|
"build": "swc src -d dist --config-file ../../.swcrc && swc tests/contracts/index.ts -o dist/tests/contracts/index.js --config-file ../../.swcrc && swc tests/contracts/job-repository.contract.ts -o dist/tests/contracts/job-repository.contract.js --config-file ../../.swcrc && tsc --emitDeclarationOnly -p tsconfig.build.json",
|
|
75
78
|
"test": "vitest run",
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
IN_PROCESS_EXECUTION_STRATEGY_PROVIDER,
|
|
7
7
|
InProcessExecutionStrategy,
|
|
8
8
|
} from '../execution/in-process-execution-strategy';
|
|
9
|
+
import { InProcessSchedule } from '../execution/in-process-schedule';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Empty Nest module class that owns the in-process execution-strategy
|
|
@@ -85,6 +86,16 @@ export class InProcessModule {}
|
|
|
85
86
|
* some "config" into it, you almost certainly want a real transport
|
|
86
87
|
* adapter instead.
|
|
87
88
|
*
|
|
89
|
+
* ## Scheduling
|
|
90
|
+
*
|
|
91
|
+
* `InProcessSchedule` is registered as a transport global provider.
|
|
92
|
+
* It consumes `BatchScheduleRegistry` after discovery/bootstrap and
|
|
93
|
+
* turns non-inert `@BatchScheduled` cron ticks into
|
|
94
|
+
* `JobLauncher.launch(...)` calls in this same process. That gives
|
|
95
|
+
* single-process apps a real cron path without Redis or another
|
|
96
|
+
* queue. It is intentionally not a distributed lock: multiple app
|
|
97
|
+
* replicas will each run their own timer.
|
|
98
|
+
*
|
|
88
99
|
* ## DI scope
|
|
89
100
|
*
|
|
90
101
|
* The module is `global: true` and exports both the strategy class
|
|
@@ -103,23 +114,16 @@ export class InProcessModule {}
|
|
|
103
114
|
* pattern uniform across the engine and its adapters — the host
|
|
104
115
|
* author only needs to learn one module-visibility model.
|
|
105
116
|
*
|
|
106
|
-
* ## Why `globalProviders` is
|
|
117
|
+
* ## Why `globalProviders` is used
|
|
107
118
|
*
|
|
108
119
|
* The `BatchAdapter` interface allows a `globalProviders` field for
|
|
109
120
|
* runtime classes (e.g. `JobExecutor`, `InProcessExecutionStrategy`)
|
|
110
121
|
* that the adapter's *own* module needs to inject but that the host
|
|
111
|
-
* should also be able to inject.
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
* works without the core module having to know which adapter is
|
|
117
|
-
* active.
|
|
118
|
-
*
|
|
119
|
-
* If a future in-process feature needs to expose a new provider to
|
|
120
|
-
* the host (e.g. an inline scheduler), prefer adding it to
|
|
121
|
-
* `exports` and updating the `BatchAdapter.globalProviders` decision
|
|
122
|
-
* — do not push it onto the host app's `providers` array.
|
|
122
|
+
* should also be able to inject. This adapter lists
|
|
123
|
+
* `InProcessExecutionStrategy`, `InProcessSchedule`, and the
|
|
124
|
+
* `EXECUTION_STRATEGY` binding there so `NestBatchModule` can merge
|
|
125
|
+
* them into the same provider graph as `JobLauncher`,
|
|
126
|
+
* `BatchScheduleRegistry`, and the executor subgraph.
|
|
123
127
|
*
|
|
124
128
|
* ## Concurrency
|
|
125
129
|
*
|
|
@@ -150,6 +154,7 @@ export class InProcessAdapter {
|
|
|
150
154
|
module: buildInProcessDynamicModule(),
|
|
151
155
|
globalProviders: [
|
|
152
156
|
InProcessExecutionStrategy,
|
|
157
|
+
InProcessSchedule,
|
|
153
158
|
IN_PROCESS_EXECUTION_STRATEGY_PROVIDER,
|
|
154
159
|
],
|
|
155
160
|
};
|