driftdetect-core 0.4.1 → 0.4.2

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 (237) hide show
  1. package/dist/boundaries/boundary-scanner.d.ts +76 -0
  2. package/dist/boundaries/boundary-scanner.d.ts.map +1 -0
  3. package/dist/boundaries/boundary-scanner.js +801 -0
  4. package/dist/boundaries/boundary-scanner.js.map +1 -0
  5. package/dist/boundaries/data-access-learner.d.ts +126 -0
  6. package/dist/boundaries/data-access-learner.d.ts.map +1 -0
  7. package/dist/boundaries/data-access-learner.js +486 -0
  8. package/dist/boundaries/data-access-learner.js.map +1 -0
  9. package/dist/boundaries/index.d.ts +6 -0
  10. package/dist/boundaries/index.d.ts.map +1 -1
  11. package/dist/boundaries/index.js +6 -0
  12. package/dist/boundaries/index.js.map +1 -1
  13. package/dist/boundaries/security-prioritizer.d.ts +118 -0
  14. package/dist/boundaries/security-prioritizer.d.ts.map +1 -0
  15. package/dist/boundaries/security-prioritizer.js +316 -0
  16. package/dist/boundaries/security-prioritizer.js.map +1 -0
  17. package/dist/call-graph/analysis/coverage-analyzer.d.ts +201 -0
  18. package/dist/call-graph/analysis/coverage-analyzer.d.ts.map +1 -0
  19. package/dist/call-graph/analysis/coverage-analyzer.js +553 -0
  20. package/dist/call-graph/analysis/coverage-analyzer.js.map +1 -0
  21. package/dist/call-graph/analysis/dead-code-detector.d.ts +145 -0
  22. package/dist/call-graph/analysis/dead-code-detector.d.ts.map +1 -0
  23. package/dist/call-graph/analysis/dead-code-detector.js +391 -0
  24. package/dist/call-graph/analysis/dead-code-detector.js.map +1 -0
  25. package/dist/call-graph/analysis/graph-builder.d.ts +142 -0
  26. package/dist/call-graph/analysis/graph-builder.d.ts.map +1 -0
  27. package/dist/call-graph/analysis/graph-builder.js +624 -0
  28. package/dist/call-graph/analysis/graph-builder.js.map +1 -0
  29. package/dist/call-graph/analysis/impact-analyzer.d.ts +150 -0
  30. package/dist/call-graph/analysis/impact-analyzer.d.ts.map +1 -0
  31. package/dist/call-graph/analysis/impact-analyzer.js +329 -0
  32. package/dist/call-graph/analysis/impact-analyzer.js.map +1 -0
  33. package/dist/call-graph/analysis/index.d.ts +11 -0
  34. package/dist/call-graph/analysis/index.d.ts.map +1 -0
  35. package/dist/call-graph/analysis/index.js +9 -0
  36. package/dist/call-graph/analysis/index.js.map +1 -0
  37. package/dist/call-graph/analysis/path-finder.d.ts +117 -0
  38. package/dist/call-graph/analysis/path-finder.d.ts.map +1 -0
  39. package/dist/call-graph/analysis/path-finder.js +360 -0
  40. package/dist/call-graph/analysis/path-finder.js.map +1 -0
  41. package/dist/call-graph/analysis/reachability.d.ts +56 -0
  42. package/dist/call-graph/analysis/reachability.d.ts.map +1 -0
  43. package/dist/call-graph/analysis/reachability.js +357 -0
  44. package/dist/call-graph/analysis/reachability.js.map +1 -0
  45. package/dist/call-graph/demo.d.ts +11 -0
  46. package/dist/call-graph/demo.d.ts.map +1 -0
  47. package/dist/call-graph/demo.js +339 -0
  48. package/dist/call-graph/demo.js.map +1 -0
  49. package/dist/call-graph/enrichment/enrichment-engine.d.ts +126 -0
  50. package/dist/call-graph/enrichment/enrichment-engine.d.ts.map +1 -0
  51. package/dist/call-graph/enrichment/enrichment-engine.js +760 -0
  52. package/dist/call-graph/enrichment/enrichment-engine.js.map +1 -0
  53. package/dist/call-graph/enrichment/impact-scorer.d.ts +59 -0
  54. package/dist/call-graph/enrichment/impact-scorer.d.ts.map +1 -0
  55. package/dist/call-graph/enrichment/impact-scorer.js +328 -0
  56. package/dist/call-graph/enrichment/impact-scorer.js.map +1 -0
  57. package/dist/call-graph/enrichment/index.d.ts +12 -0
  58. package/dist/call-graph/enrichment/index.d.ts.map +1 -0
  59. package/dist/call-graph/enrichment/index.js +15 -0
  60. package/dist/call-graph/enrichment/index.js.map +1 -0
  61. package/dist/call-graph/enrichment/remediation-generator.d.ts +41 -0
  62. package/dist/call-graph/enrichment/remediation-generator.d.ts.map +1 -0
  63. package/dist/call-graph/enrichment/remediation-generator.js +609 -0
  64. package/dist/call-graph/enrichment/remediation-generator.js.map +1 -0
  65. package/dist/call-graph/enrichment/sensitivity-classifier.d.ts +71 -0
  66. package/dist/call-graph/enrichment/sensitivity-classifier.d.ts.map +1 -0
  67. package/dist/call-graph/enrichment/sensitivity-classifier.js +454 -0
  68. package/dist/call-graph/enrichment/sensitivity-classifier.js.map +1 -0
  69. package/dist/call-graph/enrichment/types.d.ts +402 -0
  70. package/dist/call-graph/enrichment/types.d.ts.map +1 -0
  71. package/dist/call-graph/enrichment/types.js +9 -0
  72. package/dist/call-graph/enrichment/types.js.map +1 -0
  73. package/dist/call-graph/extractors/base-extractor.d.ts +112 -0
  74. package/dist/call-graph/extractors/base-extractor.d.ts.map +1 -0
  75. package/dist/call-graph/extractors/base-extractor.js +140 -0
  76. package/dist/call-graph/extractors/base-extractor.js.map +1 -0
  77. package/dist/call-graph/extractors/csharp-data-access-extractor.d.ts +76 -0
  78. package/dist/call-graph/extractors/csharp-data-access-extractor.d.ts.map +1 -0
  79. package/dist/call-graph/extractors/csharp-data-access-extractor.js +387 -0
  80. package/dist/call-graph/extractors/csharp-data-access-extractor.js.map +1 -0
  81. package/dist/call-graph/extractors/csharp-extractor.d.ts +87 -0
  82. package/dist/call-graph/extractors/csharp-extractor.d.ts.map +1 -0
  83. package/dist/call-graph/extractors/csharp-extractor.js +470 -0
  84. package/dist/call-graph/extractors/csharp-extractor.js.map +1 -0
  85. package/dist/call-graph/extractors/data-access-extractor.d.ts +76 -0
  86. package/dist/call-graph/extractors/data-access-extractor.d.ts.map +1 -0
  87. package/dist/call-graph/extractors/data-access-extractor.js +234 -0
  88. package/dist/call-graph/extractors/data-access-extractor.js.map +1 -0
  89. package/dist/call-graph/extractors/index.d.ts +26 -0
  90. package/dist/call-graph/extractors/index.d.ts.map +1 -0
  91. package/dist/call-graph/extractors/index.js +36 -0
  92. package/dist/call-graph/extractors/index.js.map +1 -0
  93. package/dist/call-graph/extractors/java-data-access-extractor.d.ts +101 -0
  94. package/dist/call-graph/extractors/java-data-access-extractor.d.ts.map +1 -0
  95. package/dist/call-graph/extractors/java-data-access-extractor.js +611 -0
  96. package/dist/call-graph/extractors/java-data-access-extractor.js.map +1 -0
  97. package/dist/call-graph/extractors/java-extractor.d.ts +87 -0
  98. package/dist/call-graph/extractors/java-extractor.d.ts.map +1 -0
  99. package/dist/call-graph/extractors/java-extractor.js +510 -0
  100. package/dist/call-graph/extractors/java-extractor.js.map +1 -0
  101. package/dist/call-graph/extractors/php-data-access-extractor.d.ts +93 -0
  102. package/dist/call-graph/extractors/php-data-access-extractor.d.ts.map +1 -0
  103. package/dist/call-graph/extractors/php-data-access-extractor.js +589 -0
  104. package/dist/call-graph/extractors/php-data-access-extractor.js.map +1 -0
  105. package/dist/call-graph/extractors/php-extractor.d.ts +104 -0
  106. package/dist/call-graph/extractors/php-extractor.d.ts.map +1 -0
  107. package/dist/call-graph/extractors/php-extractor.js +619 -0
  108. package/dist/call-graph/extractors/php-extractor.js.map +1 -0
  109. package/dist/call-graph/extractors/python-data-access-extractor.d.ts +90 -0
  110. package/dist/call-graph/extractors/python-data-access-extractor.d.ts.map +1 -0
  111. package/dist/call-graph/extractors/python-data-access-extractor.js +537 -0
  112. package/dist/call-graph/extractors/python-data-access-extractor.js.map +1 -0
  113. package/dist/call-graph/extractors/python-extractor.d.ts +98 -0
  114. package/dist/call-graph/extractors/python-extractor.d.ts.map +1 -0
  115. package/dist/call-graph/extractors/python-extractor.js +681 -0
  116. package/dist/call-graph/extractors/python-extractor.js.map +1 -0
  117. package/dist/call-graph/extractors/semantic-data-access-scanner.d.ts +91 -0
  118. package/dist/call-graph/extractors/semantic-data-access-scanner.d.ts.map +1 -0
  119. package/dist/call-graph/extractors/semantic-data-access-scanner.js +498 -0
  120. package/dist/call-graph/extractors/semantic-data-access-scanner.js.map +1 -0
  121. package/dist/call-graph/extractors/typescript-data-access-extractor.d.ts +122 -0
  122. package/dist/call-graph/extractors/typescript-data-access-extractor.d.ts.map +1 -0
  123. package/dist/call-graph/extractors/typescript-data-access-extractor.js +788 -0
  124. package/dist/call-graph/extractors/typescript-data-access-extractor.js.map +1 -0
  125. package/dist/call-graph/extractors/typescript-extractor.d.ts +145 -0
  126. package/dist/call-graph/extractors/typescript-extractor.d.ts.map +1 -0
  127. package/dist/call-graph/extractors/typescript-extractor.js +904 -0
  128. package/dist/call-graph/extractors/typescript-extractor.js.map +1 -0
  129. package/dist/call-graph/index.d.ts +127 -0
  130. package/dist/call-graph/index.d.ts.map +1 -0
  131. package/dist/call-graph/index.js +247 -0
  132. package/dist/call-graph/index.js.map +1 -0
  133. package/dist/call-graph/store/call-graph-store.d.ts +70 -0
  134. package/dist/call-graph/store/call-graph-store.d.ts.map +1 -0
  135. package/dist/call-graph/store/call-graph-store.js +210 -0
  136. package/dist/call-graph/store/call-graph-store.js.map +1 -0
  137. package/dist/call-graph/store/index.d.ts +7 -0
  138. package/dist/call-graph/store/index.d.ts.map +1 -0
  139. package/dist/call-graph/store/index.js +7 -0
  140. package/dist/call-graph/store/index.js.map +1 -0
  141. package/dist/call-graph/types.d.ts +376 -0
  142. package/dist/call-graph/types.d.ts.map +1 -0
  143. package/dist/call-graph/types.js +8 -0
  144. package/dist/call-graph/types.js.map +1 -0
  145. package/dist/index.d.ts +8 -0
  146. package/dist/index.d.ts.map +1 -1
  147. package/dist/index.js +12 -0
  148. package/dist/index.js.map +1 -1
  149. package/dist/lake/callgraph-shard-store.d.ts +168 -0
  150. package/dist/lake/callgraph-shard-store.d.ts.map +1 -0
  151. package/dist/lake/callgraph-shard-store.js +466 -0
  152. package/dist/lake/callgraph-shard-store.js.map +1 -0
  153. package/dist/lake/examples-store.d.ts +127 -0
  154. package/dist/lake/examples-store.d.ts.map +1 -0
  155. package/dist/lake/examples-store.js +389 -0
  156. package/dist/lake/examples-store.js.map +1 -0
  157. package/dist/lake/index-store.d.ts +82 -0
  158. package/dist/lake/index-store.d.ts.map +1 -0
  159. package/dist/lake/index-store.js +359 -0
  160. package/dist/lake/index-store.js.map +1 -0
  161. package/dist/lake/index.d.ts +93 -0
  162. package/dist/lake/index.d.ts.map +1 -0
  163. package/dist/lake/index.js +138 -0
  164. package/dist/lake/index.js.map +1 -0
  165. package/dist/lake/lake.bak/index-store.d.ts +82 -0
  166. package/dist/lake/lake.bak/index-store.d.ts.map +1 -0
  167. package/dist/lake/lake.bak/index-store.js +357 -0
  168. package/dist/lake/lake.bak/index-store.js.map +1 -0
  169. package/dist/lake/lake.bak/index.d.ts +81 -0
  170. package/dist/lake/lake.bak/index.d.ts.map +1 -0
  171. package/dist/lake/lake.bak/index.js +114 -0
  172. package/dist/lake/lake.bak/index.js.map +1 -0
  173. package/dist/lake/lake.bak/manifest-store.d.ts +51 -0
  174. package/dist/lake/lake.bak/manifest-store.d.ts.map +1 -0
  175. package/dist/lake/lake.bak/manifest-store.js +347 -0
  176. package/dist/lake/lake.bak/manifest-store.js.map +1 -0
  177. package/dist/lake/lake.bak/query-engine.d.ts +112 -0
  178. package/dist/lake/lake.bak/query-engine.d.ts.map +1 -0
  179. package/dist/lake/lake.bak/query-engine.js +370 -0
  180. package/dist/lake/lake.bak/query-engine.js.map +1 -0
  181. package/dist/lake/lake.bak/types.d.ts +428 -0
  182. package/dist/lake/lake.bak/types.d.ts.map +1 -0
  183. package/dist/lake/lake.bak/types.js +46 -0
  184. package/dist/lake/lake.bak/types.js.map +1 -0
  185. package/dist/lake/lake.bak/view-materializer.d.ts +70 -0
  186. package/dist/lake/lake.bak/view-materializer.d.ts.map +1 -0
  187. package/dist/lake/lake.bak/view-materializer.js +314 -0
  188. package/dist/lake/lake.bak/view-materializer.js.map +1 -0
  189. package/dist/lake/lake.bak/view-store.d.ts +57 -0
  190. package/dist/lake/lake.bak/view-store.d.ts.map +1 -0
  191. package/dist/lake/lake.bak/view-store.js +348 -0
  192. package/dist/lake/lake.bak/view-store.js.map +1 -0
  193. package/dist/lake/manifest-store.d.ts +51 -0
  194. package/dist/lake/manifest-store.d.ts.map +1 -0
  195. package/dist/lake/manifest-store.js +348 -0
  196. package/dist/lake/manifest-store.js.map +1 -0
  197. package/dist/lake/pattern-shard-store.d.ts +87 -0
  198. package/dist/lake/pattern-shard-store.d.ts.map +1 -0
  199. package/dist/lake/pattern-shard-store.js +347 -0
  200. package/dist/lake/pattern-shard-store.js.map +1 -0
  201. package/dist/lake/query-engine.d.ts +124 -0
  202. package/dist/lake/query-engine.d.ts.map +1 -0
  203. package/dist/lake/query-engine.js +453 -0
  204. package/dist/lake/query-engine.js.map +1 -0
  205. package/dist/lake/security-shard-store.d.ts +156 -0
  206. package/dist/lake/security-shard-store.d.ts.map +1 -0
  207. package/dist/lake/security-shard-store.js +498 -0
  208. package/dist/lake/security-shard-store.js.map +1 -0
  209. package/dist/lake/types.d.ts +428 -0
  210. package/dist/lake/types.d.ts.map +1 -0
  211. package/dist/lake/types.js +46 -0
  212. package/dist/lake/types.js.map +1 -0
  213. package/dist/lake/view-materializer.d.ts +70 -0
  214. package/dist/lake/view-materializer.d.ts.map +1 -0
  215. package/dist/lake/view-materializer.js +314 -0
  216. package/dist/lake/view-materializer.js.map +1 -0
  217. package/dist/lake/view-store.d.ts +57 -0
  218. package/dist/lake/view-store.d.ts.map +1 -0
  219. package/dist/lake/view-store.js +348 -0
  220. package/dist/lake/view-store.js.map +1 -0
  221. package/dist/parsers/tree-sitter/index.d.ts +1 -0
  222. package/dist/parsers/tree-sitter/index.d.ts.map +1 -1
  223. package/dist/parsers/tree-sitter/index.js +4 -0
  224. package/dist/parsers/tree-sitter/index.js.map +1 -1
  225. package/dist/parsers/tree-sitter/typescript-loader.d.ts +58 -0
  226. package/dist/parsers/tree-sitter/typescript-loader.d.ts.map +1 -0
  227. package/dist/parsers/tree-sitter/typescript-loader.js +250 -0
  228. package/dist/parsers/tree-sitter/typescript-loader.js.map +1 -0
  229. package/dist/store/project-config.d.ts +154 -0
  230. package/dist/store/project-config.d.ts.map +1 -0
  231. package/dist/store/project-config.js +235 -0
  232. package/dist/store/project-config.js.map +1 -0
  233. package/dist/store/project-registry.d.ts +241 -0
  234. package/dist/store/project-registry.d.ts.map +1 -0
  235. package/dist/store/project-registry.js +557 -0
  236. package/dist/store/project-registry.js.map +1 -0
  237. package/package.json +4 -2
@@ -0,0 +1,788 @@
1
+ /**
2
+ * TypeScript/JavaScript Semantic Data Access Extractor
3
+ *
4
+ * Extracts data access points from TypeScript/JavaScript using tree-sitter.
5
+ * Provides accurate, semantic-aware detection of database operations.
6
+ *
7
+ * Supports:
8
+ * - Supabase: supabase.from('table').select()
9
+ * - Prisma: prisma.user.findMany()
10
+ * - TypeORM: userRepository.find()
11
+ * - Sequelize: User.findAll()
12
+ * - Drizzle: db.select().from(users)
13
+ * - Knex: knex('users').select()
14
+ * - Mongoose: User.find({})
15
+ * - Raw SQL: db.query('SELECT * FROM users')
16
+ */
17
+ import { BaseDataAccessExtractor } from './data-access-extractor.js';
18
+ import { isTypeScriptTreeSitterAvailable, isJavaScriptTreeSitterAvailable, createParserForFile, } from '../../parsers/tree-sitter/typescript-loader.js';
19
+ /**
20
+ * TypeScript/JavaScript data access extractor using tree-sitter
21
+ */
22
+ export class TypeScriptDataAccessExtractor extends BaseDataAccessExtractor {
23
+ language = 'typescript';
24
+ extensions = ['.ts', '.tsx', '.js', '.jsx', '.mts', '.cts', '.mjs', '.cjs'];
25
+ parserCache = new Map();
26
+ /**
27
+ * Check if tree-sitter is available for TypeScript/JavaScript
28
+ */
29
+ static isAvailable() {
30
+ return isTypeScriptTreeSitterAvailable() || isJavaScriptTreeSitterAvailable();
31
+ }
32
+ /**
33
+ * Extract data access points from TypeScript/JavaScript source
34
+ */
35
+ extract(source, filePath) {
36
+ const result = this.createEmptyResult(filePath);
37
+ result.language = this.getLanguageFromPath(filePath);
38
+ const parser = this.getParser(filePath);
39
+ if (!parser) {
40
+ result.errors.push('Tree-sitter not available for TypeScript/JavaScript parsing');
41
+ return result;
42
+ }
43
+ try {
44
+ const tree = parser.parse(source);
45
+ this.visitNode(tree.rootNode, result, filePath, source);
46
+ }
47
+ catch (error) {
48
+ result.errors.push(error instanceof Error ? error.message : 'Unknown parse error');
49
+ }
50
+ return result;
51
+ }
52
+ getLanguageFromPath(filePath) {
53
+ if (filePath.endsWith('.ts') || filePath.endsWith('.tsx') ||
54
+ filePath.endsWith('.mts') || filePath.endsWith('.cts')) {
55
+ return 'typescript';
56
+ }
57
+ return 'javascript';
58
+ }
59
+ getParser(filePath) {
60
+ // Determine parser type based on extension
61
+ const ext = this.getParserExtension(filePath);
62
+ if (!this.parserCache.has(ext)) {
63
+ const parser = createParserForFile(filePath);
64
+ if (parser) {
65
+ this.parserCache.set(ext, parser);
66
+ }
67
+ }
68
+ return this.parserCache.get(ext) ?? null;
69
+ }
70
+ getParserExtension(filePath) {
71
+ const lower = filePath.toLowerCase();
72
+ if (lower.endsWith('.tsx'))
73
+ return '.tsx';
74
+ if (lower.endsWith('.ts') || lower.endsWith('.mts') || lower.endsWith('.cts'))
75
+ return '.ts';
76
+ if (lower.endsWith('.jsx'))
77
+ return '.jsx';
78
+ return '.js';
79
+ }
80
+ /**
81
+ * Visit AST nodes to find data access patterns
82
+ */
83
+ visitNode(node, result, filePath, source) {
84
+ // Check for call expressions
85
+ if (node.type === 'call_expression') {
86
+ this.analyzeCallExpression(node, result, filePath, source);
87
+ }
88
+ // Check for tagged template expressions (SQL template tags)
89
+ if (node.type === 'call_expression' && this.isTaggedTemplate(node)) {
90
+ this.analyzeTaggedTemplate(node, result, filePath, source);
91
+ }
92
+ // Recurse into children
93
+ for (const child of node.children) {
94
+ this.visitNode(child, result, filePath, source);
95
+ }
96
+ }
97
+ isTaggedTemplate(node) {
98
+ // Check if this is a tagged template literal
99
+ return node.children.some(c => c.type === 'template_string');
100
+ }
101
+ /**
102
+ * Analyze a call expression for data access patterns
103
+ */
104
+ analyzeCallExpression(node, result, filePath, _source) {
105
+ // Get the full call chain
106
+ const chain = this.getCallChain(node);
107
+ // Try each pattern
108
+ const accessPoint = this.trySupabasePattern(chain, node, filePath) ||
109
+ this.tryPrismaPattern(chain, node, filePath) ||
110
+ this.tryTypeORMPattern(chain, node, filePath) ||
111
+ this.trySequelizePattern(chain, node, filePath) ||
112
+ this.tryDrizzlePattern(chain, node, filePath) ||
113
+ this.tryKnexPattern(chain, node, filePath) ||
114
+ this.tryMongoosePattern(chain, node, filePath) ||
115
+ this.tryRawSQLPattern(chain, node, filePath);
116
+ if (accessPoint) {
117
+ // Avoid duplicates
118
+ const exists = result.accessPoints.some(ap => ap.id === accessPoint.id);
119
+ if (!exists) {
120
+ result.accessPoints.push(accessPoint);
121
+ }
122
+ }
123
+ }
124
+ /**
125
+ * Get the call chain from a call expression
126
+ * e.g., supabase.from('users').select('*') ->
127
+ * names: ['supabase', 'from', 'select']
128
+ * args: [[], ['users'], ['*']]
129
+ */
130
+ getCallChain(node) {
131
+ const names = [];
132
+ const args = [];
133
+ let current = node;
134
+ while (current) {
135
+ if (current.type === 'call_expression') {
136
+ // Get arguments
137
+ const argsNode = current.childForFieldName('arguments');
138
+ if (argsNode) {
139
+ const argList = [];
140
+ for (const child of argsNode.children) {
141
+ if (child.type !== '(' && child.type !== ')' && child.type !== ',') {
142
+ argList.push(child);
143
+ }
144
+ }
145
+ args.unshift(argList);
146
+ }
147
+ else {
148
+ args.unshift([]);
149
+ }
150
+ const funcNode = current.childForFieldName('function');
151
+ if (!funcNode)
152
+ break;
153
+ if (funcNode.type === 'member_expression') {
154
+ const propNode = funcNode.childForFieldName('property');
155
+ if (propNode) {
156
+ names.unshift(propNode.text);
157
+ }
158
+ const objNode = funcNode.childForFieldName('object');
159
+ current = objNode;
160
+ }
161
+ else if (funcNode.type === 'identifier') {
162
+ names.unshift(funcNode.text);
163
+ break;
164
+ }
165
+ else {
166
+ break;
167
+ }
168
+ }
169
+ else if (current.type === 'member_expression') {
170
+ const propNode = current.childForFieldName('property');
171
+ if (propNode) {
172
+ names.unshift(propNode.text);
173
+ }
174
+ args.unshift([]); // No args for property access
175
+ const objNode = current.childForFieldName('object');
176
+ current = objNode;
177
+ }
178
+ else if (current.type === 'identifier') {
179
+ names.unshift(current.text);
180
+ args.unshift([]); // No args for identifier
181
+ break;
182
+ }
183
+ else {
184
+ break;
185
+ }
186
+ }
187
+ return { names, args };
188
+ }
189
+ /**
190
+ * Try to match Supabase pattern: supabase.from('table').select()
191
+ */
192
+ trySupabasePattern(chain, node, filePath) {
193
+ const fromIndex = chain.names.indexOf('from');
194
+ if (fromIndex === -1)
195
+ return null;
196
+ // Get table name from .from('table')
197
+ const fromArgs = chain.args[fromIndex];
198
+ if (!fromArgs || fromArgs.length === 0)
199
+ return null;
200
+ const tableArg = fromArgs[0];
201
+ if (!tableArg)
202
+ return null;
203
+ const table = this.extractStringValue(tableArg);
204
+ if (!table)
205
+ return null;
206
+ // Determine operation from the chain
207
+ let operation = 'read';
208
+ let fields = [];
209
+ const whereFields = [];
210
+ for (let i = fromIndex + 1; i < chain.names.length; i++) {
211
+ const method = chain.names[i];
212
+ const methodArgs = chain.args[i];
213
+ if (method === 'select' && methodArgs && methodArgs.length > 0) {
214
+ const selectArg = methodArgs[0];
215
+ if (selectArg) {
216
+ const selectStr = this.extractStringValue(selectArg);
217
+ if (selectStr) {
218
+ fields = this.extractFieldsFromString(selectStr);
219
+ }
220
+ }
221
+ operation = 'read';
222
+ }
223
+ else if (method === 'insert' || method === 'upsert') {
224
+ operation = 'write';
225
+ if (methodArgs && methodArgs.length > 0 && methodArgs[0]) {
226
+ fields = this.extractFieldsFromObject(methodArgs[0]);
227
+ }
228
+ }
229
+ else if (method === 'update') {
230
+ operation = 'write';
231
+ if (methodArgs && methodArgs.length > 0 && methodArgs[0]) {
232
+ fields = this.extractFieldsFromObject(methodArgs[0]);
233
+ }
234
+ }
235
+ else if (method === 'delete') {
236
+ operation = 'delete';
237
+ }
238
+ // Extract fields from where clause methods
239
+ else if (method && this.isWhereClauseMethod(method) && methodArgs && methodArgs.length > 0) {
240
+ const fieldName = this.extractWhereClauseField(methodArgs);
241
+ if (fieldName) {
242
+ whereFields.push(fieldName);
243
+ }
244
+ }
245
+ }
246
+ // Merge select fields with where clause fields (deduplicated)
247
+ const allFields = [...new Set([...fields, ...whereFields])];
248
+ return this.createAccessPoint({
249
+ table,
250
+ fields: allFields,
251
+ operation,
252
+ file: filePath,
253
+ line: node.startPosition.row + 1,
254
+ column: node.startPosition.column,
255
+ context: node.text.slice(0, 200),
256
+ confidence: 0.95,
257
+ });
258
+ }
259
+ /**
260
+ * Check if a method name is a where clause method
261
+ */
262
+ isWhereClauseMethod(method) {
263
+ const whereClauseMethods = [
264
+ // Supabase/PostgREST
265
+ 'eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'like', 'ilike', 'is', 'in',
266
+ 'contains', 'containedBy', 'rangeGt', 'rangeGte', 'rangeLt', 'rangeLte',
267
+ 'rangeAdjacent', 'overlaps', 'textSearch', 'match', 'not', 'or', 'filter',
268
+ // Prisma
269
+ 'where', 'findFirst', 'findUnique',
270
+ // TypeORM/Sequelize
271
+ 'where', 'andWhere', 'orWhere', 'whereIn', 'whereNotIn',
272
+ // Knex
273
+ 'where', 'whereNot', 'whereIn', 'whereNotIn', 'whereNull', 'whereNotNull',
274
+ 'whereBetween', 'whereNotBetween', 'whereRaw', 'whereExists',
275
+ // Mongoose
276
+ 'where', 'equals', 'ne', 'gt', 'gte', 'lt', 'lte', 'in', 'nin', 'regex',
277
+ ];
278
+ return whereClauseMethods.includes(method);
279
+ }
280
+ /**
281
+ * Extract field name from a where clause method call
282
+ */
283
+ extractWhereClauseField(args) {
284
+ if (args.length === 0)
285
+ return null;
286
+ // For methods like .eq('field', value), .where('field', value)
287
+ // The first argument is typically the field name
288
+ const firstArg = args[0];
289
+ if (!firstArg)
290
+ return null;
291
+ // Direct string argument: .eq('email', value)
292
+ const strValue = this.extractStringValue(firstArg);
293
+ if (strValue) {
294
+ return strValue;
295
+ }
296
+ // Object argument: .where({ email: value })
297
+ if (firstArg.type === 'object') {
298
+ const objectFields = this.extractFieldsFromObject(firstArg);
299
+ return objectFields[0] ?? null;
300
+ }
301
+ // Identifier: .where(email, value) - less common but possible
302
+ if (firstArg.type === 'identifier') {
303
+ return firstArg.text;
304
+ }
305
+ return null;
306
+ }
307
+ /**
308
+ * Try to match Prisma pattern: prisma.user.findMany()
309
+ */
310
+ tryPrismaPattern(chain, node, filePath) {
311
+ if (chain.names.length < 3)
312
+ return null;
313
+ const firstPart = chain.names[0]?.toLowerCase();
314
+ if (!firstPart?.includes('prisma'))
315
+ return null;
316
+ const modelName = chain.names[1];
317
+ const methodName = chain.names[2];
318
+ if (!modelName || !methodName)
319
+ return null;
320
+ // Skip if model name looks like a method
321
+ const prismaClientMethods = ['$connect', '$disconnect', '$transaction', '$queryRaw', '$executeRaw'];
322
+ if (prismaClientMethods.includes(modelName))
323
+ return null;
324
+ const operation = this.detectOperation(methodName);
325
+ if (!operation)
326
+ return null;
327
+ // Extract fields from select/include
328
+ let fields = [];
329
+ const methodArgs = chain.args[2];
330
+ if (methodArgs && methodArgs.length > 0 && methodArgs[0]) {
331
+ fields = this.extractPrismaFields(methodArgs[0]);
332
+ }
333
+ return this.createAccessPoint({
334
+ table: this.inferTableFromName(modelName),
335
+ fields,
336
+ operation,
337
+ file: filePath,
338
+ line: node.startPosition.row + 1,
339
+ column: node.startPosition.column,
340
+ context: node.text.slice(0, 200),
341
+ confidence: 0.95,
342
+ });
343
+ }
344
+ /**
345
+ * Try to match TypeORM pattern: repository.find() or Entity.find()
346
+ */
347
+ tryTypeORMPattern(chain, node, filePath) {
348
+ if (chain.names.length < 2)
349
+ return null;
350
+ const receiverName = chain.names[0];
351
+ const methodName = chain.names[1];
352
+ if (!receiverName || !methodName)
353
+ return null;
354
+ const isRepository = receiverName.toLowerCase().includes('repository') ||
355
+ receiverName.toLowerCase().includes('repo');
356
+ const isEntity = /^[A-Z]/.test(receiverName) &&
357
+ !['Array', 'Object', 'String', 'Number', 'Boolean', 'Promise', 'Map', 'Set'].includes(receiverName);
358
+ if (!isRepository && !isEntity)
359
+ return null;
360
+ const typeormMethods = ['find', 'findOne', 'findOneBy', 'findBy', 'findAndCount', 'save', 'insert', 'update', 'delete', 'remove', 'count'];
361
+ if (!typeormMethods.includes(methodName))
362
+ return null;
363
+ const operation = this.detectOperation(methodName);
364
+ if (!operation)
365
+ return null;
366
+ return this.createAccessPoint({
367
+ table: this.inferTableFromName(receiverName),
368
+ fields: [],
369
+ operation,
370
+ file: filePath,
371
+ line: node.startPosition.row + 1,
372
+ column: node.startPosition.column,
373
+ context: node.text.slice(0, 200),
374
+ confidence: 0.85,
375
+ });
376
+ }
377
+ /**
378
+ * Try to match Sequelize pattern: Model.findAll()
379
+ */
380
+ trySequelizePattern(chain, node, filePath) {
381
+ if (chain.names.length < 2)
382
+ return null;
383
+ const modelName = chain.names[0];
384
+ const methodName = chain.names[1];
385
+ if (!modelName || !methodName)
386
+ return null;
387
+ if (!/^[A-Z]/.test(modelName))
388
+ return null;
389
+ const sequelizeMethods = ['findAll', 'findOne', 'findByPk', 'findOrCreate', 'create', 'bulkCreate', 'update', 'destroy', 'count'];
390
+ if (!sequelizeMethods.includes(methodName))
391
+ return null;
392
+ const operation = this.detectOperation(methodName);
393
+ if (!operation)
394
+ return null;
395
+ // Extract fields from attributes option
396
+ let fields = [];
397
+ const methodArgs = chain.args[1];
398
+ if (methodArgs && methodArgs.length > 0 && methodArgs[0]) {
399
+ fields = this.extractSequelizeFields(methodArgs[0]);
400
+ }
401
+ return this.createAccessPoint({
402
+ table: this.inferTableFromName(modelName),
403
+ fields,
404
+ operation,
405
+ file: filePath,
406
+ line: node.startPosition.row + 1,
407
+ column: node.startPosition.column,
408
+ context: node.text.slice(0, 200),
409
+ confidence: 0.85,
410
+ });
411
+ }
412
+ /**
413
+ * Try to match Drizzle pattern: db.select().from(table)
414
+ */
415
+ tryDrizzlePattern(chain, node, filePath) {
416
+ const fromIndex = chain.names.indexOf('from');
417
+ if (fromIndex === -1)
418
+ return null;
419
+ const hasSelect = chain.names.includes('select') || chain.names.includes('selectDistinct');
420
+ const hasInsert = chain.names.includes('insert');
421
+ const hasUpdate = chain.names.includes('update');
422
+ const hasDelete = chain.names.includes('delete');
423
+ if (!hasSelect && !hasInsert && !hasUpdate && !hasDelete)
424
+ return null;
425
+ const fromArgs = chain.args[fromIndex];
426
+ if (!fromArgs || fromArgs.length === 0)
427
+ return null;
428
+ const tableArg = fromArgs[0];
429
+ if (!tableArg)
430
+ return null;
431
+ let table = 'unknown';
432
+ if (tableArg.type === 'identifier') {
433
+ table = this.inferTableFromName(tableArg.text);
434
+ }
435
+ else {
436
+ const strVal = this.extractStringValue(tableArg);
437
+ if (strVal)
438
+ table = strVal;
439
+ }
440
+ let operation = 'read';
441
+ if (hasInsert)
442
+ operation = 'write';
443
+ if (hasUpdate)
444
+ operation = 'write';
445
+ if (hasDelete)
446
+ operation = 'delete';
447
+ return this.createAccessPoint({
448
+ table,
449
+ fields: [],
450
+ operation,
451
+ file: filePath,
452
+ line: node.startPosition.row + 1,
453
+ column: node.startPosition.column,
454
+ context: node.text.slice(0, 200),
455
+ confidence: 0.9,
456
+ });
457
+ }
458
+ /**
459
+ * Try to match Knex pattern: knex('table').select()
460
+ */
461
+ tryKnexPattern(chain, node, filePath) {
462
+ if (chain.names.length < 2)
463
+ return null;
464
+ const firstPart = chain.names[0]?.toLowerCase();
465
+ if (!firstPart?.includes('knex') && !firstPart?.includes('db'))
466
+ return null;
467
+ const firstArgs = chain.args[0];
468
+ if (!firstArgs || firstArgs.length === 0)
469
+ return null;
470
+ const tableArg = firstArgs[0];
471
+ if (!tableArg)
472
+ return null;
473
+ const table = this.extractStringValue(tableArg);
474
+ if (!table)
475
+ return null;
476
+ let operation = 'read';
477
+ for (const method of chain.names) {
478
+ const op = this.detectOperation(method);
479
+ if (op) {
480
+ operation = op;
481
+ break;
482
+ }
483
+ }
484
+ return this.createAccessPoint({
485
+ table,
486
+ fields: [],
487
+ operation,
488
+ file: filePath,
489
+ line: node.startPosition.row + 1,
490
+ column: node.startPosition.column,
491
+ context: node.text.slice(0, 200),
492
+ confidence: 0.9,
493
+ });
494
+ }
495
+ /**
496
+ * Try to match Mongoose pattern: User.find({})
497
+ */
498
+ tryMongoosePattern(chain, node, filePath) {
499
+ if (chain.names.length < 2)
500
+ return null;
501
+ const modelName = chain.names[0];
502
+ const methodName = chain.names[1];
503
+ if (!modelName || !methodName)
504
+ return null;
505
+ if (!/^[A-Z]/.test(modelName))
506
+ return null;
507
+ const mongooseMethods = {
508
+ read: ['find', 'findOne', 'findById', 'findOneAndUpdate', 'findByIdAndUpdate',
509
+ 'countDocuments', 'estimatedDocumentCount', 'distinct', 'exists',
510
+ 'aggregate', 'populate', 'lean', 'exec'],
511
+ write: ['create', 'insertMany', 'save', 'updateOne', 'updateMany',
512
+ 'findOneAndUpdate', 'findByIdAndUpdate', 'replaceOne', 'bulkWrite'],
513
+ delete: ['deleteOne', 'deleteMany', 'findOneAndDelete', 'findByIdAndDelete',
514
+ 'findOneAndRemove', 'findByIdAndRemove', 'remove'],
515
+ };
516
+ let operation = null;
517
+ if (mongooseMethods.read.includes(methodName)) {
518
+ operation = 'read';
519
+ }
520
+ else if (mongooseMethods.write.includes(methodName)) {
521
+ operation = 'write';
522
+ }
523
+ else if (mongooseMethods.delete.includes(methodName)) {
524
+ operation = 'delete';
525
+ }
526
+ if (!operation)
527
+ return null;
528
+ // Extract fields from projection
529
+ let fields = [];
530
+ const methodArgs = chain.args[1];
531
+ if (methodArgs && methodArgs.length >= 2) {
532
+ const projectionArg = methodArgs[1];
533
+ if (projectionArg) {
534
+ if (projectionArg.type === 'object') {
535
+ fields = this.extractFieldsFromObject(projectionArg);
536
+ }
537
+ else {
538
+ const strVal = this.extractStringValue(projectionArg);
539
+ if (strVal) {
540
+ fields = strVal.split(/\s+/).filter(f => f && !f.startsWith('-'));
541
+ }
542
+ }
543
+ }
544
+ }
545
+ return this.createAccessPoint({
546
+ table: this.inferTableFromName(modelName),
547
+ fields,
548
+ operation,
549
+ file: filePath,
550
+ line: node.startPosition.row + 1,
551
+ column: node.startPosition.column,
552
+ context: node.text.slice(0, 200),
553
+ confidence: 0.85,
554
+ });
555
+ }
556
+ /**
557
+ * Try to match raw SQL pattern: db.query('SELECT * FROM users')
558
+ */
559
+ tryRawSQLPattern(chain, node, filePath) {
560
+ const queryMethods = ['query', 'execute', 'raw', 'rawQuery', '$queryRaw', '$executeRaw', 'sql'];
561
+ const methodIndex = chain.names.findIndex(n => queryMethods.includes(n));
562
+ if (methodIndex === -1)
563
+ return null;
564
+ const methodArgs = chain.args[methodIndex];
565
+ if (!methodArgs || methodArgs.length === 0)
566
+ return null;
567
+ const sqlArg = methodArgs[0];
568
+ if (!sqlArg)
569
+ return null;
570
+ let sqlText = this.extractStringValue(sqlArg);
571
+ if (!sqlText && sqlArg.type === 'template_string') {
572
+ sqlText = sqlArg.text;
573
+ }
574
+ if (!sqlText)
575
+ return null;
576
+ const { table, operation, fields } = this.parseSQLStatement(sqlText);
577
+ if (!table || table === 'unknown')
578
+ return null;
579
+ return this.createAccessPoint({
580
+ table,
581
+ fields,
582
+ operation,
583
+ file: filePath,
584
+ line: node.startPosition.row + 1,
585
+ column: node.startPosition.column,
586
+ context: node.text.slice(0, 200),
587
+ isRawSql: true,
588
+ confidence: 0.8,
589
+ });
590
+ }
591
+ /**
592
+ * Analyze tagged template expressions for SQL
593
+ */
594
+ analyzeTaggedTemplate(node, result, filePath, _source) {
595
+ // Get the tag name
596
+ const funcNode = node.childForFieldName('function');
597
+ if (!funcNode)
598
+ return;
599
+ let tagName = '';
600
+ if (funcNode.type === 'identifier') {
601
+ tagName = funcNode.text;
602
+ }
603
+ else if (funcNode.type === 'member_expression') {
604
+ const propNode = funcNode.childForFieldName('property');
605
+ if (propNode)
606
+ tagName = propNode.text;
607
+ }
608
+ const sqlTags = ['sql', 'Sql', 'SQL', 'raw', 'query'];
609
+ if (!sqlTags.includes(tagName))
610
+ return;
611
+ // Get the template content
612
+ const templateNode = node.children.find(c => c.type === 'template_string');
613
+ if (!templateNode)
614
+ return;
615
+ const sqlText = templateNode.text;
616
+ if (!sqlText)
617
+ return;
618
+ const { table, operation, fields } = this.parseSQLStatement(sqlText);
619
+ if (!table || table === 'unknown')
620
+ return;
621
+ const accessPoint = this.createAccessPoint({
622
+ table,
623
+ fields,
624
+ operation,
625
+ file: filePath,
626
+ line: node.startPosition.row + 1,
627
+ column: node.startPosition.column,
628
+ context: node.text.slice(0, 200),
629
+ isRawSql: true,
630
+ confidence: 0.8,
631
+ });
632
+ const exists = result.accessPoints.some(ap => ap.id === accessPoint.id);
633
+ if (!exists) {
634
+ result.accessPoints.push(accessPoint);
635
+ }
636
+ }
637
+ /**
638
+ * Parse a SQL statement to extract table, operation, and fields
639
+ */
640
+ parseSQLStatement(sql) {
641
+ const upperSql = sql.toUpperCase().trim();
642
+ let operation = 'unknown';
643
+ let table = 'unknown';
644
+ const fields = [];
645
+ if (upperSql.startsWith('SELECT')) {
646
+ operation = 'read';
647
+ }
648
+ else if (upperSql.startsWith('INSERT')) {
649
+ operation = 'write';
650
+ }
651
+ else if (upperSql.startsWith('UPDATE')) {
652
+ operation = 'write';
653
+ }
654
+ else if (upperSql.startsWith('DELETE')) {
655
+ operation = 'delete';
656
+ }
657
+ const fromMatch = sql.match(/FROM\s+["'`]?(\w+)["'`]?/i);
658
+ const intoMatch = sql.match(/INTO\s+["'`]?(\w+)["'`]?/i);
659
+ const updateMatch = sql.match(/UPDATE\s+["'`]?(\w+)["'`]?/i);
660
+ if (fromMatch?.[1])
661
+ table = fromMatch[1];
662
+ else if (intoMatch?.[1])
663
+ table = intoMatch[1];
664
+ else if (updateMatch?.[1])
665
+ table = updateMatch[1];
666
+ if (operation === 'read') {
667
+ const selectMatch = sql.match(/SELECT\s+(.+?)\s+FROM/i);
668
+ if (selectMatch?.[1] && selectMatch[1] !== '*') {
669
+ const fieldList = selectMatch[1].split(',').map(f => f.trim());
670
+ for (const field of fieldList) {
671
+ const fieldName = field.split(/\s+as\s+/i)[0]?.trim();
672
+ if (fieldName && !fieldName.includes('(')) {
673
+ fields.push(fieldName.replace(/["'`]/g, ''));
674
+ }
675
+ }
676
+ }
677
+ }
678
+ return { table, operation, fields };
679
+ }
680
+ /**
681
+ * Extract string value from a node
682
+ */
683
+ extractStringValue(node) {
684
+ if (!node)
685
+ return null;
686
+ if (node.type === 'string') {
687
+ // Remove quotes
688
+ return node.text.replace(/^['"`]|['"`]$/g, '');
689
+ }
690
+ if (node.type === 'template_string') {
691
+ // Remove backticks
692
+ return node.text.replace(/^`|`$/g, '');
693
+ }
694
+ return null;
695
+ }
696
+ /**
697
+ * Extract fields from an object literal
698
+ */
699
+ extractFieldsFromObject(node) {
700
+ if (!node)
701
+ return [];
702
+ const fields = [];
703
+ if (node.type === 'object') {
704
+ for (const child of node.children) {
705
+ if (child.type === 'pair') {
706
+ const keyNode = child.childForFieldName('key');
707
+ if (keyNode) {
708
+ if (keyNode.type === 'property_identifier' || keyNode.type === 'identifier') {
709
+ fields.push(keyNode.text);
710
+ }
711
+ else if (keyNode.type === 'string') {
712
+ fields.push(keyNode.text.replace(/^['"`]|['"`]$/g, ''));
713
+ }
714
+ }
715
+ }
716
+ else if (child.type === 'shorthand_property_identifier') {
717
+ fields.push(child.text);
718
+ }
719
+ }
720
+ }
721
+ else if (node.type === 'array') {
722
+ // For array of objects, extract from first element
723
+ const firstElem = node.children.find(c => c.type === 'object');
724
+ if (firstElem) {
725
+ return this.extractFieldsFromObject(firstElem);
726
+ }
727
+ }
728
+ return fields;
729
+ }
730
+ /**
731
+ * Extract fields from Prisma select/include options
732
+ */
733
+ extractPrismaFields(node) {
734
+ if (!node || node.type !== 'object')
735
+ return [];
736
+ const fields = [];
737
+ for (const child of node.children) {
738
+ if (child.type === 'pair') {
739
+ const keyNode = child.childForFieldName('key');
740
+ const valueNode = child.childForFieldName('value');
741
+ if (keyNode && (keyNode.text === 'select' || keyNode.text === 'include')) {
742
+ if (valueNode && valueNode.type === 'object') {
743
+ for (const selectChild of valueNode.children) {
744
+ if (selectChild.type === 'pair') {
745
+ const selectKey = selectChild.childForFieldName('key');
746
+ if (selectKey) {
747
+ fields.push(selectKey.text);
748
+ }
749
+ }
750
+ }
751
+ }
752
+ }
753
+ }
754
+ }
755
+ return fields;
756
+ }
757
+ /**
758
+ * Extract fields from Sequelize attributes option
759
+ */
760
+ extractSequelizeFields(node) {
761
+ if (!node || node.type !== 'object')
762
+ return [];
763
+ const fields = [];
764
+ for (const child of node.children) {
765
+ if (child.type === 'pair') {
766
+ const keyNode = child.childForFieldName('key');
767
+ const valueNode = child.childForFieldName('value');
768
+ if (keyNode && keyNode.text === 'attributes') {
769
+ if (valueNode && valueNode.type === 'array') {
770
+ for (const elem of valueNode.children) {
771
+ if (elem.type === 'string') {
772
+ fields.push(elem.text.replace(/^['"`]|['"`]$/g, ''));
773
+ }
774
+ }
775
+ }
776
+ }
777
+ }
778
+ }
779
+ return fields;
780
+ }
781
+ }
782
+ /**
783
+ * Create a TypeScript data access extractor
784
+ */
785
+ export function createTypeScriptDataAccessExtractor() {
786
+ return new TypeScriptDataAccessExtractor();
787
+ }
788
+ //# sourceMappingURL=typescript-data-access-extractor.js.map