@dataparade/cli 0.0.1

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 (621) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +592 -0
  3. package/dist/bin/cli.d.ts +2 -0
  4. package/dist/bin/cli.js +7 -0
  5. package/dist/bin/load-env.d.ts +2 -0
  6. package/dist/bin/load-env.js +4 -0
  7. package/dist/src/ai-enrichment/agent-orchestrator.d.ts +28 -0
  8. package/dist/src/ai-enrichment/agent-orchestrator.js +476 -0
  9. package/dist/src/ai-enrichment/agents/index.d.ts +4 -0
  10. package/dist/src/ai-enrichment/agents/index.js +50 -0
  11. package/dist/src/ai-enrichment/candidate-selector.d.ts +10 -0
  12. package/dist/src/ai-enrichment/candidate-selector.js +158 -0
  13. package/dist/src/ai-enrichment/fallbacks.d.ts +6 -0
  14. package/dist/src/ai-enrichment/fallbacks.js +762 -0
  15. package/dist/src/ai-enrichment/index.d.ts +15 -0
  16. package/dist/src/ai-enrichment/index.js +31 -0
  17. package/dist/src/ai-enrichment/merge-rules.d.ts +12 -0
  18. package/dist/src/ai-enrichment/merge-rules.js +321 -0
  19. package/dist/src/ai-enrichment/non-pii-signal-rules.d.ts +10 -0
  20. package/dist/src/ai-enrichment/non-pii-signal-rules.js +72 -0
  21. package/dist/src/ai-enrichment/openai-proposals-response.schema.d.ts +94 -0
  22. package/dist/src/ai-enrichment/openai-proposals-response.schema.js +82 -0
  23. package/dist/src/ai-enrichment/pii-signal-rules.d.ts +8 -0
  24. package/dist/src/ai-enrichment/pii-signal-rules.js +68 -0
  25. package/dist/src/ai-enrichment/pipeline.d.ts +2 -0
  26. package/dist/src/ai-enrichment/pipeline.js +48 -0
  27. package/dist/src/ai-enrichment/planner.d.ts +2 -0
  28. package/dist/src/ai-enrichment/planner.js +60 -0
  29. package/dist/src/ai-enrichment/prompts/provider-enrichment-prompts.d.ts +12 -0
  30. package/dist/src/ai-enrichment/prompts/provider-enrichment-prompts.js +45 -0
  31. package/dist/src/ai-enrichment/provider-prompt.d.ts +32 -0
  32. package/dist/src/ai-enrichment/provider-prompt.js +222 -0
  33. package/dist/src/ai-enrichment/provider-topology-rules.d.ts +52 -0
  34. package/dist/src/ai-enrichment/provider-topology-rules.js +190 -0
  35. package/dist/src/ai-enrichment/provider-topology-shared.d.ts +48 -0
  36. package/dist/src/ai-enrichment/provider-topology-shared.js +329 -0
  37. package/dist/src/ai-enrichment/providers/endpoint-resolution.d.ts +13 -0
  38. package/dist/src/ai-enrichment/providers/endpoint-resolution.js +41 -0
  39. package/dist/src/ai-enrichment/providers/families/chat-completions-family.d.ts +13 -0
  40. package/dist/src/ai-enrichment/providers/families/chat-completions-family.js +181 -0
  41. package/dist/src/ai-enrichment/providers/families/generate-content-family.d.ts +10 -0
  42. package/dist/src/ai-enrichment/providers/families/generate-content-family.js +93 -0
  43. package/dist/src/ai-enrichment/providers/families/messages-family.d.ts +10 -0
  44. package/dist/src/ai-enrichment/providers/families/messages-family.js +86 -0
  45. package/dist/src/ai-enrichment/providers/families/ollama-generate-family.d.ts +10 -0
  46. package/dist/src/ai-enrichment/providers/families/ollama-generate-family.js +69 -0
  47. package/dist/src/ai-enrichment/providers/fetch-with-timeout.d.ts +2 -0
  48. package/dist/src/ai-enrichment/providers/fetch-with-timeout.js +22 -0
  49. package/dist/src/ai-enrichment/providers/index.d.ts +16 -0
  50. package/dist/src/ai-enrichment/providers/index.js +40 -0
  51. package/dist/src/ai-enrichment/providers/mock.d.ts +5 -0
  52. package/dist/src/ai-enrichment/providers/mock.js +12 -0
  53. package/dist/src/ai-enrichment/providers/platform-proxy-provider.d.ts +20 -0
  54. package/dist/src/ai-enrichment/providers/platform-proxy-provider.js +116 -0
  55. package/dist/src/ai-enrichment/providers/presets.d.ts +14 -0
  56. package/dist/src/ai-enrichment/providers/presets.js +50 -0
  57. package/dist/src/ai-enrichment/providers/provider-contract.d.ts +20 -0
  58. package/dist/src/ai-enrichment/providers/provider-contract.js +703 -0
  59. package/dist/src/ai-enrichment/providers/provider-json-parse.d.ts +23 -0
  60. package/dist/src/ai-enrichment/providers/provider-json-parse.js +96 -0
  61. package/dist/src/ai-enrichment/providers/provider-output-token-budget.d.ts +5 -0
  62. package/dist/src/ai-enrichment/providers/provider-output-token-budget.js +11 -0
  63. package/dist/src/ai-enrichment/providers/provider-pricing.d.ts +7 -0
  64. package/dist/src/ai-enrichment/providers/provider-pricing.js +32 -0
  65. package/dist/src/ai-enrichment/providers/provider-runtime.d.ts +9 -0
  66. package/dist/src/ai-enrichment/providers/provider-runtime.js +54 -0
  67. package/dist/src/ai-enrichment/providers/resolve-provider.d.ts +27 -0
  68. package/dist/src/ai-enrichment/providers/resolve-provider.js +41 -0
  69. package/dist/src/ai-enrichment/providers/types.d.ts +24 -0
  70. package/dist/src/ai-enrichment/providers/types.js +2 -0
  71. package/dist/src/ai-enrichment/scan-paths.d.ts +9 -0
  72. package/dist/src/ai-enrichment/scan-paths.js +28 -0
  73. package/dist/src/ai-enrichment/sensitive-paths.d.ts +5 -0
  74. package/dist/src/ai-enrichment/sensitive-paths.js +10 -0
  75. package/dist/src/ai-enrichment/third-party-data-flow.d.ts +27 -0
  76. package/dist/src/ai-enrichment/third-party-data-flow.js +518 -0
  77. package/dist/src/ai-enrichment/third-party-evidence.d.ts +14 -0
  78. package/dist/src/ai-enrichment/third-party-evidence.js +426 -0
  79. package/dist/src/ai-enrichment/third-party-property-matrix.d.ts +14 -0
  80. package/dist/src/ai-enrichment/third-party-property-matrix.js +52 -0
  81. package/dist/src/ai-enrichment/third-party-subtype.d.ts +14 -0
  82. package/dist/src/ai-enrichment/third-party-subtype.js +166 -0
  83. package/dist/src/ai-enrichment/tools/index.d.ts +39 -0
  84. package/dist/src/ai-enrichment/tools/index.js +121 -0
  85. package/dist/src/ai-enrichment/types.d.ts +170 -0
  86. package/dist/src/ai-enrichment/types.js +12 -0
  87. package/dist/src/analyzers/python/dependency-manifests.d.ts +8 -0
  88. package/dist/src/analyzers/python/dependency-manifests.js +131 -0
  89. package/dist/src/analyzers/python/detector.d.ts +3 -0
  90. package/dist/src/analyzers/python/detector.js +22 -0
  91. package/dist/src/analyzers/python/index.d.ts +2 -0
  92. package/dist/src/analyzers/python/index.js +11 -0
  93. package/dist/src/analyzers/python/manifest-parsers.d.ts +3 -0
  94. package/dist/src/analyzers/python/manifest-parsers.js +134 -0
  95. package/dist/src/analyzers/python/parser.d.ts +26 -0
  96. package/dist/src/analyzers/python/parser.js +187 -0
  97. package/dist/src/analyzers/python/patterns.d.ts +7 -0
  98. package/dist/src/analyzers/python/patterns.js +106 -0
  99. package/dist/src/analyzers/python/python-detection-config.d.ts +75 -0
  100. package/dist/src/analyzers/python/python-detection-config.js +148 -0
  101. package/dist/src/analyzers/registry.d.ts +13 -0
  102. package/dist/src/analyzers/registry.js +60 -0
  103. package/dist/src/analyzers/shared/manifest-budgets.d.ts +19 -0
  104. package/dist/src/analyzers/shared/manifest-budgets.js +21 -0
  105. package/dist/src/analyzers/shared/manifest-fs.d.ts +13 -0
  106. package/dist/src/analyzers/shared/manifest-fs.js +138 -0
  107. package/dist/src/analyzers/shared/property-inference.d.ts +8 -0
  108. package/dist/src/analyzers/shared/property-inference.js +165 -0
  109. package/dist/src/analyzers/terraform/detector.d.ts +10 -0
  110. package/dist/src/analyzers/terraform/detector.js +198 -0
  111. package/dist/src/analyzers/terraform/index.d.ts +2 -0
  112. package/dist/src/analyzers/terraform/index.js +13 -0
  113. package/dist/src/analyzers/terraform/parser.d.ts +29 -0
  114. package/dist/src/analyzers/terraform/parser.js +202 -0
  115. package/dist/src/analyzers/terraform/terraform-detection-config.d.ts +48 -0
  116. package/dist/src/analyzers/terraform/terraform-detection-config.js +218 -0
  117. package/dist/src/analyzers/terraform/terraform-exec.d.ts +11 -0
  118. package/dist/src/analyzers/terraform/terraform-exec.js +35 -0
  119. package/dist/src/analyzers/terraform/terraform-module-manifest.d.ts +19 -0
  120. package/dist/src/analyzers/terraform/terraform-module-manifest.js +75 -0
  121. package/dist/src/analyzers/terraform/terraform-show-json.d.ts +15 -0
  122. package/dist/src/analyzers/terraform/terraform-show-json.js +188 -0
  123. package/dist/src/analyzers/terraform/terraform-utility-resource.d.ts +12 -0
  124. package/dist/src/analyzers/terraform/terraform-utility-resource.js +30 -0
  125. package/dist/src/analyzers/types.d.ts +5 -0
  126. package/dist/src/analyzers/types.js +2 -0
  127. package/dist/src/analyzers/typescript/actor-detection.d.ts +4 -0
  128. package/dist/src/analyzers/typescript/actor-detection.js +15 -0
  129. package/dist/src/analyzers/typescript/dependency-manifests.d.ts +9 -0
  130. package/dist/src/analyzers/typescript/dependency-manifests.js +131 -0
  131. package/dist/src/analyzers/typescript/detector.d.ts +3 -0
  132. package/dist/src/analyzers/typescript/detector.js +23 -0
  133. package/dist/src/analyzers/typescript/index.d.ts +2 -0
  134. package/dist/src/analyzers/typescript/index.js +11 -0
  135. package/dist/src/analyzers/typescript/manifest-parsers.d.ts +1 -0
  136. package/dist/src/analyzers/typescript/manifest-parsers.js +30 -0
  137. package/dist/src/analyzers/typescript/parser.d.ts +44 -0
  138. package/dist/src/analyzers/typescript/parser.js +287 -0
  139. package/dist/src/analyzers/typescript/third-party-detection.d.ts +4 -0
  140. package/dist/src/analyzers/typescript/third-party-detection.js +16 -0
  141. package/dist/src/analyzers/typescript/typescript-detection-config.d.ts +68 -0
  142. package/dist/src/analyzers/typescript/typescript-detection-config.js +171 -0
  143. package/dist/src/analyzers/typescript/typescript-detection.d.ts +10 -0
  144. package/dist/src/analyzers/typescript/typescript-detection.js +57 -0
  145. package/dist/src/classifier/application-injection.d.ts +12 -0
  146. package/dist/src/classifier/application-injection.js +335 -0
  147. package/dist/src/classifier/classify.d.ts +3 -0
  148. package/dist/src/classifier/classify.js +15 -0
  149. package/dist/src/classifier/component-factory.d.ts +3 -0
  150. package/dist/src/classifier/component-factory.js +488 -0
  151. package/dist/src/classifier/config.d.ts +38 -0
  152. package/dist/src/classifier/config.js +260 -0
  153. package/dist/src/classifier/enhance-defaults.d.ts +28 -0
  154. package/dist/src/classifier/enhance-defaults.js +369 -0
  155. package/dist/src/classifier/enhance.d.ts +13 -0
  156. package/dist/src/classifier/enhance.js +295 -0
  157. package/dist/src/classifier/external-url-third-party.d.ts +26 -0
  158. package/dist/src/classifier/external-url-third-party.js +195 -0
  159. package/dist/src/classifier/grouping.d.ts +1 -0
  160. package/dist/src/classifier/grouping.js +2 -0
  161. package/dist/src/classifier/main-application-selection.d.ts +41 -0
  162. package/dist/src/classifier/main-application-selection.js +222 -0
  163. package/dist/src/classifier/naming.d.ts +4 -0
  164. package/dist/src/classifier/naming.js +78 -0
  165. package/dist/src/classifier/postprocessing.d.ts +12 -0
  166. package/dist/src/classifier/postprocessing.js +510 -0
  167. package/dist/src/classifier/sectioning.d.ts +5 -0
  168. package/dist/src/classifier/sectioning.js +60 -0
  169. package/dist/src/classifier/terraform-provider-hub.d.ts +6 -0
  170. package/dist/src/classifier/terraform-provider-hub.js +38 -0
  171. package/dist/src/cli.d.ts +2 -0
  172. package/dist/src/cli.js +498 -0
  173. package/dist/src/config/actor-detection-config.d.ts +21 -0
  174. package/dist/src/config/actor-detection-config.js +97 -0
  175. package/dist/src/config/env.d.ts +2 -0
  176. package/dist/src/config/env.js +75 -0
  177. package/dist/src/config/file.d.ts +6 -0
  178. package/dist/src/config/file.js +74 -0
  179. package/dist/src/config/inference-scope.d.ts +2 -0
  180. package/dist/src/config/inference-scope.js +15 -0
  181. package/dist/src/config/load-cli-env.d.ts +1 -0
  182. package/dist/src/config/load-cli-env.js +54 -0
  183. package/dist/src/config/normalize-ai-provider.d.ts +5 -0
  184. package/dist/src/config/normalize-ai-provider.js +18 -0
  185. package/dist/src/config/property-detection-config.d.ts +27 -0
  186. package/dist/src/config/property-detection-config.js +355 -0
  187. package/dist/src/config/redact.d.ts +3 -0
  188. package/dist/src/config/redact.js +14 -0
  189. package/dist/src/config/resolve.d.ts +15 -0
  190. package/dist/src/config/resolve.js +361 -0
  191. package/dist/src/config/scan-env.d.ts +7 -0
  192. package/dist/src/config/scan-env.js +38 -0
  193. package/dist/src/config/third-party-detection-config.d.ts +25 -0
  194. package/dist/src/config/third-party-detection-config.js +97 -0
  195. package/dist/src/config/types.d.ts +106 -0
  196. package/dist/src/config/types.js +2 -0
  197. package/dist/src/config/validate-scan-ai.d.ts +3 -0
  198. package/dist/src/config/validate-scan-ai.js +33 -0
  199. package/dist/src/core/pipeline/ai-orchestrator-options.d.ts +6 -0
  200. package/dist/src/core/pipeline/ai-orchestrator-options.js +46 -0
  201. package/dist/src/core/pipeline/classifier-phase.d.ts +7 -0
  202. package/dist/src/core/pipeline/classifier-phase.js +78 -0
  203. package/dist/src/core/pipeline/dataflow-phase.d.ts +7 -0
  204. package/dist/src/core/pipeline/dataflow-phase.js +10 -0
  205. package/dist/src/core/pipeline/diagram-layout/app-section-layout.d.ts +7 -0
  206. package/dist/src/core/pipeline/diagram-layout/app-section-layout.js +74 -0
  207. package/dist/src/core/pipeline/diagram-layout/constants.d.ts +11 -0
  208. package/dist/src/core/pipeline/diagram-layout/constants.js +14 -0
  209. package/dist/src/core/pipeline/diagram-layout/edge-handles.d.ts +3 -0
  210. package/dist/src/core/pipeline/diagram-layout/edge-handles.js +26 -0
  211. package/dist/src/core/pipeline/diagram-layout/managed-provider-layout.d.ts +3 -0
  212. package/dist/src/core/pipeline/diagram-layout/managed-provider-layout.js +40 -0
  213. package/dist/src/core/pipeline/diagram-layout/minimal-terraform-layout.d.ts +6 -0
  214. package/dist/src/core/pipeline/diagram-layout/minimal-terraform-layout.js +53 -0
  215. package/dist/src/core/pipeline/diagram-layout/section-helpers.d.ts +30 -0
  216. package/dist/src/core/pipeline/diagram-layout/section-helpers.js +62 -0
  217. package/dist/src/core/pipeline/diagram-layout/terraform-lane-layout.d.ts +3 -0
  218. package/dist/src/core/pipeline/diagram-layout/terraform-lane-layout.js +109 -0
  219. package/dist/src/core/pipeline/diagram-layout/types.d.ts +2 -0
  220. package/dist/src/core/pipeline/diagram-layout/types.js +2 -0
  221. package/dist/src/core/pipeline/graph-mapping.d.ts +6 -0
  222. package/dist/src/core/pipeline/graph-mapping.js +308 -0
  223. package/dist/src/core/pipeline/mixed-app-terraform-scan.d.ts +13 -0
  224. package/dist/src/core/pipeline/mixed-app-terraform-scan.js +81 -0
  225. package/dist/src/core/pipeline/orchestrator-result.d.ts +6 -0
  226. package/dist/src/core/pipeline/orchestrator-result.js +2 -0
  227. package/dist/src/core/pipeline/orchestrator.d.ts +26 -0
  228. package/dist/src/core/pipeline/orchestrator.js +79 -0
  229. package/dist/src/core/pipeline/resolve-path-under-scan-root.d.ts +7 -0
  230. package/dist/src/core/pipeline/resolve-path-under-scan-root.js +55 -0
  231. package/dist/src/core/pipeline/scan-pipeline.d.ts +6 -0
  232. package/dist/src/core/pipeline/scan-pipeline.js +364 -0
  233. package/dist/src/core/pipeline/sorting.d.ts +3 -0
  234. package/dist/src/core/pipeline/sorting.js +48 -0
  235. package/dist/src/core/pipeline/stable-component-ids.d.ts +15 -0
  236. package/dist/src/core/pipeline/stable-component-ids.js +68 -0
  237. package/dist/src/core/pipeline/structural-scan.d.ts +19 -0
  238. package/dist/src/core/pipeline/structural-scan.js +304 -0
  239. package/dist/src/core/pipeline/terraform-flow-utils.d.ts +6 -0
  240. package/dist/src/core/pipeline/terraform-flow-utils.js +32 -0
  241. package/dist/src/core/pipeline/terraform-minimal-services.d.ts +31 -0
  242. package/dist/src/core/pipeline/terraform-minimal-services.js +225 -0
  243. package/dist/src/core/schema/dataflow-wrapper.schema.d.ts +83 -0
  244. package/dist/src/core/schema/dataflow-wrapper.schema.js +40 -0
  245. package/dist/src/core/schema/diagram-graph.schema.d.ts +93 -0
  246. package/dist/src/core/schema/diagram-graph.schema.js +55 -0
  247. package/dist/src/core/schema/index.d.ts +4 -0
  248. package/dist/src/core/schema/index.js +20 -0
  249. package/dist/src/core/schema/scan-config.schema.d.ts +66 -0
  250. package/dist/src/core/schema/scan-config.schema.js +67 -0
  251. package/dist/src/core/schema/scan-result.schema.d.ts +450 -0
  252. package/dist/src/core/schema/scan-result.schema.js +236 -0
  253. package/dist/src/core/sectioning/discover-service-sections.d.ts +80 -0
  254. package/dist/src/core/sectioning/discover-service-sections.js +468 -0
  255. package/dist/src/core/sectioning/section-runtime.d.ts +21 -0
  256. package/dist/src/core/sectioning/section-runtime.js +148 -0
  257. package/dist/src/core/terraform/component-predicates.d.ts +13 -0
  258. package/dist/src/core/terraform/component-predicates.js +71 -0
  259. package/dist/src/core/types/component.d.ts +18 -0
  260. package/dist/src/core/types/component.js +2 -0
  261. package/dist/src/core/types/config.d.ts +57 -0
  262. package/dist/src/core/types/config.js +2 -0
  263. package/dist/src/core/types/data-flow.d.ts +24 -0
  264. package/dist/src/core/types/data-flow.js +2 -0
  265. package/dist/src/core/types/detection.d.ts +12 -0
  266. package/dist/src/core/types/detection.js +19 -0
  267. package/dist/src/core/types/file.d.ts +14 -0
  268. package/dist/src/core/types/file.js +2 -0
  269. package/dist/src/core/types/index.d.ts +7 -0
  270. package/dist/src/core/types/index.js +23 -0
  271. package/dist/src/core/types/progress.d.ts +9 -0
  272. package/dist/src/core/types/progress.js +2 -0
  273. package/dist/src/core/types/result.d.ts +140 -0
  274. package/dist/src/core/types/result.js +2 -0
  275. package/dist/src/data-flow/application-hub.d.ts +7 -0
  276. package/dist/src/data-flow/application-hub.js +88 -0
  277. package/dist/src/data-flow/dedupe.d.ts +7 -0
  278. package/dist/src/data-flow/dedupe.js +92 -0
  279. package/dist/src/data-flow/detect.d.ts +16 -0
  280. package/dist/src/data-flow/detect.js +203 -0
  281. package/dist/src/data-flow/detector.d.ts +15 -0
  282. package/dist/src/data-flow/detector.js +18 -0
  283. package/dist/src/data-flow/drop-cross-section-flows.d.ts +8 -0
  284. package/dist/src/data-flow/drop-cross-section-flows.js +32 -0
  285. package/dist/src/data-flow/ensure-actor-flow.d.ts +16 -0
  286. package/dist/src/data-flow/ensure-actor-flow.js +101 -0
  287. package/dist/src/data-flow/ensure-manifest-declared-flows.d.ts +14 -0
  288. package/dist/src/data-flow/ensure-manifest-declared-flows.js +76 -0
  289. package/dist/src/data-flow/ensure-section-api-flows.d.ts +6 -0
  290. package/dist/src/data-flow/ensure-section-api-flows.js +57 -0
  291. package/dist/src/data-flow/flow-builder.d.ts +5 -0
  292. package/dist/src/data-flow/flow-builder.js +80 -0
  293. package/dist/src/data-flow/import-graph.d.ts +10 -0
  294. package/dist/src/data-flow/import-graph.js +147 -0
  295. package/dist/src/data-flow/index.d.ts +8 -0
  296. package/dist/src/data-flow/index.js +20 -0
  297. package/dist/src/data-flow/internal-fetch.d.ts +8 -0
  298. package/dist/src/data-flow/internal-fetch.js +209 -0
  299. package/dist/src/data-flow/postprocess.d.ts +9 -0
  300. package/dist/src/data-flow/postprocess.js +63 -0
  301. package/dist/src/data-flow/rewire.d.ts +10 -0
  302. package/dist/src/data-flow/rewire.js +93 -0
  303. package/dist/src/data-flow/source-attribution.d.ts +8 -0
  304. package/dist/src/data-flow/source-attribution.js +131 -0
  305. package/dist/src/data-flow/source-resolution.d.ts +13 -0
  306. package/dist/src/data-flow/source-resolution.js +61 -0
  307. package/dist/src/data-flow/target-matching.d.ts +21 -0
  308. package/dist/src/data-flow/target-matching.js +253 -0
  309. package/dist/src/data-flow/terraform-flows.d.ts +32 -0
  310. package/dist/src/data-flow/terraform-flows.js +170 -0
  311. package/dist/src/evals/metrics.d.ts +18 -0
  312. package/dist/src/evals/metrics.js +58 -0
  313. package/dist/src/evals/run-model-matrix.d.ts +1 -0
  314. package/dist/src/evals/run-model-matrix.js +68 -0
  315. package/dist/src/index.d.ts +2 -0
  316. package/dist/src/index.js +20 -0
  317. package/dist/src/ingest/file-system.d.ts +17 -0
  318. package/dist/src/ingest/file-system.js +239 -0
  319. package/dist/src/ingest/gitignore.d.ts +12 -0
  320. package/dist/src/ingest/gitignore.js +127 -0
  321. package/dist/src/ingest/sensitive-paths.d.ts +2 -0
  322. package/dist/src/ingest/sensitive-paths.js +8 -0
  323. package/dist/src/observability/scan-sentry.d.ts +20 -0
  324. package/dist/src/observability/scan-sentry.js +133 -0
  325. package/dist/src/output/json.d.ts +51 -0
  326. package/dist/src/output/json.js +59 -0
  327. package/dist/src/patterns/config.d.ts +22 -0
  328. package/dist/src/patterns/config.js +119 -0
  329. package/dist/src/patterns/detectors/helpers.d.ts +19 -0
  330. package/dist/src/patterns/detectors/helpers.js +68 -0
  331. package/dist/src/patterns/detectors/python.d.ts +8 -0
  332. package/dist/src/patterns/detectors/python.js +412 -0
  333. package/dist/src/patterns/detectors/typescript.d.ts +8 -0
  334. package/dist/src/patterns/detectors/typescript.js +328 -0
  335. package/dist/src/patterns/engine.d.ts +40 -0
  336. package/dist/src/patterns/engine.js +111 -0
  337. package/dist/src/patterns/flow-source-patterns.d.ts +6 -0
  338. package/dist/src/patterns/flow-source-patterns.js +39 -0
  339. package/dist/src/patterns/frontend-frameworks.d.ts +4 -0
  340. package/dist/src/patterns/frontend-frameworks.js +25 -0
  341. package/dist/src/patterns/scan-exclusions.d.ts +14 -0
  342. package/dist/src/patterns/scan-exclusions.js +76 -0
  343. package/dist/src/platform-api/dataparade-api-base-url.d.ts +8 -0
  344. package/dist/src/platform-api/dataparade-api-base-url.js +17 -0
  345. package/dist/src/platform-api/estimate-scan-footprint.d.ts +8 -0
  346. package/dist/src/platform-api/estimate-scan-footprint.js +120 -0
  347. package/dist/src/platform-api/scan-quota-client.d.ts +28 -0
  348. package/dist/src/platform-api/scan-quota-client.js +79 -0
  349. package/dist/src/tracing/langsmith-tracing.d.ts +37 -0
  350. package/dist/src/tracing/langsmith-tracing.js +171 -0
  351. package/dist/tests/cli/cli.spec.d.ts +1 -0
  352. package/dist/tests/cli/cli.spec.js +87 -0
  353. package/dist/tests/fixtures/e2e-ts-sample/db-client-import.d.ts +2 -0
  354. package/dist/tests/fixtures/e2e-ts-sample/db-client-import.js +8 -0
  355. package/dist/tests/fixtures/e2e-ts-sample/db.d.ts +3 -0
  356. package/dist/tests/fixtures/e2e-ts-sample/db.js +7 -0
  357. package/dist/tests/fixtures/e2e-ts-sample/external-api.d.ts +1 -0
  358. package/dist/tests/fixtures/e2e-ts-sample/external-api.js +18 -0
  359. package/dist/tests/fixtures/e2e-ts-sample/pg-client.d.ts +3 -0
  360. package/dist/tests/fixtures/e2e-ts-sample/pg-client.js +7 -0
  361. package/dist/tests/fixtures/e2e-ts-sample/server.d.ts +14 -0
  362. package/dist/tests/fixtures/e2e-ts-sample/server.js +4 -0
  363. package/dist/tests/fixtures/e2e-ts-sentry-route-env/server.d.ts +1 -0
  364. package/dist/tests/fixtures/e2e-ts-sentry-route-env/server.js +24 -0
  365. package/dist/tests/fixtures/ingest-basic/src/index.d.ts +1 -0
  366. package/dist/tests/fixtures/ingest-basic/src/index.js +6 -0
  367. package/dist/tests/fixtures/mixed-language-basic/api.d.ts +2 -0
  368. package/dist/tests/fixtures/mixed-language-basic/api.js +12 -0
  369. package/dist/tests/fixtures/monorepo-app-plus-terraform/packages/app/app/route.d.ts +1 -0
  370. package/dist/tests/fixtures/monorepo-app-plus-terraform/packages/app/app/route.js +6 -0
  371. package/dist/tests/fixtures/monorepo-front-back-sections/backend/app/route.d.ts +1 -0
  372. package/dist/tests/fixtures/monorepo-front-back-sections/backend/app/route.js +6 -0
  373. package/dist/tests/fixtures/monorepo-front-back-sections/frontend/app/route.d.ts +1 -0
  374. package/dist/tests/fixtures/monorepo-front-back-sections/frontend/app/route.js +7 -0
  375. package/dist/tests/fixtures/typescript-basic/app/route.d.ts +1 -0
  376. package/dist/tests/fixtures/typescript-basic/app/route.js +7 -0
  377. package/dist/tests/fixtures/typescript-basic/db-client-import.d.ts +2 -0
  378. package/dist/tests/fixtures/typescript-basic/db-client-import.js +8 -0
  379. package/dist/tests/fixtures/typescript-basic/db.d.ts +3 -0
  380. package/dist/tests/fixtures/typescript-basic/db.js +7 -0
  381. package/dist/tests/fixtures/typescript-basic/external-api.d.ts +1 -0
  382. package/dist/tests/fixtures/typescript-basic/external-api.js +18 -0
  383. package/dist/tests/fixtures/typescript-basic/pg-client.d.ts +3 -0
  384. package/dist/tests/fixtures/typescript-basic/pg-client.js +7 -0
  385. package/dist/tests/fixtures/typescript-basic/server.d.ts +14 -0
  386. package/dist/tests/fixtures/typescript-basic/server.js +4 -0
  387. package/dist/tests/fixtures/typescript-dependency-manifests-basic/src/index.d.ts +0 -0
  388. package/dist/tests/fixtures/typescript-dependency-manifests-basic/src/index.js +2 -0
  389. package/dist/tests/helpers/scan-result-builders.d.ts +2 -0
  390. package/dist/tests/helpers/scan-result-builders.js +15 -0
  391. package/dist/tests/schema/dataflow-wrapper.schema.spec.d.ts +1 -0
  392. package/dist/tests/schema/dataflow-wrapper.schema.spec.js +89 -0
  393. package/dist/tests/schema/diagram-graph.schema.spec.d.ts +1 -0
  394. package/dist/tests/schema/diagram-graph.schema.spec.js +48 -0
  395. package/dist/tests/schema/scan-config.schema.spec.d.ts +1 -0
  396. package/dist/tests/schema/scan-config.schema.spec.js +135 -0
  397. package/dist/tests/schema/scan-result.schema.spec.d.ts +1 -0
  398. package/dist/tests/schema/scan-result.schema.spec.js +177 -0
  399. package/dist/tests/unit/ai-enrichment/agent-orchestrator.spec.d.ts +1 -0
  400. package/dist/tests/unit/ai-enrichment/agent-orchestrator.spec.js +483 -0
  401. package/dist/tests/unit/ai-enrichment/candidate-selector.spec.d.ts +1 -0
  402. package/dist/tests/unit/ai-enrichment/candidate-selector.spec.js +106 -0
  403. package/dist/tests/unit/ai-enrichment/create-ai-provider.spec.d.ts +1 -0
  404. package/dist/tests/unit/ai-enrichment/create-ai-provider.spec.js +32 -0
  405. package/dist/tests/unit/ai-enrichment/endpoint-resolution.spec.d.ts +1 -0
  406. package/dist/tests/unit/ai-enrichment/endpoint-resolution.spec.js +47 -0
  407. package/dist/tests/unit/ai-enrichment/fallbacks.spec.d.ts +1 -0
  408. package/dist/tests/unit/ai-enrichment/fallbacks.spec.js +1304 -0
  409. package/dist/tests/unit/ai-enrichment/families/chat-completions-family.spec.d.ts +1 -0
  410. package/dist/tests/unit/ai-enrichment/families/chat-completions-family.spec.js +219 -0
  411. package/dist/tests/unit/ai-enrichment/families/generate-content-family.spec.d.ts +1 -0
  412. package/dist/tests/unit/ai-enrichment/families/generate-content-family.spec.js +119 -0
  413. package/dist/tests/unit/ai-enrichment/families/messages-family.spec.d.ts +1 -0
  414. package/dist/tests/unit/ai-enrichment/families/messages-family.spec.js +129 -0
  415. package/dist/tests/unit/ai-enrichment/families/ollama-generate-family.spec.d.ts +1 -0
  416. package/dist/tests/unit/ai-enrichment/families/ollama-generate-family.spec.js +69 -0
  417. package/dist/tests/unit/ai-enrichment/merge-rules.spec.d.ts +1 -0
  418. package/dist/tests/unit/ai-enrichment/merge-rules.spec.js +422 -0
  419. package/dist/tests/unit/ai-enrichment/openai-proposals-response.schema.spec.d.ts +1 -0
  420. package/dist/tests/unit/ai-enrichment/openai-proposals-response.schema.spec.js +30 -0
  421. package/dist/tests/unit/ai-enrichment/pipeline.spec.d.ts +1 -0
  422. package/dist/tests/unit/ai-enrichment/pipeline.spec.js +120 -0
  423. package/dist/tests/unit/ai-enrichment/planner.spec.d.ts +1 -0
  424. package/dist/tests/unit/ai-enrichment/planner.spec.js +106 -0
  425. package/dist/tests/unit/ai-enrichment/platform-proxy-provider.spec.d.ts +1 -0
  426. package/dist/tests/unit/ai-enrichment/platform-proxy-provider.spec.js +51 -0
  427. package/dist/tests/unit/ai-enrichment/provider-contract.spec.d.ts +1 -0
  428. package/dist/tests/unit/ai-enrichment/provider-contract.spec.js +244 -0
  429. package/dist/tests/unit/ai-enrichment/provider-json-parse.spec.d.ts +1 -0
  430. package/dist/tests/unit/ai-enrichment/provider-json-parse.spec.js +24 -0
  431. package/dist/tests/unit/ai-enrichment/provider-output-token-budget.spec.d.ts +1 -0
  432. package/dist/tests/unit/ai-enrichment/provider-output-token-budget.spec.js +15 -0
  433. package/dist/tests/unit/ai-enrichment/provider-prompt-env.spec.d.ts +1 -0
  434. package/dist/tests/unit/ai-enrichment/provider-prompt-env.spec.js +54 -0
  435. package/dist/tests/unit/ai-enrichment/provider-prompt.spec.d.ts +1 -0
  436. package/dist/tests/unit/ai-enrichment/provider-prompt.spec.js +206 -0
  437. package/dist/tests/unit/ai-enrichment/provider-topology-terraform-aws.spec.d.ts +1 -0
  438. package/dist/tests/unit/ai-enrichment/provider-topology-terraform-aws.spec.js +43 -0
  439. package/dist/tests/unit/ai-enrichment/provider-topology-terraform-kubernetes.spec.d.ts +1 -0
  440. package/dist/tests/unit/ai-enrichment/provider-topology-terraform-kubernetes.spec.js +40 -0
  441. package/dist/tests/unit/ai-enrichment/providers/fetch-with-timeout.spec.d.ts +1 -0
  442. package/dist/tests/unit/ai-enrichment/providers/fetch-with-timeout.spec.js +22 -0
  443. package/dist/tests/unit/ai-enrichment/resolve-provider.spec.d.ts +1 -0
  444. package/dist/tests/unit/ai-enrichment/resolve-provider.spec.js +67 -0
  445. package/dist/tests/unit/ai-enrichment/scan-paths-exact.spec.d.ts +1 -0
  446. package/dist/tests/unit/ai-enrichment/scan-paths-exact.spec.js +35 -0
  447. package/dist/tests/unit/ai-enrichment/scan-paths.spec.d.ts +1 -0
  448. package/dist/tests/unit/ai-enrichment/scan-paths.spec.js +21 -0
  449. package/dist/tests/unit/ai-enrichment/sensitive-paths.spec.d.ts +1 -0
  450. package/dist/tests/unit/ai-enrichment/sensitive-paths.spec.js +20 -0
  451. package/dist/tests/unit/ai-enrichment/third-party-data-flow.spec.d.ts +1 -0
  452. package/dist/tests/unit/ai-enrichment/third-party-data-flow.spec.js +963 -0
  453. package/dist/tests/unit/ai-enrichment/third-party-evidence.spec.d.ts +1 -0
  454. package/dist/tests/unit/ai-enrichment/third-party-evidence.spec.js +117 -0
  455. package/dist/tests/unit/ai-enrichment/third-party-subtype.spec.d.ts +1 -0
  456. package/dist/tests/unit/ai-enrichment/third-party-subtype.spec.js +45 -0
  457. package/dist/tests/unit/ai-enrichment/tools-validation.spec.d.ts +1 -0
  458. package/dist/tests/unit/ai-enrichment/tools-validation.spec.js +66 -0
  459. package/dist/tests/unit/analyzers/python/parser.spec.d.ts +1 -0
  460. package/dist/tests/unit/analyzers/python/parser.spec.js +104 -0
  461. package/dist/tests/unit/analyzers/python/patterns.spec.d.ts +1 -0
  462. package/dist/tests/unit/analyzers/python/patterns.spec.js +289 -0
  463. package/dist/tests/unit/analyzers/registry.spec.d.ts +1 -0
  464. package/dist/tests/unit/analyzers/registry.spec.js +234 -0
  465. package/dist/tests/unit/analyzers/terraform/classifier-subtype-parity.spec.d.ts +1 -0
  466. package/dist/tests/unit/analyzers/terraform/classifier-subtype-parity.spec.js +87 -0
  467. package/dist/tests/unit/analyzers/terraform/detector.spec.d.ts +1 -0
  468. package/dist/tests/unit/analyzers/terraform/detector.spec.js +110 -0
  469. package/dist/tests/unit/analyzers/terraform/generated-provider-hints.spec.d.ts +1 -0
  470. package/dist/tests/unit/analyzers/terraform/generated-provider-hints.spec.js +53 -0
  471. package/dist/tests/unit/analyzers/terraform/parser.spec.d.ts +1 -0
  472. package/dist/tests/unit/analyzers/terraform/parser.spec.js +56 -0
  473. package/dist/tests/unit/analyzers/terraform/terraform-module-manifest.spec.d.ts +1 -0
  474. package/dist/tests/unit/analyzers/terraform/terraform-module-manifest.spec.js +63 -0
  475. package/dist/tests/unit/analyzers/terraform/terraform-utility-resource.spec.d.ts +1 -0
  476. package/dist/tests/unit/analyzers/terraform/terraform-utility-resource.spec.js +19 -0
  477. package/dist/tests/unit/analyzers/terraform-exec.spec.d.ts +1 -0
  478. package/dist/tests/unit/analyzers/terraform-exec.spec.js +27 -0
  479. package/dist/tests/unit/analyzers/terraform-show-json.spec.d.ts +1 -0
  480. package/dist/tests/unit/analyzers/terraform-show-json.spec.js +77 -0
  481. package/dist/tests/unit/analyzers/typescript/dependency-manifests.spec.d.ts +1 -0
  482. package/dist/tests/unit/analyzers/typescript/dependency-manifests.spec.js +61 -0
  483. package/dist/tests/unit/analyzers/typescript/detector.spec.d.ts +1 -0
  484. package/dist/tests/unit/analyzers/typescript/detector.spec.js +225 -0
  485. package/dist/tests/unit/analyzers/typescript/external-apis-auth0.spec.d.ts +1 -0
  486. package/dist/tests/unit/analyzers/typescript/external-apis-auth0.spec.js +49 -0
  487. package/dist/tests/unit/analyzers/typescript/external-apis-sentry.spec.d.ts +1 -0
  488. package/dist/tests/unit/analyzers/typescript/external-apis-sentry.spec.js +27 -0
  489. package/dist/tests/unit/analyzers/typescript/fixture-integration.spec.d.ts +1 -0
  490. package/dist/tests/unit/analyzers/typescript/fixture-integration.spec.js +64 -0
  491. package/dist/tests/unit/analyzers/typescript/parser.spec.d.ts +1 -0
  492. package/dist/tests/unit/analyzers/typescript/parser.spec.js +99 -0
  493. package/dist/tests/unit/analyzers/typescript/pattern-config.spec.d.ts +1 -0
  494. package/dist/tests/unit/analyzers/typescript/pattern-config.spec.js +24 -0
  495. package/dist/tests/unit/analyzers/typescript/property-detection.spec.d.ts +1 -0
  496. package/dist/tests/unit/analyzers/typescript/property-detection.spec.js +138 -0
  497. package/dist/tests/unit/classifier/classify.spec.d.ts +1 -0
  498. package/dist/tests/unit/classifier/classify.spec.js +1453 -0
  499. package/dist/tests/unit/classifier/enhance.spec.d.ts +1 -0
  500. package/dist/tests/unit/classifier/enhance.spec.js +400 -0
  501. package/dist/tests/unit/classifier/external-url-third-party.spec.d.ts +1 -0
  502. package/dist/tests/unit/classifier/external-url-third-party.spec.js +83 -0
  503. package/dist/tests/unit/classifier/frontend-framework-grouping.spec.d.ts +1 -0
  504. package/dist/tests/unit/classifier/frontend-framework-grouping.spec.js +44 -0
  505. package/dist/tests/unit/classifier/terraform-grouping.spec.d.ts +1 -0
  506. package/dist/tests/unit/classifier/terraform-grouping.spec.js +96 -0
  507. package/dist/tests/unit/cli/config-command.spec.d.ts +1 -0
  508. package/dist/tests/unit/cli/config-command.spec.js +51 -0
  509. package/dist/tests/unit/cli/scan-command-ai-flags.spec.d.ts +0 -0
  510. package/dist/tests/unit/cli/scan-command-ai-flags.spec.js +149 -0
  511. package/dist/tests/unit/cli/scan-command-diagram-graph.spec.d.ts +1 -0
  512. package/dist/tests/unit/cli/scan-command-diagram-graph.spec.js +26 -0
  513. package/dist/tests/unit/cli/scan-command-e2e-monorepo-app-plus-terraform.spec.d.ts +1 -0
  514. package/dist/tests/unit/cli/scan-command-e2e-monorepo-app-plus-terraform.spec.js +107 -0
  515. package/dist/tests/unit/cli/scan-command-e2e-monorepo-front-back-sections.spec.d.ts +1 -0
  516. package/dist/tests/unit/cli/scan-command-e2e-monorepo-front-back-sections.spec.js +74 -0
  517. package/dist/tests/unit/cli/scan-command-e2e-python-basic.spec.d.ts +1 -0
  518. package/dist/tests/unit/cli/scan-command-e2e-python-basic.spec.js +43 -0
  519. package/dist/tests/unit/cli/scan-command-e2e-terraform-basic.spec.d.ts +1 -0
  520. package/dist/tests/unit/cli/scan-command-e2e-terraform-basic.spec.js +62 -0
  521. package/dist/tests/unit/cli/scan-command-e2e-ts-sample.spec.d.ts +1 -0
  522. package/dist/tests/unit/cli/scan-command-e2e-ts-sample.spec.js +41 -0
  523. package/dist/tests/unit/cli/scan-command-e2e-ts-sentry-route-env.spec.d.ts +1 -0
  524. package/dist/tests/unit/cli/scan-command-e2e-ts-sentry-route-env.spec.js +85 -0
  525. package/dist/tests/unit/cli/scan-command-exit-code.spec.d.ts +1 -0
  526. package/dist/tests/unit/cli/scan-command-exit-code.spec.js +131 -0
  527. package/dist/tests/unit/cli/scan-quota-flow.spec.d.ts +1 -0
  528. package/dist/tests/unit/cli/scan-quota-flow.spec.js +76 -0
  529. package/dist/tests/unit/config/property-detection-config.spec.d.ts +1 -0
  530. package/dist/tests/unit/config/property-detection-config.spec.js +38 -0
  531. package/dist/tests/unit/config/redact.spec.d.ts +1 -0
  532. package/dist/tests/unit/config/redact.spec.js +19 -0
  533. package/dist/tests/unit/config/resolve-config.spec.d.ts +1 -0
  534. package/dist/tests/unit/config/resolve-config.spec.js +172 -0
  535. package/dist/tests/unit/config/scan-env-debug.spec.d.ts +1 -0
  536. package/dist/tests/unit/config/scan-env-debug.spec.js +17 -0
  537. package/dist/tests/unit/config/validate-scan-ai.spec.d.ts +1 -0
  538. package/dist/tests/unit/config/validate-scan-ai.spec.js +53 -0
  539. package/dist/tests/unit/core/classifier-phase-regressions.spec.d.ts +1 -0
  540. package/dist/tests/unit/core/classifier-phase-regressions.spec.js +68 -0
  541. package/dist/tests/unit/core/dependency-manifest-performance-budgets.spec.d.ts +1 -0
  542. package/dist/tests/unit/core/dependency-manifest-performance-budgets.spec.js +66 -0
  543. package/dist/tests/unit/core/dependency-manifest-toggles-confidence.spec.d.ts +1 -0
  544. package/dist/tests/unit/core/dependency-manifest-toggles-confidence.spec.js +132 -0
  545. package/dist/tests/unit/core/discover-service-sections.spec.d.ts +1 -0
  546. package/dist/tests/unit/core/discover-service-sections.spec.js +204 -0
  547. package/dist/tests/unit/core/graph-mapping.spec.d.ts +1 -0
  548. package/dist/tests/unit/core/graph-mapping.spec.js +779 -0
  549. package/dist/tests/unit/core/invariant-tests-dedupe-determinism.spec.d.ts +1 -0
  550. package/dist/tests/unit/core/invariant-tests-dedupe-determinism.spec.js +64 -0
  551. package/dist/tests/unit/core/manifest-package-hub.spec.d.ts +1 -0
  552. package/dist/tests/unit/core/manifest-package-hub.spec.js +53 -0
  553. package/dist/tests/unit/core/monorepo-package-sections.spec.d.ts +1 -0
  554. package/dist/tests/unit/core/monorepo-package-sections.spec.js +121 -0
  555. package/dist/tests/unit/core/no-source-code-leakage.spec.d.ts +1 -0
  556. package/dist/tests/unit/core/no-source-code-leakage.spec.js +48 -0
  557. package/dist/tests/unit/core/orchestrator.spec.d.ts +1 -0
  558. package/dist/tests/unit/core/orchestrator.spec.js +252 -0
  559. package/dist/tests/unit/core/output-json.spec.d.ts +1 -0
  560. package/dist/tests/unit/core/output-json.spec.js +65 -0
  561. package/dist/tests/unit/core/parsing-error-handling-nonfatal.spec.d.ts +1 -0
  562. package/dist/tests/unit/core/parsing-error-handling-nonfatal.spec.js +42 -0
  563. package/dist/tests/unit/core/resolve-path-under-scan-root.spec.d.ts +1 -0
  564. package/dist/tests/unit/core/resolve-path-under-scan-root.spec.js +54 -0
  565. package/dist/tests/unit/core/scan-result-language-stats.spec.d.ts +1 -0
  566. package/dist/tests/unit/core/scan-result-language-stats.spec.js +15 -0
  567. package/dist/tests/unit/core/section-discovery-excludes.spec.d.ts +1 -0
  568. package/dist/tests/unit/core/section-discovery-excludes.spec.js +49 -0
  569. package/dist/tests/unit/core/section-runtime.spec.d.ts +1 -0
  570. package/dist/tests/unit/core/section-runtime.spec.js +166 -0
  571. package/dist/tests/unit/core/stable-component-ids.spec.d.ts +1 -0
  572. package/dist/tests/unit/core/stable-component-ids.spec.js +112 -0
  573. package/dist/tests/unit/core/structural-scan.spec.d.ts +1 -0
  574. package/dist/tests/unit/core/structural-scan.spec.js +83 -0
  575. package/dist/tests/unit/core/terraform-json-overlay.spec.d.ts +1 -0
  576. package/dist/tests/unit/core/terraform-json-overlay.spec.js +36 -0
  577. package/dist/tests/unit/core/terraform-minimal-services.spec.d.ts +1 -0
  578. package/dist/tests/unit/core/terraform-minimal-services.spec.js +378 -0
  579. package/dist/tests/unit/data-flow/application-hub.spec.d.ts +1 -0
  580. package/dist/tests/unit/data-flow/application-hub.spec.js +48 -0
  581. package/dist/tests/unit/data-flow/cross-section-terraform-bridge.spec.d.ts +1 -0
  582. package/dist/tests/unit/data-flow/cross-section-terraform-bridge.spec.js +95 -0
  583. package/dist/tests/unit/data-flow/dedupe.spec.d.ts +1 -0
  584. package/dist/tests/unit/data-flow/dedupe.spec.js +134 -0
  585. package/dist/tests/unit/data-flow/detector.spec.d.ts +1 -0
  586. package/dist/tests/unit/data-flow/detector.spec.js +1254 -0
  587. package/dist/tests/unit/data-flow/drop-cross-section-flows.spec.d.ts +1 -0
  588. package/dist/tests/unit/data-flow/drop-cross-section-flows.spec.js +132 -0
  589. package/dist/tests/unit/data-flow/ensure-actor-flow.spec.d.ts +1 -0
  590. package/dist/tests/unit/data-flow/ensure-actor-flow.spec.js +233 -0
  591. package/dist/tests/unit/data-flow/ensure-manifest-declared-flows.spec.d.ts +1 -0
  592. package/dist/tests/unit/data-flow/ensure-manifest-declared-flows.spec.js +125 -0
  593. package/dist/tests/unit/data-flow/ensure-section-api-flows.spec.d.ts +1 -0
  594. package/dist/tests/unit/data-flow/ensure-section-api-flows.spec.js +40 -0
  595. package/dist/tests/unit/data-flow/postprocess.spec.d.ts +1 -0
  596. package/dist/tests/unit/data-flow/postprocess.spec.js +78 -0
  597. package/dist/tests/unit/data-flow/rewire.spec.d.ts +1 -0
  598. package/dist/tests/unit/data-flow/rewire.spec.js +194 -0
  599. package/dist/tests/unit/data-flow/terraform-flows.spec.d.ts +1 -0
  600. package/dist/tests/unit/data-flow/terraform-flows.spec.js +199 -0
  601. package/dist/tests/unit/evals/metrics.spec.d.ts +1 -0
  602. package/dist/tests/unit/evals/metrics.spec.js +54 -0
  603. package/dist/tests/unit/ingest/env-exclusion.spec.d.ts +1 -0
  604. package/dist/tests/unit/ingest/env-exclusion.spec.js +43 -0
  605. package/dist/tests/unit/ingest/file-system.spec.d.ts +1 -0
  606. package/dist/tests/unit/ingest/file-system.spec.js +180 -0
  607. package/dist/tests/unit/ingest/gitignore.spec.d.ts +1 -0
  608. package/dist/tests/unit/ingest/gitignore.spec.js +30 -0
  609. package/dist/tests/unit/observability/scan-sentry.spec.d.ts +1 -0
  610. package/dist/tests/unit/observability/scan-sentry.spec.js +60 -0
  611. package/dist/tests/unit/patterns/scan-exclusions.spec.d.ts +1 -0
  612. package/dist/tests/unit/patterns/scan-exclusions.spec.js +58 -0
  613. package/dist/tests/unit/pipeline/ai-orchestrator-options.spec.d.ts +1 -0
  614. package/dist/tests/unit/pipeline/ai-orchestrator-options.spec.js +19 -0
  615. package/dist/tests/unit/platform-api/dataparade-api-base-url.spec.d.ts +1 -0
  616. package/dist/tests/unit/platform-api/dataparade-api-base-url.spec.js +19 -0
  617. package/dist/tests/unit/platform-api/estimate-scan-footprint.spec.d.ts +1 -0
  618. package/dist/tests/unit/platform-api/estimate-scan-footprint.spec.js +50 -0
  619. package/dist/tests/unit/tracing/langsmith-tracing.spec.d.ts +1 -0
  620. package/dist/tests/unit/tracing/langsmith-tracing.spec.js +118 -0
  621. package/package.json +84 -0
@@ -0,0 +1,1453 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const classify_1 = require("../../../src/classifier/classify");
4
+ const config_1 = require("../../../src/classifier/config");
5
+ const enhance_1 = require("../../../src/classifier/enhance");
6
+ function makeLocation(overrides) {
7
+ return {
8
+ filePath: overrides?.filePath ?? "src/example.ts",
9
+ startLine: overrides?.startLine ?? 1,
10
+ endLine: overrides?.endLine ?? 1,
11
+ code: overrides?.code,
12
+ };
13
+ }
14
+ function makeFinding(overrides) {
15
+ return {
16
+ pattern: overrides.pattern,
17
+ name: overrides.name,
18
+ confidence: overrides.confidence ?? 0.9,
19
+ location: overrides.location ?? makeLocation({}),
20
+ properties: overrides.properties ?? {},
21
+ description: overrides.description,
22
+ };
23
+ }
24
+ function makeComponent(overrides) {
25
+ return {
26
+ id: overrides.id,
27
+ name: overrides.name,
28
+ type: overrides.type,
29
+ subType: overrides.subType,
30
+ confidence: overrides.confidence ?? 0.9,
31
+ detectedFrom: overrides.detectedFrom ?? [],
32
+ sourceLocations: overrides.sourceLocations ?? [],
33
+ properties: overrides.properties ?? {},
34
+ description: overrides.description,
35
+ dataFlowIds: overrides.dataFlowIds,
36
+ };
37
+ }
38
+ function makeDatabaseOnlyFindings() {
39
+ return [
40
+ makeFinding({
41
+ pattern: "database_connection",
42
+ name: "Primary Postgres",
43
+ properties: {
44
+ client: "pg",
45
+ databaseType: "postgres",
46
+ },
47
+ }),
48
+ makeFinding({
49
+ pattern: "database_connection",
50
+ name: "Reporting Postgres",
51
+ properties: {
52
+ client: "pg",
53
+ databaseType: "postgres",
54
+ },
55
+ }),
56
+ ];
57
+ }
58
+ function makeExternalApiHeavyFindings() {
59
+ return [
60
+ makeFinding({
61
+ pattern: "external_api_call",
62
+ name: "StripeClient",
63
+ properties: {
64
+ serviceName: "stripe",
65
+ client: "@stripe/stripe-js",
66
+ },
67
+ }),
68
+ makeFinding({
69
+ pattern: "external_api_call",
70
+ name: "StripeClient",
71
+ confidence: 0.85,
72
+ properties: {
73
+ serviceName: "stripe",
74
+ client: "stripe",
75
+ },
76
+ }),
77
+ makeFinding({
78
+ pattern: "external_api_call",
79
+ name: "SendGridClient",
80
+ properties: {
81
+ serviceName: "sendgrid",
82
+ },
83
+ }),
84
+ makeFinding({
85
+ pattern: "external_api_call",
86
+ name: "Auth0ApiClient",
87
+ properties: {
88
+ serviceName: "auth0",
89
+ },
90
+ }),
91
+ makeFinding({
92
+ pattern: "external_api_call",
93
+ name: "AwsSdkClient",
94
+ properties: {
95
+ serviceName: "aws",
96
+ client: "@aws-sdk/s3",
97
+ },
98
+ }),
99
+ ];
100
+ }
101
+ function makeMixedProjectFindings() {
102
+ return [
103
+ // No express_route so that injectApplicationAssetIfMissing adds a synthetic app.
104
+ makeFinding({
105
+ pattern: "database_connection",
106
+ name: "Orders DB",
107
+ properties: {
108
+ client: "pg",
109
+ databaseType: "postgres",
110
+ },
111
+ }),
112
+ makeFinding({
113
+ pattern: "external_api_call",
114
+ name: "StripePayments",
115
+ properties: {
116
+ serviceName: "stripe",
117
+ },
118
+ }),
119
+ // Web customer actor interacting with the app.
120
+ makeFinding({
121
+ pattern: "web_actor",
122
+ name: "Customer",
123
+ properties: {
124
+ actorType: "customer",
125
+ sourceContext: "frontend_session",
126
+ },
127
+ }),
128
+ // Service actor representing an internal admin.
129
+ makeFinding({
130
+ pattern: "service_actor",
131
+ name: "Admin user",
132
+ properties: {
133
+ actorType: "employee",
134
+ roleNames: ["admin"],
135
+ sourceContext: "backend_role_check",
136
+ },
137
+ }),
138
+ ];
139
+ }
140
+ describe("classifier/classify - DP-P0-CLI-201", () => {
141
+ beforeAll(() => {
142
+ // Ensure the classifier config can be loaded from YAML.
143
+ (0, config_1.clearClassifierConfigCacheForTest)();
144
+ const config = (0, config_1.loadClassifierConfig)();
145
+ expect(config.patternDefaults.length).toBeGreaterThan(0);
146
+ });
147
+ it("classifies database_connection findings as asset/database components using database_type_mapping", () => {
148
+ const findings = [
149
+ makeFinding({
150
+ pattern: "database_connection",
151
+ name: "pg",
152
+ properties: {
153
+ client: "pg",
154
+ databaseType: "postgres",
155
+ },
156
+ }),
157
+ makeFinding({
158
+ pattern: "database_connection",
159
+ name: "sql_query_detected",
160
+ confidence: 0.6,
161
+ properties: {
162
+ hint: "raw_sql_keyword",
163
+ },
164
+ }),
165
+ ];
166
+ const components = (0, classify_1.classifyRawFindings)(findings);
167
+ // Raw SQL helper findings (sql_query_detected) should not become
168
+ // standalone components; they only flag raw SQL usage. We still expect
169
+ // the primary database client component to be present.
170
+ const dbComponent = components.find((c) => c.properties.client === "pg");
171
+ const rawSqlComponent = components.find((c) => c.name === "Sql Query Detected");
172
+ expect(dbComponent).toBeDefined();
173
+ expect(dbComponent?.type).toBe("asset");
174
+ expect(dbComponent?.subType).toBe("database");
175
+ expect(dbComponent?.confidence).toBeGreaterThan(0);
176
+ expect(dbComponent?.detectedFrom.length).toBeGreaterThanOrEqual(1);
177
+ expect(dbComponent?.sourceLocations.length).toBeGreaterThanOrEqual(1);
178
+ expect(rawSqlComponent).toBeUndefined();
179
+ });
180
+ it("maps known external_api_call findings to third_party components using YAML-driven catalog", () => {
181
+ const findings = [
182
+ makeFinding({
183
+ pattern: "external_api_call",
184
+ name: "StripeClient",
185
+ properties: {
186
+ serviceName: "stripe",
187
+ },
188
+ }),
189
+ makeFinding({
190
+ pattern: "external_api_call",
191
+ name: "SendGridClient",
192
+ properties: {
193
+ serviceName: "sendgrid",
194
+ },
195
+ confidence: 0.8,
196
+ }),
197
+ makeFinding({
198
+ pattern: "external_api_call",
199
+ name: "BraintreeClient",
200
+ properties: {
201
+ serviceName: "braintree",
202
+ },
203
+ }),
204
+ makeFinding({
205
+ pattern: "external_api_call",
206
+ name: "AdyenClient",
207
+ properties: {
208
+ serviceName: "adyen",
209
+ },
210
+ }),
211
+ makeFinding({
212
+ pattern: "external_api_call",
213
+ name: "PayPalClient",
214
+ properties: {
215
+ serviceName: "paypal",
216
+ },
217
+ }),
218
+ ];
219
+ const components = (0, classify_1.classifyRawFindings)(findings);
220
+ // We expect at least several third-party components: Stripe, SendGrid, and multiple payment processors.
221
+ expect(components.length).toBeGreaterThanOrEqual(5);
222
+ const stripe = components.find((c) => c.name.toLowerCase().includes("stripe"));
223
+ const sendgrid = components.find((c) => c.name.toLowerCase().includes("sendgrid"));
224
+ const braintree = components.find((c) => c.name.toLowerCase().includes("braintree"));
225
+ const adyen = components.find((c) => c.name.toLowerCase().includes("adyen"));
226
+ const paypal = components.find((c) => c.name.toLowerCase().includes("paypal"));
227
+ expect(stripe).toBeDefined();
228
+ expect(stripe?.type).toBe("third_party");
229
+ expect(stripe?.subType).toBe("payment_processor");
230
+ expect(sendgrid).toBeDefined();
231
+ expect(sendgrid?.type).toBe("third_party");
232
+ expect(sendgrid?.subType).toBe("saas_service");
233
+ expect(braintree).toBeDefined();
234
+ expect(braintree?.type).toBe("third_party");
235
+ expect(braintree?.subType).toBe("payment_processor");
236
+ expect(adyen).toBeDefined();
237
+ expect(adyen?.type).toBe("third_party");
238
+ expect(adyen?.subType).toBe("payment_processor");
239
+ expect(paypal).toBeDefined();
240
+ expect(paypal?.type).toBe("third_party");
241
+ expect(paypal?.subType).toBe("payment_processor");
242
+ });
243
+ it("classifies multiple AI providers as third_party/ai_provider components", () => {
244
+ const findings = [
245
+ makeFinding({
246
+ pattern: "external_api_call",
247
+ name: "OpenAI SDK",
248
+ properties: {
249
+ serviceName: "openai",
250
+ },
251
+ }),
252
+ makeFinding({
253
+ pattern: "external_api_call",
254
+ name: "Anthropic SDK",
255
+ properties: {
256
+ serviceName: "anthropic",
257
+ },
258
+ }),
259
+ makeFinding({
260
+ pattern: "external_api_call",
261
+ name: "Google AI SDK",
262
+ properties: {
263
+ serviceName: "google_ai",
264
+ },
265
+ }),
266
+ ];
267
+ const components = (0, classify_1.classifyRawFindings)(findings);
268
+ const openai = components.find((c) => c.name.toLowerCase().includes("openai"));
269
+ const anthropic = components.find((c) => c.name.toLowerCase().includes("anthropic"));
270
+ const googleAi = components.find((c) => c.name.toLowerCase().includes("google ai"));
271
+ expect(openai).toBeDefined();
272
+ expect(openai?.type).toBe("third_party");
273
+ expect(openai?.subType).toBe("ai_provider");
274
+ expect(anthropic).toBeDefined();
275
+ expect(anthropic?.type).toBe("third_party");
276
+ expect(anthropic?.subType).toBe("ai_provider");
277
+ expect(googleAi).toBeDefined();
278
+ expect(googleAi?.type).toBe("third_party");
279
+ expect(googleAi?.subType).toBe("ai_provider");
280
+ });
281
+ it("classifies literal https external_api_call as third_party via hostname when not in catalog", () => {
282
+ const findings = [
283
+ makeFinding({
284
+ pattern: "external_api_call",
285
+ name: "https://api.weirdvendor.io/v1/foo",
286
+ confidence: 0.85,
287
+ properties: {
288
+ client: "fetch",
289
+ url: "https://api.weirdvendor.io/v1/foo",
290
+ },
291
+ }),
292
+ ];
293
+ const components = (0, classify_1.classifyRawFindings)(findings);
294
+ expect(components).toHaveLength(1);
295
+ expect(components[0].type).toBe("third_party");
296
+ expect(components[0].properties.serviceName).toBe("weirdvendor.io");
297
+ expect(components[0].subType).toBe("saas_service");
298
+ });
299
+ it("merges differing property values into arrays and aggregates confidence deterministically", () => {
300
+ const findings = [
301
+ makeFinding({
302
+ pattern: "external_api_call",
303
+ name: "OpenAI SDK",
304
+ confidence: 0.9,
305
+ properties: {
306
+ serviceName: "openai",
307
+ url: "https://api.openai.com/v1/chat/completions",
308
+ },
309
+ }),
310
+ makeFinding({
311
+ pattern: "external_api_call",
312
+ name: "OpenAI SDK",
313
+ confidence: 0.7,
314
+ properties: {
315
+ serviceName: "openai",
316
+ url: "https://api.openai.com/v1/embeddings",
317
+ },
318
+ }),
319
+ ];
320
+ const first = (0, classify_1.classifyRawFindings)(findings);
321
+ const second = (0, classify_1.classifyRawFindings)(findings);
322
+ expect(first).toHaveLength(1);
323
+ const [component] = first;
324
+ expect(component.type).toBe("third_party");
325
+ expect(component.subType).toBe("ai_provider");
326
+ const urlProp = component.properties.url;
327
+ expect(Array.isArray(urlProp)).toBe(true);
328
+ expect(urlProp).toContain("https://api.openai.com/v1/chat/completions");
329
+ expect(urlProp).toContain("https://api.openai.com/v1/embeddings");
330
+ // Determinism: running classification twice with the same input
331
+ // should yield the same shape (ignoring generated IDs).
332
+ expect(second).toHaveLength(1);
333
+ const again = second[0];
334
+ expect(again.type).toBe(component.type);
335
+ expect(again.subType).toBe(component.subType);
336
+ expect(again.name).toBe(component.name);
337
+ });
338
+ it("skips third-party components detected only from manifest metadata files", () => {
339
+ const findings = [
340
+ makeFinding({
341
+ pattern: "external_api_call",
342
+ name: "supabase",
343
+ location: makeLocation({
344
+ filePath: "backend/package.json",
345
+ startLine: 1,
346
+ endLine: 1,
347
+ }),
348
+ properties: {
349
+ serviceName: "supabase",
350
+ section_id: "backend",
351
+ section_label: "backend",
352
+ section_role: "service",
353
+ },
354
+ }),
355
+ ];
356
+ const components = (0, classify_1.classifyRawFindings)(findings);
357
+ expect(components).toHaveLength(0);
358
+ });
359
+ it("normalizes generic Route Handler API names to section-specific API label", () => {
360
+ const findings = [
361
+ makeFinding({
362
+ pattern: "express_route",
363
+ name: "Route Handler",
364
+ properties: {
365
+ section_id: "backend",
366
+ section_label: "Backend",
367
+ section_role: "service",
368
+ },
369
+ }),
370
+ ];
371
+ const components = (0, classify_1.classifyRawFindings)(findings);
372
+ expect(components).toHaveLength(1);
373
+ expect(components[0].type).toBe("asset");
374
+ expect(components[0].subType).toBe("api");
375
+ expect(components[0].name).toBe("Backend API");
376
+ });
377
+ it("synthesizes one explicit section API node when express_route evidence exists but no API asset exists", () => {
378
+ const routeFinding = makeFinding({
379
+ pattern: "express_route",
380
+ name: "Route Handler",
381
+ properties: {
382
+ section_id: "backend",
383
+ section_label: "Backend",
384
+ section_role: "service",
385
+ },
386
+ });
387
+ const withApiNode = (0, classify_1.synthesizeSectionApiNodes)([
388
+ {
389
+ id: "cmp_existing_service",
390
+ name: "Backend Service",
391
+ type: "asset",
392
+ subType: "service",
393
+ confidence: 0.9,
394
+ detectedFrom: [
395
+ {
396
+ pattern: routeFinding.pattern,
397
+ sourceLocation: routeFinding.location,
398
+ },
399
+ ],
400
+ sourceLocations: [routeFinding.location],
401
+ properties: {
402
+ section_id: "backend",
403
+ section_label: "Backend",
404
+ section_role: "service",
405
+ },
406
+ },
407
+ ]);
408
+ const sectionApiNodes = withApiNode.filter((c) => c.type === "asset" &&
409
+ c.subType === "api" &&
410
+ (c.properties.isSectionApiNode === true ||
411
+ c.properties.isSectionApiNode === "true"));
412
+ expect(sectionApiNodes).toHaveLength(1);
413
+ expect(sectionApiNodes[0]?.name).toBe("API");
414
+ expect(sectionApiNodes[0]?.properties.section_id).toBe("backend");
415
+ });
416
+ it("does not synthesize a duplicate section API node when a non-main concrete API already has route evidence", () => {
417
+ const findings = [
418
+ makeFinding({
419
+ pattern: "express_route",
420
+ name: "Route Handler",
421
+ properties: {
422
+ section_id: "backend",
423
+ section_label: "Backend",
424
+ section_role: "service",
425
+ },
426
+ }),
427
+ ];
428
+ const classified = (0, classify_1.classifyRawFindings)(findings);
429
+ const deduped = (0, classify_1.dedupeComponents)(classified);
430
+ const enhanced = (0, enhance_1.enhanceComponents)(deduped).map((c) => c.subType === "api"
431
+ ? {
432
+ ...c,
433
+ name: "Backend API",
434
+ properties: {
435
+ ...c.properties,
436
+ isMainApplication: false,
437
+ },
438
+ }
439
+ : c);
440
+ const withApiNode = (0, classify_1.synthesizeSectionApiNodes)(enhanced);
441
+ const apiAssets = withApiNode.filter((c) => c.type === "asset" &&
442
+ c.subType === "api" &&
443
+ c.properties.section_id === "backend");
444
+ expect(apiAssets).toHaveLength(1);
445
+ expect(apiAssets[0]?.name).toBe("Backend API");
446
+ expect(apiAssets[0]?.properties.isSectionApiNode).toBeUndefined();
447
+ });
448
+ it("synthesizes a section API node when the only section API is the main application", () => {
449
+ const findings = [
450
+ makeFinding({
451
+ pattern: "express_route",
452
+ name: "Route Handler",
453
+ properties: {
454
+ section_id: "frontend",
455
+ section_label: "frontend",
456
+ section_role: "service",
457
+ },
458
+ }),
459
+ ];
460
+ const classified = (0, classify_1.classifyRawFindings)(findings).map((c) => c.subType === "api"
461
+ ? {
462
+ ...c,
463
+ name: "frontend",
464
+ properties: {
465
+ ...c.properties,
466
+ isMainApplication: true,
467
+ },
468
+ }
469
+ : c);
470
+ const withApiNode = (0, classify_1.synthesizeSectionApiNodes)(classified);
471
+ const sectionApiNodes = withApiNode.filter((c) => c.type === "asset" &&
472
+ c.subType === "api" &&
473
+ c.properties.section_id === "frontend" &&
474
+ (c.properties.isSectionApiNode === true ||
475
+ c.properties.isSectionApiNode === "true"));
476
+ expect(sectionApiNodes).toHaveLength(1);
477
+ expect(sectionApiNodes[0]?.name).toBe("API");
478
+ });
479
+ });
480
+ describe("classifier/classify - actors", () => {
481
+ beforeEach(() => {
482
+ (0, config_1.clearClassifierConfigCacheForTest)();
483
+ });
484
+ it("classifies web_actor findings as actor/customer components", () => {
485
+ const findings = [
486
+ makeFinding({
487
+ pattern: "web_actor",
488
+ name: "Customer",
489
+ properties: {
490
+ actorType: "customer",
491
+ sourceContext: "frontend_session",
492
+ },
493
+ }),
494
+ ];
495
+ const components = (0, classify_1.classifyRawFindings)(findings);
496
+ expect(components).toHaveLength(1);
497
+ const actor = components[0];
498
+ expect(actor.type).toBe("actor");
499
+ expect(actor.subType).toBe("customer");
500
+ expect(actor.name).toBe("Customer");
501
+ });
502
+ it("classifies service_actor findings as actor/employee components", () => {
503
+ const findings = [
504
+ makeFinding({
505
+ pattern: "service_actor",
506
+ name: "Admin user",
507
+ properties: {
508
+ actorType: "employee",
509
+ roleNames: ["admin"],
510
+ sourceContext: "backend_role_check",
511
+ },
512
+ }),
513
+ ];
514
+ const components = (0, classify_1.classifyRawFindings)(findings);
515
+ expect(components).toHaveLength(1);
516
+ const actor = components[0];
517
+ expect(actor.type).toBe("actor");
518
+ expect(actor.subType).toBe("employee");
519
+ expect(actor.properties.roleNames).toEqual(["admin"]);
520
+ });
521
+ it("prefers actor type when both asset and actor findings are present with higher-priority actor defaults", () => {
522
+ const findings = [
523
+ makeFinding({
524
+ pattern: "express_route",
525
+ name: "GET /diagrams",
526
+ properties: {
527
+ framework: "express",
528
+ },
529
+ }),
530
+ makeFinding({
531
+ pattern: "web_actor",
532
+ name: "Customer",
533
+ properties: {
534
+ actorType: "customer",
535
+ },
536
+ }),
537
+ ];
538
+ const components = (0, classify_1.classifyRawFindings)(findings);
539
+ const actor = components.find((c) => c.type === "actor");
540
+ const asset = components.find((c) => c.type === "asset");
541
+ expect(actor).toBeDefined();
542
+ expect(actor?.subType).toBe("customer");
543
+ expect(asset).toBeDefined();
544
+ });
545
+ it("merges multiple express_route findings into a single HTTP API asset", () => {
546
+ const findings = [
547
+ makeFinding({
548
+ pattern: "express_route",
549
+ name: "GET *",
550
+ location: makeLocation({ filePath: "server.ts", startLine: 10 }),
551
+ properties: { httpMethod: "GET", path: "*" },
552
+ }),
553
+ makeFinding({
554
+ pattern: "express_route",
555
+ name: "GET /auth/:provider",
556
+ location: makeLocation({ filePath: "router.ts", startLine: 19 }),
557
+ properties: { httpMethod: "GET", path: "/auth/:provider" },
558
+ }),
559
+ makeFinding({
560
+ pattern: "express_route",
561
+ name: "POST /api/items",
562
+ location: makeLocation({ filePath: "routes/items.ts", startLine: 3 }),
563
+ properties: { httpMethod: "POST", path: "/api/items" },
564
+ }),
565
+ ];
566
+ const components = (0, classify_1.classifyRawFindings)(findings);
567
+ const apiAssets = components.filter((c) => c.type === "asset" && c.subType === "api");
568
+ expect(apiAssets).toHaveLength(1);
569
+ expect(apiAssets[0].name).toBe("HTTP API");
570
+ expect(apiAssets[0].detectedFrom.length).toBeGreaterThanOrEqual(3);
571
+ });
572
+ it("keeps merged HTTP API assets separated by section_id", () => {
573
+ const findings = [
574
+ makeFinding({
575
+ pattern: "express_route",
576
+ name: "GET /a",
577
+ location: makeLocation({ filePath: "packages/a/src/server.ts", startLine: 10 }),
578
+ properties: {
579
+ httpMethod: "GET",
580
+ path: "/a",
581
+ section_id: "service-a",
582
+ section_label: "service-a",
583
+ section_role: "service",
584
+ },
585
+ }),
586
+ makeFinding({
587
+ pattern: "express_route",
588
+ name: "POST /a/items",
589
+ location: makeLocation({ filePath: "packages/a/src/routes/items.ts", startLine: 3 }),
590
+ properties: {
591
+ httpMethod: "POST",
592
+ path: "/a/items",
593
+ section_id: "service-a",
594
+ section_label: "service-a",
595
+ section_role: "service",
596
+ },
597
+ }),
598
+ makeFinding({
599
+ pattern: "express_route",
600
+ name: "GET /b",
601
+ location: makeLocation({ filePath: "packages/b/src/server.ts", startLine: 5 }),
602
+ properties: {
603
+ httpMethod: "GET",
604
+ path: "/b",
605
+ section_id: "service-b",
606
+ section_label: "service-b",
607
+ section_role: "service",
608
+ },
609
+ }),
610
+ ];
611
+ const components = (0, classify_1.classifyRawFindings)(findings);
612
+ const apiAssets = components.filter((c) => c.type === "asset" && c.subType === "api");
613
+ const sectionIds = apiAssets.map((c) => typeof c.properties.section_id === "string"
614
+ ? c.properties.section_id
615
+ : String(c.properties.section_id));
616
+ expect(apiAssets).toHaveLength(2);
617
+ expect(sectionIds).toContain("service-a");
618
+ expect(sectionIds).toContain("service-b");
619
+ });
620
+ });
621
+ describe("classifier/classify - env_variable exclusion", () => {
622
+ beforeEach(() => {
623
+ (0, config_1.clearClassifierConfigCacheForTest)();
624
+ });
625
+ it("does not emit a component for env_variable findings whose key is in env_variable_exclude_keys (e.g. NODE_ENV)", () => {
626
+ const findings = [
627
+ makeFinding({
628
+ pattern: "env_variable",
629
+ name: "process.env.NODE_ENV",
630
+ properties: { key: "NODE_ENV" },
631
+ }),
632
+ ];
633
+ const components = (0, classify_1.classifyRawFindings)(findings);
634
+ expect(components).toHaveLength(0);
635
+ });
636
+ it("does not emit components for any env_variable (config assets are excluded from output)", () => {
637
+ const findings = [
638
+ makeFinding({
639
+ pattern: "env_variable",
640
+ name: "process.env.DATABASE_URL",
641
+ properties: { key: "DATABASE_URL" },
642
+ }),
643
+ ];
644
+ const components = (0, classify_1.classifyRawFindings)(findings);
645
+ expect(components).toHaveLength(0);
646
+ });
647
+ });
648
+ describe("classifier/classify - DP-P0-CLI-202", () => {
649
+ beforeEach(() => {
650
+ (0, config_1.clearClassifierConfigCacheForTest)();
651
+ });
652
+ it("classifies cloud providers from the dedicated third-party catalog (AWS, GCP, Azure)", () => {
653
+ const findings = [
654
+ makeFinding({
655
+ pattern: "external_api_call",
656
+ name: "AwsSdkClient",
657
+ properties: {
658
+ serviceName: "aws",
659
+ client: "@aws-sdk/s3",
660
+ },
661
+ }),
662
+ makeFinding({
663
+ pattern: "external_api_call",
664
+ name: "GcpClient",
665
+ properties: {
666
+ serviceName: "gcp",
667
+ client: "@google-cloud/storage",
668
+ },
669
+ }),
670
+ makeFinding({
671
+ pattern: "external_api_call",
672
+ name: "AzureClient",
673
+ properties: {
674
+ serviceName: "azure",
675
+ client: "@azure/storage-blob",
676
+ },
677
+ }),
678
+ ];
679
+ const components = (0, classify_1.classifyRawFindings)(findings);
680
+ const aws = components.find((c) => c.name.toLowerCase().includes("aws"));
681
+ const gcp = components.find((c) => c.name.toLowerCase().includes("gcp"));
682
+ const azure = components.find((c) => c.name.toLowerCase().includes("azure"));
683
+ expect(aws).toBeDefined();
684
+ expect(aws?.type).toBe("third_party");
685
+ expect(aws?.subType).toBe("cloud_provider");
686
+ expect(gcp).toBeDefined();
687
+ expect(gcp?.type).toBe("third_party");
688
+ expect(gcp?.subType).toBe("cloud_provider");
689
+ expect(azure).toBeDefined();
690
+ expect(azure?.type).toBe("third_party");
691
+ expect(azure?.subType).toBe("cloud_provider");
692
+ });
693
+ it("uses catalog to override generic external_api_call asset mapping for SaaS/API providers", () => {
694
+ const findings = [
695
+ makeFinding({
696
+ pattern: "external_api_call",
697
+ name: "TwilioApiClient",
698
+ properties: {
699
+ serviceName: "twilio",
700
+ },
701
+ }),
702
+ makeFinding({
703
+ pattern: "external_api_call",
704
+ name: "FirebaseSdk",
705
+ properties: {
706
+ serviceName: "firebase",
707
+ },
708
+ }),
709
+ makeFinding({
710
+ pattern: "external_api_call",
711
+ name: "Auth0ApiClient",
712
+ properties: {
713
+ serviceName: "auth0",
714
+ },
715
+ }),
716
+ ];
717
+ const components = (0, classify_1.classifyRawFindings)(findings);
718
+ const twilio = components.find((c) => c.name.toLowerCase().includes("twilio"));
719
+ const firebase = components.find((c) => c.name.toLowerCase().includes("firebase"));
720
+ const auth0 = components.find((c) => c.name.toLowerCase().includes("auth0"));
721
+ expect(twilio).toBeDefined();
722
+ expect(twilio?.type).toBe("third_party");
723
+ expect(twilio?.subType).toBe("saas_service");
724
+ expect(firebase).toBeDefined();
725
+ expect(firebase?.type).toBe("third_party");
726
+ expect(firebase?.subType).toBe("saas_service");
727
+ expect(auth0).toBeDefined();
728
+ expect(auth0?.type).toBe("third_party");
729
+ expect(auth0?.subType).toBe("saas_service");
730
+ });
731
+ });
732
+ describe("classifier/dedupe & application asset - DP-P0-CLI-204", () => {
733
+ it("dedupeComponents merges components by type + normalized name across assets, third parties, and actors", () => {
734
+ const components = [
735
+ makeComponent({
736
+ id: "cmp_1",
737
+ name: "User DB",
738
+ type: "asset",
739
+ subType: "database",
740
+ properties: { region: "us-east-1" },
741
+ }),
742
+ makeComponent({
743
+ id: "cmp_2",
744
+ name: "user db",
745
+ type: "asset",
746
+ subType: "database",
747
+ properties: { region: "eu-west-1" },
748
+ }),
749
+ makeComponent({
750
+ id: "cmp_3",
751
+ name: "Stripe",
752
+ type: "third_party",
753
+ subType: "payment_processor",
754
+ properties: { url: "https://api.stripe.com" },
755
+ }),
756
+ makeComponent({
757
+ id: "cmp_4",
758
+ name: "stripe ",
759
+ type: "third_party",
760
+ subType: "payment_processor",
761
+ properties: { url: "https://api.stripe.com/v2" },
762
+ }),
763
+ makeComponent({
764
+ id: "cmp_5",
765
+ name: "Customer",
766
+ type: "actor",
767
+ subType: "customer",
768
+ properties: { segment: "retail" },
769
+ }),
770
+ makeComponent({
771
+ id: "cmp_6",
772
+ name: "customer",
773
+ type: "actor",
774
+ subType: "customer",
775
+ properties: { region: "EU" },
776
+ }),
777
+ ];
778
+ const deduped = (0, classify_1.dedupeComponents)(components);
779
+ const assets = deduped.filter((c) => c.type === "asset");
780
+ const thirdParties = deduped.filter((c) => c.type === "third_party");
781
+ const actors = deduped.filter((c) => c.type === "actor");
782
+ expect(assets).toHaveLength(1);
783
+ expect(thirdParties).toHaveLength(1);
784
+ expect(actors).toHaveLength(1);
785
+ const assetRegion = assets[0].properties.region;
786
+ expect(Array.isArray(assetRegion)).toBe(true);
787
+ expect(assetRegion).toContain("us-east-1");
788
+ expect(assetRegion).toContain("eu-west-1");
789
+ const urls = thirdParties[0].properties.url;
790
+ expect(Array.isArray(urls)).toBe(true);
791
+ expect(urls).toContain("https://api.stripe.com");
792
+ expect(urls).toContain("https://api.stripe.com/v2");
793
+ });
794
+ it("merges asset components by normalized name when names differ only by case/spacing", () => {
795
+ const components = [
796
+ makeComponent({
797
+ id: "cmp_1",
798
+ name: "Postgres",
799
+ type: "asset",
800
+ subType: "database",
801
+ properties: { databaseType: "postgres", region: "us-east-1" },
802
+ }),
803
+ makeComponent({
804
+ id: "cmp_2",
805
+ name: "postgres",
806
+ type: "asset",
807
+ subType: "database",
808
+ properties: { databaseType: "postgres", region: "eu-west-1" },
809
+ }),
810
+ ];
811
+ const deduped = (0, classify_1.dedupeComponents)(components);
812
+ expect(deduped).toHaveLength(1);
813
+ const [merged] = deduped;
814
+ expect(merged.type).toBe("asset");
815
+ expect(merged.subType).toBe("database");
816
+ const regionProp = merged.properties.region;
817
+ expect(Array.isArray(regionProp)).toBe(true);
818
+ expect(regionProp).toContain("us-east-1");
819
+ expect(regionProp).toContain("eu-west-1");
820
+ });
821
+ it("does not merge components that share a name but differ in type", () => {
822
+ const components = [
823
+ makeComponent({
824
+ id: "cmp_1",
825
+ name: "Auth0",
826
+ type: "asset",
827
+ subType: "api",
828
+ properties: {},
829
+ }),
830
+ makeComponent({
831
+ id: "cmp_2",
832
+ name: "Auth0",
833
+ type: "third_party",
834
+ subType: "saas_service",
835
+ properties: {},
836
+ }),
837
+ ];
838
+ const deduped = (0, classify_1.dedupeComponents)(components);
839
+ const asset = deduped.find((c) => c.type === "asset");
840
+ const thirdParty = deduped.find((c) => c.type === "third_party");
841
+ expect(asset).toBeDefined();
842
+ expect(thirdParty).toBeDefined();
843
+ });
844
+ it("injects a synthetic application asset when none exists and marks it as main application after enhancement", () => {
845
+ const components = [
846
+ makeComponent({
847
+ id: "cmp_1",
848
+ name: "DB",
849
+ type: "asset",
850
+ subType: "database",
851
+ properties: {},
852
+ }),
853
+ makeComponent({
854
+ id: "cmp_2",
855
+ name: "Stripe",
856
+ type: "third_party",
857
+ subType: "payment_processor",
858
+ properties: {},
859
+ }),
860
+ ];
861
+ const withApp = (0, classify_1.injectApplicationAssetIfMissing)(components, {
862
+ projectName: "my-app",
863
+ });
864
+ expect(withApp.length).toBe(3);
865
+ const synthetic = withApp.find((c) => c.type === "asset" && c.name === "my-app");
866
+ expect(synthetic).toBeDefined();
867
+ const enhanced = (0, enhance_1.enhanceComponents)(withApp);
868
+ const mainApps = enhanced.filter((c) => c.properties.isMainApplication === true);
869
+ expect(mainApps).toHaveLength(1);
870
+ expect(mainApps[0].name).toBe("my-app");
871
+ });
872
+ it("does not inject an application asset when an application-like asset already exists", () => {
873
+ const components = [
874
+ makeComponent({
875
+ id: "cmp_1",
876
+ name: "App API",
877
+ type: "asset",
878
+ subType: "api",
879
+ properties: {},
880
+ }),
881
+ makeComponent({
882
+ id: "cmp_2",
883
+ name: "DB",
884
+ type: "asset",
885
+ subType: "database",
886
+ properties: {},
887
+ }),
888
+ ];
889
+ const withApp = (0, classify_1.injectApplicationAssetIfMissing)(components, {
890
+ projectName: "ignored",
891
+ });
892
+ expect(withApp).toHaveLength(2);
893
+ });
894
+ it("does not inject when merged HTTP API (express) is the only application hub", () => {
895
+ const components = [
896
+ makeComponent({
897
+ id: "cmp_api",
898
+ name: "HTTP API",
899
+ type: "asset",
900
+ subType: "api",
901
+ properties: { framework: "express" },
902
+ }),
903
+ makeComponent({
904
+ id: "cmp_db",
905
+ name: "Postgres",
906
+ type: "asset",
907
+ subType: "database",
908
+ properties: { databaseType: "postgres" },
909
+ }),
910
+ ];
911
+ const withApp = (0, classify_1.injectApplicationAssetIfMissing)(components, {
912
+ projectName: "should-not-appear",
913
+ });
914
+ expect(withApp).toHaveLength(2);
915
+ expect(withApp.some((c) => c.name === "should-not-appear")).toBe(false);
916
+ });
917
+ it("treats single-route API assets as non-application and injects a separate main application", () => {
918
+ const components = [
919
+ makeComponent({
920
+ id: "cmp_route",
921
+ name: "POST /api/ai-completion",
922
+ type: "asset",
923
+ subType: "api",
924
+ properties: {
925
+ framework: "express",
926
+ },
927
+ }),
928
+ ];
929
+ const withApp = (0, classify_1.injectApplicationAssetIfMissing)(components, {
930
+ projectName: "my-app",
931
+ });
932
+ expect(withApp).toHaveLength(2);
933
+ const routeAsset = withApp.find((c) => c.id === "cmp_route" && c.type === "asset");
934
+ const appAsset = withApp.find((c) => c.type === "asset" && c.name === "my-app");
935
+ expect(routeAsset).toBeDefined();
936
+ expect(appAsset).toBeDefined();
937
+ const enhanced = (0, enhance_1.enhanceComponents)(withApp);
938
+ const mainApps = enhanced.filter((c) => c.properties.isMainApplication === true);
939
+ expect(mainApps).toHaveLength(1);
940
+ expect(mainApps[0].name).toBe("my-app");
941
+ });
942
+ describe("injectActorIfMissing", () => {
943
+ it("injects a User actor for user-entrypoint sections (application main app)", () => {
944
+ const components = [
945
+ makeComponent({
946
+ id: "cmp_1",
947
+ name: "MyApp",
948
+ type: "asset",
949
+ subType: "application",
950
+ properties: { isMainApplication: true },
951
+ }),
952
+ makeComponent({
953
+ id: "cmp_2",
954
+ name: "Postgres",
955
+ type: "asset",
956
+ subType: "database",
957
+ properties: {},
958
+ }),
959
+ ];
960
+ const result = (0, classify_1.injectActorIfMissing)(components);
961
+ expect(result).toHaveLength(3);
962
+ const actor = result.find((c) => c.type === "actor");
963
+ expect(actor).toBeDefined();
964
+ expect(actor?.name).toBe("User");
965
+ expect(actor?.subType).toBe("customer");
966
+ expect(actor?.properties?.sourceContext).toBe("injected_default");
967
+ });
968
+ it("does not inject a User actor for backend-style main app sections", () => {
969
+ const components = [
970
+ makeComponent({
971
+ id: "cmp_1",
972
+ name: "Backend API",
973
+ type: "asset",
974
+ subType: "api",
975
+ properties: { isMainApplication: true, section_id: "backend" },
976
+ }),
977
+ ];
978
+ const result = (0, classify_1.injectActorIfMissing)(components);
979
+ expect(result).toHaveLength(1);
980
+ expect(result[0].type).toBe("asset");
981
+ });
982
+ it("injects a User actor for frontend frameworks even when main app subtype is api", () => {
983
+ const components = [
984
+ makeComponent({
985
+ id: "cmp_1",
986
+ name: "Frontend API Surface",
987
+ type: "asset",
988
+ subType: "api",
989
+ properties: {
990
+ isMainApplication: true,
991
+ section_id: "frontend",
992
+ framework: "next_or_react_route",
993
+ },
994
+ }),
995
+ ];
996
+ const result = (0, classify_1.injectActorIfMissing)(components);
997
+ const actor = result.find((c) => c.type === "actor");
998
+ expect(actor).toBeDefined();
999
+ expect(actor?.name).toBe("User");
1000
+ expect(actor?.properties?.section_id).toBe("frontend");
1001
+ });
1002
+ it("injects User as api_consumer for backend projects", () => {
1003
+ const components = [
1004
+ makeComponent({
1005
+ id: "cmp_1",
1006
+ name: "Backend App",
1007
+ type: "asset",
1008
+ subType: "application",
1009
+ properties: {
1010
+ isMainApplication: true,
1011
+ section_id: "reedy-backend",
1012
+ framework: "fastapi",
1013
+ },
1014
+ }),
1015
+ ];
1016
+ const result = (0, classify_1.injectActorIfMissing)(components);
1017
+ const actor = result.find((c) => c.type === "actor");
1018
+ expect(actor).toBeDefined();
1019
+ expect(actor?.name).toBe("User");
1020
+ expect(actor?.subType).toBe("api_consumer");
1021
+ expect(actor?.properties?.section_id).toBe("reedy-backend");
1022
+ });
1023
+ it("does not inject when actors already exist", () => {
1024
+ const components = [
1025
+ makeComponent({
1026
+ id: "cmp_1",
1027
+ name: "MyApp",
1028
+ type: "asset",
1029
+ subType: "api",
1030
+ properties: { isMainApplication: true },
1031
+ }),
1032
+ makeComponent({
1033
+ id: "cmp_2",
1034
+ name: "Customer",
1035
+ type: "actor",
1036
+ subType: "customer",
1037
+ properties: {},
1038
+ }),
1039
+ ];
1040
+ const result = (0, classify_1.injectActorIfMissing)(components);
1041
+ expect(result).toHaveLength(2);
1042
+ const actors = result.filter((c) => c.type === "actor");
1043
+ expect(actors).toHaveLength(1);
1044
+ expect(actors[0].name).toBe("Customer");
1045
+ });
1046
+ it("does not inject when no main application exists", () => {
1047
+ const components = [
1048
+ makeComponent({
1049
+ id: "cmp_1",
1050
+ name: "Postgres",
1051
+ type: "asset",
1052
+ subType: "database",
1053
+ properties: {},
1054
+ }),
1055
+ ];
1056
+ const result = (0, classify_1.injectActorIfMissing)(components);
1057
+ expect(result).toHaveLength(1);
1058
+ expect(result[0].type).toBe("asset");
1059
+ });
1060
+ it("does not inject when components is empty", () => {
1061
+ const result = (0, classify_1.injectActorIfMissing)([]);
1062
+ expect(result).toEqual([]);
1063
+ });
1064
+ });
1065
+ describe("compactAuthServiceComponents", () => {
1066
+ it("merges auth_service properties into same-section Auth0 node and removes standalone auth_service", () => {
1067
+ const components = [
1068
+ makeComponent({
1069
+ id: "tp_1",
1070
+ name: "Auth0",
1071
+ type: "third_party",
1072
+ subType: "saas_service",
1073
+ properties: {
1074
+ section_id: "backend",
1075
+ section_label: "backend",
1076
+ section_role: "service",
1077
+ serviceName: "auth0",
1078
+ },
1079
+ }),
1080
+ makeComponent({
1081
+ id: "a_1",
1082
+ name: "Jwt Auth",
1083
+ type: "asset",
1084
+ subType: "auth_service",
1085
+ properties: {
1086
+ section_id: "backend",
1087
+ section_label: "backend",
1088
+ section_role: "service",
1089
+ strategy: "jwt",
1090
+ authentication_method: "jwt",
1091
+ },
1092
+ }),
1093
+ ];
1094
+ const compacted = (0, classify_1.compactAuthServiceComponents)(components);
1095
+ expect(compacted.find((c) => c.id === "a_1")).toBeUndefined();
1096
+ const auth0 = compacted.find((c) => c.id === "tp_1");
1097
+ expect(auth0).toBeDefined();
1098
+ expect(auth0?.properties.strategy).toBe("jwt");
1099
+ expect(auth0?.properties.authentication_method).toBe("jwt");
1100
+ expect(auth0?.type).toBe("third_party");
1101
+ });
1102
+ });
1103
+ it("merges third_party components by serviceName even when names differ", () => {
1104
+ const components = [
1105
+ makeComponent({
1106
+ id: "cmp_10",
1107
+ name: "Auth0",
1108
+ type: "third_party",
1109
+ subType: "saas_service",
1110
+ properties: {
1111
+ serviceName: "auth0",
1112
+ client: "auth0",
1113
+ },
1114
+ }),
1115
+ makeComponent({
1116
+ id: "cmp_11",
1117
+ name: "Https://${auth0Domain}/dbconnections/change_password",
1118
+ type: "third_party",
1119
+ subType: "saas_service",
1120
+ properties: {
1121
+ serviceName: "auth0",
1122
+ client: "fetch",
1123
+ url: "https://${auth0Domain}/dbconnections/change_password",
1124
+ },
1125
+ }),
1126
+ ];
1127
+ const deduped = (0, classify_1.dedupeComponents)(components);
1128
+ expect(deduped).toHaveLength(1);
1129
+ const [merged] = deduped;
1130
+ expect(merged.type).toBe("third_party");
1131
+ expect(merged.subType).toBe("saas_service");
1132
+ expect(merged.properties.serviceName).toBe("auth0");
1133
+ const clientProp = merged.properties.client;
1134
+ expect(Array.isArray(clientProp)).toBe(true);
1135
+ expect(clientProp).toContain("auth0");
1136
+ expect(clientProp).toContain("fetch");
1137
+ });
1138
+ it("keeps third_party components separate per section_id (no global bucket)", () => {
1139
+ const components = [
1140
+ makeComponent({
1141
+ id: "cmp_1",
1142
+ name: "Auth0",
1143
+ type: "third_party",
1144
+ subType: "saas_service",
1145
+ properties: {
1146
+ serviceName: "auth0",
1147
+ client: "auth0",
1148
+ section_id: "frontend",
1149
+ section_label: "Frontend",
1150
+ section_role: "service",
1151
+ },
1152
+ }),
1153
+ makeComponent({
1154
+ id: "cmp_2",
1155
+ name: "Auth0",
1156
+ type: "third_party",
1157
+ subType: "saas_service",
1158
+ properties: {
1159
+ serviceName: "auth0",
1160
+ client: "fetch",
1161
+ section_id: "backend",
1162
+ section_label: "Backend",
1163
+ section_role: "service",
1164
+ },
1165
+ }),
1166
+ ];
1167
+ const deduped = (0, classify_1.dedupeComponents)(components);
1168
+ expect(deduped).toHaveLength(2);
1169
+ const frontend = deduped.find((c) => c.properties.section_id === "frontend");
1170
+ const backend = deduped.find((c) => c.properties.section_id === "backend");
1171
+ expect(frontend).toBeDefined();
1172
+ expect(backend).toBeDefined();
1173
+ expect(frontend?.type).toBe("third_party");
1174
+ expect(frontend?.subType).toBe("saas_service");
1175
+ expect(frontend?.properties.section_label).toBe("Frontend");
1176
+ expect(frontend?.properties.section_role).toBe("service");
1177
+ expect(backend?.type).toBe("third_party");
1178
+ expect(backend?.subType).toBe("saas_service");
1179
+ expect(backend?.properties.section_label).toBe("Backend");
1180
+ expect(backend?.properties.section_role).toBe("service");
1181
+ });
1182
+ describe("mergeGlobalIdentityProviderThirdParties", () => {
1183
+ it("merges Auth0 third_party nodes from different sections into one canonical component", () => {
1184
+ const components = [
1185
+ makeComponent({
1186
+ id: "cmp_fe",
1187
+ name: "Auth0",
1188
+ type: "third_party",
1189
+ subType: "saas_service",
1190
+ confidence: 0.85,
1191
+ sourceLocations: [
1192
+ {
1193
+ filePath: "frontend/app/auth/route.ts",
1194
+ startLine: 1,
1195
+ endLine: 1,
1196
+ },
1197
+ ],
1198
+ properties: {
1199
+ serviceName: "auth0",
1200
+ client: "spa",
1201
+ section_id: "frontend",
1202
+ section_label: "Frontend",
1203
+ section_role: "service",
1204
+ },
1205
+ }),
1206
+ makeComponent({
1207
+ id: "cmp_be",
1208
+ name: "Auth0",
1209
+ type: "third_party",
1210
+ subType: "saas_service",
1211
+ confidence: 0.85,
1212
+ sourceLocations: [
1213
+ {
1214
+ filePath: "backend/src/auth/guard.ts",
1215
+ startLine: 1,
1216
+ endLine: 1,
1217
+ },
1218
+ ],
1219
+ properties: {
1220
+ serviceName: "auth0",
1221
+ client: "passport",
1222
+ section_id: "backend",
1223
+ section_label: "Backend",
1224
+ section_role: "service",
1225
+ },
1226
+ }),
1227
+ ];
1228
+ const merged = (0, classify_1.mergeGlobalIdentityProviderThirdParties)(components);
1229
+ expect(merged).toHaveLength(1);
1230
+ expect(merged[0].id).toBe("cmp_be");
1231
+ const client = merged[0].properties.client;
1232
+ expect(Array.isArray(client)).toBe(true);
1233
+ expect(client).toEqual(expect.arrayContaining(["spa", "passport"]));
1234
+ expect(merged[0].properties.section_id).toBe("backend");
1235
+ expect(merged[0].sourceLocations.map((l) => l.filePath)).toEqual(expect.arrayContaining([
1236
+ "frontend/app/auth/route.ts",
1237
+ "backend/src/auth/guard.ts",
1238
+ ]));
1239
+ });
1240
+ it("prefers backend-scoped IdP base even when frontend row has higher confidence", () => {
1241
+ const components = [
1242
+ makeComponent({
1243
+ id: "cmp_fe",
1244
+ name: "Auth0",
1245
+ type: "third_party",
1246
+ subType: "saas_service",
1247
+ confidence: 0.99,
1248
+ sourceLocations: [
1249
+ {
1250
+ filePath: "frontend/app/auth/route.ts",
1251
+ startLine: 1,
1252
+ endLine: 1,
1253
+ },
1254
+ ],
1255
+ properties: {
1256
+ serviceName: "auth0",
1257
+ section_id: "frontend",
1258
+ },
1259
+ }),
1260
+ makeComponent({
1261
+ id: "cmp_be",
1262
+ name: "Auth0",
1263
+ type: "third_party",
1264
+ subType: "saas_service",
1265
+ confidence: 0.8,
1266
+ sourceLocations: [
1267
+ {
1268
+ filePath: "backend/src/auth/strategy.ts",
1269
+ startLine: 1,
1270
+ endLine: 1,
1271
+ },
1272
+ ],
1273
+ properties: {
1274
+ serviceName: "auth0",
1275
+ section_id: "backend",
1276
+ },
1277
+ }),
1278
+ ];
1279
+ const merged = (0, classify_1.mergeGlobalIdentityProviderThirdParties)(components);
1280
+ expect(merged).toHaveLength(1);
1281
+ expect(merged[0].id).toBe("cmp_be");
1282
+ expect(merged[0].properties.section_id).toBe("backend");
1283
+ });
1284
+ it("does not merge non-IdP third_party vendors across sections", () => {
1285
+ const components = [
1286
+ makeComponent({
1287
+ id: "a",
1288
+ name: "Aws",
1289
+ type: "third_party",
1290
+ subType: "saas_service",
1291
+ properties: {
1292
+ serviceName: "aws",
1293
+ section_id: "frontend",
1294
+ },
1295
+ }),
1296
+ makeComponent({
1297
+ id: "b",
1298
+ name: "Aws",
1299
+ type: "third_party",
1300
+ subType: "saas_service",
1301
+ properties: {
1302
+ serviceName: "aws",
1303
+ section_id: "backend",
1304
+ },
1305
+ }),
1306
+ ];
1307
+ const merged = (0, classify_1.mergeGlobalIdentityProviderThirdParties)(components);
1308
+ expect(merged).toHaveLength(2);
1309
+ });
1310
+ });
1311
+ it("mergeDatabaseAssetsByType merges database assets by canonical technology (postgres/supabase/pg → one, redis → one)", () => {
1312
+ const components = [
1313
+ makeComponent({
1314
+ id: "cmp_1",
1315
+ name: "Supabase",
1316
+ type: "asset",
1317
+ subType: "database",
1318
+ properties: { client: "supabase" },
1319
+ }),
1320
+ makeComponent({
1321
+ id: "cmp_2",
1322
+ name: "Pg",
1323
+ type: "asset",
1324
+ subType: "database",
1325
+ properties: { client: "pg", databaseType: "postgres" },
1326
+ }),
1327
+ makeComponent({
1328
+ id: "cmp_3",
1329
+ name: "Redis",
1330
+ type: "asset",
1331
+ subType: "database",
1332
+ properties: { client: "redis" },
1333
+ }),
1334
+ ];
1335
+ const merged = (0, classify_1.mergeDatabaseAssetsByType)(components);
1336
+ const dbAssets = merged.filter((c) => c.type === "asset" && c.subType === "database");
1337
+ expect(dbAssets).toHaveLength(2);
1338
+ const postgresLike = dbAssets.find((c) => c.name.toLowerCase().includes("postgres") ||
1339
+ c.name.toLowerCase().includes("supabase") ||
1340
+ c.name.toLowerCase().includes("pg"));
1341
+ const redisLike = dbAssets.find((c) => c.name.toLowerCase().includes("redis"));
1342
+ expect(postgresLike).toBeDefined();
1343
+ expect(redisLike).toBeDefined();
1344
+ expect(postgresLike?.properties.client || postgresLike?.properties.databaseType).toBeTruthy();
1345
+ expect(redisLike?.properties.client).toBe("redis");
1346
+ });
1347
+ it("mergeDatabaseAssetsByType keeps database components separate per section_id (no global bucket)", () => {
1348
+ const components = [
1349
+ makeComponent({
1350
+ id: "cmp_1",
1351
+ name: "Supabase",
1352
+ type: "asset",
1353
+ subType: "database",
1354
+ properties: {
1355
+ client: "supabase",
1356
+ section_id: "frontend",
1357
+ section_label: "Frontend",
1358
+ section_role: "service",
1359
+ },
1360
+ }),
1361
+ makeComponent({
1362
+ id: "cmp_2",
1363
+ name: "Pg",
1364
+ type: "asset",
1365
+ subType: "database",
1366
+ properties: {
1367
+ client: "pg",
1368
+ databaseType: "postgres",
1369
+ section_id: "backend",
1370
+ section_label: "Backend",
1371
+ section_role: "service",
1372
+ },
1373
+ }),
1374
+ ];
1375
+ const merged = (0, classify_1.mergeDatabaseAssetsByType)(components);
1376
+ const dbAssets = merged.filter((c) => c.type === "asset" && c.subType === "database");
1377
+ expect(dbAssets).toHaveLength(2);
1378
+ const frontendDb = dbAssets.find((c) => c.properties.section_id === "frontend");
1379
+ const backendDb = dbAssets.find((c) => c.properties.section_id === "backend");
1380
+ expect(frontendDb).toBeDefined();
1381
+ expect(backendDb).toBeDefined();
1382
+ expect(frontendDb?.properties.section_label).toBe("Frontend");
1383
+ expect(frontendDb?.properties.section_role).toBe("service");
1384
+ expect(backendDb?.properties.section_label).toBe("Backend");
1385
+ expect(backendDb?.properties.section_role).toBe("service");
1386
+ });
1387
+ });
1388
+ describe("classifier/classify - DP-P0-CLI-205", () => {
1389
+ beforeEach(() => {
1390
+ (0, config_1.clearClassifierConfigCacheForTest)();
1391
+ });
1392
+ it("classifies a database-only project into asset/database components", () => {
1393
+ const findings = makeDatabaseOnlyFindings();
1394
+ const classified = (0, classify_1.classifyRawFindings)(findings);
1395
+ const deduped = (0, classify_1.dedupeComponents)(classified);
1396
+ expect(classified.length).toBeGreaterThanOrEqual(1);
1397
+ expect(deduped.length).toBeGreaterThanOrEqual(1);
1398
+ for (const component of deduped) {
1399
+ expect(component.type).toBe("asset");
1400
+ expect(component.subType).toBe("database");
1401
+ expect(component.sourceLocations.length).toBeGreaterThanOrEqual(1);
1402
+ expect(component.detectedFrom.length).toBeGreaterThanOrEqual(1);
1403
+ }
1404
+ // No third-party components should be inferred in a database-only project.
1405
+ expect(deduped.find((c) => c.type === "third_party")).toBeUndefined();
1406
+ });
1407
+ it("classifies an external-API-heavy project into third_party components and merges duplicate services", () => {
1408
+ const findings = makeExternalApiHeavyFindings();
1409
+ const classified = (0, classify_1.classifyRawFindings)(findings);
1410
+ const deduped = (0, classify_1.dedupeComponents)(classified);
1411
+ const stripe = deduped.find((c) => c.name.toLowerCase().includes("stripe"));
1412
+ const sendgrid = deduped.find((c) => c.name.toLowerCase().includes("sendgrid"));
1413
+ const auth0 = deduped.find((c) => c.name.toLowerCase().includes("auth0"));
1414
+ const aws = deduped.find((c) => c.name.toLowerCase().includes("aws"));
1415
+ expect(stripe).toBeDefined();
1416
+ expect(stripe?.type).toBe("third_party");
1417
+ expect(stripe?.subType).toBe("payment_processor");
1418
+ const stripeClientProp = stripe?.properties.client;
1419
+ if (stripeClientProp) {
1420
+ expect(Array.isArray(stripeClientProp)).toBe(true);
1421
+ expect(stripeClientProp).toEqual(expect.arrayContaining(["@stripe/stripe-js", "stripe"]));
1422
+ }
1423
+ expect(sendgrid).toBeDefined();
1424
+ expect(sendgrid?.type).toBe("third_party");
1425
+ expect(sendgrid?.subType).toBe("saas_service");
1426
+ expect(auth0).toBeDefined();
1427
+ expect(auth0?.type).toBe("third_party");
1428
+ expect(auth0?.subType).toBe("saas_service");
1429
+ expect(aws).toBeDefined();
1430
+ expect(aws?.type).toBe("third_party");
1431
+ expect(aws?.subType).toBe("cloud_provider");
1432
+ });
1433
+ it("handles a mixed project with assets, actors, and third parties and injects an application asset when missing", () => {
1434
+ const findings = makeMixedProjectFindings();
1435
+ const classified = (0, classify_1.classifyRawFindings)(findings);
1436
+ const deduped = (0, classify_1.dedupeComponents)(classified);
1437
+ const assets = deduped.filter((c) => c.type === "asset");
1438
+ const thirdParties = deduped.filter((c) => c.type === "third_party");
1439
+ const actors = deduped.filter((c) => c.type === "actor");
1440
+ expect(assets.length).toBeGreaterThanOrEqual(1);
1441
+ expect(assets.some((c) => c.subType === "database")).toBe(true);
1442
+ expect(thirdParties.length).toBeGreaterThanOrEqual(1);
1443
+ expect(thirdParties.some((c) => c.subType === "payment_processor")).toBe(true);
1444
+ expect(actors.length).toBeGreaterThanOrEqual(2);
1445
+ const withApp = (0, classify_1.injectApplicationAssetIfMissing)(deduped, {
1446
+ projectName: "mixed-project",
1447
+ });
1448
+ const enhanced = (0, enhance_1.enhanceComponents)(withApp);
1449
+ const mainApps = enhanced.filter((c) => c.properties.isMainApplication === true);
1450
+ expect(mainApps).toHaveLength(1);
1451
+ expect(mainApps[0].name).toBe("mixed-project");
1452
+ });
1453
+ });