@pattern-stack/codegen 0.23.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.
Files changed (109) hide show
  1. package/CHANGELOG.md +51 -1
  2. package/consumer-skills/integration/SKILL.md +11 -3
  3. package/dist/{chunk-XKWOJZZ4.js → chunk-37PILMIT.js} +4 -4
  4. package/dist/{chunk-2VHZ7EKC.js → chunk-5AAA4LTE.js} +2 -2
  5. package/dist/{chunk-42763UEE.js → chunk-6M6LZEP6.js} +2 -2
  6. package/dist/{chunk-AS3NAZB6.js → chunk-B7SC2V45.js} +2 -2
  7. package/dist/{chunk-W72PRNJY.js → chunk-BPYZCEHS.js} +2 -2
  8. package/dist/{chunk-FIUC6QB5.js → chunk-CKLM57IE.js} +10 -10
  9. package/dist/{chunk-KYR3B3OW.js → chunk-DAHWN63L.js} +26 -5
  10. package/dist/chunk-DAHWN63L.js.map +1 -0
  11. package/dist/{chunk-SH76CFAY.js → chunk-ENAR3F5S.js} +2 -2
  12. package/dist/{chunk-RFH7N6EP.js → chunk-FCPTHS42.js} +2 -2
  13. package/dist/{chunk-FFUDEIFF.js → chunk-HN5HT5WL.js} +2 -2
  14. package/dist/{chunk-4M66MQYA.js → chunk-K4BQQ2NN.js} +4 -2
  15. package/dist/chunk-K4BQQ2NN.js.map +1 -0
  16. package/dist/{chunk-QFUIE37H.js → chunk-KFXXOFDC.js} +4 -4
  17. package/dist/{chunk-O2A6XHGD.js → chunk-LLDJS7PJ.js} +2 -2
  18. package/dist/{chunk-JOBQ6RUU.js → chunk-LQZESSM3.js} +28 -1
  19. package/dist/chunk-LQZESSM3.js.map +1 -0
  20. package/dist/{chunk-JRQO2IOF.js → chunk-MU54DZCC.js} +27 -1
  21. package/dist/chunk-MU54DZCC.js.map +1 -0
  22. package/dist/{chunk-INO47JXD.js → chunk-PBENHIN2.js} +3 -3
  23. package/dist/{chunk-CLWBNXKF.js → chunk-PLUJEQLU.js} +2 -2
  24. package/dist/{chunk-S7C6TIIF.js → chunk-S5G3HO7N.js} +3 -1
  25. package/dist/chunk-S5G3HO7N.js.map +1 -0
  26. package/dist/{chunk-JYBFPNBJ.js → chunk-SJGEBMJT.js} +2 -2
  27. package/dist/{chunk-6XP2Q5SS.js → chunk-WZOPWQN2.js} +2 -2
  28. package/dist/{chunk-TDEHU73T.js → chunk-YIVQ7KLS.js} +46 -5
  29. package/dist/chunk-YIVQ7KLS.js.map +1 -0
  30. package/dist/runtime/base-classes/activity-entity-service.js +3 -3
  31. package/dist/runtime/base-classes/base-service.js +2 -2
  32. package/dist/runtime/base-classes/index.js +20 -20
  33. package/dist/runtime/base-classes/integrated-entity-service.js +3 -3
  34. package/dist/runtime/base-classes/knowledge-entity-service.js +3 -3
  35. package/dist/runtime/base-classes/lifecycle-events.js +1 -1
  36. package/dist/runtime/base-classes/metadata-entity-service.js +3 -3
  37. package/dist/runtime/subsystems/auth/auth.module.js +1 -1
  38. package/dist/runtime/subsystems/auth/index.js +3 -3
  39. package/dist/runtime/subsystems/bridge/bridge.module.js +6 -6
  40. package/dist/runtime/subsystems/bridge/index.js +6 -6
  41. package/dist/runtime/subsystems/events/events.module.js +4 -4
  42. package/dist/runtime/subsystems/events/generated/bus.js +3 -3
  43. package/dist/runtime/subsystems/events/generated/index.d.ts +2 -2
  44. package/dist/runtime/subsystems/events/generated/index.js +9 -3
  45. package/dist/runtime/subsystems/events/generated/registry.d.ts +36 -0
  46. package/dist/runtime/subsystems/events/generated/registry.js +1 -1
  47. package/dist/runtime/subsystems/events/generated/schemas.d.ts +109 -1
  48. package/dist/runtime/subsystems/events/generated/schemas.js +7 -1
  49. package/dist/runtime/subsystems/events/generated/types.d.ts +48 -2
  50. package/dist/runtime/subsystems/events/index.js +4 -4
  51. package/dist/runtime/subsystems/index.d.ts +3 -2
  52. package/dist/runtime/subsystems/index.js +25 -21
  53. package/dist/runtime/subsystems/integration/execute-integration.use-case.d.ts +11 -1
  54. package/dist/runtime/subsystems/integration/execute-integration.use-case.js +2 -2
  55. package/dist/runtime/subsystems/integration/index.d.ts +2 -1
  56. package/dist/runtime/subsystems/integration/index.js +10 -8
  57. package/dist/runtime/subsystems/integration/integration-change-emitter.protocol.d.ts +106 -0
  58. package/dist/runtime/subsystems/integration/integration-change-emitter.protocol.js +1 -0
  59. package/dist/runtime/subsystems/integration/integration-change-emitter.protocol.js.map +1 -0
  60. package/dist/runtime/subsystems/integration/integration-cursor-store.drizzle-backend.js +2 -2
  61. package/dist/runtime/subsystems/integration/integration-run-recorder.drizzle-backend.js +2 -2
  62. package/dist/runtime/subsystems/integration/integration.module.js +4 -4
  63. package/dist/runtime/subsystems/integration/integration.tokens.d.ts +11 -1
  64. package/dist/runtime/subsystems/integration/integration.tokens.js +3 -1
  65. package/dist/runtime/subsystems/jobs/index.js +11 -11
  66. package/dist/runtime/subsystems/jobs/job-worker.module.js +5 -5
  67. package/dist/runtime/subsystems/jobs/jobs-domain.module.js +4 -4
  68. package/dist/runtime/subsystems/observability/index.js +3 -3
  69. package/dist/runtime/subsystems/observability/observability.module.js +3 -3
  70. package/dist/runtime/subsystems/observability/observability.service.js +2 -2
  71. package/dist/src/cli/index.js +300 -53
  72. package/dist/src/cli/index.js.map +1 -1
  73. package/dist/src/index.d.ts +13 -0
  74. package/dist/src/index.js +7 -7
  75. package/package.json +1 -1
  76. package/runtime/base-classes/lifecycle-events.ts +39 -6
  77. package/runtime/subsystems/events/generated/registry.ts +27 -0
  78. package/runtime/subsystems/events/generated/schemas.ts +26 -0
  79. package/runtime/subsystems/events/generated/types.ts +52 -0
  80. package/runtime/subsystems/index.ts +23 -0
  81. package/runtime/subsystems/integration/execute-integration.use-case.ts +69 -1
  82. package/runtime/subsystems/integration/index.ts +6 -0
  83. package/runtime/subsystems/integration/integration-change-emitter.protocol.ts +107 -0
  84. package/runtime/subsystems/integration/integration.tokens.ts +11 -0
  85. package/templates/subsystem/jobs/job-orchestration.schema.ejs.t +1 -0
  86. package/templates/subsystem/jobs/main-hook.ejs.t +1 -1
  87. package/templates/subsystem/jobs/prompt.js +40 -2
  88. package/templates/subsystem/jobs/worker.ejs.t +47 -35
  89. package/dist/chunk-4M66MQYA.js.map +0 -1
  90. package/dist/chunk-JOBQ6RUU.js.map +0 -1
  91. package/dist/chunk-JRQO2IOF.js.map +0 -1
  92. package/dist/chunk-KYR3B3OW.js.map +0 -1
  93. package/dist/chunk-S7C6TIIF.js.map +0 -1
  94. package/dist/chunk-TDEHU73T.js.map +0 -1
  95. /package/dist/{chunk-XKWOJZZ4.js.map → chunk-37PILMIT.js.map} +0 -0
  96. /package/dist/{chunk-2VHZ7EKC.js.map → chunk-5AAA4LTE.js.map} +0 -0
  97. /package/dist/{chunk-42763UEE.js.map → chunk-6M6LZEP6.js.map} +0 -0
  98. /package/dist/{chunk-AS3NAZB6.js.map → chunk-B7SC2V45.js.map} +0 -0
  99. /package/dist/{chunk-W72PRNJY.js.map → chunk-BPYZCEHS.js.map} +0 -0
  100. /package/dist/{chunk-FIUC6QB5.js.map → chunk-CKLM57IE.js.map} +0 -0
  101. /package/dist/{chunk-SH76CFAY.js.map → chunk-ENAR3F5S.js.map} +0 -0
  102. /package/dist/{chunk-RFH7N6EP.js.map → chunk-FCPTHS42.js.map} +0 -0
  103. /package/dist/{chunk-FFUDEIFF.js.map → chunk-HN5HT5WL.js.map} +0 -0
  104. /package/dist/{chunk-QFUIE37H.js.map → chunk-KFXXOFDC.js.map} +0 -0
  105. /package/dist/{chunk-O2A6XHGD.js.map → chunk-LLDJS7PJ.js.map} +0 -0
  106. /package/dist/{chunk-INO47JXD.js.map → chunk-PBENHIN2.js.map} +0 -0
  107. /package/dist/{chunk-CLWBNXKF.js.map → chunk-PLUJEQLU.js.map} +0 -0
  108. /package/dist/{chunk-JYBFPNBJ.js.map → chunk-SJGEBMJT.js.map} +0 -0
  109. /package/dist/{chunk-6XP2Q5SS.js.map → chunk-WZOPWQN2.js.map} +0 -0
package/CHANGELOG.md CHANGED
@@ -2,7 +2,57 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
- ## [Unreleased]
5
+ ## [0.25.0] — 2026-06-07
6
+
7
+ ### Fixed
8
+
9
+ - **Lifecycle events: revive the audit trail + diagnosable emit failures.** The
10
+ `BaseService` lifecycle/change event path (`runtime/base-classes/lifecycle-events.ts`)
11
+ predates the AUDIT tier + routing schema (ADR-039) and was never migrated:
12
+ `buildLifecycleEvent` / `buildChangeEvents` stamped no `tier`, so
13
+ `toInsertValues` defaulted the row to `tier='domain'` with NULL `pool` /
14
+ `direction` — which violates the `domain_events_tier_routing_check` CHECK
15
+ (`tier='audit' ⇔ pool IS NULL AND direction IS NULL`). Result: **every**
16
+ `BaseService` create/update/delete in every consumer paid a rejected INSERT and
17
+ silently lost its lifecycle audit trail. The builders now stamp `tier: 'audit'`
18
+ (lifecycle/change events are exactly audit-tier semantics — untyped audit
19
+ records, never bridge-routed); the rows land and surface under the
20
+ observability viewer's audit-tier toggle, and the bridge guard keeps them out
21
+ of job routing. Discovered by the swe-brain dogfood (2× `failed to emit 3
22
+ event(s)` per dispatcher fire — the 3 being the `[updated, field_changed,
23
+ field_changed]` `publishMany` batch from `BaseService.update`).
24
+ - **Lifecycle events: `emitSafely` logs the cause via the Nest Logger.** The
25
+ fire-and-forget catch was a bare `catch {}` that swallowed the error and
26
+ printed `failed to emit N event(s)` via raw `console.warn` — bypassing the Nest
27
+ `Logger` (so consumers configuring `app.useLogger` / factory `logger:` could
28
+ neither format nor filter it) with zero diagnosability. Now logs via a
29
+ module-level `Logger('LifecycleEvents')` at `warn` level including the event
30
+ count, the distinct event types, and the error message; the stack follows at
31
+ `debug`. Never-throw semantics preserved.
32
+
33
+ ## [0.24.0] — 2026-06-06
34
+
35
+ ### Added
36
+
37
+ - **Integration: opt-in post-upsert change-event emission seam.** An entity that
38
+ declares `integration.sink.emit_changes: true` now gets typed per-entity domain
39
+ events published automatically when integration sync upserts/soft-deletes rows.
40
+ Codegen (a) desugars the entity into `<entity>_created` / `<entity>_edited` /
41
+ `<entity>_deleted` events (merged into the generated events registry exactly
42
+ like a hand-authored `events/*.yaml` — TypedEventBus augmentation, the
43
+ `EventTypeName` union, payload schemas), and (b) emits a fully-`@generated`
44
+ `<entity>.change-emitter.ts` that the per-entity assembly module binds to the
45
+ new optional `INTEGRATION_CHANGE_EMITTER` token. `ExecuteIntegrationUseCase`
46
+ injects the emitter `@Optional()` and publishes after every real sink
47
+ write/soft-delete (never on a `noop` diff or a delete that hit no row). Payload:
48
+ `{ entityId, externalId, provider, changedFields?, source: 'integration' }` —
49
+ the `source` provenance marker lets a write-back action detect
50
+ integration-originated changes and break the inbound→writeback loop. The verb
51
+ is `_edited`, NOT `_updated` (swe-brain ADR-0009 B1). Entities that don't opt in
52
+ are byte-for-byte unchanged (the emitter token stays unbound). Generalizes the
53
+ emission swe-brain hand-built in its sinks. See `docs/specs/EMIT-CHANGES-1.md`.
54
+
55
+ ## [0.23.0] — 2026-06-06
6
56
 
7
57
  ### Fixed
8
58
 
@@ -114,9 +114,17 @@ export class AppModule {}
114
114
  those records. This is the most common "wait, what?" moment; document it in
115
115
  your runbooks. Retry semantics are caller-owned.
116
116
 
117
- 7. **The orchestrator does not emit events, schedule itself, retry, or resolve
118
- subscriptions.** Those are all consumer concerns. Wire event emission inside
119
- your sink's transaction; wire scheduling via a job or webhook handler.
117
+ 7. **Event emission is an opt-in seam; scheduling/retry/subscription-resolution
118
+ stay consumer concerns.** Declare `integration.sink.emit_changes: true` on an
119
+ entity and codegen generates `<entity>_created` / `<entity>_edited` /
120
+ `<entity>_deleted` typed events plus a `<entity>.change-emitter.ts` the
121
+ assembly binds to `INTEGRATION_CHANGE_EMITTER`; the orchestrator then publishes
122
+ after every real sink write/soft-delete (payload carries
123
+ `source: 'integration'` for loop-breaking). Omit the flag (the default) and the
124
+ orchestrator emits nothing — hand-roll emission in your sink if you need a
125
+ bespoke payload, or override the generated event via a top-level
126
+ `events/<entity>_created.yaml`. Scheduling is still a job/webhook concern;
127
+ retry semantics are still caller-owned. See `docs/specs/EMIT-CHANGES-1.md`.
120
128
 
121
129
  ## Do not
122
130
 
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  BridgeMetricsReporter
3
3
  } from "./chunk-AQFQ4BYM.js";
4
- import {
5
- ObservabilityService
6
- } from "./chunk-CLWBNXKF.js";
7
4
  import {
8
5
  OBSERVABILITY,
9
6
  OBSERVABILITY_MODULE_OPTIONS
10
7
  } from "./chunk-Y7RRSEOC.js";
8
+ import {
9
+ ObservabilityService
10
+ } from "./chunk-PLUJEQLU.js";
11
11
  import {
12
12
  __decorateClass
13
13
  } from "./chunk-2E224ZSN.js";
@@ -45,4 +45,4 @@ ObservabilityModule = __decorateClass([
45
45
  export {
46
46
  ObservabilityModule
47
47
  };
48
- //# sourceMappingURL=chunk-XKWOJZZ4.js.map
48
+ //# sourceMappingURL=chunk-37PILMIT.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  BaseService
3
- } from "./chunk-W72PRNJY.js";
3
+ } from "./chunk-BPYZCEHS.js";
4
4
 
5
5
  // runtime/base-classes/integrated-entity-service.ts
6
6
  var IntegratedEntityService = class extends BaseService {
@@ -34,4 +34,4 @@ var IntegratedEntityService = class extends BaseService {
34
34
  export {
35
35
  IntegratedEntityService
36
36
  };
37
- //# sourceMappingURL=chunk-2VHZ7EKC.js.map
37
+ //# sourceMappingURL=chunk-5AAA4LTE.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  TypedEventBus
3
- } from "./chunk-INO47JXD.js";
3
+ } from "./chunk-PBENHIN2.js";
4
4
  import {
5
5
  EventScheduler,
6
6
  scheduledEventsFromRegistry
@@ -200,4 +200,4 @@ export {
200
200
  EventSchedulerLifecycle,
201
201
  EventsModule
202
202
  };
203
- //# sourceMappingURL=chunk-42763UEE.js.map
203
+ //# sourceMappingURL=chunk-6M6LZEP6.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  BaseService
3
- } from "./chunk-W72PRNJY.js";
3
+ } from "./chunk-BPYZCEHS.js";
4
4
 
5
5
  // runtime/base-classes/knowledge-entity-service.ts
6
6
  var KnowledgeEntityService = class extends BaseService {
@@ -11,4 +11,4 @@ var KnowledgeEntityService = class extends BaseService {
11
11
  export {
12
12
  KnowledgeEntityService
13
13
  };
14
- //# sourceMappingURL=chunk-AS3NAZB6.js.map
14
+ //# sourceMappingURL=chunk-B7SC2V45.js.map
@@ -4,7 +4,7 @@ import {
4
4
  diffSnapshots,
5
5
  emitSafely,
6
6
  entitySnapshot
7
- } from "./chunk-KYR3B3OW.js";
7
+ } from "./chunk-DAHWN63L.js";
8
8
 
9
9
  // runtime/base-classes/base-service.ts
10
10
  var BaseService = class {
@@ -123,4 +123,4 @@ var BaseService = class {
123
123
  export {
124
124
  BaseService
125
125
  };
126
- //# sourceMappingURL=chunk-W72PRNJY.js.map
126
+ //# sourceMappingURL=chunk-BPYZCEHS.js.map
@@ -1,6 +1,3 @@
1
- import {
2
- MemoryJobOrchestrator
3
- } from "./chunk-VQOAATIG.js";
4
1
  import {
5
2
  DrizzleJobRunService
6
3
  } from "./chunk-3VEVGL74.js";
@@ -10,12 +7,6 @@ import {
10
7
  import {
11
8
  DrizzleJobStepService
12
9
  } from "./chunk-DV4RV2DC.js";
13
- import {
14
- MemoryJobStepService
15
- } from "./chunk-PNZSGAB2.js";
16
- import {
17
- MemoryJobStore
18
- } from "./chunk-SNQ3TOWP.js";
19
10
  import {
20
11
  BULLMQ_CONNECTION,
21
12
  BULLMQ_RESOLVED_CONFIG,
@@ -24,6 +15,15 @@ import {
24
15
  import {
25
16
  DrizzleJobOrchestrator
26
17
  } from "./chunk-E6PLM6QG.js";
18
+ import {
19
+ MemoryJobOrchestrator
20
+ } from "./chunk-VQOAATIG.js";
21
+ import {
22
+ MemoryJobStepService
23
+ } from "./chunk-PNZSGAB2.js";
24
+ import {
25
+ MemoryJobStore
26
+ } from "./chunk-SNQ3TOWP.js";
27
27
  import {
28
28
  JOBS_LISTEN_NOTIFY,
29
29
  JOBS_MULTI_TENANT,
@@ -114,4 +114,4 @@ JobsDomainModule = __decorateClass([
114
114
  export {
115
115
  JobsDomainModule
116
116
  };
117
- //# sourceMappingURL=chunk-FIUC6QB5.js.map
117
+ //# sourceMappingURL=chunk-CKLM57IE.js.map
@@ -1,5 +1,7 @@
1
1
  // runtime/base-classes/lifecycle-events.ts
2
2
  import { randomUUID } from "crypto";
3
+ import { Logger } from "@nestjs/common";
4
+ var logger = new Logger("LifecycleEvents");
3
5
  var SYSTEM_FIELDS = /* @__PURE__ */ new Set([
4
6
  "id",
5
7
  "createdAt",
@@ -36,7 +38,17 @@ function buildLifecycleEvent(entityName, action, entityId, snapshot) {
36
38
  aggregateType: entityName,
37
39
  payload: snapshot ? { snapshot } : {},
38
40
  occurredAt: /* @__PURE__ */ new Date(),
39
- metadata: { category: "lifecycle" }
41
+ // AUDIT tier: lifecycle/change events are untyped audit-trail records —
42
+ // never bridge-routed, no pool/direction. The `domain_events`
43
+ // `domain_events_tier_routing_check` CHECK requires `tier='audit' ⇔
44
+ // (pool IS NULL AND direction IS NULL)`; the DEFAULT `tier='domain'`
45
+ // (applied by toInsertValues when absent) requires non-null routing
46
+ // fields, so an un-tiered lifecycle row violates the constraint and the
47
+ // INSERT is rejected — silently, pre-fix, by emitSafely's catch. Stamp
48
+ // `tier:'audit'` so these rows land (and surface under the
49
+ // observability viewer's audit-tier toggle). The bridge guard keeps
50
+ // audit-tier events out of job routing.
51
+ metadata: { category: "lifecycle", tier: "audit" }
40
52
  };
41
53
  }
42
54
  function buildChangeEvents(entityName, entityId, changes) {
@@ -51,7 +63,9 @@ function buildChangeEvents(entityName, entityId, changes) {
51
63
  newValue: c.newValue
52
64
  },
53
65
  occurredAt: /* @__PURE__ */ new Date(),
54
- metadata: { category: "change" }
66
+ // AUDIT tier see buildLifecycleEvent. Change events are audit-trail
67
+ // records; tier:'audit' satisfies the tier-routing CHECK constraint.
68
+ metadata: { category: "change", tier: "audit" }
55
69
  }));
56
70
  }
57
71
  async function emitSafely(eventBus, events) {
@@ -64,8 +78,15 @@ async function emitSafely(eventBus, events) {
64
78
  } else {
65
79
  await eventBus.publishMany(events);
66
80
  }
67
- } catch {
68
- console.warn(`[lifecycle-events] failed to emit ${events.length} event(s)`);
81
+ } catch (err) {
82
+ const message = err instanceof Error ? err.message : String(err);
83
+ const types = [...new Set(events.map((e) => e.type))].join(", ");
84
+ logger.warn(
85
+ `failed to emit ${events.length} event(s) [${types}]: ${message}`
86
+ );
87
+ if (err instanceof Error && err.stack) {
88
+ logger.debug(err.stack);
89
+ }
69
90
  }
70
91
  }
71
92
 
@@ -76,4 +97,4 @@ export {
76
97
  buildChangeEvents,
77
98
  emitSafely
78
99
  };
79
- //# sourceMappingURL=chunk-KYR3B3OW.js.map
100
+ //# sourceMappingURL=chunk-DAHWN63L.js.map
@@ -0,0 +1 @@
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 { Logger } from '@nestjs/common';\nimport type { IEventBus, DomainEvent } from '../subsystems/events/event-bus.protocol';\n\n/**\n * Module-level logger for fire-and-forget emission failures. Routed through the\n * Nest `Logger` (not bare `console`) so consumers configuring `app.useLogger`\n * or the factory `logger:` option can format and filter it like any other\n * framework log line.\n */\nconst logger = new Logger('LifecycleEvents');\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\t// AUDIT tier: lifecycle/change events are untyped audit-trail records —\n\t\t// never bridge-routed, no pool/direction. The `domain_events`\n\t\t// `domain_events_tier_routing_check` CHECK requires `tier='audit' ⇔\n\t\t// (pool IS NULL AND direction IS NULL)`; the DEFAULT `tier='domain'`\n\t\t// (applied by toInsertValues when absent) requires non-null routing\n\t\t// fields, so an un-tiered lifecycle row violates the constraint and the\n\t\t// INSERT is rejected — silently, pre-fix, by emitSafely's catch. Stamp\n\t\t// `tier:'audit'` so these rows land (and surface under the\n\t\t// observability viewer's audit-tier toggle). The bridge guard keeps\n\t\t// audit-tier events out of job routing.\n\t\tmetadata: { category: 'lifecycle' as EventCategory, tier: 'audit' },\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\t// AUDIT tier — see buildLifecycleEvent. Change events are audit-trail\n\t\t// records; tier:'audit' satisfies the tier-routing CHECK constraint.\n\t\tmetadata: { category: 'change' as EventCategory, tier: 'audit' },\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 (err) {\n\t\t// Never fail the CRUD operation — but surface the cause. The bare\n\t\t// `catch` that used to live here swallowed the error entirely, so a\n\t\t// failing bus printed `failed to emit N event(s)` with zero\n\t\t// diagnosability. Route through the Nest Logger (not bare console) at\n\t\t// warn level, including the distinct event types and the error message;\n\t\t// the stack follows at debug so it's available without noising the\n\t\t// default-threshold output.\n\t\tconst message = err instanceof Error ? err.message : String(err);\n\t\tconst types = [...new Set(events.map((e) => e.type))].join(', ');\n\t\tlogger.warn(\n\t\t\t`failed to emit ${events.length} event(s) [${types}]: ${message}`,\n\t\t);\n\t\tif (err instanceof Error && err.stack) {\n\t\t\tlogger.debug(err.stack);\n\t\t}\n\t}\n}\n"],"mappings":";AAwBA,SAAS,kBAAkB;AAC3B,SAAS,cAAc;AASvB,IAAM,SAAS,IAAI,OAAO,iBAAiB;AAa3C,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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAWrB,UAAU,EAAE,UAAU,aAA8B,MAAM,QAAQ;AAAA,EACnE;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;AAAA;AAAA,IAGrB,UAAU,EAAE,UAAU,UAA2B,MAAM,QAAQ;AAAA,EAChE,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,SAAS,KAAK;AAQb,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,UAAM,QAAQ,CAAC,GAAG,IAAI,IAAI,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,EAAE,KAAK,IAAI;AAC/D,WAAO;AAAA,MACN,kBAAkB,OAAO,MAAM,cAAc,KAAK,MAAM,OAAO;AAAA,IAChE;AACA,QAAI,eAAe,SAAS,IAAI,OAAO;AACtC,aAAO,MAAM,IAAI,KAAK;AAAA,IACvB;AAAA,EACD;AACD;","names":[]}
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-7B7MMDOJ.js";
4
4
  import {
5
5
  JobsDomainModule
6
- } from "./chunk-FIUC6QB5.js";
6
+ } from "./chunk-CKLM57IE.js";
7
7
  import {
8
8
  BULLMQ_CONNECTION,
9
9
  BULLMQ_RESOLVED_CONFIG,
@@ -295,4 +295,4 @@ export {
295
295
  JobWorkerOrchestrator,
296
296
  JobWorkerModule
297
297
  };
298
- //# sourceMappingURL=chunk-SH76CFAY.js.map
298
+ //# sourceMappingURL=chunk-ENAR3F5S.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  BaseService
3
- } from "./chunk-W72PRNJY.js";
3
+ } from "./chunk-BPYZCEHS.js";
4
4
 
5
5
  // runtime/base-classes/metadata-entity-service.ts
6
6
  var MetadataEntityService = class extends BaseService {
@@ -33,4 +33,4 @@ var MetadataEntityService = class extends BaseService {
33
33
  export {
34
34
  MetadataEntityService
35
35
  };
36
- //# sourceMappingURL=chunk-RFH7N6EP.js.map
36
+ //# sourceMappingURL=chunk-FCPTHS42.js.map
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-SQDOBLBP.js";
9
9
  import {
10
10
  INTEGRATION_MULTI_TENANT
11
- } from "./chunk-S7C6TIIF.js";
11
+ } from "./chunk-S5G3HO7N.js";
12
12
  import {
13
13
  assertTenantId
14
14
  } from "./chunk-MZ6GV4YF.js";
@@ -127,4 +127,4 @@ DrizzleIntegrationRunRecorder = __decorateClass([
127
127
  export {
128
128
  DrizzleIntegrationRunRecorder
129
129
  };
130
- //# sourceMappingURL=chunk-FFUDEIFF.js.map
130
+ //# sourceMappingURL=chunk-HN5HT5WL.js.map
@@ -399,7 +399,9 @@ var ProviderIntegrationSchema = z.object({
399
399
  var SinkPolicySchema = z.object({
400
400
  delete: z.enum(["soft", "tombstone", "noop"]).optional(),
401
401
  // NO .default() — see default fence in spec §Goal
402
- exclude_fields: z.array(z.string()).optional()
402
+ exclude_fields: z.array(z.string()).optional(),
403
+ emit_changes: z.boolean().optional()
404
+ // NO .default() — absent ⇒ no emission
403
405
  }).strict();
404
406
  var IntegrationConfigSchema = z.object({
405
407
  electric: z.boolean().optional().default(false),
@@ -4347,4 +4349,4 @@ export {
4347
4349
  analyzeDomain,
4348
4350
  validateEntities
4349
4351
  };
4350
- //# sourceMappingURL=chunk-4M66MQYA.js.map
4352
+ //# sourceMappingURL=chunk-K4BQQ2NN.js.map