rea-agents 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (246) hide show
  1. package/README.md +78 -32
  2. package/bridge/ghidra/ReaGhidraBridge.java +96 -11
  3. package/dist/application/ArtifactExtraction.js +1 -1
  4. package/dist/application/ArtifactInventory/classify.js +42 -0
  5. package/dist/application/ArtifactInventory/hash.js +43 -0
  6. package/dist/application/ArtifactInventory/reader.js +41 -0
  7. package/dist/application/ArtifactInventory/scanCanonical.js +155 -0
  8. package/dist/application/ArtifactInventory/scanReader.js +124 -0
  9. package/dist/application/ArtifactInventory/types.js +10 -0
  10. package/dist/application/ArtifactInventory.js +9 -351
  11. package/dist/application/AuthorizedArtifactInventory.js +2 -2
  12. package/dist/application/BinarySession.js +47 -270
  13. package/dist/application/BinarySessionExecution.js +28 -0
  14. package/dist/application/BinarySessionOpen.js +39 -0
  15. package/dist/application/BinarySessionRecords.js +149 -0
  16. package/dist/application/BinarySessionStatus.js +75 -0
  17. package/dist/application/CapabilityInventory.js +24 -2
  18. package/dist/application/ClientRegistrationStatus.js +1 -1
  19. package/dist/application/CrossVersionInventory.js +8 -2
  20. package/dist/application/CrossVersionInvestigation.js +1 -23
  21. package/dist/application/CrossVersionInvestigationCollections.js +23 -0
  22. package/dist/application/DirectAnalysis.js +47 -44
  23. package/dist/application/Doctor.js +156 -238
  24. package/dist/application/DoctorDiagnostics.js +180 -0
  25. package/dist/application/ElectronBoundaryAnalysis.js +23 -7
  26. package/dist/application/ElectronBoundaryGraphWindows.js +32 -8
  27. package/dist/application/EnhancedToolPagination.js +22 -0
  28. package/dist/application/EnhancedToolTypes.js +4 -0
  29. package/dist/application/EnhancedTools.js +20 -37
  30. package/dist/application/InstrumentedJavaScriptReplayHost.js +36 -0
  31. package/dist/application/InvestigationWorkspaceStore.js +24 -10
  32. package/dist/application/JavaScriptArtifactAnalysis.js +45 -6
  33. package/dist/application/JavaScriptArtifactFiles.js +6 -2
  34. package/dist/application/JavaScriptArtifactGraphBuilder.js +23 -1
  35. package/dist/application/JavaScriptArtifactGraphBundlers.js +225 -0
  36. package/dist/application/JavaScriptArtifactGraphContext.js +37 -36
  37. package/dist/application/JavaScriptArtifactGraphDocuments.js +27 -4
  38. package/dist/application/JavaScriptArtifactGraphFindings.js +54 -8
  39. package/dist/application/JavaScriptArtifactGraphStructure.js +29 -101
  40. package/dist/application/JavaScriptArtifactPathResolution.js +269 -0
  41. package/dist/application/JavaScriptArtifactReconstruction.js +1 -1
  42. package/dist/application/JavaScriptJsonModules.js +39 -0
  43. package/dist/application/JavaScriptModuleRelationships.js +262 -0
  44. package/dist/application/JavaScriptReplayPermission.js +16 -0
  45. package/dist/application/JavaScriptReplayService.js +182 -156
  46. package/dist/application/LinuxHopper.js +10 -0
  47. package/dist/application/MacHopper.js +22 -11
  48. package/dist/application/NodeRuntimeCharacterizationService.js +196 -0
  49. package/dist/application/PermissionAuthority.js +106 -63
  50. package/dist/application/ProcessCaptureCapability.js +1 -1
  51. package/dist/application/ProcessCapturePermission.js +13 -0
  52. package/dist/application/ProcessCli.js +2 -12
  53. package/dist/application/ProcessHarness.js +1 -1
  54. package/dist/application/ProjectPermissionStore.js +48 -9
  55. package/dist/application/RealHopperAssertions.js +76 -0
  56. package/dist/application/ReconstructionCoverageService.js +69 -0
  57. package/dist/application/ReconstructionCoverageWorkspaceStore.js +31 -0
  58. package/dist/application/RuntimeExecutableDiagnostics.js +172 -0
  59. package/dist/application/Setup.js +147 -259
  60. package/dist/application/SetupClientConfiguration.js +220 -0
  61. package/dist/application/SetupClients.js +24 -2
  62. package/dist/application/SetupPlan.js +123 -12
  63. package/dist/application/SetupSkill.js +26 -12
  64. package/dist/application/SupportedClients.js +1 -0
  65. package/dist/application/Uninstall.js +6 -3
  66. package/dist/application/WindowsCapabilities.js +86 -0
  67. package/dist/artifacts/AsarArtifactReader.js +11 -0
  68. package/dist/browser/BrowserProcessStartup.js +88 -0
  69. package/dist/browser/CdpCaptureDocuments.js +12 -8
  70. package/dist/browser/CdpCaptureEventBodyShapes.js +75 -0
  71. package/dist/browser/CdpCaptureEventHandlers.js +336 -0
  72. package/dist/browser/CdpCaptureEventHelpers.js +71 -0
  73. package/dist/browser/CdpCaptureEventState.js +1 -0
  74. package/dist/browser/CdpCaptureEventTypes.js +1 -0
  75. package/dist/browser/CdpCaptureEvents.js +29 -496
  76. package/dist/browser/CdpObservationSession.js +51 -8
  77. package/dist/browser/CdpPageCapture.js +12 -133
  78. package/dist/browser/CdpPageCaptureScripts.js +97 -0
  79. package/dist/browser/CdpPageCaptureWorkers.js +46 -0
  80. package/dist/browser/CdpWebMcpDiscovery.js +38 -14
  81. package/dist/browser/PngVisualDiff.js +9 -2
  82. package/dist/catalogIdentity.js +4 -0
  83. package/dist/cli/artifactCommands.js +82 -0
  84. package/dist/cli/coreAnalysisCommands.js +232 -0
  85. package/dist/cli/managedCommands.js +397 -0
  86. package/dist/cli/managedProjectGraphCommand.js +24 -0
  87. package/dist/cli/options.js +9 -0
  88. package/dist/cli/setupCommands.js +108 -0
  89. package/dist/cli/types.js +1 -0
  90. package/dist/cli/utilityCommands.js +122 -0
  91. package/dist/cli.js +10 -768
  92. package/dist/cliApplicationCommands.js +118 -4
  93. package/dist/cliBrowserAdvancedCommands.js +73 -16
  94. package/dist/cliBrowserCommands.js +39 -115
  95. package/dist/cliCommandNames.js +4 -0
  96. package/dist/cliElectronCommands.js +33 -62
  97. package/dist/cliEvidenceCommands.js +13 -2
  98. package/dist/cliInvestigationCommands.js +53 -10
  99. package/dist/cliObservationOptions.js +137 -0
  100. package/dist/cliPolicyCommands.js +80 -46
  101. package/dist/cliSetup.js +289 -0
  102. package/dist/config/environment.js +107 -0
  103. package/dist/config/parseConfig.js +228 -0
  104. package/dist/config/parsers.js +68 -0
  105. package/dist/config/permissions.js +30 -0
  106. package/dist/config/types.js +1 -0
  107. package/dist/config.js +1 -375
  108. package/dist/contracts/applicationToolContracts.js +134 -0
  109. package/dist/contracts/managedToolContracts.js +1 -1
  110. package/dist/contracts/managedWorkflowToolContracts.js +3 -3
  111. package/dist/contracts/providerSelection.js +2 -0
  112. package/dist/contracts/toolContracts.js +2 -2
  113. package/dist/contracts/toolEffects.js +14 -0
  114. package/dist/contracts/toolOutputSchemaGroups.js +232 -0
  115. package/dist/contracts/toolOutputSchemaPrimitives.js +327 -0
  116. package/dist/contracts/toolOutputSchemas.js +2 -550
  117. package/dist/domain/analysisErrorPresentation.js +234 -0
  118. package/dist/domain/analysisErrorProjection.js +284 -0
  119. package/dist/domain/artifactComparison.js +34 -15
  120. package/dist/domain/binaryTarget.js +87 -37
  121. package/dist/domain/browserObservation.js +1 -285
  122. package/dist/domain/browserObservationSchemas.js +286 -0
  123. package/dist/domain/electronStaticAnalysisBrowser.js +14 -2
  124. package/dist/domain/errors.js +4 -486
  125. package/dist/domain/functionComparison.js +4 -165
  126. package/dist/domain/functionComparisonDimensions.js +164 -0
  127. package/dist/domain/javascriptApplicationVersionItems.js +18 -6
  128. package/dist/domain/javascriptApplicationVersionKeys.js +8 -8
  129. package/dist/domain/javascriptExportInstrumentation.js +97 -0
  130. package/dist/domain/javascriptFeatureTraversal.js +19 -7
  131. package/dist/domain/javascriptRuntimeStaticCandidates.js +3 -1
  132. package/dist/domain/javascriptSemanticAnalysis.js +386 -0
  133. package/dist/domain/javascriptSemanticIr.js +30 -0
  134. package/dist/domain/javascriptSemanticProjection.js +366 -0
  135. package/dist/domain/javascriptSemanticState.js +40 -0
  136. package/dist/domain/javascriptSemanticTraversal.js +22 -0
  137. package/dist/domain/javascriptSemanticValues.js +352 -0
  138. package/dist/domain/javascriptStaticAnalysis.js +5 -221
  139. package/dist/domain/javascriptStaticAnalysisBundler.js +209 -0
  140. package/dist/domain/javascriptStaticAnalysisCalls.js +180 -0
  141. package/dist/domain/javascriptStaticAnalysisHelpers.js +71 -2
  142. package/dist/domain/knownPageCoverage.js +21 -0
  143. package/dist/domain/managedApplicationGraph.js +14 -364
  144. package/dist/domain/managedApplicationGraphCoverage.js +170 -0
  145. package/dist/domain/managedApplicationGraphNodeCoverage.js +6 -0
  146. package/dist/domain/managedApplicationGraphNodes.js +336 -0
  147. package/dist/domain/managedArtifact.js +16 -8
  148. package/dist/domain/managedMemberComparison.js +37 -391
  149. package/dist/domain/managedMemberComparisonCoverage.js +72 -0
  150. package/dist/domain/managedMemberComparisonMatch.js +356 -0
  151. package/dist/domain/managedNativeVerification.js +27 -389
  152. package/dist/domain/managedNativeVerificationMatch.js +272 -0
  153. package/dist/domain/managedNativeVerificationSchemas.js +141 -0
  154. package/dist/domain/managedReconstruction.js +82 -69
  155. package/dist/domain/managedRuntimeCorrelation.js +12 -3
  156. package/dist/domain/nodeRuntimeCharacterization.js +66 -0
  157. package/dist/domain/permissionPolicy.js +2 -0
  158. package/dist/domain/reconstructionCoverage.js +387 -0
  159. package/dist/domain/reconstructionCoverageEvaluation.js +212 -0
  160. package/dist/domain/reconstructionVerificationSchemas.js +8 -0
  161. package/dist/domain/runtimeCharacterization.js +85 -0
  162. package/dist/domain/webBundleAnalyzer.js +3 -409
  163. package/dist/domain/webBundleAnalyzerAst.js +77 -0
  164. package/dist/domain/webBundleAnalyzerInspection.js +307 -0
  165. package/dist/domain/webBundleAnalyzerResult.js +120 -0
  166. package/dist/dotnet/ManagedArtifactInspector.js +50 -29
  167. package/dist/dotnet/ManagedMemberInspector.js +51 -785
  168. package/dist/dotnet/ManagedMemberInspectorCore.js +297 -0
  169. package/dist/dotnet/ManagedMemberInstructionDecoder.js +427 -0
  170. package/dist/dotnet/ManagedMemberRows.js +120 -0
  171. package/dist/dotnet/ManagedMetadataInventory.js +42 -306
  172. package/dist/dotnet/ManagedMetadataInventoryRows.js +292 -0
  173. package/dist/dotnet/ManagedMetadataLayout.js +48 -111
  174. package/dist/dotnet/ManagedMetadataRowSizes.js +54 -0
  175. package/dist/dotnet/ManagedMethodBodyReader.js +143 -0
  176. package/dist/dotnet/ManagedNativeBoundaryHelpers.js +311 -0
  177. package/dist/dotnet/ManagedNativeBoundaryInspector.js +102 -350
  178. package/dist/dotnet/ManagedPeReader.js +59 -28
  179. package/dist/generatedPackageMetadata.js +2 -2
  180. package/dist/ghidra/GhidraClient.js +76 -91
  181. package/dist/ghidra/GhidraClientStartup.js +29 -0
  182. package/dist/ghidra/GhidraClientWire.js +34 -0
  183. package/dist/ghidra/GhidraDefaults.js +1 -1
  184. package/dist/ghidra/GhidraDiagnostics.js +6 -1
  185. package/dist/ghidra/GhidraInstallation.js +20 -15
  186. package/dist/ghidra/GhidraLauncher.js +93 -29
  187. package/dist/ghidra/GhidraProvider.js +61 -161
  188. package/dist/ghidra/GhidraProviderCapabilities.js +173 -0
  189. package/dist/ghidra/GhidraSessionValues.js +2 -0
  190. package/dist/ghidra/GhidraSocketConnection.js +44 -7
  191. package/dist/ghidra/GhidraTargetSnapshot.js +30 -0
  192. package/dist/ghidra/GhidraTransport.js +71 -0
  193. package/dist/identity.js +3 -2
  194. package/dist/main/messages.js +5 -0
  195. package/dist/main/reload.js +49 -0
  196. package/dist/main/shutdown.js +25 -0
  197. package/dist/main/startup.js +14 -0
  198. package/dist/main/state.js +26 -0
  199. package/dist/main/transport.js +61 -0
  200. package/dist/main/types.js +1 -0
  201. package/dist/main.js +35 -146
  202. package/dist/process/PrivateRuntimeRoot.js +3 -2
  203. package/dist/process/ProcessOwnership.js +35 -0
  204. package/dist/process/ProviderProcess.js +2 -0
  205. package/dist/replay/JavaScriptReplayWorker.js +64 -50
  206. package/dist/replay/LinuxJavaScriptReplayRunner.js +173 -390
  207. package/dist/replay/LinuxJavaScriptReplaySandbox.js +140 -0
  208. package/dist/replay/ReplayOutcome.js +95 -0
  209. package/dist/replay/ReplayProcessLifecycle.js +71 -0
  210. package/dist/server/ProcessCaptureElicitation.js +157 -0
  211. package/dist/server/createServer.js +119 -54
  212. package/dist/server/registerApplicationTools/characterization.js +57 -0
  213. package/dist/server/registerApplicationTools/compareVersions.js +34 -0
  214. package/dist/server/registerApplicationTools/controlledReplay.js +38 -0
  215. package/dist/server/registerApplicationTools/coverage.js +84 -0
  216. package/dist/server/registerApplicationTools/helpers.js +42 -0
  217. package/dist/server/registerApplicationTools/traceFeature.js +26 -0
  218. package/dist/server/registerApplicationTools/types.js +1 -0
  219. package/dist/server/registerApplicationTools.js +10 -112
  220. package/dist/server/registerBrowserTools.js +59 -83
  221. package/dist/server/registerElectronTools.js +31 -46
  222. package/dist/server/registerEnhancedTools.js +34 -42
  223. package/dist/server/registerEvidenceResources.js +54 -0
  224. package/dist/server/registerInvestigationTools/helpers.js +74 -0
  225. package/dist/server/registerInvestigationTools/types.js +1 -0
  226. package/dist/server/registerInvestigationTools.js +83 -116
  227. package/dist/server/registerManagedWorkflowTools/compareManagedMembers.js +65 -0
  228. package/dist/server/registerManagedWorkflowTools/contract.js +8 -0
  229. package/dist/server/registerManagedWorkflowTools/evidence.js +43 -0
  230. package/dist/server/registerManagedWorkflowTools/importManagedReconstruction.js +39 -0
  231. package/dist/server/registerManagedWorkflowTools/planManagedRuntimeCorrelation.js +39 -0
  232. package/dist/server/registerManagedWorkflowTools/projectManagedApplicationGraph.js +52 -0
  233. package/dist/server/registerManagedWorkflowTools/types.js +1 -0
  234. package/dist/server/registerManagedWorkflowTools/verifyManagedNativeBoundaries.js +75 -0
  235. package/dist/server/registerManagedWorkflowTools.js +10 -274
  236. package/dist/server/registerOfficialTools.js +73 -59
  237. package/dist/server/registerSessionRecordTools.js +137 -0
  238. package/dist/server/registerSessionStatusTool.js +2 -1
  239. package/dist/server/registerSessionTools.js +60 -164
  240. package/dist/server/sessionAvailabilityPolicy.js +10 -0
  241. package/dist/server/toolRegistrationOptions.js +101 -3
  242. package/dist/server/toolResult.js +5 -0
  243. package/package.json +10 -11
  244. package/skills/{rea-analysis → reverse-engineer-anything}/SKILL.md +20 -7
  245. package/scripts/prepare-node-pty.mjs +0 -35
  246. package/scripts/rebuild-native.mjs +0 -22
@@ -1,17 +1,9 @@
1
- import { diffLines } from "diff";
2
- import { parseFunctionEvidence, } from "./functionDossierEvidence.js";
3
- import { commentProjection, combineCoverage, functionMatch, identityProjection, isAutoName, normalizeCfg, project, referenceKindProjection, referenceProjection, sorted, stringAndNameProjection, } from "./functionComparisonNormalization.js";
1
+ import { parseFunctionEvidence } from "./functionDossierEvidence.js";
2
+ import { functionMatch } from "./functionComparisonNormalization.js";
4
3
  import { functionComparisonResultSchema, } from "./functionComparisonSchemas.js";
5
- import { canonicalJson, dimensionResult, overallStatus, summarize, unresolvedDimension as buildUnresolvedDimension, } from "./functionComparisonResults.js";
4
+ import { overallStatus, summarize, canonicalJson, } from "./functionComparisonResults.js";
5
+ import { compareDimensions } from "./functionComparisonDimensions.js";
6
6
  export { functionComparisonInputSchema, functionComparisonResultSchema, } from "./functionComparisonSchemas.js";
7
- const unresolvedDimension = (...[dimension, status, links, leftCount, rightCount, limitations]) => buildUnresolvedDimension({
8
- dimension,
9
- status,
10
- links,
11
- leftCount,
12
- rightCount,
13
- limitations,
14
- });
15
7
  /** Compare two explicit function Evidence page sets without fuzzy matching. */
16
8
  export const compareFunctions = (leftInput, rightInput, offset, limit) => {
17
9
  const left = parseFunctionEvidence(leftInput);
@@ -52,156 +44,3 @@ export const compareFunctions = (leftInput, rightInput, offset, limit) => {
52
44
  ].sort((a, b) => a.localeCompare(b)),
53
45
  });
54
46
  };
55
- const compareDimensions = (left, right, links, providersDiffer) => [
56
- compareIdentity(left, right, links, providersDiffer),
57
- compareText("pseudocode", left.pseudocode.text, right.pseudocode.text, left.pseudocode.complete, right.pseudocode.complete, links, providersDiffer),
58
- compareText("assembly", left.collections.assembly.items.join("\n"), right.collections.assembly.items.join("\n"), left.collections.assembly.complete, right.collections.assembly.complete, links, providersDiffer, left.collections.assembly.truncated || right.collections.assembly.truncated, "Assembly is opaque provider text; relocation normalization is unavailable."),
59
- compareComments(left, right, links, providersDiffer),
60
- compareCalls(left, right, links, providersDiffer),
61
- compareReferences(left, right, links, providersDiffer),
62
- compareStringsAndNames(left, right, links, providersDiffer),
63
- compareCfg(left, right, links, providersDiffer),
64
- ];
65
- const compareText = (...[dimension, left, right, leftComplete, rightComplete, links, providersDiffer, explicitTruncation, limitation,]) => {
66
- const truncated = explicitTruncation ?? (!leftComplete || !rightComplete);
67
- if (!leftComplete || !rightComplete || providersDiffer)
68
- return unresolvedDimension(dimension, truncated ? "truncated" : "unknown", links, left.length, right.length, [
69
- ...(limitation === undefined ? [] : [limitation]),
70
- ...(providersDiffer
71
- ? ["Exact text comparison requires one provider identity."]
72
- : []),
73
- ]);
74
- const changes = diffLines(left, right, {
75
- timeout: 100,
76
- maxEditLength: 10_000,
77
- });
78
- if (changes === undefined)
79
- return unresolvedDimension(dimension, "unknown", links, left.length, right.length, ["Bounded line diff exceeded its time or edit-distance limit."]);
80
- const delta = changes.reduce((summary, change) => ({
81
- added_lines: summary.added_lines + (change.added ? (change.count ?? 0) : 0),
82
- removed_lines: summary.removed_lines + (change.removed ? (change.count ?? 0) : 0),
83
- hunks: summary.hunks + (change.added || change.removed ? 1 : 0),
84
- }), { added_lines: 0, removed_lines: 0, hunks: 0 });
85
- return dimensionResult({
86
- dimension,
87
- status: delta.hunks === 0 ? "unchanged" : "changed",
88
- left,
89
- right,
90
- links,
91
- providersDiffer,
92
- leftCount: [...left].length,
93
- rightCount: [...right].length,
94
- textDelta: delta,
95
- limitations: limitation === undefined ? [] : [limitation],
96
- });
97
- };
98
- const compareCollections = (...[dimension, left, right, leftCoverage, rightCoverage, links, providersDiffer,]) => {
99
- if (!leftCoverage.complete || !rightCoverage.complete)
100
- return unresolvedDimension(dimension, leftCoverage.truncated || rightCoverage.truncated
101
- ? "truncated"
102
- : "unknown", links, left.length, right.length, []);
103
- return compareValues(dimension, sorted(left), sorted(right), links, true, true, providersDiffer);
104
- };
105
- const compareComments = (left, right, links, providersDiffer) => {
106
- const leftValues = commentProjection(left);
107
- const rightValues = commentProjection(right);
108
- if (leftValues === null || rightValues === null)
109
- return unresolvedDimension("comments", "unknown", links, left.collections.comments.items.length, right.collections.comments.items.length, ["Comment locations could not be normalized relative to the function."]);
110
- return compareCollections("comments", leftValues, rightValues, left.collections.comments, right.collections.comments, links, providersDiffer);
111
- };
112
- const compareStringsAndNames = (left, right, links, providersDiffer) => {
113
- const leftValues = stringAndNameProjection(left);
114
- const rightValues = stringAndNameProjection(right);
115
- if (leftValues === null || rightValues === null)
116
- return unresolvedDimension("strings_names", "unknown", links, null, null, [
117
- "Reference source locations could not be normalized to function offsets.",
118
- ]);
119
- return compareCollections("strings_names", leftValues, rightValues, combineCoverage(left.collections.referenced_strings, left.collections.referenced_names), combineCoverage(right.collections.referenced_strings, right.collections.referenced_names), links, providersDiffer);
120
- };
121
- const compareReferences = (left, right, links, providersDiffer) => {
122
- const leftCoverage = combineCoverage(left.collections.incoming_references, left.collections.outgoing_references);
123
- const rightCoverage = combineCoverage(right.collections.incoming_references, right.collections.outgoing_references);
124
- if (!leftCoverage.complete || !rightCoverage.complete)
125
- return unresolvedDimension("references", leftCoverage.truncated || rightCoverage.truncated
126
- ? "truncated"
127
- : "unknown", links, leftCoverage.items.length, rightCoverage.items.length, []);
128
- const leftProjection = referenceProjection(left);
129
- const rightProjection = referenceProjection(right);
130
- if (leftProjection.length === 0 && rightProjection.length === 0)
131
- return compareValues("references", leftProjection, rightProjection, links, true, true, providersDiffer);
132
- if (canonicalJson(leftProjection) !== canonicalJson(rightProjection))
133
- return dimensionResult({
134
- dimension: "references",
135
- status: "changed",
136
- left: leftProjection,
137
- right: rightProjection,
138
- links,
139
- providersDiffer,
140
- leftCount: leftProjection.length,
141
- rightCount: rightProjection.length,
142
- textDelta: null,
143
- limitations: [
144
- "Reference endpoints differ independently of provider reference-kind metadata.",
145
- ],
146
- });
147
- const leftKinds = referenceKindProjection(left);
148
- const rightKinds = referenceKindProjection(right);
149
- if (leftKinds !== null && rightKinds !== null)
150
- return compareValues("references", leftKinds, rightKinds, links, true, true, providersDiffer);
151
- return unresolvedDimension("references", "unknown", links, leftProjection.length, rightProjection.length, [
152
- "At least one provider did not expose reference kinds, so equal endpoints do not prove equal edge semantics.",
153
- ]);
154
- };
155
- const compareIdentity = (left, right, links, providersDiffer) => {
156
- if (isAutoName(left.procedure.name) || isAutoName(right.procedure.name))
157
- return unresolvedDimension("identity", "unknown", links, null, null, [
158
- "Address-derived function names are not stable cross-version identity.",
159
- ]);
160
- return compareValues("identity", identityProjection(left), identityProjection(right), links, true, true, providersDiffer);
161
- };
162
- const compareCalls = (left, right, links, providersDiffer) => {
163
- const leftValues = callProjection(left);
164
- const rightValues = callProjection(right);
165
- if ([...leftValues, ...rightValues].some(({ name }) => isAutoName(name)))
166
- return unresolvedDimension("calls", "unknown", links, leftValues.length, rightValues.length, ["Address-derived callee or caller names cannot be matched safely."]);
167
- return compareCollections("calls", leftValues, rightValues, combineCoverage(left.collections.callers, left.collections.callees), combineCoverage(right.collections.callers, right.collections.callees), links, providersDiffer);
168
- };
169
- const callProjection = (snapshot) => [
170
- ...project(snapshot.collections.callers, ({ name }) => ({
171
- direction: "in",
172
- name,
173
- })),
174
- ...project(snapshot.collections.callees, ({ name }) => ({
175
- direction: "out",
176
- name,
177
- })),
178
- ];
179
- const compareCfg = (left, right, links, providersDiffer) => {
180
- const leftBlocks = left.collections.basic_blocks;
181
- const rightBlocks = right.collections.basic_blocks;
182
- if (!leftBlocks.complete || !rightBlocks.complete)
183
- return unresolvedDimension("cfg", leftBlocks.truncated || rightBlocks.truncated ? "truncated" : "unknown", links, leftBlocks.items.length, rightBlocks.items.length, []);
184
- const leftGraph = normalizeCfg(leftBlocks.items);
185
- const rightGraph = normalizeCfg(rightBlocks.items);
186
- if (leftGraph === null || rightGraph === null)
187
- return unresolvedDimension("cfg", "unknown", links, leftBlocks.items.length, rightBlocks.items.length, ["CFG addresses could not be normalized to local block indices."]);
188
- return compareValues("cfg", leftGraph, rightGraph, links, true, true, providersDiffer);
189
- };
190
- const compareValues = (...[dimension, left, right, links, leftComplete, rightComplete, providersDiffer,]) => {
191
- if (!leftComplete || !rightComplete)
192
- return unresolvedDimension(dimension, "unknown", links, null, null, []);
193
- const leftJson = canonicalJson(left);
194
- const rightJson = canonicalJson(right);
195
- return dimensionResult({
196
- dimension,
197
- status: leftJson === rightJson ? "unchanged" : "changed",
198
- left,
199
- right,
200
- links,
201
- providersDiffer,
202
- leftCount: Array.isArray(left) ? left.length : null,
203
- rightCount: Array.isArray(right) ? right.length : null,
204
- textDelta: null,
205
- limitations: [],
206
- });
207
- };
@@ -0,0 +1,164 @@
1
+ import { diffLines } from "diff";
2
+ import { commentProjection, combineCoverage, identityProjection, isAutoName, normalizeCfg, project, referenceKindProjection, referenceProjection, sorted, stringAndNameProjection, } from "./functionComparisonNormalization.js";
3
+ import { canonicalJson, dimensionResult, unresolvedDimension as buildUnresolvedDimension, } from "./functionComparisonResults.js";
4
+ const unresolvedDimension = (...[dimension, status, links, leftCount, rightCount, limitations]) => buildUnresolvedDimension({
5
+ dimension,
6
+ status,
7
+ links,
8
+ leftCount,
9
+ rightCount,
10
+ limitations,
11
+ });
12
+ export const compareDimensions = (left, right, links, providersDiffer) => [
13
+ compareIdentity(left, right, links, providersDiffer),
14
+ compareText("pseudocode", left.pseudocode.text, right.pseudocode.text, left.pseudocode.complete, right.pseudocode.complete, links, providersDiffer),
15
+ compareText("assembly", left.collections.assembly.items.join("\n"), right.collections.assembly.items.join("\n"), left.collections.assembly.complete, right.collections.assembly.complete, links, providersDiffer, left.collections.assembly.truncated || right.collections.assembly.truncated, "Assembly is opaque provider text; relocation normalization is unavailable."),
16
+ compareComments(left, right, links, providersDiffer),
17
+ compareCalls(left, right, links, providersDiffer),
18
+ compareReferences(left, right, links, providersDiffer),
19
+ compareStringsAndNames(left, right, links, providersDiffer),
20
+ compareCfg(left, right, links, providersDiffer),
21
+ ];
22
+ const compareText = (...[dimension, left, right, leftComplete, rightComplete, links, providersDiffer, explicitTruncation, limitation,]) => {
23
+ const truncated = explicitTruncation ?? (!leftComplete || !rightComplete);
24
+ if (!leftComplete || !rightComplete || providersDiffer)
25
+ return unresolvedDimension(dimension, truncated ? "truncated" : "unknown", links, left.length, right.length, [
26
+ ...(limitation === undefined ? [] : [limitation]),
27
+ ...(providersDiffer
28
+ ? ["Exact text comparison requires one provider identity."]
29
+ : []),
30
+ ]);
31
+ const changes = diffLines(left, right, {
32
+ timeout: 100,
33
+ maxEditLength: 10_000,
34
+ });
35
+ if (changes === undefined)
36
+ return unresolvedDimension(dimension, "unknown", links, left.length, right.length, ["Bounded line diff exceeded its time or edit-distance limit."]);
37
+ const delta = changes.reduce((summary, change) => ({
38
+ added_lines: summary.added_lines + (change.added ? (change.count ?? 0) : 0),
39
+ removed_lines: summary.removed_lines + (change.removed ? (change.count ?? 0) : 0),
40
+ hunks: summary.hunks + (change.added || change.removed ? 1 : 0),
41
+ }), { added_lines: 0, removed_lines: 0, hunks: 0 });
42
+ return dimensionResult({
43
+ dimension,
44
+ status: delta.hunks === 0 ? "unchanged" : "changed",
45
+ left,
46
+ right,
47
+ links,
48
+ providersDiffer,
49
+ leftCount: [...left].length,
50
+ rightCount: [...right].length,
51
+ textDelta: delta,
52
+ limitations: limitation === undefined ? [] : [limitation],
53
+ });
54
+ };
55
+ const compareCollections = (...[dimension, left, right, leftCoverage, rightCoverage, links, providersDiffer,]) => {
56
+ if (!leftCoverage.complete || !rightCoverage.complete)
57
+ return unresolvedDimension(dimension, leftCoverage.truncated || rightCoverage.truncated
58
+ ? "truncated"
59
+ : "unknown", links, left.length, right.length, []);
60
+ return compareValues(dimension, sorted(left), sorted(right), links, true, true, providersDiffer);
61
+ };
62
+ const compareComments = (left, right, links, providersDiffer) => {
63
+ const leftValues = commentProjection(left);
64
+ const rightValues = commentProjection(right);
65
+ if (leftValues === null || rightValues === null)
66
+ return unresolvedDimension("comments", "unknown", links, left.collections.comments.items.length, right.collections.comments.items.length, ["Comment locations could not be normalized relative to the function."]);
67
+ return compareCollections("comments", leftValues, rightValues, left.collections.comments, right.collections.comments, links, providersDiffer);
68
+ };
69
+ const compareStringsAndNames = (left, right, links, providersDiffer) => {
70
+ const leftValues = stringAndNameProjection(left);
71
+ const rightValues = stringAndNameProjection(right);
72
+ if (leftValues === null || rightValues === null)
73
+ return unresolvedDimension("strings_names", "unknown", links, null, null, [
74
+ "Reference source locations could not be normalized to function offsets.",
75
+ ]);
76
+ return compareCollections("strings_names", leftValues, rightValues, combineCoverage(left.collections.referenced_strings, left.collections.referenced_names), combineCoverage(right.collections.referenced_strings, right.collections.referenced_names), links, providersDiffer);
77
+ };
78
+ const compareReferences = (left, right, links, providersDiffer) => {
79
+ const leftCoverage = combineCoverage(left.collections.incoming_references, left.collections.outgoing_references);
80
+ const rightCoverage = combineCoverage(right.collections.incoming_references, right.collections.outgoing_references);
81
+ if (!leftCoverage.complete || !rightCoverage.complete)
82
+ return unresolvedDimension("references", leftCoverage.truncated || rightCoverage.truncated
83
+ ? "truncated"
84
+ : "unknown", links, leftCoverage.items.length, rightCoverage.items.length, []);
85
+ const leftProjection = referenceProjection(left);
86
+ const rightProjection = referenceProjection(right);
87
+ if (leftProjection.length === 0 && rightProjection.length === 0)
88
+ return compareValues("references", leftProjection, rightProjection, links, true, true, providersDiffer);
89
+ if (canonicalJson(leftProjection) !== canonicalJson(rightProjection))
90
+ return dimensionResult({
91
+ dimension: "references",
92
+ status: "changed",
93
+ left: leftProjection,
94
+ right: rightProjection,
95
+ links,
96
+ providersDiffer,
97
+ leftCount: leftProjection.length,
98
+ rightCount: rightProjection.length,
99
+ textDelta: null,
100
+ limitations: [
101
+ "Reference endpoints differ independently of provider reference-kind metadata.",
102
+ ],
103
+ });
104
+ const leftKinds = referenceKindProjection(left);
105
+ const rightKinds = referenceKindProjection(right);
106
+ if (leftKinds !== null && rightKinds !== null)
107
+ return compareValues("references", leftKinds, rightKinds, links, true, true, providersDiffer);
108
+ return unresolvedDimension("references", "unknown", links, leftProjection.length, rightProjection.length, [
109
+ "At least one provider did not expose reference kinds, so equal endpoints do not prove equal edge semantics.",
110
+ ]);
111
+ };
112
+ const compareIdentity = (left, right, links, providersDiffer) => {
113
+ if (isAutoName(left.procedure.name) || isAutoName(right.procedure.name))
114
+ return unresolvedDimension("identity", "unknown", links, null, null, [
115
+ "Address-derived function names are not stable cross-version identity.",
116
+ ]);
117
+ return compareValues("identity", identityProjection(left), identityProjection(right), links, true, true, providersDiffer);
118
+ };
119
+ const compareCalls = (left, right, links, providersDiffer) => {
120
+ const leftValues = callProjection(left);
121
+ const rightValues = callProjection(right);
122
+ if ([...leftValues, ...rightValues].some(({ name }) => isAutoName(name)))
123
+ return unresolvedDimension("calls", "unknown", links, leftValues.length, rightValues.length, ["Address-derived callee or caller names cannot be matched safely."]);
124
+ return compareCollections("calls", leftValues, rightValues, combineCoverage(left.collections.callers, left.collections.callees), combineCoverage(right.collections.callers, right.collections.callees), links, providersDiffer);
125
+ };
126
+ const callProjection = (snapshot) => [
127
+ ...project(snapshot.collections.callers, ({ name }) => ({
128
+ direction: "in",
129
+ name,
130
+ })),
131
+ ...project(snapshot.collections.callees, ({ name }) => ({
132
+ direction: "out",
133
+ name,
134
+ })),
135
+ ];
136
+ const compareCfg = (left, right, links, providersDiffer) => {
137
+ const leftBlocks = left.collections.basic_blocks;
138
+ const rightBlocks = right.collections.basic_blocks;
139
+ if (!leftBlocks.complete || !rightBlocks.complete)
140
+ return unresolvedDimension("cfg", leftBlocks.truncated || rightBlocks.truncated ? "truncated" : "unknown", links, leftBlocks.items.length, rightBlocks.items.length, []);
141
+ const leftGraph = normalizeCfg(leftBlocks.items);
142
+ const rightGraph = normalizeCfg(rightBlocks.items);
143
+ if (leftGraph === null || rightGraph === null)
144
+ return unresolvedDimension("cfg", "unknown", links, leftBlocks.items.length, rightBlocks.items.length, ["CFG addresses could not be normalized to local block indices."]);
145
+ return compareValues("cfg", leftGraph, rightGraph, links, true, true, providersDiffer);
146
+ };
147
+ const compareValues = (...[dimension, left, right, links, leftComplete, rightComplete, providersDiffer,]) => {
148
+ if (!leftComplete || !rightComplete)
149
+ return unresolvedDimension(dimension, "unknown", links, null, null, []);
150
+ const leftJson = canonicalJson(left);
151
+ const rightJson = canonicalJson(right);
152
+ return dimensionResult({
153
+ dimension,
154
+ status: leftJson === rightJson ? "unchanged" : "changed",
155
+ left,
156
+ right,
157
+ links,
158
+ providersDiffer,
159
+ leftCount: Array.isArray(left) ? left.length : null,
160
+ rightCount: Array.isArray(right) ? right.length : null,
161
+ textDelta: null,
162
+ limitations: [],
163
+ });
164
+ };
@@ -145,20 +145,32 @@ const relationshipSignature = (node, side, context) => {
145
145
  const signatures = graph.edges.flatMap((edge) => {
146
146
  if (edge.source_node_id === node.node_id)
147
147
  return [
148
- edgeSignature("out", edge.relation, edge.target_node_id, paired, nodeById),
148
+ edgeSignature({
149
+ direction: "out",
150
+ relation: edge.relation,
151
+ neighborId: edge.target_node_id,
152
+ paired,
153
+ nodeById,
154
+ }),
149
155
  ];
150
156
  if (edge.target_node_id === node.node_id)
151
157
  return [
152
- edgeSignature("in", edge.relation, edge.source_node_id, paired, nodeById),
158
+ edgeSignature({
159
+ direction: "in",
160
+ relation: edge.relation,
161
+ neighborId: edge.source_node_id,
162
+ paired,
163
+ nodeById,
164
+ }),
153
165
  ];
154
166
  return [];
155
167
  });
156
168
  return canonical(signatures.sort(compareCodePoints));
157
169
  };
158
- const edgeSignature = (direction, relation, neighborId, paired, nodeById) => {
159
- const mapped = paired.get(neighborId);
160
- const neighbor = nodeById.get(neighborId);
161
- return `${direction}\0${relation}\0${mapped ?? `unmatched:${neighbor?.kind ?? "unknown"}`}`;
170
+ const edgeSignature = (context) => {
171
+ const mapped = context.paired.get(context.neighborId);
172
+ const neighbor = context.nodeById.get(context.neighborId);
173
+ return `${context.direction}\0${context.relation}\0${mapped ?? `unmatched:${neighbor?.kind ?? "unknown"}`}`;
162
174
  };
163
175
  const itemEvidenceLinks = (left, right, context) => [
164
176
  ...new Set([
@@ -23,7 +23,7 @@ export const matchJavaScriptApplicationVersions = (leftNodes, rightNodes) => {
23
23
  const rightCandidates = new Map();
24
24
  const pairs = [];
25
25
  for (const tier of matchTiers())
26
- applyTier(left, right, pairs, leftCandidates, rightCandidates, tier);
26
+ applyTier({ left, right, pairs, leftCandidates, rightCandidates, tier });
27
27
  const remainingLeft = sortedNodes(left.values());
28
28
  const remainingRight = sortedNodes(right.values());
29
29
  const leftIds = new Set(remainingLeft.map(({ node_id: id }) => id));
@@ -36,24 +36,24 @@ export const matchJavaScriptApplicationVersions = (leftNodes, rightNodes) => {
36
36
  candidatesForRight: filteredCandidates(rightCandidates, leftIds),
37
37
  };
38
38
  };
39
- const applyTier = (left, right, pairs, leftCandidates, rightCandidates, tier) => {
40
- const leftByKey = nodesByKey(left.values(), tier.key);
41
- const rightByKey = nodesByKey(right.values(), tier.key);
39
+ const applyTier = (context) => {
40
+ const leftByKey = nodesByKey(context.left.values(), context.tier.key);
41
+ const rightByKey = nodesByKey(context.right.values(), context.tier.key);
42
42
  for (const key of [...leftByKey.keys()].sort(compareCodePoints)) {
43
43
  const leftGroup = leftByKey.get(key) ?? [];
44
44
  const rightGroup = rightByKey.get(key) ?? [];
45
45
  if (rightGroup.length === 0)
46
46
  continue;
47
- recordCandidates(leftGroup, rightGroup, leftCandidates, rightCandidates);
47
+ recordCandidates(leftGroup, rightGroup, context.leftCandidates, context.rightCandidates);
48
48
  if (leftGroup.length !== 1 || rightGroup.length !== 1)
49
49
  continue;
50
50
  const leftNode = leftGroup[0];
51
51
  const rightNode = rightGroup[0];
52
52
  if (leftNode === undefined || rightNode === undefined)
53
53
  continue;
54
- pairs.push({ left: leftNode, right: rightNode, ...tier });
55
- left.delete(leftNode.node_id);
56
- right.delete(rightNode.node_id);
54
+ context.pairs.push({ left: leftNode, right: rightNode, ...context.tier });
55
+ context.left.delete(leftNode.node_id);
56
+ context.right.delete(rightNode.node_id);
57
57
  }
58
58
  };
59
59
  const nodesByKey = (nodes, keyFor) => {
@@ -0,0 +1,97 @@
1
+ import { createHash } from "node:crypto";
2
+ import { z } from "zod";
3
+ const digestSchema = z.string().regex(/^[a-f0-9]{64}$/u);
4
+ export const javascriptExportInstrumentationInputSchema = z.strictObject({
5
+ artifact_path: z.string().min(1).max(4_096),
6
+ artifact_sha256: digestSchema,
7
+ selection: z.strictObject({
8
+ byte_start: z.number().int().min(0),
9
+ byte_end: z.number().int().min(1),
10
+ selected_sha256: digestSchema,
11
+ export_name: z.string().regex(/^[A-Za-z_$][A-Za-z0-9_$]{0,199}$/u),
12
+ }),
13
+ });
14
+ export const javascriptExportTransformationManifestSchema = z.strictObject({
15
+ schema_version: z.literal(1),
16
+ kind: z.literal("commonjs-factory-export-v1"),
17
+ artifact_path: z.string().min(1).max(4_096),
18
+ original_sha256: digestSchema,
19
+ original_byte_length: z.number().int().min(0),
20
+ instrumented_sha256: digestSchema,
21
+ instrumented_byte_length: z.number().int().min(0),
22
+ selected_range: z.strictObject({
23
+ byte_start: z.number().int().min(0),
24
+ byte_end: z.number().int().min(1),
25
+ selected_sha256: digestSchema,
26
+ }),
27
+ inserted_ranges: z
28
+ .array(z.strictObject({
29
+ byte_start: z.number().int().min(0),
30
+ byte_end: z.number().int().min(1),
31
+ inserted_sha256: digestSchema,
32
+ purpose: z.enum(["factory-prefix", "export-binding", "factory-suffix"]),
33
+ }))
34
+ .length(3),
35
+ output_format: z.literal("commonjs-factory"),
36
+ source_map: z.literal(null),
37
+ });
38
+ /** Instrument one exact UTF-8 expression range without mutating source bytes. */
39
+ export const instrumentJavaScriptExport = (source, rawInput) => {
40
+ const input = javascriptExportInstrumentationInputSchema.parse(rawInput);
41
+ if (sha256(source) !== input.artifact_sha256)
42
+ throw new TypeError("JavaScript instrumentation artifact digest mismatch");
43
+ const { byte_start: start, byte_end: end } = input.selection;
44
+ if (end <= start || end > source.byteLength)
45
+ throw new TypeError("JavaScript instrumentation selection is out of bounds");
46
+ const selected = source.slice(start, end);
47
+ if (sha256(selected) !== input.selection.selected_sha256)
48
+ throw new TypeError("JavaScript instrumentation selection digest mismatch");
49
+ decodeUtf8(source);
50
+ decodeUtf8(selected);
51
+ const prefix = encode("function(module, exports, require) {\n");
52
+ const binding = encode(`\nmodule.exports[${JSON.stringify(input.selection.export_name)}] = (${decodeUtf8(selected)});\n`);
53
+ const suffix = encode("}\n");
54
+ const bytes = concatenate([prefix, source, binding, suffix]);
55
+ const bindingStart = prefix.byteLength + source.byteLength;
56
+ const suffixStart = bindingStart + binding.byteLength;
57
+ const manifest = javascriptExportTransformationManifestSchema.parse({
58
+ schema_version: 1,
59
+ kind: "commonjs-factory-export-v1",
60
+ artifact_path: input.artifact_path,
61
+ original_sha256: input.artifact_sha256,
62
+ original_byte_length: source.byteLength,
63
+ instrumented_sha256: sha256(bytes),
64
+ instrumented_byte_length: bytes.byteLength,
65
+ selected_range: {
66
+ byte_start: start,
67
+ byte_end: end,
68
+ selected_sha256: input.selection.selected_sha256,
69
+ },
70
+ inserted_ranges: [
71
+ range(0, prefix, "factory-prefix"),
72
+ range(bindingStart, binding, "export-binding"),
73
+ range(suffixStart, suffix, "factory-suffix"),
74
+ ],
75
+ output_format: "commonjs-factory",
76
+ source_map: null,
77
+ });
78
+ return { bytes, manifest };
79
+ };
80
+ const range = (start, bytes, purpose) => ({
81
+ byte_start: start,
82
+ byte_end: start + bytes.byteLength,
83
+ inserted_sha256: sha256(bytes),
84
+ purpose,
85
+ });
86
+ const encode = (value) => new TextEncoder().encode(value);
87
+ const decodeUtf8 = (value) => new TextDecoder("utf-8", { fatal: true }).decode(value);
88
+ const concatenate = (values) => {
89
+ const output = new Uint8Array(values.reduce((total, value) => total + value.byteLength, 0));
90
+ let offset = 0;
91
+ for (const value of values) {
92
+ output.set(value, offset);
93
+ offset += value.byteLength;
94
+ }
95
+ return output;
96
+ };
97
+ const sha256 = (value) => createHash("sha256").update(value).digest("hex");
@@ -18,11 +18,23 @@ export const traverseApplicationFeature = (graph, seedNodeIds, direction, limits
18
18
  const depth = depths.get(current) ?? 0;
19
19
  for (const entry of adjacency.get(current) ?? []) {
20
20
  if (depth >= limits.max_depth) {
21
- recordOmission(entry, current, omittedNodes, omittedEdges, frontier);
21
+ recordOmission({
22
+ entry,
23
+ current,
24
+ omittedNodes,
25
+ omittedEdges,
26
+ frontier,
27
+ });
22
28
  continue;
23
29
  }
24
30
  if (!visited.has(entry.nextNodeId) && visited.size >= limits.max_nodes) {
25
- recordOmission(entry, current, omittedNodes, omittedEdges, frontier);
31
+ recordOmission({
32
+ entry,
33
+ current,
34
+ omittedNodes,
35
+ omittedEdges,
36
+ frontier,
37
+ });
26
38
  continue;
27
39
  }
28
40
  if (!retainedEdges.has(entry.edge.edge_id) &&
@@ -80,9 +92,9 @@ const buildAdjacency = (edges, direction) => {
80
92
  const addAdjacency = (adjacency, nodeId, entry) => {
81
93
  adjacency.set(nodeId, [...(adjacency.get(nodeId) ?? []), entry]);
82
94
  };
83
- const recordOmission = (entry, current, omittedNodes, omittedEdges, frontier) => {
84
- omittedNodes.add(entry.nextNodeId);
85
- omittedEdges.add(entry.edge.edge_id);
86
- frontier.add(current);
87
- frontier.add(entry.nextNodeId);
95
+ const recordOmission = (context) => {
96
+ context.omittedNodes.add(context.entry.nextNodeId);
97
+ context.omittedEdges.add(context.entry.edge.edge_id);
98
+ context.frontier.add(context.current);
99
+ context.frontier.add(context.entry.nextNodeId);
88
100
  };
@@ -61,7 +61,8 @@ const candidatesForLayer = (layer) => {
61
61
  const candidateCategory = (node) => {
62
62
  if (node.kind === "electron-renderer")
63
63
  return "renderer";
64
- if (node.kind === "javascript-asset" || node.kind === "javascript-module")
64
+ if (node.kind === "javascript-asset" ||
65
+ (node.kind === "javascript-module" && !isStaticSourceModule(node)))
65
66
  return "javascript";
66
67
  if (node.kind === "worker")
67
68
  return "worker";
@@ -69,6 +70,7 @@ const candidateCategory = (node) => {
69
70
  return "service-worker";
70
71
  return null;
71
72
  };
73
+ const isStaticSourceModule = (node) => node.observations.some(({ properties }) => properties.semantic_role === "source-module");
72
74
  const indexStaticPaths = (graph) => {
73
75
  const nodes = new Map(graph.nodes.map((candidate) => [candidate.node_id, candidate]));
74
76
  const relatedBySource = new Map();