@pattern-stack/codegen 0.14.0 → 0.14.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -0
- package/dist/runtime/subsystems/bridge/bridge-delivery.memory-backend.d.ts +1 -1
- package/dist/runtime/subsystems/bridge/bridge-delivery.memory-backend.js +2 -2
- package/dist/runtime/subsystems/bridge/bridge-delivery.memory-backend.js.map +1 -1
- package/dist/runtime/subsystems/bridge/bridge.module.d.ts +22 -0
- package/dist/runtime/subsystems/bridge/bridge.module.js +177 -160
- package/dist/runtime/subsystems/bridge/bridge.module.js.map +1 -1
- package/dist/runtime/subsystems/bridge/index.js +159 -142
- package/dist/runtime/subsystems/bridge/index.js.map +1 -1
- package/dist/runtime/subsystems/index.js +161 -148
- package/dist/runtime/subsystems/index.js.map +1 -1
- package/dist/runtime/subsystems/jobs/index.js +128 -115
- package/dist/runtime/subsystems/jobs/index.js.map +1 -1
- package/dist/runtime/subsystems/jobs/job-orchestrator.memory-backend.js +128 -6
- package/dist/runtime/subsystems/jobs/job-orchestrator.memory-backend.js.map +1 -1
- package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.js +17 -0
- package/dist/runtime/subsystems/jobs/job-run-service.memory-backend.js.map +1 -1
- package/dist/runtime/subsystems/jobs/job-step-service.memory-backend.js +25 -2
- package/dist/runtime/subsystems/jobs/job-step-service.memory-backend.js.map +1 -1
- package/dist/runtime/subsystems/jobs/job-worker.module.d.ts +26 -1
- package/dist/runtime/subsystems/jobs/job-worker.module.js +150 -137
- package/dist/runtime/subsystems/jobs/job-worker.module.js.map +1 -1
- package/dist/runtime/subsystems/jobs/jobs-domain.module.js +133 -124
- package/dist/runtime/subsystems/jobs/jobs-domain.module.js.map +1 -1
- package/dist/src/cli/index.js +1040 -635
- package/dist/src/cli/index.js.map +1 -1
- package/package.json +1 -1
- package/runtime/subsystems/bridge/bridge-delivery.memory-backend.ts +8 -1
- package/runtime/subsystems/bridge/bridge.module.ts +26 -1
- package/runtime/subsystems/jobs/job-orchestrator.memory-backend.ts +8 -3
- package/runtime/subsystems/jobs/job-run-service.memory-backend.ts +4 -1
- package/runtime/subsystems/jobs/job-step-service.memory-backend.ts +7 -2
- package/runtime/subsystems/jobs/job-worker.module.ts +13 -1
package/package.json
CHANGED
|
@@ -116,12 +116,19 @@ export class MemoryBridgeDeliveryRepo implements IJobBridge {
|
|
|
116
116
|
async getStatusHistogram(
|
|
117
117
|
windowHours: number,
|
|
118
118
|
tenantId?: string | null,
|
|
119
|
+
// Reference instant for the window cutoff. Defaults to the wall clock; exposed
|
|
120
|
+
// as a test-injection seam so a caller can pin the cutoff to the same `now` it
|
|
121
|
+
// positioned a boundary row from. Without it the cutoff is re-sampled here a few
|
|
122
|
+
// ms later, pushing an "exactly at the boundary" row just below the window
|
|
123
|
+
// (the OBS-3 boundary test flake). The drizzle backend has no analogue — it
|
|
124
|
+
// evaluates the cutoff once in SQL via `now()`.
|
|
125
|
+
nowMs: number = Date.now(),
|
|
119
126
|
): Promise<StatusHistogram> {
|
|
120
127
|
if (!Number.isFinite(windowHours) || windowHours <= 0) {
|
|
121
128
|
throw new RangeError('windowHours must be positive');
|
|
122
129
|
}
|
|
123
130
|
|
|
124
|
-
const cutoffMs =
|
|
131
|
+
const cutoffMs = nowMs - windowHours * 3_600_000;
|
|
125
132
|
const histogram: StatusHistogram = {
|
|
126
133
|
pending: 0,
|
|
127
134
|
delivered: 0,
|
|
@@ -70,6 +70,7 @@ import { BridgeOutboxDrainHook } from './bridge-outbox-drain-hook';
|
|
|
70
70
|
import { EventFlowService } from './event-flow.service';
|
|
71
71
|
import { BridgeDeliveryHandler } from './bridge-delivery-handler';
|
|
72
72
|
import { bridgeRegistry } from './generated/registry';
|
|
73
|
+
import type { BridgeRegistry } from './bridge.protocol';
|
|
73
74
|
import { BRIDGE_RESERVED_POOLS } from './reserved-pools';
|
|
74
75
|
|
|
75
76
|
export interface BridgeModuleOptions {
|
|
@@ -87,6 +88,26 @@ export interface BridgeModuleOptions {
|
|
|
87
88
|
* `null` always passes (cross-tenant work). Defaults to `false`.
|
|
88
89
|
*/
|
|
89
90
|
multiTenant?: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* The codegen-emitted `Record<EventTypeName, BridgeTriggerEntry[]>` that
|
|
93
|
+
* drives outbox-drain trigger lookup (BRIDGE-4) and the facade's Case B
|
|
94
|
+
* dedup (BRIDGE-7).
|
|
95
|
+
*
|
|
96
|
+
* **Package mode (ADR-037).** When the runtime is imported from
|
|
97
|
+
* `@pattern-stack/codegen` (not vendored), the bundled
|
|
98
|
+
* `./generated/registry` is a frozen empty placeholder (`{}`) — a
|
|
99
|
+
* consumer's `@JobHandler.triggers` are scanned into a registry that lives
|
|
100
|
+
* in THEIR `src/generated/bridge-registry.ts`, which the package can't
|
|
101
|
+
* import. The generated subsystem barrel therefore threads that registry in
|
|
102
|
+
* here: `BridgeModule.forRoot({ ..., registry: bridgeRegistry })`. Omitted
|
|
103
|
+
* (vendored mode / tests) ⇒ falls back to the bundled `./generated/registry`,
|
|
104
|
+
* which in vendored mode IS the consumer's freshly-generated file.
|
|
105
|
+
*
|
|
106
|
+
* Without this, package-mode consumers' triggers never bind and the bridge
|
|
107
|
+
* routes nothing (the "wrappers sit pending" footgun's silent twin —
|
|
108
|
+
* nothing is ever enqueued in the first place).
|
|
109
|
+
*/
|
|
110
|
+
registry?: BridgeRegistry;
|
|
90
111
|
}
|
|
91
112
|
|
|
92
113
|
@Module({})
|
|
@@ -108,7 +129,11 @@ export class BridgeModule implements OnModuleInit {
|
|
|
108
129
|
providers: [
|
|
109
130
|
{ provide: BRIDGE_MODULE_OPTIONS, useValue: opts },
|
|
110
131
|
{ provide: BRIDGE_MULTI_TENANT, useValue: opts.multiTenant ?? false },
|
|
111
|
-
|
|
132
|
+
// Package mode threads the consumer's generated registry through
|
|
133
|
+
// `opts.registry`; vendored mode omits it and we fall back to the
|
|
134
|
+
// bundled `./generated/registry` (which IS the consumer's generated
|
|
135
|
+
// file in a vendored tree). See `BridgeModuleOptions.registry`.
|
|
136
|
+
{ provide: BRIDGE_REGISTRY, useValue: opts.registry ?? bridgeRegistry },
|
|
112
137
|
repoProvider,
|
|
113
138
|
// Drain hook — always wired; `DrizzleEventBus` consumes it via
|
|
114
139
|
// `@Optional()`, so non-bridge mounts simply see `undefined`.
|
|
@@ -137,10 +137,15 @@ export class MemoryJobOrchestrator implements IJobOrchestrator {
|
|
|
137
137
|
private readonly queueBlockers = new Map<string, string[]>();
|
|
138
138
|
|
|
139
139
|
constructor(
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
// ADR-037 (package-mode DI): explicit `@Inject` tokens on every param —
|
|
141
|
+
// the published bundle has no `design:paramtypes` metadata (built without
|
|
142
|
+
// `emitDecoratorMetadata`), so by-type injection would resolve to
|
|
143
|
+
// `undefined` in package mode. Class tokens (`MemoryJobStore`,
|
|
144
|
+
// `MemoryJobStepService`, `ModuleRef`) are passed to `@Inject` explicitly.
|
|
145
|
+
@Inject(MemoryJobStore) private readonly store: MemoryJobStore,
|
|
146
|
+
@Inject(MemoryJobStepService) private readonly stepService: MemoryJobStepService,
|
|
142
147
|
@Inject(JOBS_MULTI_TENANT) private readonly multiTenant: boolean,
|
|
143
|
-
@Optional() private readonly moduleRef?: ModuleRef,
|
|
148
|
+
@Optional() @Inject(ModuleRef) private readonly moduleRef?: ModuleRef,
|
|
144
149
|
) {}
|
|
145
150
|
|
|
146
151
|
/**
|
|
@@ -39,7 +39,10 @@ const NON_TERMINAL_STATUSES: JobRunRow['status'][] = [
|
|
|
39
39
|
@Injectable()
|
|
40
40
|
export class MemoryJobRunService implements IJobRunService {
|
|
41
41
|
constructor(
|
|
42
|
-
|
|
42
|
+
// ADR-037 (package-mode DI): explicit `@Inject(MemoryJobStore)` — the
|
|
43
|
+
// published bundle carries no `design:paramtypes`, so a by-type inject
|
|
44
|
+
// would resolve to `undefined` in package mode.
|
|
45
|
+
@Inject(MemoryJobStore) private readonly store: MemoryJobStore,
|
|
43
46
|
@Inject(JOB_ORCHESTRATOR) private readonly orchestrator: IJobOrchestrator,
|
|
44
47
|
@Inject(JOBS_MULTI_TENANT) private readonly multiTenant: boolean,
|
|
45
48
|
) {}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* the Drizzle backend (which deletes non-completed rows on replay).
|
|
9
9
|
*/
|
|
10
10
|
import { randomUUID } from 'node:crypto';
|
|
11
|
-
import { Injectable } from '@nestjs/common';
|
|
11
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
12
12
|
import type { JobStepRow } from './job-orchestration.schema';
|
|
13
13
|
import type {
|
|
14
14
|
IJobStepService,
|
|
@@ -19,7 +19,12 @@ import { MemoryJobStore } from './memory-job-store';
|
|
|
19
19
|
|
|
20
20
|
@Injectable()
|
|
21
21
|
export class MemoryJobStepService implements IJobStepService {
|
|
22
|
-
|
|
22
|
+
// ADR-037 (package-mode DI): explicit `@Inject(MemoryJobStore)` — the
|
|
23
|
+
// published bundle carries no `design:paramtypes`, so a by-type inject
|
|
24
|
+
// would resolve to `undefined` in package mode.
|
|
25
|
+
constructor(
|
|
26
|
+
@Inject(MemoryJobStore) private readonly store: MemoryJobStore,
|
|
27
|
+
) {}
|
|
23
28
|
|
|
24
29
|
async findStep(runId: string, stepId: string): Promise<JobStep | null> {
|
|
25
30
|
const rows = this.store.steps.get(runId);
|
|
@@ -158,7 +158,19 @@ export class JobWorkerOrchestrator implements OnModuleInit, OnModuleDestroy {
|
|
|
158
158
|
* without supplying a `DRIZZLE` provider.
|
|
159
159
|
*/
|
|
160
160
|
@Optional() @Inject(DRIZZLE) private readonly db: DrizzleClient | null = null,
|
|
161
|
-
|
|
161
|
+
/**
|
|
162
|
+
* ADR-037 (package-mode DI): inject `ModuleRef` EXPLICITLY via `@Inject`
|
|
163
|
+
* rather than relying on `design:paramtypes` reflection. The published
|
|
164
|
+
* package bundle is built without `emitDecoratorMetadata` (tsup/esbuild
|
|
165
|
+
* default), so a by-type injection here would resolve to `undefined` at
|
|
166
|
+
* boot in package mode — breaking the worker entirely (the
|
|
167
|
+
* `ModuleRef not available` throw). Vendored mode happened to work only
|
|
168
|
+
* because the consumer's own `tsc` (emitDecoratorMetadata: true)
|
|
169
|
+
* recompiled the source and emitted the metadata. The explicit token is
|
|
170
|
+
* mode-agnostic. `ModuleRef` is always provided by `@nestjs/core`, so no
|
|
171
|
+
* `@Optional()` is needed (it's a hard dependency of the worker path).
|
|
172
|
+
*/
|
|
173
|
+
@Inject(ModuleRef) private readonly moduleRef?: ModuleRef,
|
|
162
174
|
/**
|
|
163
175
|
* BULLMQ-1 — resolved BullMQ connection + config, only bound when the
|
|
164
176
|
* inner `JobsDomainModule` was booted with `backend: 'bullmq'`. `@Optional()`
|