@pattern-stack/codegen 0.24.0 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/dist/{chunk-2VHZ7EKC.js → chunk-5AAA4LTE.js} +2 -2
- package/dist/{chunk-AS3NAZB6.js → chunk-B7SC2V45.js} +2 -2
- package/dist/{chunk-W72PRNJY.js → chunk-BPYZCEHS.js} +2 -2
- package/dist/{chunk-KYR3B3OW.js → chunk-DAHWN63L.js} +26 -5
- package/dist/chunk-DAHWN63L.js.map +1 -0
- package/dist/{chunk-RFH7N6EP.js → chunk-FCPTHS42.js} +2 -2
- package/dist/{chunk-JYBFPNBJ.js → chunk-SJGEBMJT.js} +2 -2
- package/dist/runtime/base-classes/activity-entity-service.js +3 -3
- package/dist/runtime/base-classes/base-service.js +2 -2
- package/dist/runtime/base-classes/index.js +26 -26
- package/dist/runtime/base-classes/integrated-entity-service.js +3 -3
- package/dist/runtime/base-classes/knowledge-entity-service.js +3 -3
- package/dist/runtime/base-classes/lifecycle-events.js +1 -1
- package/dist/runtime/base-classes/metadata-entity-service.js +3 -3
- package/dist/runtime/subsystems/index.js +1 -1
- package/dist/src/cli/index.js +97 -27
- package/dist/src/cli/index.js.map +1 -1
- package/package.json +1 -1
- package/runtime/base-classes/lifecycle-events.ts +39 -6
- package/templates/subsystem/jobs/job-orchestration.schema.ejs.t +1 -0
- package/templates/subsystem/jobs/main-hook.ejs.t +1 -1
- package/templates/subsystem/jobs/prompt.js +40 -2
- package/templates/subsystem/jobs/worker.ejs.t +47 -35
- package/dist/chunk-KYR3B3OW.js.map +0 -1
- /package/dist/{chunk-2VHZ7EKC.js.map → chunk-5AAA4LTE.js.map} +0 -0
- /package/dist/{chunk-AS3NAZB6.js.map → chunk-B7SC2V45.js.map} +0 -0
- /package/dist/{chunk-W72PRNJY.js.map → chunk-BPYZCEHS.js.map} +0 -0
- /package/dist/{chunk-RFH7N6EP.js.map → chunk-FCPTHS42.js.map} +0 -0
- /package/dist/{chunk-JYBFPNBJ.js.map → chunk-SJGEBMJT.js.map} +0 -0
package/package.json
CHANGED
|
@@ -23,8 +23,17 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import { randomUUID } from 'crypto';
|
|
26
|
+
import { Logger } from '@nestjs/common';
|
|
26
27
|
import type { IEventBus, DomainEvent } from '../subsystems/events/event-bus.protocol';
|
|
27
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Module-level logger for fire-and-forget emission failures. Routed through the
|
|
31
|
+
* Nest `Logger` (not bare `console`) so consumers configuring `app.useLogger`
|
|
32
|
+
* or the factory `logger:` option can format and filter it like any other
|
|
33
|
+
* framework log line.
|
|
34
|
+
*/
|
|
35
|
+
const logger = new Logger('LifecycleEvents');
|
|
36
|
+
|
|
28
37
|
// ============================================================================
|
|
29
38
|
// Event categories (subset of pattern-stack's EventCategory)
|
|
30
39
|
// ============================================================================
|
|
@@ -99,7 +108,17 @@ export function buildLifecycleEvent(
|
|
|
99
108
|
aggregateType: entityName,
|
|
100
109
|
payload: snapshot ? { snapshot } : {},
|
|
101
110
|
occurredAt: new Date(),
|
|
102
|
-
|
|
111
|
+
// AUDIT tier: lifecycle/change events are untyped audit-trail records —
|
|
112
|
+
// never bridge-routed, no pool/direction. The `domain_events`
|
|
113
|
+
// `domain_events_tier_routing_check` CHECK requires `tier='audit' ⇔
|
|
114
|
+
// (pool IS NULL AND direction IS NULL)`; the DEFAULT `tier='domain'`
|
|
115
|
+
// (applied by toInsertValues when absent) requires non-null routing
|
|
116
|
+
// fields, so an un-tiered lifecycle row violates the constraint and the
|
|
117
|
+
// INSERT is rejected — silently, pre-fix, by emitSafely's catch. Stamp
|
|
118
|
+
// `tier:'audit'` so these rows land (and surface under the
|
|
119
|
+
// observability viewer's audit-tier toggle). The bridge guard keeps
|
|
120
|
+
// audit-tier events out of job routing.
|
|
121
|
+
metadata: { category: 'lifecycle' as EventCategory, tier: 'audit' },
|
|
103
122
|
};
|
|
104
123
|
}
|
|
105
124
|
|
|
@@ -119,7 +138,9 @@ export function buildChangeEvents(
|
|
|
119
138
|
newValue: c.newValue,
|
|
120
139
|
},
|
|
121
140
|
occurredAt: new Date(),
|
|
122
|
-
|
|
141
|
+
// AUDIT tier — see buildLifecycleEvent. Change events are audit-trail
|
|
142
|
+
// records; tier:'audit' satisfies the tier-routing CHECK constraint.
|
|
143
|
+
metadata: { category: 'change' as EventCategory, tier: 'audit' },
|
|
123
144
|
}));
|
|
124
145
|
}
|
|
125
146
|
|
|
@@ -144,9 +165,21 @@ export async function emitSafely(
|
|
|
144
165
|
} else {
|
|
145
166
|
await eventBus.publishMany(events);
|
|
146
167
|
}
|
|
147
|
-
} catch {
|
|
148
|
-
//
|
|
149
|
-
//
|
|
150
|
-
|
|
168
|
+
} catch (err) {
|
|
169
|
+
// Never fail the CRUD operation — but surface the cause. The bare
|
|
170
|
+
// `catch` that used to live here swallowed the error entirely, so a
|
|
171
|
+
// failing bus printed `failed to emit N event(s)` with zero
|
|
172
|
+
// diagnosability. Route through the Nest Logger (not bare console) at
|
|
173
|
+
// warn level, including the distinct event types and the error message;
|
|
174
|
+
// the stack follows at debug so it's available without noising the
|
|
175
|
+
// default-threshold output.
|
|
176
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
177
|
+
const types = [...new Set(events.map((e) => e.type))].join(', ');
|
|
178
|
+
logger.warn(
|
|
179
|
+
`failed to emit ${events.length} event(s) [${types}]: ${message}`,
|
|
180
|
+
);
|
|
181
|
+
if (err instanceof Error && err.stack) {
|
|
182
|
+
logger.debug(err.stack);
|
|
183
|
+
}
|
|
151
184
|
}
|
|
152
185
|
}
|
|
@@ -7,5 +7,5 @@ skip_if: "<%= mainHookInjected %>"
|
|
|
7
7
|
// JOBS — Embedded worker mode (optional)
|
|
8
8
|
// To run the job worker in-process (single-process deploy), add to AppModule imports:
|
|
9
9
|
// JobWorkerModule.forRoot({ mode: 'embedded' })
|
|
10
|
-
// For standalone worker (separate process),
|
|
10
|
+
// For standalone worker (separate process), run src/worker.ts (bun src/worker.ts).
|
|
11
11
|
// See codegen.config.yaml jobs.worker_mode to toggle the documented default.
|
|
@@ -9,9 +9,10 @@
|
|
|
9
9
|
* Invoked via:
|
|
10
10
|
* bunx hygen subsystem jobs \
|
|
11
11
|
* --workerPath <abs> --workerExists <'true'|''> \
|
|
12
|
+
* --jobWorkerModuleImport <specifier> --workerForRootOpts <ts-literal> \
|
|
12
13
|
* --mainTsPath <abs> --configPath <abs> --schemaPath <abs> \
|
|
13
14
|
* --multiTenant <'true'|'false'> --workerMode <embedded|standalone> \
|
|
14
|
-
* --appName <string>
|
|
15
|
+
* --skipSchema <'true'|''> --appName <string>
|
|
15
16
|
*/
|
|
16
17
|
|
|
17
18
|
import { renderGeneratedBanner } from "../../_shared/generated-banner.mjs";
|
|
@@ -23,6 +24,28 @@ function coerceBool(raw) {
|
|
|
23
24
|
return false;
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
// #513: the CLI base64-encodes --workerForRootOpts because Hygen's yargs parser
|
|
28
|
+
// mangles a raw `{ mode: 'standalone', … }` TS literal (the braces/colons are
|
|
29
|
+
// read as nested object syntax). Decode it back to the source string here. A
|
|
30
|
+
// direct hygen invocation that passes a non-encoded value (or omits it) falls
|
|
31
|
+
// back to the plain default below.
|
|
32
|
+
function decodeWorkerForRootOpts(raw) {
|
|
33
|
+
if (typeof raw !== "string" || raw.length === 0) {
|
|
34
|
+
return "{ mode: 'standalone', allPools: true }";
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const decoded = Buffer.from(raw, "base64").toString("utf-8");
|
|
38
|
+
// Re-encoding round-trips iff `raw` was valid base64 of the decoded bytes;
|
|
39
|
+
// guards against a hand-passed plain literal being treated as base64.
|
|
40
|
+
if (Buffer.from(decoded, "utf-8").toString("base64") === raw) {
|
|
41
|
+
return decoded;
|
|
42
|
+
}
|
|
43
|
+
} catch {
|
|
44
|
+
/* fall through to raw */
|
|
45
|
+
}
|
|
46
|
+
return raw;
|
|
47
|
+
}
|
|
48
|
+
|
|
26
49
|
export default {
|
|
27
50
|
prompt: async ({ args }) => {
|
|
28
51
|
return {
|
|
@@ -34,9 +57,24 @@ export default {
|
|
|
34
57
|
// Hygen's skip_if treats any non-empty string as truthy, so we send an
|
|
35
58
|
// empty string when the file doesn't exist (CLI already does this).
|
|
36
59
|
workerExists: args.workerExists ?? "",
|
|
37
|
-
|
|
60
|
+
// #513: the worker lands at `src/worker.ts` (inside the default tsconfig
|
|
61
|
+
// include, next to `app.module.ts`); the CLI always passes an absolute
|
|
62
|
+
// --workerPath, this fallback only guards a direct hygen invocation.
|
|
63
|
+
workerPath: args.workerPath ?? "src/worker.ts",
|
|
64
|
+
// #513: mode-aware JobWorkerModule import + the pre-serialised
|
|
65
|
+
// forRoot(<opts>) literal (the only mode-dependent import the worker
|
|
66
|
+
// carries — AppModule is imported relatively).
|
|
67
|
+
jobWorkerModuleImport:
|
|
68
|
+
args.jobWorkerModuleImport ??
|
|
69
|
+
"@pattern-stack/codegen/runtime/subsystems/jobs/index",
|
|
70
|
+
workerForRootOpts: decodeWorkerForRootOpts(args.workerForRootOpts),
|
|
38
71
|
schemaPath:
|
|
39
72
|
args.schemaPath ?? "shared/subsystems/jobs/job-orchestration.schema.ts",
|
|
73
|
+
// #517: package mode skips the schema template (the schema ships in the
|
|
74
|
+
// package, re-exported via the schema barrel). Hygen's skip_if treats any
|
|
75
|
+
// non-empty string as truthy, so the CLI sends '' in vendored mode and
|
|
76
|
+
// 'true' in package mode. Default '' so a direct hygen invocation renders.
|
|
77
|
+
skipSchema: args.skipSchema ?? "",
|
|
40
78
|
// @generated DO-NOT-EDIT banner — the jobs subsystem schema is
|
|
41
79
|
// force-overwritten on every `subsystem install`.
|
|
42
80
|
generatedBanner: renderGeneratedBanner({
|
|
@@ -5,31 +5,42 @@ unless_exists: true
|
|
|
5
5
|
/**
|
|
6
6
|
* Standalone job worker entrypoint — emitted by `codegen subsystem install jobs`.
|
|
7
7
|
*
|
|
8
|
-
* Boots a Nest application context (NO HTTP listener)
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* allPools: true })`. Run with:
|
|
8
|
+
* Boots a Nest application context (NO HTTP listener) that composes the
|
|
9
|
+
* consumer's root `AppModule` plus `JobWorkerModule.forRoot({ mode:
|
|
10
|
+
* 'standalone', allPools: true, … })`. Run with:
|
|
12
11
|
*
|
|
13
|
-
* bun worker.ts
|
|
12
|
+
* bun src/worker.ts
|
|
14
13
|
*
|
|
15
|
-
* Why
|
|
16
|
-
* -
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
14
|
+
* Why import `AppModule` whole:
|
|
15
|
+
* - Job handlers are Nest providers registered by the consumer's handler
|
|
16
|
+
* modules (and the subsystem barrel). Composing around `AppModule` gives
|
|
17
|
+
* this worker the SAME DI graph as the HTTP process — every `@JobHandler`
|
|
18
|
+
* resolves its dependencies here exactly as it would in the API. A bare
|
|
19
|
+
* `SUBSYSTEM_MODULES`-only worker boots with an empty handler surface.
|
|
20
|
+
* - `AppModule` already wires `DatabaseModule` + `SUBSYSTEM_MODULES` (events +
|
|
21
|
+
* jobs + bridge + integration, dependency-ordered), so the worker needs no
|
|
22
|
+
* mode-aware barrel import — only `JobWorkerModule` itself.
|
|
23
|
+
*
|
|
24
|
+
* Why `allPools: true`:
|
|
25
|
+
* - The events subsystem's outbox drain and the bridge's fanout wrappers run
|
|
26
|
+
* as `job_run` rows in the RESERVED `events_*` pools. A worker that only
|
|
27
|
+
* polls the non-reserved pools (`interactive`, `batch`, …) strands those
|
|
28
|
+
* lanes — `BridgeDeliveryHandler` never fires and durable event→job fanout
|
|
29
|
+
* silently stops.
|
|
21
30
|
* - `allPools: true` activates every pool in the resolved config, reserved
|
|
22
31
|
* lanes included, so this single standalone process drains both user work
|
|
23
32
|
* and the framework's reserved lanes.
|
|
24
|
-
* - Importing `SUBSYSTEM_MODULES` (rather than `JobsDomainModule` alone)
|
|
25
|
-
* registers `EVENT_BUS` / `JOB_ORCHESTRATOR` / `BRIDGE_*` so the
|
|
26
|
-
* framework `@framework/bridge_delivery` handler resolves its DI deps.
|
|
27
|
-
* `BridgeModule`'s reserved-pool guard short-circuits to pass because
|
|
28
|
-
* `allPools` is set.
|
|
29
33
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
34
|
+
* STANDALONE ONLY: this entrypoint is for `jobs.worker_mode: standalone`. In
|
|
35
|
+
* embedded mode the worker already runs inside `AppModule` (via the
|
|
36
|
+
* `JobWorkerModule.forRoot({ mode: 'embedded' })` the barrel composes), so
|
|
37
|
+
* booting this file too would double-spawn the worker against the same pools.
|
|
38
|
+
*
|
|
39
|
+
* DO NOT boot `AppModule` twice in one process: a consumer `AppModule`
|
|
40
|
+
* registers an OpenAPI document against the per-process `OpenApiRegistry`
|
|
41
|
+
* singleton, which throws `DuplicateSchemaError` on the second registration.
|
|
42
|
+
* Multi-rung boot validation (e.g. "does the worker boot AND does the API
|
|
43
|
+
* boot?") must spawn CHILD PROCESSES, not import both modules into one.
|
|
33
44
|
*
|
|
34
45
|
* SIGTERM triggers graceful shutdown bounded by SHUTDOWN_TIMEOUT_MS; after the
|
|
35
46
|
* timeout the process exits hard so orchestrators (systemd, Kubernetes) can
|
|
@@ -39,26 +50,23 @@ import 'reflect-metadata';
|
|
|
39
50
|
import { Logger, Module } from '@nestjs/common';
|
|
40
51
|
import { NestFactory } from '@nestjs/core';
|
|
41
52
|
|
|
42
|
-
import {
|
|
43
|
-
import { JobWorkerModule } from '
|
|
44
|
-
import { SUBSYSTEM_MODULES } from '@generated/subsystems';
|
|
53
|
+
import { AppModule } from './app.module';
|
|
54
|
+
import { JobWorkerModule } from '<%= jobWorkerModuleImport %>';
|
|
45
55
|
|
|
46
56
|
const SHUTDOWN_TIMEOUT_MS = 30_000;
|
|
47
57
|
|
|
48
58
|
@Module({
|
|
49
59
|
imports: [
|
|
50
|
-
DatabaseModule
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
// same way in both processes.
|
|
55
|
-
...SUBSYSTEM_MODULES,
|
|
60
|
+
// Consumer root — DatabaseModule + SUBSYSTEM_MODULES + handler modules.
|
|
61
|
+
// Importing it whole keeps the worker's DI graph identical to the HTTP app's
|
|
62
|
+
// so every `@JobHandler` resolves the same way in both processes.
|
|
63
|
+
AppModule,
|
|
56
64
|
// `allPools: true` drains the reserved `events_*` lanes (events outbox +
|
|
57
65
|
// bridge wrappers) alongside the user pools.
|
|
58
|
-
JobWorkerModule.forRoot(
|
|
66
|
+
JobWorkerModule.forRoot(<%- workerForRootOpts %>),
|
|
59
67
|
],
|
|
60
68
|
})
|
|
61
|
-
class WorkerAppModule {}
|
|
69
|
+
export class WorkerAppModule {}
|
|
62
70
|
|
|
63
71
|
async function bootstrap(): Promise<void> {
|
|
64
72
|
const logger = new Logger('JobWorker');
|
|
@@ -98,8 +106,12 @@ async function bootstrap(): Promise<void> {
|
|
|
98
106
|
logger.log('job worker started (standalone mode, all pools)');
|
|
99
107
|
}
|
|
100
108
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
109
|
+
// Gated so the module can be imported by boot-checks / e2e without spawning a
|
|
110
|
+
// worker; `bun src/worker.ts` runs it as the entrypoint.
|
|
111
|
+
if (import.meta.main) {
|
|
112
|
+
bootstrap().catch((err) => {
|
|
113
|
+
// eslint-disable-next-line no-console
|
|
114
|
+
console.error('failed to bootstrap job worker', err);
|
|
115
|
+
process.exit(1);
|
|
116
|
+
});
|
|
117
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../runtime/base-classes/lifecycle-events.ts"],"sourcesContent":["/**\n * Lifecycle event emission for BaseService.\n *\n * Ported from pattern-stack/atoms/patterns/services/base.py — the Python\n * BaseService emits LIFECYCLE and CHANGE events on every CRUD operation.\n * This module provides the same capability for the TypeScript codegen stack.\n *\n * Design:\n * - Fire-and-forget: event emission never fails the CRUD operation.\n * - IEventBus is optional: if no EVENT_BUS is injected, emission is silently\n * skipped. This means base classes work in projects that haven't installed\n * the events subsystem.\n * - LIFECYCLE events carry an entity snapshot in payload.\n * - CHANGE events carry per-field old/new diffs.\n * - Controlled per-entity via `emitLifecycleEvents` flag (default: true).\n *\n * @deprecated EVT-7 — Lifecycle events are untyped and emit outside of the\n * CRUD transaction. New work should declare an `emits:` block on the entity\n * and publish typed domain events from use-cases via TYPED_EVENT_BUS inside\n * the same Drizzle transaction. See `docs/specs/EVT-7.md`. This helper is\n * retained for BaseService backward compatibility until all entities have\n * migrated to typed emits.\n */\n\nimport { randomUUID } from 'crypto';\nimport type { IEventBus, DomainEvent } from '../subsystems/events/event-bus.protocol';\n\n// ============================================================================\n// Event categories (subset of pattern-stack's EventCategory)\n// ============================================================================\n\nexport type EventCategory = 'lifecycle' | 'change';\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/** System fields excluded from entity snapshots and change diffs. */\nconst SYSTEM_FIELDS = new Set([\n\t'id',\n\t'createdAt',\n\t'updatedAt',\n\t'deletedAt',\n]);\n\n/**\n * Snapshot an entity's field values, excluding system fields.\n * Mirrors pattern-stack's `_get_entity_snapshot()`.\n */\nexport function entitySnapshot(entity: Record<string, unknown>): Record<string, unknown> {\n\tconst snap: Record<string, unknown> = {};\n\tfor (const [key, value] of Object.entries(entity)) {\n\t\tif (!SYSTEM_FIELDS.has(key)) {\n\t\t\tsnap[key] = value;\n\t\t}\n\t}\n\treturn snap;\n}\n\n/**\n * Diff two entity snapshots, returning per-field old/new pairs.\n * Only includes fields that actually changed.\n */\nexport function diffSnapshots(\n\tbefore: Record<string, unknown>,\n\tafter: Record<string, unknown>,\n): Array<{ field: string; oldValue: unknown; newValue: unknown }> {\n\tconst changes: Array<{ field: string; oldValue: unknown; newValue: unknown }> = [];\n\tconst allKeys = new Set([...Object.keys(before), ...Object.keys(after)]);\n\n\tfor (const key of allKeys) {\n\t\tif (SYSTEM_FIELDS.has(key)) continue;\n\t\tconst oldVal = before[key];\n\t\tconst newVal = after[key];\n\t\t// Simple equality — good enough for primitives and nulls.\n\t\t// For deep objects, JSON.stringify comparison.\n\t\tif (oldVal !== newVal && JSON.stringify(oldVal) !== JSON.stringify(newVal)) {\n\t\t\tchanges.push({ field: key, oldValue: oldVal, newValue: newVal });\n\t\t}\n\t}\n\n\treturn changes;\n}\n\n// ============================================================================\n// Event builders\n// ============================================================================\n\nexport function buildLifecycleEvent(\n\tentityName: string,\n\taction: 'created' | 'updated' | 'deleted',\n\tentityId: string,\n\tsnapshot?: Record<string, unknown>,\n): DomainEvent {\n\treturn {\n\t\tid: randomUUID(),\n\t\ttype: `${entityName}.${action}`,\n\t\taggregateId: entityId,\n\t\taggregateType: entityName,\n\t\tpayload: snapshot ? { snapshot } : {},\n\t\toccurredAt: new Date(),\n\t\tmetadata: { category: 'lifecycle' as EventCategory },\n\t};\n}\n\nexport function buildChangeEvents(\n\tentityName: string,\n\tentityId: string,\n\tchanges: Array<{ field: string; oldValue: unknown; newValue: unknown }>,\n): DomainEvent[] {\n\treturn changes.map((c) => ({\n\t\tid: randomUUID(),\n\t\ttype: `${entityName}.field_changed`,\n\t\taggregateId: entityId,\n\t\taggregateType: entityName,\n\t\tpayload: {\n\t\t\tfieldName: c.field,\n\t\t\toldValue: c.oldValue,\n\t\t\tnewValue: c.newValue,\n\t\t},\n\t\toccurredAt: new Date(),\n\t\tmetadata: { category: 'change' as EventCategory },\n\t}));\n}\n\n// ============================================================================\n// Emission helper (fire-and-forget)\n// ============================================================================\n\n/**\n * Emit events to the bus, swallowing errors.\n * Mirrors pattern-stack's `_emit_lifecycle_event()` try/except.\n */\nexport async function emitSafely(\n\teventBus: IEventBus | undefined,\n\tevents: DomainEvent[],\n): Promise<void> {\n\tif (!eventBus || events.length === 0) return;\n\ttry {\n\t\tif (events.length === 1) {\n\t\t\tconst only = events[0];\n\t\t\tif (!only) return;\n\t\t\tawait eventBus.publish(only);\n\t\t} else {\n\t\t\tawait eventBus.publishMany(events);\n\t\t}\n\t} catch {\n\t\t// Log but never fail the CRUD operation.\n\t\t// In production, this would use a structured logger.\n\t\tconsole.warn(`[lifecycle-events] failed to emit ${events.length} event(s)`);\n\t}\n}\n"],"mappings":";AAwBA,SAAS,kBAAkB;AAc3B,IAAM,gBAAgB,oBAAI,IAAI;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,CAAC;AAMM,SAAS,eAAe,QAA0D;AACxF,QAAM,OAAgC,CAAC;AACvC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AAClD,QAAI,CAAC,cAAc,IAAI,GAAG,GAAG;AAC5B,WAAK,GAAG,IAAI;AAAA,IACb;AAAA,EACD;AACA,SAAO;AACR;AAMO,SAAS,cACf,QACA,OACiE;AACjE,QAAM,UAA0E,CAAC;AACjF,QAAM,UAAU,oBAAI,IAAI,CAAC,GAAG,OAAO,KAAK,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,CAAC,CAAC;AAEvE,aAAW,OAAO,SAAS;AAC1B,QAAI,cAAc,IAAI,GAAG,EAAG;AAC5B,UAAM,SAAS,OAAO,GAAG;AACzB,UAAM,SAAS,MAAM,GAAG;AAGxB,QAAI,WAAW,UAAU,KAAK,UAAU,MAAM,MAAM,KAAK,UAAU,MAAM,GAAG;AAC3E,cAAQ,KAAK,EAAE,OAAO,KAAK,UAAU,QAAQ,UAAU,OAAO,CAAC;AAAA,IAChE;AAAA,EACD;AAEA,SAAO;AACR;AAMO,SAAS,oBACf,YACA,QACA,UACA,UACc;AACd,SAAO;AAAA,IACN,IAAI,WAAW;AAAA,IACf,MAAM,GAAG,UAAU,IAAI,MAAM;AAAA,IAC7B,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,IACpC,YAAY,oBAAI,KAAK;AAAA,IACrB,UAAU,EAAE,UAAU,YAA6B;AAAA,EACpD;AACD;AAEO,SAAS,kBACf,YACA,UACA,SACgB;AAChB,SAAO,QAAQ,IAAI,CAAC,OAAO;AAAA,IAC1B,IAAI,WAAW;AAAA,IACf,MAAM,GAAG,UAAU;AAAA,IACnB,aAAa;AAAA,IACb,eAAe;AAAA,IACf,SAAS;AAAA,MACR,WAAW,EAAE;AAAA,MACb,UAAU,EAAE;AAAA,MACZ,UAAU,EAAE;AAAA,IACb;AAAA,IACA,YAAY,oBAAI,KAAK;AAAA,IACrB,UAAU,EAAE,UAAU,SAA0B;AAAA,EACjD,EAAE;AACH;AAUA,eAAsB,WACrB,UACA,QACgB;AAChB,MAAI,CAAC,YAAY,OAAO,WAAW,EAAG;AACtC,MAAI;AACH,QAAI,OAAO,WAAW,GAAG;AACxB,YAAM,OAAO,OAAO,CAAC;AACrB,UAAI,CAAC,KAAM;AACX,YAAM,SAAS,QAAQ,IAAI;AAAA,IAC5B,OAAO;AACN,YAAM,SAAS,YAAY,MAAM;AAAA,IAClC;AAAA,EACD,QAAQ;AAGP,YAAQ,KAAK,qCAAqC,OAAO,MAAM,WAAW;AAAA,EAC3E;AACD;","names":[]}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|