weavatrix 0.3.13 → 1.0.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 (303) hide show
  1. package/LICENSE +1 -0
  2. package/README.md +231 -199
  3. package/bin/native/darwin-arm64/weavatrix +0 -0
  4. package/bin/native/darwin-x64/weavatrix +0 -0
  5. package/bin/native/linux-arm64/weavatrix +0 -0
  6. package/bin/native/linux-x64/weavatrix +0 -0
  7. package/bin/native/win32-arm64/weavatrix.exe +0 -0
  8. package/bin/native/win32-x64/weavatrix.exe +0 -0
  9. package/bin/resolve-binary.mjs +73 -0
  10. package/bin/run-native.mjs +26 -0
  11. package/bin/weavatrix-mcp.mjs +7 -6
  12. package/bin/weavatrix.mjs +5 -0
  13. package/package.json +8 -65
  14. package/skill/SKILL.md +62 -374
  15. package/skill/agents/openai.yaml +4 -0
  16. package/skill/references/tool-routing.md +45 -0
  17. package/SECURITY.md +0 -101
  18. package/docs/adr/0001-v0.3-offline-online-split.md +0 -60
  19. package/docs/agent-task-benchmark.md +0 -58
  20. package/docs/releases/v0.2.19.md +0 -33
  21. package/docs/releases/v0.3.0.md +0 -43
  22. package/docs/releases/v0.3.1.md +0 -30
  23. package/docs/releases/v0.3.10.md +0 -76
  24. package/docs/releases/v0.3.11.md +0 -55
  25. package/docs/releases/v0.3.12.md +0 -44
  26. package/docs/releases/v0.3.13.md +0 -34
  27. package/docs/releases/v0.3.2.md +0 -15
  28. package/docs/releases/v0.3.3.md +0 -25
  29. package/docs/releases/v0.3.4.md +0 -40
  30. package/docs/releases/v0.3.5.md +0 -50
  31. package/docs/releases/v0.3.6.md +0 -44
  32. package/docs/releases/v0.3.7.md +0 -37
  33. package/docs/releases/v0.3.8.md +0 -21
  34. package/docs/releases/v0.3.9.md +0 -36
  35. package/scripts/run-agent-task-benchmark.mjs +0 -118
  36. package/src/analysis/allowed-test-runner.js +0 -70
  37. package/src/analysis/architecture/contract-graph.js +0 -119
  38. package/src/analysis/architecture/contract-schema.js +0 -110
  39. package/src/analysis/architecture/contract-storage.js +0 -35
  40. package/src/analysis/architecture/contract-verification.js +0 -168
  41. package/src/analysis/architecture-contract.js +0 -16
  42. package/src/analysis/audit-debt.js +0 -94
  43. package/src/analysis/audit-extensions.js +0 -60
  44. package/src/analysis/cargo-dependency-evidence.js +0 -108
  45. package/src/analysis/cargo-manifests.js +0 -81
  46. package/src/analysis/change-classification/diff-parser.js +0 -103
  47. package/src/analysis/change-classification/options.js +0 -40
  48. package/src/analysis/change-classification/symbol-classifier.js +0 -177
  49. package/src/analysis/change-classification.js +0 -188
  50. package/src/analysis/coverage-reports.js +0 -235
  51. package/src/analysis/cycle-route.js +0 -14
  52. package/src/analysis/data-flow-evidence.js +0 -114
  53. package/src/analysis/dead-check.js +0 -300
  54. package/src/analysis/dead-code-review/candidates.js +0 -194
  55. package/src/analysis/dead-code-review/policy.js +0 -23
  56. package/src/analysis/dead-code-review.js +0 -119
  57. package/src/analysis/dep-check-ecosystems.js +0 -205
  58. package/src/analysis/dep-check.js +0 -290
  59. package/src/analysis/dep-rules.js +0 -10
  60. package/src/analysis/dependency/conventions.js +0 -16
  61. package/src/analysis/dependency/scoped-dependencies.js +0 -51
  62. package/src/analysis/dependency/source-references.js +0 -21
  63. package/src/analysis/duplicate-groups.js +0 -104
  64. package/src/analysis/duplicates-worker.js +0 -11
  65. package/src/analysis/duplicates.compute.js +0 -269
  66. package/src/analysis/duplicates.js +0 -15
  67. package/src/analysis/duplicates.run.js +0 -52
  68. package/src/analysis/duplicates.tokenize.js +0 -199
  69. package/src/analysis/endpoints/common.js +0 -62
  70. package/src/analysis/endpoints/extract.js +0 -107
  71. package/src/analysis/endpoints/inventory.js +0 -84
  72. package/src/analysis/endpoints/mounts.js +0 -129
  73. package/src/analysis/endpoints-java.js +0 -260
  74. package/src/analysis/endpoints-rust.js +0 -124
  75. package/src/analysis/endpoints.js +0 -3
  76. package/src/analysis/findings.js +0 -70
  77. package/src/analysis/git-history/analytics.js +0 -177
  78. package/src/analysis/git-history/collector.js +0 -67
  79. package/src/analysis/git-history/options.js +0 -66
  80. package/src/analysis/git-history.js +0 -143
  81. package/src/analysis/git-ref-graph.js +0 -76
  82. package/src/analysis/go-dependency-evidence.js +0 -62
  83. package/src/analysis/graph-analysis.aggregate.js +0 -295
  84. package/src/analysis/graph-analysis.edges.js +0 -24
  85. package/src/analysis/graph-analysis.js +0 -7
  86. package/src/analysis/graph-analysis.refs.js +0 -154
  87. package/src/analysis/graph-analysis.summaries.js +0 -65
  88. package/src/analysis/health-capabilities.js +0 -83
  89. package/src/analysis/hot-path-review.js +0 -266
  90. package/src/analysis/http-contract-wrappers.js +0 -241
  91. package/src/analysis/http-contracts/analysis.js +0 -179
  92. package/src/analysis/http-contracts/client-call-detection.js +0 -82
  93. package/src/analysis/http-contracts/client-call-parser.js +0 -273
  94. package/src/analysis/http-contracts/graph-context.js +0 -143
  95. package/src/analysis/http-contracts/matching.js +0 -61
  96. package/src/analysis/http-contracts/shared.js +0 -85
  97. package/src/analysis/http-contracts.js +0 -6
  98. package/src/analysis/internal-audit/dependency-health.js +0 -113
  99. package/src/analysis/internal-audit/python-manifests.js +0 -78
  100. package/src/analysis/internal-audit/repo-files.js +0 -50
  101. package/src/analysis/internal-audit/supply-chain.js +0 -149
  102. package/src/analysis/internal-audit.collect.js +0 -213
  103. package/src/analysis/internal-audit.js +0 -9
  104. package/src/analysis/internal-audit.reach.js +0 -173
  105. package/src/analysis/internal-audit.run.js +0 -256
  106. package/src/analysis/java-source.js +0 -36
  107. package/src/analysis/jvm-artifact-index.js +0 -87
  108. package/src/analysis/jvm-dependency-evidence.js +0 -138
  109. package/src/analysis/jvm-manifests.js +0 -114
  110. package/src/analysis/manifests.js +0 -123
  111. package/src/analysis/package-reachability.js +0 -39
  112. package/src/analysis/source-complexity.ast.js +0 -97
  113. package/src/analysis/source-complexity.constants.js +0 -55
  114. package/src/analysis/source-complexity.js +0 -14
  115. package/src/analysis/source-complexity.report.js +0 -81
  116. package/src/analysis/source-complexity.walk.js +0 -228
  117. package/src/analysis/source-correctness/retry-patterns.js +0 -93
  118. package/src/analysis/source-correctness.js +0 -222
  119. package/src/analysis/static-test-reachability.js +0 -150
  120. package/src/analysis/structure/dependency-graph.js +0 -151
  121. package/src/analysis/structure/findings.js +0 -162
  122. package/src/analysis/task-retrieval.js +0 -107
  123. package/src/analysis/transport-contracts.js +0 -224
  124. package/src/analysis/transport-runtime-evidence.js +0 -155
  125. package/src/analysis-kit.mjs +0 -17
  126. package/src/bounds.js +0 -4
  127. package/src/build-graph.js +0 -209
  128. package/src/child-env.js +0 -7
  129. package/src/config.js +0 -5
  130. package/src/extension/local-services.mjs +0 -75
  131. package/src/git-exec.js +0 -67
  132. package/src/graph/build-worker.js +0 -49
  133. package/src/graph/builder/go-receiver-resolution.js +0 -112
  134. package/src/graph/builder/js/dynamic-import.js +0 -26
  135. package/src/graph/builder/js/queries.js +0 -48
  136. package/src/graph/builder/lang-csharp.js +0 -40
  137. package/src/graph/builder/lang-css.js +0 -29
  138. package/src/graph/builder/lang-go.js +0 -140
  139. package/src/graph/builder/lang-html.js +0 -29
  140. package/src/graph/builder/lang-java-calls.js +0 -142
  141. package/src/graph/builder/lang-java.js +0 -215
  142. package/src/graph/builder/lang-js.js +0 -291
  143. package/src/graph/builder/lang-python.js +0 -235
  144. package/src/graph/builder/lang-rust-calls.js +0 -63
  145. package/src/graph/builder/lang-rust.js +0 -299
  146. package/src/graph/builder/lang-solidity.js +0 -126
  147. package/src/graph/builder/lang-sql.js +0 -263
  148. package/src/graph/builder/pass2-resolution.js +0 -81
  149. package/src/graph/builder/spec-pkg.js +0 -87
  150. package/src/graph/community.js +0 -27
  151. package/src/graph/edge-provenance.js +0 -31
  152. package/src/graph/file-lock.js +0 -69
  153. package/src/graph/freshness-probe.js +0 -142
  154. package/src/graph/graph-filter.js +0 -63
  155. package/src/graph/incremental-refresh.js +0 -235
  156. package/src/graph/internal-builder.barrels.js +0 -235
  157. package/src/graph/internal-builder.build.js +0 -300
  158. package/src/graph/internal-builder.java.js +0 -33
  159. package/src/graph/internal-builder.js +0 -12
  160. package/src/graph/internal-builder.langs.js +0 -157
  161. package/src/graph/internal-builder.pass2.js +0 -163
  162. package/src/graph/internal-builder.resolvers.js +0 -286
  163. package/src/graph/layout.js +0 -51
  164. package/src/graph/node-id.js +0 -7
  165. package/src/graph/parser-artifact-boundary.js +0 -55
  166. package/src/graph/relations.js +0 -4
  167. package/src/graph/repo-registry.js +0 -124
  168. package/src/graph/resolvers/rust.js +0 -117
  169. package/src/infra/infra-items.js +0 -255
  170. package/src/infra/infra-registry.js +0 -184
  171. package/src/infra/infra.detect.js +0 -119
  172. package/src/infra/infra.js +0 -38
  173. package/src/infra/infra.match.js +0 -102
  174. package/src/infra/infra.scan.js +0 -93
  175. package/src/mcp/actions/graph-lifecycle.mjs +0 -148
  176. package/src/mcp/architecture-bootstrap.mjs +0 -168
  177. package/src/mcp/architecture-starter.mjs +0 -234
  178. package/src/mcp/catalog.mjs +0 -233
  179. package/src/mcp/company-contract-page.mjs +0 -142
  180. package/src/mcp/company-contract-reconcile.mjs +0 -87
  181. package/src/mcp/company-contract-verdict.mjs +0 -42
  182. package/src/mcp/evidence/bun-lock-graph.mjs +0 -209
  183. package/src/mcp/evidence/duplicate-member-order.mjs +0 -8
  184. package/src/mcp/evidence/package-graph-common.mjs +0 -115
  185. package/src/mcp/evidence/package-lock-graph.mjs +0 -92
  186. package/src/mcp/evidence-snapshot.architecture.mjs +0 -80
  187. package/src/mcp/evidence-snapshot.common.mjs +0 -158
  188. package/src/mcp/evidence-snapshot.duplicates.mjs +0 -213
  189. package/src/mcp/evidence-snapshot.health.mjs +0 -102
  190. package/src/mcp/evidence-snapshot.inventory.mjs +0 -148
  191. package/src/mcp/evidence-snapshot.mjs +0 -50
  192. package/src/mcp/evidence-snapshot.package-graph.mjs +0 -34
  193. package/src/mcp/evidence-snapshot.structure.mjs +0 -84
  194. package/src/mcp/extension-api.mjs +0 -77
  195. package/src/mcp/git-output.mjs +0 -7
  196. package/src/mcp/graph/context-core.mjs +0 -111
  197. package/src/mcp/graph/context-seeds.mjs +0 -207
  198. package/src/mcp/graph/context-state.mjs +0 -85
  199. package/src/mcp/graph/reverse-reach.mjs +0 -42
  200. package/src/mcp/graph/tools-core.mjs +0 -143
  201. package/src/mcp/graph/tools-query.mjs +0 -276
  202. package/src/mcp/graph-context.mjs +0 -5
  203. package/src/mcp/graph-diff.mjs +0 -218
  204. package/src/mcp/health/audit-format.mjs +0 -199
  205. package/src/mcp/health/audit.mjs +0 -174
  206. package/src/mcp/health/dead-code.mjs +0 -110
  207. package/src/mcp/health/duplicates.mjs +0 -85
  208. package/src/mcp/health/endpoints.mjs +0 -71
  209. package/src/mcp/health/structure.mjs +0 -219
  210. package/src/mcp/runtime-version.mjs +0 -31
  211. package/src/mcp/server/auto-refresh.mjs +0 -66
  212. package/src/mcp/server/runtime-config.mjs +0 -43
  213. package/src/mcp/server/shutdown.mjs +0 -40
  214. package/src/mcp/staleness-notice.mjs +0 -20
  215. package/src/mcp/sync/evidence-architecture.mjs +0 -54
  216. package/src/mcp/sync/evidence-common.mjs +0 -88
  217. package/src/mcp/sync/evidence-duplicates.mjs +0 -97
  218. package/src/mcp/sync/evidence-health.mjs +0 -56
  219. package/src/mcp/sync/evidence-packages.mjs +0 -95
  220. package/src/mcp/sync/payload-common.mjs +0 -97
  221. package/src/mcp/sync/payload-v2.mjs +0 -54
  222. package/src/mcp/sync/payload-v3.mjs +0 -79
  223. package/src/mcp/sync-evidence.mjs +0 -23
  224. package/src/mcp/sync-payload.mjs +0 -70
  225. package/src/mcp/tool-result.mjs +0 -54
  226. package/src/mcp/tools-actions.mjs +0 -11
  227. package/src/mcp/tools-architecture.mjs +0 -177
  228. package/src/mcp/tools-company.mjs +0 -251
  229. package/src/mcp/tools-context.mjs +0 -222
  230. package/src/mcp/tools-endpoints.mjs +0 -207
  231. package/src/mcp/tools-graph-hubs.mjs +0 -78
  232. package/src/mcp/tools-graph.mjs +0 -17
  233. package/src/mcp/tools-health.mjs +0 -24
  234. package/src/mcp/tools-history.mjs +0 -28
  235. package/src/mcp/tools-impact-change.mjs +0 -220
  236. package/src/mcp/tools-impact-precision.mjs +0 -89
  237. package/src/mcp/tools-impact.mjs +0 -169
  238. package/src/mcp/tools-source.mjs +0 -238
  239. package/src/mcp/tools-verified-change.mjs +0 -200
  240. package/src/mcp-rg.mjs +0 -50
  241. package/src/mcp-runtime.mjs +0 -2
  242. package/src/mcp-server.mjs +0 -298
  243. package/src/mcp-source-tools.mjs +0 -163
  244. package/src/path-classification.js +0 -220
  245. package/src/path-ignore.js +0 -69
  246. package/src/precision/lsp-client/constants.js +0 -12
  247. package/src/precision/lsp-client/environment.js +0 -20
  248. package/src/precision/lsp-client/errors.js +0 -15
  249. package/src/precision/lsp-client/lifecycle.js +0 -114
  250. package/src/precision/lsp-client/message-parser.js +0 -81
  251. package/src/precision/lsp-client/protocol.js +0 -212
  252. package/src/precision/lsp-client/registry.js +0 -59
  253. package/src/precision/lsp-client/repo-uri.js +0 -60
  254. package/src/precision/lsp-client/stdio-client.js +0 -140
  255. package/src/precision/lsp-client.js +0 -10
  256. package/src/precision/lsp-overlay/budget-policy.js +0 -78
  257. package/src/precision/lsp-overlay/build.js +0 -261
  258. package/src/precision/lsp-overlay/contract.js +0 -61
  259. package/src/precision/lsp-overlay/reference-results.js +0 -108
  260. package/src/precision/lsp-overlay/semantic-inputs.js +0 -62
  261. package/src/precision/lsp-overlay/source-session.js +0 -142
  262. package/src/precision/lsp-overlay/store.js +0 -150
  263. package/src/precision/lsp-overlay/target-index.js +0 -152
  264. package/src/precision/lsp-overlay/target-query.js +0 -55
  265. package/src/precision/lsp-overlay.js +0 -11
  266. package/src/precision/symbol-query.js +0 -224
  267. package/src/precision/typescript-lsp-provider.js +0 -12
  268. package/src/precision/typescript-provider/client.js +0 -89
  269. package/src/precision/typescript-provider/discovery.js +0 -124
  270. package/src/precision/typescript-provider/isolated-runtime.js +0 -39
  271. package/src/precision/typescript-provider/project-host.js +0 -254
  272. package/src/precision/typescript-provider/project-safety.js +0 -166
  273. package/src/precision/typescript-provider/reference-usage.js +0 -53
  274. package/src/process.js +0 -95
  275. package/src/repo-path.js +0 -53
  276. package/src/scan/discover.inventory.js +0 -78
  277. package/src/scan/discover.js +0 -5
  278. package/src/scan/discover.list.js +0 -79
  279. package/src/scan/discover.stack.js +0 -227
  280. package/src/scan/search.core.js +0 -24
  281. package/src/scan/search.git.js +0 -102
  282. package/src/scan/search.js +0 -9
  283. package/src/scan/search.node.js +0 -83
  284. package/src/scan/search.preview.js +0 -49
  285. package/src/scan/search.rg.js +0 -145
  286. package/src/security/advisory-store.js +0 -172
  287. package/src/security/installed-jvm-rust.js +0 -46
  288. package/src/security/installed.js +0 -280
  289. package/src/security/malware-file-heuristics.js +0 -121
  290. package/src/security/malware-heuristics.exclusions.js +0 -134
  291. package/src/security/malware-heuristics.js +0 -15
  292. package/src/security/malware-heuristics.roots.js +0 -145
  293. package/src/security/malware-heuristics.scan.js +0 -89
  294. package/src/security/malware-heuristics.sweep.js +0 -122
  295. package/src/security/malware-scoring.js +0 -105
  296. package/src/security/match.js +0 -85
  297. package/src/security/registry-sig.classify.js +0 -137
  298. package/src/security/registry-sig.js +0 -18
  299. package/src/security/registry-sig.rules.js +0 -192
  300. package/src/security/rust-advisory-report.js +0 -60
  301. package/src/security/typosquat.js +0 -81
  302. package/src/util.js +0 -40
  303. package/src/version.js +0 -7
@@ -1,97 +0,0 @@
1
- import {
2
- CAPS, DUPLICATE_THRESHOLDS, bool, compare, count, graphId, int, list,
3
- path, reasons, state, text, verdict,
4
- } from './evidence-common.mjs'
5
- import {compareDuplicateMember} from '../evidence/duplicate-member-order.mjs'
6
-
7
- function duplicateEvidenceId(value) {
8
- const id = text(value, 64)
9
- return id && /^[a-f0-9]{24,64}$/i.test(id) ? id : undefined
10
- }
11
-
12
- function duplicateMember(value) {
13
- if (!value || typeof value !== 'object' || Array.isArray(value)) return null
14
- const file = path(value.file)
15
- const startLine = int(value.startLine), endLine = int(value.endLine), tokens = int(value.tokens)
16
- if (!file || startLine < 1 || endLine < startLine || tokens < DUPLICATE_THRESHOLDS.clones.minTokens) return null
17
- const out = {file, startLine, endLine, tokens}
18
- const nodeId = graphId(value.graphNodeId)
19
- if (nodeId?.startsWith(`${file}#`)) out.graphNodeId = nodeId
20
- return out
21
- }
22
-
23
- function duplicateMembers(values, cap = CAPS.duplicateMembers) {
24
- const raw = Array.isArray(values) ? values : []
25
- const unique = new Map()
26
- for (const value of raw) {
27
- const item = duplicateMember(value)
28
- if (!item) continue
29
- unique.set(`${item.file}\0${item.startLine}\0${item.endLine}\0${item.graphNodeId || ''}`, item)
30
- }
31
- const all = [...unique.values()].sort(compareDuplicateMember)
32
- return {items: all.slice(0, cap), total: all.length, invalid: raw.length - all.length, truncated: all.length > cap}
33
- }
34
-
35
- function cloneGroup(value) {
36
- const id = duplicateEvidenceId(value?.id)
37
- const members = duplicateMembers(value?.members)
38
- const rawStrongestSimilarity = Number(value?.strongestSimilarity)
39
- const rawWeakestLinkedSimilarity = Number(value?.weakestLinkedSimilarity)
40
- const strongestSimilarity = Math.trunc(rawStrongestSimilarity)
41
- const weakestLinkedSimilarity = Math.trunc(rawWeakestLinkedSimilarity)
42
- if (!id || members.items.length < 2 || strongestSimilarity < DUPLICATE_THRESHOLDS.clones.minSimilarityPercent ||
43
- !Number.isFinite(rawStrongestSimilarity) || rawStrongestSimilarity > 100 ||
44
- !Number.isFinite(rawWeakestLinkedSimilarity) || weakestLinkedSimilarity < DUPLICATE_THRESHOLDS.clones.minSimilarityPercent ||
45
- rawWeakestLinkedSimilarity > 100 || weakestLinkedSimilarity > strongestSimilarity) return null
46
- const memberCount = Math.max(int(value?.memberCount), members.total)
47
- const returnedTokens = members.items.reduce((sum, member) => sum + member.tokens, 0)
48
- return {
49
- id, memberCount, totalTokens: Math.max(int(value?.totalTokens), returnedTokens),
50
- strongestSimilarity, weakestLinkedSimilarity,
51
- membersTruncated: bool(value?.membersTruncated) || members.truncated || members.invalid > 0 || memberCount > members.items.length,
52
- members: members.items,
53
- }
54
- }
55
-
56
- function divergenceCandidate(value) {
57
- const id = duplicateEvidenceId(value?.id)
58
- const symbol = text(value?.symbol, 256)
59
- const rawSimilarity = Number(value?.similarity)
60
- const similarity = Math.trunc(rawSimilarity)
61
- const members = duplicateMembers(value?.members, 2)
62
- if (!id || !symbol || !/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(symbol) || members.total !== 2 || members.invalid > 0 ||
63
- !Number.isFinite(rawSimilarity) || rawSimilarity < 0 || rawSimilarity > DUPLICATE_THRESHOLDS.divergence.maxSimilarityPercent ||
64
- members.items[0].file === members.items[1].file) return null
65
- return {id, symbol, similarity, totalTokens: Math.max(int(value?.totalTokens), members.items[0].tokens + members.items[1].tokens), members: members.items}
66
- }
67
-
68
- export function sanitizeDuplicates(value) {
69
- const rawGroups = Array.isArray(value?.cloneGroups) ? value.cloneGroups : []
70
- const rawDivergence = Array.isArray(value?.divergenceCandidates) ? value.divergenceCandidates : []
71
- const cloneGroups = list(rawGroups, CAPS.duplicateGroups, cloneGroup,
72
- (a, b) => b.totalTokens - a.totalTokens || b.memberCount - a.memberCount || compare(a.id, b.id))
73
- const divergence = list(rawDivergence, CAPS.divergenceCandidates, divergenceCandidate,
74
- (a, b) => b.totalTokens - a.totalTokens || a.similarity - b.similarity || compare(a.symbol, b.symbol) || compare(a.id, b.id))
75
- const invalid = rawGroups.length - cloneGroups.total + rawDivergence.length - divergence.total
76
- const membersTruncated = cloneGroups.items.some((group) => group.membersTruncated)
77
- const groupCompleteness = count(value?.completeness?.cloneGroups, rawGroups.length, cloneGroups.items.length)
78
- const divergenceCompleteness = count(value?.completeness?.divergenceCandidates, rawDivergence.length, divergence.items.length)
79
- const truncated = cloneGroups.truncated || divergence.truncated || groupCompleteness.truncated || divergenceCompleteness.truncated || membersTruncated || invalid > 0
80
- const outReasons = reasons([
81
- ...(Array.isArray(value?.completeness?.reasons) ? value.completeness.reasons : []),
82
- ...(invalid > 0 ? ['INVALID_DUPLICATE_EVIDENCE_DROPPED'] : []),
83
- ...(membersTruncated ? ['CLONE_MEMBERS_TRUNCATED'] : []),
84
- ...(cloneGroups.truncated ? ['CLONE_GROUPS_TRUNCATED'] : []),
85
- ...(divergence.truncated ? ['DIVERGENCE_CANDIDATES_TRUNCATED'] : []),
86
- ])
87
- const rawFragments = value?.completeness?.fragments
88
- const eligible = int(rawFragments?.eligible), filtered = int(rawFragments?.filtered)
89
- const total = Math.max(int(rawFragments?.total), eligible + filtered)
90
- const outState = state(value?.state)
91
- return {
92
- state: truncated && outState === 'COMPLETE' ? 'PARTIAL' : outState,
93
- verdict: verdict(value?.verdict), thresholds: DUPLICATE_THRESHOLDS,
94
- completeness: {fragments: {total, eligible: Math.min(eligible, total), filtered: Math.max(filtered, total - eligible)}, cloneGroups: groupCompleteness, divergenceCandidates: divergenceCompleteness, reasons: outReasons},
95
- cloneGroups: cloneGroups.items, divergenceCandidates: divergence.items,
96
- }
97
- }
@@ -1,56 +0,0 @@
1
- import {
2
- CAPS, CATEGORIES, CONFIDENCE, SEVERITIES, checks, compare, count, graphId,
3
- int, list, numericRecord, packageName, path, privacySafeText, reasons, set,
4
- state, text, token, verdict,
5
- } from './evidence-common.mjs'
6
-
7
- function finding(value) {
8
- if (!value || typeof value !== 'object') return null
9
- const id = text(value.id, 64), category = token(value.category, 32), rule = token(value.rule, 64), severity = token(value.severity, 16)
10
- if (!id || !/^[a-f0-9]{8,64}$/i.test(id) || !CATEGORIES.has(category) || !rule || !SEVERITIES.has(severity)) return null
11
- const out = {id, category, rule, severity}
12
- if (CONFIDENCE.has(value.confidence)) out.confidence = value.confidence
13
- set(out, 'file', path(value.file)); set(out, 'line', Number.isFinite(value.line) && value.line >= 0 ? Math.trunc(value.line) : undefined)
14
- set(out, 'symbol', privacySafeText(value.symbol)); set(out, 'package', packageName(value.package)); set(out, 'version', token(value.version, 128)); set(out, 'graphNodeId', graphId(value.graphNodeId))
15
- return out
16
- }
17
-
18
- function hotspot(value) {
19
- const id = graphId(value?.id), file = path(value?.file), severity = token(value?.severity, 16)
20
- if (!id || !file || !['medium', 'high'].includes(severity)) return null
21
- const out = {id, file, severity, breaches: [...new Set((value.breaches || []).map((item) => token(item, 48)).filter(Boolean))].sort(compare).slice(0, 12)}
22
- set(out, 'symbol', privacySafeText(value.symbol))
23
- for (const key of ['startLine', 'endLine', 'loc', 'cyclomatic', 'params']) if (Number.isFinite(value[key]) && value[key] >= 0) out[key] = Math.trunc(value[key])
24
- return out
25
- }
26
-
27
- export function sanitizeHealth(value) {
28
- const findings = list(value?.findings, CAPS.findings, finding, (a, b) => compare(a.severity, b.severity) || compare(a.id, b.id))
29
- const hotspots = list(value?.complexity?.hotspots, CAPS.hotspots, hotspot, (a, b) => compare(a.severity, b.severity) || compare(a.id, b.id))
30
- const truncated = findings.truncated || hotspots.truncated
31
- const outState = state(value?.state)
32
- return {
33
- state: truncated && outState === 'COMPLETE' ? 'PARTIAL' : outState, verdict: verdict(value?.verdict),
34
- completeness: {findings: count(value?.completeness?.findings, findings.total, findings.items.length), hotspots: count(value?.completeness?.hotspots, hotspots.total, hotspots.items.length), reasons: reasons(value?.completeness?.reasons)},
35
- summary: {
36
- bySeverity: numericRecord(value?.summary?.bySeverity, ['critical', 'high', 'medium', 'low', 'info']),
37
- byCategory: numericRecord(value?.summary?.byCategory, ['unused', 'structure', 'vulnerability', 'malware']),
38
- dead: numericRecord(value?.summary?.dead, ['deadSymbols', 'deadFiles', 'unusedExports']),
39
- structure: numericRecord(value?.summary?.structure, ['runtimeImportEdges', 'typeOnlyImportEdges', 'compileOnlyImportEdges', 'runtimeCycles', 'compileTimeCouplings', 'largestCycle', 'largestCompileTimeCoupling', 'orphans', 'boundaryViolations']),
40
- },
41
- checks: checks(value?.checks), findings: findings.items,
42
- complexity: {thresholds: {loc: {warning: int(value?.complexity?.thresholds?.loc?.warning), high: int(value?.complexity?.thresholds?.loc?.high)}, cyclomatic: {warning: int(value?.complexity?.thresholds?.cyclomatic?.warning), high: int(value?.complexity?.thresholds?.cyclomatic?.high)}, params: {warning: int(value?.complexity?.thresholds?.params?.warning), high: int(value?.complexity?.thresholds?.params?.high)}}, analyzed: int(value?.complexity?.analyzed), hotspots: hotspots.items},
43
- }
44
- }
45
-
46
- function badge(value) {
47
- const category = token(value?.category, 32), id = token(value?.id, 128)
48
- if (!['languages', 'runtimes', 'tests', 'infra', 'deploy'].includes(category) || !id) return null
49
- const out = {category, id}; set(out, 'kind', token(value.kind, 64)); set(out, 'version', token(value.version, 64)); return out
50
- }
51
-
52
- export function sanitizeTechnologies(value) {
53
- const badges = list(value?.badges, CAPS.badges, badge, (a, b) => compare(a.category, b.category) || compare(a.id, b.id))
54
- const outState = state(value?.state)
55
- return {state: badges.truncated && outState === 'COMPLETE' ? 'PARTIAL' : outState, verdict: verdict(value?.verdict), completeness: {badges: count(value?.completeness?.badges, badges.total, badges.items.length), reasons: reasons(value?.completeness?.reasons)}, badges: badges.items}
56
- }
@@ -1,95 +0,0 @@
1
- import {
2
- CAPS, PACKAGE_DEPENDENCY_KINDS, bool, checks, compare, count, int, list,
3
- numericRecord, packageName, packageVersion, path, reasons, set, state, text,
4
- token, verdict,
5
- } from './evidence-common.mjs'
6
-
7
- const PACKAGE_SOURCES = new Set(['package-lock', 'yarn-lock', 'requirements', 'venv', 'poetry-lock', 'uv-lock', 'pipfile-lock', 'go-sum', 'go-mod', 'node_modules'])
8
-
9
- function packageSource(value) {
10
- const source = text(value, 512)
11
- if (PACKAGE_SOURCES.has(source)) return source
12
- const relative = path(source, 512)
13
- return relative && /(^|\/)(?:package-lock\.json|npm-shrinkwrap\.json|yarn\.lock|pnpm-lock\.ya?ml|requirements[\w.-]*\.(?:txt|in)|poetry\.lock|uv\.lock|Pipfile\.lock|go\.(?:mod|sum))$/i.test(relative) ? relative : undefined
14
- }
15
-
16
- function packageFact(value) {
17
- const name = packageName(value?.name), version = token(value?.version, 128), ecosystem = token(value?.ecosystem, 64), source = packageSource(value?.source)
18
- return name && version && ecosystem && source ? {name, version, ecosystem, dev: bool(value.dev), source} : null
19
- }
20
-
21
- function usage(value) {
22
- const name = packageName(value?.name), ecosystem = token(value?.ecosystem, 64)
23
- if (!name || !ecosystem) return null
24
- const files = [...new Set((value.files || []).map((item) => path(item)).filter(Boolean))].sort(compare)
25
- return {name, ecosystem, importCount: int(value.importCount), fileCount: int(value.fileCount), files: files.slice(0, CAPS.files), filesTruncated: bool(value.filesTruncated) || files.length > CAPS.files, kinds: [...new Set((value.kinds || []).map((item) => token(item, 64)).filter(Boolean))].sort(compare).slice(0, 32)}
26
- }
27
-
28
- function packageGraphNode(value) {
29
- const name = packageName(value?.name), version = packageVersion(value?.version), id = text(value?.id, 512)
30
- if (!name || !version || !id) return null
31
- const prefix = `npm:${name}@${version}:`
32
- if (!id.startsWith(prefix) || !/^[a-f0-9]{12}$/i.test(id.slice(prefix.length))) return null
33
- return {id, name, version, direct: bool(value.direct), dev: bool(value.dev), optional: bool(value.optional), peer: bool(value.peer)}
34
- }
35
-
36
- function sanitizePackageDependencyGraph(value) {
37
- if (!value || typeof value !== 'object' || Array.isArray(value)) {
38
- return {
39
- state: 'NOT_CHECKED', ecosystem: 'npm', root: '(root)',
40
- completeness: {nodes: count(null, 0, 0), edges: count(null, 0, 0), declarations: numericRecord(null, ['total', 'resolved', 'unresolved', 'local', 'optionalMissing']), reasons: ['DEPENDENCY_GRAPH_NOT_PROVIDED']},
41
- nodes: [], edges: [],
42
- }
43
- }
44
- const nodes = list(value.nodes, CAPS.packageGraphNodes, packageGraphNode,
45
- (a, b) => Number(b.direct) - Number(a.direct) || compare(a.name, b.name) || compare(a.version, b.version) || compare(a.id, b.id))
46
- const nodeIds = new Set(nodes.items.map((node) => node.id))
47
- const edge = (candidate) => {
48
- const from = candidate?.from === '(root)' ? '(root)' : text(candidate?.from, 512)
49
- const to = text(candidate?.to, 512), kind = token(candidate?.kind, 32)
50
- if (!from || !to || !PACKAGE_DEPENDENCY_KINDS.has(kind) || from === to ||
51
- (from !== '(root)' && !nodeIds.has(from)) || !nodeIds.has(to)) return null
52
- return {from, to, kind}
53
- }
54
- const edges = list(value.edges, CAPS.packageGraphEdges, edge,
55
- (a, b) => compare(a.from, b.from) || compare(a.to, b.to) || compare(a.kind, b.kind))
56
- const lockfile = path(value.lockfile, 512)
57
- const safeLockfile = lockfile && /(^|\/)(?:package-lock\.json|npm-shrinkwrap\.json)$/i.test(lockfile) ? lockfile : undefined
58
- const graphState = state(value.state)
59
- const truncated = nodes.truncated || edges.truncated
60
- const out = {
61
- state: truncated && graphState === 'COMPLETE' ? 'PARTIAL' : graphState,
62
- ecosystem: 'npm', root: '(root)',
63
- completeness: {
64
- nodes: count(value?.completeness?.nodes, nodes.total, nodes.items.length),
65
- edges: count(value?.completeness?.edges, edges.total, edges.items.length),
66
- declarations: numericRecord(value?.completeness?.declarations, ['total', 'resolved', 'unresolved', 'local', 'optionalMissing']),
67
- reasons: reasons(value?.completeness?.reasons),
68
- },
69
- nodes: nodes.items, edges: edges.items,
70
- }
71
- set(out, 'lockfile', safeLockfile)
72
- const lockfileVersion = int(value.lockfileVersion)
73
- if (lockfileVersion > 0) out.lockfileVersion = lockfileVersion
74
- return out
75
- }
76
-
77
- export function sanitizePackages(value) {
78
- const inventory = list(value?.inventory, CAPS.packages, packageFact, (a, b) => compare(a.ecosystem, b.ecosystem) || compare(a.name, b.name) || compare(a.version, b.version))
79
- const directUsage = list(value?.directUsage, CAPS.usage, usage, (a, b) => compare(a.ecosystem, b.ecosystem) || compare(a.name, b.name))
80
- const dependencyGraph = sanitizePackageDependencyGraph(value?.dependencyGraph)
81
- const outState = state(value?.state)
82
- const truncated = inventory.truncated || directUsage.truncated || (dependencyGraph.state === 'PARTIAL' && value?.dependencyGraph?.state === 'COMPLETE')
83
- return {
84
- state: truncated && outState === 'COMPLETE' ? 'PARTIAL' : outState,
85
- verdict: verdict(value?.verdict),
86
- completeness: {
87
- inventory: count(value?.completeness?.inventory, inventory.total, inventory.items.length),
88
- directUsage: count(value?.completeness?.directUsage, directUsage.total, directUsage.items.length),
89
- dependencyGraphNodes: count(value?.completeness?.dependencyGraphNodes, dependencyGraph.completeness.nodes.total, dependencyGraph.nodes.length),
90
- dependencyGraphEdges: count(value?.completeness?.dependencyGraphEdges, dependencyGraph.completeness.edges.total, dependencyGraph.edges.length),
91
- reasons: reasons(value?.completeness?.reasons),
92
- },
93
- checks: checks(value?.checks), inventory: inventory.items, directUsage: directUsage.items, dependencyGraph,
94
- }
95
- }
@@ -1,97 +0,0 @@
1
- const CONTROL_CHARS = /[\u0000-\u001f\u007f]/;
2
- const ABSOLUTE_PATH_FRAGMENT = /(?:^|[\/\s"'`(=])[a-z]:[\\/]|(?:^|[\s"'`(=])(?:\\\\[^\\/\s]+(?:[\\/]|$)|file:(?:\/\/)?[\\/]|\/(?!\/)[^\s])/i;
3
-
4
- export const MAX_SYNC_BODY_BYTES = 8 * 1024 * 1024;
5
- export const MAX_SYNC_NODES = 25_000;
6
- export const MAX_SYNC_LINKS = 100_000;
7
- export const MAX_SYNC_EXTERNAL_IMPORTS = 50_000;
8
-
9
- export function metadataString(value, max = 4096) {
10
- return typeof value === 'string' && value.length > 0 && value.length <= max && !CONTROL_CHARS.test(value)
11
- ? value : undefined;
12
- }
13
-
14
- export function repoRelativePathString(value, max = 4096) {
15
- const path = metadataString(value, max);
16
- if (!path || /^(?:[a-z][a-z0-9+.-]*:|[\\/])/i.test(path)) return undefined;
17
- const segments = path.split(/[\\/]/);
18
- if (segments.some((segment) => segment === '.' || segment === '..')) return undefined;
19
- return path;
20
- }
21
-
22
- export function graphIdString(value) {
23
- const id = metadataString(value);
24
- if (!id) return undefined;
25
- const hash = id.indexOf('#');
26
- const file = hash < 0 ? id : id.slice(0, hash);
27
- return repoRelativePathString(file) ? id : undefined;
28
- }
29
-
30
- export function privacySafeText(value, max = 4096) {
31
- const text = metadataString(value, max);
32
- return text && !ABSOLUTE_PATH_FRAGMENT.test(text) ? text : undefined;
33
- }
34
-
35
- export function repoPathV3(value, max = 4096) {
36
- const path = repoRelativePathString(value, max);
37
- return path ? path.replace(/\\/g, '/') : undefined;
38
- }
39
-
40
- export function graphIdV3(value) {
41
- const id = metadataString(value);
42
- if (!id) return undefined;
43
- const hash = id.indexOf('#');
44
- const file = hash < 0 ? id : id.slice(0, hash);
45
- const safeFile = repoPathV3(file);
46
- if (!safeFile) return undefined;
47
- if (hash < 0) return safeFile;
48
- const suffix = id.slice(hash);
49
- if (suffix.length > 512 || !/^#[^\\/\s\u0000-\u001f\u007f]{1,511}$/u.test(suffix)) return undefined;
50
- return `${safeFile}${suffix}`;
51
- }
52
-
53
- export function safeToken(value, max = 256) {
54
- const token = metadataString(value, max);
55
- return token && /^[\p{L}\p{N}_.:@+\-#$<>()\[\],]+$/u.test(token) ? token : undefined;
56
- }
57
-
58
- export function packageName(value) {
59
- const name = metadataString(value, 256);
60
- return name && /^(?:@[a-z0-9._-]+\/)?[a-z0-9][a-z0-9._-]*$/i.test(name) ? name : undefined;
61
- }
62
-
63
- export function externalSpecifier(value) {
64
- const spec = metadataString(value, 512);
65
- if (!spec || ABSOLUTE_PATH_FRAGMENT.test(spec) || /^(?:[a-z]:[\\/]|[\\/]|\.\.?[\\/])/i.test(spec)) return undefined;
66
- return /^[a-z0-9@][a-z0-9@._:/+\-]*$/i.test(spec) ? spec : undefined;
67
- }
68
-
69
- export function finiteNumber(value) {
70
- return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
71
- }
72
-
73
- export function setIf(out, key, value) {
74
- if (value !== undefined) out[key] = value;
75
- }
76
-
77
- const COMPLEXITY_NUMBERS = [
78
- 'startLine', 'endLine', 'loc', 'params', 'objectFields', 'branches', 'cyclomatic',
79
- 'loops', 'maxLoopDepth', 'returns', 'awaits', 'callCount', 'externalCalls',
80
- 'asyncBoundaries', 'allocations', 'objectLiterals', 'spreadCopies', 'sorts',
81
- 'linearOps', 'allocationsInLoops', 'copiesInLoops', 'linearOpsInLoops',
82
- 'sortsInLoops', 'recursionInLoops', 'timeRank', 'timeScore', 'memoryRank', 'memoryScore',
83
- ];
84
-
85
- export function sanitizeComplexity(value) {
86
- if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
87
- const out = {};
88
- for (const key of COMPLEXITY_NUMBERS) setIf(out, key, finiteNumber(value[key]));
89
- if (typeof value.recursion === 'boolean') out.recursion = value.recursion;
90
- for (const key of ['family', 'scope', 'complexityScope', 'confidence']) setIf(out, key, metadataString(value[key], 32));
91
- return Object.keys(out).length ? out : undefined;
92
- }
93
-
94
- export function assertArrayLimit(raw, key, limit) {
95
- const count = Array.isArray(raw[key]) ? raw[key].length : 0;
96
- if (count > limit) throw new Error(`${key} has ${count} entries; maximum is ${limit}`);
97
- }
@@ -1,54 +0,0 @@
1
- import {
2
- finiteNumber, graphIdString, metadataString, repoRelativePathString,
3
- sanitizeComplexity, setIf,
4
- } from './payload-common.mjs';
5
-
6
- export function sanitizeNode(value) {
7
- if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
8
- const id = graphIdString(value.id);
9
- if (!id) return null;
10
- const out = {id};
11
- setIf(out, 'label', metadataString(value.label, 1024));
12
- setIf(out, 'file_type', metadataString(value.file_type, 32));
13
- setIf(out, 'source_file', repoRelativePathString(value.source_file));
14
- const sourceLocation = metadataString(value.source_location, 32);
15
- const sourceEnd = metadataString(value.source_end, 32);
16
- if (sourceLocation && /^L\d+$/.test(sourceLocation)) out.source_location = sourceLocation;
17
- if (sourceEnd && /^L\d+$/.test(sourceEnd)) out.source_end = sourceEnd;
18
- const community = finiteNumber(value.community);
19
- if (community !== undefined && Number.isInteger(community) && community >= 0) out.community = community;
20
- if (typeof value.exported === 'boolean') out.exported = value.exported;
21
- if (typeof value.decorated === 'boolean') out.decorated = value.decorated;
22
- if (value.test_surface === true) out.test_surface = true;
23
- setIf(out, 'complexity', sanitizeComplexity(value.complexity));
24
- return out;
25
- }
26
-
27
- export function sanitizeLink(value) {
28
- if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
29
- const source = graphIdString(value.source);
30
- const target = graphIdString(value.target);
31
- if (!source || !target) return null;
32
- const out = {source, target};
33
- setIf(out, 'relation', metadataString(value.relation, 32));
34
- setIf(out, 'confidence', metadataString(value.confidence, 32));
35
- if (value.typeOnly === true) out.typeOnly = true;
36
- if (value.compileOnly === true) out.compileOnly = true;
37
- const line = finiteNumber(value.line);
38
- if (line !== undefined && Number.isInteger(line) && line >= 0) out.line = line;
39
- setIf(out, 'specifier', metadataString(value.specifier));
40
- return out;
41
- }
42
-
43
- export function sanitizeExternalImport(value) {
44
- if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
45
- const file = repoRelativePathString(value.file);
46
- if (!file) return null;
47
- const out = {file};
48
- for (const key of ['spec', 'target']) setIf(out, key, metadataString(value[key]));
49
- for (const key of ['pkg', 'kind', 'ecosystem']) setIf(out, key, metadataString(value[key], 256));
50
- const line = finiteNumber(value.line);
51
- if (line !== undefined && Number.isInteger(line) && line >= 0) out.line = line;
52
- for (const key of ['builtin', 'dynamic', 'unresolved']) if (typeof value[key] === 'boolean') out[key] = value[key];
53
- return out;
54
- }
@@ -1,79 +0,0 @@
1
- import {edgeProvenance} from '../../graph/edge-provenance.js';
2
- import {
3
- externalSpecifier, graphIdV3, packageName, privacySafeText, repoPathV3,
4
- safeToken, setIf,
5
- } from './payload-common.mjs';
6
- import {sanitizeExternalImport, sanitizeLink, sanitizeNode} from './payload-v2.mjs';
7
-
8
- export function sanitizeNodeV3(value) {
9
- const out = sanitizeNode(value);
10
- if (!out) return null;
11
- const id = graphIdV3(value.id);
12
- if (!id) return null;
13
- const sourceFile = value.source_file == null ? undefined : repoPathV3(value.source_file);
14
- if (value.source_file != null && !sourceFile) return null;
15
- const hash = id.indexOf('#');
16
- const idFile = hash < 0 ? id : id.slice(0, hash);
17
- if (sourceFile && idFile !== sourceFile) return null;
18
- out.id = id;
19
- const label = privacySafeText(value.label, 1024);
20
- if (label) out.label = label;
21
- else delete out.label;
22
- const fileType = safeToken(value.file_type, 32);
23
- if (fileType) out.file_type = fileType;
24
- else delete out.file_type;
25
- if (sourceFile) out.source_file = sourceFile;
26
- else delete out.source_file;
27
- for (const key of ['symbol_kind', 'symbol_space', 'member_of', 'visibility']) setIf(out, key, safeToken(value[key], 128));
28
- if (out.complexity) for (const key of ['family', 'scope', 'complexityScope', 'confidence']) {
29
- const safe = safeToken(value.complexity?.[key], 32);
30
- if (safe) out.complexity[key] = safe;
31
- else delete out.complexity[key];
32
- }
33
- return out;
34
- }
35
-
36
- export function sanitizeLinkV3(value) {
37
- const out = sanitizeLink(value);
38
- if (!out) return null;
39
- const source = graphIdV3(value.source);
40
- const target = graphIdV3(value.target);
41
- if (!source || !target) return null;
42
- out.source = source;
43
- out.target = target;
44
- for (const key of ['relation', 'confidence']) {
45
- const safe = safeToken(value[key], 32);
46
- if (safe) out[key] = safe;
47
- else delete out[key];
48
- }
49
- const provenance = edgeProvenance(value);
50
- if (provenance !== 'UNKNOWN') out.provenance = provenance;
51
- const specifier = privacySafeText(value.specifier, 1024);
52
- if (specifier) out.specifier = specifier;
53
- else delete out.specifier;
54
- return out;
55
- }
56
-
57
- export function sanitizeExternalImportV3(value) {
58
- const out = sanitizeExternalImport(value);
59
- if (!out) return null;
60
- const file = repoPathV3(value.file);
61
- if (!file) return null;
62
- out.file = file;
63
- setIf(out, 'spec', externalSpecifier(value.spec));
64
- if (!externalSpecifier(value.spec)) delete out.spec;
65
- setIf(out, 'pkg', packageName(value.pkg));
66
- if (!packageName(value.pkg)) delete out.pkg;
67
- for (const key of ['kind', 'ecosystem']) {
68
- const safe = safeToken(value[key], 64);
69
- if (safe) out[key] = safe;
70
- else delete out[key];
71
- }
72
- if (value.target != null) {
73
- const target = repoPathV3(value.target);
74
- if (target) out.target = target;
75
- else delete out.target;
76
- }
77
- if (value.typeOnly === true) out.typeOnly = true;
78
- return out;
79
- }
@@ -1,23 +0,0 @@
1
- // Defense-in-depth wire sanitizer for evidence snapshots. Each section owns its
2
- // allowlist; this facade only assembles and hashes the versioned wire envelope.
3
- import {createHash} from 'node:crypto'
4
- import {sanitizeArchitecture} from './sync/evidence-architecture.mjs'
5
- import {stableStringify} from './sync/evidence-common.mjs'
6
- import {sanitizeDuplicates} from './sync/evidence-duplicates.mjs'
7
- import {sanitizeHealth, sanitizeTechnologies} from './sync/evidence-health.mjs'
8
- import {sanitizePackages} from './sync/evidence-packages.mjs'
9
-
10
- export function sanitizeEvidenceSnapshot(value) {
11
- if (!value || typeof value !== 'object' || Array.isArray(value) || value.evidenceSnapshotV !== 1) throw new Error('invalid evidence snapshot')
12
- const sections = {
13
- architecture: sanitizeArchitecture(value.sections?.architecture),
14
- duplicates: sanitizeDuplicates(value.sections?.duplicates),
15
- health: sanitizeHealth(value.sections?.health),
16
- technologies: sanitizeTechnologies(value.sections?.technologies),
17
- packages: sanitizePackages(value.sections?.packages),
18
- }
19
- const states = Object.values(sections).map((section) => section.state)
20
- const snapshotState = states.every((item) => item === 'ERROR') ? 'ERROR' : states.every((item) => item === 'COMPLETE' || item === 'NOT_APPLICABLE') ? 'COMPLETE' : 'PARTIAL'
21
- const snapshot = {evidenceSnapshotV: 1, state: snapshotState, sections}
22
- return {...snapshot, snapshotHash: createHash('sha256').update(stableStringify(snapshot)).digest('hex')}
23
- }
@@ -1,70 +0,0 @@
1
- // Versioned, explicit source-free extension schema. Never forward graph.json wholesale.
2
- import {sanitizeEvidenceSnapshot} from './sync-evidence.mjs';
3
- import {
4
- MAX_SYNC_EXTERNAL_IMPORTS, MAX_SYNC_LINKS, MAX_SYNC_NODES, assertArrayLimit,
5
- } from './sync/payload-common.mjs';
6
- import {sanitizeExternalImport, sanitizeLink, sanitizeNode} from './sync/payload-v2.mjs';
7
- import {sanitizeExternalImportV3, sanitizeLinkV3, sanitizeNodeV3} from './sync/payload-v3.mjs';
8
-
9
- export {
10
- MAX_SYNC_BODY_BYTES, MAX_SYNC_EXTERNAL_IMPORTS, MAX_SYNC_LINKS, MAX_SYNC_NODES,
11
- } from './sync/payload-common.mjs';
12
-
13
- export function createSyncPayload(raw) {
14
- if (!raw || typeof raw !== 'object' || Array.isArray(raw) || raw.repoBoundaryV !== 1) {
15
- throw new Error('graph predates repository-boundary hardening');
16
- }
17
- if (!Number.isInteger(raw.edgeTypesV) || raw.edgeTypesV < 2) {
18
- throw new Error('graph predates compile-only edge metadata');
19
- }
20
- const nodes = Array.isArray(raw.nodes) ? raw.nodes.map(sanitizeNode).filter(Boolean) : [];
21
- const links = Array.isArray(raw.links) ? raw.links.map(sanitizeLink).filter(Boolean) : [];
22
- const externalImports = Array.isArray(raw.externalImports)
23
- ? raw.externalImports.map(sanitizeExternalImport).filter(Boolean) : [];
24
- return {
25
- syncPayloadV: 2,
26
- repoBoundaryV: 1,
27
- edgeTypesV: 2,
28
- extImportsV: Number.isInteger(raw.extImportsV) ? raw.extImportsV : 0,
29
- complexityV: Number.isInteger(raw.complexityV) ? raw.complexityV : 0,
30
- nodes,
31
- links,
32
- externalImports,
33
- };
34
- }
35
-
36
- export function createSyncPayloadV3(raw, evidence) {
37
- const base = createSyncPayload(raw);
38
- if (!Number.isInteger(raw.edgeProvenanceV) || raw.edgeProvenanceV < 1) {
39
- throw new Error('graph predates edge provenance metadata');
40
- }
41
- assertArrayLimit(raw, 'nodes', MAX_SYNC_NODES);
42
- assertArrayLimit(raw, 'links', MAX_SYNC_LINKS);
43
- assertArrayLimit(raw, 'externalImports', MAX_SYNC_EXTERNAL_IMPORTS);
44
- const nodes = (raw.nodes || []).map(sanitizeNodeV3).filter(Boolean);
45
- const nodeIds = new Set();
46
- for (const node of nodes) {
47
- if (nodeIds.has(node.id)) throw new Error(`duplicate node id in graph: ${node.id}`);
48
- nodeIds.add(node.id);
49
- }
50
- const links = (raw.links || []).map(sanitizeLinkV3).filter(Boolean);
51
- for (const link of links) {
52
- if (!nodeIds.has(link.source) || !nodeIds.has(link.target)) {
53
- throw new Error(`dangling link in graph: ${link.source} -> ${link.target}`);
54
- }
55
- }
56
- const externalImports = (raw.externalImports || []).map(sanitizeExternalImportV3).filter(Boolean);
57
- return {
58
- syncPayloadV: 3,
59
- repoBoundaryV: base.repoBoundaryV,
60
- edgeTypesV: base.edgeTypesV,
61
- edgeProvenanceV: Number.isInteger(raw.edgeProvenanceV) ? raw.edgeProvenanceV : 0,
62
- extImportsV: base.extImportsV,
63
- complexityV: base.complexityV,
64
- evidenceV: 1,
65
- nodes,
66
- links,
67
- externalImports,
68
- evidence: sanitizeEvidenceSnapshot(evidence),
69
- };
70
- }
@@ -1,54 +0,0 @@
1
- // Text mode stays genuinely compact for agents and older clients: it returns TextContent only.
2
- // JSON mode adds the stable machine-readable structuredContent envelope and mirrors that envelope
3
- // into TextContent for workflow runners that cannot consume structured results directly.
4
-
5
- const TOOL_RESULT_SCHEMA = 'weavatrix.tool.v1'
6
-
7
- export function toolResult(text, result, extra = {}) {
8
- return {
9
- __weavatrixToolResult: true,
10
- text: String(text ?? ''),
11
- result: result && typeof result === 'object' ? result : {},
12
- warnings: Array.isArray(extra.warnings) ? extra.warnings : [],
13
- page: extra.page && typeof extra.page === 'object' ? extra.page : undefined,
14
- completeness: extra.completeness && typeof extra.completeness === 'object' ? extra.completeness : undefined,
15
- }
16
- }
17
-
18
- function repoName(repoRoot) {
19
- const parts = String(repoRoot || '').replace(/[\\/]+$/, '').split(/[\\/]/)
20
- const name = parts.pop() || 'unknown'
21
- return name.normalize('NFKC').replace(/[^a-z0-9._-]+/gi, '-').replace(/^-+|-+$/g, '').slice(0, 128) || 'unknown'
22
- }
23
-
24
- export function normalizeToolResult({toolName, value, args, ctx, refresh, warnings = [], freshness = 'unknown'}) {
25
- const rich = value && typeof value === 'object' && value.__weavatrixToolResult === true
26
- const text = rich ? value.text : String(value ?? '')
27
- const resultWarnings = [...(rich ? value.warnings : []), ...warnings]
28
- if (refresh?.notice) resultWarnings.unshift({code: 'GRAPH_AUTO_REFRESHED', message: refresh.notice})
29
- if (refresh?.error) resultWarnings.unshift({code: 'GRAPH_AUTO_REFRESH_FAILED', message: refresh.error})
30
- const structured = {
31
- schemaVersion: TOOL_RESULT_SCHEMA,
32
- tool: String(toolName || ''),
33
- repo: {name: repoName(ctx?.repoRoot)},
34
- graph: {
35
- revision: refresh?.revision || null,
36
- freshness: refresh?.error ? 'stale' : (refresh?.kind ? 'fresh' : freshness),
37
- update: refresh?.kind || 'none',
38
- changedFiles: Number(refresh?.changedFiles) || 0,
39
- },
40
- result: rich ? value.result : {text},
41
- evidence: [],
42
- warnings: resultWarnings,
43
- page: rich ? (value.page || {}) : {},
44
- ...(rich && value.completeness ? {completeness: value.completeness} : {}),
45
- }
46
- const json = args?.output_format === 'json'
47
- return {
48
- text: json ? JSON.stringify(structured, null, 2) : `Repository: ${structured.repo.name}\n${text}`,
49
- // MCP output schemas apply to every invocation of a tool. The catalog intentionally does not
50
- // advertise one because text mode must not attach a second, potentially very large payload.
51
- // JSON callers still receive the stable structured result as an optional MCP extension.
52
- structured: json ? structured : undefined,
53
- }
54
- }
@@ -1,11 +0,0 @@
1
- // Propagate catalog.mjs's cache-busting query to owner modules so development hot reload remains
2
- // behaviorally identical to the former single-file implementation.
3
- const version = new URL(import.meta.url).search
4
- const load = (path) => import(new URL(`${path}${version}`, import.meta.url).href)
5
- const [lifecycle] = await Promise.all([
6
- load('./actions/graph-lifecycle.mjs'),
7
- ])
8
-
9
- export const tOpenRepo = lifecycle.tOpenRepo
10
- export const tListKnownRepos = lifecycle.tListKnownRepos
11
- export const tRebuildGraph = lifecycle.tRebuildGraph