@open-mercato/cli 0.6.7 → 0.6.8-canary.319.1.5b4be4bc52

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