@pattern-stack/codegen 0.10.1 → 0.12.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 (279) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/README.md +5 -5
  3. package/consumer-skills/codegen/SKILL.md +2 -2
  4. package/consumer-skills/{sync → integration}/SKILL.md +29 -29
  5. package/consumer-skills/{sync → integration}/audit-and-detection.md +22 -22
  6. package/consumer-skills/{sync → integration}/change-sources-and-sinks.md +60 -60
  7. package/consumer-skills/subsystems/SKILL.md +8 -8
  8. package/consumer-skills/subsystems/wiring-and-order.md +7 -7
  9. package/dist/runtime/base-classes/index.d.ts +4 -4
  10. package/dist/runtime/base-classes/index.js +35 -35
  11. package/dist/runtime/base-classes/index.js.map +1 -1
  12. package/dist/runtime/base-classes/{synced-entity-repository.d.ts → integrated-entity-repository.d.ts} +15 -15
  13. package/dist/runtime/base-classes/{synced-entity-repository.js → integrated-entity-repository.js} +21 -21
  14. package/dist/runtime/base-classes/integrated-entity-repository.js.map +1 -0
  15. package/dist/runtime/base-classes/{synced-entity-service.d.ts → integrated-entity-service.d.ts} +6 -6
  16. package/dist/runtime/base-classes/{synced-entity-service.js → integrated-entity-service.js} +4 -4
  17. package/dist/runtime/base-classes/integrated-entity-service.js.map +1 -0
  18. package/dist/runtime/base-classes/{sync-upsert-config.d.ts → integration-upsert-config.d.ts} +13 -13
  19. package/dist/runtime/base-classes/integration-upsert-config.js +1 -0
  20. package/dist/runtime/base-classes/{junction-sync-repository.d.ts → junction-integration-repository.d.ts} +11 -11
  21. package/dist/runtime/base-classes/{junction-sync-repository.js → junction-integration-repository.js} +15 -15
  22. package/dist/runtime/base-classes/junction-integration-repository.js.map +1 -0
  23. package/dist/runtime/subsystems/auth/auth-oauth-state.schema.js.map +1 -1
  24. package/dist/runtime/subsystems/auth/auth.module.d.ts +4 -4
  25. package/dist/runtime/subsystems/auth/auth.module.js +3 -3
  26. package/dist/runtime/subsystems/auth/auth.module.js.map +1 -1
  27. package/dist/runtime/subsystems/auth/auth.tokens.d.ts +8 -8
  28. package/dist/runtime/subsystems/auth/auth.tokens.js +6 -6
  29. package/dist/runtime/subsystems/auth/auth.tokens.js.map +1 -1
  30. package/dist/runtime/subsystems/auth/backends/state-store.drizzle-backend.js.map +1 -1
  31. package/dist/runtime/subsystems/auth/controllers/auth.controller.d.ts +2 -2
  32. package/dist/runtime/subsystems/auth/controllers/auth.controller.js +3 -3
  33. package/dist/runtime/subsystems/auth/controllers/auth.controller.js.map +1 -1
  34. package/dist/runtime/subsystems/auth/index.d.ts +3 -3
  35. package/dist/runtime/subsystems/auth/index.js +40 -40
  36. package/dist/runtime/subsystems/auth/index.js.map +1 -1
  37. package/dist/runtime/subsystems/auth/middleware/requester-context.js.map +1 -1
  38. package/dist/runtime/subsystems/auth/protocols/auth-strategy.d.ts +3 -3
  39. package/dist/runtime/subsystems/auth/protocols/{integration-store.d.ts → connection-store.d.ts} +20 -20
  40. package/dist/runtime/subsystems/auth/protocols/connection-store.js +1 -0
  41. package/dist/runtime/subsystems/auth/protocols/provider-strategy.d.ts +3 -3
  42. package/dist/runtime/subsystems/auth/runtime/{integration-broken.error.d.ts → connection-broken.error.d.ts} +5 -5
  43. package/dist/runtime/subsystems/auth/runtime/connection-broken.error.js +19 -0
  44. package/dist/runtime/subsystems/auth/runtime/connection-broken.error.js.map +1 -0
  45. package/dist/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.d.ts +10 -10
  46. package/dist/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.js +28 -28
  47. package/dist/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.js.map +1 -1
  48. package/dist/runtime/subsystems/auth/runtime/with-auth-retry.d.ts +1 -1
  49. package/dist/runtime/subsystems/auth/runtime/with-auth-retry.js +3 -3
  50. package/dist/runtime/subsystems/auth/runtime/with-auth-retry.js.map +1 -1
  51. package/dist/runtime/subsystems/index.d.ts +11 -7
  52. package/dist/runtime/subsystems/index.js +1041 -67
  53. package/dist/runtime/subsystems/index.js.map +1 -1
  54. package/dist/runtime/subsystems/{sync → integration}/build-change-source.d.ts +3 -3
  55. package/dist/runtime/subsystems/{sync → integration}/build-change-source.js +3 -3
  56. package/dist/runtime/subsystems/integration/build-change-source.js.map +1 -0
  57. package/dist/runtime/subsystems/{sync → integration}/deep-equal.differ.d.ts +2 -2
  58. package/dist/runtime/subsystems/{sync → integration}/deep-equal.differ.js +1 -1
  59. package/dist/runtime/subsystems/integration/deep-equal.differ.js.map +1 -0
  60. package/dist/runtime/subsystems/{sync → integration}/detection-config.schema.d.ts +3 -3
  61. package/dist/runtime/subsystems/{sync → integration}/detection-config.schema.js +1 -1
  62. package/dist/runtime/subsystems/integration/detection-config.schema.js.map +1 -0
  63. package/dist/runtime/subsystems/integration/entity-change-source-registry.memory.d.ts +25 -0
  64. package/dist/runtime/subsystems/integration/entity-change-source-registry.memory.js +34 -0
  65. package/dist/runtime/subsystems/integration/entity-change-source-registry.memory.js.map +1 -0
  66. package/dist/runtime/subsystems/integration/entity-change-source-registry.protocol.d.ts +53 -0
  67. package/dist/runtime/subsystems/integration/entity-change-source-registry.protocol.js +13 -0
  68. package/dist/runtime/subsystems/integration/entity-change-source-registry.protocol.js.map +1 -0
  69. package/dist/runtime/subsystems/{sync/execute-sync.use-case.d.ts → integration/execute-integration.use-case.d.ts} +13 -13
  70. package/dist/runtime/subsystems/{sync/execute-sync.use-case.js → integration/execute-integration.use-case.js} +30 -30
  71. package/dist/runtime/subsystems/integration/execute-integration.use-case.js.map +1 -0
  72. package/dist/runtime/subsystems/integration/index.d.ts +30 -0
  73. package/dist/runtime/subsystems/{sync → integration}/index.js +206 -171
  74. package/dist/runtime/subsystems/integration/index.js.map +1 -0
  75. package/dist/runtime/subsystems/{sync/sync-audit.schema.d.ts → integration/integration-audit.schema.d.ts} +64 -64
  76. package/dist/runtime/subsystems/{sync/sync-audit.schema.js → integration/integration-audit.schema.js} +47 -47
  77. package/dist/runtime/subsystems/integration/integration-audit.schema.js.map +1 -0
  78. package/dist/runtime/subsystems/{sync/sync-change-source.protocol.d.ts → integration/integration-change-source.protocol.d.ts} +10 -10
  79. package/dist/runtime/subsystems/integration/integration-change-source.protocol.js +1 -0
  80. package/dist/runtime/subsystems/{sync/sync-cursor-store.drizzle-backend.d.ts → integration/integration-cursor-store.drizzle-backend.d.ts} +1 -1
  81. package/dist/runtime/subsystems/{sync/sync-cursor-store.drizzle-backend.js → integration/integration-cursor-store.drizzle-backend.js} +65 -65
  82. package/dist/runtime/subsystems/integration/integration-cursor-store.drizzle-backend.js.map +1 -0
  83. package/dist/runtime/subsystems/{sync/sync-cursor-store.memory-backend.d.ts → integration/integration-cursor-store.memory-backend.d.ts} +6 -6
  84. package/dist/runtime/subsystems/{sync/sync-cursor-store.memory-backend.js → integration/integration-cursor-store.memory-backend.js} +5 -5
  85. package/dist/runtime/subsystems/integration/integration-cursor-store.memory-backend.js.map +1 -0
  86. package/dist/runtime/subsystems/{sync/sync-cursor-store.protocol.d.ts → integration/integration-cursor-store.protocol.d.ts} +13 -13
  87. package/dist/runtime/subsystems/integration/integration-cursor-store.protocol.js +1 -0
  88. package/dist/runtime/subsystems/{sync/sync-errors.d.ts → integration/integration-errors.d.ts} +2 -2
  89. package/dist/runtime/subsystems/{sync/sync-errors.js → integration/integration-errors.js} +3 -3
  90. package/dist/runtime/subsystems/integration/integration-errors.js.map +1 -0
  91. package/dist/runtime/subsystems/{sync/sync-field-diff.protocol.d.ts → integration/integration-field-diff.protocol.d.ts} +2 -2
  92. package/dist/runtime/subsystems/{sync/sync-field-diff.protocol.js → integration/integration-field-diff.protocol.js} +2 -2
  93. package/dist/runtime/subsystems/integration/integration-field-diff.protocol.js.map +1 -0
  94. package/dist/runtime/subsystems/{sync/sync-loopback.protocol.d.ts → integration/integration-loopback.protocol.d.ts} +2 -2
  95. package/dist/runtime/subsystems/integration/integration-loopback.protocol.js +1 -0
  96. package/dist/runtime/subsystems/{sync/sync-middleware.protocol.d.ts → integration/integration-middleware.protocol.d.ts} +5 -5
  97. package/dist/runtime/subsystems/integration/integration-middleware.protocol.js +1 -0
  98. package/dist/runtime/subsystems/{sync/sync-run-recorder.drizzle-backend.d.ts → integration/integration-run-recorder.drizzle-backend.d.ts} +5 -5
  99. package/dist/runtime/subsystems/{sync/sync-run-recorder.drizzle-backend.js → integration/integration-run-recorder.drizzle-backend.js} +73 -73
  100. package/dist/runtime/subsystems/integration/integration-run-recorder.drizzle-backend.js.map +1 -0
  101. package/dist/runtime/subsystems/{sync/sync-run-recorder.memory-backend.d.ts → integration/integration-run-recorder.memory-backend.d.ts} +15 -15
  102. package/dist/runtime/subsystems/{sync/sync-run-recorder.memory-backend.js → integration/integration-run-recorder.memory-backend.js} +11 -11
  103. package/dist/runtime/subsystems/integration/integration-run-recorder.memory-backend.js.map +1 -0
  104. package/dist/runtime/subsystems/{sync/sync-run-recorder.protocol.d.ts → integration/integration-run-recorder.protocol.d.ts} +25 -25
  105. package/dist/runtime/subsystems/integration/integration-run-recorder.protocol.js +1 -0
  106. package/dist/runtime/subsystems/{sync/sync-sink.protocol.d.ts → integration/integration-sink.protocol.d.ts} +5 -5
  107. package/dist/runtime/subsystems/integration/integration-sink.protocol.js +1 -0
  108. package/dist/runtime/subsystems/{sync/sync.module.d.ts → integration/integration.module.d.ts} +24 -24
  109. package/dist/runtime/subsystems/{sync/sync.module.js → integration/integration.module.js} +132 -132
  110. package/dist/runtime/subsystems/integration/integration.module.js.map +1 -0
  111. package/dist/runtime/subsystems/integration/integration.tokens.d.ts +60 -0
  112. package/dist/runtime/subsystems/integration/integration.tokens.js +20 -0
  113. package/dist/runtime/subsystems/integration/integration.tokens.js.map +1 -0
  114. package/dist/runtime/subsystems/{sync → integration}/loopback.middleware.d.ts +5 -5
  115. package/dist/runtime/subsystems/{sync → integration}/loopback.middleware.js +1 -1
  116. package/dist/runtime/subsystems/integration/loopback.middleware.js.map +1 -0
  117. package/dist/runtime/subsystems/{sync → integration}/poll-change-source.d.ts +5 -5
  118. package/dist/runtime/subsystems/{sync → integration}/poll-change-source.js +1 -1
  119. package/dist/runtime/subsystems/integration/poll-change-source.js.map +1 -0
  120. package/dist/runtime/subsystems/{sync → integration}/webhook-change-source.d.ts +5 -5
  121. package/dist/runtime/subsystems/{sync → integration}/webhook-change-source.js +1 -1
  122. package/dist/runtime/subsystems/integration/webhook-change-source.js.map +1 -0
  123. package/dist/runtime/subsystems/jobs/job-worker.module.d.ts +1 -1
  124. package/dist/runtime/subsystems/observability/index.d.ts +4 -4
  125. package/dist/runtime/subsystems/observability/index.js +11 -11
  126. package/dist/runtime/subsystems/observability/index.js.map +1 -1
  127. package/dist/runtime/subsystems/observability/observability.module.d.ts +2 -2
  128. package/dist/runtime/subsystems/observability/observability.module.js +11 -11
  129. package/dist/runtime/subsystems/observability/observability.module.js.map +1 -1
  130. package/dist/runtime/subsystems/observability/observability.protocol.d.ts +11 -11
  131. package/dist/runtime/subsystems/observability/observability.service.d.ts +6 -6
  132. package/dist/runtime/subsystems/observability/observability.service.js +11 -11
  133. package/dist/runtime/subsystems/observability/observability.service.js.map +1 -1
  134. package/dist/runtime/subsystems/observability/observability.tokens.d.ts +1 -1
  135. package/dist/runtime/subsystems/observability/observability.tokens.js.map +1 -1
  136. package/dist/runtime/subsystems/observability/reporters/bridge-metrics.reporter.d.ts +3 -3
  137. package/dist/runtime/subsystems/observability/reporters/bridge-metrics.reporter.js.map +1 -1
  138. package/dist/runtime/subsystems/observability/reporters/index.d.ts +3 -3
  139. package/dist/runtime/subsystems/observability/reporters/index.js.map +1 -1
  140. package/dist/src/cli/index.js +1336 -376
  141. package/dist/src/cli/index.js.map +1 -1
  142. package/dist/src/index.d.ts +70 -22
  143. package/dist/src/index.js +290 -194
  144. package/dist/src/index.js.map +1 -1
  145. package/examples/auth-integrations/README.md +32 -32
  146. package/examples/auth-integrations/definitions/entities/{integration.yaml → connection.yaml} +10 -10
  147. package/examples/auth-integrations/runtime/{integrations/adapters/integration-grant-sink.adapter.ts → connections/adapters/connection-grant-sink.adapter.ts} +7 -7
  148. package/examples/auth-integrations/runtime/{integrations/adapters/integration-reader.adapter.ts → connections/adapters/connection-reader.adapter.ts} +10 -10
  149. package/examples/auth-integrations/runtime/{integrations/adapters/integration-token-writer.adapter.ts → connections/adapters/connection-token-writer.adapter.ts} +11 -11
  150. package/examples/auth-integrations/runtime/connections/connections-auth.module.ts +81 -0
  151. package/examples/auth-integrations/runtime/{integrations/facade/integrations.service.ts → connections/facade/connections.service.ts} +35 -35
  152. package/examples/auth-integrations/runtime/{integrations → connections}/oauth/use-cases/create-or-update-from-oauth-grant.use-case.ts +11 -11
  153. package/examples/auth-integrations/runtime/{integrations/oauth/use-cases/disconnect-integration.use-case.ts → connections/oauth/use-cases/disconnect-connection.use-case.ts} +6 -6
  154. package/examples/auth-integrations/runtime/connections/oauth/use-cases/list-user-connections.use-case.ts +21 -0
  155. package/examples/auth-integrations/runtime/connections/oauth/use-cases/mark-connection-requires-reauth.use-case.ts +21 -0
  156. package/package.json +9 -1
  157. package/runtime/base-classes/index.ts +8 -8
  158. package/runtime/base-classes/{synced-entity-repository.ts → integrated-entity-repository.ts} +36 -36
  159. package/runtime/base-classes/{synced-entity-service.ts → integrated-entity-service.ts} +6 -6
  160. package/runtime/base-classes/{sync-upsert-config.ts → integration-upsert-config.ts} +12 -12
  161. package/runtime/base-classes/{junction-sync-repository.ts → junction-integration-repository.ts} +28 -28
  162. package/runtime/subsystems/auth/auth-oauth-state.schema.ts +1 -1
  163. package/runtime/subsystems/auth/auth.module.ts +4 -4
  164. package/runtime/subsystems/auth/auth.tokens.ts +7 -7
  165. package/runtime/subsystems/auth/controllers/auth.controller.ts +7 -7
  166. package/runtime/subsystems/auth/index.ts +19 -19
  167. package/runtime/subsystems/auth/protocols/auth-strategy.ts +3 -3
  168. package/runtime/subsystems/auth/protocols/{integration-store.ts → connection-store.ts} +19 -19
  169. package/runtime/subsystems/auth/protocols/provider-strategy.ts +2 -2
  170. package/runtime/subsystems/auth/runtime/{integration-broken.error.ts → connection-broken.error.ts} +5 -5
  171. package/runtime/subsystems/auth/runtime/oauth2-refresh.strategy.ts +35 -35
  172. package/runtime/subsystems/auth/runtime/with-auth-retry.ts +3 -3
  173. package/runtime/subsystems/index.ts +26 -11
  174. package/runtime/subsystems/{sync → integration}/build-change-source.ts +3 -3
  175. package/runtime/subsystems/{sync → integration}/deep-equal.differ.ts +7 -7
  176. package/runtime/subsystems/{sync → integration}/detection-config.schema.ts +3 -3
  177. package/runtime/subsystems/integration/entity-change-source-registry.memory.ts +40 -0
  178. package/runtime/subsystems/integration/entity-change-source-registry.protocol.ts +59 -0
  179. package/runtime/subsystems/{sync/execute-sync.use-case.ts → integration/execute-integration.use-case.ts} +40 -40
  180. package/runtime/subsystems/{sync → integration}/index.ts +56 -47
  181. package/runtime/subsystems/{sync/sync-audit.schema.ts → integration/integration-audit.schema.ts} +61 -61
  182. package/runtime/subsystems/{sync/sync-change-source.protocol.ts → integration/integration-change-source.protocol.ts} +9 -9
  183. package/runtime/subsystems/{sync/sync-cursor-store.drizzle-backend.ts → integration/integration-cursor-store.drizzle-backend.ts} +30 -30
  184. package/runtime/subsystems/{sync/sync-cursor-store.memory-backend.ts → integration/integration-cursor-store.memory-backend.ts} +9 -9
  185. package/runtime/subsystems/{sync/sync-cursor-store.protocol.ts → integration/integration-cursor-store.protocol.ts} +13 -13
  186. package/runtime/subsystems/{sync/sync-errors.ts → integration/integration-errors.ts} +3 -3
  187. package/runtime/subsystems/{sync/sync-field-diff.protocol.ts → integration/integration-field-diff.protocol.ts} +2 -2
  188. package/runtime/subsystems/{sync/sync-loopback.protocol.ts → integration/integration-loopback.protocol.ts} +2 -2
  189. package/runtime/subsystems/{sync/sync-middleware.protocol.ts → integration/integration-middleware.protocol.ts} +6 -6
  190. package/runtime/subsystems/{sync/sync-run-recorder.drizzle-backend.ts → integration/integration-run-recorder.drizzle-backend.ts} +39 -39
  191. package/runtime/subsystems/{sync/sync-run-recorder.memory-backend.ts → integration/integration-run-recorder.memory-backend.ts} +23 -23
  192. package/runtime/subsystems/{sync/sync-run-recorder.protocol.ts → integration/integration-run-recorder.protocol.ts} +25 -25
  193. package/runtime/subsystems/{sync/sync-sink.protocol.ts → integration/integration-sink.protocol.ts} +4 -4
  194. package/runtime/subsystems/{sync/sync.module.ts → integration/integration.module.ts} +48 -48
  195. package/runtime/subsystems/integration/integration.tokens.ts +63 -0
  196. package/runtime/subsystems/{sync → integration}/loopback.middleware.ts +5 -5
  197. package/runtime/subsystems/{sync → integration}/poll-change-source.ts +7 -7
  198. package/runtime/subsystems/{sync → integration}/webhook-change-source.ts +7 -7
  199. package/runtime/subsystems/observability/index.ts +1 -1
  200. package/runtime/subsystems/observability/observability.module.ts +2 -2
  201. package/runtime/subsystems/observability/observability.protocol.ts +11 -11
  202. package/runtime/subsystems/observability/observability.service.ts +13 -13
  203. package/runtime/subsystems/observability/observability.tokens.ts +1 -1
  204. package/src/patterns/library/index.ts +4 -4
  205. package/src/patterns/library/{synced.pattern.ts → integrated.pattern.ts} +12 -12
  206. package/src/patterns/library/junction.pattern.ts +1 -1
  207. package/src/patterns/pattern-definition.ts +3 -3
  208. package/templates/entity/new/backend/modules/core/{sync-source.ejs.t → integration-source.ejs.t} +6 -6
  209. package/templates/entity/new/clean-lite-ps/entity.ejs.t +12 -3
  210. package/templates/entity/new/clean-lite-ps/module.ejs.t +1 -1
  211. package/templates/entity/new/clean-lite-ps/prompt-extension.js +243 -60
  212. package/templates/entity/new/clean-lite-ps/repository.ejs.t +27 -27
  213. package/templates/entity/new/frontend/collections/collection.ejs.t +26 -1
  214. package/templates/entity/new/frontend/collections/collections-base.ejs.t +11 -0
  215. package/templates/entity/new/frontend/entity/combined.ejs.t +31 -1
  216. package/templates/entity/new/prompt.js +27 -15
  217. package/templates/junction/new/entity.ejs.t +1 -1
  218. package/templates/junction/new/prompt.js +24 -24
  219. package/templates/junction/new/repository.ejs.t +19 -19
  220. package/templates/subsystem/auth/auth-oauth-state.schema.ejs.t +2 -2
  221. package/templates/subsystem/auth-config/prompt.js +1 -1
  222. package/templates/subsystem/auth-integrations/app-module-hook.ejs.t +5 -5
  223. package/templates/subsystem/bridge/prompt.js +1 -1
  224. package/templates/subsystem/integration/integration-audit.schema.ejs.t +192 -0
  225. package/templates/subsystem/{sync → integration}/prompt.js +12 -12
  226. package/templates/subsystem/{sync-config/codegen-config-sync-block.ejs.t → integration-config/codegen-config-integration-block.ejs.t} +7 -7
  227. package/templates/subsystem/integration-config/prompt.js +22 -0
  228. package/templates/subsystem/jobs/worker.ejs.t +2 -2
  229. package/templates/subsystem/observability/main-hook.ejs.t +1 -1
  230. package/templates/subsystem/observability/prompt.js +1 -1
  231. package/templates/subsystem/openapi-config/prompt.js +1 -1
  232. package/dist/runtime/base-classes/junction-sync-repository.js.map +0 -1
  233. package/dist/runtime/base-classes/sync-upsert-config.js +0 -1
  234. package/dist/runtime/base-classes/synced-entity-repository.js.map +0 -1
  235. package/dist/runtime/base-classes/synced-entity-service.js.map +0 -1
  236. package/dist/runtime/subsystems/auth/protocols/integration-store.js +0 -1
  237. package/dist/runtime/subsystems/auth/runtime/integration-broken.error.js +0 -19
  238. package/dist/runtime/subsystems/auth/runtime/integration-broken.error.js.map +0 -1
  239. package/dist/runtime/subsystems/sync/build-change-source.js.map +0 -1
  240. package/dist/runtime/subsystems/sync/deep-equal.differ.js.map +0 -1
  241. package/dist/runtime/subsystems/sync/detection-config.schema.js.map +0 -1
  242. package/dist/runtime/subsystems/sync/execute-sync.use-case.js.map +0 -1
  243. package/dist/runtime/subsystems/sync/index.d.ts +0 -28
  244. package/dist/runtime/subsystems/sync/index.js.map +0 -1
  245. package/dist/runtime/subsystems/sync/loopback.middleware.js.map +0 -1
  246. package/dist/runtime/subsystems/sync/poll-change-source.js.map +0 -1
  247. package/dist/runtime/subsystems/sync/sync-audit.schema.js.map +0 -1
  248. package/dist/runtime/subsystems/sync/sync-change-source.protocol.js +0 -1
  249. package/dist/runtime/subsystems/sync/sync-cursor-store.drizzle-backend.js.map +0 -1
  250. package/dist/runtime/subsystems/sync/sync-cursor-store.memory-backend.js.map +0 -1
  251. package/dist/runtime/subsystems/sync/sync-cursor-store.protocol.js +0 -1
  252. package/dist/runtime/subsystems/sync/sync-errors.js.map +0 -1
  253. package/dist/runtime/subsystems/sync/sync-field-diff.protocol.js.map +0 -1
  254. package/dist/runtime/subsystems/sync/sync-loopback.protocol.js +0 -1
  255. package/dist/runtime/subsystems/sync/sync-middleware.protocol.js +0 -1
  256. package/dist/runtime/subsystems/sync/sync-run-recorder.drizzle-backend.js.map +0 -1
  257. package/dist/runtime/subsystems/sync/sync-run-recorder.memory-backend.js.map +0 -1
  258. package/dist/runtime/subsystems/sync/sync-run-recorder.protocol.js +0 -1
  259. package/dist/runtime/subsystems/sync/sync-sink.protocol.js +0 -1
  260. package/dist/runtime/subsystems/sync/sync.module.js.map +0 -1
  261. package/dist/runtime/subsystems/sync/sync.tokens.d.ts +0 -47
  262. package/dist/runtime/subsystems/sync/sync.tokens.js +0 -18
  263. package/dist/runtime/subsystems/sync/sync.tokens.js.map +0 -1
  264. package/dist/runtime/subsystems/sync/webhook-change-source.js.map +0 -1
  265. package/examples/auth-integrations/runtime/integrations/integrations-auth.module.ts +0 -81
  266. package/examples/auth-integrations/runtime/integrations/oauth/use-cases/list-user-integrations.use-case.ts +0 -21
  267. package/examples/auth-integrations/runtime/integrations/oauth/use-cases/mark-integration-requires-reauth.use-case.ts +0 -21
  268. package/runtime/subsystems/sync/sync.tokens.ts +0 -49
  269. package/templates/entity/new/backend/modules/core/sync-source.providers.ejs.t +0 -18
  270. package/templates/subsystem/sync/sync-audit.schema.ejs.t +0 -192
  271. package/templates/subsystem/sync-config/prompt.js +0 -22
  272. /package/dist/runtime/base-classes/{sync-upsert-config.js.map → integration-upsert-config.js.map} +0 -0
  273. /package/dist/runtime/subsystems/auth/protocols/{integration-store.js.map → connection-store.js.map} +0 -0
  274. /package/dist/runtime/subsystems/{sync/sync-change-source.protocol.js.map → integration/integration-change-source.protocol.js.map} +0 -0
  275. /package/dist/runtime/subsystems/{sync/sync-cursor-store.protocol.js.map → integration/integration-cursor-store.protocol.js.map} +0 -0
  276. /package/dist/runtime/subsystems/{sync/sync-loopback.protocol.js.map → integration/integration-loopback.protocol.js.map} +0 -0
  277. /package/dist/runtime/subsystems/{sync/sync-middleware.protocol.js.map → integration/integration-middleware.protocol.js.map} +0 -0
  278. /package/dist/runtime/subsystems/{sync/sync-run-recorder.protocol.js.map → integration/integration-run-recorder.protocol.js.map} +0 -0
  279. /package/dist/runtime/subsystems/{sync/sync-sink.protocol.js.map → integration/integration-sink.protocol.js.map} +0 -0
@@ -12,8 +12,8 @@ var __decorateClass = (decorators, target, key, kind) => {
12
12
  var __decorateParam = (index2, decorator) => (target, key) => decorator(target, key, index2);
13
13
 
14
14
  // src/cli/index.ts
15
- import { readFileSync as readFileSync6 } from "fs";
16
- import { join as join10 } from "path";
15
+ import { readFileSync as readFileSync9 } from "fs";
16
+ import { join as join13 } from "path";
17
17
  import { Builtins, Cli, Command as Command13 } from "clipanion";
18
18
 
19
19
  // src/cli/noun-module.ts
@@ -1081,7 +1081,7 @@ import { parse as parseYaml } from "yaml";
1081
1081
  // src/schema/entity-definition.schema.ts
1082
1082
  import { z as z3 } from "zod";
1083
1083
 
1084
- // runtime/subsystems/sync/sync-field-diff.protocol.ts
1084
+ // runtime/subsystems/integration/integration-field-diff.protocol.ts
1085
1085
  import { z } from "zod";
1086
1086
  var FieldDiffValueSchema = z.object({
1087
1087
  from: z.unknown(),
@@ -1089,7 +1089,7 @@ var FieldDiffValueSchema = z.object({
1089
1089
  });
1090
1090
  var FieldDiffSchema = z.record(z.string(), FieldDiffValueSchema);
1091
1091
 
1092
- // runtime/subsystems/sync/detection-config.schema.ts
1092
+ // runtime/subsystems/integration/detection-config.schema.ts
1093
1093
  import { z as z2 } from "zod";
1094
1094
  var FieldMappingSchema = z2.object({
1095
1095
  source: z2.string().min(1),
@@ -1147,21 +1147,21 @@ var DetectionConfigSchema = z2.discriminatedUnion("mode", [
1147
1147
  WebhookModeSchema
1148
1148
  ]);
1149
1149
 
1150
- // runtime/subsystems/sync/sync.tokens.ts
1151
- var SYNC_CHANGE_SOURCE = "SYNC_CHANGE_SOURCE";
1152
- var SYNC_CURSOR_STORE = "SYNC_CURSOR_STORE";
1153
- var SYNC_FIELD_DIFFER = "SYNC_FIELD_DIFFER";
1154
- var SYNC_SINK = "SYNC_SINK";
1155
- var SYNC_RUN_RECORDER = "SYNC_RUN_RECORDER";
1156
- var SYNC_MODULE_OPTIONS = "SYNC_MODULE_OPTIONS";
1157
- var SYNC_MULTI_TENANT = "SYNC_MULTI_TENANT";
1150
+ // runtime/subsystems/integration/integration.tokens.ts
1151
+ var INTEGRATION_CHANGE_SOURCE = "INTEGRATION_CHANGE_SOURCE";
1152
+ var INTEGRATION_CURSOR_STORE = "INTEGRATION_CURSOR_STORE";
1153
+ var INTEGRATION_FIELD_DIFFER = "INTEGRATION_FIELD_DIFFER";
1154
+ var INTEGRATION_SINK = "INTEGRATION_SINK";
1155
+ var INTEGRATION_RUN_RECORDER = "INTEGRATION_RUN_RECORDER";
1156
+ var INTEGRATION_MODULE_OPTIONS = "INTEGRATION_MODULE_OPTIONS";
1157
+ var INTEGRATION_MULTI_TENANT = "INTEGRATION_MULTI_TENANT";
1158
1158
 
1159
- // runtime/subsystems/sync/sync-errors.ts
1159
+ // runtime/subsystems/integration/integration-errors.ts
1160
1160
  var MissingTenantIdError = class extends Error {
1161
1161
  name = "MissingTenantIdError";
1162
1162
  constructor(operation) {
1163
1163
  super(
1164
- `Missing tenantId for sync operation '${operation}'. SyncModule is configured with multiTenant: true \u2014 every call must include a non-null tenantId. Either pass the tenantId or disable multi-tenancy on the module.`
1164
+ `Missing tenantId for integration operation '${operation}'. IntegrationModule is configured with multiTenant: true \u2014 every call must include a non-null tenantId. Either pass the tenantId or disable multi-tenancy on the module.`
1165
1165
  );
1166
1166
  }
1167
1167
  };
@@ -1172,7 +1172,7 @@ function assertTenantId(tenantId, options) {
1172
1172
  }
1173
1173
  }
1174
1174
 
1175
- // runtime/subsystems/sync/sync-audit.schema.ts
1175
+ // runtime/subsystems/integration/integration-audit.schema.ts
1176
1176
  import {
1177
1177
  pgEnum,
1178
1178
  pgTable,
@@ -1185,39 +1185,39 @@ import {
1185
1185
  index,
1186
1186
  uniqueIndex
1187
1187
  } from "drizzle-orm/pg-core";
1188
- var syncRunDirectionEnum = pgEnum("sync_run_direction", [
1188
+ var integrationRunDirectionEnum = pgEnum("integration_run_direction", [
1189
1189
  "inbound",
1190
1190
  "outbound"
1191
1191
  ]);
1192
- var syncRunActionEnum = pgEnum("sync_run_action", [
1192
+ var integrationRunActionEnum = pgEnum("integration_run_action", [
1193
1193
  "poll",
1194
1194
  "cdc",
1195
1195
  "webhook",
1196
1196
  "manual",
1197
1197
  "writeback"
1198
1198
  ]);
1199
- var syncRunStatusEnum = pgEnum("sync_run_status", [
1199
+ var integrationRunStatusEnum = pgEnum("integration_run_status", [
1200
1200
  "running",
1201
1201
  "success",
1202
1202
  "no_changes",
1203
1203
  "failed"
1204
1204
  ]);
1205
- var syncRunItemOperationEnum = pgEnum("sync_run_item_operation", [
1205
+ var integrationRunItemOperationEnum = pgEnum("integration_run_item_operation", [
1206
1206
  "created",
1207
1207
  "updated",
1208
1208
  "deleted",
1209
1209
  "noop"
1210
1210
  ]);
1211
- var syncRunItemStatusEnum = pgEnum("sync_run_item_status", [
1211
+ var integrationRunItemStatusEnum = pgEnum("integration_run_item_status", [
1212
1212
  "success",
1213
1213
  "failed",
1214
1214
  "skipped"
1215
1215
  ]);
1216
- var syncSubscriptions = pgTable(
1217
- "sync_subscriptions",
1216
+ var integrationSubscriptions = pgTable(
1217
+ "integration_subscriptions",
1218
1218
  {
1219
1219
  id: uuid("id").primaryKey().defaultRandom(),
1220
- integrationId: text("integration_id").notNull(),
1220
+ connectionId: text("connection_id").notNull(),
1221
1221
  adapter: text("adapter").notNull(),
1222
1222
  domain: text("domain").notNull(),
1223
1223
  externalRef: text("external_ref"),
@@ -1232,8 +1232,8 @@ var syncSubscriptions = pgTable(
1232
1232
  * successful run advances it.
1233
1233
  */
1234
1234
  cursor: jsonb("cursor").$type(),
1235
- lastSyncAt: timestamp("last_sync_at", { withTimezone: true }),
1236
- /** Runtime-enforced when `SYNC_MULTI_TENANT` is true; see SYNC-6. */
1235
+ lastIntegrationAt: timestamp("last_integration_at", { withTimezone: true }),
1236
+ /** Runtime-enforced when `INTEGRATION_MULTI_TENANT` is true; see SYNC-6. */
1237
1237
  tenantId: text("tenant_id"),
1238
1238
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
1239
1239
  updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
@@ -1242,31 +1242,31 @@ var syncSubscriptions = pgTable(
1242
1242
  /**
1243
1243
  * Composite uniqueness per the epic shape. `external_ref` is nullable;
1244
1244
  * Postgres treats NULLs as distinct in a UNIQUE constraint, which means
1245
- * two rows with the same `(integration_id, adapter, domain)` and NULL
1245
+ * two rows with the same `(connection_id, adapter, domain)` and NULL
1246
1246
  * external_ref are allowed. That's intentional — a subscription with
1247
1247
  * NULL external_ref covers the full domain, and duplicates there would
1248
1248
  * be a consumer-layer modeling issue, not a schema concern.
1249
1249
  */
1250
- uqSyncSubscriptionTuple: uniqueIndex("uq_sync_subscriptions_tuple").on(
1251
- t.integrationId,
1250
+ uqIntegrationSubscriptionTuple: uniqueIndex("uq_integration_subscriptions_tuple").on(
1251
+ t.connectionId,
1252
1252
  t.adapter,
1253
1253
  t.domain,
1254
1254
  t.externalRef
1255
1255
  ),
1256
1256
  /** Scheduling query: list enabled subscriptions ordered by staleness. */
1257
- idxSyncSubscriptionsEnabledLastSync: index(
1258
- "idx_sync_subscriptions_enabled_last_sync"
1259
- ).on(t.enabled, t.lastSyncAt)
1257
+ idxIntegrationSubscriptionsEnabledLastIntegration: index(
1258
+ "idx_integration_subscriptions_enabled_last_integration"
1259
+ ).on(t.enabled, t.lastIntegrationAt)
1260
1260
  })
1261
1261
  );
1262
- var syncRuns = pgTable(
1263
- "sync_runs",
1262
+ var integrationRuns = pgTable(
1263
+ "integration_runs",
1264
1264
  {
1265
1265
  id: uuid("id").primaryKey().defaultRandom(),
1266
- subscriptionId: uuid("subscription_id").notNull().references(() => syncSubscriptions.id, { onDelete: "cascade" }),
1267
- direction: syncRunDirectionEnum("direction").notNull(),
1268
- action: syncRunActionEnum("action").notNull(),
1269
- status: syncRunStatusEnum("status").notNull().default("running"),
1266
+ subscriptionId: uuid("subscription_id").notNull().references(() => integrationSubscriptions.id, { onDelete: "cascade" }),
1267
+ direction: integrationRunDirectionEnum("direction").notNull(),
1268
+ action: integrationRunActionEnum("action").notNull(),
1269
+ status: integrationRunStatusEnum("status").notNull().default("running"),
1270
1270
  recordsFound: integer("records_found").notNull().default(0),
1271
1271
  recordsProcessed: integer("records_processed").notNull().default(0),
1272
1272
  cursorBefore: jsonb("cursor_before").$type(),
@@ -1275,31 +1275,31 @@ var syncRuns = pgTable(
1275
1275
  error: text("error"),
1276
1276
  startedAt: timestamp("started_at", { withTimezone: true }).notNull().defaultNow(),
1277
1277
  completedAt: timestamp("completed_at", { withTimezone: true }),
1278
- /** Runtime-enforced when `SYNC_MULTI_TENANT` is true; see SYNC-6. */
1278
+ /** Runtime-enforced when `INTEGRATION_MULTI_TENANT` is true; see SYNC-6. */
1279
1279
  tenantId: text("tenant_id")
1280
1280
  },
1281
1281
  (t) => ({
1282
1282
  /** Timeline read: "most recent runs for this subscription". */
1283
- idxSyncRunsSubscriptionStartedAt: index(
1284
- "idx_sync_runs_subscription_started_at"
1283
+ idxIntegrationRunsSubscriptionStartedAt: index(
1284
+ "idx_integration_runs_subscription_started_at"
1285
1285
  ).on(t.subscriptionId, t.startedAt),
1286
1286
  /** Stale-run sweeper: "runs that started > N minutes ago and are still running". */
1287
- idxSyncRunsStatusStartedAt: index("idx_sync_runs_status_started_at").on(
1287
+ idxIntegrationRunsStatusStartedAt: index("idx_integration_runs_status_started_at").on(
1288
1288
  t.status,
1289
1289
  t.startedAt
1290
1290
  )
1291
1291
  })
1292
1292
  );
1293
- var syncRunItems = pgTable(
1294
- "sync_run_items",
1293
+ var integrationRunItems = pgTable(
1294
+ "integration_run_items",
1295
1295
  {
1296
1296
  id: uuid("id").primaryKey().defaultRandom(),
1297
- syncRunId: uuid("sync_run_id").notNull().references(() => syncRuns.id, { onDelete: "cascade" }),
1297
+ integrationRunId: uuid("integration_run_id").notNull().references(() => integrationRuns.id, { onDelete: "cascade" }),
1298
1298
  entityType: text("entity_type").notNull(),
1299
1299
  externalId: text("external_id").notNull(),
1300
1300
  localId: text("local_id"),
1301
- operation: syncRunItemOperationEnum("operation").notNull(),
1302
- status: syncRunItemStatusEnum("status").notNull(),
1301
+ operation: integrationRunItemOperationEnum("operation").notNull(),
1302
+ status: integrationRunItemStatusEnum("status").notNull(),
1303
1303
  /**
1304
1304
  * Structured per-field diff — ADR-0003 shape enforced by
1305
1305
  * `FieldDiffSchema.parse` at the recorder service layer.
@@ -1313,23 +1313,23 @@ var syncRunItems = pgTable(
1313
1313
  title: text("title"),
1314
1314
  error: text("error"),
1315
1315
  createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
1316
- /** Runtime-enforced when `SYNC_MULTI_TENANT` is true; see SYNC-6. */
1316
+ /** Runtime-enforced when `INTEGRATION_MULTI_TENANT` is true; see SYNC-6. */
1317
1317
  tenantId: text("tenant_id")
1318
1318
  },
1319
1319
  (t) => ({
1320
1320
  /** Ordered timeline within a run. */
1321
- idxSyncRunItemsRunCreatedAt: index("idx_sync_run_items_run_created_at").on(
1322
- t.syncRunId,
1321
+ idxIntegrationRunItemsRunCreatedAt: index("idx_integration_run_items_run_created_at").on(
1322
+ t.integrationRunId,
1323
1323
  t.createdAt
1324
1324
  ),
1325
- /** Per-record history: "every sync that touched opportunity/$extId". */
1326
- idxSyncRunItemsEntityExternal: index(
1327
- "idx_sync_run_items_entity_external"
1325
+ /** Per-record history: "every integration that touched opportunity/$extId". */
1326
+ idxIntegrationRunItemsEntityExternal: index(
1327
+ "idx_integration_run_items_entity_external"
1328
1328
  ).on(t.entityType, t.externalId)
1329
1329
  })
1330
1330
  );
1331
1331
 
1332
- // runtime/subsystems/sync/sync-cursor-store.memory-backend.ts
1332
+ // runtime/subsystems/integration/integration-cursor-store.memory-backend.ts
1333
1333
  import { Injectable } from "@nestjs/common";
1334
1334
  var MemoryCursorStore = class {
1335
1335
  /**
@@ -1340,7 +1340,7 @@ var MemoryCursorStore = class {
1340
1340
  /**
1341
1341
  * Seedable subscription metadata for `listAll` — the memory backend
1342
1342
  * stores only `subscriptionId → cursor` in its write path, so the
1343
- * snapshot shape (`integrationId`, `adapter`, `domain`, `externalRef`,
1343
+ * snapshot shape (`connectionId`, `adapter`, `domain`, `externalRef`,
1344
1344
  * timestamps) has no natural source without test seeding. Tests populate
1345
1345
  * this map; unseeded entries get empty-string metadata and `new Date(0)`
1346
1346
  * timestamps so the shape stays stable. Production paths go through the
@@ -1360,12 +1360,12 @@ var MemoryCursorStore = class {
1360
1360
  const meta = this.subscriptions.get(subscriptionId);
1361
1361
  snapshots.push({
1362
1362
  subscriptionId,
1363
- integrationId: meta?.integrationId ?? "",
1363
+ connectionId: meta?.connectionId ?? "",
1364
1364
  adapter: meta?.adapter ?? "",
1365
1365
  domain: meta?.domain ?? "",
1366
1366
  externalRef: meta?.externalRef ?? null,
1367
1367
  cursor: cursor ?? null,
1368
- lastSyncAt: meta?.lastSyncAt ?? null,
1368
+ lastIntegrationAt: meta?.lastIntegrationAt ?? null,
1369
1369
  updatedAt: meta?.updatedAt ?? /* @__PURE__ */ new Date(0),
1370
1370
  tenantId: null
1371
1371
  });
@@ -1384,7 +1384,7 @@ MemoryCursorStore = __decorateClass([
1384
1384
  Injectable()
1385
1385
  ], MemoryCursorStore);
1386
1386
 
1387
- // runtime/subsystems/sync/sync-run-recorder.memory-backend.ts
1387
+ // runtime/subsystems/integration/integration-run-recorder.memory-backend.ts
1388
1388
  import { Injectable as Injectable2 } from "@nestjs/common";
1389
1389
  var MemoryRunRecorder = class {
1390
1390
  /**
@@ -1393,14 +1393,14 @@ var MemoryRunRecorder = class {
1393
1393
  */
1394
1394
  runs = /* @__PURE__ */ new Map();
1395
1395
  /**
1396
- * Items keyed by `sync_run_id`, array order matches insertion order —
1397
- * mirrors the timeline the `(sync_run_id, created_at)` index produces
1396
+ * Items keyed by `integration_run_id`, array order matches insertion order —
1397
+ * mirrors the timeline the `(integration_run_id, created_at)` index produces
1398
1398
  * in Postgres.
1399
1399
  */
1400
1400
  items = /* @__PURE__ */ new Map();
1401
1401
  /**
1402
1402
  * Seedable subscription metadata — tests populate this to make
1403
- * `listRecent` return meaningful `integrationId` values. The memory
1403
+ * `listRecent` return meaningful `connectionId` values. The memory
1404
1404
  * backend doesn't track subscriptions on its own (only runs + items), so
1405
1405
  * this map is the intentional extension point: tests write entries for
1406
1406
  * the subscription ids they use, production code never touches it.
@@ -1429,10 +1429,10 @@ var MemoryRunRecorder = class {
1429
1429
  }
1430
1430
  async recordItem(input) {
1431
1431
  FieldDiffSchema.parse(input.changedFields);
1432
- const bucket = this.items.get(input.syncRunId);
1432
+ const bucket = this.items.get(input.integrationRunId);
1433
1433
  if (!bucket) {
1434
1434
  throw new Error(
1435
- `MemoryRunRecorder.recordItem: no run started for id '${input.syncRunId}'. Call startRun(...) first.`
1435
+ `MemoryRunRecorder.recordItem: no run started for id '${input.integrationRunId}'. Call startRun(...) first.`
1436
1436
  );
1437
1437
  }
1438
1438
  bucket.push(input);
@@ -1458,10 +1458,10 @@ var MemoryRunRecorder = class {
1458
1458
  return filtered.sort((a, b) => b.startedAt.getTime() - a.startedAt.getTime()).slice(0, limit).map((r) => ({
1459
1459
  id: r.id,
1460
1460
  subscriptionId: r.subscriptionId,
1461
- // integrationId is only knowable if the test seeded subscriptions
1461
+ // connectionId is only knowable if the test seeded subscriptions
1462
1462
  // metadata; empty string otherwise. The Drizzle backend resolves
1463
1463
  // it via JOIN, which is the production path.
1464
- integrationId: this.subscriptions.get(r.subscriptionId)?.integrationId ?? "",
1464
+ connectionId: this.subscriptions.get(r.subscriptionId)?.connectionId ?? "",
1465
1465
  status: r.status,
1466
1466
  startedAt: r.startedAt,
1467
1467
  completedAt: r.completedAt,
@@ -1489,7 +1489,7 @@ MemoryRunRecorder = __decorateClass([
1489
1489
  Injectable2()
1490
1490
  ], MemoryRunRecorder);
1491
1491
 
1492
- // runtime/subsystems/sync/deep-equal.differ.ts
1492
+ // runtime/subsystems/integration/deep-equal.differ.ts
1493
1493
  import { Injectable as Injectable3 } from "@nestjs/common";
1494
1494
  var DEFAULT_IGNORE_FIELDS = /* @__PURE__ */ new Set([
1495
1495
  "id",
@@ -1588,9 +1588,9 @@ function deepEqualObject(a, b) {
1588
1588
  return true;
1589
1589
  }
1590
1590
 
1591
- // runtime/subsystems/sync/execute-sync.use-case.ts
1591
+ // runtime/subsystems/integration/execute-integration.use-case.ts
1592
1592
  import { Inject, Injectable as Injectable4, Logger, Optional } from "@nestjs/common";
1593
- var ExecuteSyncUseCase = class {
1593
+ var ExecuteIntegrationUseCase = class {
1594
1594
  constructor(source, cursors, differ, sink, recorder, multiTenant = false) {
1595
1595
  this.source = source;
1596
1596
  this.cursors = cursors;
@@ -1605,7 +1605,7 @@ var ExecuteSyncUseCase = class {
1605
1605
  sink;
1606
1606
  recorder;
1607
1607
  multiTenant;
1608
- logger = new Logger(ExecuteSyncUseCase.name);
1608
+ logger = new Logger(ExecuteIntegrationUseCase.name);
1609
1609
  async execute(input) {
1610
1610
  assertTenantId(input.tenantId, {
1611
1611
  multiTenant: this.multiTenant,
@@ -1640,10 +1640,10 @@ var ExecuteSyncUseCase = class {
1640
1640
  recordsFailed++;
1641
1641
  const message = err instanceof Error ? err.message : String(err);
1642
1642
  this.logger.warn(
1643
- `sync item failed: subscription=${input.subscription.id} externalId=${change.externalId}: ${message}`
1643
+ `integration item failed: subscription=${input.subscription.id} externalId=${change.externalId}: ${message}`
1644
1644
  );
1645
1645
  await this.recorder.recordItem({
1646
- syncRunId: runId,
1646
+ integrationRunId: runId,
1647
1647
  entityType: input.subscription.domain,
1648
1648
  externalId: change.externalId,
1649
1649
  operation: change.operation === "deleted" ? "deleted" : "updated",
@@ -1666,7 +1666,7 @@ var ExecuteSyncUseCase = class {
1666
1666
  status = "failed";
1667
1667
  runError = err instanceof Error ? err.message : String(err);
1668
1668
  this.logger.error(
1669
- `sync source failed: subscription=${input.subscription.id}: ${runError}`
1669
+ `integration source failed: subscription=${input.subscription.id}: ${runError}`
1670
1670
  );
1671
1671
  }
1672
1672
  if (cursorAdvanced && latestCursor !== null && latestCursor !== void 0) {
@@ -1711,7 +1711,7 @@ var ExecuteSyncUseCase = class {
1711
1711
  change.externalId
1712
1712
  );
1713
1713
  await this.recorder.recordItem({
1714
- syncRunId: runId,
1714
+ integrationRunId: runId,
1715
1715
  entityType: input.subscription.domain,
1716
1716
  externalId: change.externalId,
1717
1717
  localId: result?.id ?? null,
@@ -1734,7 +1734,7 @@ var ExecuteSyncUseCase = class {
1734
1734
  if (diff === "noop") {
1735
1735
  if (!this.sink.reprojectsOnNoop) {
1736
1736
  await this.recorder.recordItem({
1737
- syncRunId: runId,
1737
+ integrationRunId: runId,
1738
1738
  entityType: input.subscription.domain,
1739
1739
  externalId: change.externalId,
1740
1740
  localId: null,
@@ -1751,7 +1751,7 @@ var ExecuteSyncUseCase = class {
1751
1751
  input.provider
1752
1752
  );
1753
1753
  await this.recorder.recordItem({
1754
- syncRunId: runId,
1754
+ integrationRunId: runId,
1755
1755
  entityType: input.subscription.domain,
1756
1756
  externalId: change.externalId,
1757
1757
  localId: noopLocalId,
@@ -1768,7 +1768,7 @@ var ExecuteSyncUseCase = class {
1768
1768
  input.provider
1769
1769
  );
1770
1770
  await this.recorder.recordItem({
1771
- syncRunId: runId,
1771
+ integrationRunId: runId,
1772
1772
  entityType: input.subscription.domain,
1773
1773
  externalId: change.externalId,
1774
1774
  localId,
@@ -1779,25 +1779,25 @@ var ExecuteSyncUseCase = class {
1779
1779
  });
1780
1780
  }
1781
1781
  };
1782
- ExecuteSyncUseCase = __decorateClass([
1782
+ ExecuteIntegrationUseCase = __decorateClass([
1783
1783
  Injectable4(),
1784
- __decorateParam(0, Inject(SYNC_CHANGE_SOURCE)),
1785
- __decorateParam(1, Inject(SYNC_CURSOR_STORE)),
1786
- __decorateParam(2, Inject(SYNC_FIELD_DIFFER)),
1787
- __decorateParam(3, Inject(SYNC_SINK)),
1788
- __decorateParam(4, Inject(SYNC_RUN_RECORDER)),
1784
+ __decorateParam(0, Inject(INTEGRATION_CHANGE_SOURCE)),
1785
+ __decorateParam(1, Inject(INTEGRATION_CURSOR_STORE)),
1786
+ __decorateParam(2, Inject(INTEGRATION_FIELD_DIFFER)),
1787
+ __decorateParam(3, Inject(INTEGRATION_SINK)),
1788
+ __decorateParam(4, Inject(INTEGRATION_RUN_RECORDER)),
1789
1789
  __decorateParam(5, Optional()),
1790
- __decorateParam(5, Inject(SYNC_MULTI_TENANT))
1791
- ], ExecuteSyncUseCase);
1790
+ __decorateParam(5, Inject(INTEGRATION_MULTI_TENANT))
1791
+ ], ExecuteIntegrationUseCase);
1792
1792
 
1793
- // runtime/subsystems/sync/sync-cursor-store.drizzle-backend.ts
1793
+ // runtime/subsystems/integration/integration-cursor-store.drizzle-backend.ts
1794
1794
  import { Inject as Inject2, Injectable as Injectable5, Optional as Optional2 } from "@nestjs/common";
1795
1795
  import { and, desc, eq } from "drizzle-orm";
1796
1796
 
1797
1797
  // runtime/constants/tokens.ts
1798
1798
  var DRIZZLE = "DRIZZLE";
1799
1799
 
1800
- // runtime/subsystems/sync/sync-cursor-store.drizzle-backend.ts
1800
+ // runtime/subsystems/integration/integration-cursor-store.drizzle-backend.ts
1801
1801
  var PostgresCursorStore = class {
1802
1802
  constructor(db, multiTenant) {
1803
1803
  this.db = db;
@@ -1807,15 +1807,15 @@ var PostgresCursorStore = class {
1807
1807
  multiTenant;
1808
1808
  async get(subscriptionId, tenantId) {
1809
1809
  const where = this.buildWhere(subscriptionId, tenantId, "cursor.get");
1810
- const rows = await this.db.select({ cursor: syncSubscriptions.cursor }).from(syncSubscriptions).where(where).limit(1);
1810
+ const rows = await this.db.select({ cursor: integrationSubscriptions.cursor }).from(integrationSubscriptions).where(where).limit(1);
1811
1811
  if (rows.length === 0) return null;
1812
1812
  return rows[0]?.cursor ?? null;
1813
1813
  }
1814
1814
  async put(subscriptionId, cursor, tenantId) {
1815
1815
  const where = this.buildWhere(subscriptionId, tenantId, "cursor.put");
1816
- await this.db.update(syncSubscriptions).set({
1816
+ await this.db.update(integrationSubscriptions).set({
1817
1817
  cursor,
1818
- lastSyncAt: /* @__PURE__ */ new Date(),
1818
+ lastIntegrationAt: /* @__PURE__ */ new Date(),
1819
1819
  updatedAt: /* @__PURE__ */ new Date()
1820
1820
  }).where(where);
1821
1821
  }
@@ -1824,26 +1824,26 @@ var PostgresCursorStore = class {
1824
1824
  multiTenant: this.multiTenant,
1825
1825
  operation: "cursor.listAll"
1826
1826
  });
1827
- const where = this.multiTenant ? eq(syncSubscriptions.tenantId, tenantId) : void 0;
1827
+ const where = this.multiTenant ? eq(integrationSubscriptions.tenantId, tenantId) : void 0;
1828
1828
  const rows = await this.db.select({
1829
- id: syncSubscriptions.id,
1830
- integrationId: syncSubscriptions.integrationId,
1831
- adapter: syncSubscriptions.adapter,
1832
- domain: syncSubscriptions.domain,
1833
- externalRef: syncSubscriptions.externalRef,
1834
- cursor: syncSubscriptions.cursor,
1835
- lastSyncAt: syncSubscriptions.lastSyncAt,
1836
- updatedAt: syncSubscriptions.updatedAt,
1837
- tenantId: syncSubscriptions.tenantId
1838
- }).from(syncSubscriptions).where(where).orderBy(desc(syncSubscriptions.updatedAt));
1829
+ id: integrationSubscriptions.id,
1830
+ connectionId: integrationSubscriptions.connectionId,
1831
+ adapter: integrationSubscriptions.adapter,
1832
+ domain: integrationSubscriptions.domain,
1833
+ externalRef: integrationSubscriptions.externalRef,
1834
+ cursor: integrationSubscriptions.cursor,
1835
+ lastIntegrationAt: integrationSubscriptions.lastIntegrationAt,
1836
+ updatedAt: integrationSubscriptions.updatedAt,
1837
+ tenantId: integrationSubscriptions.tenantId
1838
+ }).from(integrationSubscriptions).where(where).orderBy(desc(integrationSubscriptions.updatedAt));
1839
1839
  return rows.map((row) => ({
1840
1840
  subscriptionId: row.id,
1841
- integrationId: row.integrationId,
1841
+ connectionId: row.connectionId,
1842
1842
  adapter: row.adapter,
1843
1843
  domain: row.domain,
1844
1844
  externalRef: row.externalRef,
1845
1845
  cursor: row.cursor ?? null,
1846
- lastSyncAt: row.lastSyncAt,
1846
+ lastIntegrationAt: row.lastIntegrationAt,
1847
1847
  updatedAt: row.updatedAt,
1848
1848
  tenantId: row.tenantId
1849
1849
  }));
@@ -1860,24 +1860,24 @@ var PostgresCursorStore = class {
1860
1860
  });
1861
1861
  if (this.multiTenant) {
1862
1862
  return and(
1863
- eq(syncSubscriptions.id, subscriptionId),
1864
- eq(syncSubscriptions.tenantId, tenantId)
1863
+ eq(integrationSubscriptions.id, subscriptionId),
1864
+ eq(integrationSubscriptions.tenantId, tenantId)
1865
1865
  );
1866
1866
  }
1867
- return eq(syncSubscriptions.id, subscriptionId);
1867
+ return eq(integrationSubscriptions.id, subscriptionId);
1868
1868
  }
1869
1869
  };
1870
1870
  PostgresCursorStore = __decorateClass([
1871
1871
  Injectable5(),
1872
1872
  __decorateParam(0, Inject2(DRIZZLE)),
1873
1873
  __decorateParam(1, Optional2()),
1874
- __decorateParam(1, Inject2(SYNC_MULTI_TENANT))
1874
+ __decorateParam(1, Inject2(INTEGRATION_MULTI_TENANT))
1875
1875
  ], PostgresCursorStore);
1876
1876
 
1877
- // runtime/subsystems/sync/sync-run-recorder.drizzle-backend.ts
1877
+ // runtime/subsystems/integration/integration-run-recorder.drizzle-backend.ts
1878
1878
  import { Inject as Inject3, Injectable as Injectable6, Optional as Optional3 } from "@nestjs/common";
1879
1879
  import { and as and2, desc as desc2, eq as eq2 } from "drizzle-orm";
1880
- var DrizzleSyncRunRecorder = class {
1880
+ var DrizzleIntegrationRunRecorder = class {
1881
1881
  constructor(db, multiTenant) {
1882
1882
  this.db = db;
1883
1883
  this.multiTenant = multiTenant ?? false;
@@ -1889,17 +1889,17 @@ var DrizzleSyncRunRecorder = class {
1889
1889
  multiTenant: this.multiTenant,
1890
1890
  operation: "startRun"
1891
1891
  });
1892
- const rows = await this.db.insert(syncRuns).values({
1892
+ const rows = await this.db.insert(integrationRuns).values({
1893
1893
  subscriptionId: input.subscriptionId,
1894
1894
  direction: input.direction,
1895
1895
  action: input.action,
1896
1896
  status: "running",
1897
1897
  cursorBefore: input.cursorBefore ?? null,
1898
1898
  tenantId: input.tenantId ?? null
1899
- }).returning({ id: syncRuns.id });
1899
+ }).returning({ id: integrationRuns.id });
1900
1900
  const id = rows[0]?.id;
1901
1901
  if (!id) {
1902
- throw new Error("DrizzleSyncRunRecorder: INSERT RETURNING produced no id");
1902
+ throw new Error("DrizzleIntegrationRunRecorder: INSERT RETURNING produced no id");
1903
1903
  }
1904
1904
  return { id };
1905
1905
  }
@@ -1909,8 +1909,8 @@ var DrizzleSyncRunRecorder = class {
1909
1909
  operation: "recordItem"
1910
1910
  });
1911
1911
  FieldDiffSchema.parse(input.changedFields);
1912
- await this.db.insert(syncRunItems).values({
1913
- syncRunId: input.syncRunId,
1912
+ await this.db.insert(integrationRunItems).values({
1913
+ integrationRunId: input.integrationRunId,
1914
1914
  entityType: input.entityType,
1915
1915
  externalId: input.externalId,
1916
1916
  localId: input.localId ?? null,
@@ -1929,29 +1929,29 @@ var DrizzleSyncRunRecorder = class {
1929
1929
  });
1930
1930
  const conditions = [];
1931
1931
  if (subscriptionId !== void 0) {
1932
- conditions.push(eq2(syncRuns.subscriptionId, subscriptionId));
1932
+ conditions.push(eq2(integrationRuns.subscriptionId, subscriptionId));
1933
1933
  }
1934
1934
  if (this.multiTenant) {
1935
- conditions.push(eq2(syncRuns.tenantId, tenantId));
1935
+ conditions.push(eq2(integrationRuns.tenantId, tenantId));
1936
1936
  }
1937
1937
  const where = conditions.length === 0 ? void 0 : conditions.length === 1 ? conditions[0] : and2(...conditions);
1938
1938
  const rows = await this.db.select({
1939
- id: syncRuns.id,
1940
- subscriptionId: syncRuns.subscriptionId,
1941
- integrationId: syncSubscriptions.integrationId,
1942
- status: syncRuns.status,
1943
- startedAt: syncRuns.startedAt,
1944
- completedAt: syncRuns.completedAt,
1945
- recordsProcessed: syncRuns.recordsProcessed,
1946
- tenantId: syncRuns.tenantId
1947
- }).from(syncRuns).innerJoin(
1948
- syncSubscriptions,
1949
- eq2(syncRuns.subscriptionId, syncSubscriptions.id)
1950
- ).where(where).orderBy(desc2(syncRuns.startedAt)).limit(limit);
1939
+ id: integrationRuns.id,
1940
+ subscriptionId: integrationRuns.subscriptionId,
1941
+ connectionId: integrationSubscriptions.connectionId,
1942
+ status: integrationRuns.status,
1943
+ startedAt: integrationRuns.startedAt,
1944
+ completedAt: integrationRuns.completedAt,
1945
+ recordsProcessed: integrationRuns.recordsProcessed,
1946
+ tenantId: integrationRuns.tenantId
1947
+ }).from(integrationRuns).innerJoin(
1948
+ integrationSubscriptions,
1949
+ eq2(integrationRuns.subscriptionId, integrationSubscriptions.id)
1950
+ ).where(where).orderBy(desc2(integrationRuns.startedAt)).limit(limit);
1951
1951
  return rows.map((row) => ({
1952
1952
  id: row.id,
1953
1953
  subscriptionId: row.subscriptionId,
1954
- integrationId: row.integrationId,
1954
+ connectionId: row.connectionId,
1955
1955
  status: row.status,
1956
1956
  startedAt: row.startedAt,
1957
1957
  completedAt: row.completedAt,
@@ -1960,7 +1960,7 @@ var DrizzleSyncRunRecorder = class {
1960
1960
  }));
1961
1961
  }
1962
1962
  async completeRun(runId, input) {
1963
- await this.db.update(syncRuns).set({
1963
+ await this.db.update(integrationRuns).set({
1964
1964
  status: input.status,
1965
1965
  recordsFound: input.recordsFound,
1966
1966
  recordsProcessed: input.recordsProcessed,
@@ -1968,27 +1968,27 @@ var DrizzleSyncRunRecorder = class {
1968
1968
  durationMs: input.durationMs,
1969
1969
  error: input.error ?? null,
1970
1970
  completedAt: /* @__PURE__ */ new Date()
1971
- }).where(eq2(syncRuns.id, runId));
1971
+ }).where(eq2(integrationRuns.id, runId));
1972
1972
  }
1973
1973
  };
1974
- DrizzleSyncRunRecorder = __decorateClass([
1974
+ DrizzleIntegrationRunRecorder = __decorateClass([
1975
1975
  Injectable6(),
1976
1976
  __decorateParam(0, Inject3(DRIZZLE)),
1977
1977
  __decorateParam(1, Optional3()),
1978
- __decorateParam(1, Inject3(SYNC_MULTI_TENANT))
1979
- ], DrizzleSyncRunRecorder);
1978
+ __decorateParam(1, Inject3(INTEGRATION_MULTI_TENANT))
1979
+ ], DrizzleIntegrationRunRecorder);
1980
1980
 
1981
- // runtime/subsystems/sync/sync.module.ts
1981
+ // runtime/subsystems/integration/integration.module.ts
1982
1982
  import { Module } from "@nestjs/common";
1983
- var SyncModule = class {
1983
+ var IntegrationModule = class {
1984
1984
  static forRoot(options) {
1985
1985
  const multiTenant = options.multiTenant ?? false;
1986
1986
  const sharedProviders = [
1987
- { provide: SYNC_MODULE_OPTIONS, useValue: options },
1988
- { provide: SYNC_MULTI_TENANT, useValue: multiTenant },
1987
+ { provide: INTEGRATION_MODULE_OPTIONS, useValue: options },
1988
+ { provide: INTEGRATION_MULTI_TENANT, useValue: multiTenant },
1989
1989
  // Default differ — consumers can override by binding a different
1990
- // `IFieldDiffer<T>` to `SYNC_FIELD_DIFFER` in their feature module.
1991
- { provide: SYNC_FIELD_DIFFER, useValue: new DeepEqualDiffer() }
1990
+ // `IFieldDiffer<T>` to `INTEGRATION_FIELD_DIFFER` in their feature module.
1991
+ { provide: INTEGRATION_FIELD_DIFFER, useValue: new DeepEqualDiffer() }
1992
1992
  ];
1993
1993
  const backendProviders = options.backend === "memory" ? [
1994
1994
  // Wired as singletons via `useValue` so tests can pull
@@ -1996,38 +1996,38 @@ var SyncModule = class {
1996
1996
  // direct assertions. Matches JOB-4 / MemoryJobStore shape.
1997
1997
  { provide: MemoryCursorStore, useValue: new MemoryCursorStore() },
1998
1998
  {
1999
- provide: SYNC_CURSOR_STORE,
1999
+ provide: INTEGRATION_CURSOR_STORE,
2000
2000
  useExisting: MemoryCursorStore
2001
2001
  },
2002
2002
  { provide: MemoryRunRecorder, useValue: new MemoryRunRecorder() },
2003
2003
  {
2004
- provide: SYNC_RUN_RECORDER,
2004
+ provide: INTEGRATION_RUN_RECORDER,
2005
2005
  useExisting: MemoryRunRecorder
2006
2006
  }
2007
2007
  ] : [
2008
2008
  // Drizzle backends — injected with DRIZZLE (provided by the
2009
- // consumer's DrizzleModule) + the SYNC_MULTI_TENANT flag
2009
+ // consumer's DrizzleModule) + the INTEGRATION_MULTI_TENANT flag
2010
2010
  // we bound above.
2011
- { provide: SYNC_CURSOR_STORE, useClass: PostgresCursorStore },
2012
- { provide: SYNC_RUN_RECORDER, useClass: DrizzleSyncRunRecorder }
2011
+ { provide: INTEGRATION_CURSOR_STORE, useClass: PostgresCursorStore },
2012
+ { provide: INTEGRATION_RUN_RECORDER, useClass: DrizzleIntegrationRunRecorder }
2013
2013
  ];
2014
2014
  return {
2015
- module: SyncModule,
2015
+ module: IntegrationModule,
2016
2016
  global: true,
2017
2017
  providers: [...sharedProviders, ...backendProviders],
2018
2018
  exports: [
2019
- SYNC_MODULE_OPTIONS,
2020
- SYNC_MULTI_TENANT,
2021
- SYNC_FIELD_DIFFER,
2022
- SYNC_CURSOR_STORE,
2023
- SYNC_RUN_RECORDER
2019
+ INTEGRATION_MODULE_OPTIONS,
2020
+ INTEGRATION_MULTI_TENANT,
2021
+ INTEGRATION_FIELD_DIFFER,
2022
+ INTEGRATION_CURSOR_STORE,
2023
+ INTEGRATION_RUN_RECORDER
2024
2024
  ]
2025
2025
  };
2026
2026
  }
2027
2027
  };
2028
- SyncModule = __decorateClass([
2028
+ IntegrationModule = __decorateClass([
2029
2029
  Module({})
2030
- ], SyncModule);
2030
+ ], IntegrationModule);
2031
2031
 
2032
2032
  // src/schema/entity-definition.schema.ts
2033
2033
  var FieldTypeSchema = z3.enum([
@@ -2335,21 +2335,21 @@ var AnyQueryDeclarationSchema = z3.union([
2335
2335
  SearchQueryDeclarationSchema,
2336
2336
  QueryDeclarationSchema
2337
2337
  ]);
2338
- var SyncDirectionSchema = z3.enum([
2338
+ var IntegrationDirectionSchema = z3.enum([
2339
2339
  "inbound",
2340
2340
  "outbound",
2341
2341
  "bidirectional"
2342
2342
  ]);
2343
- var ProviderSyncSchema = z3.object({
2343
+ var ProviderIntegrationSchema = z3.object({
2344
2344
  remote_entity: z3.string(),
2345
- direction: SyncDirectionSchema,
2345
+ direction: IntegrationDirectionSchema,
2346
2346
  cdc: z3.boolean().optional().default(false),
2347
2347
  field_mapping: z3.record(z3.string(), z3.string()).optional(),
2348
2348
  read_only_fields: z3.array(z3.string()).optional()
2349
2349
  });
2350
- var SyncConfigSchema = z3.object({
2350
+ var IntegrationConfigSchema = z3.object({
2351
2351
  electric: z3.boolean().optional().default(false),
2352
- providers: z3.record(z3.string(), ProviderSyncSchema).optional()
2352
+ providers: z3.record(z3.string(), ProviderIntegrationSchema).optional()
2353
2353
  });
2354
2354
  var EventDeclarationSchema = z3.object({
2355
2355
  name: z3.string().regex(/^[a-z][a-z0-9_]*$/, "Event name must be snake_case"),
@@ -2449,22 +2449,52 @@ var EntityDefinitionSchema = z3.object({
2449
2449
  // v2: Declarative query generation (ADR-005)
2450
2450
  // Generates repository + service + use case methods from declarations
2451
2451
  queries: z3.array(AnyQueryDeclarationSchema).optional(),
2452
- // v2: Integration sync configuration (CODEGEN-EVOLUTION-PLAN Phase 2)
2453
- // Electric SQL + provider sync (Salesforce, HubSpot, etc.)
2454
- sync: SyncConfigSchema.optional(),
2452
+ // v2: Integration integration configuration (CODEGEN-EVOLUTION-PLAN Phase 2)
2453
+ // Electric SQL + provider integration (Salesforce, HubSpot, etc.)
2454
+ integration: IntegrationConfigSchema.optional(),
2455
2455
  // ADR-033.1: Provider-keyed change-source detection.
2456
2456
  //
2457
2457
  // Map of provider name → DetectionConfig. Single-provider entities use
2458
2458
  // a one-key map; multi-provider entities list each provider as a
2459
2459
  // separate key. Each value is an independent `DetectionConfig` (its
2460
2460
  // own mode/cursor/mapping/filters) sourced from the canonical schema
2461
- // in `runtime/subsystems/sync` so this validator and the runtime
2461
+ // in `runtime/subsystems/integration` so this validator and the runtime
2462
2462
  // parser stay in lockstep.
2463
2463
  //
2464
2464
  // Within-file cross-check (ADR-033.1 §6): every key here must also
2465
- // appear in `sync.providers` — see the superRefine on
2465
+ // appear in `integration.providers` — see the superRefine on
2466
2466
  // `EntityDefinitionSchema` below.
2467
2467
  detection: z3.record(z3.string(), DetectionConfigSchema).optional(),
2468
+ // RFC-0001 §1/§8: the integration *surface* this entity belongs to
2469
+ // (e.g. 'calendar', 'mail', 'crm'). Surfaces span provider contexts
2470
+ // (ADR-0006) — one Google OAuth feeds calendar+mail+transcript. The union
2471
+ // of `surface:` values across all entity YAML is the closed set that a
2472
+ // provider's `surfaces:` must be a subset of (cross-checked in
2473
+ // src/parser/validate-providers.ts). Optional: entities without an
2474
+ // integration surface omit it. The surface-package *emission* convention
2475
+ // is Track C (#329); this field is only the declarative input both tracks
2476
+ // read.
2477
+ surface: z3.string().optional(),
2478
+ // Bounded-context declaration (ADR-0004) — "which bounded context this
2479
+ // entity belongs to". This is the DURABLE decision; it is a plain
2480
+ // bounded-context slug, NOT a folder knob. Different features consume it:
2481
+ //
2482
+ // - #403 (this PR, the FIRST consumer): drives the generated code's
2483
+ // module output folder. clean-lite-ps nests the entity's module under
2484
+ // `<modules>/<context>/<entity>/` so same-context entities group
2485
+ // together; untagged entities stay flat (`<modules>/<entity>/`).
2486
+ // - ADR-0004 (deferred): a later `naming: prefix | schema` knob reads
2487
+ // this SAME field to drive the Postgres physical layout —
2488
+ // `prefix` → `pgTable('<context>__<table>')`, then the flip to
2489
+ // `schema` → `pgSchema('<context>').table('<table>')`. NOT wired here;
2490
+ // #403 makes no table/column/schema changes.
2491
+ //
2492
+ // Sibling to `surface:` and orthogonal to it (ADR-0006): context = model
2493
+ // cohesion (which domain), surface = vendor composition (which integration).
2494
+ context: z3.string().regex(
2495
+ /^[a-z][a-z0-9_]*$/,
2496
+ "context must be lowercase snake_case (e.g. 'integration')"
2497
+ ).optional(),
2468
2498
  // v2: Domain event declarations (CODEGEN-EVOLUTION-PLAN Phase 2)
2469
2499
  // Generates typed event classes, handlers, and queue registration
2470
2500
  events: z3.array(EventDeclarationSchema).optional(),
@@ -2483,7 +2513,18 @@ var EntityDefinitionSchema = z3.object({
2483
2513
  ).optional(),
2484
2514
  // v2: Analytics / semantic layer configuration
2485
2515
  // Cube.js measure packs, custom cube name, and metric definitions
2486
- analytics: AnalyticsBlockSchema.optional()
2516
+ analytics: AnalyticsBlockSchema.optional(),
2517
+ // Composite (multi-column) unique indexes (#356). Single-column uniqueness
2518
+ // is `unique: true` on the field itself; this declares constraints that
2519
+ // span 2+ columns, e.g. UNIQUE (conversation_id, sequence). Emitted as a
2520
+ // `uniqueIndex(...).on(...)` entry in the generated entity's pgTable
2521
+ // extra-config callback. `name` defaults to <table>_<col1>_<col2>_uniq.
2522
+ unique_indexes: z3.array(
2523
+ z3.object({
2524
+ fields: z3.array(z3.string()).min(2, "unique_indexes entries span 2+ columns \u2014 use `unique: true` on the field for single-column uniqueness"),
2525
+ name: z3.string().optional()
2526
+ }).strict()
2527
+ ).optional()
2487
2528
  }).strict().refine(
2488
2529
  (data) => !data.eav_value_table || typeof data.eav_definition_table === "string",
2489
2530
  {
@@ -2492,13 +2533,13 @@ var EntityDefinitionSchema = z3.object({
2492
2533
  }
2493
2534
  ).superRefine((entity, ctx) => {
2494
2535
  if (!entity.detection) return;
2495
- const declared = new Set(Object.keys(entity.sync?.providers ?? {}));
2536
+ const declared = new Set(Object.keys(entity.integration?.providers ?? {}));
2496
2537
  for (const provider of Object.keys(entity.detection)) {
2497
2538
  if (!declared.has(provider)) {
2498
2539
  ctx.addIssue({
2499
2540
  code: "custom",
2500
2541
  path: ["detection", provider],
2501
- message: `Provider '${provider}' used in detection: but not declared in sync.providers. Known providers: ${[...declared].join(", ")}`
2542
+ message: `Provider '${provider}' used in detection: but not declared in integration.providers. Known providers: ${[...declared].join(", ")}`
2502
2543
  });
2503
2544
  }
2504
2545
  }
@@ -2910,6 +2951,60 @@ var JunctionDefinitionSchema = z6.object({
2910
2951
  }
2911
2952
  );
2912
2953
 
2954
+ // src/schema/provider-definition.schema.ts
2955
+ import { z as z7 } from "zod";
2956
+ var IMPORT_REF_RE = /^[^#\s]+#[A-Za-z_$][A-Za-z0-9_$]*$/;
2957
+ var ImportRefSchema = z7.string().regex(
2958
+ IMPORT_REF_RE,
2959
+ "must be an 'import-path#Export' reference (e.g. '@app/foo/bar.strategy#BarStrategy')"
2960
+ );
2961
+ function parseImportRef(ref) {
2962
+ const hash = ref.indexOf("#");
2963
+ return { path: ref.slice(0, hash), exportName: ref.slice(hash + 1) };
2964
+ }
2965
+ var AuthTypeSchema = z7.enum(["oauth2", "api-key", "app-password"]);
2966
+ var AuthSchema = z7.object({
2967
+ type: AuthTypeSchema,
2968
+ // Class implementing the auth subsystem's strategy contract (ADR-031).
2969
+ // Pre-flight verified against a real export at codegen time.
2970
+ strategy: ImportRefSchema,
2971
+ // Required (and non-empty) iff type === 'oauth2'; see refine below.
2972
+ scopes: z7.array(z7.string()).optional()
2973
+ }).strict().refine(
2974
+ (a) => a.type !== "oauth2" || a.scopes !== void 0 && a.scopes.length > 0,
2975
+ {
2976
+ message: "auth.scopes is required and must be non-empty when auth.type is 'oauth2'",
2977
+ path: ["scopes"]
2978
+ }
2979
+ );
2980
+ var ClientSchema = z7.object({
2981
+ // API client class. Pre-flight verified against a real export.
2982
+ class: ImportRefSchema,
2983
+ base_url: z7.string().url("client.base_url must be an absolute URL")
2984
+ }).strict();
2985
+ var ProviderDefinitionSchema = z7.object({
2986
+ // Provider id — the canonical string used as detection: keys, audit rows,
2987
+ // subscription rows. kebab/lower; unique across definitions/providers/
2988
+ // (uniqueness is a cross-file check in validate-providers.ts).
2989
+ slug: z7.string().regex(
2990
+ /^[a-z][a-z0-9-]*$/,
2991
+ "slug must be kebab-case lower (e.g. 'google', 'hubspot')"
2992
+ ),
2993
+ display_name: z7.string().optional(),
2994
+ auth: AuthSchema,
2995
+ client: ClientSchema,
2996
+ // Surfaces this provider serves (ADR-0006: surfaces span contexts — one
2997
+ // Google OAuth feeds calendar+mail+transcript). Each must reference a real
2998
+ // `surface:` declared on some entity; that cross-check is in
2999
+ // validate-providers.ts. Non-empty enforced here.
3000
+ surfaces: z7.array(z7.string()).min(1, "surfaces must list at least one surface"),
3001
+ // Optional auth lifecycle hints consumed by provider-module emission (D2).
3002
+ // `refresh_behavior` is left as a free string in D1 — its domain firms up
3003
+ // when D2 consumes it; carrying it now keeps the YAML lossless.
3004
+ token_lifetime: z7.number().int().positive().optional(),
3005
+ refresh_behavior: z7.string().optional()
3006
+ }).strict();
3007
+
2913
3008
  // src/utils/yaml-loader.ts
2914
3009
  function loadEntityFromYaml(filePath) {
2915
3010
  if (!existsSync6(filePath)) {
@@ -2963,6 +3058,19 @@ function formatZodErrors(error) {
2963
3058
  return `${err.message} ${location}`;
2964
3059
  });
2965
3060
  }
3061
+ function loadEntitiesFromYaml(filePaths) {
3062
+ const successes = [];
3063
+ const failures = [];
3064
+ for (const filePath of filePaths) {
3065
+ const result = loadEntityFromYaml(filePath);
3066
+ if (result.success) {
3067
+ successes.push(result);
3068
+ } else {
3069
+ failures.push(result);
3070
+ }
3071
+ }
3072
+ return { successes, failures };
3073
+ }
2966
3074
  function loadRelationshipFromYaml(filePath) {
2967
3075
  if (!existsSync6(filePath)) {
2968
3076
  return {
@@ -3098,6 +3206,64 @@ function loadJunctionFromYaml(filePath) {
3098
3206
  filePath
3099
3207
  };
3100
3208
  }
3209
+ function loadProviderFromYaml(filePath) {
3210
+ if (!existsSync6(filePath)) {
3211
+ return {
3212
+ success: false,
3213
+ error: `File not found: ${filePath}`,
3214
+ filePath
3215
+ };
3216
+ }
3217
+ let content;
3218
+ try {
3219
+ content = readFileSync4(filePath, "utf-8");
3220
+ } catch (err) {
3221
+ return {
3222
+ success: false,
3223
+ error: `Failed to read file: ${filePath}`,
3224
+ details: [err instanceof Error ? err.message : String(err)],
3225
+ filePath
3226
+ };
3227
+ }
3228
+ let parsed;
3229
+ try {
3230
+ parsed = parseYaml(content);
3231
+ } catch (err) {
3232
+ return {
3233
+ success: false,
3234
+ error: `Invalid YAML syntax in ${filePath}`,
3235
+ details: [err instanceof Error ? err.message : String(err)],
3236
+ filePath
3237
+ };
3238
+ }
3239
+ const result = ProviderDefinitionSchema.safeParse(parsed);
3240
+ if (!result.success) {
3241
+ return {
3242
+ success: false,
3243
+ error: `Validation failed for ${filePath}`,
3244
+ details: formatZodErrors(result.error),
3245
+ filePath
3246
+ };
3247
+ }
3248
+ return {
3249
+ success: true,
3250
+ definition: result.data,
3251
+ filePath
3252
+ };
3253
+ }
3254
+ function loadProvidersFromYaml(filePaths) {
3255
+ const successes = [];
3256
+ const failures = [];
3257
+ for (const filePath of filePaths) {
3258
+ const result = loadProviderFromYaml(filePath);
3259
+ if (result.success) {
3260
+ successes.push(result);
3261
+ } else {
3262
+ failures.push(result);
3263
+ }
3264
+ }
3265
+ return { successes, failures };
3266
+ }
3101
3267
  function detectYamlType(filePath) {
3102
3268
  if (!existsSync6(filePath)) return "unknown";
3103
3269
  try {
@@ -3182,14 +3348,14 @@ function transformToEntity(result) {
3182
3348
  entity.relationships.set(name, relationship);
3183
3349
  }
3184
3350
  }
3185
- if (definition.sync) {
3186
- const syncDef = definition.sync;
3187
- const parsedSync = {
3188
- electric: syncDef.electric ?? false
3351
+ if (definition.integration) {
3352
+ const integrationDef = definition.integration;
3353
+ const parsedIntegration = {
3354
+ electric: integrationDef.electric ?? false
3189
3355
  };
3190
- if (syncDef.providers) {
3191
- parsedSync.providers = {};
3192
- for (const [providerName, providerDef] of Object.entries(syncDef.providers)) {
3356
+ if (integrationDef.providers) {
3357
+ parsedIntegration.providers = {};
3358
+ for (const [providerName, providerDef] of Object.entries(integrationDef.providers)) {
3193
3359
  const parsedProvider = {
3194
3360
  remoteEntity: providerDef.remote_entity,
3195
3361
  direction: providerDef.direction,
@@ -3201,10 +3367,10 @@ function transformToEntity(result) {
3201
3367
  if (providerDef.read_only_fields) {
3202
3368
  parsedProvider.readOnlyFields = providerDef.read_only_fields;
3203
3369
  }
3204
- parsedSync.providers[providerName] = parsedProvider;
3370
+ parsedIntegration.providers[providerName] = parsedProvider;
3205
3371
  }
3206
3372
  }
3207
- entity.sync = parsedSync;
3373
+ entity.integration = parsedIntegration;
3208
3374
  }
3209
3375
  if (definition.events) {
3210
3376
  entity.events = definition.events.map((ev) => ({
@@ -3477,6 +3643,160 @@ function resolveRelationshipReferences(relationshipDefs, entities) {
3477
3643
  return issues;
3478
3644
  }
3479
3645
 
3646
+ // src/parser/validate-providers.ts
3647
+ import { existsSync as existsSync7, readFileSync as readFileSync5, statSync as statSync3 } from "fs";
3648
+ import { isAbsolute, join as join8, resolve as resolve3 } from "path";
3649
+ import ts from "typescript";
3650
+ function collectEntitySurfaces(entities) {
3651
+ const surfaces = /* @__PURE__ */ new Set();
3652
+ for (const e of entities) {
3653
+ if (e.surface) surfaces.add(e.surface);
3654
+ }
3655
+ return surfaces;
3656
+ }
3657
+ function resolveImportRef(ref, opts) {
3658
+ const { path: path34, exportName } = parseImportRef(ref);
3659
+ const file = resolveModuleFile(path34, opts);
3660
+ if (!file) {
3661
+ return { status: "module-not-found", resolvedFrom: opts.sourceRoot };
3662
+ }
3663
+ const content = readFileSync5(file, "utf-8");
3664
+ const { names: names2, hasWildcard } = collectExportedNames(file, content);
3665
+ if (names2.has(exportName) || hasWildcard) {
3666
+ return { status: "ok", file };
3667
+ }
3668
+ return { status: "export-not-found", file };
3669
+ }
3670
+ function resolveModuleFile(importPath, opts) {
3671
+ let base = null;
3672
+ for (const [alias, target] of Object.entries(opts.aliases ?? {})) {
3673
+ if (importPath === alias || importPath.startsWith(`${alias}/`)) {
3674
+ const rest = importPath.slice(alias.length);
3675
+ base = join8(target, rest);
3676
+ break;
3677
+ }
3678
+ }
3679
+ if (base === null) {
3680
+ base = isAbsolute(importPath) ? importPath : resolve3(opts.sourceRoot, importPath);
3681
+ }
3682
+ const candidates = [
3683
+ base,
3684
+ `${base}.ts`,
3685
+ `${base}.tsx`,
3686
+ join8(base, "index.ts"),
3687
+ join8(base, "index.tsx")
3688
+ ];
3689
+ for (const c of candidates) {
3690
+ if (existsSync7(c) && statSync3(c).isFile()) return c;
3691
+ }
3692
+ return null;
3693
+ }
3694
+ function collectExportedNames(fileName, content) {
3695
+ const sf = ts.createSourceFile(
3696
+ fileName,
3697
+ content,
3698
+ ts.ScriptTarget.Latest,
3699
+ /* setParentNodes */
3700
+ true
3701
+ );
3702
+ const names2 = /* @__PURE__ */ new Set();
3703
+ let hasWildcard = false;
3704
+ const hasExportModifier = (node) => ts.canHaveModifiers(node) && (ts.getModifiers(node)?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false);
3705
+ sf.forEachChild((node) => {
3706
+ if (hasExportModifier(node)) {
3707
+ if ((ts.isClassDeclaration(node) || ts.isFunctionDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node) || ts.isEnumDeclaration(node) || ts.isModuleDeclaration(node)) && node.name && ts.isIdentifier(node.name)) {
3708
+ names2.add(node.name.text);
3709
+ } else if (ts.isVariableStatement(node)) {
3710
+ for (const decl of node.declarationList.declarations) {
3711
+ if (ts.isIdentifier(decl.name)) names2.add(decl.name.text);
3712
+ }
3713
+ }
3714
+ }
3715
+ if (ts.isExportDeclaration(node)) {
3716
+ if (!node.exportClause) {
3717
+ hasWildcard = true;
3718
+ } else if (ts.isNamedExports(node.exportClause)) {
3719
+ for (const el of node.exportClause.elements) names2.add(el.name.text);
3720
+ } else if (ts.isNamespaceExport(node.exportClause)) {
3721
+ names2.add(node.exportClause.name.text);
3722
+ }
3723
+ }
3724
+ });
3725
+ return { names: names2, hasWildcard };
3726
+ }
3727
+ function validateProviders(providers, opts) {
3728
+ const issues = [];
3729
+ const knownSurfaces = new Set(opts.entitySurfaces);
3730
+ const slugToFiles = /* @__PURE__ */ new Map();
3731
+ for (const p of providers) {
3732
+ const files = slugToFiles.get(p.definition.slug) ?? [];
3733
+ files.push(p.filePath);
3734
+ slugToFiles.set(p.definition.slug, files);
3735
+ }
3736
+ for (const [slug, files] of slugToFiles) {
3737
+ if (files.length > 1) {
3738
+ for (const file of files) {
3739
+ const others = files.filter((f) => f !== file);
3740
+ issues.push({
3741
+ severity: "error",
3742
+ type: "provider_duplicate_slug",
3743
+ message: `provider slug '${slug}' is declared more than once (also in: ${others.join(", ")})`,
3744
+ path: file
3745
+ });
3746
+ }
3747
+ }
3748
+ }
3749
+ for (const { definition, filePath } of providers) {
3750
+ const { slug } = definition;
3751
+ for (const surface of definition.surfaces) {
3752
+ if (!knownSurfaces.has(surface)) {
3753
+ const known = [...knownSurfaces].sort().join(", ") || "(none declared)";
3754
+ issues.push({
3755
+ severity: "error",
3756
+ type: "provider_unknown_surface",
3757
+ message: `provider ${slug}: surface '${surface}' is not declared by any entity (known surfaces: ${known})`,
3758
+ path: filePath
3759
+ });
3760
+ }
3761
+ }
3762
+ if (!opts.skipImportCheck) {
3763
+ if (!opts.sourceRoot) {
3764
+ throw new Error(
3765
+ "validateProviders: sourceRoot is required for the import pre-flight check (or set skipImportCheck: true)"
3766
+ );
3767
+ }
3768
+ const resolveOpts = {
3769
+ sourceRoot: opts.sourceRoot,
3770
+ aliases: opts.aliases
3771
+ };
3772
+ const refs = [
3773
+ { field: "auth.strategy", ref: definition.auth.strategy },
3774
+ { field: "client.class", ref: definition.client.class }
3775
+ ];
3776
+ for (const { field, ref } of refs) {
3777
+ const result = resolveImportRef(ref, resolveOpts);
3778
+ if (result.status === "module-not-found") {
3779
+ issues.push({
3780
+ severity: "error",
3781
+ type: "provider_import_unresolved",
3782
+ message: `provider ${slug}: ${field} '${ref}' not found \u2014 module could not be resolved from ${result.resolvedFrom}`,
3783
+ path: filePath
3784
+ });
3785
+ } else if (result.status === "export-not-found") {
3786
+ const { exportName } = parseImportRef(ref);
3787
+ issues.push({
3788
+ severity: "error",
3789
+ type: "provider_import_unresolved",
3790
+ message: `provider ${slug}: ${field} '${ref}' not found \u2014 export '${exportName}' is missing from ${result.file}`,
3791
+ path: filePath
3792
+ });
3793
+ }
3794
+ }
3795
+ }
3796
+ }
3797
+ return issues;
3798
+ }
3799
+
3480
3800
  // src/analyzer/graph-builder.ts
3481
3801
  function inferCardinality(type) {
3482
3802
  switch (type) {
@@ -3833,8 +4153,8 @@ function checkConsistency(graph) {
3833
4153
  if (entity.queries !== void 0) {
3834
4154
  issues.push(...checkQueryFieldReferences(entity));
3835
4155
  }
3836
- if (entity.sync !== void 0) {
3837
- issues.push(...checkSyncFieldMappingReferences(entity));
4156
+ if (entity.integration !== void 0) {
4157
+ issues.push(...checkIntegrationFieldMappingReferences(entity));
3838
4158
  issues.push(...checkExternalIdTrackingCollision(entity));
3839
4159
  }
3840
4160
  }
@@ -4070,19 +4390,19 @@ function checkQueryFieldReferences(entity) {
4070
4390
  }
4071
4391
  return issues;
4072
4392
  }
4073
- function checkSyncFieldMappingReferences(entity) {
4393
+ function checkIntegrationFieldMappingReferences(entity) {
4074
4394
  const issues = [];
4075
4395
  const availableFields = getAvailableFieldNames(entity);
4076
4396
  const availableSet = new Set(availableFields);
4077
- for (const [providerName, provider] of Object.entries(entity.sync?.providers ?? {})) {
4397
+ for (const [providerName, provider] of Object.entries(entity.integration?.providers ?? {})) {
4078
4398
  for (const fieldName of Object.keys(provider.fieldMapping ?? {})) {
4079
4399
  if (!availableSet.has(fieldName)) {
4080
4400
  issues.push({
4081
4401
  severity: "warning",
4082
- type: "unknown_sync_field_mapping",
4402
+ type: "unknown_integration_field_mapping",
4083
4403
  entity: entity.name,
4084
4404
  field: fieldName,
4085
- message: `Sync field mapping references unknown field "${fieldName}" for provider "${providerName}" in entity "${entity.name}"`
4405
+ message: `Integration field mapping references unknown field "${fieldName}" for provider "${providerName}" in entity "${entity.name}"`
4086
4406
  });
4087
4407
  }
4088
4408
  }
@@ -4090,10 +4410,10 @@ function checkSyncFieldMappingReferences(entity) {
4090
4410
  if (!availableSet.has(fieldName)) {
4091
4411
  issues.push({
4092
4412
  severity: "warning",
4093
- type: "unknown_sync_field_mapping",
4413
+ type: "unknown_integration_field_mapping",
4094
4414
  entity: entity.name,
4095
4415
  field: fieldName,
4096
- message: `Sync field mapping references unknown field "${fieldName}" for provider "${providerName}" in entity "${entity.name}"`
4416
+ message: `Integration field mapping references unknown field "${fieldName}" for provider "${providerName}" in entity "${entity.name}"`
4097
4417
  });
4098
4418
  }
4099
4419
  }
@@ -4104,14 +4424,14 @@ function checkExternalIdTrackingCollision(entity) {
4104
4424
  const issues = [];
4105
4425
  const hasExternalIdTracking = entity.behaviors.includes("external_id_tracking");
4106
4426
  if (!hasExternalIdTracking) return issues;
4107
- for (const [providerName, provider] of Object.entries(entity.sync?.providers ?? {})) {
4427
+ for (const [providerName, provider] of Object.entries(entity.integration?.providers ?? {})) {
4108
4428
  if (provider.fieldMapping && "external_id" in provider.fieldMapping) {
4109
4429
  issues.push({
4110
4430
  severity: "warning",
4111
4431
  type: "external_id_tracking_collision",
4112
4432
  entity: entity.name,
4113
4433
  field: "external_id",
4114
- message: `Entity "${entity.name}" has external_id_tracking behavior and also maps "external_id" in sync field_mapping for provider "${providerName}". The behavior-added field may collide with the mapped field.`
4434
+ message: `Entity "${entity.name}" has external_id_tracking behavior and also maps "external_id" in integration field_mapping for provider "${providerName}". The behavior-added field may collide with the mapped field.`
4115
4435
  });
4116
4436
  }
4117
4437
  }
@@ -4290,28 +4610,28 @@ function createSuggestion(path34) {
4290
4610
 
4291
4611
  // src/analyzer/manifest.ts
4292
4612
  import { createHash } from "crypto";
4293
- import { readFileSync as readFileSync5, writeFileSync, existsSync as existsSync7, mkdirSync, readdirSync as readdirSync6, statSync as statSync3 } from "fs";
4294
- import { join as join8 } from "path";
4613
+ import { readFileSync as readFileSync6, writeFileSync, existsSync as existsSync8, mkdirSync, readdirSync as readdirSync6, statSync as statSync4 } from "fs";
4614
+ import { join as join9 } from "path";
4295
4615
  var MANIFEST_FILE = "manifest.json";
4296
4616
  var MANIFEST_VERSION = 1;
4297
4617
  function getManifestDir() {
4298
4618
  return process.env.CODEGEN_MANIFEST_DIR || ".codegen";
4299
4619
  }
4300
4620
  function getManifestPaths(projectRoot) {
4301
- const dir = join8(projectRoot, getManifestDir());
4302
- const file = join8(dir, MANIFEST_FILE);
4621
+ const dir = join9(projectRoot, getManifestDir());
4622
+ const file = join9(dir, MANIFEST_FILE);
4303
4623
  return { dir, file };
4304
4624
  }
4305
4625
  async function computeEntityFilesHash(entitiesDir) {
4306
- if (!existsSync7(entitiesDir)) {
4626
+ if (!existsSync8(entitiesDir)) {
4307
4627
  return createHash("sha256").update("").digest("hex");
4308
4628
  }
4309
4629
  const yamlFiles = [];
4310
4630
  function walkDir(dir) {
4311
4631
  const entries = readdirSync6(dir);
4312
4632
  for (const entry of entries) {
4313
- const fullPath = join8(dir, entry);
4314
- const stat = statSync3(fullPath);
4633
+ const fullPath = join9(dir, entry);
4634
+ const stat = statSync4(fullPath);
4315
4635
  if (stat.isDirectory()) {
4316
4636
  walkDir(fullPath);
4317
4637
  } else if (stat.isFile() && (entry.endsWith(".yaml") || entry.endsWith(".yml"))) {
@@ -4323,7 +4643,7 @@ async function computeEntityFilesHash(entitiesDir) {
4323
4643
  yamlFiles.sort();
4324
4644
  const hash = createHash("sha256");
4325
4645
  for (const file of yamlFiles) {
4326
- const content = readFileSync5(file, "utf-8");
4646
+ const content = readFileSync6(file, "utf-8");
4327
4647
  hash.update(file);
4328
4648
  hash.update(content);
4329
4649
  }
@@ -4331,11 +4651,11 @@ async function computeEntityFilesHash(entitiesDir) {
4331
4651
  }
4332
4652
  function readManifest(projectRoot) {
4333
4653
  const { file } = getManifestPaths(projectRoot);
4334
- if (!existsSync7(file)) {
4654
+ if (!existsSync8(file)) {
4335
4655
  return null;
4336
4656
  }
4337
4657
  try {
4338
- const content = readFileSync5(file, "utf-8");
4658
+ const content = readFileSync6(file, "utf-8");
4339
4659
  const manifest = JSON.parse(content);
4340
4660
  if (manifest.version !== MANIFEST_VERSION) {
4341
4661
  return null;
@@ -4347,7 +4667,7 @@ function readManifest(projectRoot) {
4347
4667
  }
4348
4668
  function writeManifest(projectRoot, manifest) {
4349
4669
  const { dir, file } = getManifestPaths(projectRoot);
4350
- if (!existsSync7(dir)) {
4670
+ if (!existsSync8(dir)) {
4351
4671
  mkdirSync(dir, { recursive: true });
4352
4672
  }
4353
4673
  const content = JSON.stringify(manifest, null, 2);
@@ -5442,8 +5762,8 @@ var BasePattern = definePattern({
5442
5762
  });
5443
5763
 
5444
5764
  // src/patterns/library/junction.pattern.ts
5445
- import { z as z7 } from "zod";
5446
- var JunctionPatternConfigSchema = z7.object({}).strict();
5765
+ import { z as z8 } from "zod";
5766
+ var JunctionPatternConfigSchema = z8.object({}).strict();
5447
5767
  var JunctionPattern = definePattern({
5448
5768
  name: "Junction",
5449
5769
  description: "Explicit many-to-many junction with role + temporal + sourcing metadata",
@@ -5489,30 +5809,30 @@ var MetadataPattern = definePattern({
5489
5809
  description: "History-tracked metadata rows \u2014 entity-id + type scoped lookups"
5490
5810
  });
5491
5811
 
5492
- // src/patterns/library/synced.pattern.ts
5493
- var SyncedPattern = definePattern({
5494
- name: "Synced",
5812
+ // src/patterns/library/integrated.pattern.ts
5813
+ var IntegratedPattern = definePattern({
5814
+ name: "Integrated",
5495
5815
  extends: ["Base"],
5496
- repositoryClass: "SyncedEntityRepository",
5497
- serviceClass: "SyncedEntityService",
5498
- repositoryImport: "@shared/base-classes/synced-entity-repository",
5499
- serviceImport: "@shared/base-classes/synced-entity-service",
5816
+ repositoryClass: "IntegratedEntityRepository",
5817
+ serviceClass: "IntegratedEntityService",
5818
+ repositoryImport: "@shared/base-classes/integrated-entity-repository",
5819
+ serviceImport: "@shared/base-classes/integrated-entity-service",
5500
5820
  repositoryInheritedMethods: [
5501
5821
  "findById, findByIds, list, count, exists, create, update, delete, upsertMany",
5502
5822
  "findByExternalId, findManyByExternalIds, findAllByUserId, findVisibleByUserId",
5503
- "syncUpsertOne, findByExternalIdProjected, softDeleteByExternalId, syncUpsert"
5823
+ "integrationUpsertOne, findByExternalIdProjected, softDeleteByExternalId, integrationUpsert"
5504
5824
  ],
5505
5825
  serviceInheritedMethods: [
5506
5826
  "findById, findByIds, list, count, exists, create, update, delete",
5507
5827
  "findByExternalId, findAllByUserId, findVisibleByUserId"
5508
5828
  ],
5509
5829
  impliedBehaviors: ["external_id_tracking"],
5510
- description: "External CRM/system sync columns and syncUpsert methods"
5830
+ description: "External CRM/system integration columns and integrationUpsert methods"
5511
5831
  });
5512
5832
 
5513
5833
  // src/patterns/library/index.ts
5514
5834
  registerLibraryPattern(BasePattern);
5515
- registerLibraryPattern(SyncedPattern);
5835
+ registerLibraryPattern(IntegratedPattern);
5516
5836
  registerLibraryPattern(ActivityPattern);
5517
5837
  registerLibraryPattern(KnowledgePattern);
5518
5838
  registerLibraryPattern(MetadataPattern);
@@ -5572,9 +5892,9 @@ function validateEntities(entitiesDir) {
5572
5892
 
5573
5893
  // src/cli/shared/hygen.ts
5574
5894
  import { execSync } from "child_process";
5575
- import { join as join9 } from "path";
5895
+ import { join as join10 } from "path";
5576
5896
  function defaultTemplateRoot() {
5577
- return join9(import.meta.dirname, "..", "..", "..", "templates");
5897
+ return join10(import.meta.dirname, "..", "..", "..", "templates");
5578
5898
  }
5579
5899
  function quoteArg(a) {
5580
5900
  if (a === "" || /[\s"'$`\\]/.test(a)) {
@@ -5698,7 +6018,9 @@ function collectEntities(entitiesDir) {
5698
6018
  const def = result.definition;
5699
6019
  entities.push({
5700
6020
  name: def.entity.name,
5701
- plural: def.entity.plural
6021
+ plural: def.entity.plural,
6022
+ // #403: top-level `context:` nests the module folder; undefined → flat.
6023
+ context: def.context
5702
6024
  });
5703
6025
  }
5704
6026
  entities.sort((a, b) => a.name.localeCompare(b.name));
@@ -5754,11 +6076,12 @@ function entityFilePaths(info, architecture, backendSrc) {
5754
6076
  const pluralKebab = toKebabCase(plural);
5755
6077
  if (architecture === "clean-lite-ps") {
5756
6078
  const prefix = backendSrc && backendSrc !== "." ? `${backendSrc}/` : "";
6079
+ const ctxSeg = info.context ? `${info.context}/` : "";
5757
6080
  return {
5758
- moduleFile: `${prefix}modules/${plural}/${plural}.module.ts`,
6081
+ moduleFile: `${prefix}modules/${ctxSeg}${plural}/${plural}.module.ts`,
5759
6082
  moduleClass: `${toPascalCase(plural)}Module`,
5760
6083
  // Drizzle entity schema lives alongside the entity file in clean-lite-ps.
5761
- schemaFile: `${prefix}modules/${plural}/${name}.entity.ts`
6084
+ schemaFile: `${prefix}modules/${ctxSeg}${plural}/${name}.entity.ts`
5762
6085
  };
5763
6086
  }
5764
6087
  return {
@@ -5879,22 +6202,22 @@ var HEADER2 = `// AUTO-GENERATED by @pattern-stack/codegen. Do not edit.
5879
6202
  function collectScopeableNames(entitiesDir) {
5880
6203
  if (!fs3.existsSync(entitiesDir)) return [];
5881
6204
  const files = findYamlFiles(entitiesDir);
5882
- const names = [];
6205
+ const names2 = [];
5883
6206
  for (const file of files) {
5884
6207
  const result = loadEntityFromYaml(file);
5885
6208
  if (!result.success) continue;
5886
6209
  if (result.definition.entity.scopeable === true) {
5887
- names.push(result.definition.entity.name);
6210
+ names2.push(result.definition.entity.name);
5888
6211
  }
5889
6212
  }
5890
- names.sort((a, b) => a.localeCompare(b));
5891
- return names;
6213
+ names2.sort((a, b) => a.localeCompare(b));
6214
+ return names2;
5892
6215
  }
5893
- function buildScopeEntityTypeContent(names) {
5894
- if (names.length === 0) {
6216
+ function buildScopeEntityTypeContent(names2) {
6217
+ if (names2.length === 0) {
5895
6218
  return HEADER2 + "\nexport type ScopeEntityType = never;\n\nexport const SCOPE_ENTITY_TYPES = [] as const;\n";
5896
6219
  }
5897
- const quoted = names.map((n) => `'${n}'`);
6220
+ const quoted = names2.map((n) => `'${n}'`);
5898
6221
  const unionLiteral = quoted.join(" | ");
5899
6222
  const tupleLiteral = quoted.join(", ");
5900
6223
  return HEADER2 + `
@@ -5953,8 +6276,8 @@ var SUBSYSTEMS = [
5953
6276
  defaultBackend: "local"
5954
6277
  },
5955
6278
  {
5956
- name: "sync",
5957
- description: "External-system sync engine (IChangeSource<T> + orchestrator + audit log)",
6279
+ name: "integration",
6280
+ description: "External-system integration engine (IChangeSource<T> + orchestrator + audit log)",
5958
6281
  backends: ["drizzle", "memory"],
5959
6282
  defaultBackend: "drizzle"
5960
6283
  },
@@ -5977,7 +6300,7 @@ var SUBSYSTEMS = [
5977
6300
  {
5978
6301
  // OBS-7 / ADR-025. Combiner subsystem — no schema, no worker, no
5979
6302
  // generated/ dir. `ObservabilityModule` composes sibling read ports
5980
- // (events/jobs/bridge/sync) via @Optional() DI. The `combiner`
6303
+ // (events/jobs/bridge/integration) via @Optional() DI. The `combiner`
5981
6304
  // pseudo-backend is parallel to `openapi-config`'s `config-only`.
5982
6305
  name: "observability",
5983
6306
  description: "Observability combiner \u2014 composes sibling read ports via @Optional() DI (ADR-025)",
@@ -5999,14 +6322,14 @@ var SUBSYSTEMS = [
5999
6322
  {
6000
6323
  // #287. Auth-integrations starter (PR #290) — vendored from
6001
6324
  // `examples/auth-integrations/`, NOT from `runtime/subsystems/`.
6002
- // Bundles a canonical `integration` entity yaml + the three
6003
- // integration-store-port adapters + the `IntegrationsService`
6325
+ // Bundles a canonical `connection` entity yaml + the three
6326
+ // connection-store-port adapters + the `ConnectionsService`
6004
6327
  // facade. Single-backend (drizzle); the runtime adapters call
6005
- // directly into the codegen-emitted `IntegrationService` from the
6328
+ // directly into the codegen-emitted `ConnectionService` from the
6006
6329
  // entity layer. Detection: presence of
6007
- // `<sharedRoot>/integrations/integrations-auth.module.ts`.
6330
+ // `<vendorRoot>/connections/connections-auth.module.ts`.
6008
6331
  name: "auth-integrations",
6009
- description: "Vendored integrations entity + adapters (consumes auth subsystem)",
6332
+ description: "Vendored connection entity + adapters (consumes auth subsystem)",
6010
6333
  backends: ["drizzle"],
6011
6334
  defaultBackend: "drizzle"
6012
6335
  }
@@ -6017,7 +6340,7 @@ var SUBSYSTEM_MODULE_FILE = {
6017
6340
  jobs: "jobs-domain.module.ts",
6018
6341
  cache: "cache.module.ts",
6019
6342
  storage: "storage.module.ts",
6020
- sync: "sync.module.ts",
6343
+ integration: "integration.module.ts",
6021
6344
  bridge: "bridge.module.ts",
6022
6345
  observability: "observability.module.ts",
6023
6346
  auth: "auth.module.ts"
@@ -6099,11 +6422,8 @@ async function detectSubsystemStatesImpl(ctx) {
6099
6422
  const pathsAny = ctx.config?.paths;
6100
6423
  const modulesConfigured = pathsAny?.modules_dir;
6101
6424
  const vendorRoot = typeof modulesConfigured === "string" && modulesConfigured.length > 0 ? path6.resolve(ctx.cwd, modulesConfigured) : path6.resolve(ctx.cwd, backendSrc, "modules");
6102
- const sharedConfigured = pathsAny?.shared;
6103
- const sharedRoot = typeof sharedConfigured === "string" && sharedConfigured.length > 0 ? path6.resolve(ctx.cwd, sharedConfigured) : path6.resolve(ctx.cwd, backendSrc, "shared");
6104
6425
  const candidates = [
6105
- path6.join(vendorRoot, "integrations", "integrations-auth.module.ts"),
6106
- path6.join(sharedRoot, "integrations", "integrations-auth.module.ts")
6426
+ path6.join(vendorRoot, "connections", "connections-auth.module.ts")
6107
6427
  ];
6108
6428
  for (const moduleFile of candidates) {
6109
6429
  if (fs4.existsSync(moduleFile)) {
@@ -6217,24 +6537,24 @@ var COMPOSERS = {
6217
6537
  ]
6218
6538
  };
6219
6539
  },
6220
- sync: ({ subsystemsRel, cfg }) => {
6540
+ integration: ({ subsystemsRel, cfg }) => {
6221
6541
  const backend = cfg?.backend ?? "drizzle";
6222
6542
  const multiTenant = Boolean(cfg?.multi_tenant);
6223
6543
  return {
6224
6544
  imports: [
6225
- `import { SyncModule } from '${subsystemsRel}/sync/sync.module';`
6545
+ `import { IntegrationModule } from '${subsystemsRel}/integration/integration.module';`
6226
6546
  ],
6227
6547
  calls: [
6228
- ` SyncModule.forRoot(${quoteOpts({ backend, multiTenant })}),`
6548
+ ` IntegrationModule.forRoot(${quoteOpts({ backend, multiTenant })}),`
6229
6549
  ]
6230
6550
  };
6231
6551
  }
6232
6552
  };
6233
- var COMPOSABLE_ORDER = ["events", "jobs", "bridge", "sync"];
6553
+ var COMPOSABLE_ORDER = ["events", "jobs", "bridge", "integration"];
6234
6554
  var HEADER3 = `// AUTO-GENERATED by @pattern-stack/codegen. DO NOT EDIT.
6235
6555
  // Subsystem composition barrel \u2014 reflects \`subsystems.install\` in
6236
6556
  // codegen.config.yaml and the per-subsystem option blocks
6237
- // (\`events:\`, \`jobs:\`, \`bridge:\`, \`sync:\`).
6557
+ // (\`events:\`, \`jobs:\`, \`bridge:\`, \`integration:\`).
6238
6558
  //
6239
6559
  // Wire into AppModule once:
6240
6560
  //
@@ -6308,7 +6628,7 @@ async function regenerateSubsystemBarrel(opts) {
6308
6628
  // src/cli/shared/bridge-registry-generator.ts
6309
6629
  import fs6 from "fs";
6310
6630
  import path9 from "path";
6311
- import ts from "typescript";
6631
+ import ts2 from "typescript";
6312
6632
  var HEADER4 = `// AUTO-GENERATED by @pattern-stack/codegen. Do not edit.
6313
6633
  // Run \`codegen entity new --all\` to refresh.
6314
6634
  `;
@@ -6384,34 +6704,34 @@ function findHandlerFiles(dir) {
6384
6704
  function extractTriggersFromSourceFile(sourceFile, filePath) {
6385
6705
  const triggers = [];
6386
6706
  function visit(node) {
6387
- if (ts.isClassDeclaration(node)) {
6388
- const modifiers = ts.canHaveDecorators(node) ? ts.getDecorators(node) ?? [] : [];
6707
+ if (ts2.isClassDeclaration(node)) {
6708
+ const modifiers = ts2.canHaveDecorators(node) ? ts2.getDecorators(node) ?? [] : [];
6389
6709
  for (const decorator of modifiers) {
6390
6710
  const call = decorator.expression;
6391
- if (!ts.isCallExpression(call)) continue;
6392
- if (!ts.isIdentifier(call.expression)) continue;
6711
+ if (!ts2.isCallExpression(call)) continue;
6712
+ if (!ts2.isIdentifier(call.expression)) continue;
6393
6713
  if (call.expression.text !== "JobHandler") continue;
6394
6714
  const [typeArg, metaArg] = call.arguments;
6395
- if (!typeArg || !ts.isStringLiteralLike(typeArg)) continue;
6396
- if (!metaArg || !ts.isObjectLiteralExpression(metaArg)) continue;
6715
+ if (!typeArg || !ts2.isStringLiteralLike(typeArg)) continue;
6716
+ if (!metaArg || !ts2.isObjectLiteralExpression(metaArg)) continue;
6397
6717
  const jobType = typeArg.text;
6398
6718
  const triggersProp = metaArg.properties.find(
6399
- (p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === "triggers"
6719
+ (p) => ts2.isPropertyAssignment(p) && ts2.isIdentifier(p.name) && p.name.text === "triggers"
6400
6720
  );
6401
6721
  if (!triggersProp) continue;
6402
- if (!ts.isArrayLiteralExpression(triggersProp.initializer)) continue;
6722
+ if (!ts2.isArrayLiteralExpression(triggersProp.initializer)) continue;
6403
6723
  triggersProp.initializer.elements.forEach((el, index2) => {
6404
- if (!ts.isObjectLiteralExpression(el)) return;
6724
+ if (!ts2.isObjectLiteralExpression(el)) return;
6405
6725
  const eventProp = el.properties.find(
6406
- (p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === "event"
6726
+ (p) => ts2.isPropertyAssignment(p) && ts2.isIdentifier(p.name) && p.name.text === "event"
6407
6727
  );
6408
6728
  const mapProp = el.properties.find(
6409
- (p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === "map"
6729
+ (p) => ts2.isPropertyAssignment(p) && ts2.isIdentifier(p.name) && p.name.text === "map"
6410
6730
  );
6411
6731
  const whenProp = el.properties.find(
6412
- (p) => ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === "when"
6732
+ (p) => ts2.isPropertyAssignment(p) && ts2.isIdentifier(p.name) && p.name.text === "when"
6413
6733
  );
6414
- if (!eventProp || !ts.isStringLiteralLike(eventProp.initializer)) return;
6734
+ if (!eventProp || !ts2.isStringLiteralLike(eventProp.initializer)) return;
6415
6735
  if (!mapProp) return;
6416
6736
  const { line } = sourceFile.getLineAndCharacterOfPosition(el.getStart(sourceFile));
6417
6737
  triggers.push({
@@ -6426,7 +6746,7 @@ function extractTriggersFromSourceFile(sourceFile, filePath) {
6426
6746
  });
6427
6747
  }
6428
6748
  }
6429
- ts.forEachChild(node, visit);
6749
+ ts2.forEachChild(node, visit);
6430
6750
  }
6431
6751
  visit(sourceFile);
6432
6752
  return triggers;
@@ -6436,13 +6756,13 @@ function scanHandlerFiles(handlersDir) {
6436
6756
  const out = [];
6437
6757
  for (const filePath of files) {
6438
6758
  const text2 = fs6.readFileSync(filePath, "utf8");
6439
- const sourceFile = ts.createSourceFile(
6759
+ const sourceFile = ts2.createSourceFile(
6440
6760
  filePath,
6441
6761
  text2,
6442
- ts.ScriptTarget.Latest,
6762
+ ts2.ScriptTarget.Latest,
6443
6763
  /* setParentNodes */
6444
6764
  true,
6445
- ts.ScriptKind.TS
6765
+ ts2.ScriptKind.TS
6446
6766
  );
6447
6767
  out.push(...extractTriggersFromSourceFile(sourceFile, filePath));
6448
6768
  }
@@ -6741,8 +7061,8 @@ function emitTypeImports(imports) {
6741
7061
  }
6742
7062
  const out = [];
6743
7063
  for (const specifier of [...grouped.keys()].sort()) {
6744
- const names = [...grouped.get(specifier)].sort();
6745
- out.push(`import type { ${names.join(", ")} } from '${specifier}';`);
7064
+ const names2 = [...grouped.get(specifier)].sort();
7065
+ out.push(`import type { ${names2.join(", ")} } from '${specifier}';`);
6746
7066
  }
6747
7067
  return out;
6748
7068
  }
@@ -6755,8 +7075,8 @@ function emitValueImports(imports) {
6755
7075
  }
6756
7076
  const out = [];
6757
7077
  for (const specifier of [...grouped.keys()].sort()) {
6758
- const names = [...grouped.get(specifier)].sort();
6759
- out.push(`import { ${names.join(", ")} } from '${specifier}';`);
7078
+ const names2 = [...grouped.get(specifier)].sort();
7079
+ out.push(`import { ${names2.join(", ")} } from '${specifier}';`);
6760
7080
  }
6761
7081
  return out;
6762
7082
  }
@@ -6788,13 +7108,13 @@ function buildTokensTs(pattern) {
6788
7108
  lines.push("");
6789
7109
  for (const i of emitTypeImports(typeImports)) lines.push(i);
6790
7110
  lines.push("");
6791
- for (const { names } of registries) {
6792
- lines.push(`export const ${names.tokenConst} = Symbol('${names.tokenConst}');`);
7111
+ for (const { names: names2 } of registries) {
7112
+ lines.push(`export const ${names2.tokenConst} = Symbol('${names2.tokenConst}');`);
6793
7113
  }
6794
7114
  lines.push("");
6795
- for (const { reg, names } of registries) {
7115
+ for (const { reg, names: names2 } of registries) {
6796
7116
  lines.push(
6797
- `export type ${names.mapType} = Map<${reg.keyType}, ${reg.valueType}>;`
7117
+ `export type ${names2.mapType} = Map<${reg.keyType}, ${reg.valueType}>;`
6798
7118
  );
6799
7119
  }
6800
7120
  lines.push("");
@@ -6808,8 +7128,8 @@ function buildProvidersTs(pattern) {
6808
7128
  providerImports.push({ specifier: e.providerImport, name: e.provider });
6809
7129
  }
6810
7130
  }
6811
- const tokenValueImports = registries.map(({ names }) => names.tokenConst);
6812
- const mapTypeImports = registries.map(({ names }) => names.mapType);
7131
+ const tokenValueImports = registries.map(({ names: names2 }) => names2.tokenConst);
7132
+ const mapTypeImports = registries.map(({ names: names2 }) => names2.mapType);
6813
7133
  const typeImports = [];
6814
7134
  for (const { reg } of registries) {
6815
7135
  typeImports.push({ specifier: reg.keyTypeImport, name: reg.keyType });
@@ -6834,24 +7154,24 @@ function buildProvidersTs(pattern) {
6834
7154
  `export function build${pascal}RegistryProviders(opts?: ${optsType}): Provider[] {`
6835
7155
  );
6836
7156
  lines.push(" return [");
6837
- for (const { reg, names } of registries) {
7157
+ for (const { reg, names: names2 } of registries) {
6838
7158
  const factoryArgs = reg.entries.map((e, i) => `${camelArg(e.provider, i)}: ${e.provider}`).join(", ");
6839
7159
  lines.push(" {");
6840
- lines.push(` provide: ${names.tokenConst},`);
7160
+ lines.push(` provide: ${names2.tokenConst},`);
6841
7161
  lines.push(` useFactory: (${factoryArgs}) => {`);
6842
- lines.push(` const base: ${names.mapType} = new Map<${reg.keyType}, ${reg.valueType}>([`);
7162
+ lines.push(` const base: ${names2.mapType} = new Map<${reg.keyType}, ${reg.valueType}>([`);
6843
7163
  for (const [i, e] of reg.entries.entries()) {
6844
7164
  lines.push(
6845
7165
  ` ['${e.key}' as ${reg.keyType}, ${camelArg(e.provider, i)}],`
6846
7166
  );
6847
7167
  }
6848
7168
  lines.push(" ]);");
6849
- lines.push(` if (opts?.${names.overrideField}) {`);
6850
- lines.push(` for (const [k, v] of Object.entries(opts.${names.overrideField})) {`);
7169
+ lines.push(` if (opts?.${names2.overrideField}) {`);
7170
+ lines.push(` for (const [k, v] of Object.entries(opts.${names2.overrideField})) {`);
6851
7171
  lines.push(` if (v !== undefined) base.set(k as ${reg.keyType}, v as ${reg.valueType});`);
6852
7172
  lines.push(" }");
6853
7173
  lines.push(" }");
6854
- lines.push(` return base as ${names.mapType};`);
7174
+ lines.push(` return base as ${names2.mapType};`);
6855
7175
  lines.push(" },");
6856
7176
  const injectList = reg.entries.map((e) => e.provider).join(", ");
6857
7177
  lines.push(` inject: [${injectList}],`);
@@ -6881,8 +7201,8 @@ function buildDispatcherTs(pattern) {
6881
7201
  providerImports.push({ specifier: e.providerImport, name: e.provider });
6882
7202
  }
6883
7203
  }
6884
- const tokenValues = registries.map(({ names }) => names.tokenConst);
6885
- const mapTypes = registries.map(({ names }) => names.mapType);
7204
+ const tokenValues = registries.map(({ names: names2 }) => names2.tokenConst);
7205
+ const mapTypes = registries.map(({ names: names2 }) => names2.mapType);
6886
7206
  const lines = [];
6887
7207
  lines.push(HEADER5.trimEnd());
6888
7208
  lines.push("");
@@ -6900,21 +7220,21 @@ function buildDispatcherTs(pattern) {
6900
7220
  lines.push("@Injectable()");
6901
7221
  lines.push(`export class ${className} {`);
6902
7222
  lines.push(" constructor(");
6903
- for (const { names } of registries) {
6904
- const fieldName = names.method === "select" ? "registry" : `${camelFromMethod(names.method)}Registry`;
7223
+ for (const { names: names2 } of registries) {
7224
+ const fieldName = names2.method === "select" ? "registry" : `${camelFromMethod(names2.method)}Registry`;
6905
7225
  lines.push(
6906
- ` @Inject(${names.tokenConst}) protected readonly ${fieldName}: ${names.mapType},`
7226
+ ` @Inject(${names2.tokenConst}) protected readonly ${fieldName}: ${names2.mapType},`
6907
7227
  );
6908
7228
  }
6909
7229
  lines.push(" ) {}");
6910
7230
  lines.push("");
6911
- for (const { reg, names } of registries) {
6912
- const fieldName = names.method === "select" ? "registry" : `${camelFromMethod(names.method)}Registry`;
7231
+ for (const { reg, names: names2 } of registries) {
7232
+ const fieldName = names2.method === "select" ? "registry" : `${camelFromMethod(names2.method)}Registry`;
6913
7233
  for (const e of reg.entries) {
6914
- lines.push(` ${names.method}(key: '${e.key}'): ${e.provider};`);
7234
+ lines.push(` ${names2.method}(key: '${e.key}'): ${e.provider};`);
6915
7235
  }
6916
- lines.push(` ${names.method}(key: ${reg.keyType}): ${reg.valueType};`);
6917
- lines.push(` ${names.method}(key: ${reg.keyType}): ${reg.valueType} {`);
7236
+ lines.push(` ${names2.method}(key: ${reg.keyType}): ${reg.valueType};`);
7237
+ lines.push(` ${names2.method}(key: ${reg.keyType}): ${reg.valueType} {`);
6918
7238
  lines.push(` const entry = this.${fieldName}.get(key);`);
6919
7239
  lines.push(
6920
7240
  ` if (!entry) throw new ${errorClass}(\`Unknown ${reg.keyType}: \${String(key)}\`);`
@@ -6956,7 +7276,7 @@ function buildModuleTs(pattern) {
6956
7276
  typeImports.push({ specifier: reg.keyTypeImport, name: reg.keyType });
6957
7277
  typeImports.push({ specifier: reg.valueTypeImport, name: reg.valueType });
6958
7278
  }
6959
- const tokenValues = registries.map(({ names }) => names.tokenConst);
7279
+ const tokenValues = registries.map(({ names: names2 }) => names2.tokenConst);
6960
7280
  const lines = [];
6961
7281
  lines.push(HEADER5.trimEnd());
6962
7282
  lines.push("");
@@ -6967,9 +7287,9 @@ function buildModuleTs(pattern) {
6967
7287
  lines.push(`import { ${tokenValues.sort().join(", ")} } from './tokens.js';`);
6968
7288
  lines.push("");
6969
7289
  lines.push(`export interface ${optsType} {`);
6970
- for (const { reg, names } of registries) {
7290
+ for (const { reg, names: names2 } of registries) {
6971
7291
  lines.push(
6972
- ` ${names.overrideField}?: Partial<Record<${reg.keyType}, ${reg.valueType}>>;`
7292
+ ` ${names2.overrideField}?: Partial<Record<${reg.keyType}, ${reg.valueType}>>;`
6973
7293
  );
6974
7294
  }
6975
7295
  lines.push("}");
@@ -7083,7 +7403,7 @@ import fs8 from "fs";
7083
7403
  import path11 from "path";
7084
7404
 
7085
7405
  // src/parser/load-events.ts
7086
- import { basename as basename2, resolve as resolve3 } from "path";
7406
+ import { basename as basename2, resolve as resolve4 } from "path";
7087
7407
  function loadErrorToIssue2(error) {
7088
7408
  const issues = [];
7089
7409
  issues.push({
@@ -7113,7 +7433,7 @@ function stripYamlExt(file) {
7113
7433
  function loadEvents(eventsDir, entityNames) {
7114
7434
  const events = [];
7115
7435
  const issues = [];
7116
- const resolvedDir = resolve3(eventsDir);
7436
+ const resolvedDir = resolve4(eventsDir);
7117
7437
  let files;
7118
7438
  try {
7119
7439
  files = findYamlFiles(resolvedDir);
@@ -7755,6 +8075,564 @@ function validateEntityEmits(entities, events) {
7755
8075
  return issues;
7756
8076
  }
7757
8077
 
8078
+ // src/cli/shared/provider-module-generator.ts
8079
+ import { existsSync as existsSync9, mkdirSync as mkdirSync2, readFileSync as readFileSync7, statSync as statSync5, writeFileSync as writeFileSync2 } from "fs";
8080
+ import { dirname, isAbsolute as isAbsolute2, join as join11, resolve as resolve5 } from "path";
8081
+ function providerPascalCase(slug) {
8082
+ return slug.split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
8083
+ }
8084
+ function providerConstantCase(slug) {
8085
+ return slug.replace(/-/g, "_").toUpperCase();
8086
+ }
8087
+ function providerModuleBanner(sourceYaml) {
8088
+ return `// @generated by @pattern-stack/codegen from ${sourceYaml} \u2014 DO NOT EDIT.
8089
+ // Hand edits are overwritten on re-emit. Regenerate with \`bun run codegen\`. To change this provider, edit its \`definitions/providers/*.yaml\`.`;
8090
+ }
8091
+ function generateProviderModule(def, sourceYaml) {
8092
+ const { slug } = def;
8093
+ const Pascal = providerPascalCase(slug);
8094
+ const CONST = providerConstantCase(slug);
8095
+ const strategy = parseImportRef(def.auth.strategy);
8096
+ const client = parseImportRef(def.client.class);
8097
+ const strategyToken = `${CONST}_AUTH_STRATEGY`;
8098
+ const clientToken = `${CONST}_CLIENT`;
8099
+ const surfaces = def.surfaces.join(", ");
8100
+ const title = def.display_name ? `${def.display_name} (\`${slug}\`)` : `\`${slug}\``;
8101
+ return `${providerModuleBanner(sourceYaml)}
8102
+ /**
8103
+ * Provider module for ${title}.
8104
+ *
8105
+ * Surfaces: ${surfaces}.
8106
+ *
8107
+ * Wires the declared auth strategy and API client under the provider-specific
8108
+ * DI tokens \`${strategyToken}\` and \`${clientToken}\`. Per the auth subsystem
8109
+ * contract, strategies are registered under provider-specific tokens rather
8110
+ * than a single \`AUTH_STRATEGY\`. Registry aggregation (RFC-0001 \xA73) is
8111
+ * emitted by a later codegen step.
8112
+ */
8113
+ import { Module } from '@nestjs/common';
8114
+ import { ${strategy.exportName} } from '${strategy.path}';
8115
+ import { ${client.exportName} } from '${client.path}';
8116
+
8117
+ /** DI token for the \`${slug}\` provider's auth strategy. */
8118
+ export const ${strategyToken} = Symbol('${strategyToken}');
8119
+ /** DI token for the \`${slug}\` provider's API client. */
8120
+ export const ${clientToken} = Symbol('${clientToken}');
8121
+
8122
+ @Module({
8123
+ providers: [
8124
+ ${strategy.exportName},
8125
+ ${client.exportName},
8126
+ { provide: ${strategyToken}, useExisting: ${strategy.exportName} },
8127
+ { provide: ${clientToken}, useExisting: ${client.exportName} },
8128
+ ],
8129
+ exports: [
8130
+ ${strategy.exportName},
8131
+ ${client.exportName},
8132
+ ${strategyToken},
8133
+ ${clientToken},
8134
+ ],
8135
+ })
8136
+ export class ${Pascal}ProviderModule {}
8137
+ `;
8138
+ }
8139
+ function resolveTsconfigAliases(consumerRoot) {
8140
+ const tsconfigPath = join11(consumerRoot, "tsconfig.json");
8141
+ if (!existsSync9(tsconfigPath)) return null;
8142
+ let parsed;
8143
+ try {
8144
+ parsed = JSON.parse(stripJsonComments(readFileSync7(tsconfigPath, "utf-8")));
8145
+ } catch {
8146
+ return null;
8147
+ }
8148
+ const compilerOptions = parsed.compilerOptions ?? {};
8149
+ const baseUrl = compilerOptions.baseUrl ?? ".";
8150
+ const sourceRoot = isAbsolute2(baseUrl) ? baseUrl : resolve5(consumerRoot, baseUrl);
8151
+ const aliases = {};
8152
+ for (const [pattern, targets] of Object.entries(compilerOptions.paths ?? {})) {
8153
+ if (!Array.isArray(targets) || targets.length === 0) continue;
8154
+ const aliasKey = pattern.replace(/\/\*$/, "");
8155
+ const target = targets[0].replace(/\/\*$/, "");
8156
+ aliases[aliasKey] = isAbsolute2(target) ? target : resolve5(sourceRoot, target);
8157
+ }
8158
+ return { sourceRoot, aliases };
8159
+ }
8160
+ function stripJsonComments(input) {
8161
+ return input.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/.*$/gm, "$1");
8162
+ }
8163
+ function generateProviderModules(opts) {
8164
+ const base = {
8165
+ providersDir: opts.providersDir,
8166
+ skipped: false,
8167
+ written: [],
8168
+ loadFailures: [],
8169
+ issues: []
8170
+ };
8171
+ if (!existsSync9(opts.providersDir) || !statSync5(opts.providersDir).isDirectory()) {
8172
+ return { ...base, skipped: true };
8173
+ }
8174
+ const files = findYamlFiles(opts.providersDir);
8175
+ if (files.length === 0) return { ...base, skipped: true };
8176
+ const { successes, failures } = loadProvidersFromYaml(files);
8177
+ const loaded = successes.map((s) => ({
8178
+ definition: s.definition,
8179
+ filePath: s.filePath
8180
+ }));
8181
+ const issues = failures.map((f) => ({
8182
+ severity: "error",
8183
+ type: "provider_load_failed",
8184
+ message: `${f.error}${f.details?.length ? ` \u2014 ${f.details.join("; ")}` : ""}`,
8185
+ path: f.filePath
8186
+ }));
8187
+ issues.push(
8188
+ ...validateProviders(loaded, {
8189
+ entitySurfaces: opts.entitySurfaces,
8190
+ sourceRoot: opts.sourceRoot,
8191
+ aliases: opts.aliases,
8192
+ skipImportCheck: opts.skipImportCheck
8193
+ })
8194
+ );
8195
+ if (issues.some((i) => i.severity === "error")) {
8196
+ return { ...base, loadFailures: failures, issues };
8197
+ }
8198
+ const written = [];
8199
+ for (const { definition, filePath } of loaded) {
8200
+ const sourceYaml = relativeSource(filePath);
8201
+ const content = generateProviderModule(definition, sourceYaml);
8202
+ const outPath = join11(
8203
+ opts.outputRoot,
8204
+ definition.slug,
8205
+ `${definition.slug}.provider.module.ts`
8206
+ );
8207
+ if (!opts.dryRun) {
8208
+ writeIfChanged(outPath, content);
8209
+ }
8210
+ written.push(outPath);
8211
+ }
8212
+ return { ...base, written, loadFailures: failures, issues };
8213
+ }
8214
+ function writeIfChanged(outPath, content) {
8215
+ if (existsSync9(outPath) && readFileSync7(outPath, "utf-8") === content) return;
8216
+ mkdirSync2(dirname(outPath), { recursive: true });
8217
+ writeFileSync2(outPath, content);
8218
+ }
8219
+ function relativeSource(filePath) {
8220
+ const marker = "definitions/providers/";
8221
+ const idx = filePath.lastIndexOf(marker);
8222
+ if (idx !== -1) return filePath.slice(idx);
8223
+ const slash = filePath.lastIndexOf("/");
8224
+ return slash === -1 ? filePath : `definitions/providers/${filePath.slice(slash + 1)}`;
8225
+ }
8226
+
8227
+ // src/cli/shared/adapter-emission-generator.ts
8228
+ import {
8229
+ existsSync as existsSync10,
8230
+ mkdirSync as mkdirSync3,
8231
+ readFileSync as readFileSync8,
8232
+ statSync as statSync6,
8233
+ writeFileSync as writeFileSync3
8234
+ } from "fs";
8235
+ import { dirname as dirname2, join as join12 } from "path";
8236
+ var SURFACE_REGISTRY = {
8237
+ crm: {
8238
+ packageName: "@pattern-stack/codegen-crm",
8239
+ portType: "CrmPort",
8240
+ capabilitiesType: "CrmCapabilities",
8241
+ noCapsConst: "NO_CRM_CAPABILITIES",
8242
+ l2Ports: [
8243
+ {
8244
+ prop: "fields",
8245
+ type: "IFieldDefinitionReader",
8246
+ method: "list",
8247
+ params: "(_integrationId, _entity)",
8248
+ capFlag: "fieldDefinitions"
8249
+ },
8250
+ {
8251
+ prop: "picklists",
8252
+ type: "IPicklistReader",
8253
+ method: "values",
8254
+ params: "(_integrationId, _entity, _fieldId)",
8255
+ capFlag: "picklists"
8256
+ },
8257
+ {
8258
+ prop: "associations",
8259
+ type: "IAssociationReader",
8260
+ method: "list",
8261
+ params: "(_integrationId, _fromEntity, _fromId, _toEntity)",
8262
+ capFlag: "associations"
8263
+ }
8264
+ ]
8265
+ },
8266
+ // Interaction surfaces (#416) are incremental-read with no L2 sub-ports — the
8267
+ // port composes L1 (auth + sources) + a capabilities descriptor whose only
8268
+ // field is `entities`; reads go through the change-source registry. So
8269
+ // `l2Ports: []` and the scaffold emits no L2 readers/stubs/capability flags.
8270
+ calendar: {
8271
+ packageName: "@pattern-stack/codegen-calendar",
8272
+ portType: "CalendarPort",
8273
+ capabilitiesType: "CalendarCapabilities",
8274
+ noCapsConst: "NO_CALENDAR_CAPABILITIES",
8275
+ l2Ports: []
8276
+ },
8277
+ mail: {
8278
+ packageName: "@pattern-stack/codegen-mail",
8279
+ portType: "MailPort",
8280
+ capabilitiesType: "MailCapabilities",
8281
+ noCapsConst: "NO_MAIL_CAPABILITIES",
8282
+ l2Ports: []
8283
+ },
8284
+ transcript: {
8285
+ packageName: "@pattern-stack/codegen-transcript",
8286
+ portType: "TranscriptPort",
8287
+ capabilitiesType: "TranscriptCapabilities",
8288
+ noCapsConst: "NO_TRANSCRIPT_CAPABILITIES",
8289
+ l2Ports: []
8290
+ }
8291
+ };
8292
+ var SCAFFOLD_SENTINEL = "// <CODEGEN-SCAFFOLD-V1>";
8293
+ function generatedBanner(sourceDesc) {
8294
+ return `// @generated by @pattern-stack/codegen from ${sourceDesc} \u2014 DO NOT EDIT.
8295
+ // Hand edits are overwritten on re-emit. Regenerate with \`bun run codegen\`.`;
8296
+ }
8297
+ function collectEntitiesBySurface(entities) {
8298
+ const bySurface = /* @__PURE__ */ new Map();
8299
+ for (const e of entities) {
8300
+ if (!e.surface) continue;
8301
+ const list = bySurface.get(e.surface) ?? [];
8302
+ list.push(e.entity.name);
8303
+ bySurface.set(e.surface, list);
8304
+ }
8305
+ for (const [surface, list] of bySurface) {
8306
+ bySurface.set(surface, [...list].sort());
8307
+ }
8308
+ return bySurface;
8309
+ }
8310
+ function names(providerSlug, surface) {
8311
+ const providerPascal = providerPascalCase(providerSlug);
8312
+ const providerConst = providerConstantCase(providerSlug);
8313
+ const surfacePascal = providerPascalCase(surface);
8314
+ const surfaceConst = providerConstantCase(surface);
8315
+ return {
8316
+ providerPascal,
8317
+ providerConst,
8318
+ surfacePascal,
8319
+ surfaceConst,
8320
+ adapterClass: `${providerPascal}${surfacePascal}Adapter`,
8321
+ adapterModuleClass: `${providerPascal}${surfacePascal}AdapterModule`,
8322
+ providerModuleClass: `${providerPascal}ProviderModule`,
8323
+ aggregatorClass: `${surfacePascal}AdaptersModule`,
8324
+ strategyToken: `${providerConst}_AUTH_STRATEGY`,
8325
+ clientToken: `${providerConst}_CLIENT`,
8326
+ contributionsToken: `${surfaceConst}_ADAPTER_CONTRIBUTIONS`,
8327
+ entitySourcesToken: `${surfaceConst}_ENTITY_SOURCES`
8328
+ };
8329
+ }
8330
+ function generateAdapterScaffold(def, surface, entities) {
8331
+ const spec = SURFACE_REGISTRY[surface];
8332
+ if (!spec) throw new Error(`no surface package for '${surface}'`);
8333
+ const n = names(def.slug, surface);
8334
+ const client = parseImportRef(def.client.class);
8335
+ const entitiesLiteral = entities.length ? `[${entities.map((e) => `'${e}'`).join(", ")}]` : "[]";
8336
+ const surfaceTypeImports = [
8337
+ spec.portType,
8338
+ ...spec.l2Ports.map((p) => p.type),
8339
+ spec.capabilitiesType
8340
+ ].map((t) => ` ${t},`).join("\n");
8341
+ const capabilityBody = [
8342
+ ` ...${spec.noCapsConst},`,
8343
+ ...spec.l2Ports.map((p) => ` ${p.capFlag}: true,`),
8344
+ ` entities: ${entitiesLiteral},`
8345
+ ].join("\n");
8346
+ const l2Members = spec.l2Ports.map(
8347
+ (p) => ` /** L2 \u2014 fill in the provider-specific implementation. */
8348
+ readonly ${p.prop}: ${p.type} = {
8349
+ ${p.method}: async ${p.params} => {
8350
+ throw new Error('not implemented: ${n.adapterClass}.${p.prop}.${p.method}');
8351
+ },
8352
+ };`
8353
+ ).join("\n\n");
8354
+ const l2Section = l2Members ? `
8355
+ ${l2Members}
8356
+ ` : "";
8357
+ return `${SCAFFOLD_SENTINEL}
8358
+ // Scaffolded once by @pattern-stack/codegen, then author-owned. Re-running
8359
+ // codegen detects the sentinel above and SKIPS this file \u2014 your edits are safe.
8360
+ // Source: definitions/providers/${def.slug}.yaml (surface: ${surface}).
8361
+ import { Inject, Injectable } from '@nestjs/common';
8362
+ import type {
8363
+ ${surfaceTypeImports}
8364
+ } from '${spec.packageName}';
8365
+ import { ${spec.noCapsConst} } from '${spec.packageName}';
8366
+ import type {
8367
+ IAuthStrategy,
8368
+ IChangeSource,
8369
+ IEntityChangeSourceRegistry,
8370
+ } from '@pattern-stack/codegen/subsystems';
8371
+ import type { ${client.exportName} } from '${client.path}';
8372
+ import { ${n.strategyToken}, ${n.clientToken} } from '../../../providers/${def.slug}/${def.slug}.provider.module';
8373
+ import { ${n.entitySourcesToken} } from '../../${surface}-adapters.tokens';
8374
+
8375
+ @Injectable()
8376
+ export class ${n.adapterClass} implements ${spec.portType} {
8377
+ /** Declared capabilities. \`entities\` derives from \`surface: ${surface}\` entity YAML. */
8378
+ readonly capabilities: ${spec.capabilitiesType} = {
8379
+ ${capabilityBody}
8380
+ };
8381
+
8382
+ constructor(
8383
+ @Inject(${n.strategyToken}) readonly auth: IAuthStrategy,
8384
+ @Inject(${n.clientToken}) private readonly client: ${client.exportName},
8385
+ @Inject(${n.entitySourcesToken}) readonly sources: IEntityChangeSourceRegistry,
8386
+ ) {}
8387
+ ${l2Section}
8388
+ /**
8389
+ * Per-entity change sources this adapter contributes to the ${surface}
8390
+ * registry (ADR-033 \`buildChangeSource\`), keyed by entity name. The
8391
+ * surface aggregator folds these into the \`IEntityChangeSourceRegistry\`
8392
+ * bound under \`${n.entitySourcesToken}\`. Author-owned \u2014 populate one entry
8393
+ * per entity in \`capabilities.entities\`.
8394
+ */
8395
+ readonly changeSources: Record<string, IChangeSource<unknown>> = {};
8396
+
8397
+ // surface-only methods (optional on ${spec.portType}): add here
8398
+ }
8399
+ `;
8400
+ }
8401
+ function generateAdapterModule(def, surface) {
8402
+ const n = names(def.slug, surface);
8403
+ return `${generatedBanner(`definitions/providers/${def.slug}.yaml (surface: ${surface})`)}
8404
+ import { Module } from '@nestjs/common';
8405
+ import { ${n.providerModuleClass} } from '../../../providers/${def.slug}/${def.slug}.provider.module';
8406
+ import { ${n.adapterClass} } from './${def.slug}-${surface}.adapter';
8407
+
8408
+ @Module({
8409
+ imports: [${n.providerModuleClass}],
8410
+ providers: [${n.adapterClass}],
8411
+ exports: [${n.adapterClass}],
8412
+ })
8413
+ export class ${n.adapterModuleClass} {}
8414
+ `;
8415
+ }
8416
+ function generateAdaptersBarrel(surface, providerSlugs) {
8417
+ const lines = [...providerSlugs].sort().map((slug) => {
8418
+ const n = names(slug, surface);
8419
+ return `export { ${n.adapterModuleClass} } from './${slug}/${slug}-${surface}.adapter.module';`;
8420
+ }).join("\n");
8421
+ return `${generatedBanner(`definitions/providers/*.yaml (surface: ${surface})`)}
8422
+ ${lines}
8423
+ `;
8424
+ }
8425
+ function generateSurfaceTokens(surface) {
8426
+ const n = names("__placeholder__", surface);
8427
+ return `${generatedBanner(`surface: ${surface}`)}
8428
+ import type { IChangeSource } from '@pattern-stack/codegen/subsystems';
8429
+
8430
+ /** The assembled list of every ${surface} adapter's contribution. */
8431
+ export const ${n.contributionsToken} = Symbol.for('@app/integrations/${surface}.adapter-contributions');
8432
+
8433
+ /** Resolved registry token \u2014 resolves to a C7 IEntityChangeSourceRegistry. */
8434
+ export const ${n.entitySourcesToken} = Symbol.for('@app/integrations/${surface}.entity-sources');
8435
+
8436
+ /** One provider-adapter's contribution to the surface registry. */
8437
+ export interface AdapterContribution {
8438
+ /** Provider slug. */
8439
+ provider: string;
8440
+ /** Entities this provider serves on this surface \u2192 their change sources. */
8441
+ sources: Record<string, IChangeSource<unknown>>;
8442
+ }
8443
+ `;
8444
+ }
8445
+ function generateSurfaceAggregator(surface, providerSlugs) {
8446
+ const n = names("__placeholder__", surface);
8447
+ const slugs = [...providerSlugs].sort();
8448
+ const per = slugs.map((slug) => names(slug, surface));
8449
+ const moduleClasses = per.map((p) => p.adapterModuleClass);
8450
+ const moduleImport = `import {
8451
+ ${moduleClasses.join(",\n ")},
8452
+ } from './adapters';`;
8453
+ const adapterImports = slugs.map((slug) => {
8454
+ const p = names(slug, surface);
8455
+ return `import { ${p.adapterClass} } from './adapters/${slug}/${slug}-${surface}.adapter';`;
8456
+ }).join("\n");
8457
+ const contributionEntries = slugs.map((slug) => {
8458
+ const p = names(slug, surface);
8459
+ return ` { provider: '${slug}', sources: ${lowerFirst(p.adapterClass)}.changeSources },`;
8460
+ }).join("\n");
8461
+ const injectParams = slugs.map((slug) => {
8462
+ const p = names(slug, surface);
8463
+ return `${lowerFirst(p.adapterClass)}: ${p.adapterClass}`;
8464
+ }).join(", ");
8465
+ const injectTokens = per.map((p) => p.adapterClass).join(", ");
8466
+ return `${generatedBanner(`surface: ${surface}`)}
8467
+ import { Module } from '@nestjs/common';
8468
+ import {
8469
+ MemoryEntityChangeSourceRegistry,
8470
+ type IChangeSource,
8471
+ type IEntityChangeSourceRegistry,
8472
+ } from '@pattern-stack/codegen/subsystems';
8473
+ ${moduleImport}
8474
+ ${adapterImports}
8475
+ import {
8476
+ ${n.contributionsToken},
8477
+ ${n.entitySourcesToken},
8478
+ type AdapterContribution,
8479
+ } from './${surface}-adapters.tokens';
8480
+
8481
+ /**
8482
+ * Fold every adapter contribution into one entity-keyed registry. Each entity
8483
+ * resolves to exactly one change source; two providers serving the same entity
8484
+ * is an ambiguous-source boot error.
8485
+ */
8486
+ function provide${n.surfacePascal}EntitySources(
8487
+ contribs: AdapterContribution[],
8488
+ ): IEntityChangeSourceRegistry {
8489
+ const merged = new Map<string, IChangeSource<unknown>>();
8490
+ const owner = new Map<string, string>();
8491
+ for (const contrib of contribs ?? []) {
8492
+ for (const [entity, source] of Object.entries(contrib.sources)) {
8493
+ const prior = owner.get(entity);
8494
+ if (prior !== undefined) {
8495
+ throw new Error(
8496
+ \`entity '\${entity}' is served by both '\${prior}' and '\${contrib.provider}' \u2014 ambiguous change source\`,
8497
+ );
8498
+ }
8499
+ owner.set(entity, contrib.provider);
8500
+ merged.set(entity, source);
8501
+ }
8502
+ }
8503
+ return new MemoryEntityChangeSourceRegistry(merged);
8504
+ }
8505
+
8506
+ @Module({
8507
+ imports: [${moduleClasses.join(", ")}],
8508
+ providers: [
8509
+ {
8510
+ provide: ${n.contributionsToken},
8511
+ useFactory: (${injectParams}): AdapterContribution[] => [
8512
+ ${contributionEntries}
8513
+ ],
8514
+ inject: [${injectTokens}],
8515
+ },
8516
+ {
8517
+ provide: ${n.entitySourcesToken},
8518
+ useFactory: (contributions: AdapterContribution[]) =>
8519
+ provide${n.surfacePascal}EntitySources(contributions),
8520
+ inject: [${n.contributionsToken}],
8521
+ },
8522
+ ],
8523
+ exports: [${n.entitySourcesToken}, ${n.contributionsToken}],
8524
+ })
8525
+ export class ${n.aggregatorClass} {}
8526
+ `;
8527
+ }
8528
+ function lowerFirst(s) {
8529
+ return s.length ? s[0].toLowerCase() + s.slice(1) : s;
8530
+ }
8531
+ function generateTypedView(surface, providerSlugs, entities) {
8532
+ const surfacePascal = providerPascalCase(surface);
8533
+ const slugs = [...providerSlugs].sort();
8534
+ const ents = [...entities].sort();
8535
+ const providerUnion = slugs.length ? slugs.map((s) => `'${s}'`).join(" | ") : "never";
8536
+ const entityUnion = ents.length ? ents.map((e) => `'${e}'`).join(" | ") : "never";
8537
+ const mapEntries = slugs.map((s) => ` ${jsKey(s)}: ${surfacePascal}Entity;`).join("\n");
8538
+ return `${generatedBanner(`surface: ${surface}`)}
8539
+ /**
8540
+ * Per-consumer typed view for the \`${surface}\` surface. Surface-scoped unions
8541
+ * + a (provider, entity) validity map for compile-time-checked consumer
8542
+ * use-cases. Replaces ADR-033.2's per-entity provider tuples (RFC-0001 \xA75/\xA78).
8543
+ */
8544
+
8545
+ /** Providers whose \`surfaces[]\` include \`${surface}\`. */
8546
+ export type ${surfacePascal}Provider = ${providerUnion};
8547
+
8548
+ /** Entities declared with \`surface: ${surface}\`. */
8549
+ export type ${surfacePascal}Entity = ${entityUnion};
8550
+
8551
+ /** Valid entities per provider on this surface. */
8552
+ export interface ${surfacePascal}ProviderEntities {
8553
+ ${mapEntries || " // no providers serve this surface yet"}
8554
+ }
8555
+ `;
8556
+ }
8557
+ function jsKey(key) {
8558
+ return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : `'${key}'`;
8559
+ }
8560
+ function emitAdapters(opts) {
8561
+ const result = {
8562
+ written: [],
8563
+ scaffoldsWritten: [],
8564
+ scaffoldsSkipped: [],
8565
+ skippedSurfaces: []
8566
+ };
8567
+ const entitiesBySurface = collectEntitiesBySurface(opts.entities);
8568
+ const bySurface = /* @__PURE__ */ new Map();
8569
+ for (const { definition } of opts.providers) {
8570
+ for (const surface of definition.surfaces) {
8571
+ if (!SURFACE_REGISTRY[surface]) {
8572
+ result.skippedSurfaces.push({
8573
+ provider: definition.slug,
8574
+ surface,
8575
+ reason: `no surface package for '${surface}' yet \u2014 add codegen-${surface} (Track C) to emit its adapters`
8576
+ });
8577
+ continue;
8578
+ }
8579
+ const list = bySurface.get(surface) ?? [];
8580
+ list.push(definition.slug);
8581
+ bySurface.set(surface, list);
8582
+ }
8583
+ }
8584
+ const defBySlug = new Map(opts.providers.map((p) => [p.definition.slug, p.definition]));
8585
+ for (const [surface, slugs] of bySurface) {
8586
+ const surfaceDir = join12(opts.outputRoot, surface);
8587
+ const adaptersDir = join12(surfaceDir, "adapters");
8588
+ for (const slug of slugs) {
8589
+ const def = defBySlug.get(slug);
8590
+ const providerDir = join12(adaptersDir, slug);
8591
+ const scaffoldPath = join12(providerDir, `${slug}-${surface}.adapter.ts`);
8592
+ const modulePath = join12(providerDir, `${slug}-${surface}.adapter.module.ts`);
8593
+ if (existsSync10(scaffoldPath)) {
8594
+ result.scaffoldsSkipped.push(scaffoldPath);
8595
+ } else {
8596
+ const content = generateAdapterScaffold(
8597
+ def,
8598
+ surface,
8599
+ entitiesBySurface.get(surface) ?? []
8600
+ );
8601
+ if (!opts.dryRun) writeFile(scaffoldPath, content);
8602
+ result.scaffoldsWritten.push(scaffoldPath);
8603
+ }
8604
+ const moduleContent = generateAdapterModule(def, surface);
8605
+ if (!opts.dryRun) writeIfChanged2(modulePath, moduleContent);
8606
+ result.written.push(modulePath);
8607
+ }
8608
+ const barrelPath = join12(adaptersDir, "index.ts");
8609
+ const tokensPath = join12(surfaceDir, `${surface}-adapters.tokens.ts`);
8610
+ const aggregatorPath = join12(surfaceDir, `${surface}-adapters.module.ts`);
8611
+ const typedViewPath = join12(surfaceDir, "types.generated.ts");
8612
+ const files = [
8613
+ [barrelPath, generateAdaptersBarrel(surface, slugs)],
8614
+ [tokensPath, generateSurfaceTokens(surface)],
8615
+ [aggregatorPath, generateSurfaceAggregator(surface, slugs)],
8616
+ [typedViewPath, generateTypedView(surface, slugs, entitiesBySurface.get(surface) ?? [])]
8617
+ ];
8618
+ for (const [path34, content] of files) {
8619
+ if (!opts.dryRun) writeIfChanged2(path34, content);
8620
+ result.written.push(path34);
8621
+ }
8622
+ }
8623
+ return result;
8624
+ }
8625
+ function writeFile(outPath, content) {
8626
+ mkdirSync3(dirname2(outPath), { recursive: true });
8627
+ writeFileSync3(outPath, content);
8628
+ }
8629
+ function writeIfChanged2(outPath, content) {
8630
+ if (existsSync10(outPath) && statSync6(outPath).isFile() && readFileSync8(outPath, "utf-8") === content) {
8631
+ return;
8632
+ }
8633
+ writeFile(outPath, content);
8634
+ }
8635
+
7758
8636
  // src/cli/shared/events-path.ts
7759
8637
  import path12 from "path";
7760
8638
  var FALLBACK = "events";
@@ -8264,6 +9142,92 @@ var EntityNewCommand = class extends Command2 {
8264
9142
  }
8265
9143
  }
8266
9144
  }
9145
+ let providerResult = null;
9146
+ try {
9147
+ const providersDir = ctx.config?.paths?.providers != null ? path13.resolve(
9148
+ ctx.cwd,
9149
+ ctx.config.paths.providers
9150
+ ) : path13.resolve(ctx.cwd, "definitions/providers");
9151
+ const providerOutputRoot = path13.resolve(
9152
+ ctx.cwd,
9153
+ backendSrcForHandlers,
9154
+ "integrations/providers"
9155
+ );
9156
+ const entitySurfaces = fs9.existsSync(entitiesDir) ? collectEntitySurfaces(
9157
+ loadEntitiesFromYaml(findYamlFiles(entitiesDir)).successes.map(
9158
+ (s) => s.definition
9159
+ )
9160
+ ) : /* @__PURE__ */ new Set();
9161
+ const tsAliases = resolveTsconfigAliases(ctx.cwd);
9162
+ providerResult = generateProviderModules({
9163
+ providersDir,
9164
+ outputRoot: providerOutputRoot,
9165
+ entitySurfaces,
9166
+ sourceRoot: tsAliases?.sourceRoot,
9167
+ aliases: tsAliases?.aliases,
9168
+ skipImportCheck: tsAliases === null
9169
+ });
9170
+ if (!providerResult.skipped && !isJsonMode()) {
9171
+ for (const issue of providerResult.issues) {
9172
+ printError(`provider codegen: ${issue.message}`);
9173
+ }
9174
+ if (providerResult.issues.length === 0) {
9175
+ printInfo(
9176
+ `provider modules regenerated (${providerResult.written.length}) \u2192 ${providerOutputRoot}`
9177
+ );
9178
+ }
9179
+ }
9180
+ if (providerResult.issues.some((i) => i.severity === "error") && !this.continueOnError) {
9181
+ return 1;
9182
+ }
9183
+ } catch (err) {
9184
+ const msg = err instanceof Error ? err.message : String(err);
9185
+ if (!isJsonMode()) {
9186
+ printWarning(`provider codegen failed \u2014 ${msg}`);
9187
+ }
9188
+ }
9189
+ try {
9190
+ if (providerResult && !providerResult.skipped && providerResult.issues.length === 0) {
9191
+ const providersDir = providerResult.providersDir;
9192
+ const adapterOutputRoot = path13.resolve(
9193
+ ctx.cwd,
9194
+ backendSrcForHandlers,
9195
+ "integrations"
9196
+ );
9197
+ const entityDefs = fs9.existsSync(entitiesDir) ? loadEntitiesFromYaml(findYamlFiles(entitiesDir)).successes.map(
9198
+ (s) => s.definition
9199
+ ) : [];
9200
+ const loadedProviders = loadProvidersFromYaml(
9201
+ findYamlFiles(providersDir)
9202
+ ).successes.map((s) => ({
9203
+ definition: s.definition,
9204
+ filePath: s.filePath
9205
+ }));
9206
+ const adapterResult = emitAdapters({
9207
+ providers: loadedProviders,
9208
+ entities: entityDefs,
9209
+ outputRoot: adapterOutputRoot
9210
+ });
9211
+ if (!isJsonMode()) {
9212
+ if (adapterResult.written.length || adapterResult.scaffoldsWritten.length) {
9213
+ printInfo(
9214
+ `adapter codegen: ${adapterResult.scaffoldsWritten.length} scaffold(s) + ${adapterResult.written.length} @generated \u2192 ${adapterOutputRoot}`
9215
+ );
9216
+ }
9217
+ for (const s of adapterResult.scaffoldsSkipped) {
9218
+ printInfo(`skipped scaffold ${s} (author-owned)`);
9219
+ }
9220
+ for (const s of adapterResult.skippedSurfaces) {
9221
+ printWarning(`adapter codegen: ${s.reason} (provider ${s.provider})`);
9222
+ }
9223
+ }
9224
+ }
9225
+ } catch (err) {
9226
+ const msg = err instanceof Error ? err.message : String(err);
9227
+ if (!isJsonMode()) {
9228
+ printWarning(`adapter codegen failed \u2014 ${msg}`);
9229
+ }
9230
+ }
8267
9231
  if (isJsonMode()) {
8268
9232
  printJson({
8269
9233
  command: "entity new",
@@ -8627,22 +9591,22 @@ function localsToHygenArgs2(locals) {
8627
9591
  ];
8628
9592
  }
8629
9593
 
8630
- // src/cli/shared/sync-scaffold-locals.ts
9594
+ // src/cli/shared/integration-scaffold-locals.ts
8631
9595
  import path16 from "path";
8632
- function resolveSyncScaffoldLocals(input) {
9596
+ function resolveIntegrationScaffoldLocals(input) {
8633
9597
  const { cwd, config } = input;
8634
9598
  void input.fileExists;
8635
- const syncBlock = config?.sync ?? {};
9599
+ const integrationBlock = config?.integration ?? {};
8636
9600
  const subsystemsRoot = resolveSubsystemsRootFromConfig(cwd, config);
8637
9601
  const configPath = path16.resolve(cwd, "codegen.config.yaml");
8638
9602
  const schemaPath = path16.resolve(
8639
9603
  subsystemsRoot,
8640
- "sync",
8641
- "sync-audit.schema.ts"
9604
+ "integration",
9605
+ "integration-audit.schema.ts"
8642
9606
  );
8643
9607
  return {
8644
9608
  appName: path16.basename(cwd),
8645
- multiTenant: normaliseMultiTenant3(syncBlock.multi_tenant),
9609
+ multiTenant: normaliseMultiTenant3(integrationBlock.multi_tenant),
8646
9610
  configPath,
8647
9611
  schemaPath
8648
9612
  };
@@ -8781,19 +9745,16 @@ function localsToHygenArgs6(locals) {
8781
9745
  // src/cli/shared/auth-integrations-scaffold-locals.ts
8782
9746
  import path20 from "path";
8783
9747
  var FALLBACK_BACKEND_SRC4 = "src";
8784
- var SHARED_DIR_NAME = "shared";
8785
9748
  var DEFAULT_MODULES_DIR = "modules";
8786
9749
  var DEFAULT_DEFINITIONS_DIR = "definitions/entities";
8787
9750
  function resolveAuthIntegrationsScaffoldLocals(input) {
8788
9751
  const { cwd, config } = input;
8789
9752
  const backendSrc = typeof config?.paths?.backend_src === "string" && config.paths.backend_src.length > 0 ? config.paths.backend_src : FALLBACK_BACKEND_SRC4;
8790
9753
  const pathsAny = config?.paths;
8791
- const sharedConfigured = pathsAny?.shared;
8792
- const sharedRoot = typeof sharedConfigured === "string" && sharedConfigured.length > 0 ? path20.resolve(cwd, sharedConfigured) : path20.resolve(cwd, backendSrc, SHARED_DIR_NAME);
8793
9754
  const modulesConfigured = pathsAny?.modules_dir;
8794
9755
  const vendorRoot = typeof modulesConfigured === "string" && modulesConfigured.length > 0 ? path20.resolve(cwd, modulesConfigured) : path20.resolve(cwd, backendSrc, DEFAULT_MODULES_DIR);
8795
9756
  const entitiesConfigured = typeof pathsAny?.entities === "string" && pathsAny.entities.length > 0 ? pathsAny.entities : typeof pathsAny?.entities_dir === "string" && pathsAny.entities_dir.length > 0 ? pathsAny.entities_dir : null;
8796
- const definitionsPath = entitiesConfigured !== null ? path20.resolve(cwd, entitiesConfigured, "integration.yaml") : path20.resolve(cwd, DEFAULT_DEFINITIONS_DIR, "integration.yaml");
9757
+ const definitionsPath = entitiesConfigured !== null ? path20.resolve(cwd, entitiesConfigured, "connection.yaml") : path20.resolve(cwd, DEFAULT_DEFINITIONS_DIR, "connection.yaml");
8797
9758
  const appModulePath = path20.resolve(cwd, backendSrc, "app.module.ts");
8798
9759
  let authModuleRegistered = false;
8799
9760
  const appModuleSource = input.readFile(appModulePath);
@@ -8803,7 +9764,6 @@ function resolveAuthIntegrationsScaffoldLocals(input) {
8803
9764
  return {
8804
9765
  appName: path20.basename(cwd),
8805
9766
  appModulePath,
8806
- sharedRoot,
8807
9767
  vendorRoot,
8808
9768
  definitionsPath,
8809
9769
  authModuleRegistered
@@ -9002,7 +9962,7 @@ function backendFileFilter(backend, subsystemName) {
9002
9962
  if (subsystemName === "events" && file === "domain-events.schema.ts") {
9003
9963
  return false;
9004
9964
  }
9005
- if (subsystemName === "sync" && file === "sync-audit.schema.ts") {
9965
+ if (subsystemName === "integration" && file === "integration-audit.schema.ts") {
9006
9966
  return false;
9007
9967
  }
9008
9968
  if (subsystemName === "auth" && file === "auth-oauth-state.schema.ts") {
@@ -9126,7 +10086,7 @@ var SubsystemInstallCommand = class extends Command3 {
9126
10086
  json: isJsonMode(),
9127
10087
  forceConfig: this.forceConfig
9128
10088
  }) : null;
9129
- const syncScaffold = desc3.name === "sync" ? runSyncScaffold(ctx.cwd, ctx.config, {
10089
+ const integrationScaffold = desc3.name === "integration" ? runIntegrationScaffold(ctx.cwd, ctx.config, {
9130
10090
  dryRun: this.dryRun,
9131
10091
  json: isJsonMode(),
9132
10092
  forceConfig: this.forceConfig
@@ -9158,9 +10118,9 @@ var SubsystemInstallCommand = class extends Command3 {
9158
10118
  );
9159
10119
  return 1;
9160
10120
  }
9161
- if (syncScaffold?.configBlockOutcome === "parse-error") {
10121
+ if (integrationScaffold?.configBlockOutcome === "parse-error") {
9162
10122
  printError(
9163
- "codegen.config.yaml is not valid YAML: refusing to inject sync config block. Fix the YAML and re-run."
10123
+ "codegen.config.yaml is not valid YAML: refusing to inject integration config block. Fix the YAML and re-run."
9164
10124
  );
9165
10125
  return 1;
9166
10126
  }
@@ -9198,7 +10158,7 @@ var SubsystemInstallCommand = class extends Command3 {
9198
10158
  },
9199
10159
  ...jobsScaffold ? { scaffold: jobsScaffold } : {},
9200
10160
  ...eventsScaffold ? { scaffold: eventsScaffold } : {},
9201
- ...syncScaffold ? { scaffold: syncScaffold } : {},
10161
+ ...integrationScaffold ? { scaffold: integrationScaffold } : {},
9202
10162
  ...bridgeScaffold ? { scaffold: bridgeScaffold } : {},
9203
10163
  ...observabilityScaffold ? { scaffold: observabilityScaffold } : {},
9204
10164
  ...authScaffold ? { scaffold: authScaffold } : {}
@@ -9226,11 +10186,11 @@ var SubsystemInstallCommand = class extends Command3 {
9226
10186
  console.log(` ${theme.muted(icons.arrow)} ${path22.relative(ctx.cwd, p) || p}`);
9227
10187
  }
9228
10188
  }
9229
- if (syncScaffold?.planned?.length) {
10189
+ if (integrationScaffold?.planned?.length) {
9230
10190
  printInfo(
9231
- `Sync scaffold \u2014 ${syncScaffold.planned.length} template targets`
10191
+ `Integration scaffold \u2014 ${integrationScaffold.planned.length} template targets`
9232
10192
  );
9233
- for (const p of syncScaffold.planned) {
10193
+ for (const p of integrationScaffold.planned) {
9234
10194
  console.log(` ${theme.muted(icons.arrow)} ${path22.relative(ctx.cwd, p) || p}`);
9235
10195
  }
9236
10196
  }
@@ -9289,14 +10249,14 @@ var SubsystemInstallCommand = class extends Command3 {
9289
10249
  );
9290
10250
  }
9291
10251
  }
9292
- if (syncScaffold) {
9293
- if (syncScaffold.ok) {
10252
+ if (integrationScaffold) {
10253
+ if (integrationScaffold.ok) {
9294
10254
  printSuccess(
9295
- `sync scaffold applied (config block, schema)`
10255
+ `integration scaffold applied (config block, schema)`
9296
10256
  );
9297
10257
  } else {
9298
10258
  printWarning(
9299
- `sync scaffold (Hygen) failed \u2014 runtime files were written; re-run after fixing: ${syncScaffold.error ?? "unknown error"}`
10259
+ `integration scaffold (Hygen) failed \u2014 runtime files were written; re-run after fixing: ${integrationScaffold.error ?? "unknown error"}`
9300
10260
  );
9301
10261
  }
9302
10262
  }
@@ -9343,7 +10303,7 @@ var SubsystemInstallCommand = class extends Command3 {
9343
10303
  }
9344
10304
  if (desc3.name === "observability") {
9345
10305
  printInfo(
9346
- "Register `ObservabilityModule.forRoot()` AFTER Events/Jobs/Bridge/Sync in app.module.ts"
10306
+ "Register `ObservabilityModule.forRoot()` AFTER Events/Jobs/Bridge/Integration in app.module.ts"
9347
10307
  );
9348
10308
  } else if (desc3.name === "auth") {
9349
10309
  printInfo("auth subsystem installed.");
@@ -9357,9 +10317,9 @@ var SubsystemInstallCommand = class extends Command3 {
9357
10317
  `Register ${capitalize(desc3.name)}Module.forRoot({ backend: '${backend}' }) in your app.module.ts`
9358
10318
  );
9359
10319
  }
9360
- if (desc3.name === "sync") {
10320
+ if (desc3.name === "integration") {
9361
10321
  printInfo(
9362
- `Per-entity: register ExecuteSyncUseCase + your IChangeSource/ISyncSink bindings in a feature module (see SyncModule docstring).`
10322
+ `Per-entity: register ExecuteIntegrationUseCase + your IChangeSource/IIntegrationSink bindings in a feature module (see IntegrationModule docstring).`
9363
10323
  );
9364
10324
  }
9365
10325
  return 0;
@@ -9372,7 +10332,7 @@ var SubsystemInstallCommand = class extends Command3 {
9372
10332
  * `codegen project init`. This method just invokes the
9373
10333
  * `subsystem/openapi-config` Hygen action to inject the `openapi:`
9374
10334
  * block into `codegen.config.yaml`, honoring the same `--force-config`
9375
- * semantics as jobs/events/sync/bridge.
10335
+ * semantics as jobs/events/integration/bridge.
9376
10336
  */
9377
10337
  async executeOpenApiConfig(ctx) {
9378
10338
  const configPath = path22.join(ctx.cwd, "codegen.config.yaml");
@@ -9435,9 +10395,9 @@ var SubsystemInstallCommand = class extends Command3 {
9435
10395
  *
9436
10396
  * Source is `examples/auth-integrations/`, NOT `runtime/subsystems/`,
9437
10397
  * so this method short-circuits the `copyRuntime` flow. It vendors the
9438
- * adapters tree + the canonical `integration.yaml`, then invokes the
10398
+ * adapters tree + the canonical `connection.yaml`, then invokes the
9439
10399
  * `subsystem auth-integrations` Hygen action to append the
9440
- * `IntegrationsAuthModule` TODO to `app.module.ts`.
10400
+ * `ConnectionsAuthModule` TODO to `app.module.ts`.
9441
10401
  *
9442
10402
  * Idempotent: pre-existing files are skipped unless `--force` is set.
9443
10403
  */
@@ -9485,7 +10445,7 @@ var SubsystemInstallCommand = class extends Command3 {
9485
10445
  }
9486
10446
  if (this.dryRun) {
9487
10447
  printInfo(
9488
- `Dry run \u2014 auth-integrations would vendor adapters + integration.yaml + append TODO`
10448
+ `Dry run \u2014 auth-integrations would vendor adapters + connection.yaml + append TODO`
9489
10449
  );
9490
10450
  for (const p of scaffold.planned) {
9491
10451
  console.log(
@@ -9501,14 +10461,14 @@ var SubsystemInstallCommand = class extends Command3 {
9501
10461
  );
9502
10462
  if (scaffold.authModuleRegistered === false) {
9503
10463
  printWarning(
9504
- "AuthModule.forRoot(...) not detected in app.module.ts. Run `cdp subsystem install auth` first \u2014 IntegrationsAuthModule requires ENCRYPTION_KEY from it."
10464
+ "AuthModule.forRoot(...) not detected in app.module.ts. Run `cdp subsystem install auth` first \u2014 ConnectionsAuthModule requires ENCRYPTION_KEY from it."
9505
10465
  );
9506
10466
  }
9507
10467
  printInfo("auth-integrations starter vendored.");
9508
10468
  printInfo("Next steps:");
9509
- printInfo(" 1. Run `cdp entity new integration` to scaffold the codegen layer (apps/api/src/modules/integrations/integration.service) the adapters import.");
10469
+ printInfo(" 1. Run `cdp entity new connection` to scaffold the codegen layer (apps/api/src/modules/connections/connection.service) the adapters import.");
9510
10470
  printInfo(" 2. Ensure AuthModule.forRoot(...) is registered in AppModule (run `cdp subsystem install auth` if not).");
9511
- printInfo(" 3. Wire IntegrationsAuthModule into AppModule (see TODO appended to app.module.ts).");
10471
+ printInfo(" 3. Wire ConnectionsAuthModule into AppModule (see TODO appended to app.module.ts).");
9512
10472
  return 0;
9513
10473
  }
9514
10474
  };
@@ -9689,8 +10649,8 @@ function runEventsScaffold(cwd, config, opts) {
9689
10649
  }
9690
10650
  return { ok: true, planned, configBlockOutcome };
9691
10651
  }
9692
- function runSyncScaffold(cwd, config, opts) {
9693
- const locals = resolveSyncScaffoldLocals({
10652
+ function runIntegrationScaffold(cwd, config, opts) {
10653
+ const locals = resolveIntegrationScaffoldLocals({
9694
10654
  cwd,
9695
10655
  config,
9696
10656
  fileExists: (p) => fs11.existsSync(p)
@@ -9701,7 +10661,7 @@ function runSyncScaffold(cwd, config, opts) {
9701
10661
  ];
9702
10662
  const configBlockOutcome = planConfigBlockAction(
9703
10663
  locals.configPath,
9704
- "sync",
10664
+ "integration",
9705
10665
  opts.forceConfig
9706
10666
  );
9707
10667
  if (configBlockOutcome === "parse-error") {
@@ -9712,7 +10672,7 @@ function runSyncScaffold(cwd, config, opts) {
9712
10672
  }
9713
10673
  const result = invokeHygen({
9714
10674
  generator: "subsystem",
9715
- action: "sync",
10675
+ action: "integration",
9716
10676
  cwd,
9717
10677
  args: localsToHygenArgs3(locals),
9718
10678
  // Suppress Hygen stdout in JSON mode so it doesn't corrupt the JSON output.
@@ -9728,9 +10688,9 @@ function runSyncScaffold(cwd, config, opts) {
9728
10688
  }
9729
10689
  const configResult = runConfigBlockAction({
9730
10690
  cwd,
9731
- actionFolder: "sync-config",
10691
+ actionFolder: "integration-config",
9732
10692
  configPath: locals.configPath,
9733
- subsystem: "sync",
10693
+ subsystem: "integration",
9734
10694
  outcome: configBlockOutcome,
9735
10695
  json: opts.json
9736
10696
  });
@@ -9981,18 +10941,18 @@ function runAuthIntegrationsScaffold(cwd, config, opts) {
9981
10941
  error: `auth-integrations starter source missing: ${examplesRoot}`
9982
10942
  };
9983
10943
  }
9984
- const adaptersSrc = path22.join(examplesRoot, "runtime", "integrations");
9985
- const adaptersDest = path22.join(locals.vendorRoot, "integrations");
9986
- const integrationYamlSrc = path22.join(
10944
+ const adaptersSrc = path22.join(examplesRoot, "runtime", "connections");
10945
+ const adaptersDest = path22.join(locals.vendorRoot, "connections");
10946
+ const connectionYamlSrc = path22.join(
9987
10947
  examplesRoot,
9988
10948
  "definitions",
9989
10949
  "entities",
9990
- "integration.yaml"
10950
+ "connection.yaml"
9991
10951
  );
9992
- const integrationYamlDest = locals.definitionsPath;
10952
+ const connectionYamlDest = locals.definitionsPath;
9993
10953
  const planned = [
9994
10954
  adaptersDest,
9995
- integrationYamlDest,
10955
+ connectionYamlDest,
9996
10956
  locals.appModulePath
9997
10957
  ];
9998
10958
  if (opts.dryRun) {
@@ -10012,18 +10972,18 @@ function runAuthIntegrationsScaffold(cwd, config, opts) {
10012
10972
  let yamlWritten = false;
10013
10973
  let yamlSkipped = false;
10014
10974
  try {
10015
- if (fs11.existsSync(integrationYamlDest) && !opts.force) {
10975
+ if (fs11.existsSync(connectionYamlDest) && !opts.force) {
10016
10976
  yamlSkipped = true;
10017
- } else if (fs11.existsSync(integrationYamlSrc)) {
10018
- fs11.mkdirSync(path22.dirname(integrationYamlDest), { recursive: true });
10019
- fs11.copyFileSync(integrationYamlSrc, integrationYamlDest);
10977
+ } else if (fs11.existsSync(connectionYamlSrc)) {
10978
+ fs11.mkdirSync(path22.dirname(connectionYamlDest), { recursive: true });
10979
+ fs11.copyFileSync(connectionYamlSrc, connectionYamlDest);
10020
10980
  yamlWritten = true;
10021
10981
  }
10022
10982
  } catch (err) {
10023
10983
  return {
10024
10984
  ok: false,
10025
10985
  planned,
10026
- error: `failed to vendor integration.yaml: ${err instanceof Error ? err.message : String(err)}`,
10986
+ error: `failed to vendor connection.yaml: ${err instanceof Error ? err.message : String(err)}`,
10027
10987
  authModuleRegistered: locals.authModuleRegistered
10028
10988
  };
10029
10989
  }
@@ -10039,16 +10999,16 @@ function runAuthIntegrationsScaffold(cwd, config, opts) {
10039
10999
  ok: false,
10040
11000
  planned,
10041
11001
  error: result.stderr?.trim() || "hygen exited non-zero",
10042
- written: adapterCopy.written.concat(yamlWritten ? [integrationYamlDest] : []),
10043
- skipped: adapterCopy.skipped.concat(yamlSkipped ? [integrationYamlDest] : []),
11002
+ written: adapterCopy.written.concat(yamlWritten ? [connectionYamlDest] : []),
11003
+ skipped: adapterCopy.skipped.concat(yamlSkipped ? [connectionYamlDest] : []),
10044
11004
  authModuleRegistered: locals.authModuleRegistered
10045
11005
  };
10046
11006
  }
10047
11007
  return {
10048
11008
  ok: true,
10049
11009
  planned,
10050
- written: adapterCopy.written.concat(yamlWritten ? [integrationYamlDest] : []),
10051
- skipped: adapterCopy.skipped.concat(yamlSkipped ? [integrationYamlDest] : []),
11010
+ written: adapterCopy.written.concat(yamlWritten ? [connectionYamlDest] : []),
11011
+ skipped: adapterCopy.skipped.concat(yamlSkipped ? [connectionYamlDest] : []),
10052
11012
  authModuleRegistered: locals.authModuleRegistered
10053
11013
  };
10054
11014
  }
@@ -10151,7 +11111,7 @@ var SubsystemRemoveCommand = class extends Command3 {
10151
11111
  "auth-integrations is vendored under <modules>/integrations/ alongside the codegen-emitted entity layer \u2014 not auto-removable here."
10152
11112
  );
10153
11113
  printInfo(
10154
- "To uninstall: remove the integrations/ directory and the IntegrationsAuthModule registration from app.module.ts by hand."
11114
+ "To uninstall: remove the integrations/ directory and the ConnectionsAuthModule registration from app.module.ts by hand."
10155
11115
  );
10156
11116
  return 1;
10157
11117
  }
@@ -10270,11 +11230,11 @@ var VENDORED_RUNTIME_FILES = [
10270
11230
  // Ambient tenant scope — imported by base-repository.ts (scopePredicate)
10271
11231
  { runtime: "base-classes/tenant-context.ts", target: "src/shared/base-classes/tenant-context.ts" },
10272
11232
  { runtime: "base-classes/base-service.ts", target: "src/shared/base-classes/base-service.ts" },
10273
- { runtime: "base-classes/synced-entity-repository.ts", target: "src/shared/base-classes/synced-entity-repository.ts" },
10274
- { runtime: "base-classes/synced-entity-service.ts", target: "src/shared/base-classes/synced-entity-service.ts" },
10275
- // Inbound-sync write surface (#374) — deps of synced/junction repos
10276
- { runtime: "base-classes/sync-upsert-config.ts", target: "src/shared/base-classes/sync-upsert-config.ts" },
10277
- { runtime: "base-classes/junction-sync-repository.ts", target: "src/shared/base-classes/junction-sync-repository.ts" },
11233
+ { runtime: "base-classes/integrated-entity-repository.ts", target: "src/shared/base-classes/integrated-entity-repository.ts" },
11234
+ { runtime: "base-classes/integrated-entity-service.ts", target: "src/shared/base-classes/integrated-entity-service.ts" },
11235
+ // Inbound-integration write surface (#374) — deps of integrated/junction repos
11236
+ { runtime: "base-classes/integration-upsert-config.ts", target: "src/shared/base-classes/integration-upsert-config.ts" },
11237
+ { runtime: "base-classes/junction-integration-repository.ts", target: "src/shared/base-classes/junction-integration-repository.ts" },
10278
11238
  { runtime: "base-classes/activity-entity-repository.ts", target: "src/shared/base-classes/activity-entity-repository.ts" },
10279
11239
  { runtime: "base-classes/activity-entity-service.ts", target: "src/shared/base-classes/activity-entity-service.ts" },
10280
11240
  { runtime: "base-classes/metadata-entity-repository.ts", target: "src/shared/base-classes/metadata-entity-repository.ts" },
@@ -10539,7 +11499,7 @@ function exampleEntityYaml() {
10539
11499
  #
10540
11500
  # entity:
10541
11501
  # name: account
10542
- # pattern: Synced # Base | Synced | Activity | Metadata | Knowledge (or app-defined)
11502
+ # pattern: Integrated # Base | Integrated | Activity | Metadata | Knowledge (or app-defined)
10543
11503
  #
10544
11504
  # fields:
10545
11505
  # name:
@@ -10587,7 +11547,7 @@ var REQUIRED_ALIASES = {
10587
11547
  "@modules/*": ["./src/modules/*"],
10588
11548
  "@generated/*": ["./src/generated/*"]
10589
11549
  };
10590
- function stripJsonComments(raw) {
11550
+ function stripJsonComments2(raw) {
10591
11551
  let out = "";
10592
11552
  let i = 0;
10593
11553
  let inString = false;
@@ -10635,7 +11595,7 @@ var REQUIRED_COMPILER_OPTIONS = {
10635
11595
  function mergeTsconfig(raw) {
10636
11596
  let parsed;
10637
11597
  try {
10638
- parsed = JSON.parse(stripJsonComments(raw));
11598
+ parsed = JSON.parse(stripJsonComments2(raw));
10639
11599
  } catch (err) {
10640
11600
  return {
10641
11601
  content: raw,
@@ -11105,8 +12065,8 @@ function ensureMainSwaggerBlock(sourceFile, opts) {
11105
12065
  /^import\s+\{\s*([^}]+)\s*\}\s+from\s+['"]([^'"]+)['"]\s*;?\s*$/
11106
12066
  );
11107
12067
  if (match) {
11108
- const names = match[1].split(",").map((s) => s.trim()).filter(Boolean);
11109
- ensureImport(sourceFile, match[2], names);
12068
+ const names2 = match[1].split(",").map((s) => s.trim()).filter(Boolean);
12069
+ ensureImport(sourceFile, match[2], names2);
11110
12070
  } else {
11111
12071
  sourceFile.insertStatements(0, importLine);
11112
12072
  }
@@ -12112,10 +13072,10 @@ var ProjectInitCommand = class extends Command7 {
12112
13072
  };
12113
13073
  function askConfirm(question) {
12114
13074
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
12115
- return new Promise((resolve4) => {
13075
+ return new Promise((resolve6) => {
12116
13076
  rl.question(`${question} [Y/n] `, (answer) => {
12117
13077
  rl.close();
12118
- resolve4(answer.trim().toLowerCase() !== "n");
13078
+ resolve6(answer.trim().toLowerCase() !== "n");
12119
13079
  });
12120
13080
  });
12121
13081
  }
@@ -13722,7 +14682,7 @@ var junction_default = junctionNoun;
13722
14682
  // src/cli/commands/events.ts
13723
14683
  import fs20 from "fs";
13724
14684
  import path32 from "path";
13725
- import ts2 from "typescript";
14685
+ import ts3 from "typescript";
13726
14686
  import { Command as Command11, Option as Option11 } from "clipanion";
13727
14687
  function scanSourceFileForConsumers(sourceFile, filePath, eventType) {
13728
14688
  const tier2 = [];
@@ -13736,13 +14696,13 @@ function scanSourceFileForConsumers(sourceFile, filePath, eventType) {
13736
14696
  }
13737
14697
  function checkImport(node) {
13738
14698
  const moduleSpec = node.moduleSpecifier;
13739
- if (ts2.isStringLiteral(moduleSpec) && moduleSpec.text.includes("subsystems/bridge")) {
14699
+ if (ts3.isStringLiteral(moduleSpec) && moduleSpec.text.includes("subsystems/bridge")) {
13740
14700
  hasEventFlowImport = true;
13741
14701
  }
13742
14702
  const clause = node.importClause;
13743
14703
  if (!clause) return;
13744
14704
  const named = clause.namedBindings;
13745
- if (named && ts2.isNamedImports(named)) {
14705
+ if (named && ts3.isNamedImports(named)) {
13746
14706
  for (const el of named.elements) {
13747
14707
  const name = el.name.text;
13748
14708
  if (name === "EventFlowService" || name === "EVENT_FLOW") {
@@ -13752,11 +14712,11 @@ function scanSourceFileForConsumers(sourceFile, filePath, eventType) {
13752
14712
  }
13753
14713
  }
13754
14714
  function checkCall(node) {
13755
- if (!ts2.isPropertyAccessExpression(node.expression)) return;
14715
+ if (!ts3.isPropertyAccessExpression(node.expression)) return;
13756
14716
  const methodName = node.expression.name.text;
13757
14717
  if (methodName !== "publishAndStart" && methodName !== "subscribe") return;
13758
14718
  const firstArg = node.arguments[0];
13759
- if (!firstArg || !ts2.isStringLiteralLike(firstArg)) return;
14719
+ if (!firstArg || !ts3.isStringLiteralLike(firstArg)) return;
13760
14720
  if (firstArg.text !== eventType) return;
13761
14721
  const receiverText = node.expression.expression.getText(sourceFile);
13762
14722
  if (methodName === "publishAndStart") {
@@ -13777,24 +14737,24 @@ function scanSourceFileForConsumers(sourceFile, filePath, eventType) {
13777
14737
  function findEnclosingClassName(node) {
13778
14738
  let cur = node.parent;
13779
14739
  while (cur) {
13780
- if (ts2.isClassDeclaration(cur) && cur.name) return cur.name.text;
13781
- if (ts2.isClassExpression(cur) && cur.name) return cur.name.text;
14740
+ if (ts3.isClassDeclaration(cur) && cur.name) return cur.name.text;
14741
+ if (ts3.isClassExpression(cur) && cur.name) return cur.name.text;
13782
14742
  cur = cur.parent;
13783
14743
  }
13784
14744
  return null;
13785
14745
  }
13786
14746
  function checkDecoratorOn(member) {
13787
- const decorators = ts2.canHaveDecorators(member) ? ts2.getDecorators(member) ?? [] : [];
14747
+ const decorators = ts3.canHaveDecorators(member) ? ts3.getDecorators(member) ?? [] : [];
13788
14748
  for (const decorator of decorators) {
13789
14749
  const call = decorator.expression;
13790
- if (!ts2.isCallExpression(call)) continue;
13791
- if (!ts2.isIdentifier(call.expression)) continue;
14750
+ if (!ts3.isCallExpression(call)) continue;
14751
+ if (!ts3.isIdentifier(call.expression)) continue;
13792
14752
  if (call.expression.text !== "OnEvent") continue;
13793
14753
  const firstArg = call.arguments[0];
13794
- if (!firstArg || !ts2.isStringLiteralLike(firstArg)) continue;
14754
+ if (!firstArg || !ts3.isStringLiteralLike(firstArg)) continue;
13795
14755
  if (firstArg.text !== eventType) continue;
13796
14756
  const className = findEnclosingClassName(member) ?? "<anonymous>";
13797
- const memberName = ts2.isIdentifier(member.name) ? member.name.text : member.name.getText(sourceFile);
14757
+ const memberName = ts3.isIdentifier(member.name) ? member.name.text : member.name.getText(sourceFile);
13798
14758
  tier1.push({
13799
14759
  kind: "on-event",
13800
14760
  siteLabel: `${className}.${memberName} @OnEvent('${eventType}')`,
@@ -13804,12 +14764,12 @@ function scanSourceFileForConsumers(sourceFile, filePath, eventType) {
13804
14764
  }
13805
14765
  }
13806
14766
  function visit(node) {
13807
- if (ts2.isImportDeclaration(node)) checkImport(node);
13808
- if (ts2.isCallExpression(node)) checkCall(node);
13809
- if (ts2.isMethodDeclaration(node) || ts2.isPropertyDeclaration(node)) {
14767
+ if (ts3.isImportDeclaration(node)) checkImport(node);
14768
+ if (ts3.isCallExpression(node)) checkCall(node);
14769
+ if (ts3.isMethodDeclaration(node) || ts3.isPropertyDeclaration(node)) {
13810
14770
  checkDecoratorOn(node);
13811
14771
  }
13812
- ts2.forEachChild(node, visit);
14772
+ ts3.forEachChild(node, visit);
13813
14773
  }
13814
14774
  visit(sourceFile);
13815
14775
  return { tier2, tier1, hasEventFlowImport };
@@ -13821,12 +14781,12 @@ function scanDirectoryForConsumers(rootDir, eventType) {
13821
14781
  let hasEventFlowImport = false;
13822
14782
  for (const filePath of files) {
13823
14783
  const text2 = fs20.readFileSync(filePath, "utf8");
13824
- const sourceFile = ts2.createSourceFile(
14784
+ const sourceFile = ts3.createSourceFile(
13825
14785
  filePath,
13826
14786
  text2,
13827
- ts2.ScriptTarget.Latest,
14787
+ ts3.ScriptTarget.Latest,
13828
14788
  true,
13829
- ts2.ScriptKind.TS
14789
+ ts3.ScriptKind.TS
13830
14790
  );
13831
14791
  const result = scanSourceFileForConsumers(sourceFile, filePath, eventType);
13832
14792
  tier2.push(...result.tier2);
@@ -14240,12 +15200,12 @@ async function summary9(ctx) {
14240
15200
  await reloadRegistry(ctx);
14241
15201
  } catch {
14242
15202
  }
14243
- const names = getOrchestrationPatternNames();
15203
+ const names2 = getOrchestrationPatternNames();
14244
15204
  return {
14245
15205
  title: "orchestration",
14246
15206
  body: [
14247
- `patterns: ${names.length}`,
14248
- ...names.length > 0 ? [` ${names.join(", ")}`] : []
15207
+ `patterns: ${names2.length}`,
15208
+ ...names2.length > 0 ? [` ${names2.join(", ")}`] : []
14249
15209
  ]
14250
15210
  };
14251
15211
  }
@@ -14294,8 +15254,8 @@ var update_default = UpdateShortcut;
14294
15254
  // src/cli/index.ts
14295
15255
  function readVersion() {
14296
15256
  try {
14297
- const pkgPath = join10(import.meta.dirname, "..", "..", "package.json");
14298
- const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
15257
+ const pkgPath = join13(import.meta.dirname, "..", "..", "package.json");
15258
+ const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
14299
15259
  return typeof pkg.version === "string" ? pkg.version : "0.0.0";
14300
15260
  } catch {
14301
15261
  return "0.0.0";