@ai-agent-context/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +285 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/adapters/filesystem/fs-adapter.d.ts +50 -0
  5. package/dist/adapters/filesystem/fs-adapter.d.ts.map +1 -0
  6. package/dist/adapters/filesystem/fs-adapter.js +168 -0
  7. package/dist/adapters/filesystem/fs-adapter.js.map +1 -0
  8. package/dist/adapters/filesystem/sensitive.d.ts +26 -0
  9. package/dist/adapters/filesystem/sensitive.d.ts.map +1 -0
  10. package/dist/adapters/filesystem/sensitive.js +67 -0
  11. package/dist/adapters/filesystem/sensitive.js.map +1 -0
  12. package/dist/adapters/git/git-adapter.d.ts +47 -0
  13. package/dist/adapters/git/git-adapter.d.ts.map +1 -0
  14. package/dist/adapters/git/git-adapter.js +109 -0
  15. package/dist/adapters/git/git-adapter.js.map +1 -0
  16. package/dist/adapters/language/json/adapter.d.ts +24 -0
  17. package/dist/adapters/language/json/adapter.d.ts.map +1 -0
  18. package/dist/adapters/language/json/adapter.js +54 -0
  19. package/dist/adapters/language/json/adapter.js.map +1 -0
  20. package/dist/adapters/language/markdown/adapter.d.ts +41 -0
  21. package/dist/adapters/language/markdown/adapter.d.ts.map +1 -0
  22. package/dist/adapters/language/markdown/adapter.js +124 -0
  23. package/dist/adapters/language/markdown/adapter.js.map +1 -0
  24. package/dist/adapters/language/package-json/adapter.d.ts +47 -0
  25. package/dist/adapters/language/package-json/adapter.d.ts.map +1 -0
  26. package/dist/adapters/language/package-json/adapter.js +153 -0
  27. package/dist/adapters/language/package-json/adapter.js.map +1 -0
  28. package/dist/adapters/language/registry.d.ts +35 -0
  29. package/dist/adapters/language/registry.d.ts.map +1 -0
  30. package/dist/adapters/language/registry.js +79 -0
  31. package/dist/adapters/language/registry.js.map +1 -0
  32. package/dist/adapters/language/ts-js/adapter.d.ts +14 -0
  33. package/dist/adapters/language/ts-js/adapter.d.ts.map +1 -0
  34. package/dist/adapters/language/ts-js/adapter.js +59 -0
  35. package/dist/adapters/language/ts-js/adapter.js.map +1 -0
  36. package/dist/adapters/language/ts-js/extractor.d.ts +21 -0
  37. package/dist/adapters/language/ts-js/extractor.d.ts.map +1 -0
  38. package/dist/adapters/language/ts-js/extractor.js +779 -0
  39. package/dist/adapters/language/ts-js/extractor.js.map +1 -0
  40. package/dist/adapters/language/ts-js/lexer.d.ts +30 -0
  41. package/dist/adapters/language/ts-js/lexer.d.ts.map +1 -0
  42. package/dist/adapters/language/ts-js/lexer.js +446 -0
  43. package/dist/adapters/language/ts-js/lexer.js.map +1 -0
  44. package/dist/adapters/language/ts-js/package-signals.d.ts +5 -0
  45. package/dist/adapters/language/ts-js/package-signals.d.ts.map +1 -0
  46. package/dist/adapters/language/ts-js/package-signals.js +113 -0
  47. package/dist/adapters/language/ts-js/package-signals.js.map +1 -0
  48. package/dist/adapters/language/tsconfig/adapter.d.ts +38 -0
  49. package/dist/adapters/language/tsconfig/adapter.d.ts.map +1 -0
  50. package/dist/adapters/language/tsconfig/adapter.js +130 -0
  51. package/dist/adapters/language/tsconfig/adapter.js.map +1 -0
  52. package/dist/adapters/language/types.d.ts +152 -0
  53. package/dist/adapters/language/types.d.ts.map +1 -0
  54. package/dist/adapters/language/types.js +51 -0
  55. package/dist/adapters/language/types.js.map +1 -0
  56. package/dist/agent-context.d.ts +122 -0
  57. package/dist/agent-context.d.ts.map +1 -0
  58. package/dist/agent-context.js +243 -0
  59. package/dist/agent-context.js.map +1 -0
  60. package/dist/config/defaults.d.ts +13 -0
  61. package/dist/config/defaults.d.ts.map +1 -0
  62. package/dist/config/defaults.js +98 -0
  63. package/dist/config/defaults.js.map +1 -0
  64. package/dist/config/load.d.ts +25 -0
  65. package/dist/config/load.d.ts.map +1 -0
  66. package/dist/config/load.js +106 -0
  67. package/dist/config/load.js.map +1 -0
  68. package/dist/config/types.d.ts +95 -0
  69. package/dist/config/types.d.ts.map +1 -0
  70. package/dist/config/types.js +8 -0
  71. package/dist/config/types.js.map +1 -0
  72. package/dist/config/validate.d.ts +15 -0
  73. package/dist/config/validate.d.ts.map +1 -0
  74. package/dist/config/validate.js +202 -0
  75. package/dist/config/validate.js.map +1 -0
  76. package/dist/core/architecture/detector.d.ts +10 -0
  77. package/dist/core/architecture/detector.d.ts.map +1 -0
  78. package/dist/core/architecture/detector.js +37 -0
  79. package/dist/core/architecture/detector.js.map +1 -0
  80. package/dist/core/architecture/entry-points.d.ts +20 -0
  81. package/dist/core/architecture/entry-points.d.ts.map +1 -0
  82. package/dist/core/architecture/entry-points.js +190 -0
  83. package/dist/core/architecture/entry-points.js.map +1 -0
  84. package/dist/core/architecture/role-detector.d.ts +32 -0
  85. package/dist/core/architecture/role-detector.d.ts.map +1 -0
  86. package/dist/core/architecture/role-detector.js +240 -0
  87. package/dist/core/architecture/role-detector.js.map +1 -0
  88. package/dist/core/context/explain.d.ts +36 -0
  89. package/dist/core/context/explain.d.ts.map +1 -0
  90. package/dist/core/context/explain.js +199 -0
  91. package/dist/core/context/explain.js.map +1 -0
  92. package/dist/core/context/repository-context.d.ts +16 -0
  93. package/dist/core/context/repository-context.d.ts.map +1 -0
  94. package/dist/core/context/repository-context.js +134 -0
  95. package/dist/core/context/repository-context.js.map +1 -0
  96. package/dist/core/context/types.d.ts +94 -0
  97. package/dist/core/context/types.d.ts.map +1 -0
  98. package/dist/core/context/types.js +2 -0
  99. package/dist/core/context/types.js.map +1 -0
  100. package/dist/core/conventions/detector.d.ts +55 -0
  101. package/dist/core/conventions/detector.d.ts.map +1 -0
  102. package/dist/core/conventions/detector.js +438 -0
  103. package/dist/core/conventions/detector.js.map +1 -0
  104. package/dist/core/conventions/renderer.d.ts +16 -0
  105. package/dist/core/conventions/renderer.d.ts.map +1 -0
  106. package/dist/core/conventions/renderer.js +42 -0
  107. package/dist/core/conventions/renderer.js.map +1 -0
  108. package/dist/core/decisions/detector.d.ts +45 -0
  109. package/dist/core/decisions/detector.d.ts.map +1 -0
  110. package/dist/core/decisions/detector.js +256 -0
  111. package/dist/core/decisions/detector.js.map +1 -0
  112. package/dist/core/diff/differ.d.ts +26 -0
  113. package/dist/core/diff/differ.d.ts.map +1 -0
  114. package/dist/core/diff/differ.js +159 -0
  115. package/dist/core/diff/differ.js.map +1 -0
  116. package/dist/core/git-analysis/activity.d.ts +35 -0
  117. package/dist/core/git-analysis/activity.d.ts.map +1 -0
  118. package/dist/core/git-analysis/activity.js +123 -0
  119. package/dist/core/git-analysis/activity.js.map +1 -0
  120. package/dist/core/graph/aliases.d.ts +20 -0
  121. package/dist/core/graph/aliases.d.ts.map +1 -0
  122. package/dist/core/graph/aliases.js +99 -0
  123. package/dist/core/graph/aliases.js.map +1 -0
  124. package/dist/core/graph/knowledge-graph.d.ts +91 -0
  125. package/dist/core/graph/knowledge-graph.d.ts.map +1 -0
  126. package/dist/core/graph/knowledge-graph.js +572 -0
  127. package/dist/core/graph/knowledge-graph.js.map +1 -0
  128. package/dist/core/graph/modules.d.ts +28 -0
  129. package/dist/core/graph/modules.d.ts.map +1 -0
  130. package/dist/core/graph/modules.js +139 -0
  131. package/dist/core/graph/modules.js.map +1 -0
  132. package/dist/core/graph/resolver.d.ts +50 -0
  133. package/dist/core/graph/resolver.d.ts.map +1 -0
  134. package/dist/core/graph/resolver.js +180 -0
  135. package/dist/core/graph/resolver.js.map +1 -0
  136. package/dist/core/scanner/cache.d.ts +49 -0
  137. package/dist/core/scanner/cache.d.ts.map +1 -0
  138. package/dist/core/scanner/cache.js +101 -0
  139. package/dist/core/scanner/cache.js.map +1 -0
  140. package/dist/core/scanner/scanner.d.ts +60 -0
  141. package/dist/core/scanner/scanner.d.ts.map +1 -0
  142. package/dist/core/scanner/scanner.js +304 -0
  143. package/dist/core/scanner/scanner.js.map +1 -0
  144. package/dist/core/scanner/types.d.ts +47 -0
  145. package/dist/core/scanner/types.d.ts.map +1 -0
  146. package/dist/core/scanner/types.js +18 -0
  147. package/dist/core/scanner/types.js.map +1 -0
  148. package/dist/core/search/analyzer.d.ts +16 -0
  149. package/dist/core/search/analyzer.d.ts.map +1 -0
  150. package/dist/core/search/analyzer.js +165 -0
  151. package/dist/core/search/analyzer.js.map +1 -0
  152. package/dist/core/search/bm25.d.ts +21 -0
  153. package/dist/core/search/bm25.d.ts.map +1 -0
  154. package/dist/core/search/bm25.js +108 -0
  155. package/dist/core/search/bm25.js.map +1 -0
  156. package/dist/core/search/documents.d.ts +20 -0
  157. package/dist/core/search/documents.d.ts.map +1 -0
  158. package/dist/core/search/documents.js +106 -0
  159. package/dist/core/search/documents.js.map +1 -0
  160. package/dist/core/search/service.d.ts +30 -0
  161. package/dist/core/search/service.d.ts.map +1 -0
  162. package/dist/core/search/service.js +54 -0
  163. package/dist/core/search/service.js.map +1 -0
  164. package/dist/core/search/types.d.ts +44 -0
  165. package/dist/core/search/types.d.ts.map +1 -0
  166. package/dist/core/search/types.js +2 -0
  167. package/dist/core/search/types.js.map +1 -0
  168. package/dist/core/serialization/documents.d.ts +26 -0
  169. package/dist/core/serialization/documents.d.ts.map +1 -0
  170. package/dist/core/serialization/documents.js +224 -0
  171. package/dist/core/serialization/documents.js.map +1 -0
  172. package/dist/core/serialization/reader.d.ts +22 -0
  173. package/dist/core/serialization/reader.d.ts.map +1 -0
  174. package/dist/core/serialization/reader.js +65 -0
  175. package/dist/core/serialization/reader.js.map +1 -0
  176. package/dist/core/serialization/types.d.ts +184 -0
  177. package/dist/core/serialization/types.d.ts.map +1 -0
  178. package/dist/core/serialization/types.js +2 -0
  179. package/dist/core/serialization/types.js.map +1 -0
  180. package/dist/core/serialization/writer.d.ts +17 -0
  181. package/dist/core/serialization/writer.d.ts.map +1 -0
  182. package/dist/core/serialization/writer.js +64 -0
  183. package/dist/core/serialization/writer.js.map +1 -0
  184. package/dist/core/workspace/package-facts.d.ts +12 -0
  185. package/dist/core/workspace/package-facts.d.ts.map +1 -0
  186. package/dist/core/workspace/package-facts.js +50 -0
  187. package/dist/core/workspace/package-facts.js.map +1 -0
  188. package/dist/core/workspace/workspace.d.ts +32 -0
  189. package/dist/core/workspace/workspace.d.ts.map +1 -0
  190. package/dist/core/workspace/workspace.js +167 -0
  191. package/dist/core/workspace/workspace.js.map +1 -0
  192. package/dist/errors.d.ts +50 -0
  193. package/dist/errors.d.ts.map +1 -0
  194. package/dist/errors.js +64 -0
  195. package/dist/errors.js.map +1 -0
  196. package/dist/index.d.ts +80 -0
  197. package/dist/index.d.ts.map +1 -0
  198. package/dist/index.js +66 -0
  199. package/dist/index.js.map +1 -0
  200. package/dist/logging/diagnostics.d.ts +64 -0
  201. package/dist/logging/diagnostics.d.ts.map +1 -0
  202. package/dist/logging/diagnostics.js +92 -0
  203. package/dist/logging/diagnostics.js.map +1 -0
  204. package/dist/model/canonical.d.ts +24 -0
  205. package/dist/model/canonical.d.ts.map +1 -0
  206. package/dist/model/canonical.js +69 -0
  207. package/dist/model/canonical.js.map +1 -0
  208. package/dist/model/schema.d.ts +37 -0
  209. package/dist/model/schema.d.ts.map +1 -0
  210. package/dist/model/schema.js +42 -0
  211. package/dist/model/schema.js.map +1 -0
  212. package/dist/model/types.d.ts +402 -0
  213. package/dist/model/types.d.ts.map +1 -0
  214. package/dist/model/types.js +10 -0
  215. package/dist/model/types.js.map +1 -0
  216. package/dist/pipeline/scan-pipeline.d.ts +69 -0
  217. package/dist/pipeline/scan-pipeline.d.ts.map +1 -0
  218. package/dist/pipeline/scan-pipeline.js +270 -0
  219. package/dist/pipeline/scan-pipeline.js.map +1 -0
  220. package/dist/util/concurrency.d.ts +17 -0
  221. package/dist/util/concurrency.d.ts.map +1 -0
  222. package/dist/util/concurrency.js +46 -0
  223. package/dist/util/concurrency.js.map +1 -0
  224. package/dist/util/glob.d.ts +26 -0
  225. package/dist/util/glob.d.ts.map +1 -0
  226. package/dist/util/glob.js +198 -0
  227. package/dist/util/glob.js.map +1 -0
  228. package/dist/util/paths.d.ts +23 -0
  229. package/dist/util/paths.d.ts.map +1 -0
  230. package/dist/util/paths.js +115 -0
  231. package/dist/util/paths.js.map +1 -0
  232. package/dist/util/text.d.ts +22 -0
  233. package/dist/util/text.d.ts.map +1 -0
  234. package/dist/util/text.js +66 -0
  235. package/dist/util/text.js.map +1 -0
  236. package/package.json +52 -0
@@ -0,0 +1,779 @@
1
+ /**
2
+ * Token-stream fact extraction for TypeScript/JavaScript.
3
+ *
4
+ * Design stance: a *reliable* import/export graph beats a pseudo-intelligent
5
+ * call graph. Only syntactically unambiguous constructs produce graph edges
6
+ * (`import`, `export ... from`, `require`, dynamic `import()`), while `calls`
7
+ * edges are limited to identifiers that are actually imported by the file, which
8
+ * keeps false positives low.
9
+ */
10
+ import { tokenize, tokensWithoutComments } from "./lexer.js";
11
+ import { SIGNALS, normalizeSignals } from "../types.js";
12
+ const ROUTE_METHODS = new Set(['get', 'post', 'put', 'patch', 'delete', 'options', 'head', 'all', 'use']);
13
+ const ROUTE_RECEIVER = /^(app|router|server|api|routes?|fastify|instance|handler|controller)$/i;
14
+ const HTTP_DECORATORS = new Set(['Get', 'Post', 'Put', 'Patch', 'Delete', 'Options', 'Head', 'All', 'Controller', 'Route']);
15
+ /** Keywords that may still be used as a declared name in the supported subset. */
16
+ const NOT_A_NAME = new Set([
17
+ 'import',
18
+ 'export',
19
+ 'from',
20
+ 'as',
21
+ 'class',
22
+ 'function',
23
+ 'const',
24
+ 'let',
25
+ 'var',
26
+ 'extends',
27
+ 'implements',
28
+ 'interface',
29
+ 'enum',
30
+ 'return',
31
+ 'if',
32
+ 'else',
33
+ 'for',
34
+ 'while',
35
+ 'do',
36
+ 'switch',
37
+ 'case',
38
+ 'break',
39
+ 'continue',
40
+ 'new',
41
+ 'delete',
42
+ 'typeof',
43
+ 'void',
44
+ 'in',
45
+ 'of',
46
+ 'instanceof',
47
+ 'default',
48
+ 'try',
49
+ 'catch',
50
+ 'finally',
51
+ 'throw',
52
+ 'await',
53
+ 'yield',
54
+ 'this',
55
+ 'super',
56
+ ]);
57
+ const DECLARATION_START = new Set([
58
+ 'class',
59
+ 'interface',
60
+ 'function',
61
+ 'type',
62
+ 'enum',
63
+ 'namespace',
64
+ 'module',
65
+ 'const',
66
+ 'let',
67
+ 'var',
68
+ 'async',
69
+ 'abstract',
70
+ 'declare',
71
+ ]);
72
+ const STATEMENT_START = new Set([
73
+ 'import',
74
+ 'export',
75
+ 'const',
76
+ 'let',
77
+ 'var',
78
+ 'function',
79
+ 'class',
80
+ 'interface',
81
+ 'type',
82
+ 'enum',
83
+ 'namespace',
84
+ 'async',
85
+ 'declare',
86
+ 'abstract',
87
+ ]);
88
+ function isPunct(token, value) {
89
+ return token !== undefined && token.type === 'punct' && token.value === value;
90
+ }
91
+ function isKeyword(token, value) {
92
+ return token !== undefined && token.type === 'keyword' && token.value === value;
93
+ }
94
+ function isName(token) {
95
+ if (token === undefined)
96
+ return false;
97
+ if (token.type === 'ident')
98
+ return true;
99
+ return token.type === 'keyword' && !NOT_A_NAME.has(token.value);
100
+ }
101
+ function isDeclarationStart(token) {
102
+ return token !== undefined && token.type === 'keyword' && DECLARATION_START.has(token.value);
103
+ }
104
+ /** String literal or a template without interpolation. */
105
+ function literalValue(token) {
106
+ if (token === undefined)
107
+ return null;
108
+ if (token.type === 'string')
109
+ return token.stringValue ?? token.value.replace(/^['"]|['"]$/g, '');
110
+ if (token.type === 'template' && !token.value.includes('${')) {
111
+ return token.value.replace(/^`|`$/g, '');
112
+ }
113
+ return null;
114
+ }
115
+ /** Returns the index *after* the matching closing token, or `tokens.length`. */
116
+ function skipBalanced(tokens, index, open, close) {
117
+ let depth = 0;
118
+ for (let cursor = index; cursor < tokens.length; cursor += 1) {
119
+ const value = tokens[cursor].value;
120
+ if (tokens[cursor].type !== 'punct')
121
+ continue;
122
+ if (value === open)
123
+ depth += 1;
124
+ else if (value === close) {
125
+ depth -= 1;
126
+ if (depth === 0)
127
+ return cursor + 1;
128
+ }
129
+ }
130
+ return tokens.length;
131
+ }
132
+ function findNext(tokens, from, value, limit = 200) {
133
+ const end = Math.min(tokens.length, from + limit);
134
+ for (let cursor = from; cursor < end; cursor += 1) {
135
+ if (isPunct(tokens[cursor], value))
136
+ return cursor;
137
+ }
138
+ return -1;
139
+ }
140
+ /**
141
+ * Skips a declarator/initializer, stopping at the next `,` or `;` at depth 0 or
142
+ * at a keyword that starts a new statement on a later line.
143
+ */
144
+ function skipDeclarator(tokens, from) {
145
+ const startLine = tokens[from]?.line ?? 0;
146
+ let depth = 0;
147
+ for (let cursor = from; cursor < tokens.length; cursor += 1) {
148
+ const token = tokens[cursor];
149
+ if (token.type === 'punct') {
150
+ if (token.value === '(' || token.value === '{' || token.value === '[' || token.value === '<')
151
+ depth += 1;
152
+ else if (token.value === ')' || token.value === '}' || token.value === ']' || token.value === '>') {
153
+ if (depth === 0)
154
+ return cursor;
155
+ depth -= 1;
156
+ }
157
+ else if (depth === 0 && (token.value === ';' || token.value === ','))
158
+ return cursor;
159
+ continue;
160
+ }
161
+ if (depth === 0 && token.line > startLine && token.type === 'keyword' && STATEMENT_START.has(token.value)) {
162
+ return cursor;
163
+ }
164
+ }
165
+ return tokens.length;
166
+ }
167
+ function skipStatement(tokens, from) {
168
+ const end = skipDeclarator(tokens, from);
169
+ return isPunct(tokens[end], ';') ? end + 1 : end;
170
+ }
171
+ /** Collects `extends` / `implements` identifiers up to the declaration body. */
172
+ function collectHeritage(tokens, from) {
173
+ const extended = [];
174
+ const implemented = [];
175
+ let mode = 'none';
176
+ for (let cursor = from; cursor < tokens.length; cursor += 1) {
177
+ const token = tokens[cursor];
178
+ if (token.type === 'punct') {
179
+ if (token.value === '{' || token.value === ';' || token.value === '=') {
180
+ return { extendsList: extended, implementsList: implemented, bodyIndex: token.value === '{' ? cursor : -1 };
181
+ }
182
+ if (token.value === '(' || token.value === '[') {
183
+ cursor = skipBalanced(tokens, cursor, token.value, token.value === '(' ? ')' : ']') - 1;
184
+ continue;
185
+ }
186
+ if (token.value === '<') {
187
+ cursor = skipBalanced(tokens, cursor, '<', '>') - 1;
188
+ continue;
189
+ }
190
+ continue;
191
+ }
192
+ if (isKeyword(token, 'extends')) {
193
+ mode = 'extends';
194
+ continue;
195
+ }
196
+ if (isKeyword(token, 'implements')) {
197
+ mode = 'implements';
198
+ continue;
199
+ }
200
+ if (isName(token)) {
201
+ if (mode === 'extends')
202
+ extended.push(token.value);
203
+ else if (mode === 'implements')
204
+ implemented.push(token.value);
205
+ if (isPunct(tokens[cursor + 1], '<')) {
206
+ cursor = skipBalanced(tokens, cursor + 1, '<', '>') - 1;
207
+ }
208
+ continue;
209
+ }
210
+ }
211
+ return { extendsList: extended, implementsList: implemented, bodyIndex: -1 };
212
+ }
213
+ /** Reads `{ a, b as c, type D }` between two brace indices. */
214
+ function collectBracedNames(tokens, openIndex, closeIndex) {
215
+ const entries = [];
216
+ let cursor = openIndex + 1;
217
+ while (cursor < closeIndex) {
218
+ let name = '';
219
+ let alias;
220
+ if (isKeyword(tokens[cursor], 'type'))
221
+ cursor += 1; // inline `type` specifier
222
+ const nameToken = tokens[cursor];
223
+ if (isName(nameToken)) {
224
+ name = nameToken.value;
225
+ cursor += 1;
226
+ if (isKeyword(tokens[cursor], 'as') && isName(tokens[cursor + 1])) {
227
+ alias = tokens[cursor + 1].value;
228
+ cursor += 2;
229
+ }
230
+ }
231
+ else {
232
+ cursor += 1;
233
+ }
234
+ if (name.length > 0)
235
+ entries.push(alias === undefined ? { name } : { name, alias });
236
+ while (cursor < closeIndex && !isPunct(tokens[cursor], ','))
237
+ cursor += 1;
238
+ cursor += 1;
239
+ }
240
+ return entries;
241
+ }
242
+ function mapDeclarationKeyword(keyword) {
243
+ switch (keyword) {
244
+ case 'class':
245
+ return 'class';
246
+ case 'interface':
247
+ return 'interface';
248
+ case 'enum':
249
+ return 'enum';
250
+ case 'namespace':
251
+ case 'module':
252
+ return 'namespace';
253
+ case 'type':
254
+ return 'type';
255
+ case 'function':
256
+ return 'function';
257
+ default:
258
+ return 'variable';
259
+ }
260
+ }
261
+ /** Parses an `import` statement starting at `index`; returns the next index. */
262
+ function parseImport(tokens, index, result) {
263
+ let cursor = index + 1;
264
+ let typeOnly = false;
265
+ const line = tokens[index].line;
266
+ if (isKeyword(tokens[cursor], 'type') &&
267
+ (isPunct(tokens[cursor + 1], '{') ||
268
+ isPunct(tokens[cursor + 1], '*') ||
269
+ (isName(tokens[cursor + 1]) && (isKeyword(tokens[cursor + 2], 'from') || isPunct(tokens[cursor + 2], ','))))) {
270
+ typeOnly = true;
271
+ cursor += 1;
272
+ }
273
+ // dynamic import('...')
274
+ if (isPunct(tokens[cursor], '(')) {
275
+ const specifier = literalValue(tokens[cursor + 1]);
276
+ if (specifier !== null) {
277
+ result.imports.push({ specifier, syntax: 'dynamic', names: [], line, hasSideEffects: false, typeOnly: false });
278
+ result.usesEsmSyntax = true;
279
+ }
280
+ return skipBalanced(tokens, cursor, '(', ')');
281
+ }
282
+ // side-effect only: import './styles.css'
283
+ if (tokens[cursor]?.type === 'string') {
284
+ const specifier = literalValue(tokens[cursor]);
285
+ if (specifier !== null) {
286
+ result.imports.push({ specifier, syntax: 'side-effect', names: [], line, hasSideEffects: true, typeOnly });
287
+ result.usesEsmSyntax = true;
288
+ }
289
+ return skipStatement(tokens, cursor + 1);
290
+ }
291
+ const names = [];
292
+ let defaultImport;
293
+ let namespaceImport;
294
+ while (cursor < tokens.length) {
295
+ const token = tokens[cursor];
296
+ if (isKeyword(token, 'from')) {
297
+ cursor += 1;
298
+ break;
299
+ }
300
+ if (token.type === 'string')
301
+ break;
302
+ if (isPunct(token, '{')) {
303
+ const close = skipBalanced(tokens, cursor, '{', '}');
304
+ for (const entry of collectBracedNames(tokens, cursor, close - 1))
305
+ names.push(entry.name);
306
+ cursor = close;
307
+ continue;
308
+ }
309
+ if (isPunct(token, '*')) {
310
+ if (isKeyword(tokens[cursor + 1], 'as') && isName(tokens[cursor + 2])) {
311
+ namespaceImport = tokens[cursor + 2].value;
312
+ cursor += 3;
313
+ continue;
314
+ }
315
+ cursor += 1;
316
+ continue;
317
+ }
318
+ if (isPunct(token, '=')) {
319
+ // `import x = require('y')` or `import x = ns.Y`
320
+ if (isKeyword(tokens[cursor + 1], 'require') && isPunct(tokens[cursor + 2], '(')) {
321
+ const specifier = literalValue(tokens[cursor + 3]);
322
+ if (specifier !== null) {
323
+ result.imports.push({ specifier, syntax: 'import-equals', names: [], line, hasSideEffects: false, typeOnly });
324
+ result.usesCommonJs = true;
325
+ }
326
+ return skipBalanced(tokens, cursor + 2, '(', ')');
327
+ }
328
+ return skipStatement(tokens, cursor + 1);
329
+ }
330
+ if (isName(token) && defaultImport === undefined) {
331
+ defaultImport = token.value;
332
+ cursor += 1;
333
+ continue;
334
+ }
335
+ cursor += 1;
336
+ }
337
+ const specifier = literalValue(tokens[cursor]);
338
+ if (specifier !== null) {
339
+ const record = {
340
+ specifier,
341
+ syntax: typeOnly ? 'type-only' : 'static',
342
+ names,
343
+ line,
344
+ hasSideEffects: false,
345
+ typeOnly,
346
+ };
347
+ if (defaultImport !== undefined)
348
+ record.defaultImport = defaultImport;
349
+ if (namespaceImport !== undefined)
350
+ record.namespaceImport = namespaceImport;
351
+ result.imports.push(record);
352
+ if (!typeOnly)
353
+ result.usesEsmSyntax = true;
354
+ return Math.min(cursor + 1, tokens.length);
355
+ }
356
+ return skipStatement(tokens, cursor);
357
+ }
358
+ /** Parses an `export` statement starting at `index`; returns the next index. */
359
+ function parseExport(tokens, index, result) {
360
+ const line = tokens[index].line;
361
+ let cursor = index + 1;
362
+ if (isKeyword(tokens[cursor], 'default')) {
363
+ if (isDeclarationStart(tokens[cursor + 1])) {
364
+ const next = parseDeclaration(tokens, index, result, true, true);
365
+ recordDefaultExportIfMissing(result, line);
366
+ return next;
367
+ }
368
+ result.exports.push({ name: 'default', kind: 'unknown', isDefault: true, line });
369
+ return skipStatement(tokens, cursor + 1);
370
+ }
371
+ let typeOnly = false;
372
+ if (isKeyword(tokens[cursor], 'type') && isPunct(tokens[cursor + 1], '{')) {
373
+ typeOnly = true;
374
+ cursor += 1;
375
+ }
376
+ // export { a, b as c } [from '...']
377
+ if (isPunct(tokens[cursor], '{')) {
378
+ const close = skipBalanced(tokens, cursor, '{', '}');
379
+ const entries = collectBracedNames(tokens, cursor, close - 1);
380
+ if (isKeyword(tokens[close], 'from')) {
381
+ const specifier = literalValue(tokens[close + 1]);
382
+ if (specifier !== null) {
383
+ result.imports.push({
384
+ specifier,
385
+ syntax: 'reexport',
386
+ names: entries.map((entry) => entry.name),
387
+ line,
388
+ hasSideEffects: false,
389
+ typeOnly,
390
+ });
391
+ result.usesEsmSyntax = true;
392
+ for (const entry of entries) {
393
+ result.exports.push({
394
+ name: entry.alias ?? entry.name,
395
+ kind: 'reexport',
396
+ isDefault: false,
397
+ line,
398
+ fromSpecifier: specifier,
399
+ names: [entry.name],
400
+ typeOnly,
401
+ });
402
+ }
403
+ return skipStatement(tokens, close + 2);
404
+ }
405
+ }
406
+ for (const entry of entries) {
407
+ result.exports.push({
408
+ name: entry.alias ?? entry.name,
409
+ kind: 'unknown',
410
+ isDefault: false,
411
+ line,
412
+ typeOnly,
413
+ });
414
+ }
415
+ return skipStatement(tokens, close);
416
+ }
417
+ // export * from '...' / export * as ns from '...'
418
+ if (isPunct(tokens[cursor], '*')) {
419
+ let namespace;
420
+ let scan = cursor + 1;
421
+ if (isKeyword(tokens[scan], 'as') && isName(tokens[scan + 1])) {
422
+ namespace = tokens[scan + 1].value;
423
+ scan += 2;
424
+ }
425
+ if (isKeyword(tokens[scan], 'from')) {
426
+ const specifier = literalValue(tokens[scan + 1]);
427
+ if (specifier !== null) {
428
+ const importRecord = {
429
+ specifier,
430
+ syntax: 'reexport',
431
+ names: [],
432
+ line,
433
+ hasSideEffects: false,
434
+ typeOnly,
435
+ };
436
+ if (namespace !== undefined)
437
+ importRecord.namespaceImport = namespace;
438
+ result.imports.push(importRecord);
439
+ result.usesEsmSyntax = true;
440
+ result.exports.push({
441
+ name: namespace ?? '*',
442
+ kind: namespace !== undefined ? 'reexport' : 'star',
443
+ isDefault: false,
444
+ line,
445
+ fromSpecifier: specifier,
446
+ typeOnly,
447
+ });
448
+ return skipStatement(tokens, scan + 2);
449
+ }
450
+ }
451
+ return skipStatement(tokens, scan);
452
+ }
453
+ // TypeScript `export = ...`
454
+ if (isPunct(tokens[cursor], '=')) {
455
+ result.exports.push({ name: 'export=', kind: 'unknown', isDefault: false, line });
456
+ result.usesCommonJs = true;
457
+ return skipStatement(tokens, cursor + 1);
458
+ }
459
+ if (isDeclarationStart(tokens[cursor])) {
460
+ return parseDeclaration(tokens, index, result, true, false);
461
+ }
462
+ return skipStatement(tokens, cursor);
463
+ }
464
+ function recordDefaultExportIfMissing(result, line) {
465
+ const hasDefault = result.exports.some((entry) => entry.isDefault);
466
+ if (!hasDefault)
467
+ result.exports.push({ name: 'default', kind: 'unknown', isDefault: true, line });
468
+ }
469
+ function pushSymbol(result, symbol) {
470
+ const duplicate = result.symbols.some((entry) => entry.name === symbol.name && entry.line === symbol.line);
471
+ if (!duplicate)
472
+ result.symbols.push(symbol);
473
+ }
474
+ /** Classifies a variable initializer (`() => {}`, `function () {}`, plain value). */
475
+ function inferInitializerKind(tokens, from, declarationKeyword) {
476
+ const fallback = declarationKeyword === 'const' ? 'const' : 'variable';
477
+ if (from >= tokens.length)
478
+ return fallback;
479
+ let cursor = from;
480
+ if (isPunct(tokens[cursor], ':')) {
481
+ // annotated declaration: skip the type annotation
482
+ cursor += 1;
483
+ while (cursor < tokens.length && !isPunct(tokens[cursor], '=')) {
484
+ if (isPunct(tokens[cursor], '<'))
485
+ cursor = skipBalanced(tokens, cursor, '<', '>');
486
+ if (isPunct(tokens[cursor], '('))
487
+ cursor = skipBalanced(tokens, cursor, '(', ')');
488
+ if (isPunct(tokens[cursor], '{'))
489
+ cursor = skipBalanced(tokens, cursor, '{', '}');
490
+ cursor += 1;
491
+ }
492
+ }
493
+ if (!isPunct(tokens[cursor], '='))
494
+ return fallback;
495
+ cursor += 1;
496
+ if (isKeyword(tokens[cursor], 'async'))
497
+ cursor += 1;
498
+ if (isKeyword(tokens[cursor], 'function'))
499
+ return 'function';
500
+ if (isKeyword(tokens[cursor], 'class'))
501
+ return 'class';
502
+ if (isPunct(tokens[cursor], '(')) {
503
+ const afterParams = skipBalanced(tokens, cursor, '(', ')');
504
+ if (isPunct(tokens[afterParams], '=>'))
505
+ return 'function';
506
+ }
507
+ const end = skipDeclarator(tokens, cursor);
508
+ for (let scan = cursor; scan < end; scan += 1) {
509
+ if (isPunct(tokens[scan], '=>'))
510
+ return 'function';
511
+ if (isKeyword(tokens[scan], 'function'))
512
+ return 'function';
513
+ }
514
+ return fallback;
515
+ }
516
+ function parseVariableDeclaration(tokens, keywordIndex, result, exported, isDefault) {
517
+ const declarationKeyword = tokens[keywordIndex].value;
518
+ let cursor = keywordIndex + 1;
519
+ for (;;) {
520
+ const nameToken = tokens[cursor];
521
+ if (!isName(nameToken))
522
+ return skipStatement(tokens, cursor);
523
+ pushSymbol(result, {
524
+ name: nameToken.value,
525
+ kind: inferInitializerKind(tokens, cursor + 1, declarationKeyword),
526
+ exported,
527
+ isDefault,
528
+ extends: [],
529
+ implements: [],
530
+ line: nameToken.line,
531
+ });
532
+ const next = skipDeclarator(tokens, cursor + 1);
533
+ if (isPunct(tokens[next], ',')) {
534
+ cursor = next + 1;
535
+ continue;
536
+ }
537
+ return isPunct(tokens[next], ';') ? next + 1 : next;
538
+ }
539
+ }
540
+ /** Parses a class/interface/function/type/enum/var declaration; returns the next index. */
541
+ function parseDeclaration(tokens, index, result, exported, isDefault) {
542
+ let cursor = index;
543
+ let exportedFlag = exported;
544
+ let defaultFlag = isDefault;
545
+ while (cursor < tokens.length) {
546
+ const token = tokens[cursor];
547
+ if (isKeyword(token, 'export')) {
548
+ exportedFlag = true;
549
+ cursor += 1;
550
+ continue;
551
+ }
552
+ if (isKeyword(token, 'default')) {
553
+ defaultFlag = true;
554
+ cursor += 1;
555
+ continue;
556
+ }
557
+ if (isKeyword(token, 'declare') || isKeyword(token, 'abstract') || isKeyword(token, 'async')) {
558
+ cursor += 1;
559
+ continue;
560
+ }
561
+ break;
562
+ }
563
+ const keywordToken = tokens[cursor];
564
+ if (keywordToken === undefined || keywordToken.type !== 'keyword')
565
+ return cursor;
566
+ const keyword = keywordToken.value;
567
+ const line = keywordToken.line;
568
+ if (keyword === 'class' || keyword === 'interface' || keyword === 'enum' || keyword === 'namespace' || keyword === 'module') {
569
+ const nameToken = tokens[cursor + 1];
570
+ const name = isName(nameToken) ? nameToken.value : '';
571
+ const heritage = collectHeritage(tokens, cursor + 2);
572
+ if (name.length > 0) {
573
+ pushSymbol(result, {
574
+ name,
575
+ kind: mapDeclarationKeyword(keyword),
576
+ exported: exportedFlag,
577
+ isDefault: defaultFlag,
578
+ extends: heritage.extendsList,
579
+ implements: heritage.implementsList,
580
+ line,
581
+ });
582
+ if (heritage.extendsList.includes('Error'))
583
+ result.signals.push(SIGNALS.customErrorClass);
584
+ }
585
+ if (heritage.bodyIndex >= 0)
586
+ return skipBalanced(tokens, heritage.bodyIndex, '{', '}');
587
+ return skipStatement(tokens, cursor + 2);
588
+ }
589
+ if (keyword === 'function') {
590
+ let scan = cursor + 1;
591
+ if (isPunct(tokens[scan], '*'))
592
+ scan += 1;
593
+ const nameToken = tokens[scan];
594
+ if (isName(nameToken)) {
595
+ pushSymbol(result, {
596
+ name: nameToken.value,
597
+ kind: 'function',
598
+ exported: exportedFlag,
599
+ isDefault: defaultFlag,
600
+ extends: [],
601
+ implements: [],
602
+ line: nameToken.line,
603
+ });
604
+ scan += 1;
605
+ }
606
+ const params = findNext(tokens, scan, '(');
607
+ if (params === -1)
608
+ return skipStatement(tokens, scan);
609
+ const afterParams = skipBalanced(tokens, params, '(', ')');
610
+ if (isPunct(tokens[afterParams], '{'))
611
+ return skipBalanced(tokens, afterParams, '{', '}');
612
+ return skipStatement(tokens, afterParams);
613
+ }
614
+ if (keyword === 'type') {
615
+ const nameToken = tokens[cursor + 1];
616
+ if (isName(nameToken)) {
617
+ pushSymbol(result, {
618
+ name: nameToken.value,
619
+ kind: 'type',
620
+ exported: exportedFlag,
621
+ isDefault: defaultFlag,
622
+ extends: [],
623
+ implements: [],
624
+ line: nameToken.line,
625
+ });
626
+ }
627
+ return skipStatement(tokens, cursor + 2);
628
+ }
629
+ if (keyword === 'const' || keyword === 'let' || keyword === 'var') {
630
+ return parseVariableDeclaration(tokens, cursor, result, exportedFlag, defaultFlag);
631
+ }
632
+ return cursor;
633
+ }
634
+ /** Detects coarse language signals around a single token position. */
635
+ function collectSignals(tokens, cursor, result) {
636
+ const token = tokens[cursor];
637
+ if (token.type !== 'ident' && token.type !== 'keyword')
638
+ return;
639
+ const next = tokens[cursor + 1];
640
+ const next2 = tokens[cursor + 2];
641
+ const value = token.value;
642
+ if (isPunct(next, '(')) {
643
+ if (value === 'require') {
644
+ const specifier = literalValue(next2);
645
+ if (specifier !== null) {
646
+ result.imports.push({ specifier, syntax: 'require', names: [], line: token.line, hasSideEffects: false, typeOnly: false });
647
+ result.usesCommonJs = true;
648
+ return;
649
+ }
650
+ }
651
+ if (value === 'describe' || value === 'it' || value === 'test' || value === 'beforeEach' || value === 'afterEach') {
652
+ result.signals.push(SIGNALS.testSuite);
653
+ }
654
+ if (value === 'EventEmitter')
655
+ result.signals.push(SIGNALS.eventEmitter);
656
+ if (value === 'Queue' || value === 'Job' || value === 'Worker') {
657
+ result.signals.push(value === 'Worker' ? SIGNALS.worker : SIGNALS.queue);
658
+ }
659
+ if (value === 'setInterval' || value === 'setTimeout' || value === 'cron' || value === 'schedule') {
660
+ result.signals.push(SIGNALS.schedule);
661
+ }
662
+ }
663
+ if (token.type === 'ident' && /^[A-Z]/.test(value) && isPunct(next, '(')) {
664
+ result.calls.push({ name: value, line: token.line });
665
+ }
666
+ if (isKeyword(token, 'new') && isName(next)) {
667
+ result.calls.push({ name: next.value, line: token.line });
668
+ }
669
+ if (value === 'process' && isPunct(next, '.') && next2?.value === 'env') {
670
+ result.signals.push(SIGNALS.envAccess);
671
+ }
672
+ if (value === 'console' && isPunct(next, '.'))
673
+ result.signals.push(SIGNALS.consoleUsage);
674
+ if ((value === 'window' || value === 'document') && isPunct(next, '.'))
675
+ result.signals.push(SIGNALS.browserApi);
676
+ if ((value === 'emit' || value === 'publish' || value === 'dispatch' || value === 'subscribe') && isPunct(next, '(')) {
677
+ result.signals.push(SIGNALS.eventEmitter);
678
+ }
679
+ if (value === 'listen' && isPunct(next, '('))
680
+ result.signals.push(SIGNALS.httpFramework);
681
+ if (ROUTE_RECEIVER.test(value) && isPunct(next, '.') && next2?.type === 'ident' && ROUTE_METHODS.has(next2.value)) {
682
+ const routePath = literalValue(tokens[cursor + 4]);
683
+ if (isPunct(tokens[cursor + 3], '(') && routePath !== null && routePath.startsWith('/')) {
684
+ result.routes.push({ method: next2.value.toUpperCase(), path: routePath, line: token.line });
685
+ result.signals.push(SIGNALS.httpRoute);
686
+ }
687
+ }
688
+ }
689
+ /**
690
+ * Extracts imports, exports, symbols, calls, routes and signals from TS/JS source.
691
+ *
692
+ * The function never throws: malformed input yields `parseStatus: 'partial'`
693
+ * together with the facts that could still be recovered.
694
+ */
695
+ export function extractModuleFacts(source) {
696
+ const tokens = tokensWithoutComments(tokenize(source));
697
+ const result = {
698
+ imports: [],
699
+ exports: [],
700
+ symbols: [],
701
+ calls: [],
702
+ routes: [],
703
+ signals: [],
704
+ parseStatus: 'ok',
705
+ usesEsmSyntax: false,
706
+ usesCommonJs: false,
707
+ };
708
+ let depth = 0;
709
+ let cursor = 0;
710
+ while (cursor < tokens.length) {
711
+ const token = tokens[cursor];
712
+ if (token.type === 'punct') {
713
+ if (token.value === '{' || token.value === '(' || token.value === '[')
714
+ depth += 1;
715
+ else if (token.value === '}' || token.value === ')' || token.value === ']')
716
+ depth = Math.max(0, depth - 1);
717
+ else if (token.value === '@') {
718
+ const decorator = tokens[cursor + 1];
719
+ result.signals.push(SIGNALS.decorators);
720
+ if (isName(decorator)) {
721
+ result.signals.push(`decorator:${decorator.value}`);
722
+ if (HTTP_DECORATORS.has(decorator.value))
723
+ result.signals.push(SIGNALS.httpRoute);
724
+ if (decorator.value === 'Controller')
725
+ result.signals.push(SIGNALS.httpFramework);
726
+ }
727
+ }
728
+ cursor += 1;
729
+ continue;
730
+ }
731
+ if (isKeyword(token, 'import')) {
732
+ const next = parseImport(tokens, cursor, result);
733
+ cursor = next > cursor ? next : cursor + 1;
734
+ continue;
735
+ }
736
+ if (isKeyword(token, 'export') && depth === 0) {
737
+ const next = parseExport(tokens, cursor, result);
738
+ cursor = next > cursor ? next : cursor + 1;
739
+ continue;
740
+ }
741
+ if (depth === 0 && isDeclarationStart(token)) {
742
+ const next = parseDeclaration(tokens, cursor, result, false, false);
743
+ cursor = next > cursor ? next : cursor + 1;
744
+ continue;
745
+ }
746
+ collectSignals(tokens, cursor, result);
747
+ cursor += 1;
748
+ }
749
+ let braceBalance = 0;
750
+ let unterminatedLiteral = false;
751
+ for (const token of tokens) {
752
+ if (token.type === 'punct') {
753
+ if (token.value === '{')
754
+ braceBalance += 1;
755
+ else if (token.value === '}')
756
+ braceBalance -= 1;
757
+ continue;
758
+ }
759
+ if (token.type === 'string') {
760
+ const first = token.value[0];
761
+ const last = token.value[token.value.length - 1];
762
+ if (token.value.length > 1 && first !== last)
763
+ unterminatedLiteral = true;
764
+ }
765
+ if (token.type === 'template') {
766
+ if (!token.value.endsWith('`'))
767
+ unterminatedLiteral = true;
768
+ }
769
+ }
770
+ if (braceBalance !== 0 || unterminatedLiteral)
771
+ result.parseStatus = 'partial';
772
+ if (result.usesEsmSyntax)
773
+ result.signals.push(SIGNALS.esm);
774
+ if (result.usesCommonJs && !result.usesEsmSyntax)
775
+ result.signals.push(SIGNALS.commonjs);
776
+ result.signals = normalizeSignals(result.signals);
777
+ return result;
778
+ }
779
+ //# sourceMappingURL=extractor.js.map