@euconform/core 1.0.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +12 -0
  3. package/dist/evidence/bundle.d.ts +23 -0
  4. package/dist/evidence/bundle.d.ts.map +1 -0
  5. package/dist/evidence/bundle.js +44 -0
  6. package/dist/evidence/compliance-evaluation.d.ts +12 -0
  7. package/dist/evidence/compliance-evaluation.d.ts.map +1 -0
  8. package/dist/evidence/compliance-evaluation.js +78 -0
  9. package/dist/evidence/hash.d.ts +2 -0
  10. package/dist/evidence/hash.d.ts.map +1 -0
  11. package/dist/evidence/hash.js +4 -0
  12. package/dist/evidence/index.d.ts +13 -0
  13. package/dist/evidence/index.d.ts.map +1 -0
  14. package/dist/evidence/index.js +11 -0
  15. package/dist/evidence/markdown.d.ts +9 -0
  16. package/dist/evidence/markdown.d.ts.map +1 -0
  17. package/dist/evidence/markdown.js +169 -0
  18. package/dist/evidence/output.d.ts +13 -0
  19. package/dist/evidence/output.d.ts.map +1 -0
  20. package/dist/evidence/output.js +547 -0
  21. package/dist/evidence/types.d.ts +230 -0
  22. package/dist/evidence/types.d.ts.map +1 -0
  23. package/dist/evidence/types.js +7 -0
  24. package/dist/evidence/validate.d.ts +8 -0
  25. package/dist/evidence/validate.d.ts.map +1 -0
  26. package/dist/evidence/validate.js +175 -0
  27. package/dist/index.d.ts +2 -2
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +1 -1
  30. package/dist/inference/ollama-client.d.ts +3 -0
  31. package/dist/inference/ollama-client.d.ts.map +1 -1
  32. package/dist/inference/ollama-client.js +69 -71
  33. package/dist/legal-checks/annex-iv-report.d.ts +3 -0
  34. package/dist/legal-checks/annex-iv-report.d.ts.map +1 -1
  35. package/dist/legal-checks/annex-iv-report.js +1 -0
  36. package/dist/legal-checks/deadline-timeline.d.ts +57 -0
  37. package/dist/legal-checks/deadline-timeline.d.ts.map +1 -0
  38. package/dist/legal-checks/deadline-timeline.js +265 -0
  39. package/dist/legal-checks/gap-analysis.d.ts +48 -0
  40. package/dist/legal-checks/gap-analysis.d.ts.map +1 -0
  41. package/dist/legal-checks/gap-analysis.js +397 -0
  42. package/dist/legal-checks/gpai-classifier.d.ts +49 -0
  43. package/dist/legal-checks/gpai-classifier.d.ts.map +1 -0
  44. package/dist/legal-checks/gpai-classifier.js +197 -0
  45. package/dist/legal-checks/index.d.ts +6 -0
  46. package/dist/legal-checks/index.d.ts.map +1 -1
  47. package/dist/legal-checks/index.js +3 -0
  48. package/dist/scanner/aggregator.d.ts +7 -0
  49. package/dist/scanner/aggregator.d.ts.map +1 -0
  50. package/dist/scanner/aggregator.js +200 -0
  51. package/dist/scanner/detectors/ai-framework-detector.d.ts +3 -0
  52. package/dist/scanner/detectors/ai-framework-detector.d.ts.map +1 -0
  53. package/dist/scanner/detectors/ai-framework-detector.js +74 -0
  54. package/dist/scanner/detectors/ai-provider-detector.d.ts +3 -0
  55. package/dist/scanner/detectors/ai-provider-detector.d.ts.map +1 -0
  56. package/dist/scanner/detectors/ai-provider-detector.js +100 -0
  57. package/dist/scanner/detectors/compliance-detector.d.ts +3 -0
  58. package/dist/scanner/detectors/compliance-detector.d.ts.map +1 -0
  59. package/dist/scanner/detectors/compliance-detector.js +201 -0
  60. package/dist/scanner/detectors/config-detector.d.ts +3 -0
  61. package/dist/scanner/detectors/config-detector.d.ts.map +1 -0
  62. package/dist/scanner/detectors/config-detector.js +248 -0
  63. package/dist/scanner/detectors/framework-detector.d.ts +3 -0
  64. package/dist/scanner/detectors/framework-detector.d.ts.map +1 -0
  65. package/dist/scanner/detectors/framework-detector.js +141 -0
  66. package/dist/scanner/detectors/index.d.ts +8 -0
  67. package/dist/scanner/detectors/index.d.ts.map +1 -0
  68. package/dist/scanner/detectors/index.js +37 -0
  69. package/dist/scanner/detectors/local-inference-detector.d.ts +3 -0
  70. package/dist/scanner/detectors/local-inference-detector.d.ts.map +1 -0
  71. package/dist/scanner/detectors/local-inference-detector.js +93 -0
  72. package/dist/scanner/detectors/markdown-detector.d.ts +3 -0
  73. package/dist/scanner/detectors/markdown-detector.d.ts.map +1 -0
  74. package/dist/scanner/detectors/markdown-detector.js +139 -0
  75. package/dist/scanner/detectors/package-json-detector.d.ts +3 -0
  76. package/dist/scanner/detectors/package-json-detector.d.ts.map +1 -0
  77. package/dist/scanner/detectors/package-json-detector.js +166 -0
  78. package/dist/scanner/detectors/rag-detector.d.ts +3 -0
  79. package/dist/scanner/detectors/rag-detector.d.ts.map +1 -0
  80. package/dist/scanner/detectors/rag-detector.js +132 -0
  81. package/dist/scanner/detectors/shared.d.ts +18 -0
  82. package/dist/scanner/detectors/shared.d.ts.map +1 -0
  83. package/dist/scanner/detectors/shared.js +36 -0
  84. package/dist/scanner/discovery.d.ts +14 -0
  85. package/dist/scanner/discovery.d.ts.map +1 -0
  86. package/dist/scanner/discovery.js +235 -0
  87. package/dist/scanner/index.d.ts +9 -0
  88. package/dist/scanner/index.d.ts.map +1 -0
  89. package/dist/scanner/index.js +8 -0
  90. package/package.json +6 -3
  91. package/src/evidence/bundle.ts +65 -0
  92. package/src/evidence/compliance-evaluation.ts +114 -0
  93. package/src/evidence/hash.ts +5 -0
  94. package/src/evidence/index.ts +51 -0
  95. package/src/evidence/markdown.ts +226 -0
  96. package/src/evidence/output.ts +717 -0
  97. package/src/evidence/types.ts +329 -0
  98. package/src/evidence/validate.ts +270 -0
  99. package/src/index.ts +25 -0
  100. package/src/inference/ollama-client.ts +93 -86
  101. package/src/legal-checks/annex-iv-report.ts +4 -0
  102. package/src/legal-checks/deadline-timeline.ts +313 -0
  103. package/src/legal-checks/gap-analysis.ts +454 -0
  104. package/src/legal-checks/gpai-classifier.ts +281 -0
  105. package/src/legal-checks/index.ts +33 -0
  106. package/src/scanner/aggregator.ts +258 -0
  107. package/src/scanner/detectors/ai-framework-detector.ts +94 -0
  108. package/src/scanner/detectors/ai-provider-detector.ts +135 -0
  109. package/src/scanner/detectors/compliance-detector.ts +232 -0
  110. package/src/scanner/detectors/config-detector.ts +316 -0
  111. package/src/scanner/detectors/framework-detector.ts +163 -0
  112. package/src/scanner/detectors/index.ts +40 -0
  113. package/src/scanner/detectors/local-inference-detector.ts +124 -0
  114. package/src/scanner/detectors/markdown-detector.ts +176 -0
  115. package/src/scanner/detectors/package-json-detector.ts +202 -0
  116. package/src/scanner/detectors/rag-detector.ts +162 -0
  117. package/src/scanner/detectors/shared.ts +45 -0
  118. package/src/scanner/discovery.ts +290 -0
  119. package/src/scanner/index.ts +9 -0
  120. package/tests/evidence/bundle.test.ts +128 -0
  121. package/tests/evidence/ecef-schemas.test.ts +316 -0
  122. package/tests/evidence/hash.test.ts +25 -0
  123. package/tests/evidence/markdown.test.ts +92 -0
  124. package/tests/evidence/output.test.ts +203 -0
  125. package/tests/fixtures/compliance-good/README.md +14 -0
  126. package/tests/fixtures/compliance-good/lib/ai-service.ts +17 -0
  127. package/tests/fixtures/compliance-good/lib/review-flow.ts +11 -0
  128. package/tests/fixtures/compliance-good/package.json +10 -0
  129. package/tests/fixtures/compliance-good/tests/bias-check.test.ts +13 -0
  130. package/tests/fixtures/nextjs-openai/README.md +5 -0
  131. package/tests/fixtures/nextjs-openai/app/page.tsx +11 -0
  132. package/tests/fixtures/nextjs-openai/package.json +9 -0
  133. package/tests/fixtures/ollama-rag/.env.example +2 -0
  134. package/tests/fixtures/ollama-rag/docker-compose.yml +9 -0
  135. package/tests/fixtures/ollama-rag/package.json +9 -0
  136. package/tests/fixtures/ollama-rag/server.ts +21 -0
  137. package/tests/fixtures/plain-webapp/README.md +3 -0
  138. package/tests/fixtures/plain-webapp/index.js +5 -0
  139. package/tests/fixtures/plain-webapp/package.json +8 -0
  140. package/tests/gpai-classifier.test.ts +125 -0
  141. package/tests/gpai-timeline.test.ts +55 -0
  142. package/tests/scanner/aggregator.test.ts +122 -0
  143. package/tests/scanner/detectors.test.ts +274 -0
  144. package/tests/scanner/discovery.test.ts +87 -0
  145. package/vitest.config.ts +1 -0
  146. package/.turbo/turbo-check-types.log +0 -4
  147. package/.turbo/turbo-test.log +0 -364
  148. package/coverage/base.css +0 -224
  149. package/coverage/block-navigation.js +0 -87
  150. package/coverage/clover.xml +0 -3282
  151. package/coverage/coverage-final.json +0 -20
  152. package/coverage/favicon.png +0 -0
  153. package/coverage/index.html +0 -161
  154. package/coverage/prettify.css +0 -1
  155. package/coverage/prettify.js +0 -2
  156. package/coverage/sort-arrow-sprite.png +0 -0
  157. package/coverage/sorter.js +0 -210
  158. package/coverage/src/fairness-metrics.ts.html +0 -1579
  159. package/coverage/src/index.html +0 -161
  160. package/coverage/src/index.ts.html +0 -535
  161. package/coverage/src/inference/browser-inference.ts.html +0 -859
  162. package/coverage/src/inference/capability-detection.ts.html +0 -2227
  163. package/coverage/src/inference/index.html +0 -176
  164. package/coverage/src/inference/index.ts.html +0 -553
  165. package/coverage/src/inference/inference-factory.ts.html +0 -751
  166. package/coverage/src/inference/ollama-client.ts.html +0 -1627
  167. package/coverage/src/legal-checks/annex-iv-report.ts.html +0 -556
  168. package/coverage/src/legal-checks/bias-metrics.ts.html +0 -1855
  169. package/coverage/src/legal-checks/data-governance.ts.html +0 -529
  170. package/coverage/src/legal-checks/human-oversight-logging.ts.html +0 -490
  171. package/coverage/src/legal-checks/index.html +0 -206
  172. package/coverage/src/legal-checks/index.ts.html +0 -250
  173. package/coverage/src/legal-checks/risk-classifier.ts.html +0 -940
  174. package/coverage/src/legal-checks/robustness-transparency.ts.html +0 -571
  175. package/coverage/src/metrics/crows-pairs-bias.ts.html +0 -820
  176. package/coverage/src/metrics/index.html +0 -146
  177. package/coverage/src/metrics/index.ts.html +0 -151
  178. package/coverage/src/metrics/stereotype-bias.ts.html +0 -886
  179. package/coverage/src/risk-engine.ts.html +0 -1660
  180. package/coverage/src/types.ts.html +0 -547
  181. package/dist/legal-checks/disclaimer.d.ts +0 -2
  182. package/dist/legal-checks/disclaimer.d.ts.map +0 -1
  183. package/dist/legal-checks/disclaimer.js +0 -1
@@ -0,0 +1,139 @@
1
+ const rules = [
2
+ // --- compliance-disclosure ---
3
+ {
4
+ id: "compliance-disclosure-readme",
5
+ name: "AI disclosure in README",
6
+ category: "compliance-disclosure",
7
+ pattern: /\b(?:artificial\s+intelligence|machine\s+learning|AI[- ]powered|AI[- ]generated|uses?\s+AI|deep\s+learning|neural\s+network|large\s+language\s+model|LLM)\b/i,
8
+ confidence: "low",
9
+ },
10
+ {
11
+ id: "compliance-disclosure-transparency",
12
+ name: "Transparency notice",
13
+ category: "compliance-disclosure",
14
+ pattern: /\b(?:transparency\s+notice|AI\s+disclosure|generated\s+by\s+AI)\b/i,
15
+ confidence: "medium",
16
+ },
17
+ // --- Model card ---
18
+ {
19
+ id: "compliance-disclosure-model-card",
20
+ name: "Model card documentation",
21
+ category: "compliance-disclosure",
22
+ pattern: /^#{1,3}\s+Model\s+Card\b/i,
23
+ confidence: "medium",
24
+ sectionHeading: true,
25
+ },
26
+ // --- compliance-data ---
27
+ {
28
+ id: "compliance-data-docs",
29
+ name: "Data governance documentation",
30
+ category: "compliance-data",
31
+ pattern: /^#{1,3}\s+(?:Data\s+(?:Sources?|Governance|Quality|Lineage)|Training\s+Data)\b/i,
32
+ confidence: "medium",
33
+ sectionHeading: true,
34
+ },
35
+ {
36
+ id: "compliance-data-reference",
37
+ name: "Data source references",
38
+ category: "compliance-data",
39
+ pattern: /\b(?:training\s+data|data\s+sources?|dataset\s+documentation)\b/i,
40
+ confidence: "low",
41
+ },
42
+ // --- Privacy / GDPR ---
43
+ {
44
+ id: "compliance-data-privacy",
45
+ name: "Privacy policy reference",
46
+ category: "compliance-data",
47
+ pattern: /\b(?:privacy\s+policy|GDPR|data\s+protection|DSGVO)\b/i,
48
+ confidence: "medium",
49
+ },
50
+ // --- License / AI-specific ---
51
+ {
52
+ id: "compliance-disclosure-license",
53
+ name: "AI-relevant license terms",
54
+ category: "compliance-disclosure",
55
+ pattern: /\b(?:model\s+license|AI\s+license|responsible\s+AI|acceptable\s+use\s+policy|RAIL\s+license)\b/i,
56
+ confidence: "medium",
57
+ },
58
+ ];
59
+ // ---------------------------------------------------------------------------
60
+ // File filtering
61
+ // ---------------------------------------------------------------------------
62
+ /** Files that should always be skipped unless they contain AI-specific content */
63
+ const SKIP_BASENAMES = new Set([
64
+ "changelog.md",
65
+ "changes.md",
66
+ "history.md",
67
+ "contributing.md",
68
+ "code_of_conduct.md",
69
+ ]);
70
+ /** Only process these docs-level paths */
71
+ function isRelevantMarkdown(relativePath) {
72
+ const lower = relativePath.toLowerCase();
73
+ const basename = lower.split("/").pop() ?? "";
74
+ // Always process README files at any level
75
+ if (basename.startsWith("readme"))
76
+ return true;
77
+ // Process files inside docs/ directories
78
+ if (lower.includes("/docs/") || lower.startsWith("docs/")) {
79
+ return !SKIP_BASENAMES.has(basename);
80
+ }
81
+ // For other markdown files, skip known non-relevant ones
82
+ if (SKIP_BASENAMES.has(basename))
83
+ return false;
84
+ return true;
85
+ }
86
+ // ---------------------------------------------------------------------------
87
+ // Detector
88
+ // ---------------------------------------------------------------------------
89
+ function gatherMarkdownEvidence(rule, lines, filePath) {
90
+ const evidence = [];
91
+ for (let i = 0; i < lines.length; i++) {
92
+ const line = lines[i];
93
+ if (!line)
94
+ continue;
95
+ if (rule.sectionHeading && !line.startsWith("#"))
96
+ continue;
97
+ if (rule.pattern.test(line)) {
98
+ evidence.push({ file: filePath, line: i + 1, snippet: line.trim().slice(0, 120) });
99
+ if (evidence.length >= 5)
100
+ break;
101
+ }
102
+ }
103
+ return evidence;
104
+ }
105
+ function isSkippableGenericContent(basename, evidence) {
106
+ if (!SKIP_BASENAMES.has(basename))
107
+ return false;
108
+ return !evidence.some((e) => /\bAI\b|\bML\b|\bmodel\b|\binference\b/i.test(e.snippet));
109
+ }
110
+ export function detectMarkdown(ctx) {
111
+ const { file } = ctx;
112
+ if (file.extension !== ".md")
113
+ return [];
114
+ if (!isRelevantMarkdown(file.relativePath))
115
+ return [];
116
+ const signalMap = new Map();
117
+ const basename = file.relativePath.toLowerCase().split("/").pop() ?? "";
118
+ for (const rule of rules) {
119
+ const evidence = gatherMarkdownEvidence(rule, ctx.lines, file.relativePath);
120
+ if (evidence.length === 0)
121
+ continue;
122
+ if (isSkippableGenericContent(basename, evidence))
123
+ continue;
124
+ const existing = signalMap.get(rule.id);
125
+ if (existing) {
126
+ existing.evidence.push(...evidence);
127
+ }
128
+ else {
129
+ signalMap.set(rule.id, {
130
+ id: rule.id,
131
+ name: rule.name,
132
+ category: rule.category,
133
+ confidence: rule.confidence,
134
+ evidence,
135
+ });
136
+ }
137
+ }
138
+ return Array.from(signalMap.values());
139
+ }
@@ -0,0 +1,3 @@
1
+ import type { DetectedSignal, DetectorContext } from "../../evidence/types";
2
+ export declare function detectPackageJsonSignals(ctx: DetectorContext): DetectedSignal[];
3
+ //# sourceMappingURL=package-json-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-json-detector.d.ts","sourceRoot":"","sources":["../../../src/scanner/detectors/package-json-detector.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAkB,MAAM,sBAAsB,CAAC;AA6E5F,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,eAAe,GAAG,cAAc,EAAE,CAY/E"}
@@ -0,0 +1,166 @@
1
+ import { basename } from "node:path";
2
+ import { escapeRegex } from "./shared";
3
+ const AI_PACKAGES = {
4
+ // Providers
5
+ openai: { category: "ai-provider", label: "OpenAI SDK" },
6
+ "@anthropic-ai/sdk": { category: "ai-provider", label: "Anthropic SDK" },
7
+ "cohere-ai": { category: "ai-provider", label: "Cohere SDK" },
8
+ "@google/generative-ai": { category: "ai-provider", label: "Google Generative AI" },
9
+ "@mistralai/mistralai": { category: "ai-provider", label: "Mistral AI SDK" },
10
+ // Frameworks
11
+ langchain: { category: "ai-framework", label: "LangChain" },
12
+ "@langchain/core": { category: "ai-framework", label: "LangChain Core" },
13
+ llamaindex: { category: "ai-framework", label: "LlamaIndex" },
14
+ "@xenova/transformers": { category: "ai-framework", label: "Transformers.js" },
15
+ ai: { category: "ai-framework", label: "Vercel AI SDK" },
16
+ "@huggingface/inference": { category: "ai-framework", label: "HuggingFace Inference" },
17
+ // Local inference
18
+ ollama: { category: "local-inference", label: "Ollama" },
19
+ "@ollama/ollama": { category: "local-inference", label: "Ollama SDK" },
20
+ // RAG / Vector
21
+ chromadb: { category: "rag", label: "ChromaDB" },
22
+ "@pinecone-database/pinecone": { category: "rag", label: "Pinecone" },
23
+ "weaviate-client": { category: "rag", label: "Weaviate" },
24
+ "@qdrant/js-client-rest": { category: "rag", label: "Qdrant" },
25
+ pgvector: { category: "rag", label: "pgvector" },
26
+ // Bias / Eval
27
+ "@euconform/core": { category: "compliance-bias", label: "EuConform Core" },
28
+ };
29
+ const FRAMEWORK_PACKAGES = {
30
+ next: { category: "framework", label: "Next.js" },
31
+ react: { category: "framework", label: "React" },
32
+ express: { category: "framework", label: "Express" },
33
+ fastify: { category: "framework", label: "Fastify" },
34
+ "@nestjs/core": { category: "framework", label: "NestJS" },
35
+ hono: { category: "framework", label: "Hono" },
36
+ elysia: { category: "framework", label: "Elysia" },
37
+ };
38
+ const ALL_PACKAGES = {
39
+ ...AI_PACKAGES,
40
+ ...FRAMEWORK_PACKAGES,
41
+ };
42
+ // ---------------------------------------------------------------------------
43
+ // Helpers
44
+ // ---------------------------------------------------------------------------
45
+ const PACKAGE_JSON_NAMES = new Set([
46
+ "package.json",
47
+ "pnpm-lock.yaml",
48
+ "package-lock.json",
49
+ "yarn.lock",
50
+ ]);
51
+ function isPackageJsonFile(relativePath) {
52
+ return PACKAGE_JSON_NAMES.has(basename(relativePath));
53
+ }
54
+ // ---------------------------------------------------------------------------
55
+ // Detector
56
+ // ---------------------------------------------------------------------------
57
+ export function detectPackageJsonSignals(ctx) {
58
+ const { file } = ctx;
59
+ if (!isPackageJsonFile(file.relativePath))
60
+ return [];
61
+ const fileName = basename(file.relativePath);
62
+ if (fileName === "package.json") {
63
+ return detectFromPackageJson(file.relativePath, file.content);
64
+ }
65
+ // Lock files — scan for known package name strings
66
+ return detectFromLockFile(file.relativePath, file.content);
67
+ }
68
+ // ---------------------------------------------------------------------------
69
+ // package.json detection
70
+ // ---------------------------------------------------------------------------
71
+ function detectFromPackageJson(filePath, content) {
72
+ let parsed;
73
+ try {
74
+ parsed = JSON.parse(content);
75
+ }
76
+ catch {
77
+ return [];
78
+ }
79
+ const signals = [];
80
+ const deps = {
81
+ ...parsed.dependencies,
82
+ ...parsed.devDependencies,
83
+ };
84
+ for (const [pkg, info] of Object.entries(ALL_PACKAGES)) {
85
+ if (pkg in deps) {
86
+ const version = deps[pkg] ?? "unknown";
87
+ signals.push({
88
+ id: `${info.category}-${pkg.replace(/[@/]/g, "").replace(/\s/g, "-")}`,
89
+ name: info.label,
90
+ category: info.category,
91
+ confidence: "high",
92
+ evidence: [
93
+ {
94
+ file: filePath,
95
+ snippet: `"${pkg}": "${version}"`,
96
+ },
97
+ ],
98
+ });
99
+ }
100
+ }
101
+ // Monorepo signals
102
+ if (parsed.workspaces) {
103
+ signals.push({
104
+ id: "runtime-monorepo-workspaces",
105
+ name: "Monorepo (workspaces)",
106
+ category: "runtime",
107
+ confidence: "high",
108
+ evidence: [
109
+ {
110
+ file: filePath,
111
+ snippet: `"workspaces": ${JSON.stringify(parsed.workspaces).slice(0, 120)}`,
112
+ },
113
+ ],
114
+ });
115
+ }
116
+ const devDeps = parsed.devDependencies;
117
+ if (devDeps && "turbo" in devDeps) {
118
+ signals.push({
119
+ id: "runtime-monorepo-turbo",
120
+ name: "Monorepo (Turborepo)",
121
+ category: "runtime",
122
+ confidence: "high",
123
+ evidence: [
124
+ {
125
+ file: filePath,
126
+ snippet: `"turbo": "${devDeps.turbo}"`,
127
+ },
128
+ ],
129
+ });
130
+ }
131
+ return signals;
132
+ }
133
+ // ---------------------------------------------------------------------------
134
+ // Lock-file detection (string search, no full parse)
135
+ // ---------------------------------------------------------------------------
136
+ // Pre-compiled lock-file patterns — avoids RegExp construction on every call
137
+ const LOCK_FILE_PATTERNS = new Map(Object.keys(AI_PACKAGES).map((pkg) => [pkg, new RegExp(`["'/]${escapeRegex(pkg)}["'/@:]`)]));
138
+ function detectFromLockFile(filePath, content) {
139
+ const signals = [];
140
+ const seen = new Set();
141
+ for (const [pkg, info] of Object.entries(AI_PACKAGES)) {
142
+ if (seen.has(pkg))
143
+ continue;
144
+ // Use boundary-aware pattern to avoid false positives on short names
145
+ // Lock files quote package names or use them as path segments
146
+ const pattern = LOCK_FILE_PATTERNS.get(pkg);
147
+ if (!pattern)
148
+ continue;
149
+ if (pattern.test(content)) {
150
+ seen.add(pkg);
151
+ signals.push({
152
+ id: `${info.category}-${pkg.replace(/[@/]/g, "").replace(/\s/g, "-")}`,
153
+ name: info.label,
154
+ category: info.category,
155
+ confidence: "medium",
156
+ evidence: [
157
+ {
158
+ file: filePath,
159
+ snippet: `Found "${pkg}" in lock file`,
160
+ },
161
+ ],
162
+ });
163
+ }
164
+ }
165
+ return signals;
166
+ }
@@ -0,0 +1,3 @@
1
+ import type { DetectedSignal, DetectorContext } from "../../evidence/types";
2
+ export declare function detectRagSignals(ctx: DetectorContext): DetectedSignal[];
3
+ //# sourceMappingURL=rag-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rag-detector.d.ts","sourceRoot":"","sources":["../../../src/scanner/detectors/rag-detector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,cAAc,EACd,eAAe,EAEhB,MAAM,sBAAsB,CAAC;AAsH9B,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,eAAe,GAAG,cAAc,EAAE,CAsCvE"}
@@ -0,0 +1,132 @@
1
+ import { SOURCE_EXTENSIONS, higherConfidence } from "./shared";
2
+ // Explicit vector store imports (high confidence)
3
+ const IMPORT_RULES = [
4
+ {
5
+ id: "rag-chromadb",
6
+ name: "ChromaDB",
7
+ pattern: /(?:from\s+['"]chromadb['"]|require\s*\(\s*['"]chromadb['"])/,
8
+ confidence: "high",
9
+ },
10
+ {
11
+ id: "rag-pinecone",
12
+ name: "Pinecone",
13
+ pattern: /(?:from\s+['"]@pinecone-database\/pinecone['"]|require\s*\(\s*['"]@pinecone-database\/pinecone['"])/,
14
+ confidence: "high",
15
+ },
16
+ {
17
+ id: "rag-weaviate",
18
+ name: "Weaviate",
19
+ pattern: /(?:from\s+['"]weaviate-client['"]|require\s*\(\s*['"]weaviate-client['"])/,
20
+ confidence: "high",
21
+ },
22
+ {
23
+ id: "rag-qdrant",
24
+ name: "Qdrant",
25
+ pattern: /(?:from\s+['"]@qdrant\/js-client-rest['"]|require\s*\(\s*['"]@qdrant\/js-client-rest['"])/,
26
+ confidence: "high",
27
+ },
28
+ {
29
+ id: "rag-pgvector",
30
+ name: "pgvector",
31
+ pattern: /(?:from\s+['"]pgvector['"]|require\s*\(\s*['"]pgvector['"])/,
32
+ confidence: "high",
33
+ },
34
+ {
35
+ id: "rag-faiss",
36
+ name: "FAISS",
37
+ pattern: /(?:from\s+['"]faiss-node['"]|require\s*\(\s*['"]faiss-node['"])/,
38
+ confidence: "high",
39
+ },
40
+ ];
41
+ // Function call / usage patterns (medium confidence)
42
+ const USAGE_RULES = [
43
+ {
44
+ id: "rag-embedding-call",
45
+ name: "Embedding function call",
46
+ pattern: /\.(?:createEmbedding|embedDocuments|embedQuery|embed)\s*\(/,
47
+ confidence: "medium",
48
+ },
49
+ {
50
+ id: "rag-retriever-pattern",
51
+ name: "Retriever pattern",
52
+ pattern: /\.(?:asRetriever|getRelevantDocuments|similaritySearch|similaritySearchWithScore)\s*\(/,
53
+ confidence: "medium",
54
+ },
55
+ {
56
+ id: "rag-vectorstore-usage",
57
+ name: "Vector store usage",
58
+ pattern: /(?:VectorStore|vectorStore|fromDocuments|fromTexts|addDocuments)\s*\(/,
59
+ confidence: "medium",
60
+ },
61
+ ];
62
+ // Keyword patterns (low confidence)
63
+ const KEYWORD_RULES = [
64
+ {
65
+ id: "rag-keyword-embedding",
66
+ name: "Embedding keyword reference",
67
+ pattern: /\b(?:text[_-]?embedding|embedding[_-]?(?:model|vector|dimension|size)|openai.*embedding|embedding[_-]?api)\b/i,
68
+ confidence: "low",
69
+ },
70
+ {
71
+ id: "rag-keyword-vector",
72
+ name: "Vector keyword reference",
73
+ pattern: /\b(?:vector(?:Store|DB|Database|Index))\b/,
74
+ confidence: "low",
75
+ },
76
+ {
77
+ id: "rag-keyword-retriever",
78
+ name: "Retriever keyword reference",
79
+ pattern: /\bretriever\b/i,
80
+ confidence: "low",
81
+ },
82
+ {
83
+ id: "rag-keyword-similarity",
84
+ name: "Similarity search keyword",
85
+ pattern: /\bsimilarity_search\b/,
86
+ confidence: "low",
87
+ },
88
+ ];
89
+ // ---------------------------------------------------------------------------
90
+ // Combined rules (hoisted to module scope to avoid per-call reconstruction)
91
+ // ---------------------------------------------------------------------------
92
+ const ALL_RULES = [...IMPORT_RULES, ...USAGE_RULES, ...KEYWORD_RULES];
93
+ // ---------------------------------------------------------------------------
94
+ // Detector
95
+ // ---------------------------------------------------------------------------
96
+ export function detectRagSignals(ctx) {
97
+ const { file } = ctx;
98
+ if (!SOURCE_EXTENSIONS.has(file.extension))
99
+ return [];
100
+ const lines = ctx.lines;
101
+ const signalMap = new Map();
102
+ for (let i = 0; i < lines.length; i++) {
103
+ const line = lines[i];
104
+ if (!line)
105
+ continue;
106
+ const lineNum = i + 1;
107
+ for (const rule of ALL_RULES) {
108
+ if (rule.pattern.test(line)) {
109
+ const evidence = {
110
+ file: file.relativePath,
111
+ line: lineNum,
112
+ snippet: line.trim().slice(0, 200),
113
+ };
114
+ const existing = signalMap.get(rule.id);
115
+ if (existing) {
116
+ existing.evidence.push(evidence);
117
+ existing.confidence = higherConfidence(existing.confidence, rule.confidence);
118
+ }
119
+ else {
120
+ signalMap.set(rule.id, {
121
+ id: rule.id,
122
+ name: rule.name,
123
+ category: "rag",
124
+ confidence: rule.confidence,
125
+ evidence: [evidence],
126
+ });
127
+ }
128
+ }
129
+ }
130
+ }
131
+ return Array.from(signalMap.values());
132
+ }
@@ -0,0 +1,18 @@
1
+ import type { ConfidenceLevel } from "../../evidence/types";
2
+ /** Source-code extensions used by ai-provider, ai-framework, rag, compliance detectors */
3
+ export declare const SOURCE_EXTENSIONS: Set<string>;
4
+ /** Extended set including config formats, used by framework and local-inference detectors */
5
+ export declare const EXTENDED_EXTENSIONS: Set<string>;
6
+ /** Standardized snippet truncation length */
7
+ export declare const MAX_SNIPPET_LENGTH = 200;
8
+ /** Numeric ranking for confidence levels (higher = more confident) */
9
+ export declare const CONFIDENCE_RANK: Record<ConfidenceLevel, number>;
10
+ /** Return the higher of two confidence levels */
11
+ export declare function higherConfidence(a: ConfidenceLevel, b: ConfidenceLevel): ConfidenceLevel;
12
+ /** Escape special regex characters in a string */
13
+ export declare function escapeRegex(str: string): string;
14
+ /** Check whether an extension is a standard source file */
15
+ export declare function isSourceFile(ext: string): boolean;
16
+ /** Check whether an extension is in the extended (source + config) set */
17
+ export declare function isExtendedSourceFile(ext: string): boolean;
18
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../../src/scanner/detectors/shared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5D,0FAA0F;AAC1F,eAAO,MAAM,iBAAiB,aAA0C,CAAC;AAEzE,6FAA6F;AAC7F,eAAO,MAAM,mBAAmB,aAQ9B,CAAC;AAEH,6CAA6C;AAC7C,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,sEAAsE;AACtE,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,CAI3D,CAAC;AAEF,iDAAiD;AACjD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,eAAe,GAAG,eAAe,CAExF;AAED,kDAAkD;AAClD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,2DAA2D;AAC3D,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED,0EAA0E;AAC1E,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEzD"}
@@ -0,0 +1,36 @@
1
+ /** Source-code extensions used by ai-provider, ai-framework, rag, compliance detectors */
2
+ export const SOURCE_EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx"]);
3
+ /** Extended set including config formats, used by framework and local-inference detectors */
4
+ export const EXTENDED_EXTENSIONS = new Set([
5
+ ".ts",
6
+ ".tsx",
7
+ ".js",
8
+ ".jsx",
9
+ ".json",
10
+ ".yaml",
11
+ ".yml",
12
+ ]);
13
+ /** Standardized snippet truncation length */
14
+ export const MAX_SNIPPET_LENGTH = 200;
15
+ /** Numeric ranking for confidence levels (higher = more confident) */
16
+ export const CONFIDENCE_RANK = {
17
+ high: 2,
18
+ medium: 1,
19
+ low: 0,
20
+ };
21
+ /** Return the higher of two confidence levels */
22
+ export function higherConfidence(a, b) {
23
+ return CONFIDENCE_RANK[a] >= CONFIDENCE_RANK[b] ? a : b;
24
+ }
25
+ /** Escape special regex characters in a string */
26
+ export function escapeRegex(str) {
27
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
28
+ }
29
+ /** Check whether an extension is a standard source file */
30
+ export function isSourceFile(ext) {
31
+ return SOURCE_EXTENSIONS.has(ext);
32
+ }
33
+ /** Check whether an extension is in the extended (source + config) set */
34
+ export function isExtendedSourceFile(ext) {
35
+ return EXTENDED_EXTENSIONS.has(ext);
36
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * File discovery for the EuConform scanner.
3
+ *
4
+ * Walks a target directory, respects .gitignore, filters by extension
5
+ * and named files, and returns file contents for pattern matching.
6
+ */
7
+ import type { FileProvenance, ScanFile, ScanOptions } from "../evidence/types";
8
+ export declare function classifyFileProvenance(relativePath: string): FileProvenance;
9
+ export interface DiscoveryResult {
10
+ files: ScanFile[];
11
+ skippedCount: number;
12
+ }
13
+ export declare function discoverFiles(options: ScanOptions): Promise<DiscoveryResult>;
14
+ //# sourceMappingURL=discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../src/scanner/discovery.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,WAAW,EAAa,MAAM,mBAAmB,CAAC;AAsG1F,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,GAAG,cAAc,CAoC3E;AAeD,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAiFD,wBAAsB,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC,CA0ClF"}