@open-mercato/cli 0.6.7-develop.6795.1.8a3f27921c → 0.6.7-develop.6814.1.0627c7e9f1

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 (95) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/build.mjs +1 -1
  3. package/dist/agentic/guides/module-facts.json +90266 -20749
  4. package/dist/agentic/guides/modules/ai_assistant.md +170 -60
  5. package/dist/agentic/guides/modules/api_docs.md +26 -1
  6. package/dist/agentic/guides/modules/api_keys.md +57 -17
  7. package/dist/agentic/guides/modules/attachments.md +72 -19
  8. package/dist/agentic/guides/modules/audit_logs.md +95 -29
  9. package/dist/agentic/guides/modules/auth.md +218 -83
  10. package/dist/agentic/guides/modules/business_rules.md +108 -34
  11. package/dist/agentic/guides/modules/catalog.md +386 -157
  12. package/dist/agentic/guides/modules/channel_gmail.md +55 -6
  13. package/dist/agentic/guides/modules/channel_imap.md +55 -6
  14. package/dist/agentic/guides/modules/checkout.md +254 -129
  15. package/dist/agentic/guides/modules/communication_channels.md +280 -109
  16. package/dist/agentic/guides/modules/configs.md +68 -11
  17. package/dist/agentic/guides/modules/content.md +26 -1
  18. package/dist/agentic/guides/modules/currencies.md +109 -44
  19. package/dist/agentic/guides/modules/customer_accounts.md +350 -156
  20. package/dist/agentic/guides/modules/customers.md +642 -294
  21. package/dist/agentic/guides/modules/dashboards.md +122 -28
  22. package/dist/agentic/guides/modules/data_sync.md +117 -37
  23. package/dist/agentic/guides/modules/design_system.md +36 -2
  24. package/dist/agentic/guides/modules/dictionaries.md +78 -20
  25. package/dist/agentic/guides/modules/directory.md +110 -45
  26. package/dist/agentic/guides/modules/entities.md +95 -38
  27. package/dist/agentic/guides/modules/events.md +31 -1
  28. package/dist/agentic/guides/modules/feature_toggles.md +79 -12
  29. package/dist/agentic/guides/modules/gateway_stripe.md +61 -6
  30. package/dist/agentic/guides/modules/generators.md +17 -1
  31. package/dist/agentic/guides/modules/inbox_ops.md +171 -60
  32. package/dist/agentic/guides/modules/integrations.md +116 -30
  33. package/dist/agentic/guides/modules/messages.md +187 -66
  34. package/dist/agentic/guides/modules/notifications.md +64 -9
  35. package/dist/agentic/guides/modules/onboarding.md +47 -8
  36. package/dist/agentic/guides/modules/payment_gateways.md +133 -48
  37. package/dist/agentic/guides/modules/perspectives.md +50 -12
  38. package/dist/agentic/guides/modules/planner.md +102 -32
  39. package/dist/agentic/guides/modules/portal.md +58 -15
  40. package/dist/agentic/guides/modules/progress.md +78 -30
  41. package/dist/agentic/guides/modules/query_index.md +115 -53
  42. package/dist/agentic/guides/modules/record_locks.md +178 -59
  43. package/dist/agentic/guides/modules/resources.md +155 -78
  44. package/dist/agentic/guides/modules/sales.md +620 -257
  45. package/dist/agentic/guides/modules/scheduler.md +95 -19
  46. package/dist/agentic/guides/modules/search.md +124 -20
  47. package/dist/agentic/guides/modules/security.md +251 -105
  48. package/dist/agentic/guides/modules/shipping_carriers.md +106 -37
  49. package/dist/agentic/guides/modules/sso.md +121 -49
  50. package/dist/agentic/guides/modules/staff.md +314 -126
  51. package/dist/agentic/guides/modules/storage_s3.md +55 -6
  52. package/dist/agentic/guides/modules/sync_akeneo.md +54 -4
  53. package/dist/agentic/guides/modules/sync_excel.md +49 -11
  54. package/dist/agentic/guides/modules/system_status_overlays.md +33 -5
  55. package/dist/agentic/guides/modules/translations.md +76 -21
  56. package/dist/agentic/guides/modules/webhooks.md +166 -82
  57. package/dist/agentic/guides/modules/wms.md +383 -223
  58. package/dist/agentic/guides/modules/workflows.md +248 -112
  59. package/dist/agentic/guides/upstream/AGENTS.md +1 -1
  60. package/dist/agentic/guides/upstream/manifest.json +2 -2
  61. package/dist/agentic/shared/AGENTS.md.template +6 -5
  62. package/dist/agentic/shared/ai/harness/cases.json +3 -0
  63. package/dist/agentic/shared/ai/lessons/_template.md +16 -0
  64. package/dist/agentic/shared/ai/lessons.md +26 -3
  65. package/dist/agentic/shared/ai/skills/om-evolve-harness/SKILL.md +7 -6
  66. package/dist/agentic/shared/ai/skills/om-system-extension/references/unified-overrides.md +4 -2
  67. package/dist/agentic/shared/scripts/check-lessons.mjs +178 -0
  68. package/dist/lib/agentic-setup.js +2 -2
  69. package/dist/lib/agentic-setup.js.map +2 -2
  70. package/dist/lib/generators/module-extension-facts.js +652 -24
  71. package/dist/lib/generators/module-extension-facts.js.map +3 -3
  72. package/dist/lib/generators/module-fact-sources.js +70 -0
  73. package/dist/lib/generators/module-fact-sources.js.map +7 -0
  74. package/dist/lib/generators/module-facts.js +1001 -26
  75. package/dist/lib/generators/module-facts.js.map +3 -3
  76. package/dist/lib/generators/module-override-targets.js +688 -0
  77. package/dist/lib/generators/module-override-targets.js.map +7 -0
  78. package/dist/lib/testing/integration.js +5 -0
  79. package/dist/lib/testing/integration.js.map +2 -2
  80. package/package.json +5 -5
  81. package/src/lib/__tests__/agentic-setup.ownership.test.ts +6 -0
  82. package/src/lib/agentic-setup.ts +6 -2
  83. package/src/lib/generators/__tests__/activation-adapter-coverage.test.ts +92 -0
  84. package/src/lib/generators/__tests__/module-extension-activations.test.ts +762 -0
  85. package/src/lib/generators/__tests__/module-extension-facts.test.ts +4 -2
  86. package/src/lib/generators/__tests__/module-facts.bc-guard.test.ts +37 -3
  87. package/src/lib/generators/__tests__/module-facts.owned-contracts.test.ts +561 -0
  88. package/src/lib/generators/__tests__/module-override-targets.roundtrip.test.ts +149 -0
  89. package/src/lib/generators/__tests__/module-override-targets.test.ts +304 -0
  90. package/src/lib/generators/module-extension-facts.ts +923 -27
  91. package/src/lib/generators/module-fact-sources.ts +108 -0
  92. package/src/lib/generators/module-facts.ts +1360 -25
  93. package/src/lib/generators/module-override-targets.ts +863 -0
  94. package/src/lib/testing/__tests__/integration.test.ts +4 -0
  95. package/src/lib/testing/integration.ts +5 -0
@@ -2,15 +2,23 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import ts from "typescript-js";
4
4
  import { toSnake } from "../utils.js";
5
+ import { extractCommandIdsFromSource } from "./module-registry.js";
6
+ import { MODULE_CODE_EXTENSIONS } from "./scanner.js";
5
7
  import {
6
8
  assertNoUnresolvedExtensionTargets,
9
+ correlateIncomingExtensions,
7
10
  correlateModuleExtensionFacts,
11
+ extractActivationTargetOwners,
8
12
  extractKnownApiRouteIds,
9
13
  extractKnownCommandIds,
10
14
  extractModuleExtensionFacts,
11
15
  renderFrameworkExtensionPointsMarkdown,
12
16
  withModuleExtensionFactExtractionCache
13
17
  } from "./module-extension-facts.js";
18
+ import {
19
+ collectModuleOverrideTargets
20
+ } from "./module-override-targets.js";
21
+ import { buildFactSourceLookup } from "./module-fact-sources.js";
14
22
  function readSourceFile(filePath) {
15
23
  if (!fs.existsSync(filePath)) return null;
16
24
  const source = fs.readFileSync(filePath, "utf8");
@@ -582,16 +590,21 @@ function extractModulePages(moduleId, moduleRoot, sourceRoot, surface) {
582
590
  const fileName = segments.pop();
583
591
  const fileStem = fileName.replace(/\.tsx$/, "");
584
592
  const isModernPage = fileStem === "page";
585
- if (!isModernPage && (fileStem.endsWith(".meta") || /^[A-Z]/.test(fileStem))) continue;
593
+ if (!isModernPage && (fileStem === "meta" || fileStem.endsWith(".meta") || /^[A-Z]/.test(fileStem))) continue;
586
594
  const sourceFile = readSourceFile(filePath);
587
595
  if (!sourceFile || !sourceHasDefaultExport(sourceFile)) continue;
588
596
  const routeSegments = isModernPage ? segments : [...segments, fileStem];
589
597
  const routePath = surface === "frontend" ? `/${routeSegments.filter(Boolean).join("/")}` : isModernPage ? `/backend/${routeSegments.join("/") || moduleId}` : `/backend/${routeSegments[0] === moduleId ? routeSegments.filter(Boolean).join("/") : [moduleId, ...routeSegments].filter(Boolean).join("/")}`;
590
598
  if (seen.has(routePath)) continue;
591
599
  seen.add(routePath);
600
+ const metadataFilePath = resolvePageMetadataFile(path.dirname(filePath));
601
+ const metadataSourceFile = metadataFilePath ? readSourceFile(metadataFilePath) : sourceFile;
602
+ const metadata = metadataSourceFile ? readPageMetadata(metadataSourceFile) : void 0;
592
603
  pages.push({
593
604
  path: routePath,
594
- sourcePath: toPortableSourcePath(moduleRoot, sourceRoot, filePath)
605
+ sourcePath: toPortableSourcePath(moduleRoot, sourceRoot, filePath),
606
+ ...metadataFilePath ? { metadataSourcePath: toPortableSourcePath(moduleRoot, sourceRoot, metadataFilePath) } : {},
607
+ ...metadata ? { metadata } : {}
595
608
  });
596
609
  }
597
610
  return pages.sort((left, right) => left.path.localeCompare(right.path));
@@ -725,6 +738,580 @@ function extractModuleMeta(indexFilePath) {
725
738
  description: readStringPropertyInitializer(metadata, "description") ?? null
726
739
  };
727
740
  }
741
+ function nodeLine(sourceFile, node) {
742
+ return sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;
743
+ }
744
+ function readPropertyNameText(name) {
745
+ if (ts.isIdentifier(name) || ts.isStringLiteralLike(name)) return name.text;
746
+ return void 0;
747
+ }
748
+ function readMethodBearingPropertyNames(objectLiteral) {
749
+ const names = [];
750
+ for (const property of objectLiteral.properties) {
751
+ if (ts.isMethodDeclaration(property)) {
752
+ const name = readPropertyNameText(property.name);
753
+ if (name) names.push(name);
754
+ continue;
755
+ }
756
+ if (ts.isPropertyAssignment(property)) {
757
+ const name = readPropertyNameText(property.name);
758
+ if (!name) continue;
759
+ const initializer = unwrapExpression(property.initializer);
760
+ if (ts.isArrowFunction(initializer) || ts.isFunctionExpression(initializer)) names.push(name);
761
+ }
762
+ }
763
+ return names.sort((left, right) => left.localeCompare(right));
764
+ }
765
+ function readObjectKeyNames(objectLiteral) {
766
+ const names = [];
767
+ for (const property of objectLiteral.properties) {
768
+ const name = getPropertyName(property);
769
+ if (name) names.push(name);
770
+ }
771
+ return names.sort((left, right) => left.localeCompare(right));
772
+ }
773
+ function mapDiRegistrationKind(base) {
774
+ switch (base) {
775
+ case "asFunction":
776
+ return "function";
777
+ case "asClass":
778
+ return "class";
779
+ case "asValue":
780
+ return "value";
781
+ case "aliasTo":
782
+ return "alias";
783
+ default:
784
+ return null;
785
+ }
786
+ }
787
+ function readDiChainMetadata(initializer) {
788
+ const result = {};
789
+ let current = unwrapExpression(initializer);
790
+ while (ts.isCallExpression(current) && ts.isPropertyAccessExpression(current.expression)) {
791
+ const method = current.expression.name.text;
792
+ if ((method === "singleton" || method === "scoped" || method === "transient") && !result.lifetime) {
793
+ result.lifetime = method;
794
+ } else if (method === "proxy" && !result.injectionMode) {
795
+ result.injectionMode = "proxy";
796
+ } else if (method === "classic" && !result.injectionMode) {
797
+ result.injectionMode = "classic";
798
+ }
799
+ current = unwrapExpression(current.expression.expression);
800
+ }
801
+ return result;
802
+ }
803
+ function readDiProviderSymbol(initializer, kind) {
804
+ let current = unwrapExpression(initializer);
805
+ while (ts.isCallExpression(current) && ts.isPropertyAccessExpression(current.expression)) {
806
+ current = unwrapExpression(current.expression.expression);
807
+ }
808
+ if (!ts.isCallExpression(current)) return void 0;
809
+ const argument = current.arguments[0];
810
+ if (!argument) return void 0;
811
+ if (kind === "value") return void 0;
812
+ if (kind === "alias" && ts.isStringLiteralLike(argument)) return argument.text;
813
+ if ((kind === "function" || kind === "class") && ts.isIdentifier(argument)) return argument.text;
814
+ return void 0;
815
+ }
816
+ function extractDiRegistrationFacts(diFilePath, sourcePath) {
817
+ if (!diFilePath) return { facts: [], unresolvedTokens: [] };
818
+ const sourceFile = readSourceFile(diFilePath);
819
+ if (!sourceFile) return { facts: [], unresolvedTokens: [] };
820
+ const facts = [];
821
+ const unresolvedTokens = [];
822
+ const visit = (node) => {
823
+ if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && node.expression.name.text === "register") {
824
+ const argument = node.arguments[0];
825
+ if (argument && ts.isObjectLiteralExpression(argument)) {
826
+ for (const property of argument.properties) {
827
+ if (!ts.isPropertyAssignment(property)) continue;
828
+ const token = getPropertyName(property);
829
+ if (!token) continue;
830
+ const kind = mapDiRegistrationKind(detectAwilixRegistrationKind(property.initializer));
831
+ if (!kind) {
832
+ if (!unresolvedTokens.includes(token)) unresolvedTokens.push(token);
833
+ continue;
834
+ }
835
+ const chain = readDiChainMetadata(property.initializer);
836
+ const providerSymbol = readDiProviderSymbol(property.initializer, kind);
837
+ facts.push({
838
+ token,
839
+ registrationKind: kind,
840
+ ...providerSymbol ? { providerSymbol } : {},
841
+ ...chain.lifetime ? { lifetime: chain.lifetime } : {},
842
+ ...chain.injectionMode ? { injectionMode: chain.injectionMode } : {},
843
+ source: { sourcePath, exportName: token, line: nodeLine(sourceFile, property) }
844
+ });
845
+ }
846
+ }
847
+ }
848
+ node.forEachChild(visit);
849
+ };
850
+ sourceFile.forEachChild(visit);
851
+ return { facts, unresolvedTokens };
852
+ }
853
+ function deriveLegacyDiTokens(facts) {
854
+ const tokens = [];
855
+ const seen = /* @__PURE__ */ new Set();
856
+ for (const fact of facts) {
857
+ if (fact.registrationKind !== "function" && fact.registrationKind !== "class") continue;
858
+ if (seen.has(fact.token)) continue;
859
+ seen.add(fact.token);
860
+ tokens.push(fact.token);
861
+ }
862
+ return tokens;
863
+ }
864
+ function dedupeOwnedContractFacts(facts) {
865
+ const byId = /* @__PURE__ */ new Map();
866
+ for (const fact of facts) {
867
+ const existing = byId.get(fact.id);
868
+ if (!existing) {
869
+ byId.set(fact.id, fact);
870
+ continue;
871
+ }
872
+ if (fact.source.sourcePath.localeCompare(existing.source.sourcePath) < 0) byId.set(fact.id, fact);
873
+ }
874
+ return [...byId.values()].sort((left, right) => left.id.localeCompare(right.id));
875
+ }
876
+ const PAGE_CONTEXT_VALUES = /* @__PURE__ */ new Set(["main", "admin", "settings", "profile"]);
877
+ function readAliasedStringProperty(metadata, preferred, fallback) {
878
+ return readStringPropertyInitializer(metadata, preferred) ?? readStringPropertyInitializer(metadata, fallback);
879
+ }
880
+ function readNumericProperty(metadata, propertyName) {
881
+ const initializer = getObjectPropertyInitializer(metadata, propertyName);
882
+ if (!initializer || !ts.isNumericLiteral(initializer)) return void 0;
883
+ return Number(initializer.text);
884
+ }
885
+ function readPageMetadata(sourceFile) {
886
+ const metadata = findObjectLiteralDeclaration(sourceFile, "metadata");
887
+ if (!metadata) return void 0;
888
+ const result = {};
889
+ const requireAuth = readBooleanPropertyInitializer(metadata, "requireAuth");
890
+ if (requireAuth !== void 0) result.requireAuth = requireAuth;
891
+ const requireCustomerAuth = readBooleanPropertyInitializer(metadata, "requireCustomerAuth");
892
+ if (requireCustomerAuth !== void 0) result.requireCustomerAuth = requireCustomerAuth;
893
+ const requireFeatures = readStringArrayPropertyInitializer(metadata, "requireFeatures");
894
+ if (requireFeatures && requireFeatures.length > 0) result.requireFeatures = requireFeatures;
895
+ const requireCustomerFeatures = readStringArrayPropertyInitializer(metadata, "requireCustomerFeatures");
896
+ if (requireCustomerFeatures && requireCustomerFeatures.length > 0) {
897
+ result.requireCustomerFeatures = requireCustomerFeatures;
898
+ }
899
+ const navHidden = readBooleanPropertyInitializer(metadata, "navHidden");
900
+ if (navHidden !== void 0) result.navHidden = navHidden;
901
+ const navGroup = readAliasedStringProperty(metadata, "pageGroup", "group");
902
+ if (navGroup) result.navGroup = navGroup;
903
+ const navGroupKey = readAliasedStringProperty(metadata, "pageGroupKey", "groupKey");
904
+ if (navGroupKey) result.navGroupKey = navGroupKey;
905
+ const navOrder = readNumericProperty(metadata, "pageOrder") ?? readNumericProperty(metadata, "order");
906
+ if (navOrder !== void 0) result.navOrder = navOrder;
907
+ const pageContext = readStringPropertyInitializer(metadata, "pageContext");
908
+ if (pageContext && PAGE_CONTEXT_VALUES.has(pageContext)) {
909
+ result.pageContext = pageContext;
910
+ }
911
+ return Object.keys(result).length > 0 ? result : void 0;
912
+ }
913
+ function resolvePageMetadataFile(pageDirectory) {
914
+ for (const baseName of ["page.meta", "meta"]) {
915
+ for (const extension of MODULE_CODE_EXTENSIONS) {
916
+ const candidate = path.join(pageDirectory, `${baseName}${extension}`);
917
+ if (fs.existsSync(candidate)) return candidate;
918
+ }
919
+ }
920
+ return null;
921
+ }
922
+ function extractModuleMetadataContract(indexFilePath, moduleId, sourcePath) {
923
+ if (!indexFilePath || !sourcePath) return null;
924
+ const sourceFile = readSourceFile(indexFilePath);
925
+ if (!sourceFile) return null;
926
+ const metadata = findObjectLiteralDeclaration(sourceFile, "metadata");
927
+ if (!metadata) return null;
928
+ const facts = {};
929
+ const name = readStringPropertyInitializer(metadata, "name");
930
+ if (name) facts.name = name;
931
+ const version = readStringPropertyInitializer(metadata, "version");
932
+ if (version) facts.version = version;
933
+ const requires = readStringArrayPropertyInitializer(metadata, "requires");
934
+ if (requires && requires.length > 0) facts.requires = requires;
935
+ const ejectable = readBooleanPropertyInitializer(metadata, "ejectable");
936
+ if (ejectable !== void 0) facts.ejectable = ejectable;
937
+ return {
938
+ kind: "module-metadata",
939
+ id: moduleId,
940
+ source: { sourcePath, exportName: "metadata", line: nodeLine(sourceFile, metadata) },
941
+ ...Object.keys(facts).length > 0 ? { metadata: facts } : {}
942
+ };
943
+ }
944
+ function extractCommandContractCandidates(moduleId, moduleRoot, sourceRoot) {
945
+ const commandsRoot = path.join(moduleRoot, "commands");
946
+ if (!fs.existsSync(commandsRoot)) return [];
947
+ const candidates = [];
948
+ for (const filePath of listSourceFilesRecursive(commandsRoot)) {
949
+ if (path.basename(filePath).replace(/\.tsx?$/, "") === "interceptors") continue;
950
+ const sourcePath = toPortableSourcePath(moduleRoot, sourceRoot, filePath);
951
+ for (const id of extractCommandIdsFromSource(filePath)) candidates.push({ id, sourcePath });
952
+ }
953
+ return candidates;
954
+ }
955
+ function deriveWorkerId(moduleId, workersRoot, filePath) {
956
+ const relativePath = path.relative(workersRoot, filePath).split(path.sep).join("/");
957
+ const segments = relativePath.split("/");
958
+ const fileName = segments.pop();
959
+ const fileStem = fileName.replace(/\.tsx?$/, "");
960
+ return [moduleId, "workers", ...segments, fileStem].filter(Boolean).join(":");
961
+ }
962
+ function readResolvedStringProperty(objectLiteral, propertyName, initializers) {
963
+ const initializer = getObjectPropertyInitializer(objectLiteral, propertyName);
964
+ if (!initializer) return void 0;
965
+ const resolved = unwrapExpression(initializer);
966
+ if (ts.isStringLiteralLike(resolved)) return resolved.text;
967
+ if (ts.isIdentifier(resolved)) {
968
+ const declaration = initializers.get(resolved.text);
969
+ if (declaration) {
970
+ const declarationValue = unwrapExpression(declaration);
971
+ if (ts.isStringLiteralLike(declarationValue)) return declarationValue.text;
972
+ }
973
+ }
974
+ return void 0;
975
+ }
976
+ function extractWorkerContracts(moduleId, moduleRoot, sourceRoot) {
977
+ const workersRoot = path.join(moduleRoot, "workers");
978
+ if (!fs.existsSync(workersRoot)) return { facts: [], unresolved: [] };
979
+ const facts = [];
980
+ const unresolved = [];
981
+ for (const filePath of listSourceFilesRecursive(workersRoot)) {
982
+ const sourceFile = readSourceFile(filePath);
983
+ if (!sourceFile) continue;
984
+ const metadata = findObjectLiteralDeclaration(sourceFile, "metadata");
985
+ if (!metadata) continue;
986
+ const sourcePath = toPortableSourcePath(moduleRoot, sourceRoot, filePath);
987
+ const source = { sourcePath, exportName: "metadata", line: nodeLine(sourceFile, metadata) };
988
+ const initializers = buildVariableInitializerMap(sourceFile);
989
+ const workerId = readResolvedStringProperty(metadata, "id", initializers) ?? deriveWorkerId(moduleId, workersRoot, filePath);
990
+ const hasQueueProperty = getObjectPropertyInitializer(metadata, "queue") !== void 0;
991
+ const queue = readResolvedStringProperty(metadata, "queue", initializers);
992
+ if (!hasQueueProperty) {
993
+ unresolved.push({ code: "unresolved-static-contract", kind: "worker", id: workerId, source });
994
+ continue;
995
+ }
996
+ const workerMetadata = {};
997
+ if (queue) workerMetadata.queue = queue;
998
+ else unresolved.push({ code: "unresolved-static-contract", kind: "worker", id: workerId, source });
999
+ const name = readResolvedStringProperty(metadata, "name", initializers);
1000
+ if (name) workerMetadata.name = name;
1001
+ const concurrencyInitializer = getObjectPropertyInitializer(metadata, "concurrency");
1002
+ if (concurrencyInitializer && ts.isNumericLiteral(concurrencyInitializer)) {
1003
+ workerMetadata.concurrency = Number(concurrencyInitializer.text);
1004
+ }
1005
+ facts.push({
1006
+ kind: "worker",
1007
+ id: workerId,
1008
+ source,
1009
+ ...Object.keys(workerMetadata).length > 0 ? { metadata: workerMetadata } : {}
1010
+ });
1011
+ }
1012
+ return { facts: dedupeOwnedContractFacts(facts), unresolved };
1013
+ }
1014
+ function extractPageMiddlewareContracts(moduleRoot, sourceRoot) {
1015
+ const facts = [];
1016
+ const unresolved = [];
1017
+ for (const surface of ["backend", "frontend"]) {
1018
+ const filePath = resolveConventionFile(path.join(moduleRoot, surface), "middleware");
1019
+ if (!filePath) continue;
1020
+ const sourceFile = readSourceFile(filePath);
1021
+ if (!sourceFile) continue;
1022
+ const sourcePath = toPortableSourcePath(moduleRoot, sourceRoot, filePath);
1023
+ const middlewareArray = findArrayLiteralDeclaration(sourceFile, "middleware") ?? (() => {
1024
+ const defaultExport = findDefaultExportExpression(sourceFile);
1025
+ return defaultExport ? unwrapArrayLiteral(defaultExport) : null;
1026
+ })();
1027
+ if (!middlewareArray) continue;
1028
+ let index = 0;
1029
+ for (const element of middlewareArray.elements) {
1030
+ if (!ts.isObjectLiteralExpression(element)) continue;
1031
+ const middlewareId = readStringPropertyInitializer(element, "id");
1032
+ if (!middlewareId) {
1033
+ unresolved.push({
1034
+ code: "unresolved-static-contract",
1035
+ kind: "page-middleware",
1036
+ id: `${sourcePath}#${index}`,
1037
+ source: { sourcePath, line: nodeLine(sourceFile, element) }
1038
+ });
1039
+ index += 1;
1040
+ continue;
1041
+ }
1042
+ const metadata = { surface };
1043
+ const mode = readStringPropertyInitializer(element, "mode");
1044
+ if (mode) metadata.mode = mode;
1045
+ const priorityInitializer = getObjectPropertyInitializer(element, "priority");
1046
+ if (priorityInitializer && ts.isNumericLiteral(priorityInitializer)) {
1047
+ metadata.priority = Number(priorityInitializer.text);
1048
+ }
1049
+ facts.push({
1050
+ kind: "page-middleware",
1051
+ id: middlewareId,
1052
+ source: { sourcePath, exportName: "middleware", line: nodeLine(sourceFile, element) },
1053
+ metadata
1054
+ });
1055
+ index += 1;
1056
+ }
1057
+ }
1058
+ return { facts: dedupeOwnedContractFacts(facts), unresolved };
1059
+ }
1060
+ function extractSetupContract(moduleId, setupFilePath, sourcePath) {
1061
+ if (!setupFilePath || !sourcePath) return null;
1062
+ const sourceFile = readSourceFile(setupFilePath);
1063
+ if (!sourceFile) return null;
1064
+ const setupObject = findObjectLiteralDeclaration(sourceFile, "setup") ?? (() => {
1065
+ const defaultExport = findDefaultExportExpression(sourceFile);
1066
+ if (!defaultExport) return null;
1067
+ const unwrapped = unwrapExpression(defaultExport);
1068
+ return ts.isObjectLiteralExpression(unwrapped) ? unwrapped : null;
1069
+ })();
1070
+ if (!setupObject) return null;
1071
+ const hooks = readMethodBearingPropertyNames(setupObject);
1072
+ const metadata = {};
1073
+ if (hooks.length > 0) metadata.hooks = hooks;
1074
+ const roleFeatures = getObjectPropertyInitializer(setupObject, "defaultRoleFeatures");
1075
+ if (roleFeatures && ts.isObjectLiteralExpression(roleFeatures)) {
1076
+ const roles = readObjectKeyNames(roleFeatures);
1077
+ if (roles.length > 0) metadata.roles = roles;
1078
+ }
1079
+ const customerRoleFeatures = getObjectPropertyInitializer(setupObject, "defaultCustomerRoleFeatures");
1080
+ if (customerRoleFeatures && ts.isObjectLiteralExpression(customerRoleFeatures)) {
1081
+ const customerRoles = readObjectKeyNames(customerRoleFeatures);
1082
+ if (customerRoles.length > 0) metadata.customerRoles = customerRoles;
1083
+ }
1084
+ return {
1085
+ kind: "setup",
1086
+ id: `${moduleId}:setup`,
1087
+ source: { sourcePath, exportName: "setup", line: nodeLine(sourceFile, setupObject) },
1088
+ ...Object.keys(metadata).length > 0 ? { metadata } : {}
1089
+ };
1090
+ }
1091
+ function extractEncryptionContracts(encryptionFilePath, sourcePath) {
1092
+ if (!encryptionFilePath || !sourcePath) return [];
1093
+ const sourceFile = readSourceFile(encryptionFilePath);
1094
+ if (!sourceFile) return [];
1095
+ const mapsArray = findArrayLiteralDeclaration(sourceFile, "defaultEncryptionMaps") ?? findArrayLiteralDeclaration(sourceFile, "encryptionMaps");
1096
+ if (!mapsArray) return [];
1097
+ const facts = [];
1098
+ for (const element of mapsArray.elements) {
1099
+ if (!ts.isObjectLiteralExpression(element)) continue;
1100
+ const entityId = readStringPropertyInitializer(element, "entityId");
1101
+ if (!entityId) continue;
1102
+ const fieldsInitializer = getObjectPropertyInitializer(element, "fields");
1103
+ const fields = [];
1104
+ if (fieldsInitializer) {
1105
+ const fieldsArray = unwrapArrayLiteral(fieldsInitializer);
1106
+ if (fieldsArray) {
1107
+ for (const fieldElement of fieldsArray.elements) {
1108
+ if (!ts.isObjectLiteralExpression(fieldElement)) continue;
1109
+ const field = readStringPropertyInitializer(fieldElement, "field");
1110
+ if (field) fields.push(field);
1111
+ }
1112
+ }
1113
+ }
1114
+ facts.push({
1115
+ kind: "encryption",
1116
+ id: entityId,
1117
+ source: { sourcePath, exportName: "defaultEncryptionMaps", line: nodeLine(sourceFile, element) },
1118
+ ...fields.length > 0 ? { metadata: { fields: fields.sort((left, right) => left.localeCompare(right)) } } : {}
1119
+ });
1120
+ }
1121
+ return dedupeOwnedContractFacts(facts);
1122
+ }
1123
+ function extractCustomEntityContracts(ceFilePath, sourcePath) {
1124
+ if (!ceFilePath || !sourcePath) return [];
1125
+ const entityIds = [...collectCustomFieldEntityIds(ceFilePath)].sort((left, right) => left.localeCompare(right));
1126
+ return entityIds.map((id) => ({
1127
+ kind: "custom-entity",
1128
+ id,
1129
+ source: { sourcePath, exportName: "default" }
1130
+ }));
1131
+ }
1132
+ const AI_OVERRIDE_EXPORTS = [
1133
+ { exportName: "aiAgentOverrides", target: "agent" },
1134
+ { exportName: "aiToolOverrides", target: "tool" }
1135
+ ];
1136
+ function extractAiExtensionContracts(moduleRoot, sourceRoot) {
1137
+ const facts = [];
1138
+ const sources = [
1139
+ { basename: "ai-agents", relativeDir: "." },
1140
+ { basename: "ai-tools", relativeDir: "." },
1141
+ { basename: "ai-agents.extensions", relativeDir: "." },
1142
+ { basename: "ai-tools.extensions", relativeDir: "." }
1143
+ ];
1144
+ for (const entry of sources) {
1145
+ const filePath = resolveConventionFile(path.join(moduleRoot, entry.relativeDir), entry.basename);
1146
+ if (!filePath) continue;
1147
+ const sourceFile = readSourceFile(filePath);
1148
+ if (!sourceFile) continue;
1149
+ const sourcePath = toPortableSourcePath(moduleRoot, sourceRoot, filePath);
1150
+ const extensionsArray = findArrayLiteralDeclaration(sourceFile, "aiAgentExtensions");
1151
+ if (extensionsArray) {
1152
+ for (const element of extensionsArray.elements) {
1153
+ const objectLiteral = ts.isObjectLiteralExpression(element) ? element : ts.isCallExpression(element) && element.arguments[0] && ts.isObjectLiteralExpression(element.arguments[0]) ? element.arguments[0] : null;
1154
+ if (!objectLiteral) continue;
1155
+ const targetAgentId = readStringPropertyInitializer(objectLiteral, "targetAgentId");
1156
+ if (!targetAgentId) continue;
1157
+ facts.push({
1158
+ kind: "ai-extension",
1159
+ id: `agent-extension:${targetAgentId}`,
1160
+ source: { sourcePath, exportName: "aiAgentExtensions", line: nodeLine(sourceFile, objectLiteral) },
1161
+ metadata: { target: "agent", mode: "extension", targetAgentId }
1162
+ });
1163
+ }
1164
+ }
1165
+ for (const { exportName, target } of AI_OVERRIDE_EXPORTS) {
1166
+ const overridesObject = findObjectLiteralDeclaration(sourceFile, exportName);
1167
+ if (!overridesObject) continue;
1168
+ for (const property of overridesObject.properties) {
1169
+ const overrideId = getPropertyName(property);
1170
+ if (!overrideId) continue;
1171
+ facts.push({
1172
+ kind: "ai-extension",
1173
+ id: `${target}-override:${overrideId}`,
1174
+ source: { sourcePath, exportName, line: nodeLine(sourceFile, property) },
1175
+ metadata: { target, mode: "override", overrideKey: overrideId }
1176
+ });
1177
+ }
1178
+ }
1179
+ }
1180
+ return dedupeOwnedContractFacts(facts);
1181
+ }
1182
+ function extractGeneratorPluginContracts(moduleRoot, sourceRoot) {
1183
+ const filePath = resolveConventionFile(moduleRoot, "generators");
1184
+ if (!filePath) return { facts: [], unresolved: [] };
1185
+ const sourceFile = readSourceFile(filePath);
1186
+ if (!sourceFile) return { facts: [], unresolved: [] };
1187
+ const pluginsArray = findArrayLiteralDeclaration(sourceFile, "generatorPlugins");
1188
+ if (!pluginsArray) return { facts: [], unresolved: [] };
1189
+ const sourcePath = toPortableSourcePath(moduleRoot, sourceRoot, filePath);
1190
+ const initializers = buildVariableInitializerMap(sourceFile);
1191
+ const facts = [];
1192
+ const unresolved = [];
1193
+ let index = 0;
1194
+ for (const element of pluginsArray.elements) {
1195
+ const objectLiteral = resolveToObjectLiteral(element, initializers);
1196
+ if (!objectLiteral) {
1197
+ index += 1;
1198
+ continue;
1199
+ }
1200
+ const pluginId = readStringPropertyInitializer(objectLiteral, "id");
1201
+ if (!pluginId) {
1202
+ unresolved.push({
1203
+ code: "unresolved-static-contract",
1204
+ kind: "generator-plugin",
1205
+ id: `${sourcePath}#${index}`,
1206
+ source: { sourcePath, exportName: "generatorPlugins", line: nodeLine(sourceFile, objectLiteral) }
1207
+ });
1208
+ index += 1;
1209
+ continue;
1210
+ }
1211
+ const metadata = {};
1212
+ const conventionFile = readStringPropertyInitializer(objectLiteral, "conventionFile");
1213
+ if (conventionFile) metadata.conventionFile = conventionFile;
1214
+ const outputFileName = readStringPropertyInitializer(objectLiteral, "outputFileName");
1215
+ if (outputFileName) metadata.outputFileName = outputFileName;
1216
+ facts.push({
1217
+ kind: "generator-plugin",
1218
+ id: pluginId,
1219
+ source: { sourcePath, exportName: "generatorPlugins", line: nodeLine(sourceFile, objectLiteral) },
1220
+ ...Object.keys(metadata).length > 0 ? { metadata } : {}
1221
+ });
1222
+ index += 1;
1223
+ }
1224
+ return { facts: dedupeOwnedContractFacts(facts), unresolved };
1225
+ }
1226
+ function compareSourceRefs(left, right) {
1227
+ return left.sourcePath.localeCompare(right.sourcePath) || (left.line ?? 0) - (right.line ?? 0) || (left.exportName ?? "").localeCompare(right.exportName ?? "");
1228
+ }
1229
+ function sameSourceRef(left, right) {
1230
+ return left.sourcePath === right.sourcePath && (left.line ?? null) === (right.line ?? null) && (left.exportName ?? null) === (right.exportName ?? null);
1231
+ }
1232
+ function provenanceCandidateOrderKey(candidate) {
1233
+ if (candidate.source) {
1234
+ return JSON.stringify([
1235
+ 0,
1236
+ candidate.source.sourcePath,
1237
+ candidate.source.line ?? 0,
1238
+ candidate.source.exportName ?? ""
1239
+ ]);
1240
+ }
1241
+ return JSON.stringify([1, candidate.factRef?.factSection ?? "", candidate.factRef?.factKey ?? ""]);
1242
+ }
1243
+ function buildProvenanceIndex(candidates) {
1244
+ const grouped = /* @__PURE__ */ new Map();
1245
+ for (const candidate of candidates) {
1246
+ const key = JSON.stringify([candidate.kind, candidate.id]);
1247
+ const group = grouped.get(key) ?? [];
1248
+ group.push(candidate);
1249
+ grouped.set(key, group);
1250
+ }
1251
+ const factSources = [];
1252
+ const diagnostics = [];
1253
+ for (const group of grouped.values()) {
1254
+ const sorted = [...group].sort(
1255
+ (left, right) => provenanceCandidateOrderKey(left).localeCompare(provenanceCandidateOrderKey(right))
1256
+ );
1257
+ const canonical = sorted[0];
1258
+ factSources.push(
1259
+ canonical.factRef ? {
1260
+ kind: canonical.kind,
1261
+ id: canonical.id,
1262
+ factRef: {
1263
+ factSection: canonical.factRef.factSection,
1264
+ ...canonical.factRef.factKey === canonical.id ? {} : { factKey: canonical.factRef.factKey }
1265
+ }
1266
+ } : {
1267
+ kind: canonical.kind,
1268
+ id: canonical.id,
1269
+ source: { sourcePath: canonical.source.sourcePath }
1270
+ }
1271
+ );
1272
+ const canonicalKey = provenanceCandidateOrderKey(canonical);
1273
+ for (const rejected of sorted.slice(1)) {
1274
+ if (provenanceCandidateOrderKey(rejected) === canonicalKey) continue;
1275
+ diagnostics.push({
1276
+ code: "duplicate-source",
1277
+ kind: rejected.kind,
1278
+ id: rejected.id,
1279
+ ...rejected.source ? { source: rejected.source } : {}
1280
+ });
1281
+ }
1282
+ }
1283
+ factSources.sort(
1284
+ (left, right) => left.kind.localeCompare(right.kind) || left.id.localeCompare(right.id)
1285
+ );
1286
+ diagnostics.sort(
1287
+ (left, right) => left.code.localeCompare(right.code) || left.kind.localeCompare(right.kind) || left.id.localeCompare(right.id) || compareSourceRefs(left.source ?? { sourcePath: "" }, right.source ?? { sourcePath: "" })
1288
+ );
1289
+ return { factSources, diagnostics };
1290
+ }
1291
+ function buildOwnedContracts(families) {
1292
+ const result = {};
1293
+ for (const [kind, facts] of families) {
1294
+ if (facts.length === 0) continue;
1295
+ result[kind] = facts.map((fact) => ({
1296
+ ...fact,
1297
+ source: {
1298
+ sourcePath: fact.source.sourcePath,
1299
+ ...fact.source.line ? { line: fact.source.line } : {}
1300
+ }
1301
+ }));
1302
+ }
1303
+ return Object.keys(result).length > 0 ? result : void 0;
1304
+ }
1305
+ const CONTRIBUTION_KIND_TO_SOURCE_KIND = {
1306
+ subscriber: "subscriber"
1307
+ };
1308
+ const REGISTRY_TO_SOURCE_KIND = {
1309
+ vector: "vector",
1310
+ integration: "integration",
1311
+ payment: "integration",
1312
+ shipping: "integration",
1313
+ currency: "integration"
1314
+ };
728
1315
  function extractModuleFacts(options) {
729
1316
  const { moduleId, coreVersion = null, sourcePackage = null, sourceVersion = null } = options;
730
1317
  const moduleRoot = options.moduleRoot ?? (options.coreSrcRoot ? path.join(options.coreSrcRoot, moduleId) : null);
@@ -741,6 +1328,9 @@ function extractModuleFacts(options) {
741
1328
  const notificationsFilePath = resolveConventionFile(moduleRoot, "notifications");
742
1329
  const cliFilePath = resolveConventionFile(moduleRoot, "cli");
743
1330
  const indexFilePath = resolveConventionFile(moduleRoot, "index");
1331
+ const setupFilePath = resolveConventionFile(moduleRoot, "setup");
1332
+ const encryptionFilePath = resolveConventionFile(moduleRoot, "encryption");
1333
+ const portableOf = (filePath) => filePath ? toPortableSourcePath(moduleRoot, sourceRoot, filePath) : null;
744
1334
  const warnings = [];
745
1335
  const { title, description } = extractModuleMeta(indexFilePath);
746
1336
  const customFieldEntityIds = collectCustomFieldEntityIds(ceFilePath);
@@ -750,7 +1340,11 @@ function extractModuleFacts(options) {
750
1340
  const { source: registrySource, description: registryDescription } = resolveRegistrySource(options);
751
1341
  const apiRouteSourcePaths = extractApiRouteSourcePaths(moduleId, moduleRoot, sourceRoot);
752
1342
  const apiRoutes = extractApiRoutes(moduleId, registrySource, registryDescription, apiRouteSourcePaths, warnings);
753
- const diTokens = extractDiTokens(diFilePath);
1343
+ const diRegistrations = extractDiRegistrationFacts(diFilePath, portableOf(diFilePath) ?? "");
1344
+ const diTokens = deriveLegacyDiTokens(diRegistrations.facts);
1345
+ for (const legacyToken of extractDiTokens(diFilePath)) {
1346
+ if (!diTokens.includes(legacyToken)) diTokens.push(legacyToken);
1347
+ }
754
1348
  const searchEntities = extractSearchEntities(searchFilePath, warnings);
755
1349
  const notifications = extractNotifications(notificationsFilePath, warnings);
756
1350
  const cli = extractCli(cliFilePath, warnings);
@@ -778,7 +1372,183 @@ function extractModuleFacts(options) {
778
1372
  aiTools,
779
1373
  aiAgents
780
1374
  });
781
- return {
1375
+ const indexSourcePath = portableOf(indexFilePath);
1376
+ const diSourcePath = portableOf(diFilePath);
1377
+ const moduleMetadataFact = extractModuleMetadataContract(indexFilePath, moduleId, indexSourcePath);
1378
+ const commandCandidates = extractCommandContractCandidates(moduleId, moduleRoot, sourceRoot);
1379
+ const commandFacts = dedupeOwnedContractFacts(
1380
+ commandCandidates.map((candidate) => ({
1381
+ kind: "command",
1382
+ id: candidate.id,
1383
+ source: { sourcePath: candidate.sourcePath }
1384
+ }))
1385
+ );
1386
+ const workers = extractWorkerContracts(moduleId, moduleRoot, sourceRoot);
1387
+ const pageMiddleware = extractPageMiddlewareContracts(moduleRoot, sourceRoot);
1388
+ const setupFact = extractSetupContract(moduleId, setupFilePath, portableOf(setupFilePath));
1389
+ const encryptionFacts = extractEncryptionContracts(encryptionFilePath, portableOf(encryptionFilePath));
1390
+ const customEntityFacts = extractCustomEntityContracts(ceFilePath, portableOf(ceFilePath));
1391
+ const aiExtensionFacts = extractAiExtensionContracts(moduleRoot, sourceRoot);
1392
+ const generatorPlugins = extractGeneratorPluginContracts(moduleRoot, sourceRoot);
1393
+ const diContractFacts = dedupeOwnedContractFacts(
1394
+ diRegistrations.facts.map((fact) => ({
1395
+ kind: "di-registration",
1396
+ id: fact.token,
1397
+ source: fact.source,
1398
+ metadata: {
1399
+ registrationKind: fact.registrationKind,
1400
+ ...fact.providerSymbol ? { providerSymbol: fact.providerSymbol } : {},
1401
+ ...fact.lifetime ? { lifetime: fact.lifetime } : {},
1402
+ ...fact.injectionMode ? { injectionMode: fact.injectionMode } : {}
1403
+ }
1404
+ }))
1405
+ );
1406
+ const ownedContracts = buildOwnedContracts([
1407
+ ["module-metadata", moduleMetadataFact ? [moduleMetadataFact] : []],
1408
+ ["command", commandFacts],
1409
+ ["worker", workers.facts],
1410
+ ["page-middleware", pageMiddleware.facts],
1411
+ ["setup", setupFact ? [setupFact] : []],
1412
+ ["encryption", encryptionFacts],
1413
+ ["di-registration", diContractFacts],
1414
+ ["custom-entity", customEntityFacts],
1415
+ ["ai-extension", aiExtensionFacts],
1416
+ ["generator-plugin", generatorPlugins.facts]
1417
+ ]);
1418
+ const provenanceCandidates = [];
1419
+ const pushSource = (kind, id, source) => {
1420
+ provenanceCandidates.push({ kind, id, source });
1421
+ };
1422
+ const pushRef = (kind, id, source, factSection, factKey) => {
1423
+ provenanceCandidates.push({ kind, id, source, factRef: { factSection, factKey } });
1424
+ };
1425
+ const pushOwnedRefs = (kind, facts2) => {
1426
+ for (const fact of facts2) pushRef(kind, fact.id, fact.source, `ownedContracts.${kind}`, fact.id);
1427
+ };
1428
+ if (moduleMetadataFact) {
1429
+ pushRef(
1430
+ "module-metadata",
1431
+ moduleId,
1432
+ moduleMetadataFact.source,
1433
+ "ownedContracts.module-metadata",
1434
+ moduleMetadataFact.id
1435
+ );
1436
+ }
1437
+ if (entitiesFilePath) {
1438
+ const entitiesSourcePath = portableOf(entitiesFilePath);
1439
+ for (const entity of entities) {
1440
+ pushSource("entity", entity.id, { sourcePath: entitiesSourcePath, exportName: entity.class });
1441
+ }
1442
+ }
1443
+ if (eventsFilePath) {
1444
+ const eventsSourcePath = portableOf(eventsFilePath);
1445
+ for (const event of events) pushSource("event", event.id, { sourcePath: eventsSourcePath, exportName: "events" });
1446
+ }
1447
+ if (aclFilePath) {
1448
+ const aclSourcePath = portableOf(aclFilePath);
1449
+ for (const feature of aclFeatures) pushSource("acl-feature", feature, { sourcePath: aclSourcePath, exportName: "features" });
1450
+ }
1451
+ for (const route of apiRoutes) {
1452
+ if (route.sourcePath) {
1453
+ pushRef("api-route", route.path, { sourcePath: route.sourcePath }, "apiRoutes", route.path);
1454
+ }
1455
+ }
1456
+ if (searchFilePath) {
1457
+ const searchSourcePath = portableOf(searchFilePath);
1458
+ for (const entityId of searchEntities) pushSource("search", entityId, { sourcePath: searchSourcePath, exportName: "searchConfig" });
1459
+ }
1460
+ if (notificationsFilePath) {
1461
+ const notificationsSourcePath = portableOf(notificationsFilePath);
1462
+ for (const id of notifications) pushSource("notification", id, { sourcePath: notificationsSourcePath });
1463
+ }
1464
+ for (const command of cliCommands) {
1465
+ pushRef("cli-command", command.command, { sourcePath: command.sourcePath }, "cliCommands", command.command);
1466
+ }
1467
+ for (const page of backendPages) {
1468
+ pushRef("backend-page", page.path, { sourcePath: page.sourcePath }, "backendPages", page.path);
1469
+ }
1470
+ for (const page of frontendPages) {
1471
+ pushRef("frontend-page", page.path, { sourcePath: page.sourcePath }, "frontendPages", page.path);
1472
+ }
1473
+ for (const tool of aiTools) pushRef("ai-tool", tool.name, { sourcePath: tool.sourcePath }, "aiTools", tool.name);
1474
+ for (const agent of aiAgents) pushRef("ai-agent", agent.id, { sourcePath: agent.sourcePath }, "aiAgents", agent.id);
1475
+ for (const candidate of commandCandidates) {
1476
+ pushRef("command", candidate.id, { sourcePath: candidate.sourcePath }, "ownedContracts.command", candidate.id);
1477
+ }
1478
+ pushOwnedRefs("worker", workers.facts);
1479
+ pushOwnedRefs("page-middleware", pageMiddleware.facts);
1480
+ if (setupFact) pushOwnedRefs("setup", [setupFact]);
1481
+ pushOwnedRefs("encryption", encryptionFacts);
1482
+ for (const fact of diRegistrations.facts) {
1483
+ pushRef("di-registration", fact.token, fact.source, "ownedContracts.di-registration", fact.token);
1484
+ }
1485
+ pushOwnedRefs("custom-entity", customEntityFacts);
1486
+ pushOwnedRefs("ai-extension", aiExtensionFacts);
1487
+ pushOwnedRefs("generator-plugin", generatorPlugins.facts);
1488
+ const contributionRef = (contributionId) => ({
1489
+ factSection: "extensionSurfaces.contributions",
1490
+ factKey: contributionId
1491
+ });
1492
+ for (const contribution of extensionSurfaces.contributions) {
1493
+ const contributionSource = {
1494
+ sourcePath: contribution.source.path,
1495
+ ...contribution.source.symbol ? { exportName: contribution.source.symbol } : {}
1496
+ };
1497
+ const ref = contributionRef(contribution.id);
1498
+ if (contribution.kind === "subscriber") {
1499
+ pushRef("subscriber", contribution.id, contributionSource, ref.factSection, ref.factKey);
1500
+ continue;
1501
+ }
1502
+ if (contribution.kind === "specialized-registry") {
1503
+ const details = contribution.details;
1504
+ const sourceKind = details.registry ? REGISTRY_TO_SOURCE_KIND[details.registry] : void 0;
1505
+ if (sourceKind) {
1506
+ pushRef(
1507
+ sourceKind,
1508
+ details.registryId ?? contribution.id,
1509
+ contributionSource,
1510
+ ref.factSection,
1511
+ ref.factKey
1512
+ );
1513
+ }
1514
+ continue;
1515
+ }
1516
+ const mapped = CONTRIBUTION_KIND_TO_SOURCE_KIND[contribution.kind];
1517
+ if (mapped) pushRef(mapped, contribution.id, contributionSource, ref.factSection, ref.factKey);
1518
+ }
1519
+ for (const host of extensionSurfaces.hosts) {
1520
+ provenanceCandidates.push({
1521
+ kind: "extension-host",
1522
+ id: host.key,
1523
+ ...host.source.kind === "fact-ref" ? {} : { source: { sourcePath: host.source.path } },
1524
+ factRef: { factSection: "extensionSurfaces.hosts", factKey: host.key }
1525
+ });
1526
+ }
1527
+ for (const contribution of extensionSurfaces.contributions) {
1528
+ pushRef(
1529
+ "extension-contribution",
1530
+ contribution.id,
1531
+ { sourcePath: contribution.source.path },
1532
+ "extensionSurfaces.contributions",
1533
+ contribution.id
1534
+ );
1535
+ }
1536
+ const provenance = buildProvenanceIndex(provenanceCandidates);
1537
+ const factDiagnostics = [
1538
+ ...provenance.diagnostics,
1539
+ ...workers.unresolved,
1540
+ ...pageMiddleware.unresolved,
1541
+ ...generatorPlugins.unresolved,
1542
+ ...diRegistrations.unresolvedTokens.map((token) => ({
1543
+ code: "unresolved-static-contract",
1544
+ kind: "di-registration",
1545
+ id: token,
1546
+ ...diSourcePath ? { source: { sourcePath: diSourcePath, exportName: token } } : {}
1547
+ }))
1548
+ ].sort(
1549
+ (left, right) => left.code.localeCompare(right.code) || left.kind.localeCompare(right.kind) || left.id.localeCompare(right.id)
1550
+ );
1551
+ const facts = {
782
1552
  module: moduleId,
783
1553
  title,
784
1554
  description,
@@ -801,8 +1571,17 @@ function extractModuleFacts(options) {
801
1571
  aiTools,
802
1572
  aiAgents,
803
1573
  extensionSurfaces,
1574
+ ...provenance.factSources.length > 0 ? { factSources: provenance.factSources } : {},
1575
+ ...ownedContracts ? { ownedContracts } : {},
1576
+ ...factDiagnostics.length > 0 ? { factDiagnostics } : {},
804
1577
  warnings
805
1578
  };
1579
+ const overrides = collectModuleOverrideTargets(facts, buildFactSourceLookup(facts));
1580
+ if (overrides.overrideTargets.length > 0) facts.overrideTargets = overrides.overrideTargets;
1581
+ if (overrides.overrideTargetDiagnostics.length > 0) {
1582
+ facts.overrideTargetDiagnostics = overrides.overrideTargetDiagnostics;
1583
+ }
1584
+ return facts;
806
1585
  }
807
1586
  const EMPTY_SECTION_MARKER = "_none_";
808
1587
  function renderVersionStamp(coreVersion, sourcePackage, sourceVersion) {
@@ -812,27 +1591,27 @@ function renderVersionStamp(coreVersion, sourcePackage, sourceVersion) {
812
1591
  const version = coreVersion && coreVersion.length > 0 ? coreVersion : "<unknown>";
813
1592
  return `<!-- generated from @open-mercato/core ${version} \u2014 R1 staleness stamp -->`;
814
1593
  }
815
- function renderEntitiesSection(entities) {
1594
+ function renderEntitiesSection(entities, lookup) {
816
1595
  if (entities.length === 0) return `## Entities
817
1596
 
818
1597
  ${EMPTY_SECTION_MARKER}`;
819
- const header = "| Entity ID | Class | Table | Editable | CustomFields |";
820
- const divider = "|---|---|---|---|---|";
1598
+ const header = "| Entity ID | Class | Table | Editable | CustomFields | Source |";
1599
+ const divider = "|---|---|---|---|---|---|";
821
1600
  const rows = entities.map(
822
- (entity) => `| ${entity.id} | ${entity.class} | ${entity.table} | ${entity.editable ? "yes" : "no"} | ${entity.customFields ? "yes" : "no"} |`
1601
+ (entity) => `| ${entity.id} | ${entity.class} | ${entity.table} | ${entity.editable ? "yes" : "no"} | ${entity.customFields ? "yes" : "no"} | ${renderLookedUpSource(lookup, "entity", entity.id)} |`
823
1602
  );
824
1603
  return ["## Entities", "", header, divider, ...rows].join("\n");
825
1604
  }
826
- function renderEventsSection(events) {
1605
+ function renderEventsSection(events, lookup) {
827
1606
  const heading = `## Events (${events.length})`;
828
1607
  if (events.length === 0) return `${heading}
829
1608
 
830
1609
  ${EMPTY_SECTION_MARKER}`;
831
- const header = "| ID | Category | Entity | Browser transport |";
832
- const divider = "|---|---|---|---|";
1610
+ const header = "| ID | Category | Entity | Browser transport | Source |";
1611
+ const divider = "|---|---|---|---|---|";
833
1612
  const rows = events.map((event) => {
834
1613
  const transports = [event.clientBroadcast ? "client" : null, event.portalBroadcast ? "portal" : null].filter((value) => value !== null);
835
- return `| ${event.id} | ${event.category ?? "\u2014"} | ${event.entity ?? "\u2014"} | ${transports.join(", ") || "\u2014"} |`;
1614
+ return `| ${event.id} | ${event.category ?? "\u2014"} | ${event.entity ?? "\u2014"} | ${transports.join(", ") || "\u2014"} | ${renderLookedUpSource(lookup, "event", event.id)} |`;
836
1615
  });
837
1616
  return [heading, "", header, divider, ...rows].join("\n");
838
1617
  }
@@ -844,9 +1623,57 @@ ${EMPTY_SECTION_MARKER}`;
844
1623
 
845
1624
  ${values.join(" \xB7 ")}`;
846
1625
  }
1626
+ function renderSourceLinkedListSection(heading, values, lookup, kind, idColumnLabel) {
1627
+ if (values.length === 0) return `${heading}
1628
+
1629
+ ${EMPTY_SECTION_MARKER}`;
1630
+ const rows = values.map((value) => `| ${value} | ${renderLookedUpSource(lookup, kind, value)} |`);
1631
+ return [heading, "", `| ${idColumnLabel} | Source |`, "|---|---|", ...rows].join("\n");
1632
+ }
847
1633
  function renderSourceLink(sourcePath) {
848
1634
  return `[${sourcePath}](../../../${sourcePath})`;
849
1635
  }
1636
+ function renderSourceRefLink(source) {
1637
+ const anchor = source.line ? `#L${source.line}` : "";
1638
+ const label = source.line ? `${source.sourcePath}:${source.line}` : source.sourcePath;
1639
+ return `[${label}](../../../${source.sourcePath}${anchor})`;
1640
+ }
1641
+ function renderLookedUpSource(lookup, kind, id) {
1642
+ const source = lookup(kind, id);
1643
+ return source ? renderSourceRefLink(source) : "\u2014";
1644
+ }
1645
+ function renderSafeScalar(value) {
1646
+ if (value === void 0 || value === null) return "\u2014";
1647
+ if (Array.isArray(value)) return value.length > 0 ? value.map((entry) => String(entry)).join(", ") : "\u2014";
1648
+ return String(value);
1649
+ }
1650
+ function renderOwnedContractMetadata(metadata) {
1651
+ if (!metadata) return "\u2014";
1652
+ const keys = Object.keys(metadata).sort((left, right) => left.localeCompare(right));
1653
+ if (keys.length === 0) return "\u2014";
1654
+ return keys.map((key) => `${key}=${renderSafeScalar(metadata[key])}`).join("; ");
1655
+ }
1656
+ function renderOwnedContractSection(heading, facts) {
1657
+ if (!facts || facts.length === 0) return "";
1658
+ const rows = facts.map(
1659
+ (fact) => `| ${fact.id} | ${renderOwnedContractMetadata(fact.metadata)} | ${renderSourceRefLink(fact.source)} |`
1660
+ );
1661
+ return [heading, "", "| ID | Metadata | Source |", "|---|---|---|", ...rows].join("\n");
1662
+ }
1663
+ function renderDiRegistrationsSection(facts) {
1664
+ if (!facts || facts.length === 0) return "";
1665
+ const rows = facts.map((fact) => {
1666
+ const metadata = fact.metadata ?? {};
1667
+ return `| ${fact.id} | ${renderSafeScalar(metadata.registrationKind)} | ${renderSafeScalar(metadata.lifetime)} | ${renderSafeScalar(metadata.injectionMode)} | ${renderSafeScalar(metadata.providerSymbol)} | ${renderSourceRefLink(fact.source)} |`;
1668
+ });
1669
+ return [
1670
+ "## DI registrations (rich)",
1671
+ "",
1672
+ "| Token | Kind | Lifetime | Injection | Provider | Source |",
1673
+ "|---|---|---|---|---|---|",
1674
+ ...rows
1675
+ ].join("\n");
1676
+ }
850
1677
  function describeAuthRule(rule) {
851
1678
  if (!rule) return "public";
852
1679
  if (rule.requireFeatures && rule.requireFeatures.length > 0) return rule.requireFeatures.join(", ");
@@ -891,19 +1718,23 @@ function renderHostTokensSection(hostTokens) {
891
1718
  function renderExtensionHostContext(host) {
892
1719
  return host.contextContract ?? host.runtimeContract ?? host.scopeContract ?? "\u2014";
893
1720
  }
1721
+ function renderExtensionHostSource(host) {
1722
+ if (host.source.kind === "fact-ref") return `${host.source.factSection}:${host.source.factKey}`;
1723
+ return renderSourceRefLink({ sourcePath: host.source.path });
1724
+ }
894
1725
  function renderExtensionHostsSection(extensionSurfaces) {
895
1726
  const boundHosts = extensionSurfaces.hosts.filter((host) => host.bound);
896
1727
  if (boundHosts.length === 0) return `## UMES hosts
897
1728
 
898
1729
  ${EMPTY_SECTION_MARKER}`;
899
1730
  const rows = boundHosts.map(
900
- (host) => `| ${host.id} | ${host.family} | ${host.capabilities.join(", ") || "\u2014"} | ${renderExtensionHostContext(host)} | ${host.stability.toUpperCase()} |`
1731
+ (host) => `| ${host.id} | ${host.family} | ${host.capabilities.join(", ") || "\u2014"} | ${renderExtensionHostContext(host)} | ${host.stability.toUpperCase()} | ${renderExtensionHostSource(host)} |`
901
1732
  );
902
1733
  return [
903
1734
  "## UMES hosts",
904
1735
  "",
905
- "| ID / pattern | Family | Supports | Context | Stability |",
906
- "|---|---|---|---|---|",
1736
+ "| ID / pattern | Family | Supports | Context | Stability | Source |",
1737
+ "|---|---|---|---|---|---|",
907
1738
  ...rows
908
1739
  ].join("\n");
909
1740
  }
@@ -931,13 +1762,14 @@ ${EMPTY_SECTION_MARKER}`;
931
1762
  const targets = contribution.targets.map((entry) => entry.id).join(", ");
932
1763
  const resolution = contribution.targets.map((entry) => entry.resolution).join(", ");
933
1764
  const phases = [...contribution.phases ?? [], ...contribution.operations ?? []].join(", ") || "\u2014";
934
- return `| ${contribution.id} | ${contribution.kind} | ${targets || "\u2014"} | ${phases} | ${compactContributionDetails(contribution)} | ${resolution || "\u2014"} |`;
1765
+ const source = renderSourceRefLink({ sourcePath: contribution.source.path });
1766
+ return `| ${contribution.id} | ${contribution.kind} | ${targets || "\u2014"} | ${phases} | ${compactContributionDetails(contribution)} | ${resolution || "\u2014"} | ${source} |`;
935
1767
  });
936
1768
  return [
937
1769
  "## UMES contributions",
938
1770
  "",
939
- "| ID | Kind | Target | Phase / operations | Contract | Resolution |",
940
- "|---|---|---|---|---|---|",
1771
+ "| ID | Kind | Target | Phase / operations | Contract | Resolution | Source |",
1772
+ "|---|---|---|---|---|---|---|",
941
1773
  ...rows
942
1774
  ].join("\n");
943
1775
  }
@@ -950,18 +1782,108 @@ function renderExtensionDiagnosticsSection(extensionSurfaces) {
950
1782
  ];
951
1783
  return ["## UMES diagnostics", "", ...diagnostics].join("\n");
952
1784
  }
1785
+ function renderExtensionTargetRef(target) {
1786
+ const method = target.method ? ` ${target.method}` : "";
1787
+ const owner = target.moduleId ? ` @${target.moduleId}` : "";
1788
+ return `${target.kind}:${target.id}${method}${owner}`;
1789
+ }
1790
+ function renderActivationBindingsSection(extensionSurfaces) {
1791
+ const activations = extensionSurfaces.activations ?? [];
1792
+ if (activations.length === 0) return `## Active extension bindings
1793
+
1794
+ ${EMPTY_SECTION_MARKER}`;
1795
+ const rows = activations.map((activation) => {
1796
+ const phases = activation.phases && activation.phases.length > 0 ? activation.phases.join(", ") : "\u2014";
1797
+ const source = activation.source ? renderSourceRefLink(activation.source) : "\u2014";
1798
+ const bridge = activation.bridge ? `${activation.bridge.factSection}:${activation.bridge.factKey}` : "\u2014";
1799
+ return `| ${activation.id} | ${activation.kind} | ${renderExtensionTargetRef(activation.host)} | ${activation.contributionKinds.join(", ") || "\u2014"} | ${phases} | ${bridge} | ${source} |`;
1800
+ });
1801
+ return [
1802
+ "## Active extension bindings",
1803
+ "",
1804
+ "| Activation | Kind | Host | Contribution kinds | Phases | Bridge | Source |",
1805
+ "|---|---|---|---|---|---|---|",
1806
+ ...rows
1807
+ ].join("\n");
1808
+ }
1809
+ function renderIncomingContributionsSection(extensionSurfaces) {
1810
+ const incoming = extensionSurfaces.incoming ?? [];
1811
+ if (incoming.length === 0) return `## Incoming installed contributions
1812
+
1813
+ ${EMPTY_SECTION_MARKER}`;
1814
+ const rows = incoming.map((entry) => {
1815
+ const activation = entry.activationId ?? "\u2014";
1816
+ const source = entry.source ? renderSourceRefLink(entry.source) : "\u2014";
1817
+ return `| ${entry.contributorModuleId} | ${entry.contributionKind} | ${renderExtensionTargetRef(entry.target)} | ${entry.resolution} | ${activation} | ${entry.contributionId} \xB7 ${source} |`;
1818
+ });
1819
+ return [
1820
+ "## Incoming installed contributions",
1821
+ "",
1822
+ "| Contributor | Kind | Target | Resolution | Activation | Contribution \xB7 Source |",
1823
+ "|---|---|---|---|---|---|",
1824
+ ...rows
1825
+ ].join("\n");
1826
+ }
1827
+ function renderContributionResolutionsSection(extensionSurfaces, lookup) {
1828
+ const resolutions = extensionSurfaces.contributionResolutions ?? [];
1829
+ if (resolutions.length === 0) return `## Contribution resolutions
1830
+
1831
+ ${EMPTY_SECTION_MARKER}`;
1832
+ const rows = resolutions.map((resolution) => {
1833
+ const activations = resolution.activationIds.length > 0 ? resolution.activationIds.join(", ") : "\u2014";
1834
+ const source = renderLookedUpSource(lookup, "extension-contribution", resolution.contributionId);
1835
+ return `| ${resolution.contributionId} | ${renderExtensionTargetRef(resolution.target)} | ${resolution.resolution} | ${activations} | ${source} |`;
1836
+ });
1837
+ return [
1838
+ "## Contribution resolutions",
1839
+ "",
1840
+ "| Contribution | Target | Resolution | Activations | Source |",
1841
+ "|---|---|---|---|---|",
1842
+ ...rows
1843
+ ].join("\n");
1844
+ }
1845
+ function renderOverrideTargetPath(target) {
1846
+ let display = "overrides";
1847
+ for (let index = 0; index < target.path.length; index += 1) {
1848
+ const segment = target.path[index];
1849
+ const isKeyedTerminal = target.key !== void 0 && index === target.path.length - 1;
1850
+ display += isKeyedTerminal ? `[${JSON.stringify(segment)}]` : `.${segment}`;
1851
+ }
1852
+ return display;
1853
+ }
1854
+ function renderOverrideTargetsSection(targets) {
1855
+ if (!targets || targets.length === 0) return `## Exact override targets
1856
+
1857
+ ${EMPTY_SECTION_MARKER}`;
1858
+ const header = "| Domain | Path / key | Supported modes | Referenced fact | Source |";
1859
+ const divider = "|---|---|---|---|---|";
1860
+ const rows = targets.map((target) => {
1861
+ const path2 = `\`${renderOverrideTargetPath(target)}\``;
1862
+ const modes = target.modes.join(", ") || "\u2014";
1863
+ const fact = `${target.factRef.factSection}:${target.factRef.factKey}`;
1864
+ return `| ${target.domain} | ${path2} | ${modes} | ${fact} | ${renderSourceRefLink(target.source)} |`;
1865
+ });
1866
+ return ["## Exact override targets", "", header, divider, ...rows].join("\n");
1867
+ }
953
1868
  function renderModuleFactsMarkdown(facts) {
954
1869
  const extensionSurfaces = facts.extensionSurfaces ?? { hosts: [], contributions: [], unresolved: [] };
1870
+ const lookup = buildFactSourceLookup(facts);
955
1871
  const sections = [
956
1872
  `# ${facts.module} \u2014 module facts (generated, do not edit)`,
957
1873
  renderVersionStamp(facts.coreVersion, facts.sourcePackage, facts.sourceVersion),
958
1874
  `Source root: ${renderSourceLink(facts.sourceRoot)}`,
959
1875
  "",
960
- renderEntitiesSection(facts.entities),
1876
+ renderEntitiesSection(facts.entities, lookup),
961
1877
  "",
962
- renderEventsSection(facts.events),
1878
+ renderEventsSection(facts.events, lookup),
963
1879
  "",
964
- renderInlineListSection(`## ACL features (${facts.aclFeatures.length})`, facts.aclFeatures),
1880
+ renderSourceLinkedListSection(
1881
+ `## ACL features (${facts.aclFeatures.length})`,
1882
+ facts.aclFeatures,
1883
+ lookup,
1884
+ "acl-feature",
1885
+ "Feature"
1886
+ ),
965
1887
  "",
966
1888
  renderApiRoutesSection(facts.apiRoutes),
967
1889
  "",
@@ -969,9 +1891,9 @@ function renderModuleFactsMarkdown(facts) {
969
1891
  "",
970
1892
  renderLinkedFactsSection("## Frontend pages", facts.frontendPages.map((page) => ({ label: page.path, sourcePath: page.sourcePath }))),
971
1893
  "",
972
- renderInlineListSection("## DI service tokens", facts.diTokens),
1894
+ renderSourceLinkedListSection("## DI service tokens", facts.diTokens, lookup, "di-registration", "Token"),
973
1895
  "",
974
- renderInlineListSection("## Search entities", facts.searchEntities),
1896
+ renderSourceLinkedListSection("## Search entities", facts.searchEntities, lookup, "search", "Entity ID"),
975
1897
  "",
976
1898
  renderHostTokensSection(facts.hostTokens),
977
1899
  "",
@@ -979,17 +1901,41 @@ function renderModuleFactsMarkdown(facts) {
979
1901
  "",
980
1902
  renderExtensionContributionsSection(extensionSurfaces),
981
1903
  "",
1904
+ renderActivationBindingsSection(extensionSurfaces),
1905
+ "",
1906
+ renderIncomingContributionsSection(extensionSurfaces),
1907
+ "",
1908
+ renderContributionResolutionsSection(extensionSurfaces, lookup),
1909
+ "",
982
1910
  renderExtensionDiagnosticsSection(extensionSurfaces),
983
1911
  "",
984
- renderInlineListSection("## Notifications", facts.notifications),
1912
+ renderSourceLinkedListSection("## Notifications", facts.notifications, lookup, "notification", "Notification ID"),
985
1913
  "",
986
1914
  renderLinkedFactsSection("## CLI commands", facts.cliCommands.map((command) => ({ label: command.command, sourcePath: command.sourcePath }))),
987
1915
  "",
988
1916
  renderLinkedFactsSection("## AI tools / MCP capabilities", facts.aiTools.map((tool) => ({ label: tool.name, sourcePath: tool.sourcePath }))),
989
1917
  "",
990
1918
  renderLinkedFactsSection("## AI agents", facts.aiAgents.map((agent) => ({ label: agent.id, sourcePath: agent.sourcePath }))),
1919
+ "",
1920
+ renderOverrideTargetsSection(facts.overrideTargets),
991
1921
  ""
992
1922
  ];
1923
+ const owned = facts.ownedContracts ?? {};
1924
+ const ownedSections = [
1925
+ renderOwnedContractSection("## Owned contract \u2014 module metadata", owned["module-metadata"]),
1926
+ renderOwnedContractSection("## Domain commands", owned.command),
1927
+ renderOwnedContractSection("## Workers", owned.worker),
1928
+ renderOwnedContractSection("## Page middleware", owned["page-middleware"]),
1929
+ renderOwnedContractSection("## Setup", owned.setup),
1930
+ renderOwnedContractSection("## Encryption", owned.encryption),
1931
+ renderDiRegistrationsSection(owned["di-registration"]),
1932
+ renderOwnedContractSection("## Custom entities", owned["custom-entity"]),
1933
+ renderOwnedContractSection("## AI extensions", owned["ai-extension"]),
1934
+ renderOwnedContractSection("## Generator plugins", owned["generator-plugin"])
1935
+ ].filter((section) => section.length > 0);
1936
+ for (const section of ownedSections) {
1937
+ sections.push(section, "");
1938
+ }
993
1939
  return sections.join("\n");
994
1940
  }
995
1941
  function toModuleFactsJsonEntry(facts) {
@@ -1020,7 +1966,12 @@ function toModuleFactsJsonEntry(facts) {
1020
1966
  cliCommands: facts.cliCommands,
1021
1967
  aiTools: facts.aiTools,
1022
1968
  aiAgents: facts.aiAgents,
1023
- ...facts.extensionSurfaces ? { extensionSurfaces: facts.extensionSurfaces } : {}
1969
+ ...facts.extensionSurfaces ? { extensionSurfaces: facts.extensionSurfaces } : {},
1970
+ ...facts.factSources && facts.factSources.length > 0 ? { factSources: facts.factSources } : {},
1971
+ ...facts.ownedContracts && Object.keys(facts.ownedContracts).length > 0 ? { ownedContracts: facts.ownedContracts } : {},
1972
+ ...facts.factDiagnostics && facts.factDiagnostics.length > 0 ? { factDiagnostics: facts.factDiagnostics } : {},
1973
+ ...facts.overrideTargets && facts.overrideTargets.length > 0 ? { overrideTargets: facts.overrideTargets } : {},
1974
+ ...facts.overrideTargetDiagnostics && facts.overrideTargetDiagnostics.length > 0 ? { overrideTargetDiagnostics: facts.overrideTargetDiagnostics } : {}
1024
1975
  };
1025
1976
  }
1026
1977
  function buildModuleFactsJsonObject(factsByModule) {
@@ -1075,8 +2026,32 @@ function extractAllModuleFactsWithCache(options) {
1075
2026
  commandIds: new Set(sources.flatMap((source) => extractKnownCommandIds(source.moduleId, source.moduleRoot)))
1076
2027
  });
1077
2028
  assertNoUnresolvedExtensionTargets(correlated);
2029
+ const apiRouteOwners = /* @__PURE__ */ new Map();
2030
+ const commandOwners = /* @__PURE__ */ new Map();
2031
+ for (const source of [...sources].sort((left, right) => left.moduleId.localeCompare(right.moduleId))) {
2032
+ const facts = factsByModule[source.moduleId];
2033
+ const sourceRoot = facts ? facts.sourceRoot : source.moduleId;
2034
+ const owners = extractActivationTargetOwners({
2035
+ moduleId: source.moduleId,
2036
+ moduleRoot: source.moduleRoot,
2037
+ sourceRoot
2038
+ });
2039
+ for (const route of owners.apiRoutes) {
2040
+ if (!apiRouteOwners.has(route.id)) {
2041
+ apiRouteOwners.set(route.id, { moduleId: source.moduleId, source: route.source, bridges: route.bridges });
2042
+ }
2043
+ }
2044
+ for (const command of owners.commands) {
2045
+ if (!commandOwners.has(command.id)) commandOwners.set(command.id, { moduleId: source.moduleId, source: command.source });
2046
+ }
2047
+ }
2048
+ const withIncoming = correlateIncomingExtensions({
2049
+ surfacesByModule: correlated,
2050
+ apiRouteOwners,
2051
+ commandOwners
2052
+ });
1078
2053
  for (const moduleId of Object.keys(factsByModule).sort((left, right) => left.localeCompare(right))) {
1079
- factsByModule[moduleId].extensionSurfaces = correlated[moduleId];
2054
+ factsByModule[moduleId].extensionSurfaces = withIncoming[moduleId];
1080
2055
  markdownByModule[moduleId] = renderModuleFactsMarkdown(factsByModule[moduleId]);
1081
2056
  warnings.push(...correlated[moduleId].unresolved.map(
1082
2057
  (entry) => `[module-facts] ${moduleId} ${entry.reason}: ${entry.key} (${entry.source.path})`