@pattern-stack/codegen 0.23.0 → 0.24.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 (85) hide show
  1. package/CHANGELOG.md +23 -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-42763UEE.js → chunk-6M6LZEP6.js} +2 -2
  5. package/dist/{chunk-FIUC6QB5.js → chunk-CKLM57IE.js} +10 -10
  6. package/dist/{chunk-SH76CFAY.js → chunk-ENAR3F5S.js} +2 -2
  7. package/dist/{chunk-FFUDEIFF.js → chunk-HN5HT5WL.js} +2 -2
  8. package/dist/{chunk-4M66MQYA.js → chunk-K4BQQ2NN.js} +4 -2
  9. package/dist/chunk-K4BQQ2NN.js.map +1 -0
  10. package/dist/{chunk-QFUIE37H.js → chunk-KFXXOFDC.js} +4 -4
  11. package/dist/{chunk-O2A6XHGD.js → chunk-LLDJS7PJ.js} +2 -2
  12. package/dist/{chunk-JOBQ6RUU.js → chunk-LQZESSM3.js} +28 -1
  13. package/dist/chunk-LQZESSM3.js.map +1 -0
  14. package/dist/{chunk-JRQO2IOF.js → chunk-MU54DZCC.js} +27 -1
  15. package/dist/chunk-MU54DZCC.js.map +1 -0
  16. package/dist/{chunk-INO47JXD.js → chunk-PBENHIN2.js} +3 -3
  17. package/dist/{chunk-CLWBNXKF.js → chunk-PLUJEQLU.js} +2 -2
  18. package/dist/{chunk-S7C6TIIF.js → chunk-S5G3HO7N.js} +3 -1
  19. package/dist/chunk-S5G3HO7N.js.map +1 -0
  20. package/dist/{chunk-6XP2Q5SS.js → chunk-WZOPWQN2.js} +2 -2
  21. package/dist/{chunk-TDEHU73T.js → chunk-YIVQ7KLS.js} +46 -5
  22. package/dist/chunk-YIVQ7KLS.js.map +1 -0
  23. package/dist/runtime/base-classes/index.js +17 -17
  24. package/dist/runtime/subsystems/auth/auth.module.js +1 -1
  25. package/dist/runtime/subsystems/auth/index.js +3 -3
  26. package/dist/runtime/subsystems/bridge/bridge.module.js +6 -6
  27. package/dist/runtime/subsystems/bridge/index.js +6 -6
  28. package/dist/runtime/subsystems/events/events.module.js +4 -4
  29. package/dist/runtime/subsystems/events/generated/bus.js +3 -3
  30. package/dist/runtime/subsystems/events/generated/index.d.ts +2 -2
  31. package/dist/runtime/subsystems/events/generated/index.js +9 -3
  32. package/dist/runtime/subsystems/events/generated/registry.d.ts +36 -0
  33. package/dist/runtime/subsystems/events/generated/registry.js +1 -1
  34. package/dist/runtime/subsystems/events/generated/schemas.d.ts +109 -1
  35. package/dist/runtime/subsystems/events/generated/schemas.js +7 -1
  36. package/dist/runtime/subsystems/events/generated/types.d.ts +48 -2
  37. package/dist/runtime/subsystems/events/index.js +4 -4
  38. package/dist/runtime/subsystems/index.d.ts +3 -2
  39. package/dist/runtime/subsystems/index.js +26 -22
  40. package/dist/runtime/subsystems/integration/execute-integration.use-case.d.ts +11 -1
  41. package/dist/runtime/subsystems/integration/execute-integration.use-case.js +2 -2
  42. package/dist/runtime/subsystems/integration/index.d.ts +2 -1
  43. package/dist/runtime/subsystems/integration/index.js +10 -8
  44. package/dist/runtime/subsystems/integration/integration-change-emitter.protocol.d.ts +106 -0
  45. package/dist/runtime/subsystems/integration/integration-change-emitter.protocol.js +1 -0
  46. package/dist/runtime/subsystems/integration/integration-change-emitter.protocol.js.map +1 -0
  47. package/dist/runtime/subsystems/integration/integration-cursor-store.drizzle-backend.js +2 -2
  48. package/dist/runtime/subsystems/integration/integration-run-recorder.drizzle-backend.js +2 -2
  49. package/dist/runtime/subsystems/integration/integration.module.js +4 -4
  50. package/dist/runtime/subsystems/integration/integration.tokens.d.ts +11 -1
  51. package/dist/runtime/subsystems/integration/integration.tokens.js +3 -1
  52. package/dist/runtime/subsystems/jobs/index.js +11 -11
  53. package/dist/runtime/subsystems/jobs/job-worker.module.js +5 -5
  54. package/dist/runtime/subsystems/jobs/jobs-domain.module.js +4 -4
  55. package/dist/runtime/subsystems/observability/index.js +3 -3
  56. package/dist/runtime/subsystems/observability/observability.module.js +3 -3
  57. package/dist/runtime/subsystems/observability/observability.service.js +2 -2
  58. package/dist/src/cli/index.js +203 -26
  59. package/dist/src/cli/index.js.map +1 -1
  60. package/dist/src/index.d.ts +13 -0
  61. package/dist/src/index.js +7 -7
  62. package/package.json +1 -1
  63. package/runtime/subsystems/events/generated/registry.ts +27 -0
  64. package/runtime/subsystems/events/generated/schemas.ts +26 -0
  65. package/runtime/subsystems/events/generated/types.ts +52 -0
  66. package/runtime/subsystems/index.ts +23 -0
  67. package/runtime/subsystems/integration/execute-integration.use-case.ts +69 -1
  68. package/runtime/subsystems/integration/index.ts +6 -0
  69. package/runtime/subsystems/integration/integration-change-emitter.protocol.ts +107 -0
  70. package/runtime/subsystems/integration/integration.tokens.ts +11 -0
  71. package/dist/chunk-4M66MQYA.js.map +0 -1
  72. package/dist/chunk-JOBQ6RUU.js.map +0 -1
  73. package/dist/chunk-JRQO2IOF.js.map +0 -1
  74. package/dist/chunk-S7C6TIIF.js.map +0 -1
  75. package/dist/chunk-TDEHU73T.js.map +0 -1
  76. /package/dist/{chunk-XKWOJZZ4.js.map → chunk-37PILMIT.js.map} +0 -0
  77. /package/dist/{chunk-42763UEE.js.map → chunk-6M6LZEP6.js.map} +0 -0
  78. /package/dist/{chunk-FIUC6QB5.js.map → chunk-CKLM57IE.js.map} +0 -0
  79. /package/dist/{chunk-SH76CFAY.js.map → chunk-ENAR3F5S.js.map} +0 -0
  80. /package/dist/{chunk-FFUDEIFF.js.map → chunk-HN5HT5WL.js.map} +0 -0
  81. /package/dist/{chunk-QFUIE37H.js.map → chunk-KFXXOFDC.js.map} +0 -0
  82. /package/dist/{chunk-O2A6XHGD.js.map → chunk-LLDJS7PJ.js.map} +0 -0
  83. /package/dist/{chunk-INO47JXD.js.map → chunk-PBENHIN2.js.map} +0 -0
  84. /package/dist/{chunk-CLWBNXKF.js.map → chunk-PLUJEQLU.js.map} +0 -0
  85. /package/dist/{chunk-6XP2Q5SS.js.map → chunk-WZOPWQN2.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  PostgresCursorStore
3
- } from "../../../chunk-O2A6XHGD.js";
3
+ } from "../../../chunk-LLDJS7PJ.js";
4
4
  import "../../../chunk-HNWZFNKP.js";
5
- import "../../../chunk-S7C6TIIF.js";
5
+ import "../../../chunk-S5G3HO7N.js";
6
6
  import "../../../chunk-MZ6GV4YF.js";
7
7
  import "../../../chunk-U64T4YZE.js";
8
8
  import "../../../chunk-2E224ZSN.js";
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  DrizzleIntegrationRunRecorder
3
- } from "../../../chunk-FFUDEIFF.js";
3
+ } from "../../../chunk-HN5HT5WL.js";
4
4
  import "../../../chunk-HNWZFNKP.js";
5
5
  import "../../../chunk-SQDOBLBP.js";
6
- import "../../../chunk-S7C6TIIF.js";
6
+ import "../../../chunk-S5G3HO7N.js";
7
7
  import "../../../chunk-MZ6GV4YF.js";
8
8
  import "../../../chunk-U64T4YZE.js";
9
9
  import "../../../chunk-2E224ZSN.js";
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  IntegrationModule
3
- } from "../../../chunk-QFUIE37H.js";
4
- import "../../../chunk-FFUDEIFF.js";
3
+ } from "../../../chunk-KFXXOFDC.js";
4
+ import "../../../chunk-HN5HT5WL.js";
5
5
  import "../../../chunk-EO2QPOKH.js";
6
- import "../../../chunk-O2A6XHGD.js";
6
+ import "../../../chunk-LLDJS7PJ.js";
7
7
  import "../../../chunk-HNWZFNKP.js";
8
8
  import "../../../chunk-AHV4GDYM.js";
9
9
  import "../../../chunk-SQDOBLBP.js";
10
10
  import "../../../chunk-JEINYUJH.js";
11
- import "../../../chunk-S7C6TIIF.js";
11
+ import "../../../chunk-S5G3HO7N.js";
12
12
  import "../../../chunk-MZ6GV4YF.js";
13
13
  import "../../../chunk-U64T4YZE.js";
14
14
  import "../../../chunk-2E224ZSN.js";
@@ -23,6 +23,16 @@ declare const INTEGRATION_CHANGE_SOURCE: "INTEGRATION_CHANGE_SOURCE";
23
23
  declare const INTEGRATION_CURSOR_STORE: "INTEGRATION_CURSOR_STORE";
24
24
  declare const INTEGRATION_FIELD_DIFFER: "INTEGRATION_FIELD_DIFFER";
25
25
  declare const INTEGRATION_SINK: "INTEGRATION_SINK";
26
+ /**
27
+ * Optional post-upsert change-event emitter token (EMIT-CHANGES seam).
28
+ *
29
+ * Backed by `IIntegrationChangeEmitter`. Bound ONLY by codegen-emitted assembly
30
+ * modules whose entity opts in via `integration.sink.emit_changes: true`. The
31
+ * orchestrator injects it `@Optional()` — an unbound token means no domain
32
+ * events are published (the back-compat default for non-opted-in entities).
33
+ * See `integration-change-emitter.protocol.ts`.
34
+ */
35
+ declare const INTEGRATION_CHANGE_EMITTER: "INTEGRATION_CHANGE_EMITTER";
26
36
  /**
27
37
  * Run-recorder token (SYNC-5). Backed by `IIntegrationRunRecorder`. Drizzle impl
28
38
  * lands in SYNC-4; tests provide inline fakes.
@@ -57,4 +67,4 @@ declare const INTEGRATION_MULTI_TENANT: "INTEGRATION_MULTI_TENANT";
57
67
  */
58
68
  declare const ENTITY_CHANGE_SOURCE_REGISTRY: "ENTITY_CHANGE_SOURCE_REGISTRY";
59
69
 
60
- export { ENTITY_CHANGE_SOURCE_REGISTRY, INTEGRATION_CHANGE_SOURCE, INTEGRATION_CURSOR_STORE, INTEGRATION_FIELD_DIFFER, INTEGRATION_MODULE_OPTIONS, INTEGRATION_MULTI_TENANT, INTEGRATION_RUN_RECORDER, INTEGRATION_SINK };
70
+ export { ENTITY_CHANGE_SOURCE_REGISTRY, INTEGRATION_CHANGE_EMITTER, INTEGRATION_CHANGE_SOURCE, INTEGRATION_CURSOR_STORE, INTEGRATION_FIELD_DIFFER, INTEGRATION_MODULE_OPTIONS, INTEGRATION_MULTI_TENANT, INTEGRATION_RUN_RECORDER, INTEGRATION_SINK };
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  ENTITY_CHANGE_SOURCE_REGISTRY,
3
+ INTEGRATION_CHANGE_EMITTER,
3
4
  INTEGRATION_CHANGE_SOURCE,
4
5
  INTEGRATION_CURSOR_STORE,
5
6
  INTEGRATION_FIELD_DIFFER,
@@ -7,10 +8,11 @@ import {
7
8
  INTEGRATION_MULTI_TENANT,
8
9
  INTEGRATION_RUN_RECORDER,
9
10
  INTEGRATION_SINK
10
- } from "../../../chunk-S7C6TIIF.js";
11
+ } from "../../../chunk-S5G3HO7N.js";
11
12
  import "../../../chunk-2E224ZSN.js";
12
13
  export {
13
14
  ENTITY_CHANGE_SOURCE_REGISTRY,
15
+ INTEGRATION_CHANGE_EMITTER,
14
16
  INTEGRATION_CHANGE_SOURCE,
15
17
  INTEGRATION_CURSOR_STORE,
16
18
  INTEGRATION_FIELD_DIFFER,
@@ -2,7 +2,7 @@ import "../../../chunk-W4HOHZVF.js";
2
2
  import {
3
3
  JobWorkerModule,
4
4
  JobWorkerOrchestrator
5
- } from "../../../chunk-SH76CFAY.js";
5
+ } from "../../../chunk-ENAR3F5S.js";
6
6
  import {
7
7
  JOB_WORKER_OPTIONS,
8
8
  JobWorker,
@@ -13,10 +13,7 @@ import {
13
13
  } from "../../../chunk-7B7MMDOJ.js";
14
14
  import {
15
15
  JobsDomainModule
16
- } from "../../../chunk-FIUC6QB5.js";
17
- import {
18
- MemoryJobOrchestrator
19
- } from "../../../chunk-VQOAATIG.js";
16
+ } from "../../../chunk-CKLM57IE.js";
20
17
  import {
21
18
  DrizzleJobRunService
22
19
  } from "../../../chunk-3VEVGL74.js";
@@ -27,12 +24,6 @@ import "../../../chunk-L3LZWWSX.js";
27
24
  import {
28
25
  DrizzleJobStepService
29
26
  } from "../../../chunk-DV4RV2DC.js";
30
- import {
31
- MemoryJobStepService
32
- } from "../../../chunk-PNZSGAB2.js";
33
- import {
34
- MemoryJobStore
35
- } from "../../../chunk-SNQ3TOWP.js";
36
27
  import {
37
28
  BULLMQ_CONNECTION,
38
29
  BULLMQ_RESOLVED_CONFIG,
@@ -49,6 +40,15 @@ import {
49
40
  import {
50
41
  DrizzleJobOrchestrator
51
42
  } from "../../../chunk-E6PLM6QG.js";
43
+ import {
44
+ MemoryJobOrchestrator
45
+ } from "../../../chunk-VQOAATIG.js";
46
+ import {
47
+ MemoryJobStepService
48
+ } from "../../../chunk-PNZSGAB2.js";
49
+ import {
50
+ MemoryJobStore
51
+ } from "../../../chunk-SNQ3TOWP.js";
52
52
  import {
53
53
  BootValidationError,
54
54
  JobCollisionError,
@@ -2,19 +2,19 @@ import {
2
2
  JOB_WORKER_MODULE_OPTIONS,
3
3
  JobWorkerModule,
4
4
  JobWorkerOrchestrator
5
- } from "../../../chunk-SH76CFAY.js";
5
+ } from "../../../chunk-ENAR3F5S.js";
6
6
  import "../../../chunk-7B7MMDOJ.js";
7
- import "../../../chunk-FIUC6QB5.js";
8
- import "../../../chunk-VQOAATIG.js";
7
+ import "../../../chunk-CKLM57IE.js";
9
8
  import "../../../chunk-3VEVGL74.js";
10
9
  import "../../../chunk-CDLWYZVQ.js";
11
10
  import "../../../chunk-L3LZWWSX.js";
12
11
  import "../../../chunk-DV4RV2DC.js";
13
- import "../../../chunk-PNZSGAB2.js";
14
- import "../../../chunk-SNQ3TOWP.js";
15
12
  import "../../../chunk-I6MVCB5A.js";
16
13
  import "../../../chunk-RHVN6NA7.js";
17
14
  import "../../../chunk-E6PLM6QG.js";
15
+ import "../../../chunk-VQOAATIG.js";
16
+ import "../../../chunk-PNZSGAB2.js";
17
+ import "../../../chunk-SNQ3TOWP.js";
18
18
  import "../../../chunk-T4BIIU5E.js";
19
19
  import "../../../chunk-Q6LRJ4VI.js";
20
20
  import "../../../chunk-7P5ODGLA.js";
@@ -1,16 +1,16 @@
1
1
  import {
2
2
  JobsDomainModule
3
- } from "../../../chunk-FIUC6QB5.js";
4
- import "../../../chunk-VQOAATIG.js";
3
+ } from "../../../chunk-CKLM57IE.js";
5
4
  import "../../../chunk-3VEVGL74.js";
6
5
  import "../../../chunk-CDLWYZVQ.js";
7
6
  import "../../../chunk-L3LZWWSX.js";
8
7
  import "../../../chunk-DV4RV2DC.js";
9
- import "../../../chunk-PNZSGAB2.js";
10
- import "../../../chunk-SNQ3TOWP.js";
11
8
  import "../../../chunk-I6MVCB5A.js";
12
9
  import "../../../chunk-RHVN6NA7.js";
13
10
  import "../../../chunk-E6PLM6QG.js";
11
+ import "../../../chunk-VQOAATIG.js";
12
+ import "../../../chunk-PNZSGAB2.js";
13
+ import "../../../chunk-SNQ3TOWP.js";
14
14
  import "../../../chunk-T4BIIU5E.js";
15
15
  import "../../../chunk-Q6LRJ4VI.js";
16
16
  import "../../../chunk-7P5ODGLA.js";
@@ -5,20 +5,20 @@ import {
5
5
  } from "../../../chunk-EWYCWP4H.js";
6
6
  import {
7
7
  ObservabilityModule
8
- } from "../../../chunk-XKWOJZZ4.js";
8
+ } from "../../../chunk-37PILMIT.js";
9
9
  import {
10
10
  BridgeMetricsReporter
11
11
  } from "../../../chunk-AQFQ4BYM.js";
12
- import "../../../chunk-CLWBNXKF.js";
13
12
  import {
14
13
  OBSERVABILITY,
15
14
  OBSERVABILITY_MODULE_OPTIONS
16
15
  } from "../../../chunk-Y7RRSEOC.js";
16
+ import "../../../chunk-PLUJEQLU.js";
17
17
  import "../../../chunk-ZPL74UQN.js";
18
18
  import "../../../chunk-H5NH7KPE.js";
19
19
  import "../../../chunk-4LH67P4U.js";
20
20
  import "../../../chunk-GYGNEQSC.js";
21
- import "../../../chunk-S7C6TIIF.js";
21
+ import "../../../chunk-S5G3HO7N.js";
22
22
  import "../../../chunk-2E224ZSN.js";
23
23
  export {
24
24
  BridgeMetricsReporter,
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  ObservabilityModule
3
- } from "../../../chunk-XKWOJZZ4.js";
3
+ } from "../../../chunk-37PILMIT.js";
4
4
  import "../../../chunk-AQFQ4BYM.js";
5
- import "../../../chunk-CLWBNXKF.js";
6
5
  import "../../../chunk-Y7RRSEOC.js";
6
+ import "../../../chunk-PLUJEQLU.js";
7
7
  import "../../../chunk-ZPL74UQN.js";
8
8
  import "../../../chunk-H5NH7KPE.js";
9
9
  import "../../../chunk-4LH67P4U.js";
10
10
  import "../../../chunk-GYGNEQSC.js";
11
- import "../../../chunk-S7C6TIIF.js";
11
+ import "../../../chunk-S5G3HO7N.js";
12
12
  import "../../../chunk-2E224ZSN.js";
13
13
  export {
14
14
  ObservabilityModule
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  ObservabilityService
3
- } from "../../../chunk-CLWBNXKF.js";
3
+ } from "../../../chunk-PLUJEQLU.js";
4
4
  import "../../../chunk-ZPL74UQN.js";
5
5
  import "../../../chunk-H5NH7KPE.js";
6
6
  import "../../../chunk-4LH67P4U.js";
7
7
  import "../../../chunk-GYGNEQSC.js";
8
- import "../../../chunk-S7C6TIIF.js";
8
+ import "../../../chunk-S5G3HO7N.js";
9
9
  import "../../../chunk-2E224ZSN.js";
10
10
  export {
11
11
  ObservabilityService
@@ -44,13 +44,13 @@ import {
44
44
  validateOrchestrationProject,
45
45
  validateProviders,
46
46
  writeManifest
47
- } from "../../chunk-4M66MQYA.js";
47
+ } from "../../chunk-K4BQQ2NN.js";
48
48
  import "../../chunk-KVOWSC5S.js";
49
- import "../../chunk-QFUIE37H.js";
50
- import "../../chunk-FFUDEIFF.js";
51
- import "../../chunk-EO2QPOKH.js";
52
49
  import "../../chunk-PRWIX6UW.js";
53
- import "../../chunk-O2A6XHGD.js";
50
+ import "../../chunk-KFXXOFDC.js";
51
+ import "../../chunk-HN5HT5WL.js";
52
+ import "../../chunk-EO2QPOKH.js";
53
+ import "../../chunk-LLDJS7PJ.js";
54
54
  import "../../chunk-HNWZFNKP.js";
55
55
  import "../../chunk-AHV4GDYM.js";
56
56
  import "../../chunk-SQDOBLBP.js";
@@ -63,8 +63,8 @@ import {
63
63
  } from "../../chunk-5TK7MEN4.js";
64
64
  import "../../chunk-4KNXX6TI.js";
65
65
  import "../../chunk-3CJFPU6Q.js";
66
- import "../../chunk-TDEHU73T.js";
67
- import "../../chunk-S7C6TIIF.js";
66
+ import "../../chunk-YIVQ7KLS.js";
67
+ import "../../chunk-S5G3HO7N.js";
68
68
  import "../../chunk-MZ6GV4YF.js";
69
69
  import "../../chunk-LG57S2SC.js";
70
70
  import "../../chunk-U64T4YZE.js";
@@ -2151,7 +2151,7 @@ function loadEvents(eventsDir, entityNames) {
2151
2151
  function desugarEntityEvents(entity) {
2152
2152
  const entityName = entity.entity.name;
2153
2153
  const entityEvents = entity.events ?? [];
2154
- return entityEvents.map((ev) => {
2154
+ const explicit = entityEvents.map((ev) => {
2155
2155
  const payload = {};
2156
2156
  for (const [key, typeString] of Object.entries(ev.body)) {
2157
2157
  if (!isEventFieldType(typeString)) {
@@ -2173,6 +2173,56 @@ function desugarEntityEvents(entity) {
2173
2173
  };
2174
2174
  return def;
2175
2175
  });
2176
+ const changeTriad = desugarEmitChangeEvents(entity);
2177
+ return [...explicit, ...changeTriad];
2178
+ }
2179
+ var EMIT_CHANGE_SUFFIXES = ["created", "edited", "deleted"];
2180
+ function desugarEmitChangeEvents(entity) {
2181
+ if (entity.integration?.sink?.emit_changes !== true) return [];
2182
+ const entityName = entity.entity.name;
2183
+ const basePayload = {
2184
+ entity_id: {
2185
+ type: "uuid",
2186
+ nullable: false,
2187
+ description: "Local aggregate id the sink wrote/soft-deleted."
2188
+ },
2189
+ external_id: {
2190
+ type: "string",
2191
+ nullable: false,
2192
+ description: "Vendor external id the change keyed on."
2193
+ },
2194
+ provider: {
2195
+ type: "string",
2196
+ nullable: false,
2197
+ description: "Provider label (e.g. 'slack', 'google')."
2198
+ },
2199
+ source: {
2200
+ type: "string",
2201
+ nullable: false,
2202
+ description: "Provenance marker \u2014 always 'integration'. A write-back action reads this to avoid echoing the change back to the vendor."
2203
+ }
2204
+ };
2205
+ return EMIT_CHANGE_SUFFIXES.map((suffix) => {
2206
+ const payload = { ...basePayload };
2207
+ if (suffix !== "deleted") {
2208
+ payload.changed_fields = {
2209
+ type: "json",
2210
+ nullable: true,
2211
+ description: "Differ's per-field before/after map (same value as integration_run_items.changed_fields)."
2212
+ };
2213
+ }
2214
+ const def = {
2215
+ type: `${entityName}_${suffix}`,
2216
+ tier: "domain",
2217
+ direction: "change",
2218
+ aggregate: entityName,
2219
+ payload,
2220
+ retry: { attempts: 3, backoff: "exponential" },
2221
+ version: 1,
2222
+ pool: DIRECTION_TO_POOL.change
2223
+ };
2224
+ return def;
2225
+ });
2176
2226
  }
2177
2227
  function isEventFieldType(s) {
2178
2228
  return EVENT_FIELD_TYPES.includes(s);
@@ -3696,6 +3746,97 @@ import {
3696
3746
  } from "fs";
3697
3747
  import { dirname, join as join8 } from "path";
3698
3748
 
3749
+ // src/cli/shared/change-emitter-emission-generator.ts
3750
+ function generatedBanner(sourceDesc) {
3751
+ return `// @generated by @pattern-stack/codegen from ${sourceDesc} \u2014 DO NOT EDIT.
3752
+ // Hand edits are overwritten on re-emit. Regenerate with \`bun run codegen\`.`;
3753
+ }
3754
+ function changeEmitterClass(entityClass) {
3755
+ return `${entityClass}ChangeEmitter`;
3756
+ }
3757
+ function generateChangeEmitter(input) {
3758
+ const mode = input.mode ?? "package";
3759
+ const cls = changeEmitterClass(input.entityClass);
3760
+ const eventsImport = subsystemsImport(mode, "events");
3761
+ const integrationImport = subsystemsImport(mode, "integration");
3762
+ const createdType = `${input.entityName}_created`;
3763
+ const editedType = `${input.entityName}_edited`;
3764
+ const deletedType = `${input.entityName}_deleted`;
3765
+ return `${generatedBanner(input.sourceDesc)}
3766
+ import { Inject, Injectable } from '@nestjs/common';
3767
+ import { TYPED_EVENT_BUS } from '${eventsImport}';
3768
+ import type {
3769
+ IIntegrationChangeEmitter,
3770
+ IntegrationChangeNotification,
3771
+ } from '${integrationImport}';
3772
+
3773
+ /**
3774
+ * Minimal publisher shape the emitter calls \u2014 decoupled from BOTH the package's
3775
+ * \`TypedEventBus\` (whose \`EventTypeName\` union is the package's own events) and
3776
+ * the consumer's generated \`TypedEventBus\` (whose union is the consumer's
3777
+ * events). The \`${createdType}\` / \`${editedType}\` / \`${deletedType}\` events
3778
+ * THIS class publishes live in the CONSUMER's registry; the structural \`publish\`
3779
+ * here accepts the string types without depending on either compile-time union.
3780
+ * The TYPED_EVENT_BUS token resolves to the consumer's generated bus at runtime
3781
+ * (bound by EventsModule.forRoot()), which validates the payload against the
3782
+ * generated event schema.
3783
+ */
3784
+ interface ChangeEventPublisher {
3785
+ publish(
3786
+ type: string,
3787
+ aggregateId: string,
3788
+ payload: Record<string, unknown>,
3789
+ opts?: { tx?: unknown; metadata?: Record<string, unknown> },
3790
+ ): Promise<void>;
3791
+ }
3792
+
3793
+ /**
3794
+ * ${cls} \u2014 publishes the typed ${input.entityName} data-level change events
3795
+ * (\`${createdType}\` / \`${editedType}\` / \`${deletedType}\`) after the
3796
+ * integration orchestrator's sink write/soft-delete (EMIT-CHANGES seam).
3797
+ *
3798
+ * Bound to INTEGRATION_CHANGE_EMITTER in the ${input.entityName} integration
3799
+ * assembly; the orchestrator calls \`emitChange\` once per real change. The
3800
+ * payload carries \`source: 'integration'\` so a write-back action can detect an
3801
+ * integration-originated change and decline to echo it back to the vendor.
3802
+ */
3803
+ @Injectable()
3804
+ export class ${cls} implements IIntegrationChangeEmitter {
3805
+ constructor(
3806
+ @Inject(TYPED_EVENT_BUS) private readonly bus: ChangeEventPublisher,
3807
+ ) {}
3808
+
3809
+ async emitChange(notification: IntegrationChangeNotification): Promise<void> {
3810
+ const type =
3811
+ notification.action === 'created'
3812
+ ? '${createdType}'
3813
+ : notification.action === 'updated'
3814
+ ? '${editedType}'
3815
+ : '${deletedType}';
3816
+
3817
+ const payload: Record<string, unknown> = {
3818
+ entityId: notification.entityId,
3819
+ externalId: notification.externalId,
3820
+ provider: notification.provider,
3821
+ source: 'integration',
3822
+ };
3823
+ // changedFields is present on created/edited only (deletes are tombstones).
3824
+ if (notification.changedFields !== undefined) {
3825
+ payload['changedFields'] = notification.changedFields;
3826
+ }
3827
+
3828
+ await this.bus.publish(type, notification.entityId, payload, {
3829
+ tx: notification.tx,
3830
+ metadata:
3831
+ notification.tenantId !== undefined && notification.tenantId !== null
3832
+ ? { tenantId: notification.tenantId }
3833
+ : undefined,
3834
+ });
3835
+ }
3836
+ }
3837
+ `;
3838
+ }
3839
+
3699
3840
  // src/cli/shared/sink-emission-generator.ts
3700
3841
  var USER_ID_FIELD = "userId";
3701
3842
  function sinkNames(entityClass) {
@@ -3916,7 +4057,7 @@ export class ${n.sinkClass} extends ${n.sinkBaseClass} {
3916
4057
 
3917
4058
  // src/cli/shared/assembly-emission-generator.ts
3918
4059
  import { relative, resolve as resolve2, sep } from "path";
3919
- function generatedBanner(sourceDesc) {
4060
+ function generatedBanner2(sourceDesc) {
3920
4061
  return `// @generated by @pattern-stack/codegen from ${sourceDesc} \u2014 DO NOT EDIT.
3921
4062
  // Hand edits are overwritten on re-emit. Regenerate with \`bun run codegen\`.`;
3922
4063
  }
@@ -3947,19 +4088,30 @@ function generateAssemblyModule(input) {
3947
4088
  const token = integrationUseCaseToken(input.entityName, input.provider);
3948
4089
  const moduleClass = assemblyModuleClass(input.entityName, input.provider);
3949
4090
  const tokensImport = `../../${input.surface}-integration.tokens`;
3950
- return `${generatedBanner(input.sourceDesc)}
4091
+ const emitChanges = input.emitChanges === true;
4092
+ const emitterClass = changeEmitterClass(input.entityClass);
4093
+ const emitterImport = `../../sinks/${input.entityName}.change-emitter`;
4094
+ const integrationTokenImports = emitChanges ? ` INTEGRATION_CHANGE_EMITTER,
4095
+ INTEGRATION_CHANGE_SOURCE,
4096
+ INTEGRATION_SINK,` : ` INTEGRATION_CHANGE_SOURCE,
4097
+ INTEGRATION_SINK,`;
4098
+ const emitterImportLine = emitChanges ? `
4099
+ import { ${emitterClass} } from '${emitterImport}';` : "";
4100
+ const emitterProviderBlock = emitChanges ? `
4101
+ ${emitterClass},
4102
+ { provide: INTEGRATION_CHANGE_EMITTER, useExisting: ${emitterClass} },` : "";
4103
+ return `${generatedBanner2(input.sourceDesc)}
3951
4104
  import { Module } from '@nestjs/common';
3952
4105
  import {
3953
4106
  ExecuteIntegrationUseCase,
3954
- INTEGRATION_CHANGE_SOURCE,
3955
- INTEGRATION_SINK,
4107
+ ${integrationTokenImports}
3956
4108
  } from '${subsystemsImport(input.mode ?? "package", "integration")}';
3957
4109
  import { ${adapterClass} } from '${adapterImport}';
3958
4110
  import { ${adapterModuleClass} } from '${adapterModuleImport}';
3959
4111
  import { ${sinkClass} } from '${sinkImport}';
3960
4112
  import { ${input.repoClass} } from '${input.repoImportSpecifier}';
3961
4113
  import { ${input.moduleClass} } from '${input.moduleImportSpecifier}';
3962
- import { ${token} } from '${tokensImport}';
4114
+ import { ${token} } from '${tokensImport}';${emitterImportLine}
3963
4115
 
3964
4116
  /**
3965
4117
  * ${moduleClass} \u2014 the ${input.surface}/${input.entityName} \u2190 ${input.provider}
@@ -3969,7 +4121,12 @@ import { ${token} } from '${tokensImport}';
3969
4121
  * \`changeSources.${input.entityName}\` and INTEGRATION_SINK from
3970
4122
  * ${sinkClass}, provides a local ExecuteIntegrationUseCase, and aliases+exports
3971
4123
  * it under ${token} (the bare class token is ambiguous at app root \u2014 every
3972
- * assembly provides it). The substrate (cursor store, run recorder, differ,
4124
+ * assembly provides it).${emitChanges ? `
4125
+ *
4126
+ * EMIT-CHANGES: binds INTEGRATION_CHANGE_EMITTER to ${emitterClass} so the
4127
+ * orchestrator publishes typed ${input.entityName}_created/_edited/_deleted events
4128
+ * after every sink write/soft-delete (integration.sink.emit_changes).
4129
+ *` : ""} The substrate (cursor store, run recorder, differ,
3973
4130
  * multi-tenant flag) comes from the global IntegrationModule.forRoot(...) in
3974
4131
  * AppModule, never re-bound here.
3975
4132
  */
@@ -3985,7 +4142,7 @@ import { ${token} } from '${tokensImport}';
3985
4142
  provide: INTEGRATION_SINK,
3986
4143
  useFactory: (repo: ${input.repoClass}) => new ${sinkClass}(repo, '${input.provider}'),
3987
4144
  inject: [${input.repoClass}],
3988
- },
4145
+ },${emitterProviderBlock}
3989
4146
  ExecuteIntegrationUseCase,
3990
4147
  { provide: ${token}, useExisting: ExecuteIntegrationUseCase },
3991
4148
  ],
@@ -4005,7 +4162,7 @@ function generateIntegrationTokens(surface, entries) {
4005
4162
  * (${assemblyModuleClass(e.entityName, e.provider)}). A trigger grabs this to run it. */
4006
4163
  export const ${token} = Symbol.for('${key}');`;
4007
4164
  }).join("\n\n");
4008
- return `${generatedBanner(`surface: ${surface}`)}
4165
+ return `${generatedBanner2(`surface: ${surface}`)}
4009
4166
  /**
4010
4167
  * Use-case handles for the \`${surface}\` surface \u2014 one per (entity, provider)
4011
4168
  * assembly. Each is a Symbol \`provide:\` token a per-entity
@@ -4031,7 +4188,7 @@ function generateIntegrationAggregator(surface, entries) {
4031
4188
  return `import { ${cls} } from '${path36}';`;
4032
4189
  }).join("\n");
4033
4190
  const membersInline = moduleClasses.join(", ");
4034
- return `${generatedBanner(`surface: ${surface}`)}
4191
+ return `${generatedBanner2(`surface: ${surface}`)}
4035
4192
  import { Module } from '@nestjs/common';
4036
4193
  ${importLines || "// no (entity, provider) integration assemblies on this surface yet"}
4037
4194
 
@@ -4184,7 +4341,7 @@ function isClientlessProvider(surfaces) {
4184
4341
  return surfaces.length > 0 && surfaces.every((s) => SURFACE_REGISTRY[s]?.readPrimitive === true);
4185
4342
  }
4186
4343
  var SCAFFOLD_SENTINEL = "// <CODEGEN-SCAFFOLD-V1>";
4187
- function generatedBanner2(sourceDesc) {
4344
+ function generatedBanner3(sourceDesc) {
4188
4345
  return `// @generated by @pattern-stack/codegen from ${sourceDesc} \u2014 DO NOT EDIT.
4189
4346
  // Hand edits are overwritten on re-emit. Regenerate with \`bun run codegen\`.`;
4190
4347
  }
@@ -4424,7 +4581,7 @@ ${changeSourcesDecl}
4424
4581
  }
4425
4582
  function generateAdapterModule(def, surface) {
4426
4583
  const n = names(def.slug, surface);
4427
- return `${generatedBanner2(`definitions/providers/${def.slug}.yaml (surface: ${surface})`)}
4584
+ return `${generatedBanner3(`definitions/providers/${def.slug}.yaml (surface: ${surface})`)}
4428
4585
  import { Module } from '@nestjs/common';
4429
4586
  import { ${n.providerModuleClass} } from '../../../providers/${def.slug}/${def.slug}.provider.module';
4430
4587
  import { ${n.adapterClass} } from './${def.slug}-${surface}.adapter';
@@ -4442,13 +4599,13 @@ function generateAdaptersBarrel(surface, providerSlugs) {
4442
4599
  const n = names(slug, surface);
4443
4600
  return `export { ${n.adapterModuleClass} } from './${slug}/${slug}-${surface}.adapter.module';`;
4444
4601
  }).join("\n");
4445
- return `${generatedBanner2(`definitions/providers/*.yaml (surface: ${surface})`)}
4602
+ return `${generatedBanner3(`definitions/providers/*.yaml (surface: ${surface})`)}
4446
4603
  ${lines}
4447
4604
  `;
4448
4605
  }
4449
4606
  function generateSurfaceTokens(surface, mode = "package") {
4450
4607
  const n = names("__placeholder__", surface);
4451
- return `${generatedBanner2(`surface: ${surface}`)}
4608
+ return `${generatedBanner3(`surface: ${surface}`)}
4452
4609
  import type { IChangeSource } from '${subsystemsImport(mode, "integration")}';
4453
4610
 
4454
4611
  /** The assembled list of every ${surface} adapter's contribution. */
@@ -4487,7 +4644,7 @@ function generateSurfaceAggregator(surface, providerSlugs, mode = "package") {
4487
4644
  return `${lowerFirst(p.adapterClass)}: ${p.adapterClass}`;
4488
4645
  }).join(", ");
4489
4646
  const injectTokens = per.map((p) => p.adapterClass).join(", ");
4490
- return `${generatedBanner2(`surface: ${surface}`)}
4647
+ return `${generatedBanner3(`surface: ${surface}`)}
4491
4648
  import { Module } from '@nestjs/common';
4492
4649
  import {
4493
4650
  MemoryEntityChangeSourceRegistry,
@@ -4559,7 +4716,7 @@ function generateTypedView(surface, providerSlugs, entities) {
4559
4716
  const providerUnion = slugs.length ? slugs.map((s) => `'${s}'`).join(" | ") : "never";
4560
4717
  const entityUnion = ents.length ? ents.map((e) => `'${e}'`).join(" | ") : "never";
4561
4718
  const mapEntries = slugs.map((s) => ` ${jsKey(s)}: ${surfacePascal}Entity;`).join("\n");
4562
- return `${generatedBanner2(`surface: ${surface}`)}
4719
+ return `${generatedBanner3(`surface: ${surface}`)}
4563
4720
  /**
4564
4721
  * Per-consumer typed view for the \`${surface}\` surface. Surface-scoped unions
4565
4722
  * + a (provider, entity) validity map for compile-time-checked consumer
@@ -4589,6 +4746,7 @@ function emitAdapters(opts) {
4589
4746
  scaffoldsSkipped: [],
4590
4747
  skippedSurfaces: [],
4591
4748
  assembliesWritten: [],
4749
+ changeEmittersWritten: [],
4592
4750
  tokensWritten: [],
4593
4751
  integrationAggregatorsWritten: [],
4594
4752
  skippedAssemblies: []
@@ -4702,6 +4860,19 @@ function emitAdapters(opts) {
4702
4860
  if (!opts.dryRun) writeFile(subclassPath, subclassContent);
4703
4861
  result.scaffoldsWritten.push(subclassPath);
4704
4862
  }
4863
+ const emitChanges = def?.integration?.sink?.emit_changes === true;
4864
+ if (emitChanges) {
4865
+ const emitterPath = join8(sinksDir, `${entityName}.change-emitter.ts`);
4866
+ const emitterContent = generateChangeEmitter({
4867
+ entityName,
4868
+ entityClass: loc.entityClass,
4869
+ surface,
4870
+ sourceDesc: `definitions entity '${entityName}' (integration.sink.emit_changes)`,
4871
+ mode
4872
+ });
4873
+ if (!opts.dryRun) writeIfChanged(emitterPath, emitterContent);
4874
+ result.changeEmittersWritten.push(emitterPath);
4875
+ }
4705
4876
  for (const slug of slugs) {
4706
4877
  const assemblyPath = join8(
4707
4878
  modulesDir,
@@ -4718,7 +4889,8 @@ function emitAdapters(opts) {
4718
4889
  repoImportSpecifier: loc.repoImportSpecifier,
4719
4890
  repoClass: loc.repoClass,
4720
4891
  sourceDesc: `definitions/providers/${slug}.yaml`,
4721
- mode
4892
+ mode,
4893
+ emitChanges
4722
4894
  });
4723
4895
  if (!opts.dryRun) writeIfChanged(assemblyPath, assemblyContent);
4724
4896
  result.assembliesWritten.push(assemblyPath);
@@ -5052,12 +5224,12 @@ function resolveSyncMode(entity, config) {
5052
5224
  // src/emitters/frontend/emit-utils.ts
5053
5225
  import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
5054
5226
  import { dirname as dirname3 } from "path";
5055
- function generatedBanner3(sourceDesc) {
5227
+ function generatedBanner4(sourceDesc) {
5056
5228
  return `// @generated by @pattern-stack/codegen from ${sourceDesc} \u2014 DO NOT EDIT.
5057
5229
  // Hand edits are overwritten on re-emit. Regenerate with \`bun run codegen\`.`;
5058
5230
  }
5059
5231
  function withBanner(sourceDesc, body) {
5060
- return `${generatedBanner3(sourceDesc)}
5232
+ return `${generatedBanner4(sourceDesc)}
5061
5233
 
5062
5234
  ${body}`;
5063
5235
  }
@@ -6893,6 +7065,11 @@ var EntityNewCommand = class extends Command2 {
6893
7065
  `integration assembly codegen: ${adapterResult.assembliesWritten.length} module(s) + ${adapterResult.tokensWritten.length} tokens file(s) + ${adapterResult.integrationAggregatorsWritten.length} aggregator(s)`
6894
7066
  );
6895
7067
  }
7068
+ if (adapterResult.changeEmittersWritten.length) {
7069
+ printInfo(
7070
+ `integration change-emitters (emit_changes): ${adapterResult.changeEmittersWritten.length} emitter(s)`
7071
+ );
7072
+ }
6896
7073
  for (const s of adapterResult.scaffoldsSkipped) {
6897
7074
  printInfo(`skipped scaffold ${s} (author-owned)`);
6898
7075
  }