@open-mercato/cli 0.6.7 → 0.6.8-develop.6875.1.871a4afc94

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 (278) hide show
  1. package/.turbo/turbo-build.log +4 -4
  2. package/build.mjs +24 -7
  3. package/dist/agentic/claude-code/hooks/entity-migration-check.ts +32 -88
  4. package/dist/agentic/codex/enforcement-rules.md +2 -16
  5. package/dist/agentic/cursor/hooks/entity-migration-check.mjs +19 -50
  6. package/dist/agentic/cursor/rules/entity-guard.mdc +9 -21
  7. package/dist/agentic/cursor/rules/generated-guard.mdc +3 -5
  8. package/dist/agentic/cursor/rules/open-mercato.mdc +3 -29
  9. package/dist/agentic/guides/ai-workflows.md +76 -0
  10. package/dist/agentic/guides/architecture.md +107 -0
  11. package/dist/agentic/guides/backend-ui.md +80 -0
  12. package/dist/agentic/guides/contracts.md +92 -0
  13. package/dist/agentic/guides/extensions.md +121 -0
  14. package/dist/agentic/guides/framework-extension-points.md +36 -0
  15. package/dist/agentic/guides/integrations.md +81 -0
  16. package/dist/agentic/guides/module-facts.json +115537 -3951
  17. package/dist/agentic/guides/module-system.md +12 -127
  18. package/dist/agentic/guides/modules/ai_assistant.md +227 -28
  19. package/dist/agentic/guides/modules/api_docs.md +58 -2
  20. package/dist/agentic/guides/modules/api_keys.md +92 -7
  21. package/dist/agentic/guides/modules/attachments.md +119 -14
  22. package/dist/agentic/guides/modules/audit_logs.md +131 -10
  23. package/dist/agentic/guides/modules/auth.md +276 -33
  24. package/dist/agentic/guides/modules/business_rules.md +149 -14
  25. package/dist/agentic/guides/modules/catalog.md +475 -43
  26. package/dist/agentic/guides/modules/channel_gmail.md +82 -3
  27. package/dist/agentic/guides/modules/channel_imap.md +82 -3
  28. package/dist/agentic/guides/modules/checkout.md +299 -27
  29. package/dist/agentic/guides/modules/communication_channels.md +315 -33
  30. package/dist/agentic/guides/modules/configs.md +104 -8
  31. package/dist/agentic/guides/modules/content.md +57 -2
  32. package/dist/agentic/guides/modules/currencies.md +152 -17
  33. package/dist/agentic/guides/modules/customer_accounts.md +387 -45
  34. package/dist/agentic/guides/modules/customers.md +746 -85
  35. package/dist/agentic/guides/modules/dashboards.md +155 -10
  36. package/dist/agentic/guides/modules/data_sync.md +149 -16
  37. package/dist/agentic/guides/modules/design_system.md +102 -0
  38. package/dist/agentic/guides/modules/dictionaries.md +107 -12
  39. package/dist/agentic/guides/modules/directory.md +148 -15
  40. package/dist/agentic/guides/modules/entities.md +145 -13
  41. package/dist/agentic/guides/modules/events.md +59 -2
  42. package/dist/agentic/guides/modules/feature_toggles.md +119 -9
  43. package/dist/agentic/guides/modules/gateway_stripe.md +92 -4
  44. package/dist/agentic/guides/modules/generators.md +45 -2
  45. package/dist/agentic/guides/modules/inbox_ops.md +207 -27
  46. package/dist/agentic/guides/modules/integrations.md +146 -16
  47. package/dist/agentic/guides/modules/messages.md +223 -24
  48. package/dist/agentic/guides/modules/notifications.md +96 -8
  49. package/dist/agentic/guides/modules/onboarding.md +77 -6
  50. package/dist/agentic/guides/modules/payment_gateways.md +164 -21
  51. package/dist/agentic/guides/modules/perspectives.md +77 -7
  52. package/dist/agentic/guides/modules/planner.md +136 -18
  53. package/dist/agentic/guides/modules/portal.md +93 -2
  54. package/dist/agentic/guides/modules/progress.md +104 -14
  55. package/dist/agentic/guides/modules/query_index.md +154 -15
  56. package/dist/agentic/guides/modules/record_locks.md +206 -21
  57. package/dist/agentic/guides/modules/resources.md +197 -30
  58. package/dist/agentic/guides/modules/sales.md +678 -84
  59. package/dist/agentic/guides/modules/scheduler.md +133 -14
  60. package/dist/agentic/guides/modules/search.md +170 -6
  61. package/dist/agentic/guides/modules/security.md +294 -35
  62. package/dist/agentic/guides/modules/shipping_carriers.md +134 -18
  63. package/dist/agentic/guides/modules/sso.md +151 -26
  64. package/dist/agentic/guides/modules/staff.md +369 -57
  65. package/dist/agentic/guides/modules/storage_s3.md +95 -5
  66. package/dist/agentic/guides/modules/sync_akeneo.md +85 -3
  67. package/dist/agentic/guides/modules/sync_excel.md +75 -6
  68. package/dist/agentic/guides/modules/system_status_overlays.md +60 -2
  69. package/dist/agentic/guides/modules/translations.md +104 -10
  70. package/dist/agentic/guides/modules/webhooks.md +203 -24
  71. package/dist/agentic/guides/modules/wms.md +441 -46
  72. package/dist/agentic/guides/modules/workflows.md +302 -43
  73. package/dist/agentic/guides/spec-delivery.md +22 -0
  74. package/dist/agentic/guides/testing-debugging.md +80 -0
  75. package/dist/agentic/guides/upstream/AGENTS.md +299 -0
  76. package/dist/agentic/guides/upstream/BACKWARD_COMPATIBILITY.md +316 -0
  77. package/dist/agentic/guides/upstream/manifest.json +8 -0
  78. package/dist/agentic/shared/AGENTS.md.template +124 -311
  79. package/dist/agentic/shared/ai/agentic.config.json +2 -0
  80. package/dist/agentic/shared/ai/harness/README.md +50 -0
  81. package/dist/agentic/shared/ai/harness/RELEASE.md +57 -0
  82. package/dist/agentic/shared/ai/harness/cases.json +13680 -0
  83. package/dist/agentic/shared/ai/harness/cases.schema.json +133 -0
  84. package/dist/agentic/shared/ai/harness/fixtures/index.json +49 -0
  85. package/dist/agentic/shared/ai/harness/fixtures/seeds.json +162 -0
  86. package/dist/agentic/shared/ai/harness/generated-code-review-policy.md +21 -0
  87. package/dist/agentic/shared/ai/harness/generated-code-review-response.schema.json +96 -0
  88. package/dist/agentic/shared/ai/harness/generated-code-review-result.schema.json +172 -0
  89. package/dist/agentic/shared/ai/harness/release-matrix.json +158 -0
  90. package/dist/agentic/shared/ai/harness/release-result.schema.json +219 -0
  91. package/dist/agentic/shared/ai/harness/result.schema.json +76 -0
  92. package/dist/agentic/shared/ai/harness/routing-response.schema.json +12 -0
  93. package/dist/agentic/shared/ai/harness/target-validation-result.schema.json +73 -0
  94. package/dist/agentic/shared/ai/harness/validators.json +78 -0
  95. package/dist/agentic/shared/ai/harness/writable-ast-oracles.mjs +2121 -0
  96. package/dist/agentic/shared/ai/harness/writable-behavior-oracles.mjs +1096 -0
  97. package/dist/agentic/shared/ai/lessons/_template.md +16 -0
  98. package/dist/agentic/shared/ai/lessons.md +26 -3
  99. package/dist/agentic/shared/ai/review-checklist.md +36 -0
  100. package/dist/agentic/shared/ai/skills/om-auto-implement-spec/SKILL.md +21 -0
  101. package/dist/agentic/shared/ai/skills/om-auto-review-pr/SKILL.md +4 -0
  102. package/dist/agentic/shared/ai/skills/om-auto-upgrade-0.6.6-to-0.6.7/SKILL.md +98 -0
  103. package/dist/agentic/shared/ai/skills/om-backend-ui-design/SKILL.md +20 -241
  104. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/crud-surfaces.md +16 -0
  105. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/frontend-and-design-system.md +53 -0
  106. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/page-and-navigation.md +13 -0
  107. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/quality-states.md +11 -0
  108. package/dist/agentic/shared/ai/skills/om-build-workflow/SKILL.md +30 -0
  109. package/dist/agentic/shared/ai/skills/om-build-workflow/references/activity-contracts.md +12 -0
  110. package/dist/agentic/shared/ai/skills/om-build-workflow/references/durability-and-progress.md +13 -0
  111. package/dist/agentic/shared/ai/skills/om-build-workflow/references/workflow-design.md +14 -0
  112. package/dist/agentic/shared/ai/skills/om-create-ai-agent/SKILL.md +28 -0
  113. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/attachments-and-overrides.md +12 -0
  114. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/module-agents-and-tools.md +22 -0
  115. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/orchestrator-agents.md +12 -0
  116. package/dist/agentic/shared/ai/skills/om-create-ai-agent/references/surface-selector.md +19 -0
  117. package/dist/agentic/shared/ai/skills/om-data-model-design/SKILL.md +16 -601
  118. package/dist/agentic/shared/ai/skills/om-data-model-design/references/integrity-and-concurrency.md +19 -0
  119. package/dist/agentic/shared/ai/skills/om-data-model-design/references/migration-workflow.md +13 -0
  120. package/dist/agentic/shared/ai/skills/om-data-model-design/references/schema-design.md +14 -0
  121. package/dist/agentic/shared/ai/skills/om-data-model-design/references/sensitive-data.md +10 -0
  122. package/dist/agentic/shared/ai/skills/om-eject-and-customize/SKILL.md +14 -310
  123. package/dist/agentic/shared/ai/skills/om-eject-and-customize/references/decision-and-procedure.md +17 -0
  124. package/dist/agentic/shared/ai/skills/om-evolve-harness/SKILL.md +29 -0
  125. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-template.md +100 -0
  126. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/case-workflow.md +16 -0
  127. package/dist/agentic/shared/ai/skills/om-evolve-harness/references/owner-selection.md +17 -0
  128. package/dist/agentic/shared/ai/skills/om-framework-context/SKILL.md +25 -0
  129. package/dist/agentic/shared/ai/skills/om-framework-context/references/resolver-procedure.md +11 -0
  130. package/dist/agentic/shared/ai/skills/om-framework-context/references/skew-and-escalation.md +13 -0
  131. package/dist/agentic/shared/ai/skills/om-help/SKILL.md +15 -97
  132. package/dist/agentic/shared/ai/skills/om-help/references/delivery-workflows.md +18 -0
  133. package/dist/agentic/shared/ai/skills/om-help/references/task-families.md +20 -0
  134. package/dist/agentic/shared/ai/skills/om-implement-spec/SKILL.md +33 -152
  135. package/dist/agentic/shared/ai/skills/om-implement-spec/references/phases-and-gates.md +33 -0
  136. package/dist/agentic/shared/ai/skills/om-implement-spec/references/planning-and-progress.md +40 -0
  137. package/dist/agentic/shared/ai/skills/om-implement-spec/references/report-templates.md +27 -0
  138. package/dist/agentic/shared/ai/skills/om-implement-spec/references/spec-resolution.md +24 -0
  139. package/dist/agentic/shared/ai/skills/om-integration-builder/SKILL.md +24 -708
  140. package/dist/agentic/shared/ai/skills/om-integration-builder/references/package-and-activation.md +16 -0
  141. package/dist/agentic/shared/ai/skills/om-integration-builder/references/provider-families.md +14 -0
  142. package/dist/agentic/shared/ai/skills/om-integration-builder/references/security-and-reliability.md +15 -0
  143. package/dist/agentic/shared/ai/skills/om-integration-builder/references/sync-and-files.md +12 -0
  144. package/dist/agentic/shared/ai/skills/om-judge-agent-session/SKILL.md +34 -0
  145. package/dist/agentic/shared/ai/skills/om-judge-agent-session/agents/openai.yaml +4 -0
  146. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/agentic-setup.md +11 -0
  147. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/input-normalization.md +31 -0
  148. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/judge-workflow.md +39 -0
  149. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/report-template.md +43 -0
  150. package/dist/agentic/shared/ai/skills/om-judge-agent-session/references/rules.md +11 -0
  151. package/dist/agentic/shared/ai/skills/om-module-scaffold/SKILL.md +20 -820
  152. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/api-and-domain.md +19 -0
  153. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/business-one-shot-blueprints.md +172 -0
  154. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/data-and-migrations.md +12 -0
  155. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/discovery-surface-catalog.md +83 -0
  156. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/module-surfaces.md +21 -0
  157. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/planning.md +11 -0
  158. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/runtime-cache-and-queues.md +22 -0
  159. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/verification.md +13 -0
  160. package/dist/agentic/shared/ai/skills/om-share-this-session/SKILL.md +41 -0
  161. package/dist/agentic/shared/ai/skills/om-share-this-session/references/agentic-setup.md +19 -0
  162. package/dist/agentic/shared/ai/skills/om-share-this-session/references/bundle-preparation.md +44 -0
  163. package/dist/agentic/shared/ai/skills/om-share-this-session/references/consent-and-review.md +39 -0
  164. package/dist/agentic/shared/ai/skills/om-share-this-session/references/publication.md +20 -0
  165. package/dist/agentic/shared/ai/skills/om-share-this-session/references/report-templates.md +57 -0
  166. package/dist/agentic/shared/ai/skills/om-share-this-session/references/rules.md +11 -0
  167. package/dist/agentic/shared/ai/skills/om-share-this-session/scripts/prepare-share-bundle.mjs +486 -0
  168. package/dist/agentic/shared/ai/skills/om-system-extension/SKILL.md +19 -843
  169. package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-branches.md +18 -0
  170. package/dist/agentic/shared/ai/skills/om-system-extension/references/mechanism-selector.md +27 -0
  171. package/dist/agentic/shared/ai/skills/om-system-extension/references/read-write-roundtrip.md +13 -0
  172. package/dist/agentic/shared/ai/skills/om-system-extension/references/unified-overrides.md +41 -0
  173. package/dist/agentic/shared/ai/skills/om-system-extension/references/verification.md +11 -0
  174. package/dist/agentic/shared/ai/skills/om-trim-unused-modules/SKILL.md +15 -63
  175. package/dist/agentic/shared/ai/skills/om-trim-unused-modules/references/dependency-and-redirect.md +15 -0
  176. package/dist/agentic/shared/ai/skills/om-troubleshooter/SKILL.md +16 -467
  177. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnosis-map.md +16 -0
  178. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/regression-oracles.md +14 -0
  179. package/dist/agentic/shared/ai/skills/tiers.json +143 -21
  180. package/dist/agentic/shared/ai/skills/tiers.schema.json +52 -40
  181. package/dist/agentic/shared/ai/specs/README.md +4 -3
  182. package/dist/agentic/shared/ai/specs/SPEC-000-template.md +226 -10
  183. package/dist/agentic/shared/ai/trackers/github.md +108 -4
  184. package/dist/agentic/shared/scripts/agent-harness-tool-server.mjs +187 -0
  185. package/dist/agentic/shared/scripts/check-lessons.mjs +178 -0
  186. package/dist/agentic/shared/scripts/evaluate-agent-harness.mjs +2958 -0
  187. package/dist/agentic/shared/scripts/execution-sandbox.mjs +329 -0
  188. package/dist/agentic/shared/scripts/framework-context.mjs +888 -0
  189. package/dist/agentic/shared/scripts/install-skills.mjs +1029 -0
  190. package/dist/agentic/shared/scripts/install-skills.sh +4 -694
  191. package/dist/agentic/shared/scripts/prepare-agent-harness-fixture.mjs +222 -0
  192. package/dist/agentic/shared/scripts/run-agent-harness-release.mjs +1774 -0
  193. package/dist/bin.js +13 -1
  194. package/dist/bin.js.map +2 -2
  195. package/dist/lib/__integration__/TC-INT-008.spec.js +88 -8
  196. package/dist/lib/__integration__/TC-INT-008.spec.js.map +2 -2
  197. package/dist/lib/agentic-init.js +13 -3
  198. package/dist/lib/agentic-init.js.map +2 -2
  199. package/dist/lib/agentic-setup.js +434 -186
  200. package/dist/lib/agentic-setup.js.map +2 -2
  201. package/dist/lib/generators/extensions/injection-widgets.js +20 -0
  202. package/dist/lib/generators/extensions/injection-widgets.js.map +2 -2
  203. package/dist/lib/generators/module-extension-facts.js +1840 -0
  204. package/dist/lib/generators/module-extension-facts.js.map +7 -0
  205. package/dist/lib/generators/module-fact-sources.js +70 -0
  206. package/dist/lib/generators/module-fact-sources.js.map +7 -0
  207. package/dist/lib/generators/module-facts-discovery.js +41 -7
  208. package/dist/lib/generators/module-facts-discovery.js.map +2 -2
  209. package/dist/lib/generators/module-facts.js +1387 -45
  210. package/dist/lib/generators/module-facts.js.map +3 -3
  211. package/dist/lib/generators/module-override-targets.js +688 -0
  212. package/dist/lib/generators/module-override-targets.js.map +7 -0
  213. package/dist/lib/generators/module-registry.js +11 -5
  214. package/dist/lib/generators/module-registry.js.map +2 -2
  215. package/dist/lib/load-env.js +24 -0
  216. package/dist/lib/load-env.js.map +7 -0
  217. package/dist/lib/resolver.js +2 -0
  218. package/dist/lib/resolver.js.map +2 -2
  219. package/dist/lib/telemetry-init.js +358 -0
  220. package/dist/lib/telemetry-init.js.map +7 -0
  221. package/dist/lib/testing/integration.js +5 -0
  222. package/dist/lib/testing/integration.js.map +2 -2
  223. package/dist/mercato.js +23 -1
  224. package/dist/mercato.js.map +2 -2
  225. package/package.json +13 -11
  226. package/src/bin.ts +33 -1
  227. package/src/lib/__integration__/TC-INT-008.spec.ts +107 -9
  228. package/src/lib/__tests__/agentic-init.test.ts +41 -5
  229. package/src/lib/__tests__/agentic-setup.ownership.test.ts +386 -0
  230. package/src/lib/__tests__/injection-widget-id-validation.test.ts +184 -0
  231. package/src/lib/__tests__/load-env.test.ts +65 -0
  232. package/src/lib/__tests__/telemetry-init.test.ts +298 -0
  233. package/src/lib/agentic-init.ts +15 -3
  234. package/src/lib/agentic-setup.ts +573 -222
  235. package/src/lib/generators/__tests__/activation-adapter-coverage.test.ts +92 -0
  236. package/src/lib/generators/__tests__/module-extension-activations.test.ts +762 -0
  237. package/src/lib/generators/__tests__/module-extension-facts.test.ts +404 -0
  238. package/src/lib/generators/__tests__/module-facts.bc-guard.test.ts +88 -2
  239. package/src/lib/generators/__tests__/module-facts.customers.fixture.test.ts +1 -0
  240. package/src/lib/generators/__tests__/module-facts.discovery.test.ts +54 -10
  241. package/src/lib/generators/__tests__/module-facts.extension-hosts.test.ts +108 -0
  242. package/src/lib/generators/__tests__/module-facts.owned-contracts.test.ts +561 -0
  243. package/src/lib/generators/__tests__/module-facts.source-links.test.ts +119 -0
  244. package/src/lib/generators/__tests__/module-override-targets.roundtrip.test.ts +149 -0
  245. package/src/lib/generators/__tests__/module-override-targets.test.ts +304 -0
  246. package/src/lib/generators/__tests__/module-subset.test.ts +18 -0
  247. package/src/lib/generators/extensions/injection-widgets.ts +51 -0
  248. package/src/lib/generators/module-extension-facts.ts +2365 -0
  249. package/src/lib/generators/module-fact-sources.ts +108 -0
  250. package/src/lib/generators/module-facts-discovery.ts +58 -7
  251. package/src/lib/generators/module-facts.ts +1851 -46
  252. package/src/lib/generators/module-override-targets.ts +863 -0
  253. package/src/lib/generators/module-registry.ts +16 -6
  254. package/src/lib/load-env.ts +30 -0
  255. package/src/lib/resolver.ts +3 -0
  256. package/src/lib/telemetry-init.ts +461 -0
  257. package/src/lib/testing/__tests__/integration.test.ts +4 -0
  258. package/src/lib/testing/integration.ts +5 -0
  259. package/src/mercato.ts +26 -1
  260. package/dist/agentic/guides/cache.md +0 -45
  261. package/dist/agentic/guides/core.md +0 -453
  262. package/dist/agentic/guides/events.md +0 -117
  263. package/dist/agentic/guides/queue.md +0 -53
  264. package/dist/agentic/guides/search.md +0 -115
  265. package/dist/agentic/guides/shared.md +0 -108
  266. package/dist/agentic/guides/ui.md +0 -347
  267. package/dist/agentic/shared/ai/skills/om-auto-continue-pr-loop/SKILL.md +0 -27
  268. package/dist/agentic/shared/ai/skills/om-auto-create-pr-loop/SKILL.md +0 -27
  269. package/dist/agentic/shared/ai/skills/om-backend-ui-design/references/ui-components.md +0 -260
  270. package/dist/agentic/shared/ai/skills/om-data-model-design/references/mikro-orm-cheatsheet.md +0 -146
  271. package/dist/agentic/shared/ai/skills/om-help/references/skills-catalog.md +0 -84
  272. package/dist/agentic/shared/ai/skills/om-help/references/workflow-sequences.md +0 -193
  273. package/dist/agentic/shared/ai/skills/om-integration-builder/STANDALONE.md +0 -34
  274. package/dist/agentic/shared/ai/skills/om-integration-builder/references/adapter-contracts.md +0 -762
  275. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/naming-conventions.md +0 -50
  276. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/navigation-patterns.md +0 -97
  277. package/dist/agentic/shared/ai/skills/om-system-extension/references/extension-contracts.md +0 -271
  278. package/dist/agentic/shared/ai/skills/om-troubleshooter/references/diagnostic-commands.md +0 -101
@@ -2,6 +2,23 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import ts from "typescript-js";
4
4
  import { toSnake } from "../utils.js";
5
+ import { extractCommandIdsFromSource } from "./module-registry.js";
6
+ import { MODULE_CODE_EXTENSIONS } from "./scanner.js";
7
+ import {
8
+ assertNoUnresolvedExtensionTargets,
9
+ correlateIncomingExtensions,
10
+ correlateModuleExtensionFacts,
11
+ extractActivationTargetOwners,
12
+ extractKnownApiRouteIds,
13
+ extractKnownCommandIds,
14
+ extractModuleExtensionFacts,
15
+ renderFrameworkExtensionPointsMarkdown,
16
+ withModuleExtensionFactExtractionCache
17
+ } from "./module-extension-facts.js";
18
+ import {
19
+ collectModuleOverrideTargets
20
+ } from "./module-override-targets.js";
21
+ import { buildFactSourceLookup } from "./module-fact-sources.js";
5
22
  function readSourceFile(filePath) {
6
23
  if (!fs.existsSync(filePath)) return null;
7
24
  const source = fs.readFileSync(filePath, "utf8");
@@ -67,10 +84,38 @@ function collectCustomFieldEntityIds(ceFilePath) {
67
84
  if (!ceFilePath) return result;
68
85
  const sourceFile = readSourceFile(ceFilePath);
69
86
  if (!sourceFile) return result;
87
+ const readEntityId = (expression) => {
88
+ const current = unwrapExpression(expression);
89
+ if (ts.isStringLiteralLike(current)) {
90
+ return current.text.includes(":") ? current.text : void 0;
91
+ }
92
+ const readAccessPath = (candidate) => {
93
+ const access = unwrapExpression(candidate);
94
+ if (ts.isIdentifier(access)) return [access.text];
95
+ if (ts.isPropertyAccessExpression(access)) {
96
+ const parent = readAccessPath(access.expression);
97
+ return parent ? [...parent, access.name.text] : null;
98
+ }
99
+ if (ts.isElementAccessExpression(access) && access.argumentExpression) {
100
+ const parent = readAccessPath(access.expression);
101
+ const key = unwrapExpression(access.argumentExpression);
102
+ return parent && ts.isStringLiteralLike(key) ? [...parent, key.text] : null;
103
+ }
104
+ return null;
105
+ };
106
+ const accessPath = readAccessPath(current);
107
+ if (accessPath?.length === 3 && accessPath[0] === "E") {
108
+ return `${accessPath[1]}:${accessPath[2]}`;
109
+ }
110
+ return void 0;
111
+ };
70
112
  const visit = (node) => {
71
113
  if (ts.isObjectLiteralExpression(node)) {
72
- const id = readStringPropertyInitializer(node, "id");
73
- if (id && id.includes(":")) result.add(id);
114
+ const initializer = getObjectPropertyInitializer(node, "id");
115
+ if (initializer) {
116
+ const id = readEntityId(initializer);
117
+ if (id) result.add(id);
118
+ }
74
119
  }
75
120
  node.forEachChild(visit);
76
121
  };
@@ -212,7 +257,11 @@ function extractEvents(eventsFilePath) {
212
257
  category: category ?? null,
213
258
  entity: entity ?? null
214
259
  };
260
+ const clientBroadcast = readBooleanPropertyInitializer(element, "clientBroadcast");
261
+ const portalBroadcast = readBooleanPropertyInitializer(element, "portalBroadcast");
215
262
  if (label !== void 0) fact.label = label;
263
+ if (clientBroadcast !== void 0) fact.clientBroadcast = clientBroadcast;
264
+ if (portalBroadcast !== void 0) fact.portalBroadcast = portalBroadcast;
216
265
  facts.push(fact);
217
266
  }
218
267
  return facts;
@@ -302,9 +351,9 @@ function parseApiRouteEntry(entryLiteral) {
302
351
  }
303
352
  }
304
353
  }
305
- return { path: routePath, methods, auth };
354
+ return { path: routePath, methods, auth, sourcePath: null };
306
355
  }
307
- function extractApiRoutes(moduleId, registrySource, registryDescription, warnings) {
356
+ function extractApiRoutes(moduleId, registrySource, registryDescription, routeSourcePaths, warnings) {
308
357
  if (registrySource == null) {
309
358
  warnings.push(`[module-facts] module registry unavailable (${registryDescription}); API route auth omitted for ${moduleId}`);
310
359
  return [];
@@ -328,7 +377,7 @@ function extractApiRoutes(moduleId, registrySource, registryDescription, warning
328
377
  const route = parseApiRouteEntry(element);
329
378
  if (route && !seenPaths.has(route.path)) {
330
379
  seenPaths.add(route.path);
331
- routes.push(route);
380
+ routes.push({ ...route, sourcePath: routeSourcePaths.get(route.path) ?? null });
332
381
  }
333
382
  }
334
383
  }
@@ -476,31 +525,195 @@ function extractCli(cliFilePath, warnings) {
476
525
  function listSourceFilesRecursive(directory) {
477
526
  const files = [];
478
527
  for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
479
- if (entry.name === "__tests__" || entry.name === "node_modules") continue;
528
+ if (entry.name === "__tests__" || entry.name === "__mocks__" || entry.name === "node_modules") continue;
480
529
  const fullPath = path.join(directory, entry.name);
481
530
  if (entry.isDirectory()) {
482
531
  files.push(...listSourceFilesRecursive(fullPath));
483
- } else if (/\.tsx?$/.test(entry.name) && !entry.name.endsWith(".d.ts")) {
532
+ } else if (/\.tsx?$/.test(entry.name) && !entry.name.endsWith(".d.ts") && !/\.(?:test|spec)\.tsx?$/.test(entry.name)) {
484
533
  files.push(fullPath);
485
534
  }
486
535
  }
487
536
  return files;
488
537
  }
489
- function extractTableIds(backendDir) {
490
- if (!fs.existsSync(backendDir)) return [];
538
+ function toPortableSourceRoot(moduleId, sourcePackage) {
539
+ return path.posix.join("node_modules", sourcePackage ?? "@open-mercato/core", "src", "modules", moduleId);
540
+ }
541
+ function toPortableSourcePath(moduleRoot, sourceRoot, filePath) {
542
+ const relativePath = path.relative(moduleRoot, filePath).split(path.sep).join("/");
543
+ return path.posix.join(sourceRoot, relativePath);
544
+ }
545
+ function sourceHasDefaultExport(sourceFile) {
546
+ return sourceFile.statements.some((statement) => {
547
+ if (ts.isExportAssignment(statement) && !statement.isExportEquals) return true;
548
+ if (!ts.isFunctionDeclaration(statement) && !ts.isClassDeclaration(statement)) return false;
549
+ return statement.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword) ?? false;
550
+ });
551
+ }
552
+ function readRouteMetadataPath(sourceFile) {
553
+ const metadata = findObjectLiteralDeclaration(sourceFile, "metadata");
554
+ return metadata ? readStringPropertyInitializer(metadata, "path") ?? null : null;
555
+ }
556
+ function extractApiRouteSourcePaths(moduleId, moduleRoot, sourceRoot) {
557
+ const apiRoot = path.join(moduleRoot, "api");
558
+ const sources = /* @__PURE__ */ new Map();
559
+ if (!fs.existsSync(apiRoot)) return sources;
560
+ const methodDirectories = /* @__PURE__ */ new Set(["get", "post", "put", "patch", "delete"]);
561
+ for (const filePath of listSourceFilesRecursive(apiRoot)) {
562
+ const relativePath = path.relative(apiRoot, filePath).split(path.sep).join("/");
563
+ const segments = relativePath.split("/");
564
+ const fileName = segments.pop();
565
+ const fileStem = fileName.replace(/\.tsx?$/, "");
566
+ let routeSegments;
567
+ if (fileStem === "route") {
568
+ routeSegments = segments;
569
+ } else if (segments[0] && methodDirectories.has(segments[0].toLowerCase())) {
570
+ routeSegments = [...segments.slice(1), fileStem];
571
+ } else {
572
+ routeSegments = [...segments, fileStem];
573
+ }
574
+ const defaultPath = `/${[moduleId, ...routeSegments].filter(Boolean).join("/")}`;
575
+ const sourceFile = readSourceFile(filePath);
576
+ const routePath = sourceFile ? readRouteMetadataPath(sourceFile) ?? defaultPath : defaultPath;
577
+ sources.set(routePath, toPortableSourcePath(moduleRoot, sourceRoot, filePath));
578
+ }
579
+ return sources;
580
+ }
581
+ function extractModulePages(moduleId, moduleRoot, sourceRoot, surface) {
582
+ const pageRoot = path.join(moduleRoot, surface);
583
+ if (!fs.existsSync(pageRoot)) return [];
584
+ const pages = [];
585
+ const seen = /* @__PURE__ */ new Set();
586
+ for (const filePath of listSourceFilesRecursive(pageRoot)) {
587
+ if (!filePath.endsWith(".tsx")) continue;
588
+ const relativePath = path.relative(pageRoot, filePath).split(path.sep).join("/");
589
+ const segments = relativePath.split("/");
590
+ const fileName = segments.pop();
591
+ const fileStem = fileName.replace(/\.tsx$/, "");
592
+ const isModernPage = fileStem === "page";
593
+ if (!isModernPage && (fileStem === "meta" || fileStem.endsWith(".meta") || /^[A-Z]/.test(fileStem))) continue;
594
+ const sourceFile = readSourceFile(filePath);
595
+ if (!sourceFile || !sourceHasDefaultExport(sourceFile)) continue;
596
+ const routeSegments = isModernPage ? segments : [...segments, fileStem];
597
+ const routePath = surface === "frontend" ? `/${routeSegments.filter(Boolean).join("/")}` : isModernPage ? `/backend/${routeSegments.join("/") || moduleId}` : `/backend/${routeSegments[0] === moduleId ? routeSegments.filter(Boolean).join("/") : [moduleId, ...routeSegments].filter(Boolean).join("/")}`;
598
+ if (seen.has(routePath)) continue;
599
+ seen.add(routePath);
600
+ const metadataFilePath = resolvePageMetadataFile(path.dirname(filePath));
601
+ const metadataSourceFile = metadataFilePath ? readSourceFile(metadataFilePath) : sourceFile;
602
+ const metadata = metadataSourceFile ? readPageMetadata(metadataSourceFile) : void 0;
603
+ pages.push({
604
+ path: routePath,
605
+ sourcePath: toPortableSourcePath(moduleRoot, sourceRoot, filePath),
606
+ ...metadataFilePath ? { metadataSourcePath: toPortableSourcePath(moduleRoot, sourceRoot, metadataFilePath) } : {},
607
+ ...metadata ? { metadata } : {}
608
+ });
609
+ }
610
+ return pages.sort((left, right) => left.path.localeCompare(right.path));
611
+ }
612
+ function findAncestorVariableDeclaration(node) {
613
+ let current = node.parent;
614
+ while (current) {
615
+ if (ts.isVariableDeclaration(current)) return current;
616
+ current = current.parent;
617
+ }
618
+ return null;
619
+ }
620
+ function findAncestorCallExpression(node) {
621
+ let current = node.parent;
622
+ while (current) {
623
+ if (ts.isCallExpression(current)) return current;
624
+ if (ts.isVariableDeclaration(current) || ts.isStatement(current)) return null;
625
+ current = current.parent;
626
+ }
627
+ return null;
628
+ }
629
+ function extractNamedAiFacts(filePaths, moduleRoot, sourceRoot, propertyName) {
630
+ const facts = [];
631
+ const seen = /* @__PURE__ */ new Set();
632
+ for (const filePath of filePaths) {
633
+ const sourceFile = readSourceFile(filePath);
634
+ if (!sourceFile) continue;
635
+ const initializers = buildVariableInitializerMap(sourceFile);
636
+ const readDefinitionValue = (objectLiteral) => {
637
+ const initializer = getObjectPropertyInitializer(objectLiteral, propertyName);
638
+ if (!initializer) return void 0;
639
+ const resolved = unwrapExpression(initializer);
640
+ if (ts.isStringLiteralLike(resolved)) return resolved.text;
641
+ if (ts.isIdentifier(resolved)) {
642
+ const declarationInitializer = initializers.get(resolved.text);
643
+ if (declarationInitializer) {
644
+ const declarationValue = unwrapExpression(declarationInitializer);
645
+ if (ts.isStringLiteralLike(declarationValue)) return declarationValue.text;
646
+ }
647
+ }
648
+ return void 0;
649
+ };
650
+ const visit = (node) => {
651
+ if (ts.isObjectLiteralExpression(node)) {
652
+ const value = readDefinitionValue(node);
653
+ if (value && !seen.has(value)) {
654
+ const declaration = findAncestorVariableDeclaration(node);
655
+ const declarationName = declaration && ts.isIdentifier(declaration.name) ? declaration.name.text : "";
656
+ const declarationType = declaration?.type?.getText(sourceFile) ?? "";
657
+ const call = findAncestorCallExpression(node);
658
+ const callee = call?.expression.getText(sourceFile) ?? "";
659
+ const isDefinition = propertyName === "name" ? /AiTool/.test(declarationType) || /tool$/i.test(declarationName) || /AiTool/.test(callee) : /AiAgentDefinition/.test(declarationType) || /agent$/i.test(declarationName) || /defineAiAgent/.test(callee);
660
+ if (isDefinition) {
661
+ seen.add(value);
662
+ facts.push({
663
+ value,
664
+ sourcePath: toPortableSourcePath(moduleRoot, sourceRoot, filePath)
665
+ });
666
+ }
667
+ }
668
+ }
669
+ node.forEachChild(visit);
670
+ };
671
+ sourceFile.forEachChild(visit);
672
+ }
673
+ return facts.sort((left, right) => left.value.localeCompare(right.value));
674
+ }
675
+ function extractAiTools(moduleRoot, sourceRoot) {
676
+ const files = /* @__PURE__ */ new Set();
677
+ const rootFile = resolveConventionFile(moduleRoot, "ai-tools");
678
+ if (rootFile) files.add(rootFile);
679
+ const toolsDirectory = path.join(moduleRoot, "ai-tools");
680
+ if (fs.existsSync(toolsDirectory)) {
681
+ for (const filePath of listSourceFilesRecursive(toolsDirectory)) files.add(filePath);
682
+ }
683
+ return extractNamedAiFacts([...files], moduleRoot, sourceRoot, "name").map((fact) => ({ name: fact.value, sourcePath: fact.sourcePath }));
684
+ }
685
+ function extractAiAgents(moduleRoot, sourceRoot) {
686
+ const agentsFile = resolveConventionFile(moduleRoot, "ai-agents");
687
+ if (!agentsFile) return [];
688
+ return extractNamedAiFacts([agentsFile], moduleRoot, sourceRoot, "id").map((fact) => ({ id: fact.value, sourcePath: fact.sourcePath }));
689
+ }
690
+ function extractTableIds(moduleRoot) {
691
+ if (!fs.existsSync(moduleRoot)) return [];
491
692
  const tableIds = [];
492
693
  const seen = /* @__PURE__ */ new Set();
493
- for (const filePath of listSourceFilesRecursive(backendDir)) {
694
+ const collectLiteralValues = (expression) => {
695
+ const current = unwrapExpression(expression);
696
+ if (ts.isStringLiteralLike(current)) return [current.text];
697
+ if (ts.isConditionalExpression(current)) {
698
+ return [
699
+ ...collectLiteralValues(current.whenTrue),
700
+ ...collectLiteralValues(current.whenFalse)
701
+ ];
702
+ }
703
+ return [];
704
+ };
705
+ for (const filePath of listSourceFilesRecursive(moduleRoot)) {
494
706
  const sourceFile = readSourceFile(filePath);
495
707
  if (!sourceFile) continue;
496
708
  const visit = (node) => {
497
709
  if (ts.isPropertyAssignment(node)) {
498
710
  const propertyName = getPropertyName(node);
499
- if ((propertyName === "tableId" || propertyName === "extensionTableId") && ts.isStringLiteralLike(node.initializer)) {
500
- const value = node.initializer.text;
501
- if (value && !seen.has(value)) {
502
- seen.add(value);
503
- tableIds.push(value);
711
+ if (propertyName === "tableId" || propertyName === "extensionTableId") {
712
+ for (const value of collectLiteralValues(node.initializer)) {
713
+ if (value && !seen.has(value)) {
714
+ seen.add(value);
715
+ tableIds.push(value);
716
+ }
504
717
  }
505
718
  }
506
719
  }
@@ -525,12 +738,587 @@ function extractModuleMeta(indexFilePath) {
525
738
  description: readStringPropertyInitializer(metadata, "description") ?? null
526
739
  };
527
740
  }
741
+ function nodeLine(sourceFile, node) {
742
+ return sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;
743
+ }
744
+ function readPropertyNameText(name) {
745
+ if (ts.isIdentifier(name) || ts.isStringLiteralLike(name)) return name.text;
746
+ return void 0;
747
+ }
748
+ function readMethodBearingPropertyNames(objectLiteral) {
749
+ const names = [];
750
+ for (const property of objectLiteral.properties) {
751
+ if (ts.isMethodDeclaration(property)) {
752
+ const name = readPropertyNameText(property.name);
753
+ if (name) names.push(name);
754
+ continue;
755
+ }
756
+ if (ts.isPropertyAssignment(property)) {
757
+ const name = readPropertyNameText(property.name);
758
+ if (!name) continue;
759
+ const initializer = unwrapExpression(property.initializer);
760
+ if (ts.isArrowFunction(initializer) || ts.isFunctionExpression(initializer)) names.push(name);
761
+ }
762
+ }
763
+ return names.sort((left, right) => left.localeCompare(right));
764
+ }
765
+ function readObjectKeyNames(objectLiteral) {
766
+ const names = [];
767
+ for (const property of objectLiteral.properties) {
768
+ const name = getPropertyName(property);
769
+ if (name) names.push(name);
770
+ }
771
+ return names.sort((left, right) => left.localeCompare(right));
772
+ }
773
+ function mapDiRegistrationKind(base) {
774
+ switch (base) {
775
+ case "asFunction":
776
+ return "function";
777
+ case "asClass":
778
+ return "class";
779
+ case "asValue":
780
+ return "value";
781
+ case "aliasTo":
782
+ return "alias";
783
+ default:
784
+ return null;
785
+ }
786
+ }
787
+ function readDiChainMetadata(initializer) {
788
+ const result = {};
789
+ let current = unwrapExpression(initializer);
790
+ while (ts.isCallExpression(current) && ts.isPropertyAccessExpression(current.expression)) {
791
+ const method = current.expression.name.text;
792
+ if ((method === "singleton" || method === "scoped" || method === "transient") && !result.lifetime) {
793
+ result.lifetime = method;
794
+ } else if (method === "proxy" && !result.injectionMode) {
795
+ result.injectionMode = "proxy";
796
+ } else if (method === "classic" && !result.injectionMode) {
797
+ result.injectionMode = "classic";
798
+ }
799
+ current = unwrapExpression(current.expression.expression);
800
+ }
801
+ return result;
802
+ }
803
+ function readDiProviderSymbol(initializer, kind) {
804
+ let current = unwrapExpression(initializer);
805
+ while (ts.isCallExpression(current) && ts.isPropertyAccessExpression(current.expression)) {
806
+ current = unwrapExpression(current.expression.expression);
807
+ }
808
+ if (!ts.isCallExpression(current)) return void 0;
809
+ const argument = current.arguments[0];
810
+ if (!argument) return void 0;
811
+ if (kind === "value") return void 0;
812
+ if (kind === "alias" && ts.isStringLiteralLike(argument)) return argument.text;
813
+ if ((kind === "function" || kind === "class") && ts.isIdentifier(argument)) return argument.text;
814
+ return void 0;
815
+ }
816
+ function extractDiRegistrationFacts(diFilePath, sourcePath) {
817
+ if (!diFilePath) return { facts: [], unresolvedTokens: [] };
818
+ const sourceFile = readSourceFile(diFilePath);
819
+ if (!sourceFile) return { facts: [], unresolvedTokens: [] };
820
+ const facts = [];
821
+ const unresolvedTokens = [];
822
+ const visit = (node) => {
823
+ if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && node.expression.name.text === "register") {
824
+ const argument = node.arguments[0];
825
+ if (argument && ts.isObjectLiteralExpression(argument)) {
826
+ for (const property of argument.properties) {
827
+ if (!ts.isPropertyAssignment(property)) continue;
828
+ const token = getPropertyName(property);
829
+ if (!token) continue;
830
+ const kind = mapDiRegistrationKind(detectAwilixRegistrationKind(property.initializer));
831
+ if (!kind) {
832
+ if (!unresolvedTokens.includes(token)) unresolvedTokens.push(token);
833
+ continue;
834
+ }
835
+ const chain = readDiChainMetadata(property.initializer);
836
+ const providerSymbol = readDiProviderSymbol(property.initializer, kind);
837
+ facts.push({
838
+ token,
839
+ registrationKind: kind,
840
+ ...providerSymbol ? { providerSymbol } : {},
841
+ ...chain.lifetime ? { lifetime: chain.lifetime } : {},
842
+ ...chain.injectionMode ? { injectionMode: chain.injectionMode } : {},
843
+ source: { sourcePath, exportName: token, line: nodeLine(sourceFile, property) }
844
+ });
845
+ }
846
+ }
847
+ }
848
+ node.forEachChild(visit);
849
+ };
850
+ sourceFile.forEachChild(visit);
851
+ return { facts, unresolvedTokens };
852
+ }
853
+ function deriveLegacyDiTokens(facts) {
854
+ const tokens = [];
855
+ const seen = /* @__PURE__ */ new Set();
856
+ for (const fact of facts) {
857
+ if (fact.registrationKind !== "function" && fact.registrationKind !== "class") continue;
858
+ if (seen.has(fact.token)) continue;
859
+ seen.add(fact.token);
860
+ tokens.push(fact.token);
861
+ }
862
+ return tokens;
863
+ }
864
+ function dedupeOwnedContractFacts(facts) {
865
+ const byId = /* @__PURE__ */ new Map();
866
+ for (const fact of facts) {
867
+ const existing = byId.get(fact.id);
868
+ if (!existing) {
869
+ byId.set(fact.id, fact);
870
+ continue;
871
+ }
872
+ if (fact.source.sourcePath.localeCompare(existing.source.sourcePath) < 0) byId.set(fact.id, fact);
873
+ }
874
+ return [...byId.values()].sort((left, right) => left.id.localeCompare(right.id));
875
+ }
876
+ const PAGE_CONTEXT_VALUES = /* @__PURE__ */ new Set(["main", "admin", "settings", "profile"]);
877
+ function readAliasedStringProperty(metadata, preferred, fallback) {
878
+ return readStringPropertyInitializer(metadata, preferred) ?? readStringPropertyInitializer(metadata, fallback);
879
+ }
880
+ function readNumericProperty(metadata, propertyName) {
881
+ const initializer = getObjectPropertyInitializer(metadata, propertyName);
882
+ if (!initializer || !ts.isNumericLiteral(initializer)) return void 0;
883
+ return Number(initializer.text);
884
+ }
885
+ function readPageMetadata(sourceFile) {
886
+ const metadata = findObjectLiteralDeclaration(sourceFile, "metadata");
887
+ if (!metadata) return void 0;
888
+ const result = {};
889
+ const requireAuth = readBooleanPropertyInitializer(metadata, "requireAuth");
890
+ if (requireAuth !== void 0) result.requireAuth = requireAuth;
891
+ const requireCustomerAuth = readBooleanPropertyInitializer(metadata, "requireCustomerAuth");
892
+ if (requireCustomerAuth !== void 0) result.requireCustomerAuth = requireCustomerAuth;
893
+ const requireFeatures = readStringArrayPropertyInitializer(metadata, "requireFeatures");
894
+ if (requireFeatures && requireFeatures.length > 0) result.requireFeatures = requireFeatures;
895
+ const requireCustomerFeatures = readStringArrayPropertyInitializer(metadata, "requireCustomerFeatures");
896
+ if (requireCustomerFeatures && requireCustomerFeatures.length > 0) {
897
+ result.requireCustomerFeatures = requireCustomerFeatures;
898
+ }
899
+ const navHidden = readBooleanPropertyInitializer(metadata, "navHidden");
900
+ if (navHidden !== void 0) result.navHidden = navHidden;
901
+ const navGroup = readAliasedStringProperty(metadata, "pageGroup", "group");
902
+ if (navGroup) result.navGroup = navGroup;
903
+ const navGroupKey = readAliasedStringProperty(metadata, "pageGroupKey", "groupKey");
904
+ if (navGroupKey) result.navGroupKey = navGroupKey;
905
+ const navOrder = readNumericProperty(metadata, "pageOrder") ?? readNumericProperty(metadata, "order");
906
+ if (navOrder !== void 0) result.navOrder = navOrder;
907
+ const pageContext = readStringPropertyInitializer(metadata, "pageContext");
908
+ if (pageContext && PAGE_CONTEXT_VALUES.has(pageContext)) {
909
+ result.pageContext = pageContext;
910
+ }
911
+ return Object.keys(result).length > 0 ? result : void 0;
912
+ }
913
+ function resolvePageMetadataFile(pageDirectory) {
914
+ for (const baseName of ["page.meta", "meta"]) {
915
+ for (const extension of MODULE_CODE_EXTENSIONS) {
916
+ const candidate = path.join(pageDirectory, `${baseName}${extension}`);
917
+ if (fs.existsSync(candidate)) return candidate;
918
+ }
919
+ }
920
+ return null;
921
+ }
922
+ function extractModuleMetadataContract(indexFilePath, moduleId, sourcePath) {
923
+ if (!indexFilePath || !sourcePath) return null;
924
+ const sourceFile = readSourceFile(indexFilePath);
925
+ if (!sourceFile) return null;
926
+ const metadata = findObjectLiteralDeclaration(sourceFile, "metadata");
927
+ if (!metadata) return null;
928
+ const facts = {};
929
+ const name = readStringPropertyInitializer(metadata, "name");
930
+ if (name) facts.name = name;
931
+ const version = readStringPropertyInitializer(metadata, "version");
932
+ if (version) facts.version = version;
933
+ const requires = readStringArrayPropertyInitializer(metadata, "requires");
934
+ if (requires && requires.length > 0) facts.requires = requires;
935
+ const ejectable = readBooleanPropertyInitializer(metadata, "ejectable");
936
+ if (ejectable !== void 0) facts.ejectable = ejectable;
937
+ return {
938
+ kind: "module-metadata",
939
+ id: moduleId,
940
+ source: { sourcePath, exportName: "metadata", line: nodeLine(sourceFile, metadata) },
941
+ ...Object.keys(facts).length > 0 ? { metadata: facts } : {}
942
+ };
943
+ }
944
+ function extractCommandContractCandidates(moduleId, moduleRoot, sourceRoot) {
945
+ const commandsRoot = path.join(moduleRoot, "commands");
946
+ if (!fs.existsSync(commandsRoot)) return [];
947
+ const candidates = [];
948
+ for (const filePath of listSourceFilesRecursive(commandsRoot)) {
949
+ if (path.basename(filePath).replace(/\.tsx?$/, "") === "interceptors") continue;
950
+ const sourcePath = toPortableSourcePath(moduleRoot, sourceRoot, filePath);
951
+ for (const id of extractCommandIdsFromSource(filePath)) candidates.push({ id, sourcePath });
952
+ }
953
+ return candidates;
954
+ }
955
+ function deriveWorkerId(moduleId, workersRoot, filePath) {
956
+ const relativePath = path.relative(workersRoot, filePath).split(path.sep).join("/");
957
+ const segments = relativePath.split("/");
958
+ const fileName = segments.pop();
959
+ const fileStem = fileName.replace(/\.tsx?$/, "");
960
+ return [moduleId, "workers", ...segments, fileStem].filter(Boolean).join(":");
961
+ }
962
+ function readResolvedStringProperty(objectLiteral, propertyName, initializers) {
963
+ const initializer = getObjectPropertyInitializer(objectLiteral, propertyName);
964
+ if (!initializer) return void 0;
965
+ const resolved = unwrapExpression(initializer);
966
+ if (ts.isStringLiteralLike(resolved)) return resolved.text;
967
+ if (ts.isIdentifier(resolved)) {
968
+ const declaration = initializers.get(resolved.text);
969
+ if (declaration) {
970
+ const declarationValue = unwrapExpression(declaration);
971
+ if (ts.isStringLiteralLike(declarationValue)) return declarationValue.text;
972
+ }
973
+ }
974
+ return void 0;
975
+ }
976
+ function extractWorkerContracts(moduleId, moduleRoot, sourceRoot) {
977
+ const workersRoot = path.join(moduleRoot, "workers");
978
+ if (!fs.existsSync(workersRoot)) return { facts: [], unresolved: [] };
979
+ const facts = [];
980
+ const unresolved = [];
981
+ for (const filePath of listSourceFilesRecursive(workersRoot)) {
982
+ const sourceFile = readSourceFile(filePath);
983
+ if (!sourceFile) continue;
984
+ const metadata = findObjectLiteralDeclaration(sourceFile, "metadata");
985
+ if (!metadata) continue;
986
+ const sourcePath = toPortableSourcePath(moduleRoot, sourceRoot, filePath);
987
+ const source = { sourcePath, exportName: "metadata", line: nodeLine(sourceFile, metadata) };
988
+ const initializers = buildVariableInitializerMap(sourceFile);
989
+ const workerId = readResolvedStringProperty(metadata, "id", initializers) ?? deriveWorkerId(moduleId, workersRoot, filePath);
990
+ const hasQueueProperty = getObjectPropertyInitializer(metadata, "queue") !== void 0;
991
+ const queue = readResolvedStringProperty(metadata, "queue", initializers);
992
+ if (!hasQueueProperty) {
993
+ unresolved.push({ code: "unresolved-static-contract", kind: "worker", id: workerId, source });
994
+ continue;
995
+ }
996
+ const workerMetadata = {};
997
+ if (queue) workerMetadata.queue = queue;
998
+ else unresolved.push({ code: "unresolved-static-contract", kind: "worker", id: workerId, source });
999
+ const name = readResolvedStringProperty(metadata, "name", initializers);
1000
+ if (name) workerMetadata.name = name;
1001
+ const concurrencyInitializer = getObjectPropertyInitializer(metadata, "concurrency");
1002
+ if (concurrencyInitializer && ts.isNumericLiteral(concurrencyInitializer)) {
1003
+ workerMetadata.concurrency = Number(concurrencyInitializer.text);
1004
+ }
1005
+ facts.push({
1006
+ kind: "worker",
1007
+ id: workerId,
1008
+ source,
1009
+ ...Object.keys(workerMetadata).length > 0 ? { metadata: workerMetadata } : {}
1010
+ });
1011
+ }
1012
+ return { facts: dedupeOwnedContractFacts(facts), unresolved };
1013
+ }
1014
+ function extractPageMiddlewareContracts(moduleRoot, sourceRoot) {
1015
+ const facts = [];
1016
+ const unresolved = [];
1017
+ for (const surface of ["backend", "frontend"]) {
1018
+ const filePath = resolveConventionFile(path.join(moduleRoot, surface), "middleware");
1019
+ if (!filePath) continue;
1020
+ const sourceFile = readSourceFile(filePath);
1021
+ if (!sourceFile) continue;
1022
+ const sourcePath = toPortableSourcePath(moduleRoot, sourceRoot, filePath);
1023
+ const middlewareArray = findArrayLiteralDeclaration(sourceFile, "middleware") ?? (() => {
1024
+ const defaultExport = findDefaultExportExpression(sourceFile);
1025
+ return defaultExport ? unwrapArrayLiteral(defaultExport) : null;
1026
+ })();
1027
+ if (!middlewareArray) continue;
1028
+ let index = 0;
1029
+ for (const element of middlewareArray.elements) {
1030
+ if (!ts.isObjectLiteralExpression(element)) continue;
1031
+ const middlewareId = readStringPropertyInitializer(element, "id");
1032
+ if (!middlewareId) {
1033
+ unresolved.push({
1034
+ code: "unresolved-static-contract",
1035
+ kind: "page-middleware",
1036
+ id: `${sourcePath}#${index}`,
1037
+ source: { sourcePath, line: nodeLine(sourceFile, element) }
1038
+ });
1039
+ index += 1;
1040
+ continue;
1041
+ }
1042
+ const metadata = { surface };
1043
+ const mode = readStringPropertyInitializer(element, "mode");
1044
+ if (mode) metadata.mode = mode;
1045
+ const priorityInitializer = getObjectPropertyInitializer(element, "priority");
1046
+ if (priorityInitializer && ts.isNumericLiteral(priorityInitializer)) {
1047
+ metadata.priority = Number(priorityInitializer.text);
1048
+ }
1049
+ facts.push({
1050
+ kind: "page-middleware",
1051
+ id: middlewareId,
1052
+ source: { sourcePath, exportName: "middleware", line: nodeLine(sourceFile, element) },
1053
+ metadata
1054
+ });
1055
+ index += 1;
1056
+ }
1057
+ }
1058
+ return { facts: dedupeOwnedContractFacts(facts), unresolved };
1059
+ }
1060
+ function extractSetupContract(moduleId, setupFilePath, sourcePath) {
1061
+ if (!setupFilePath || !sourcePath) return null;
1062
+ const sourceFile = readSourceFile(setupFilePath);
1063
+ if (!sourceFile) return null;
1064
+ const setupObject = findObjectLiteralDeclaration(sourceFile, "setup") ?? (() => {
1065
+ const defaultExport = findDefaultExportExpression(sourceFile);
1066
+ if (!defaultExport) return null;
1067
+ const unwrapped = unwrapExpression(defaultExport);
1068
+ return ts.isObjectLiteralExpression(unwrapped) ? unwrapped : null;
1069
+ })();
1070
+ if (!setupObject) return null;
1071
+ const hooks = readMethodBearingPropertyNames(setupObject);
1072
+ const metadata = {};
1073
+ if (hooks.length > 0) metadata.hooks = hooks;
1074
+ const roleFeatures = getObjectPropertyInitializer(setupObject, "defaultRoleFeatures");
1075
+ if (roleFeatures && ts.isObjectLiteralExpression(roleFeatures)) {
1076
+ const roles = readObjectKeyNames(roleFeatures);
1077
+ if (roles.length > 0) metadata.roles = roles;
1078
+ }
1079
+ const customerRoleFeatures = getObjectPropertyInitializer(setupObject, "defaultCustomerRoleFeatures");
1080
+ if (customerRoleFeatures && ts.isObjectLiteralExpression(customerRoleFeatures)) {
1081
+ const customerRoles = readObjectKeyNames(customerRoleFeatures);
1082
+ if (customerRoles.length > 0) metadata.customerRoles = customerRoles;
1083
+ }
1084
+ return {
1085
+ kind: "setup",
1086
+ id: `${moduleId}:setup`,
1087
+ source: { sourcePath, exportName: "setup", line: nodeLine(sourceFile, setupObject) },
1088
+ ...Object.keys(metadata).length > 0 ? { metadata } : {}
1089
+ };
1090
+ }
1091
+ function extractEncryptionContracts(encryptionFilePath, sourcePath) {
1092
+ if (!encryptionFilePath || !sourcePath) return [];
1093
+ const sourceFile = readSourceFile(encryptionFilePath);
1094
+ if (!sourceFile) return [];
1095
+ const mapsArray = findArrayLiteralDeclaration(sourceFile, "defaultEncryptionMaps") ?? findArrayLiteralDeclaration(sourceFile, "encryptionMaps");
1096
+ if (!mapsArray) return [];
1097
+ const facts = [];
1098
+ for (const element of mapsArray.elements) {
1099
+ if (!ts.isObjectLiteralExpression(element)) continue;
1100
+ const entityId = readStringPropertyInitializer(element, "entityId");
1101
+ if (!entityId) continue;
1102
+ const fieldsInitializer = getObjectPropertyInitializer(element, "fields");
1103
+ const fields = [];
1104
+ if (fieldsInitializer) {
1105
+ const fieldsArray = unwrapArrayLiteral(fieldsInitializer);
1106
+ if (fieldsArray) {
1107
+ for (const fieldElement of fieldsArray.elements) {
1108
+ if (!ts.isObjectLiteralExpression(fieldElement)) continue;
1109
+ const field = readStringPropertyInitializer(fieldElement, "field");
1110
+ if (field) fields.push(field);
1111
+ }
1112
+ }
1113
+ }
1114
+ facts.push({
1115
+ kind: "encryption",
1116
+ id: entityId,
1117
+ source: { sourcePath, exportName: "defaultEncryptionMaps", line: nodeLine(sourceFile, element) },
1118
+ ...fields.length > 0 ? { metadata: { fields: fields.sort((left, right) => left.localeCompare(right)) } } : {}
1119
+ });
1120
+ }
1121
+ return dedupeOwnedContractFacts(facts);
1122
+ }
1123
+ function extractCustomEntityContracts(ceFilePath, sourcePath) {
1124
+ if (!ceFilePath || !sourcePath) return [];
1125
+ const entityIds = [...collectCustomFieldEntityIds(ceFilePath)].sort((left, right) => left.localeCompare(right));
1126
+ return entityIds.map((id) => ({
1127
+ kind: "custom-entity",
1128
+ id,
1129
+ source: { sourcePath, exportName: "default" }
1130
+ }));
1131
+ }
1132
+ const AI_OVERRIDE_EXPORTS = [
1133
+ { exportName: "aiAgentOverrides", target: "agent" },
1134
+ { exportName: "aiToolOverrides", target: "tool" }
1135
+ ];
1136
+ function extractAiExtensionContracts(moduleRoot, sourceRoot) {
1137
+ const facts = [];
1138
+ const sources = [
1139
+ { basename: "ai-agents", relativeDir: "." },
1140
+ { basename: "ai-tools", relativeDir: "." },
1141
+ { basename: "ai-agents.extensions", relativeDir: "." },
1142
+ { basename: "ai-tools.extensions", relativeDir: "." }
1143
+ ];
1144
+ for (const entry of sources) {
1145
+ const filePath = resolveConventionFile(path.join(moduleRoot, entry.relativeDir), entry.basename);
1146
+ if (!filePath) continue;
1147
+ const sourceFile = readSourceFile(filePath);
1148
+ if (!sourceFile) continue;
1149
+ const sourcePath = toPortableSourcePath(moduleRoot, sourceRoot, filePath);
1150
+ const extensionsArray = findArrayLiteralDeclaration(sourceFile, "aiAgentExtensions");
1151
+ if (extensionsArray) {
1152
+ for (const element of extensionsArray.elements) {
1153
+ const objectLiteral = ts.isObjectLiteralExpression(element) ? element : ts.isCallExpression(element) && element.arguments[0] && ts.isObjectLiteralExpression(element.arguments[0]) ? element.arguments[0] : null;
1154
+ if (!objectLiteral) continue;
1155
+ const targetAgentId = readStringPropertyInitializer(objectLiteral, "targetAgentId");
1156
+ if (!targetAgentId) continue;
1157
+ facts.push({
1158
+ kind: "ai-extension",
1159
+ id: `agent-extension:${targetAgentId}`,
1160
+ source: { sourcePath, exportName: "aiAgentExtensions", line: nodeLine(sourceFile, objectLiteral) },
1161
+ metadata: { target: "agent", mode: "extension", targetAgentId }
1162
+ });
1163
+ }
1164
+ }
1165
+ for (const { exportName, target } of AI_OVERRIDE_EXPORTS) {
1166
+ const overridesObject = findObjectLiteralDeclaration(sourceFile, exportName);
1167
+ if (!overridesObject) continue;
1168
+ for (const property of overridesObject.properties) {
1169
+ const overrideId = getPropertyName(property);
1170
+ if (!overrideId) continue;
1171
+ facts.push({
1172
+ kind: "ai-extension",
1173
+ id: `${target}-override:${overrideId}`,
1174
+ source: { sourcePath, exportName, line: nodeLine(sourceFile, property) },
1175
+ metadata: { target, mode: "override", overrideKey: overrideId }
1176
+ });
1177
+ }
1178
+ }
1179
+ }
1180
+ return dedupeOwnedContractFacts(facts);
1181
+ }
1182
+ function extractGeneratorPluginContracts(moduleRoot, sourceRoot) {
1183
+ const filePath = resolveConventionFile(moduleRoot, "generators");
1184
+ if (!filePath) return { facts: [], unresolved: [] };
1185
+ const sourceFile = readSourceFile(filePath);
1186
+ if (!sourceFile) return { facts: [], unresolved: [] };
1187
+ const pluginsArray = findArrayLiteralDeclaration(sourceFile, "generatorPlugins");
1188
+ if (!pluginsArray) return { facts: [], unresolved: [] };
1189
+ const sourcePath = toPortableSourcePath(moduleRoot, sourceRoot, filePath);
1190
+ const initializers = buildVariableInitializerMap(sourceFile);
1191
+ const facts = [];
1192
+ const unresolved = [];
1193
+ let index = 0;
1194
+ for (const element of pluginsArray.elements) {
1195
+ const objectLiteral = resolveToObjectLiteral(element, initializers);
1196
+ if (!objectLiteral) {
1197
+ index += 1;
1198
+ continue;
1199
+ }
1200
+ const pluginId = readStringPropertyInitializer(objectLiteral, "id");
1201
+ if (!pluginId) {
1202
+ unresolved.push({
1203
+ code: "unresolved-static-contract",
1204
+ kind: "generator-plugin",
1205
+ id: `${sourcePath}#${index}`,
1206
+ source: { sourcePath, exportName: "generatorPlugins", line: nodeLine(sourceFile, objectLiteral) }
1207
+ });
1208
+ index += 1;
1209
+ continue;
1210
+ }
1211
+ const metadata = {};
1212
+ const conventionFile = readStringPropertyInitializer(objectLiteral, "conventionFile");
1213
+ if (conventionFile) metadata.conventionFile = conventionFile;
1214
+ const outputFileName = readStringPropertyInitializer(objectLiteral, "outputFileName");
1215
+ if (outputFileName) metadata.outputFileName = outputFileName;
1216
+ facts.push({
1217
+ kind: "generator-plugin",
1218
+ id: pluginId,
1219
+ source: { sourcePath, exportName: "generatorPlugins", line: nodeLine(sourceFile, objectLiteral) },
1220
+ ...Object.keys(metadata).length > 0 ? { metadata } : {}
1221
+ });
1222
+ index += 1;
1223
+ }
1224
+ return { facts: dedupeOwnedContractFacts(facts), unresolved };
1225
+ }
1226
+ function compareSourceRefs(left, right) {
1227
+ return left.sourcePath.localeCompare(right.sourcePath) || (left.line ?? 0) - (right.line ?? 0) || (left.exportName ?? "").localeCompare(right.exportName ?? "");
1228
+ }
1229
+ function sameSourceRef(left, right) {
1230
+ return left.sourcePath === right.sourcePath && (left.line ?? null) === (right.line ?? null) && (left.exportName ?? null) === (right.exportName ?? null);
1231
+ }
1232
+ function provenanceCandidateOrderKey(candidate) {
1233
+ if (candidate.source) {
1234
+ return JSON.stringify([
1235
+ 0,
1236
+ candidate.source.sourcePath,
1237
+ candidate.source.line ?? 0,
1238
+ candidate.source.exportName ?? ""
1239
+ ]);
1240
+ }
1241
+ return JSON.stringify([1, candidate.factRef?.factSection ?? "", candidate.factRef?.factKey ?? ""]);
1242
+ }
1243
+ function buildProvenanceIndex(candidates) {
1244
+ const grouped = /* @__PURE__ */ new Map();
1245
+ for (const candidate of candidates) {
1246
+ const key = JSON.stringify([candidate.kind, candidate.id]);
1247
+ const group = grouped.get(key) ?? [];
1248
+ group.push(candidate);
1249
+ grouped.set(key, group);
1250
+ }
1251
+ const factSources = [];
1252
+ const diagnostics = [];
1253
+ for (const group of grouped.values()) {
1254
+ const sorted = [...group].sort(
1255
+ (left, right) => provenanceCandidateOrderKey(left).localeCompare(provenanceCandidateOrderKey(right))
1256
+ );
1257
+ const canonical = sorted[0];
1258
+ factSources.push(
1259
+ canonical.factRef ? {
1260
+ kind: canonical.kind,
1261
+ id: canonical.id,
1262
+ factRef: {
1263
+ factSection: canonical.factRef.factSection,
1264
+ ...canonical.factRef.factKey === canonical.id ? {} : { factKey: canonical.factRef.factKey }
1265
+ }
1266
+ } : {
1267
+ kind: canonical.kind,
1268
+ id: canonical.id,
1269
+ source: { sourcePath: canonical.source.sourcePath }
1270
+ }
1271
+ );
1272
+ const canonicalKey = provenanceCandidateOrderKey(canonical);
1273
+ for (const rejected of sorted.slice(1)) {
1274
+ if (provenanceCandidateOrderKey(rejected) === canonicalKey) continue;
1275
+ diagnostics.push({
1276
+ code: "duplicate-source",
1277
+ kind: rejected.kind,
1278
+ id: rejected.id,
1279
+ ...rejected.source ? { source: rejected.source } : {}
1280
+ });
1281
+ }
1282
+ }
1283
+ factSources.sort(
1284
+ (left, right) => left.kind.localeCompare(right.kind) || left.id.localeCompare(right.id)
1285
+ );
1286
+ diagnostics.sort(
1287
+ (left, right) => left.code.localeCompare(right.code) || left.kind.localeCompare(right.kind) || left.id.localeCompare(right.id) || compareSourceRefs(left.source ?? { sourcePath: "" }, right.source ?? { sourcePath: "" })
1288
+ );
1289
+ return { factSources, diagnostics };
1290
+ }
1291
+ function buildOwnedContracts(families) {
1292
+ const result = {};
1293
+ for (const [kind, facts] of families) {
1294
+ if (facts.length === 0) continue;
1295
+ result[kind] = facts.map((fact) => ({
1296
+ ...fact,
1297
+ source: {
1298
+ sourcePath: fact.source.sourcePath,
1299
+ ...fact.source.line ? { line: fact.source.line } : {}
1300
+ }
1301
+ }));
1302
+ }
1303
+ return Object.keys(result).length > 0 ? result : void 0;
1304
+ }
1305
+ const CONTRIBUTION_KIND_TO_SOURCE_KIND = {
1306
+ subscriber: "subscriber"
1307
+ };
1308
+ const REGISTRY_TO_SOURCE_KIND = {
1309
+ vector: "vector",
1310
+ integration: "integration",
1311
+ payment: "integration",
1312
+ shipping: "integration",
1313
+ currency: "integration"
1314
+ };
528
1315
  function extractModuleFacts(options) {
529
- const { moduleId, coreVersion = null } = options;
1316
+ const { moduleId, coreVersion = null, sourcePackage = null, sourceVersion = null } = options;
530
1317
  const moduleRoot = options.moduleRoot ?? (options.coreSrcRoot ? path.join(options.coreSrcRoot, moduleId) : null);
531
1318
  if (!moduleRoot) {
532
1319
  throw new Error(`[internal] extractModuleFacts requires moduleRoot or coreSrcRoot for module "${moduleId}"`);
533
1320
  }
1321
+ const sourceRoot = toPortableSourceRoot(moduleId, sourcePackage);
534
1322
  const entitiesFilePath = resolveConventionFile(path.join(moduleRoot, "data"), "entities") ?? resolveConventionFile(path.join(moduleRoot, "db"), "entities") ?? resolveConventionFile(path.join(moduleRoot, "data"), "schema");
535
1323
  const ceFilePath = resolveConventionFile(moduleRoot, "ce");
536
1324
  const eventsFilePath = resolveConventionFile(moduleRoot, "events");
@@ -540,7 +1328,9 @@ function extractModuleFacts(options) {
540
1328
  const notificationsFilePath = resolveConventionFile(moduleRoot, "notifications");
541
1329
  const cliFilePath = resolveConventionFile(moduleRoot, "cli");
542
1330
  const indexFilePath = resolveConventionFile(moduleRoot, "index");
543
- const backendDir = path.join(moduleRoot, "backend");
1331
+ const setupFilePath = resolveConventionFile(moduleRoot, "setup");
1332
+ const encryptionFilePath = resolveConventionFile(moduleRoot, "encryption");
1333
+ const portableOf = (filePath) => filePath ? toPortableSourcePath(moduleRoot, sourceRoot, filePath) : null;
544
1334
  const warnings = [];
545
1335
  const { title, description } = extractModuleMeta(indexFilePath);
546
1336
  const customFieldEntityIds = collectCustomFieldEntityIds(ceFilePath);
@@ -548,20 +1338,224 @@ function extractModuleFacts(options) {
548
1338
  const events = extractEvents(eventsFilePath);
549
1339
  const aclFeatures = extractAclFeatures(aclFilePath);
550
1340
  const { source: registrySource, description: registryDescription } = resolveRegistrySource(options);
551
- const apiRoutes = extractApiRoutes(moduleId, registrySource, registryDescription, warnings);
552
- const diTokens = extractDiTokens(diFilePath);
1341
+ const apiRouteSourcePaths = extractApiRouteSourcePaths(moduleId, moduleRoot, sourceRoot);
1342
+ const apiRoutes = extractApiRoutes(moduleId, registrySource, registryDescription, apiRouteSourcePaths, warnings);
1343
+ const diRegistrations = extractDiRegistrationFacts(diFilePath, portableOf(diFilePath) ?? "");
1344
+ const diTokens = deriveLegacyDiTokens(diRegistrations.facts);
1345
+ for (const legacyToken of extractDiTokens(diFilePath)) {
1346
+ if (!diTokens.includes(legacyToken)) diTokens.push(legacyToken);
1347
+ }
553
1348
  const searchEntities = extractSearchEntities(searchFilePath, warnings);
554
1349
  const notifications = extractNotifications(notificationsFilePath, warnings);
555
1350
  const cli = extractCli(cliFilePath, warnings);
1351
+ const cliCommands = cliFilePath ? cli.map((command) => ({
1352
+ command,
1353
+ sourcePath: toPortableSourcePath(moduleRoot, sourceRoot, cliFilePath)
1354
+ })) : [];
1355
+ const backendPages = extractModulePages(moduleId, moduleRoot, sourceRoot, "backend");
1356
+ const frontendPages = extractModulePages(moduleId, moduleRoot, sourceRoot, "frontend");
1357
+ const aiTools = extractAiTools(moduleRoot, sourceRoot);
1358
+ const aiAgents = extractAiAgents(moduleRoot, sourceRoot);
556
1359
  const hostTokens = {
557
1360
  entityIds: extractHostEntityIds(entities),
558
- tableIds: extractTableIds(backendDir)
1361
+ tableIds: extractTableIds(moduleRoot)
559
1362
  };
560
- return {
1363
+ const extensionSurfaces = extractModuleExtensionFacts({
1364
+ moduleId,
1365
+ moduleRoot,
1366
+ sourceRoot,
1367
+ entities,
1368
+ events,
1369
+ apiRoutes,
1370
+ searchEntities,
1371
+ notifications,
1372
+ aiTools,
1373
+ aiAgents
1374
+ });
1375
+ const indexSourcePath = portableOf(indexFilePath);
1376
+ const diSourcePath = portableOf(diFilePath);
1377
+ const moduleMetadataFact = extractModuleMetadataContract(indexFilePath, moduleId, indexSourcePath);
1378
+ const commandCandidates = extractCommandContractCandidates(moduleId, moduleRoot, sourceRoot);
1379
+ const commandFacts = dedupeOwnedContractFacts(
1380
+ commandCandidates.map((candidate) => ({
1381
+ kind: "command",
1382
+ id: candidate.id,
1383
+ source: { sourcePath: candidate.sourcePath }
1384
+ }))
1385
+ );
1386
+ const workers = extractWorkerContracts(moduleId, moduleRoot, sourceRoot);
1387
+ const pageMiddleware = extractPageMiddlewareContracts(moduleRoot, sourceRoot);
1388
+ const setupFact = extractSetupContract(moduleId, setupFilePath, portableOf(setupFilePath));
1389
+ const encryptionFacts = extractEncryptionContracts(encryptionFilePath, portableOf(encryptionFilePath));
1390
+ const customEntityFacts = extractCustomEntityContracts(ceFilePath, portableOf(ceFilePath));
1391
+ const aiExtensionFacts = extractAiExtensionContracts(moduleRoot, sourceRoot);
1392
+ const generatorPlugins = extractGeneratorPluginContracts(moduleRoot, sourceRoot);
1393
+ const diContractFacts = dedupeOwnedContractFacts(
1394
+ diRegistrations.facts.map((fact) => ({
1395
+ kind: "di-registration",
1396
+ id: fact.token,
1397
+ source: fact.source,
1398
+ metadata: {
1399
+ registrationKind: fact.registrationKind,
1400
+ ...fact.providerSymbol ? { providerSymbol: fact.providerSymbol } : {},
1401
+ ...fact.lifetime ? { lifetime: fact.lifetime } : {},
1402
+ ...fact.injectionMode ? { injectionMode: fact.injectionMode } : {}
1403
+ }
1404
+ }))
1405
+ );
1406
+ const ownedContracts = buildOwnedContracts([
1407
+ ["module-metadata", moduleMetadataFact ? [moduleMetadataFact] : []],
1408
+ ["command", commandFacts],
1409
+ ["worker", workers.facts],
1410
+ ["page-middleware", pageMiddleware.facts],
1411
+ ["setup", setupFact ? [setupFact] : []],
1412
+ ["encryption", encryptionFacts],
1413
+ ["di-registration", diContractFacts],
1414
+ ["custom-entity", customEntityFacts],
1415
+ ["ai-extension", aiExtensionFacts],
1416
+ ["generator-plugin", generatorPlugins.facts]
1417
+ ]);
1418
+ const provenanceCandidates = [];
1419
+ const pushSource = (kind, id, source) => {
1420
+ provenanceCandidates.push({ kind, id, source });
1421
+ };
1422
+ const pushRef = (kind, id, source, factSection, factKey) => {
1423
+ provenanceCandidates.push({ kind, id, source, factRef: { factSection, factKey } });
1424
+ };
1425
+ const pushOwnedRefs = (kind, facts2) => {
1426
+ for (const fact of facts2) pushRef(kind, fact.id, fact.source, `ownedContracts.${kind}`, fact.id);
1427
+ };
1428
+ if (moduleMetadataFact) {
1429
+ pushRef(
1430
+ "module-metadata",
1431
+ moduleId,
1432
+ moduleMetadataFact.source,
1433
+ "ownedContracts.module-metadata",
1434
+ moduleMetadataFact.id
1435
+ );
1436
+ }
1437
+ if (entitiesFilePath) {
1438
+ const entitiesSourcePath = portableOf(entitiesFilePath);
1439
+ for (const entity of entities) {
1440
+ pushSource("entity", entity.id, { sourcePath: entitiesSourcePath, exportName: entity.class });
1441
+ }
1442
+ }
1443
+ if (eventsFilePath) {
1444
+ const eventsSourcePath = portableOf(eventsFilePath);
1445
+ for (const event of events) pushSource("event", event.id, { sourcePath: eventsSourcePath, exportName: "events" });
1446
+ }
1447
+ if (aclFilePath) {
1448
+ const aclSourcePath = portableOf(aclFilePath);
1449
+ for (const feature of aclFeatures) pushSource("acl-feature", feature, { sourcePath: aclSourcePath, exportName: "features" });
1450
+ }
1451
+ for (const route of apiRoutes) {
1452
+ if (route.sourcePath) {
1453
+ pushRef("api-route", route.path, { sourcePath: route.sourcePath }, "apiRoutes", route.path);
1454
+ }
1455
+ }
1456
+ if (searchFilePath) {
1457
+ const searchSourcePath = portableOf(searchFilePath);
1458
+ for (const entityId of searchEntities) pushSource("search", entityId, { sourcePath: searchSourcePath, exportName: "searchConfig" });
1459
+ }
1460
+ if (notificationsFilePath) {
1461
+ const notificationsSourcePath = portableOf(notificationsFilePath);
1462
+ for (const id of notifications) pushSource("notification", id, { sourcePath: notificationsSourcePath });
1463
+ }
1464
+ for (const command of cliCommands) {
1465
+ pushRef("cli-command", command.command, { sourcePath: command.sourcePath }, "cliCommands", command.command);
1466
+ }
1467
+ for (const page of backendPages) {
1468
+ pushRef("backend-page", page.path, { sourcePath: page.sourcePath }, "backendPages", page.path);
1469
+ }
1470
+ for (const page of frontendPages) {
1471
+ pushRef("frontend-page", page.path, { sourcePath: page.sourcePath }, "frontendPages", page.path);
1472
+ }
1473
+ for (const tool of aiTools) pushRef("ai-tool", tool.name, { sourcePath: tool.sourcePath }, "aiTools", tool.name);
1474
+ for (const agent of aiAgents) pushRef("ai-agent", agent.id, { sourcePath: agent.sourcePath }, "aiAgents", agent.id);
1475
+ for (const candidate of commandCandidates) {
1476
+ pushRef("command", candidate.id, { sourcePath: candidate.sourcePath }, "ownedContracts.command", candidate.id);
1477
+ }
1478
+ pushOwnedRefs("worker", workers.facts);
1479
+ pushOwnedRefs("page-middleware", pageMiddleware.facts);
1480
+ if (setupFact) pushOwnedRefs("setup", [setupFact]);
1481
+ pushOwnedRefs("encryption", encryptionFacts);
1482
+ for (const fact of diRegistrations.facts) {
1483
+ pushRef("di-registration", fact.token, fact.source, "ownedContracts.di-registration", fact.token);
1484
+ }
1485
+ pushOwnedRefs("custom-entity", customEntityFacts);
1486
+ pushOwnedRefs("ai-extension", aiExtensionFacts);
1487
+ pushOwnedRefs("generator-plugin", generatorPlugins.facts);
1488
+ const contributionRef = (contributionId) => ({
1489
+ factSection: "extensionSurfaces.contributions",
1490
+ factKey: contributionId
1491
+ });
1492
+ for (const contribution of extensionSurfaces.contributions) {
1493
+ const contributionSource = {
1494
+ sourcePath: contribution.source.path,
1495
+ ...contribution.source.symbol ? { exportName: contribution.source.symbol } : {}
1496
+ };
1497
+ const ref = contributionRef(contribution.id);
1498
+ if (contribution.kind === "subscriber") {
1499
+ pushRef("subscriber", contribution.id, contributionSource, ref.factSection, ref.factKey);
1500
+ continue;
1501
+ }
1502
+ if (contribution.kind === "specialized-registry") {
1503
+ const details = contribution.details;
1504
+ const sourceKind = details.registry ? REGISTRY_TO_SOURCE_KIND[details.registry] : void 0;
1505
+ if (sourceKind) {
1506
+ pushRef(
1507
+ sourceKind,
1508
+ details.registryId ?? contribution.id,
1509
+ contributionSource,
1510
+ ref.factSection,
1511
+ ref.factKey
1512
+ );
1513
+ }
1514
+ continue;
1515
+ }
1516
+ const mapped = CONTRIBUTION_KIND_TO_SOURCE_KIND[contribution.kind];
1517
+ if (mapped) pushRef(mapped, contribution.id, contributionSource, ref.factSection, ref.factKey);
1518
+ }
1519
+ for (const host of extensionSurfaces.hosts) {
1520
+ provenanceCandidates.push({
1521
+ kind: "extension-host",
1522
+ id: host.key,
1523
+ ...host.source.kind === "fact-ref" ? {} : { source: { sourcePath: host.source.path } },
1524
+ factRef: { factSection: "extensionSurfaces.hosts", factKey: host.key }
1525
+ });
1526
+ }
1527
+ for (const contribution of extensionSurfaces.contributions) {
1528
+ pushRef(
1529
+ "extension-contribution",
1530
+ contribution.id,
1531
+ { sourcePath: contribution.source.path },
1532
+ "extensionSurfaces.contributions",
1533
+ contribution.id
1534
+ );
1535
+ }
1536
+ const provenance = buildProvenanceIndex(provenanceCandidates);
1537
+ const factDiagnostics = [
1538
+ ...provenance.diagnostics,
1539
+ ...workers.unresolved,
1540
+ ...pageMiddleware.unresolved,
1541
+ ...generatorPlugins.unresolved,
1542
+ ...diRegistrations.unresolvedTokens.map((token) => ({
1543
+ code: "unresolved-static-contract",
1544
+ kind: "di-registration",
1545
+ id: token,
1546
+ ...diSourcePath ? { source: { sourcePath: diSourcePath, exportName: token } } : {}
1547
+ }))
1548
+ ].sort(
1549
+ (left, right) => left.code.localeCompare(right.code) || left.kind.localeCompare(right.kind) || left.id.localeCompare(right.id)
1550
+ );
1551
+ const facts = {
561
1552
  module: moduleId,
562
1553
  title,
563
1554
  description,
564
1555
  coreVersion,
1556
+ sourcePackage,
1557
+ sourceVersion,
1558
+ sourceRoot,
565
1559
  entities,
566
1560
  events,
567
1561
  aclFeatures,
@@ -571,33 +1565,54 @@ function extractModuleFacts(options) {
571
1565
  hostTokens,
572
1566
  notifications,
573
1567
  cli,
1568
+ backendPages,
1569
+ frontendPages,
1570
+ cliCommands,
1571
+ aiTools,
1572
+ aiAgents,
1573
+ extensionSurfaces,
1574
+ ...provenance.factSources.length > 0 ? { factSources: provenance.factSources } : {},
1575
+ ...ownedContracts ? { ownedContracts } : {},
1576
+ ...factDiagnostics.length > 0 ? { factDiagnostics } : {},
574
1577
  warnings
575
1578
  };
1579
+ const overrides = collectModuleOverrideTargets(facts, buildFactSourceLookup(facts));
1580
+ if (overrides.overrideTargets.length > 0) facts.overrideTargets = overrides.overrideTargets;
1581
+ if (overrides.overrideTargetDiagnostics.length > 0) {
1582
+ facts.overrideTargetDiagnostics = overrides.overrideTargetDiagnostics;
1583
+ }
1584
+ return facts;
576
1585
  }
577
1586
  const EMPTY_SECTION_MARKER = "_none_";
578
- function renderVersionStamp(coreVersion) {
1587
+ function renderVersionStamp(coreVersion, sourcePackage, sourceVersion) {
1588
+ if (sourcePackage) {
1589
+ return `<!-- generated from ${sourcePackage} ${sourceVersion || "<unknown>"}; core ${coreVersion || "<unknown>"} \u2014 R1 staleness stamp -->`;
1590
+ }
579
1591
  const version = coreVersion && coreVersion.length > 0 ? coreVersion : "<unknown>";
580
1592
  return `<!-- generated from @open-mercato/core ${version} \u2014 R1 staleness stamp -->`;
581
1593
  }
582
- function renderEntitiesSection(entities) {
1594
+ function renderEntitiesSection(entities, lookup) {
583
1595
  if (entities.length === 0) return `## Entities
584
1596
 
585
1597
  ${EMPTY_SECTION_MARKER}`;
586
- const header = "| Entity ID | Class | Table | Editable | CustomFields |";
587
- const divider = "|---|---|---|---|---|";
1598
+ const header = "| Entity ID | Class | Table | Editable | CustomFields | Source |";
1599
+ const divider = "|---|---|---|---|---|---|";
588
1600
  const rows = entities.map(
589
- (entity) => `| ${entity.id} | ${entity.class} | ${entity.table} | ${entity.editable ? "yes" : "no"} | ${entity.customFields ? "yes" : "no"} |`
1601
+ (entity) => `| ${entity.id} | ${entity.class} | ${entity.table} | ${entity.editable ? "yes" : "no"} | ${entity.customFields ? "yes" : "no"} | ${renderLookedUpSource(lookup, "entity", entity.id)} |`
590
1602
  );
591
1603
  return ["## Entities", "", header, divider, ...rows].join("\n");
592
1604
  }
593
- function renderEventsSection(events) {
1605
+ function renderEventsSection(events, lookup) {
594
1606
  const heading = `## Events (${events.length})`;
595
1607
  if (events.length === 0) return `${heading}
596
1608
 
597
1609
  ${EMPTY_SECTION_MARKER}`;
598
- const header = "| ID | Category | Entity |";
599
- const divider = "|---|---|---|";
600
- const rows = events.map((event) => `| ${event.id} | ${event.category ?? "\u2014"} | ${event.entity ?? "\u2014"} |`);
1610
+ const header = "| ID | Category | Entity | Browser transport | Source |";
1611
+ const divider = "|---|---|---|---|---|";
1612
+ const rows = events.map((event) => {
1613
+ const transports = [event.clientBroadcast ? "client" : null, event.portalBroadcast ? "portal" : null].filter((value) => value !== null);
1614
+ return `| ${event.id} | ${event.category ?? "\u2014"} | ${event.entity ?? "\u2014"} | ${transports.join(", ") || "\u2014"} | ${renderLookedUpSource(lookup, "event", event.id)} |`;
1615
+ });
601
1616
  return [heading, "", header, divider, ...rows].join("\n");
602
1617
  }
603
1618
  function renderInlineListSection(heading, values) {
@@ -608,6 +1623,57 @@ ${EMPTY_SECTION_MARKER}`;
608
1623
 
609
1624
  ${values.join(" \xB7 ")}`;
610
1625
  }
1626
+ function renderSourceLinkedListSection(heading, values, lookup, kind, idColumnLabel) {
1627
+ if (values.length === 0) return `${heading}
1628
+
1629
+ ${EMPTY_SECTION_MARKER}`;
1630
+ const rows = values.map((value) => `| ${value} | ${renderLookedUpSource(lookup, kind, value)} |`);
1631
+ return [heading, "", `| ${idColumnLabel} | Source |`, "|---|---|", ...rows].join("\n");
1632
+ }
1633
+ function renderSourceLink(sourcePath) {
1634
+ return `[${sourcePath}](../../../${sourcePath})`;
1635
+ }
1636
+ function renderSourceRefLink(source) {
1637
+ const anchor = source.line ? `#L${source.line}` : "";
1638
+ const label = source.line ? `${source.sourcePath}:${source.line}` : source.sourcePath;
1639
+ return `[${label}](../../../${source.sourcePath}${anchor})`;
1640
+ }
1641
+ function renderLookedUpSource(lookup, kind, id) {
1642
+ const source = lookup(kind, id);
1643
+ return source ? renderSourceRefLink(source) : "\u2014";
1644
+ }
1645
+ function renderSafeScalar(value) {
1646
+ if (value === void 0 || value === null) return "\u2014";
1647
+ if (Array.isArray(value)) return value.length > 0 ? value.map((entry) => String(entry)).join(", ") : "\u2014";
1648
+ return String(value);
1649
+ }
1650
+ function renderOwnedContractMetadata(metadata) {
1651
+ if (!metadata) return "\u2014";
1652
+ const keys = Object.keys(metadata).sort((left, right) => left.localeCompare(right));
1653
+ if (keys.length === 0) return "\u2014";
1654
+ return keys.map((key) => `${key}=${renderSafeScalar(metadata[key])}`).join("; ");
1655
+ }
1656
+ function renderOwnedContractSection(heading, facts) {
1657
+ if (!facts || facts.length === 0) return "";
1658
+ const rows = facts.map(
1659
+ (fact) => `| ${fact.id} | ${renderOwnedContractMetadata(fact.metadata)} | ${renderSourceRefLink(fact.source)} |`
1660
+ );
1661
+ return [heading, "", "| ID | Metadata | Source |", "|---|---|---|", ...rows].join("\n");
1662
+ }
1663
+ function renderDiRegistrationsSection(facts) {
1664
+ if (!facts || facts.length === 0) return "";
1665
+ const rows = facts.map((fact) => {
1666
+ const metadata = fact.metadata ?? {};
1667
+ return `| ${fact.id} | ${renderSafeScalar(metadata.registrationKind)} | ${renderSafeScalar(metadata.lifetime)} | ${renderSafeScalar(metadata.injectionMode)} | ${renderSafeScalar(metadata.providerSymbol)} | ${renderSourceRefLink(fact.source)} |`;
1668
+ });
1669
+ return [
1670
+ "## DI registrations (rich)",
1671
+ "",
1672
+ "| Token | Kind | Lifetime | Injection | Provider | Source |",
1673
+ "|---|---|---|---|---|---|",
1674
+ ...rows
1675
+ ].join("\n");
1676
+ }
611
1677
  function describeAuthRule(rule) {
612
1678
  if (!rule) return "public";
613
1679
  if (rule.requireFeatures && rule.requireFeatures.length > 0) return rule.requireFeatures.join(", ");
@@ -630,42 +1696,246 @@ function renderApiRoutesSection(routes) {
630
1696
  if (routes.length === 0) return `## API routes
631
1697
 
632
1698
  ${EMPTY_SECTION_MARKER}`;
633
- const header = "| Path | Methods | Auth (per-method requireFeatures) |";
634
- const divider = "|---|---|---|";
1699
+ const header = "| Path | Methods | Auth (per-method requireFeatures) | Source |";
1700
+ const divider = "|---|---|---|---|";
635
1701
  const rows = routes.map(
636
- (route) => `| ${route.path} | ${route.methods.join(" ")} | ${renderApiRouteAuthCell(route)} |`
1702
+ (route) => `| ${route.path} | ${route.methods.join(" ")} | ${renderApiRouteAuthCell(route)} | ${route.sourcePath ? renderSourceLink(route.sourcePath) : "\u2014"} |`
637
1703
  );
638
1704
  return ["## API routes", "", header, divider, ...rows].join("\n");
639
1705
  }
1706
+ function renderLinkedFactsSection(heading, facts) {
1707
+ if (facts.length === 0) return `${heading}
1708
+
1709
+ ${EMPTY_SECTION_MARKER}`;
1710
+ const rows = facts.map((fact) => `| ${fact.label} | ${renderSourceLink(fact.sourcePath)} |`);
1711
+ return [heading, "", "| ID / path | Source |", "|---|---|", ...rows].join("\n");
1712
+ }
640
1713
  function renderHostTokensSection(hostTokens) {
641
1714
  const entityIdsLine = hostTokens.entityIds.length > 0 ? hostTokens.entityIds.join(" \xB7 ") : EMPTY_SECTION_MARKER;
642
1715
  const tableIdsLine = hostTokens.tableIds.length > 0 ? hostTokens.tableIds.join(" \xB7 ") : EMPTY_SECTION_MARKER;
643
1716
  return ["## Host extension points", "", `- Entity IDs: ${entityIdsLine}`, `- Table IDs: ${tableIdsLine}`].join("\n");
644
1717
  }
1718
+ function renderExtensionHostContext(host) {
1719
+ return host.contextContract ?? host.runtimeContract ?? host.scopeContract ?? "\u2014";
1720
+ }
1721
+ function renderExtensionHostSource(host) {
1722
+ if (host.source.kind === "fact-ref") return `${host.source.factSection}:${host.source.factKey}`;
1723
+ return renderSourceRefLink({ sourcePath: host.source.path });
1724
+ }
1725
+ function renderExtensionHostsSection(extensionSurfaces) {
1726
+ const boundHosts = extensionSurfaces.hosts.filter((host) => host.bound);
1727
+ if (boundHosts.length === 0) return `## UMES hosts
1728
+
1729
+ ${EMPTY_SECTION_MARKER}`;
1730
+ const rows = boundHosts.map(
1731
+ (host) => `| ${host.id} | ${host.family} | ${host.capabilities.join(", ") || "\u2014"} | ${renderExtensionHostContext(host)} | ${host.stability.toUpperCase()} | ${renderExtensionHostSource(host)} |`
1732
+ );
1733
+ return [
1734
+ "## UMES hosts",
1735
+ "",
1736
+ "| ID / pattern | Family | Supports | Context | Stability | Source |",
1737
+ "|---|---|---|---|---|---|",
1738
+ ...rows
1739
+ ].join("\n");
1740
+ }
1741
+ function compactContributionDetails(contribution) {
1742
+ const details = contribution.details;
1743
+ return Object.keys(details).sort((left, right) => left.localeCompare(right)).flatMap((key) => {
1744
+ const value = details[key];
1745
+ if (value === void 0) return [];
1746
+ if (Array.isArray(value)) return [`${key}=${value.join(",") || "none"}`];
1747
+ if (value && typeof value === "object") {
1748
+ const nested = Object.keys(value).sort((left, right) => left.localeCompare(right)).map((nestedKey) => {
1749
+ const nestedValue = value[nestedKey];
1750
+ return `${nestedKey}:${Array.isArray(nestedValue) ? nestedValue.join(",") : String(nestedValue)}`;
1751
+ });
1752
+ return [`${key}={${nested.join(";")}}`];
1753
+ }
1754
+ return [`${key}=${String(value)}`];
1755
+ }).join("; ");
1756
+ }
1757
+ function renderExtensionContributionsSection(extensionSurfaces) {
1758
+ if (extensionSurfaces.contributions.length === 0) return `## UMES contributions
1759
+
1760
+ ${EMPTY_SECTION_MARKER}`;
1761
+ const rows = extensionSurfaces.contributions.map((contribution) => {
1762
+ const targets = contribution.targets.map((entry) => entry.id).join(", ");
1763
+ const resolution = contribution.targets.map((entry) => entry.resolution).join(", ");
1764
+ const phases = [...contribution.phases ?? [], ...contribution.operations ?? []].join(", ") || "\u2014";
1765
+ const source = renderSourceRefLink({ sourcePath: contribution.source.path });
1766
+ return `| ${contribution.id} | ${contribution.kind} | ${targets || "\u2014"} | ${phases} | ${compactContributionDetails(contribution)} | ${resolution || "\u2014"} | ${source} |`;
1767
+ });
1768
+ return [
1769
+ "## UMES contributions",
1770
+ "",
1771
+ "| ID | Kind | Target | Phase / operations | Contract | Resolution | Source |",
1772
+ "|---|---|---|---|---|---|---|",
1773
+ ...rows
1774
+ ].join("\n");
1775
+ }
1776
+ function renderExtensionDiagnosticsSection(extensionSurfaces) {
1777
+ const unbound = extensionSurfaces.hosts.filter((host) => !host.bound);
1778
+ if (unbound.length === 0 && extensionSurfaces.unresolved.length === 0) return "";
1779
+ const diagnostics = [
1780
+ ...unbound.map((host) => `- unbound-helper: ${host.id}`),
1781
+ ...extensionSurfaces.unresolved.map((entry) => `- ${entry.reason}: ${entry.key} (${entry.source.path})`)
1782
+ ];
1783
+ return ["## UMES diagnostics", "", ...diagnostics].join("\n");
1784
+ }
1785
+ function renderExtensionTargetRef(target) {
1786
+ const method = target.method ? ` ${target.method}` : "";
1787
+ const owner = target.moduleId ? ` @${target.moduleId}` : "";
1788
+ return `${target.kind}:${target.id}${method}${owner}`;
1789
+ }
1790
+ function renderActivationBindingsSection(extensionSurfaces) {
1791
+ const activations = extensionSurfaces.activations ?? [];
1792
+ if (activations.length === 0) return `## Active extension bindings
1793
+
1794
+ ${EMPTY_SECTION_MARKER}`;
1795
+ const rows = activations.map((activation) => {
1796
+ const phases = activation.phases && activation.phases.length > 0 ? activation.phases.join(", ") : "\u2014";
1797
+ const source = activation.source ? renderSourceRefLink(activation.source) : "\u2014";
1798
+ const bridge = activation.bridge ? `${activation.bridge.factSection}:${activation.bridge.factKey}` : "\u2014";
1799
+ return `| ${activation.id} | ${activation.kind} | ${renderExtensionTargetRef(activation.host)} | ${activation.contributionKinds.join(", ") || "\u2014"} | ${phases} | ${bridge} | ${source} |`;
1800
+ });
1801
+ return [
1802
+ "## Active extension bindings",
1803
+ "",
1804
+ "| Activation | Kind | Host | Contribution kinds | Phases | Bridge | Source |",
1805
+ "|---|---|---|---|---|---|---|",
1806
+ ...rows
1807
+ ].join("\n");
1808
+ }
1809
+ function renderIncomingContributionsSection(extensionSurfaces) {
1810
+ const incoming = extensionSurfaces.incoming ?? [];
1811
+ if (incoming.length === 0) return `## Incoming installed contributions
1812
+
1813
+ ${EMPTY_SECTION_MARKER}`;
1814
+ const rows = incoming.map((entry) => {
1815
+ const activation = entry.activationId ?? "\u2014";
1816
+ const source = entry.source ? renderSourceRefLink(entry.source) : "\u2014";
1817
+ return `| ${entry.contributorModuleId} | ${entry.contributionKind} | ${renderExtensionTargetRef(entry.target)} | ${entry.resolution} | ${activation} | ${entry.contributionId} \xB7 ${source} |`;
1818
+ });
1819
+ return [
1820
+ "## Incoming installed contributions",
1821
+ "",
1822
+ "| Contributor | Kind | Target | Resolution | Activation | Contribution \xB7 Source |",
1823
+ "|---|---|---|---|---|---|",
1824
+ ...rows
1825
+ ].join("\n");
1826
+ }
1827
+ function renderContributionResolutionsSection(extensionSurfaces, lookup) {
1828
+ const resolutions = extensionSurfaces.contributionResolutions ?? [];
1829
+ if (resolutions.length === 0) return `## Contribution resolutions
1830
+
1831
+ ${EMPTY_SECTION_MARKER}`;
1832
+ const rows = resolutions.map((resolution) => {
1833
+ const activations = resolution.activationIds.length > 0 ? resolution.activationIds.join(", ") : "\u2014";
1834
+ const source = renderLookedUpSource(lookup, "extension-contribution", resolution.contributionId);
1835
+ return `| ${resolution.contributionId} | ${renderExtensionTargetRef(resolution.target)} | ${resolution.resolution} | ${activations} | ${source} |`;
1836
+ });
1837
+ return [
1838
+ "## Contribution resolutions",
1839
+ "",
1840
+ "| Contribution | Target | Resolution | Activations | Source |",
1841
+ "|---|---|---|---|---|",
1842
+ ...rows
1843
+ ].join("\n");
1844
+ }
1845
+ function renderOverrideTargetPath(target) {
1846
+ let display = "overrides";
1847
+ for (let index = 0; index < target.path.length; index += 1) {
1848
+ const segment = target.path[index];
1849
+ const isKeyedTerminal = target.key !== void 0 && index === target.path.length - 1;
1850
+ display += isKeyedTerminal ? `[${JSON.stringify(segment)}]` : `.${segment}`;
1851
+ }
1852
+ return display;
1853
+ }
1854
+ function renderOverrideTargetsSection(targets) {
1855
+ if (!targets || targets.length === 0) return `## Exact override targets
1856
+
1857
+ ${EMPTY_SECTION_MARKER}`;
1858
+ const header = "| Domain | Path / key | Supported modes | Referenced fact | Source |";
1859
+ const divider = "|---|---|---|---|---|";
1860
+ const rows = targets.map((target) => {
1861
+ const path2 = `\`${renderOverrideTargetPath(target)}\``;
1862
+ const modes = target.modes.join(", ") || "\u2014";
1863
+ const fact = `${target.factRef.factSection}:${target.factRef.factKey}`;
1864
+ return `| ${target.domain} | ${path2} | ${modes} | ${fact} | ${renderSourceRefLink(target.source)} |`;
1865
+ });
1866
+ return ["## Exact override targets", "", header, divider, ...rows].join("\n");
1867
+ }
645
1868
  function renderModuleFactsMarkdown(facts) {
1869
+ const extensionSurfaces = facts.extensionSurfaces ?? { hosts: [], contributions: [], unresolved: [] };
1870
+ const lookup = buildFactSourceLookup(facts);
646
1871
  const sections = [
647
1872
  `# ${facts.module} \u2014 module facts (generated, do not edit)`,
648
- renderVersionStamp(facts.coreVersion),
1873
+ renderVersionStamp(facts.coreVersion, facts.sourcePackage, facts.sourceVersion),
1874
+ `Source root: ${renderSourceLink(facts.sourceRoot)}`,
649
1875
  "",
650
- renderEntitiesSection(facts.entities),
1876
+ renderEntitiesSection(facts.entities, lookup),
651
1877
  "",
652
- renderEventsSection(facts.events),
1878
+ renderEventsSection(facts.events, lookup),
653
1879
  "",
654
- renderInlineListSection(`## ACL features (${facts.aclFeatures.length})`, facts.aclFeatures),
1880
+ renderSourceLinkedListSection(
1881
+ `## ACL features (${facts.aclFeatures.length})`,
1882
+ facts.aclFeatures,
1883
+ lookup,
1884
+ "acl-feature",
1885
+ "Feature"
1886
+ ),
655
1887
  "",
656
1888
  renderApiRoutesSection(facts.apiRoutes),
657
1889
  "",
658
- renderInlineListSection("## DI service tokens", facts.diTokens),
1890
+ renderLinkedFactsSection("## Backend pages", facts.backendPages.map((page) => ({ label: page.path, sourcePath: page.sourcePath }))),
1891
+ "",
1892
+ renderLinkedFactsSection("## Frontend pages", facts.frontendPages.map((page) => ({ label: page.path, sourcePath: page.sourcePath }))),
1893
+ "",
1894
+ renderSourceLinkedListSection("## DI service tokens", facts.diTokens, lookup, "di-registration", "Token"),
659
1895
  "",
660
- renderInlineListSection("## Search entities", facts.searchEntities),
1896
+ renderSourceLinkedListSection("## Search entities", facts.searchEntities, lookup, "search", "Entity ID"),
661
1897
  "",
662
1898
  renderHostTokensSection(facts.hostTokens),
663
1899
  "",
664
- renderInlineListSection("## Notifications", facts.notifications),
1900
+ renderExtensionHostsSection(extensionSurfaces),
1901
+ "",
1902
+ renderExtensionContributionsSection(extensionSurfaces),
1903
+ "",
1904
+ renderActivationBindingsSection(extensionSurfaces),
665
1905
  "",
666
- renderInlineListSection("## CLI", facts.cli),
1906
+ renderIncomingContributionsSection(extensionSurfaces),
1907
+ "",
1908
+ renderContributionResolutionsSection(extensionSurfaces, lookup),
1909
+ "",
1910
+ renderExtensionDiagnosticsSection(extensionSurfaces),
1911
+ "",
1912
+ renderSourceLinkedListSection("## Notifications", facts.notifications, lookup, "notification", "Notification ID"),
1913
+ "",
1914
+ renderLinkedFactsSection("## CLI commands", facts.cliCommands.map((command) => ({ label: command.command, sourcePath: command.sourcePath }))),
1915
+ "",
1916
+ renderLinkedFactsSection("## AI tools / MCP capabilities", facts.aiTools.map((tool) => ({ label: tool.name, sourcePath: tool.sourcePath }))),
1917
+ "",
1918
+ renderLinkedFactsSection("## AI agents", facts.aiAgents.map((agent) => ({ label: agent.id, sourcePath: agent.sourcePath }))),
1919
+ "",
1920
+ renderOverrideTargetsSection(facts.overrideTargets),
667
1921
  ""
668
1922
  ];
1923
+ const owned = facts.ownedContracts ?? {};
1924
+ const ownedSections = [
1925
+ renderOwnedContractSection("## Owned contract \u2014 module metadata", owned["module-metadata"]),
1926
+ renderOwnedContractSection("## Domain commands", owned.command),
1927
+ renderOwnedContractSection("## Workers", owned.worker),
1928
+ renderOwnedContractSection("## Page middleware", owned["page-middleware"]),
1929
+ renderOwnedContractSection("## Setup", owned.setup),
1930
+ renderOwnedContractSection("## Encryption", owned.encryption),
1931
+ renderDiRegistrationsSection(owned["di-registration"]),
1932
+ renderOwnedContractSection("## Custom entities", owned["custom-entity"]),
1933
+ renderOwnedContractSection("## AI extensions", owned["ai-extension"]),
1934
+ renderOwnedContractSection("## Generator plugins", owned["generator-plugin"])
1935
+ ].filter((section) => section.length > 0);
1936
+ for (const section of ownedSections) {
1937
+ sections.push(section, "");
1938
+ }
669
1939
  return sections.join("\n");
670
1940
  }
671
1941
  function toModuleFactsJsonEntry(facts) {
@@ -673,15 +1943,35 @@ function toModuleFactsJsonEntry(facts) {
673
1943
  title: facts.title,
674
1944
  description: facts.description,
675
1945
  coreVersion: facts.coreVersion,
1946
+ sourcePackage: facts.sourcePackage,
1947
+ sourceVersion: facts.sourceVersion,
1948
+ sourceRoot: facts.sourceRoot,
676
1949
  entities: facts.entities,
677
- events: facts.events.map((event) => ({ id: event.id, category: event.category, entity: event.entity })),
1950
+ events: facts.events.map((event) => ({
1951
+ id: event.id,
1952
+ category: event.category,
1953
+ entity: event.entity,
1954
+ ...event.clientBroadcast !== void 0 ? { clientBroadcast: event.clientBroadcast } : {},
1955
+ ...event.portalBroadcast !== void 0 ? { portalBroadcast: event.portalBroadcast } : {}
1956
+ })),
678
1957
  aclFeatures: facts.aclFeatures,
679
1958
  apiRoutes: facts.apiRoutes,
680
1959
  diTokens: facts.diTokens,
681
1960
  searchEntities: facts.searchEntities,
682
1961
  hostTokens: facts.hostTokens,
683
1962
  notifications: facts.notifications,
684
- cli: facts.cli
1963
+ cli: facts.cli,
1964
+ backendPages: facts.backendPages,
1965
+ frontendPages: facts.frontendPages,
1966
+ cliCommands: facts.cliCommands,
1967
+ aiTools: facts.aiTools,
1968
+ aiAgents: facts.aiAgents,
1969
+ ...facts.extensionSurfaces ? { extensionSurfaces: facts.extensionSurfaces } : {},
1970
+ ...facts.factSources && facts.factSources.length > 0 ? { factSources: facts.factSources } : {},
1971
+ ...facts.ownedContracts && Object.keys(facts.ownedContracts).length > 0 ? { ownedContracts: facts.ownedContracts } : {},
1972
+ ...facts.factDiagnostics && facts.factDiagnostics.length > 0 ? { factDiagnostics: facts.factDiagnostics } : {},
1973
+ ...facts.overrideTargets && facts.overrideTargets.length > 0 ? { overrideTargets: facts.overrideTargets } : {},
1974
+ ...facts.overrideTargetDiagnostics && facts.overrideTargetDiagnostics.length > 0 ? { overrideTargetDiagnostics: facts.overrideTargetDiagnostics } : {}
685
1975
  };
686
1976
  }
687
1977
  function buildModuleFactsJsonObject(factsByModule) {
@@ -696,6 +1986,9 @@ function renderModuleFactsJson(factsByModule) {
696
1986
  `;
697
1987
  }
698
1988
  function extractAllModuleFacts(options) {
1989
+ return withModuleExtensionFactExtractionCache(() => extractAllModuleFactsWithCache(options));
1990
+ }
1991
+ function extractAllModuleFactsWithCache(options) {
699
1992
  const sources = options.sources ? [...options.sources] : options.coreSrcRoot ? (options.moduleIds ?? []).map((moduleId) => ({
700
1993
  moduleId,
701
1994
  moduleRoot: path.join(options.coreSrcRoot, moduleId)
@@ -708,14 +2001,63 @@ function extractAllModuleFacts(options) {
708
2001
  moduleId: source.moduleId,
709
2002
  moduleRoot: source.moduleRoot,
710
2003
  coreVersion: options.coreVersion ?? null,
2004
+ sourcePackage: source.from ?? null,
2005
+ sourceVersion: source.packageVersion ?? null,
711
2006
  registryPath: options.registryPath ?? null,
712
2007
  registrySource: options.registrySource ?? null
713
2008
  });
714
2009
  factsByModule[source.moduleId] = facts;
715
- markdownByModule[source.moduleId] = renderModuleFactsMarkdown(facts);
716
2010
  warnings.push(...facts.warnings);
717
2011
  }
718
- return { factsByModule, markdownByModule, warnings };
2012
+ const surfacesByModule = Object.fromEntries(
2013
+ Object.entries(factsByModule).map(([moduleId, facts]) => [
2014
+ moduleId,
2015
+ facts.extensionSurfaces ?? { hosts: [], contributions: [], unresolved: [] }
2016
+ ])
2017
+ );
2018
+ const correlated = correlateModuleExtensionFacts({
2019
+ surfacesByModule,
2020
+ entityIds: new Set(Object.values(factsByModule).flatMap((facts) => facts.entities.map((entity) => entity.id))),
2021
+ eventIds: new Set(Object.values(factsByModule).flatMap((facts) => facts.events.map((event) => event.id))),
2022
+ apiRoutes: /* @__PURE__ */ new Set([
2023
+ ...Object.values(factsByModule).flatMap((facts) => facts.apiRoutes.map((route) => route.path)),
2024
+ ...sources.flatMap((source) => extractKnownApiRouteIds(source.moduleId, source.moduleRoot))
2025
+ ]),
2026
+ commandIds: new Set(sources.flatMap((source) => extractKnownCommandIds(source.moduleId, source.moduleRoot)))
2027
+ });
2028
+ assertNoUnresolvedExtensionTargets(correlated);
2029
+ const apiRouteOwners = /* @__PURE__ */ new Map();
2030
+ const commandOwners = /* @__PURE__ */ new Map();
2031
+ for (const source of [...sources].sort((left, right) => left.moduleId.localeCompare(right.moduleId))) {
2032
+ const facts = factsByModule[source.moduleId];
2033
+ const sourceRoot = facts ? facts.sourceRoot : source.moduleId;
2034
+ const owners = extractActivationTargetOwners({
2035
+ moduleId: source.moduleId,
2036
+ moduleRoot: source.moduleRoot,
2037
+ sourceRoot
2038
+ });
2039
+ for (const route of owners.apiRoutes) {
2040
+ if (!apiRouteOwners.has(route.id)) {
2041
+ apiRouteOwners.set(route.id, { moduleId: source.moduleId, source: route.source, bridges: route.bridges });
2042
+ }
2043
+ }
2044
+ for (const command of owners.commands) {
2045
+ if (!commandOwners.has(command.id)) commandOwners.set(command.id, { moduleId: source.moduleId, source: command.source });
2046
+ }
2047
+ }
2048
+ const withIncoming = correlateIncomingExtensions({
2049
+ surfacesByModule: correlated,
2050
+ apiRouteOwners,
2051
+ commandOwners
2052
+ });
2053
+ for (const moduleId of Object.keys(factsByModule).sort((left, right) => left.localeCompare(right))) {
2054
+ factsByModule[moduleId].extensionSurfaces = withIncoming[moduleId];
2055
+ markdownByModule[moduleId] = renderModuleFactsMarkdown(factsByModule[moduleId]);
2056
+ warnings.push(...correlated[moduleId].unresolved.map(
2057
+ (entry) => `[module-facts] ${moduleId} ${entry.reason}: ${entry.key} (${entry.source.path})`
2058
+ ));
2059
+ }
2060
+ return { factsByModule, markdownByModule, warnings, frameworkMarkdown: renderFrameworkExtensionPointsMarkdown() };
719
2061
  }
720
2062
  export {
721
2063
  buildModuleFactsJsonObject,