@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
@@ -1,17 +1,17 @@
1
1
  ---
2
- to: "<%= hasDetection ? (isCleanLitePs ? clpOutputPaths.syncSourceModule : `${basePaths.backendSrc}/${paths.modules}/${name}-sync-source.module.ts`) : null %>"
2
+ to: "<%= hasDetection ? (isCleanLitePs ? clpOutputPaths.integrationSourceModule : `${basePaths.backendSrc}/${paths.modules}/${name}-integration-source.module.ts`) : null %>"
3
3
  skip_if: <%= !hasDetection %>
4
4
  force: true
5
5
  ---
6
6
  <%- typeof generatedBanner !== 'undefined' ? generatedBanner : '' %>
7
7
  import { Module } from '@nestjs/common';
8
- import { buildChangeSource } from '@shared/subsystems/sync';
8
+ import { buildChangeSource } from '@shared/subsystems/integration';
9
9
  import type {
10
10
  DetectionConfig,
11
11
  IChangeSource,
12
12
  PollFetchCallback,
13
- } from '@shared/subsystems/sync';
14
- import type { <%= className %> } from '<%= isCleanLitePs ? clpImports.syncSourceToEntity : imports.moduleToDomain %>';
13
+ } from '@shared/subsystems/integration';
14
+ import type { <%= className %> } from '<%= isCleanLitePs ? clpImports.integrationSourceToEntity : imports.moduleToDomain %>';
15
15
 
16
16
  const <%= name.toUpperCase() %>_DETECTION_CONFIGS: Record<string, DetectionConfig> = <%- detectionConfigsLiteral %>;
17
17
 
@@ -31,7 +31,7 @@ export const <%= name.toUpperCase() %>_CHANGE_SOURCES = Symbol('<%= name.toUpper
31
31
  const fetch = fetches[provider];
32
32
  if (!fetch) {
33
33
  throw new Error(
34
- `<%= className %>SyncSourceModule: missing fetch callback for provider '${provider}' in <%= name.toUpperCase() %>_POLL_FETCH_REGISTRY`,
34
+ `<%= className %>IntegrationSourceModule: missing fetch callback for provider '${provider}' in <%= name.toUpperCase() %>_POLL_FETCH_REGISTRY`,
35
35
  );
36
36
  }
37
37
  return [provider, buildChangeSource(cfg, fetch)];
@@ -41,4 +41,4 @@ export const <%= name.toUpperCase() %>_CHANGE_SOURCES = Symbol('<%= name.toUpper
41
41
  ],
42
42
  exports: [<%= name.toUpperCase() %>_CHANGE_SOURCES],
43
43
  })
44
- export class <%= className %>SyncSourceModule {}
44
+ export class <%= className %>IntegrationSourceModule {}
@@ -22,6 +22,10 @@ import { type InferSelectModel } from 'drizzle-orm';
22
22
  import { <%= rel.relatedTable %> } from '<%= rel.importPath %>';
23
23
  <%_ } _%>
24
24
  <%_ }) _%>
25
+ <%_ /* #354: field-level foreign_key target table imports */ _%>
26
+ <%_ if (typeof clpFieldFkImports !== 'undefined') { clpFieldFkImports.forEach(imp => { _%>
27
+ import { <%= imp.relatedTable %> } from '<%= imp.importPath %>';
28
+ <%_ }) } _%>
25
29
  <%_ /* CGP-358b: import has_many target tables for many() relation const */ _%>
26
30
  <%_ if (typeof clpExistingHasMany !== 'undefined') { _%>
27
31
  <%_ clpExistingHasMany.filter(rel => !rel.isSelfRef).forEach(rel => { _%>
@@ -65,10 +69,15 @@ export const <%= entityNamePlural %> = pgTable(
65
69
  deletedAt: timestamp('deleted_at'),
66
70
  <%_ } _%>
67
71
  },
68
- <%_ if (hasExternalIdTracking) { _%>
72
+ <%_ /* #355/#356: pgTable extra-config — indexes + composite unique indexes + external_id unique index */ _%>
73
+ <%_ if (typeof clpTableConstraints !== 'undefined' && clpTableConstraints.length > 0) { _%>
69
74
  (t) => [
70
- // external_id_tracking behavior ON CONFLICT target for syncUpsert
71
- uniqueIndex('uq_<%= entityNamePlural %>_provider_external_id').on(t.provider, t.externalId),
75
+ <%_ clpTableConstraints.forEach(c => { _%>
76
+ <%_ if (c.comment) { _%>
77
+ // <%= c.comment %>
78
+ <%_ } _%>
79
+ <%- c.expr %>,
80
+ <%_ }) _%>
72
81
  ],
73
82
  <%_ } _%>
74
83
  );
@@ -116,7 +116,7 @@ import { <%= classNames.searchController %> } from './<%= entityName %>-search.c
116
116
  // exported so sibling modules that compose this entity cross-module (junction
117
117
  // `.list()`, EAV value→definition resolution) inject the home-module instance
118
118
  // — the only place the repo's own deps are wired (e.g. an EAV entity's repo
119
- // injects FieldValueService for the #374 sync dual-write tx). Local-providing
119
+ // injects FieldValueService for the #374 integration dual-write tx). Local-providing
120
120
  // such a repo elsewhere can't satisfy those deps. Use-case internals stay unexported.
121
121
  exports: [<%= classNames.service %>, <%= classNames.repository %>],
122
122
  })
@@ -9,7 +9,7 @@ import fs from 'node:fs';
9
9
  import path from 'node:path';
10
10
  import pluralizePkg from 'pluralize';
11
11
  // The patterns barrel has the side effect of pre-registering the five
12
- // library-shipped patterns (Base / Synced / Activity / Knowledge /
12
+ // library-shipped patterns (Base / Integrated / Activity / Knowledge /
13
13
  // Metadata). App-defined patterns are loaded separately in the parent
14
14
  // prompt.js via loadAppPatterns() against `codegen.config.yaml patterns:`
15
15
  // globs before this helper runs — we only read the registry here.
@@ -108,7 +108,7 @@ export function resolvePatternBaseClasses(entity) {
108
108
  /**
109
109
  * Resolve the behaviors implied by an entity's declared pattern(s).
110
110
  *
111
- * A pattern (e.g. `Synced`) may declare `impliedBehaviors` — behaviors the
111
+ * A pattern (e.g. `Integrated`) may declare `impliedBehaviors` — behaviors the
112
112
  * entity gets for free without re-declaring them in its `behaviors:` array.
113
113
  * Walks every declared pattern (both the `pattern: X` and `patterns: [...]`
114
114
  * shapes), unions their `impliedBehaviors`, and returns a deduped list.
@@ -145,6 +145,7 @@ const capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
145
145
  const camelCase = (s) => s.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
146
146
  const pascalCase = (s) => capitalize(camelCase(s));
147
147
  const pluralize = (s) => pluralizePkg.plural(s);
148
+ const singularize = (s) => pluralizePkg.singular(s);
148
149
 
149
150
  // ============================================================================
150
151
  // Drizzle type mapping
@@ -260,15 +261,45 @@ function buildDrizzleChain(fieldName, field, drizzleType, enumName) {
260
261
  chain += '.notNull()';
261
262
  }
262
263
 
263
- // Boolean defaults
264
- if (drizzleType === 'boolean' && hasDefault) {
265
- chain += `.default(${field.default})`;
264
+ // Column defaults (#345). The value is validated upstream (schema `default:`).
265
+ // Covers every scalar type, enum literals, and the `now` sentinel on
266
+ // timestamp/date columns — not just booleans.
267
+ if (hasDefault) {
268
+ chain += renderColumnDefault(field.default, drizzleType);
266
269
  }
267
270
 
268
- // Timestamp defaults for datetime fields in behavior context handled separately
269
271
  return chain;
270
272
  }
271
273
 
274
+ /**
275
+ * Render a Drizzle `.default(...)` (or `.defaultNow()`) suffix for a column.
276
+ *
277
+ * - timestamp/date + a `now`/`now()`/`current_timestamp` sentinel → `.defaultNow()`
278
+ * - numeric (Drizzle returns it as a string) → quoted, even for numeric YAML values
279
+ * - string / enum literal → single-quoted, escaped
280
+ * - number / boolean → bare literal
281
+ * - anything else (jsonb object/array default) → JSON literal
282
+ */
283
+ function renderColumnDefault(value, drizzleType) {
284
+ if (
285
+ (drizzleType === 'timestamp' || drizzleType === 'date') &&
286
+ typeof value === 'string' &&
287
+ /^(now|now\(\)|current_timestamp)$/i.test(value)
288
+ ) {
289
+ return '.defaultNow()';
290
+ }
291
+ if (drizzleType === 'numeric') {
292
+ return `.default('${String(value)}')`;
293
+ }
294
+ if (typeof value === 'number' || typeof value === 'boolean') {
295
+ return `.default(${value})`;
296
+ }
297
+ if (typeof value === 'string') {
298
+ return `.default('${value.replace(/\\/g, '\\\\').replace(/'/g, "\\'")}')`;
299
+ }
300
+ return `.default(${JSON.stringify(value)})`;
301
+ }
302
+
272
303
  /**
273
304
  * Process entity fields into ProcessedField[]
274
305
  */
@@ -446,10 +477,95 @@ function processBelongsTo(relationships, parentEntityNamePlural) {
446
477
  return result;
447
478
  }
448
479
 
480
+ /**
481
+ * Field-level `foreign_key:` + `index:` emission (#354, #355).
482
+ *
483
+ * Distinct from `relationships:`-driven belongs_to FKs (processBelongsTo),
484
+ * which own their own column + import. This handles features declared
485
+ * directly on a column field:
486
+ *
487
+ * - `foreign_key: <table>.<column>` → append `.references(() => <table>.<column>)`
488
+ * to the column's Drizzle chain (self-FKs get the `: AnyPgColumn` annotation)
489
+ * and record the cross-module import. The table segment is the Drizzle table
490
+ * export name (plural, e.g. `conversations`); the import path singularizes it
491
+ * to the entity file (`../conversations/conversation.entity`).
492
+ * - `index: true` → emit a named single-column index in the pgTable
493
+ * extra-config callback (`<table>_<column>_idx`).
494
+ *
495
+ * Mutates each processed field's `drizzleChain` in place (the same objects are
496
+ * later rendered via clpProcessedFields). Returns the imports + index
497
+ * expressions the template needs.
498
+ *
499
+ * @param {object[]} renderedFields the fields actually emitted as columns
500
+ * (nonFkFields — belongs_to FK columns excluded)
501
+ * @param {object} fields raw field map keyed by snake_case name
502
+ * @param {string} entityNamePlural Drizzle table export name for self-FK detection
503
+ */
504
+ function processFieldFeatures(renderedFields, fields, entityNamePlural) {
505
+ const fkImports = [];
506
+ const indexExpressions = [];
507
+ const seenImports = new Set();
508
+ let hasSelfFieldFk = false;
509
+
510
+ for (const pf of renderedFields) {
511
+ const field = fields[pf.name];
512
+ if (!field) continue;
513
+
514
+ // --- foreign_key (#354) ---
515
+ if (typeof field.foreign_key === 'string' && field.foreign_key.includes('.')) {
516
+ const [relatedTable, fkColumn] = field.foreign_key.split('.');
517
+ const isSelfFk = relatedTable === entityNamePlural;
518
+ pf.drizzleChain += isSelfFk
519
+ ? `.references((): AnyPgColumn => ${relatedTable}.${fkColumn})`
520
+ : `.references(() => ${relatedTable}.${fkColumn})`;
521
+
522
+ if (isSelfFk) {
523
+ hasSelfFieldFk = true;
524
+ } else if (!seenImports.has(relatedTable)) {
525
+ seenImports.add(relatedTable);
526
+ fkImports.push({
527
+ relatedTable,
528
+ importPath: `../${relatedTable}/${singularize(relatedTable)}.entity`,
529
+ });
530
+ }
531
+ }
532
+
533
+ // --- index: true (#355) ---
534
+ if (field.index === true) {
535
+ indexExpressions.push({
536
+ comment: null,
537
+ expr: `index('${entityNamePlural}_${pf.name}_idx').on(t.${pf.camelName})`,
538
+ });
539
+ }
540
+ }
541
+
542
+ return { fkImports, indexExpressions, hasSelfFieldFk };
543
+ }
544
+
545
+ /**
546
+ * Composite unique index emission (#356).
547
+ *
548
+ * Top-level `unique_indexes: [{ fields: [...], name? }]` → a `uniqueIndex(...)`
549
+ * entry in the pgTable extra-config callback. Column names are camelCased to
550
+ * match the emitted Drizzle column identifiers; they may reference FK columns
551
+ * (belongs_to) or ordinary fields. Index name defaults to
552
+ * `<table>_<col1>_<col2>_..._uniq`.
553
+ */
554
+ function processUniqueIndexes(uniqueIndexes, entityNamePlural) {
555
+ if (!Array.isArray(uniqueIndexes)) return [];
556
+
557
+ return uniqueIndexes.map((ui) => {
558
+ const cols = ui.fields;
559
+ const name = ui.name || `${entityNamePlural}_${cols.join('_')}_uniq`;
560
+ const onCols = cols.map((c) => `t.${camelCase(c)}`).join(', ');
561
+ return { comment: null, expr: `uniqueIndex('${name}').on(${onCols})` };
562
+ });
563
+ }
564
+
449
565
  /**
450
566
  * Collect drizzle imports needed for entity fields
451
567
  */
452
- function collectDrizzleImports(processedFields, belongsTo, hasTimestamps, hasSoftDelete, hasExternalIdTracking, hasMany = []) {
568
+ function collectDrizzleImports(processedFields, belongsTo, hasTimestamps, hasSoftDelete, hasExternalIdTracking, hasMany = [], extraImports = []) {
453
569
  const imports = new Set(['pgTable', 'uuid']);
454
570
 
455
571
  for (const field of processedFields) {
@@ -475,7 +591,7 @@ function collectDrizzleImports(processedFields, belongsTo, hasTimestamps, hasSof
475
591
 
476
592
  // external_id_tracking behavior injects varchar + jsonb columns plus a
477
593
  // unique index over (provider, external_id) — the ON CONFLICT target the
478
- // sync sink's syncUpsert relies on.
594
+ // integration sink's integrationUpsert relies on.
479
595
  if (hasExternalIdTracking) {
480
596
  imports.add('varchar');
481
597
  imports.add('jsonb');
@@ -486,6 +602,13 @@ function collectDrizzleImports(processedFields, belongsTo, hasTimestamps, hasSof
486
602
  imports.add('relations');
487
603
  }
488
604
 
605
+ // Caller-supplied extras: `index` (field-level index: true) and
606
+ // `uniqueIndex` (composite unique_indexes) — see processFieldFeatures /
607
+ // processUniqueIndexes.
608
+ for (const extra of extraImports) {
609
+ imports.add(extra);
610
+ }
611
+
489
612
  return Array.from(imports).sort();
490
613
  }
491
614
 
@@ -725,16 +848,16 @@ function processSearchQueries(queriesBlock, processedFields, belongsTo, entityNa
725
848
  }
726
849
 
727
850
  // ============================================================================
728
- // Sync write-surface derivation (#374)
851
+ // Integration write-surface derivation (#374)
729
852
  // ============================================================================
730
853
 
731
854
  /**
732
- * Pre-compute the inbound-sync write surface for a `pattern: Synced` entity.
733
- * Keeps the EJS thin + unit-testable: the template hand-emits the syncConfig
855
+ * Pre-compute the inbound-integration write surface for a `pattern: Integrated` entity.
856
+ * Keeps the EJS thin + unit-testable: the template hand-emits the integrationConfig
734
857
  * literal (so `refTable` can carry a LIVE Drizzle table handle, which
735
858
  * renderPatternConfigLiteral cannot express) using these locals.
736
859
  *
737
- * Returns null when the entity is not Synced.
860
+ * Returns null when the entity is not Integrated.
738
861
  *
739
862
  * @param {string} patternName resolved pattern name
740
863
  * @param {object[]} processedFields nonFkFields (camel + tsType + nullable)
@@ -743,8 +866,8 @@ function processSearchQueries(queriesBlock, processedFields, belongsTo, entityNa
743
866
  * @param {boolean} eavEnabled
744
867
  * @param {boolean} hasSoftDelete
745
868
  */
746
- export function buildSyncSurface(patternName, processedFields, belongsTo, hasTimestamps, eavEnabled, hasSoftDelete, fields) {
747
- if (patternName !== 'Synced') return null;
869
+ export function buildIntegrationSurface(patternName, processedFields, belongsTo, hasTimestamps, eavEnabled, hasSoftDelete, fields) {
870
+ if (patternName !== 'Integrated') return null;
748
871
 
749
872
  // Copy-through columns: every non-FK declared field. external_id_tracking
750
873
  // columns (external_id/provider/provider_metadata) are injected by the
@@ -782,10 +905,10 @@ export function buildSyncSurface(patternName, processedFields, belongsTo, hasTim
782
905
  ...(hasTimestamps ? ['createdAt', 'updatedAt'] : []),
783
906
  ];
784
907
 
785
- // The syncConfig object literal the template hand-emits. fkResolvers carry a
908
+ // The integrationConfig object literal the template hand-emits. fkResolvers carry a
786
909
  // sentinel so the template can swap `refTable` to either 'self' or the live
787
910
  // table identifier.
788
- const syncConfig = {
911
+ const integrationConfig = {
789
912
  conflictTarget: ['provider', 'externalId'],
790
913
  writeColumns,
791
914
  projectionColumns,
@@ -793,7 +916,7 @@ export function buildSyncSurface(patternName, processedFields, belongsTo, hasTim
793
916
  softDelete: !!hasSoftDelete,
794
917
  };
795
918
 
796
- // TSyncWrite fields: externalId:string, copy-through (typed, nullable-aware),
919
+ // TIntegrationWrite fields: externalId:string, copy-through (typed, nullable-aware),
797
920
  // one `<writeKey>?: string | null` per FK, fields?: Record<string, unknown>.
798
921
  const writeFields = processedFields.map((f) => ({
799
922
  camelName: f.camelName,
@@ -804,7 +927,7 @@ export function buildSyncSurface(patternName, processedFields, belongsTo, hasTim
804
927
  tsType: 'string | null',
805
928
  }));
806
929
 
807
- // TSyncProjection fields: id + externalId + copy-through (typed) + each local
930
+ // TIntegrationProjection fields: id + externalId + copy-through (typed) + each local
808
931
  // FK column (typed string, nullable per rel) + createdAt/updatedAt.
809
932
  const projectionFields = [
810
933
  { camelName: 'id', tsType: 'string' },
@@ -841,7 +964,7 @@ export function buildSyncSurface(patternName, processedFields, belongsTo, hasTim
841
964
  const parentTableImports = Array.from(parentImportMap.values());
842
965
 
843
966
  return {
844
- syncConfig,
967
+ integrationConfig,
845
968
  fkResolvers,
846
969
  writeFields,
847
970
  writeFkFields,
@@ -885,6 +1008,18 @@ export function buildCleanLitePsLocals(definition, baseLocals) {
885
1008
  const entityNamePlural = entity.plural || pluralize(entityName);
886
1009
  const entityNamePluralPascal = pascalCase(entityNamePlural);
887
1010
 
1011
+ // #403: bounded-context folder grouping. A top-level `context:` nests this
1012
+ // entity's module folder under that segment so same-context entities group
1013
+ // together (`<modules>/<context>/<plural>/`); no context → flat
1014
+ // (`<modules>/<plural>/`, byte-identical to pre-#403). Emit-folder-only —
1015
+ // every intra-module import is folder-relative and therefore unaffected, and
1016
+ // the generated barrel recomputes its import paths from the full file paths
1017
+ // below. The module-folder base used by every clpOutputPaths entry:
1018
+ const entityContext = definition.context || null;
1019
+ const moduleGroupDir = entityContext
1020
+ ? `${srcRoot}/modules/${entityContext}`
1021
+ : `${srcRoot}/modules`;
1022
+
888
1023
  // Generation toggles — `generate.writes` defaults to true so consumers who
889
1024
  // regenerate pick up create/update/delete use cases without YAML changes.
890
1025
  // Set `generate.writes: false` in YAML to suppress write-side emission
@@ -953,7 +1088,7 @@ export function buildCleanLitePsLocals(definition, baseLocals) {
953
1088
  // Behavior flags (re-read from behaviors array for clean-lite-ps use).
954
1089
  //
955
1090
  // Fold in the resolved pattern's `impliedBehaviors` (ADR-031): an entity
956
- // declaring e.g. `pattern: Synced` need not re-declare the
1091
+ // declaring e.g. `pattern: Integrated` need not re-declare the
957
1092
  // `external_id_tracking` behavior — the pattern contributes it. Deduped
958
1093
  // with any explicit `behaviors:` entries, explicit-first so order is
959
1094
  // stable for pre-existing fixtures. Mirrors the dedup in
@@ -1033,6 +1168,33 @@ export function buildCleanLitePsLocals(definition, baseLocals) {
1033
1168
  const fkFieldNames = new Set(belongsTo.map((r) => r.field));
1034
1169
  const nonFkFields = processedFields.filter((f) => !fkFieldNames.has(f.name));
1035
1170
 
1171
+ // Field-level foreign_key + index emission (#354, #355). Mutates the
1172
+ // drizzleChain of the rendered (non-belongs_to) columns in place. Skip FK
1173
+ // imports for tables belongs_to already imports to avoid duplicate import
1174
+ // lines.
1175
+ const fieldFeatures = processFieldFeatures(nonFkFields, fields, entityNamePlural);
1176
+ const belongsToTables = new Set(belongsTo.map((r) => r.relatedTable));
1177
+ const clpFieldFkImports = fieldFeatures.fkImports.filter(
1178
+ (imp) => !belongsToTables.has(imp.relatedTable),
1179
+ );
1180
+
1181
+ // Composite unique indexes (#356).
1182
+ const uniqueIndexExpressions = processUniqueIndexes(definition.unique_indexes, entityNamePlural);
1183
+
1184
+ // pgTable extra-config callback entries, in emission order: single-column
1185
+ // indexes, composite unique indexes, then the external_id_tracking unique
1186
+ // index (the ON CONFLICT target integrationUpsert relies on).
1187
+ const clpTableConstraints = [
1188
+ ...fieldFeatures.indexExpressions,
1189
+ ...uniqueIndexExpressions,
1190
+ ];
1191
+ if (hasExternalIdTracking) {
1192
+ clpTableConstraints.push({
1193
+ comment: 'external_id_tracking behavior — ON CONFLICT target for integrationUpsert',
1194
+ expr: `uniqueIndex('uq_${entityNamePlural}_provider_external_id').on(t.provider, t.externalId)`,
1195
+ });
1196
+ }
1197
+
1036
1198
  // Enum field declarations — surface a separate collection so the entity
1037
1199
  // template can emit `export const xEnum = pgEnum('x', [...])` ahead of
1038
1200
  // the `pgTable(...)` block. Both FK-filtered and unfiltered processing
@@ -1045,59 +1207,70 @@ export function buildCleanLitePsLocals(definition, baseLocals) {
1045
1207
  choices: f.choices,
1046
1208
  }));
1047
1209
 
1048
- // Drizzle imports needed
1049
- const drizzleEntityImports = collectDrizzleImports(processedFields, belongsTo, hasTimestamps, hasSoftDelete, hasExternalIdTracking, hasMany);
1210
+ // Drizzle imports needed. `index` / `uniqueIndex` are pulled in only when a
1211
+ // field declares `index: true` or the entity declares `unique_indexes:`
1212
+ // (external_id_tracking adds `uniqueIndex` on its own flag below).
1213
+ const extraDrizzleImports = [];
1214
+ if (fieldFeatures.indexExpressions.length > 0) extraDrizzleImports.push('index');
1215
+ if (uniqueIndexExpressions.length > 0) extraDrizzleImports.push('uniqueIndex');
1216
+ const drizzleEntityImports = collectDrizzleImports(processedFields, belongsTo, hasTimestamps, hasSoftDelete, hasExternalIdTracking, hasMany, extraDrizzleImports);
1050
1217
  // Whether relations() import is needed (CGP-358b: also trigger on has_many)
1051
1218
  const hasRelationsBlock = belongsTo.length > 0 || hasMany.length > 0;
1052
1219
 
1053
1220
  // Output paths
1054
1221
  const outputPaths = {
1055
- entity: `${srcRoot}/modules/${entityNamePlural}/${entityName}.entity.ts`,
1056
- repository: `${srcRoot}/modules/${entityNamePlural}/${entityName}.repository.ts`,
1057
- service: `${srcRoot}/modules/${entityNamePlural}/${entityName}.service.ts`,
1058
- controller: `${srcRoot}/modules/${entityNamePlural}/${entityName}.controller.ts`,
1059
- module: `${srcRoot}/modules/${entityNamePlural}/${entityNamePlural}.module.ts`,
1060
- index: `${srcRoot}/modules/${entityNamePlural}/index.ts`,
1061
- findByIdUseCase: `${srcRoot}/modules/${entityNamePlural}/use-cases/find-${entityName}-by-id.use-case.ts`,
1062
- listUseCase: `${srcRoot}/modules/${entityNamePlural}/use-cases/list-${entityNamePlural}.use-case.ts`,
1222
+ entity: `${moduleGroupDir}/${entityNamePlural}/${entityName}.entity.ts`,
1223
+ repository: `${moduleGroupDir}/${entityNamePlural}/${entityName}.repository.ts`,
1224
+ service: `${moduleGroupDir}/${entityNamePlural}/${entityName}.service.ts`,
1225
+ controller: `${moduleGroupDir}/${entityNamePlural}/${entityName}.controller.ts`,
1226
+ module: `${moduleGroupDir}/${entityNamePlural}/${entityNamePlural}.module.ts`,
1227
+ index: `${moduleGroupDir}/${entityNamePlural}/index.ts`,
1228
+ findByIdUseCase: `${moduleGroupDir}/${entityNamePlural}/use-cases/find-${entityName}-by-id.use-case.ts`,
1229
+ listUseCase: `${moduleGroupDir}/${entityNamePlural}/use-cases/list-${entityNamePlural}.use-case.ts`,
1063
1230
  findByIdWithFieldsUseCase: eavEnabled
1064
- ? `${srcRoot}/modules/${entityNamePlural}/use-cases/find-${entityName}-by-id-with-fields.use-case.ts`
1231
+ ? `${moduleGroupDir}/${entityNamePlural}/use-cases/find-${entityName}-by-id-with-fields.use-case.ts`
1065
1232
  : null,
1066
1233
  listWithFieldsUseCase: eavEnabled
1067
- ? `${srcRoot}/modules/${entityNamePlural}/use-cases/list-${entityNamePlural}-with-fields.use-case.ts`
1234
+ ? `${moduleGroupDir}/${entityNamePlural}/use-cases/list-${entityNamePlural}-with-fields.use-case.ts`
1068
1235
  : null,
1069
1236
  createUseCase: generateWrites
1070
- ? `${srcRoot}/modules/${entityNamePlural}/use-cases/create-${entityName}.use-case.ts`
1237
+ ? `${moduleGroupDir}/${entityNamePlural}/use-cases/create-${entityName}.use-case.ts`
1071
1238
  : null,
1072
1239
  updateUseCase: generateWrites
1073
- ? `${srcRoot}/modules/${entityNamePlural}/use-cases/update-${entityName}.use-case.ts`
1240
+ ? `${moduleGroupDir}/${entityNamePlural}/use-cases/update-${entityName}.use-case.ts`
1074
1241
  : null,
1075
1242
  deleteUseCase: generateWrites
1076
- ? `${srcRoot}/modules/${entityNamePlural}/use-cases/delete-${entityName}.use-case.ts`
1243
+ ? `${moduleGroupDir}/${entityNamePlural}/use-cases/delete-${entityName}.use-case.ts`
1077
1244
  : null,
1078
- createDto: `${srcRoot}/modules/${entityNamePlural}/dto/create-${entityName}.dto.ts`,
1079
- updateDto: `${srcRoot}/modules/${entityNamePlural}/dto/update-${entityName}.dto.ts`,
1080
- outputDto: `${srcRoot}/modules/${entityNamePlural}/dto/${entityName}-output.dto.ts`,
1245
+ createDto: `${moduleGroupDir}/${entityNamePlural}/dto/create-${entityName}.dto.ts`,
1246
+ updateDto: `${moduleGroupDir}/${entityNamePlural}/dto/update-${entityName}.dto.ts`,
1247
+ outputDto: `${moduleGroupDir}/${entityNamePlural}/dto/${entityName}-output.dto.ts`,
1081
1248
  searchUseCase: searchQueryResolved
1082
- ? `${srcRoot}/modules/${entityNamePlural}/use-cases/search-${entityNamePlural}.use-case.ts`
1249
+ ? `${moduleGroupDir}/${entityNamePlural}/use-cases/search-${entityNamePlural}.use-case.ts`
1083
1250
  : null,
1084
1251
  searchController: searchQueryResolved
1085
- ? `${srcRoot}/modules/${entityNamePlural}/${entityName}-search.controller.ts`
1252
+ ? `${moduleGroupDir}/${entityNamePlural}/${entityName}-search.controller.ts`
1086
1253
  : null,
1087
1254
  declarativeQueries: hasDeclarativeQueries
1088
- ? `${srcRoot}/modules/${entityNamePlural}/use-cases/declarative-queries.ts`
1255
+ ? `${moduleGroupDir}/${entityNamePlural}/use-cases/declarative-queries.ts`
1089
1256
  : null,
1090
- // ADR-033.1 §8 — sync-source module emission for clean-lite-ps. Co-located
1257
+ // ADR-033.1 §8 — integration-source module emission for clean-lite-ps. Co-located
1091
1258
  // with the entity feature module under src/modules/<plural>/. Closes #267.
1092
- syncSourceModule: `${srcRoot}/modules/${entityNamePlural}/${entityName}-sync-source.module.ts`,
1093
- syncSourceProviders: `${srcRoot}/modules/${entityNamePlural}/${entityName}-sync-source.providers.ts`,
1259
+ // #403: routed through moduleGroupDir so a `context:`-tagged entity nests the
1260
+ // integration-source module under its context segment (untagged → flat, the
1261
+ // same `${srcRoot}/modules/<plural>/…` path as before).
1262
+ integrationSourceModule: `${moduleGroupDir}/${entityNamePlural}/${entityName}-integration-source.module.ts`,
1263
+ // ADR-033.2's per-entity provider tuples (`<entity>-integration-source.providers.ts`)
1264
+ // are removed by RFC-0001 §8 (D4). The surface-scoped typed view
1265
+ // (`src/integrations/<surface>/types.generated.ts`) is the single source of
1266
+ // provider truth now — see src/cli/shared/adapter-emission-generator.ts.
1094
1267
  };
1095
1268
 
1096
- // Architecture-specific imports for clean-lite-ps. The sync-source module
1269
+ // Architecture-specific imports for clean-lite-ps. The integration-source module
1097
1270
  // imports the entity type sibling-style (`./<entity>.entity`) since the
1098
1271
  // module file lives next to the entity file in the same feature folder.
1099
1272
  const clpImports = {
1100
- syncSourceToEntity: `./${entityName}.entity`,
1273
+ integrationSourceToEntity: `./${entityName}.entity`,
1101
1274
  };
1102
1275
 
1103
1276
  // Class names
@@ -1156,8 +1329,8 @@ export function buildCleanLitePsLocals(definition, baseLocals) {
1156
1329
  zodChainOutput: zodChainForOutput(f),
1157
1330
  }));
1158
1331
 
1159
- // Sync write-surface derivation (#374) — null unless pattern: Synced.
1160
- const syncSurface = buildSyncSurface(
1332
+ // Integration write-surface derivation (#374) — null unless pattern: Integrated.
1333
+ const integrationSurface = buildIntegrationSurface(
1161
1334
  patternName,
1162
1335
  nonFkFields,
1163
1336
  belongsTo,
@@ -1211,16 +1384,16 @@ export function buildCleanLitePsLocals(definition, baseLocals) {
1211
1384
  renderPatternConfigLiteral,
1212
1385
  ...patternConfigClasses,
1213
1386
 
1214
- // Sync write-surface (#374) — emitted only for pattern: Synced. The
1215
- // template hand-emits the syncConfig literal (live refTable handles) +
1216
- // TSyncWrite/TSyncProjection from these.
1217
- hasSyncSurface: syncSurface !== null,
1218
- clpSyncConfig: syncSurface?.syncConfig ?? null,
1219
- clpSyncFkResolvers: syncSurface?.fkResolvers ?? [],
1220
- clpSyncWriteFields: syncSurface?.writeFields ?? [],
1221
- clpSyncWriteFkFields: syncSurface?.writeFkFields ?? [],
1222
- clpSyncProjectionFields: syncSurface?.projectionFields ?? [],
1223
- clpSyncParentTableImports: syncSurface?.parentTableImports ?? [],
1387
+ // Integration write-surface (#374) — emitted only for pattern: Integrated. The
1388
+ // template hand-emits the integrationConfig literal (live refTable handles) +
1389
+ // TIntegrationWrite/TIntegrationProjection from these.
1390
+ hasIntegrationSurface: integrationSurface !== null,
1391
+ clpIntegrationConfig: integrationSurface?.integrationConfig ?? null,
1392
+ clpIntegrationFkResolvers: integrationSurface?.fkResolvers ?? [],
1393
+ clpIntegrationWriteFields: integrationSurface?.writeFields ?? [],
1394
+ clpIntegrationWriteFkFields: integrationSurface?.writeFkFields ?? [],
1395
+ clpIntegrationProjectionFields: integrationSurface?.projectionFields ?? [],
1396
+ clpIntegrationParentTableImports: integrationSurface?.parentTableImports ?? [],
1224
1397
 
1225
1398
  // Behavior flags (also exposed at top level for template use)
1226
1399
  hasTimestamps,
@@ -1245,10 +1418,14 @@ export function buildCleanLitePsLocals(definition, baseLocals) {
1245
1418
  hasSearchQuery: !!searchQueryResolved,
1246
1419
 
1247
1420
 
1421
+ // #403: bounded-context segment (null when untagged). Drives the
1422
+ // module-folder nesting reflected in clpOutputPaths above.
1423
+ clpContext: entityContext,
1424
+
1248
1425
  // Output paths
1249
1426
  clpOutputPaths: outputPaths,
1250
1427
 
1251
- // Architecture-specific imports (ADR-033.1 §8 — sync-source closes #267)
1428
+ // Architecture-specific imports (ADR-033.1 §8 — integration-source closes #267)
1252
1429
  clpImports,
1253
1430
 
1254
1431
  // Class names
@@ -1269,9 +1446,15 @@ export function buildCleanLitePsLocals(definition, baseLocals) {
1269
1446
  // strict mode flags the table const with TS7022/TS7024 (circular initializer).
1270
1447
  // Surfaced by the cgp-62 relationship-scenario smoke when generating a CRM
1271
1448
  // account with a `parent_account_id` self-FK.
1272
- clpHasSelfFk: belongsTo.some((rel) => rel.isSelfFk),
1449
+ clpHasSelfFk: belongsTo.some((rel) => rel.isSelfFk) || fieldFeatures.hasSelfFieldFk,
1273
1450
  clpEnumFields,
1274
1451
 
1452
+ // Field-level foreign_key imports (#354) and pgTable extra-config
1453
+ // entries: single-column indexes (#355) + composite unique indexes (#356)
1454
+ // + the external_id_tracking unique index.
1455
+ clpFieldFkImports,
1456
+ clpTableConstraints,
1457
+
1275
1458
  // Declarative queries
1276
1459
  processedQueries,
1277
1460
  hasDeclarativeQueries,