@eventra_dev/eventra-cli 0.2.3 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/README.md +154 -32
  2. package/dist/analysis/cache/evaluationCache.js +21 -0
  3. package/dist/analysis/cache/resolvedCallCache.js +21 -0
  4. package/dist/analysis/cache/resolvedExportCache.js +21 -0
  5. package/dist/analysis/cache/semanticDependencyGraph.js +25 -0
  6. package/dist/analysis/cache/symbolInvalidation.js +20 -0
  7. package/dist/analysis/extractor/extractor.js +47 -0
  8. package/dist/analysis/extractor/propagationExtractor.js +52 -0
  9. package/dist/analysis/pipeline/semanticPass.js +2 -0
  10. package/dist/analysis/resolver/callResolver.js +125 -0
  11. package/dist/analysis/resolver/exportResolver.js +75 -0
  12. package/dist/analysis/resolver/resolver.js +133 -0
  13. package/dist/analysis/scanner/analyzer/propagationAnalyzer.js +193 -0
  14. package/dist/analysis/scanner/analyzer/returnPropagationAnalyzer.js +105 -0
  15. package/dist/analysis/scanner/scanner.js +117 -0
  16. package/dist/analysis/scanner/sinkDetector.js +91 -0
  17. package/dist/analysis/shared/evaluationContext.js +9 -0
  18. package/dist/analysis/shared/propagation.js +3 -0
  19. package/dist/analysis/shared/returnPropagation.js +2 -0
  20. package/dist/analysis/shared/types.js +2 -0
  21. package/dist/analysis/shared/utils.js +62 -0
  22. package/dist/analysis/symbols/symbolUtils.js +68 -0
  23. package/dist/analysis/symbols/wrapperRegistry.js +55 -0
  24. package/dist/cli/check.js +73 -0
  25. package/dist/{commands → cli}/init.js +1 -1
  26. package/dist/{commands → cli}/send.js +1 -1
  27. package/dist/cli/sync.js +56 -0
  28. package/dist/{commands → cli}/watch.js +41 -25
  29. package/dist/compiler/compilerContext.js +204 -0
  30. package/dist/compiler/documentRegistry.js +78 -0
  31. package/dist/compiler/importGraph.js +83 -0
  32. package/dist/compiler/scheduler.js +29 -0
  33. package/dist/core/EventraEngine.js +234 -0
  34. package/dist/core/projectScanner.js +73 -0
  35. package/dist/{utils → filesystem}/getVirtualFile.js +1 -3
  36. package/dist/filesystem/processFile.js +38 -0
  37. package/dist/index.js +5 -5
  38. package/dist/shared/buildSyntheticTsx.js +20 -0
  39. package/dist/shared/templateExpressionExtractor.js +22 -0
  40. package/package.json +32 -9
  41. package/.github/workflows/release.yml +0 -43
  42. package/ARCHITECTURE.md +0 -88
  43. package/assets/eventra-icon-animated.svg +0 -19
  44. package/dist/commands/check.js +0 -78
  45. package/dist/commands/sync.js +0 -79
  46. package/dist/engine/boundary.js +0 -14
  47. package/dist/engine/callResolver.js +0 -58
  48. package/dist/engine/engine.js +0 -154
  49. package/dist/engine/exportResolver.js +0 -46
  50. package/dist/engine/functionAnalyzer.js +0 -71
  51. package/dist/engine/graph.js +0 -168
  52. package/dist/engine/languageService.js +0 -162
  53. package/dist/engine/resolver.js +0 -149
  54. package/dist/engine/scanner.js +0 -230
  55. package/dist/utils/processFile.js +0 -28
  56. package/dist/utils/universal.js +0 -190
  57. package/src/commands/check.ts +0 -91
  58. package/src/commands/init.ts +0 -174
  59. package/src/commands/send.ts +0 -213
  60. package/src/commands/sync.ts +0 -106
  61. package/src/commands/watch.ts +0 -144
  62. package/src/engine/boundary.ts +0 -12
  63. package/src/engine/callResolver.ts +0 -65
  64. package/src/engine/engine.ts +0 -227
  65. package/src/engine/exportResolver.ts +0 -56
  66. package/src/engine/functionAnalyzer.ts +0 -81
  67. package/src/engine/graph.ts +0 -193
  68. package/src/engine/hash.ts +0 -5
  69. package/src/engine/languageService.ts +0 -209
  70. package/src/engine/resolver.ts +0 -177
  71. package/src/engine/scanner.ts +0 -298
  72. package/src/index.ts +0 -49
  73. package/src/types.ts +0 -35
  74. package/src/utils/config.ts +0 -53
  75. package/src/utils/getVirtualFile.ts +0 -5
  76. package/src/utils/processFile.ts +0 -29
  77. package/src/utils/universal.ts +0 -249
  78. package/tests/fixtures/backend/express/app.ts +0 -233
  79. package/tests/fixtures/backend/nest/service.ts +0 -70
  80. package/tests/fixtures/backend/node/index.ts +0 -63
  81. package/tests/fixtures/frontend/next/page.tsx +0 -101
  82. package/tests/fixtures/frontend/react/App.tsx +0 -104
  83. package/tests/fixtures/frontend/vue/App.vue +0 -83
  84. package/tests/fixtures/wrappers/component/test.tsx +0 -62
  85. package/tests/fixtures/wrappers/function/test.ts +0 -60
  86. package/tests/run.ts +0 -106
  87. package/tsconfig.json +0 -14
  88. /package/dist/{utils → config}/config.js +0 -0
  89. /package/dist/{engine → shared}/hash.js +0 -0
@@ -0,0 +1,204 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CompilerContext = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const typescript_1 = __importDefault(require("typescript"));
10
+ const documentRegistry_1 = require("./documentRegistry");
11
+ const processFile_1 = require("../filesystem/processFile");
12
+ class CompilerContext {
13
+ rootDir;
14
+ registry = new documentRegistry_1.DocumentRegistry();
15
+ compilerOptions;
16
+ host;
17
+ builder;
18
+ rootNames = new Set();
19
+ constructor(rootDir) {
20
+ this.rootDir = rootDir;
21
+ const configPath = typescript_1.default.findConfigFile(rootDir, typescript_1.default.sys.fileExists, "tsconfig.json");
22
+ let compilerOptions = {
23
+ target: typescript_1.default.ScriptTarget.ESNext,
24
+ module: typescript_1.default.ModuleKind.ESNext,
25
+ moduleResolution: typescript_1.default.ModuleResolutionKind.Bundler,
26
+ jsx: typescript_1.default.JsxEmit.Preserve,
27
+ allowJs: true,
28
+ checkJs: false,
29
+ strict: false,
30
+ skipLibCheck: true,
31
+ noEmit: true,
32
+ allowSyntheticDefaultImports: true,
33
+ esModuleInterop: true,
34
+ resolveJsonModule: true,
35
+ allowNonTsExtensions: true,
36
+ incremental: true,
37
+ };
38
+ if (configPath) {
39
+ const config = typescript_1.default.readConfigFile(configPath, typescript_1.default.sys.readFile);
40
+ if (config.error) {
41
+ throw new Error(typescript_1.default.flattenDiagnosticMessageText(config.error.messageText, "\n"));
42
+ }
43
+ const parsed = typescript_1.default.parseJsonConfigFileContent(config.config, typescript_1.default.sys, path_1.default.dirname(configPath));
44
+ compilerOptions = {
45
+ ...compilerOptions,
46
+ ...parsed.options,
47
+ };
48
+ for (const file of parsed.fileNames) {
49
+ this.rootNames.add(this.normalize(file));
50
+ }
51
+ }
52
+ this.compilerOptions =
53
+ compilerOptions;
54
+ this.host =
55
+ typescript_1.default.createCompilerHost(this.compilerOptions, true);
56
+ this.patchCompilerHost();
57
+ this.builder =
58
+ typescript_1.default.createEmitAndSemanticDiagnosticsBuilderProgram([...this.rootNames], this.compilerOptions, this.host);
59
+ }
60
+ normalize(fileName) {
61
+ return this.registry
62
+ .normalize(fileName);
63
+ }
64
+ patchCompilerHost() {
65
+ const originalGetSourceFile = this.host.getSourceFile;
66
+ this.host.getSourceFile = (fileName, languageVersion, onError, shouldCreateNewSourceFile) => {
67
+ const normalized = this.normalize(fileName);
68
+ this.registry.ensure(normalized);
69
+ const snapshot = this.registry.getSnapshot(normalized);
70
+ if (!snapshot) {
71
+ return originalGetSourceFile(normalized, languageVersion, onError, shouldCreateNewSourceFile);
72
+ }
73
+ return typescript_1.default.createSourceFile(normalized, snapshot.getText(0, snapshot.getLength()), languageVersion, true, this.getScriptKind(normalized));
74
+ };
75
+ this.host.readFile = (fileName) => {
76
+ const normalized = this.normalize(fileName);
77
+ this.registry.ensure(normalized);
78
+ return (this.registry.getContent(normalized)
79
+ ?? fs_1.default.readFileSync(normalized, "utf8"));
80
+ };
81
+ this.host.fileExists = (fileName) => {
82
+ const normalized = this.normalize(fileName);
83
+ return (this.registry.has(normalized)
84
+ || fs_1.default.existsSync(normalized));
85
+ };
86
+ this.host.getCanonicalFileName =
87
+ (f) => this.normalize(f);
88
+ this.host.useCaseSensitiveFileNames =
89
+ () => true;
90
+ this.host.getCurrentDirectory =
91
+ () => this.rootDir;
92
+ this.host.getNewLine =
93
+ () => "\n";
94
+ }
95
+ getScriptKind(fileName) {
96
+ if (fileName.endsWith(".tsx")) {
97
+ return typescript_1.default.ScriptKind.TSX;
98
+ }
99
+ if (fileName.endsWith(".ts")) {
100
+ return typescript_1.default.ScriptKind.TS;
101
+ }
102
+ if (fileName.endsWith(".jsx")) {
103
+ return typescript_1.default.ScriptKind.JSX;
104
+ }
105
+ if (fileName.endsWith(".js")) {
106
+ return typescript_1.default.ScriptKind.JS;
107
+ }
108
+ if (fileName.endsWith(".json")) {
109
+ return typescript_1.default.ScriptKind.JSON;
110
+ }
111
+ return typescript_1.default.ScriptKind.Unknown;
112
+ }
113
+ rebuild() {
114
+ this.builder =
115
+ typescript_1.default.createEmitAndSemanticDiagnosticsBuilderProgram([...this.rootNames], this.compilerOptions, this.host, this.builder);
116
+ }
117
+ async updateFile(fileName, content) {
118
+ const normalized = this.normalize(fileName);
119
+ const processed = await (0, processFile_1.processFile)(normalized, content);
120
+ this.registry.update(processed.fileName, processed.content);
121
+ this.rootNames.add(processed.fileName);
122
+ this.rebuild();
123
+ }
124
+ removeFile(fileName) {
125
+ const normalized = this.normalize(fileName);
126
+ this.registry.remove(normalized);
127
+ this.rootNames.delete(normalized);
128
+ this.rebuild();
129
+ }
130
+ invalidate() {
131
+ this.registry.invalidate();
132
+ this.rebuild();
133
+ }
134
+ getProgram() {
135
+ return this.builder
136
+ .getProgram();
137
+ }
138
+ getChecker() {
139
+ return this.builder
140
+ .getProgram()
141
+ .getTypeChecker();
142
+ }
143
+ getSourceFile(fileName) {
144
+ return this.builder
145
+ .getProgram()
146
+ .getSourceFile(this.normalize(fileName));
147
+ }
148
+ getSemanticDiagnostics(fileName) {
149
+ const program = this.builder.getProgram();
150
+ if (!fileName) {
151
+ return typescript_1.default.getPreEmitDiagnostics(program);
152
+ }
153
+ const sourceFile = this.getSourceFile(fileName);
154
+ if (!sourceFile) {
155
+ return [];
156
+ }
157
+ return [
158
+ ...program.getSemanticDiagnostics(sourceFile),
159
+ ...program.getSyntacticDiagnostics(sourceFile),
160
+ ];
161
+ }
162
+ resolveModule(moduleName, fromFile) {
163
+ const resolved = typescript_1.default.resolveModuleName(moduleName, this.normalize(fromFile), this.compilerOptions, this.host);
164
+ return resolved
165
+ .resolvedModule
166
+ ?.resolvedFileName;
167
+ }
168
+ getResolvedModules(fileName) {
169
+ const source = this.getSourceFile(fileName);
170
+ if (!source) {
171
+ return [];
172
+ }
173
+ const modules = new Set();
174
+ for (const statement of source.statements) {
175
+ if (!typescript_1.default.isImportDeclaration(statement)
176
+ && !typescript_1.default.isExportDeclaration(statement)) {
177
+ continue;
178
+ }
179
+ const specifier = statement.moduleSpecifier;
180
+ if (!specifier
181
+ || !typescript_1.default.isStringLiteral(specifier)) {
182
+ continue;
183
+ }
184
+ const resolved = this.resolveModule(specifier.text, source.fileName);
185
+ if (!resolved) {
186
+ continue;
187
+ }
188
+ modules.add(this.normalize(resolved));
189
+ }
190
+ return [
191
+ ...modules,
192
+ ];
193
+ }
194
+ getAllSourceFiles() {
195
+ return this.builder
196
+ .getProgram()
197
+ .getSourceFiles()
198
+ .filter(sf => !sf.isDeclarationFile);
199
+ }
200
+ getRealFileName(fileName) {
201
+ return fileName;
202
+ }
203
+ }
204
+ exports.CompilerContext = CompilerContext;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DocumentRegistry = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const typescript_1 = __importDefault(require("typescript"));
10
+ class DocumentRegistry {
11
+ documents = new Map();
12
+ normalize(fileName) {
13
+ return path_1.default.resolve(fileName)
14
+ .replace(/\\/g, "/");
15
+ }
16
+ has(fileName) {
17
+ return this.documents.has(this.normalize(fileName));
18
+ }
19
+ get(fileName) {
20
+ return this.documents.get(this.normalize(fileName));
21
+ }
22
+ getContent(fileName) {
23
+ return this.get(fileName)?.content;
24
+ }
25
+ getSnapshot(fileName) {
26
+ return this.get(fileName)?.snapshot;
27
+ }
28
+ getVersion(fileName) {
29
+ return (this.get(fileName)
30
+ ?.version
31
+ .toString()
32
+ ?? "0");
33
+ }
34
+ update(fileName, content) {
35
+ const normalized = this.normalize(fileName);
36
+ const existing = this.documents.get(normalized);
37
+ // critical:
38
+ // avoid snapshot churn
39
+ if (existing &&
40
+ existing.content === content) {
41
+ return;
42
+ }
43
+ this.documents.set(normalized, {
44
+ fileName: normalized,
45
+ content,
46
+ version: existing
47
+ ? existing.version + 1
48
+ : 1,
49
+ snapshot: typescript_1.default.ScriptSnapshot
50
+ .fromString(content),
51
+ });
52
+ }
53
+ ensure(fileName) {
54
+ const normalized = this.normalize(fileName);
55
+ if (this.documents.has(normalized)) {
56
+ return;
57
+ }
58
+ try {
59
+ const content = fs_1.default.readFileSync(normalized, "utf8");
60
+ this.update(normalized, content);
61
+ }
62
+ catch {
63
+ // ignore missing files
64
+ }
65
+ }
66
+ remove(fileName) {
67
+ this.documents.delete(this.normalize(fileName));
68
+ }
69
+ getFileNames() {
70
+ return [
71
+ ...this.documents.keys()
72
+ ];
73
+ }
74
+ invalidate() {
75
+ this.documents.clear();
76
+ }
77
+ }
78
+ exports.DocumentRegistry = DocumentRegistry;
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ // src/compiler/importGraph.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.ImportGraph = void 0;
5
+ class ImportGraph {
6
+ // file -> imports
7
+ imports = new Map();
8
+ // import -> importers
9
+ reverse = new Map();
10
+ updateFile(file, nextImports) {
11
+ const previous = this.imports.get(file)
12
+ ?? new Set();
13
+ const next = new Set(nextImports);
14
+ // remove old edges
15
+ for (const imported of previous) {
16
+ if (next.has(imported)) {
17
+ continue;
18
+ }
19
+ const reverseImporters = this.reverse.get(imported);
20
+ if (reverseImporters) {
21
+ reverseImporters.delete(file);
22
+ if (reverseImporters.size
23
+ === 0) {
24
+ this.reverse.delete(imported);
25
+ }
26
+ }
27
+ }
28
+ // add new edges
29
+ for (const imported of next) {
30
+ if (previous.has(imported)) {
31
+ continue;
32
+ }
33
+ let importers = this.reverse.get(imported);
34
+ if (!importers) {
35
+ importers =
36
+ new Set();
37
+ this.reverse.set(imported, importers);
38
+ }
39
+ importers.add(file);
40
+ }
41
+ this.imports.set(file, next);
42
+ }
43
+ removeFile(file) {
44
+ const imports = this.imports.get(file);
45
+ if (imports) {
46
+ for (const imported of imports) {
47
+ const importers = this.reverse.get(imported);
48
+ if (importers) {
49
+ importers.delete(file);
50
+ if (importers.size
51
+ === 0) {
52
+ this.reverse.delete(imported);
53
+ }
54
+ }
55
+ }
56
+ }
57
+ this.imports.delete(file);
58
+ this.reverse.delete(file);
59
+ for (const importers of this.reverse.values()) {
60
+ importers.delete(file);
61
+ }
62
+ }
63
+ collectDependents(file) {
64
+ const result = new Set();
65
+ const queue = [file];
66
+ while (queue.length) {
67
+ const current = queue.pop();
68
+ if (result.has(current)) {
69
+ continue;
70
+ }
71
+ result.add(current);
72
+ const importers = this.reverse.get(current);
73
+ if (!importers) {
74
+ continue;
75
+ }
76
+ for (const importer of importers) {
77
+ queue.push(importer);
78
+ }
79
+ }
80
+ return result;
81
+ }
82
+ }
83
+ exports.ImportGraph = ImportGraph;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Scheduler = void 0;
4
+ class Scheduler {
5
+ flush;
6
+ pending = new Map();
7
+ scheduled = false;
8
+ currentFlush = null;
9
+ constructor(flush) {
10
+ this.flush = flush;
11
+ }
12
+ async enqueue(fileName, content) {
13
+ this.pending.set(fileName, content);
14
+ if (!this.scheduled) {
15
+ this.scheduled = true;
16
+ this.currentFlush =
17
+ Promise.resolve()
18
+ .then(() => this.run());
19
+ }
20
+ await this.currentFlush;
21
+ }
22
+ async run() {
23
+ this.scheduled = false;
24
+ const batch = new Map(this.pending);
25
+ this.pending.clear();
26
+ await this.flush(batch);
27
+ }
28
+ }
29
+ exports.Scheduler = Scheduler;
@@ -0,0 +1,234 @@
1
+ "use strict";
2
+ // src/engine/engine.ts
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.EventraEngine = void 0;
8
+ const path_1 = __importDefault(require("path"));
9
+ const typescript_1 = __importDefault(require("typescript"));
10
+ const compilerContext_1 = require("../compiler/compilerContext");
11
+ const scheduler_1 = require("../compiler/scheduler");
12
+ const importGraph_1 = require("../compiler/importGraph");
13
+ const evaluationCache_1 = require("../analysis/cache/evaluationCache");
14
+ const resolvedCallCache_1 = require("../analysis/cache/resolvedCallCache");
15
+ const resolvedExportCache_1 = require("../analysis/cache/resolvedExportCache");
16
+ const symbolInvalidation_1 = require("../analysis/cache/symbolInvalidation");
17
+ const scanner_1 = require("../analysis/scanner/scanner");
18
+ const extractor_1 = require("../analysis/extractor/extractor");
19
+ const wrapperRegistry_1 = require("../analysis/symbols/wrapperRegistry");
20
+ const EMPTY_RESULT = () => ({
21
+ events: new Set(),
22
+ detectedFunctionWrappers: new Set(),
23
+ detectedComponentWrappers: new Map(),
24
+ });
25
+ class EventraEngine {
26
+ compiler;
27
+ scheduler;
28
+ importGraph = new importGraph_1.ImportGraph();
29
+ evaluationCache = new evaluationCache_1.EvaluationCache();
30
+ resolvedCallCache = new resolvedCallCache_1.ResolvedCallCache();
31
+ resolvedExportCache = new resolvedExportCache_1.ResolvedExportCache();
32
+ wrapperRegistry;
33
+ fileResults = new Map();
34
+ normalizedPathCache = new Map();
35
+ isPreloading = false;
36
+ lastConfig = {
37
+ events: [],
38
+ wrappers: [],
39
+ functionWrappers: [],
40
+ sync: {
41
+ include: [],
42
+ exclude: [],
43
+ },
44
+ };
45
+ constructor(rootDir) {
46
+ this.compiler =
47
+ new compilerContext_1.CompilerContext(rootDir);
48
+ this.wrapperRegistry = new wrapperRegistry_1.WrapperRegistry();
49
+ this.scheduler =
50
+ new scheduler_1.Scheduler(async (updates) => {
51
+ //
52
+ // APPLY UPDATES
53
+ //
54
+ for (const [file, content] of updates) {
55
+ //
56
+ // SYMBOL INVALIDATION
57
+ //
58
+ const existing = this.compiler.getSourceFile(file);
59
+ if (existing) {
60
+ (0, symbolInvalidation_1.invalidateSourceFileSymbols)(existing, this.compiler.getChecker(), this.evaluationCache, this.resolvedCallCache, this.resolvedExportCache);
61
+ }
62
+ //
63
+ // UPDATE FILE
64
+ //
65
+ await this.compiler.updateFile(file, content);
66
+ //
67
+ // UPDATE IMPORT GRAPH
68
+ //
69
+ this.updateImportGraph(file);
70
+ }
71
+ //
72
+ // COLLECT AFFECTED FILES
73
+ //
74
+ const affected = new Set();
75
+ for (const file of updates.keys()) {
76
+ const dependents = this.importGraph
77
+ .collectDependents(file);
78
+ for (const dependent of dependents) {
79
+ affected.add(dependent);
80
+ }
81
+ }
82
+ //
83
+ // RE-ANALYZE
84
+ //
85
+ for (const file of affected) {
86
+ this.analyzeFile(file, this.lastConfig);
87
+ }
88
+ });
89
+ }
90
+ invalidateCaches() {
91
+ this.evaluationCache
92
+ .clear();
93
+ this.resolvedCallCache
94
+ .clear();
95
+ this.resolvedExportCache
96
+ .clear();
97
+ this.wrapperRegistry
98
+ .clear();
99
+ }
100
+ beginPreload() {
101
+ this.isPreloading =
102
+ true;
103
+ }
104
+ endPreload() {
105
+ this.isPreloading =
106
+ false;
107
+ }
108
+ normalize(fileName) {
109
+ const cached = this.normalizedPathCache.get(fileName);
110
+ if (cached) {
111
+ return cached;
112
+ }
113
+ const normalized = path_1.default.resolve(fileName)
114
+ .replace(/\\/g, "/");
115
+ this.normalizedPathCache.set(fileName, normalized);
116
+ return normalized;
117
+ }
118
+ updateImportGraph(fileName) {
119
+ const normalized = this.normalize(fileName);
120
+ const source = this.compiler.getSourceFile(normalized);
121
+ if (!source) {
122
+ this.importGraph.removeFile(normalized);
123
+ return;
124
+ }
125
+ const imports = [];
126
+ for (const statement of source.statements) {
127
+ if (typescript_1.default.isExportAssignment(statement)) {
128
+ continue;
129
+ }
130
+ if (!typescript_1.default.isImportDeclaration(statement)
131
+ && !typescript_1.default.isExportDeclaration(statement)) {
132
+ continue;
133
+ }
134
+ const specifier = statement.moduleSpecifier;
135
+ if (!specifier
136
+ || !typescript_1.default.isStringLiteral(specifier)) {
137
+ continue;
138
+ }
139
+ const resolved = this.compiler.resolveModule(specifier.text, this.compiler.getRealFileName(source.fileName));
140
+ if (!resolved) {
141
+ continue;
142
+ }
143
+ imports.push(this.normalize(resolved));
144
+ }
145
+ this.importGraph.updateFile(normalized, imports);
146
+ }
147
+ analyzeFile(fileName, config) {
148
+ const normalized = this.normalize(fileName);
149
+ const source = this.compiler.getSourceFile(normalized);
150
+ if (!source) {
151
+ this.fileResults.delete(normalized);
152
+ return EMPTY_RESULT();
153
+ }
154
+ const checker = this.compiler.getChecker();
155
+ const index = (0, scanner_1.scanSource)(source, checker, config, this.resolvedCallCache, this.resolvedExportCache, this.wrapperRegistry);
156
+ const result = (0, extractor_1.extractEvents)(index, checker, config, this.evaluationCache, this.resolvedExportCache, this.resolvedCallCache, this.wrapperRegistry);
157
+ this.fileResults.set(normalized, result);
158
+ return result;
159
+ }
160
+ async preloadFile(fileName, content) {
161
+ if (!this.isPreloading) {
162
+ throw new Error("preload phase not active");
163
+ }
164
+ const normalized = this.normalize(fileName);
165
+ await this.compiler.updateFile(normalized, content);
166
+ this.updateImportGraph(normalized);
167
+ }
168
+ async updateFile(fileName, content, config) {
169
+ this.lastConfig =
170
+ config;
171
+ const normalized = this.normalize(fileName);
172
+ await this.scheduler.enqueue(normalized, content);
173
+ }
174
+ scanFile(fileName, config) {
175
+ this.lastConfig =
176
+ config;
177
+ return this.analyzeFile(fileName, config);
178
+ }
179
+ async syncFile(fileName, content, config) {
180
+ this.lastConfig =
181
+ config;
182
+ const normalized = this.normalize(fileName);
183
+ const existing = this.compiler.getSourceFile(normalized);
184
+ if (existing) {
185
+ (0, symbolInvalidation_1.invalidateSourceFileSymbols)(existing, this.compiler.getChecker(), this.evaluationCache, this.resolvedCallCache, this.resolvedExportCache);
186
+ }
187
+ await this.compiler.updateFile(normalized, content);
188
+ this.updateImportGraph(normalized);
189
+ return this.analyzeFile(normalized, config);
190
+ }
191
+ async removeFile(fileName, config) {
192
+ const normalized = this.normalize(fileName);
193
+ this.invalidateCaches();
194
+ const affected = this.importGraph
195
+ .collectDependents(normalized);
196
+ this.importGraph.removeFile(normalized);
197
+ this.compiler.removeFile(normalized);
198
+ this.fileResults.delete(normalized);
199
+ this.normalizedPathCache.delete(fileName);
200
+ this.normalizedPathCache.delete(normalized);
201
+ const nextConfig = config
202
+ ?? this.lastConfig;
203
+ for (const file of affected) {
204
+ if (file === normalized) {
205
+ continue;
206
+ }
207
+ this.analyzeFile(file, nextConfig);
208
+ }
209
+ }
210
+ getResult(fileName) {
211
+ return (this.fileResults.get(this.normalize(fileName))
212
+ ?? EMPTY_RESULT());
213
+ }
214
+ getAllEvents() {
215
+ const events = new Set();
216
+ for (const result of this.fileResults.values()) {
217
+ for (const event of result.events) {
218
+ events.add(event);
219
+ }
220
+ }
221
+ return [
222
+ ...events,
223
+ ].sort();
224
+ }
225
+ getDiagnostics(fileName) {
226
+ return this.compiler
227
+ .getSemanticDiagnostics(fileName);
228
+ }
229
+ invalidate() {
230
+ this.invalidateCaches();
231
+ this.fileResults.clear();
232
+ }
233
+ }
234
+ exports.EventraEngine = EventraEngine;
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ // src/core/projectScanner.ts
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.scanProject = scanProject;
8
+ const fast_glob_1 = __importDefault(require("fast-glob"));
9
+ const promises_1 = __importDefault(require("fs/promises"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const EventraEngine_1 = require("./EventraEngine");
12
+ const processFile_1 = require("../filesystem/processFile");
13
+ const getVirtualFile_1 = require("../filesystem/getVirtualFile");
14
+ async function getParsedFile(file, cache) {
15
+ const existing = cache.get(file);
16
+ if (existing) {
17
+ return existing;
18
+ }
19
+ const raw = await promises_1.default.readFile(file, "utf8");
20
+ const parsed = await (0, processFile_1.processFile)(file, raw);
21
+ cache.set(file, parsed);
22
+ return parsed;
23
+ }
24
+ async function scanProject(config) {
25
+ const files = await (0, fast_glob_1.default)(config.sync.include, {
26
+ ignore: config.sync.exclude,
27
+ absolute: true,
28
+ });
29
+ const engine = new EventraEngine_1.EventraEngine(process.cwd());
30
+ const cache = new Map();
31
+ const toScan = new Set();
32
+ // COLLECT
33
+ for (const file of files) {
34
+ try {
35
+ const raw = await promises_1.default.readFile(file, "utf8");
36
+ if (raw.length
37
+ > 2_000_000) {
38
+ continue;
39
+ }
40
+ const parsed = await (0, processFile_1.processFile)(file, raw);
41
+ cache.set(file, parsed);
42
+ toScan.add(file);
43
+ for (const dep of parsed.dependencies) {
44
+ toScan.add(path_1.default.resolve(path_1.default.dirname(file), dep));
45
+ }
46
+ }
47
+ catch { }
48
+ }
49
+ // PRELOAD
50
+ engine.beginPreload();
51
+ for (const file of toScan) {
52
+ try {
53
+ const parsed = await getParsedFile(file, cache);
54
+ await engine.preloadFile((0, getVirtualFile_1.getVirtualFile)(file), parsed.content);
55
+ }
56
+ catch { }
57
+ }
58
+ engine.endPreload();
59
+ // SCAN
60
+ const results = new Map();
61
+ for (const file of toScan) {
62
+ try {
63
+ const result = engine.scanFile((0, getVirtualFile_1.getVirtualFile)(file), config);
64
+ results.set(file, result);
65
+ }
66
+ catch { }
67
+ }
68
+ return {
69
+ engine,
70
+ results,
71
+ files,
72
+ };
73
+ }