@eventra_dev/eventra-cli 0.3.11 → 0.3.13

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.
package/dist/cli/watch.js CHANGED
@@ -41,10 +41,12 @@ async function watch() {
41
41
  try {
42
42
  const abs = normalize(file);
43
43
  const raw = await promises_1.default.readFile(abs, "utf-8");
44
+ // processFile extracts import specifiers for chokidar tracking;
45
+ // the engine itself receives raw file content.
44
46
  const parsed = await (0, processFile_1.processFile)(abs, raw);
45
47
  cache.set(abs, parsed);
46
48
  trackedFiles.add(abs);
47
- await engine.preloadFile((0, getVirtualFile_1.getVirtualFile)(abs), parsed.content);
49
+ await engine.preloadFile((0, getVirtualFile_1.getVirtualFile)(abs), raw);
48
50
  fileDeps.set(abs, new Set(parsed.dependencies
49
51
  .filter((dep) => dep.startsWith(".") || dep.startsWith("/"))
50
52
  .map((dep) => normalize(path_1.default.resolve(path_1.default.dirname(abs), dep)))));
@@ -68,10 +70,13 @@ async function watch() {
68
70
  for (const file of batch) {
69
71
  try {
70
72
  const raw = await promises_1.default.readFile(file, "utf-8");
73
+ // processFile here is used ONLY to extract import specifiers for
74
+ // chokidar's dependency tracking. The engine receives raw content
75
+ // and parses it itself.
71
76
  const parsed = await (0, processFile_1.processFile)(file, raw);
72
77
  cache.set(file, parsed);
73
78
  trackedFiles.add(file);
74
- await engine.updateFile((0, getVirtualFile_1.getVirtualFile)(file), parsed.content, config);
79
+ await engine.updateFile((0, getVirtualFile_1.getVirtualFile)(file), raw, config);
75
80
  const abs = normalize(file);
76
81
  const prevDeps = fileDeps.get(abs) ?? new Set();
77
82
  const nextDeps = new Set(parsed.dependencies
@@ -8,7 +8,6 @@ const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
9
  const typescript_1 = __importDefault(require("typescript"));
10
10
  const documentRegistry_1 = require("./documentRegistry");
11
- const processFile_1 = require("../filesystem/processFile");
12
11
  class CompilerContext {
13
12
  rootDir;
14
13
  registry = new documentRegistry_1.DocumentRegistry();
@@ -137,10 +136,10 @@ class CompilerContext {
137
136
  flushUpdates() {
138
137
  this.rebuildProgram();
139
138
  }
140
- async updateFile(fileName, content) {
139
+ // accept content as-is; the engine layer is responsible for parsing via processFile
140
+ updateFile(fileName, content) {
141
141
  const normalized = this.normalize(fileName);
142
- const processed = await (0, processFile_1.processFile)(normalized, content);
143
- this.stageFile(processed.fileName, processed.content);
142
+ this.stageFile(normalized, content);
144
143
  this.rebuildProgram();
145
144
  }
146
145
  removeFile(fileName) {
@@ -13,7 +13,6 @@ const evaluationCache_1 = require("../analysis/cache/evaluationCache");
13
13
  const resolvedCallCache_1 = require("../analysis/cache/resolvedCallCache");
14
14
  const resolvedExportCache_1 = require("../analysis/cache/resolvedExportCache");
15
15
  const returnPropagationCache_1 = require("../analysis/cache/returnPropagationCache");
16
- const symbolInvalidation_1 = require("../analysis/cache/symbolInvalidation");
17
16
  const analyzeFile_1 = require("../analysis/engine/analyzeFile");
18
17
  const wrapperRegistry_1 = require("../analysis/symbols/wrapperRegistry");
19
18
  const processFile_1 = require("../filesystem/processFile");
@@ -46,22 +45,52 @@ class EventraEngine {
46
45
  this.compiler = new compilerContext_1.CompilerContext(rootDir);
47
46
  this.wrapperRegistry = new wrapperRegistry_1.WrapperRegistry(this.compiler.getChecker(), this.resolvedExportCache);
48
47
  this.scheduler = new scheduler_1.Scheduler(async (updates) => {
49
- const affected = new Set();
48
+ const changedFiles = new Set();
50
49
  for (const [file, content] of updates) {
51
- const existing = this.compiler.getSourceFile(file);
52
- if (existing) {
53
- (0, symbolInvalidation_1.invalidateSourceFileSymbols)(existing, this.compiler.getChecker(), this.evaluationCache, this.resolvedCallCache, this.resolvedExportCache, this.returnPropagationCache, this.wrapperRegistry);
54
- }
55
50
  await this.compiler.updateFile(file, content);
56
51
  this.updateImportGraph(file);
52
+ changedFiles.add(file);
53
+ }
54
+ // Program was rebuilt → every ts.Symbol from the previous program is now
55
+ // dead. WeakMap-keyed caches (incl. WrapperRegistry) must be dropped before
56
+ // we touch any NEW symbols, otherwise cross-file wrapper lookups miss.
57
+ this.resetCaches();
58
+ this.refreshChecker();
59
+ // Full re-index: WrapperRegistry must contain entries keyed by NEW
60
+ // symbols for every file, otherwise affected files cannot resolve
61
+ // wrappers defined in unaffected files (this is the watch-corruption bug).
62
+ this.reindexAll(this.lastConfig);
63
+ // Incremental extract: only re-extract changed files + their dependents.
64
+ // Unaffected files keep their previously-computed Set<string> events,
65
+ // which is safe (just strings, no AST refs).
66
+ const affected = new Set();
67
+ for (const file of changedFiles) {
57
68
  affected.add(file);
58
69
  for (const dependent of this.importGraph.collectDependents(file)) {
59
70
  affected.add(dependent);
60
71
  }
61
72
  }
62
- await this.reanalyzeFiles([...affected], this.lastConfig);
73
+ for (const file of affected) {
74
+ this.extractFile(file, this.lastConfig);
75
+ }
63
76
  });
64
77
  }
78
+ // drop every symbol-keyed cache; call after a program rebuild
79
+ resetCaches() {
80
+ this.evaluationCache.clear();
81
+ this.resolvedCallCache.clear();
82
+ this.resolvedExportCache.clear();
83
+ this.returnPropagationCache.clear();
84
+ this.wrapperRegistry.clear();
85
+ }
86
+ // re-index every source file in the current program (skips .d.ts)
87
+ reindexAll(config) {
88
+ const sources = this.compiler.getAllSourceFiles();
89
+ this.fileIndices.clear();
90
+ for (const sf of sources) {
91
+ this.indexFile(sf.fileName, config);
92
+ }
93
+ }
65
94
  beginPreload() {
66
95
  this.isPreloading = true;
67
96
  }
@@ -137,11 +166,13 @@ class EventraEngine {
137
166
  }
138
167
  async reanalyzeFiles(fileNames, config) {
139
168
  this.lastConfig = config;
169
+ // Same correctness contract as the scheduler flush: the program may have
170
+ // just been rebuilt (e.g. via removeFile), so clear symbol caches and
171
+ // re-index every file before re-extracting the requested slice.
172
+ this.resetCaches();
140
173
  this.refreshChecker();
174
+ this.reindexAll(config);
141
175
  const normalized = fileNames.map((f) => this.normalize(f));
142
- for (const file of normalized) {
143
- this.indexFile(file, config);
144
- }
145
176
  for (const file of normalized) {
146
177
  this.extractFile(file, config);
147
178
  }
@@ -175,10 +206,6 @@ class EventraEngine {
175
206
  }
176
207
  async removeFile(fileName, config) {
177
208
  const normalized = this.normalize(fileName);
178
- const existing = this.compiler.getSourceFile(normalized);
179
- if (existing) {
180
- (0, symbolInvalidation_1.invalidateSourceFileSymbols)(existing, this.compiler.getChecker(), this.evaluationCache, this.resolvedCallCache, this.resolvedExportCache, this.returnPropagationCache, this.wrapperRegistry);
181
- }
182
209
  const affected = this.importGraph.collectDependents(normalized);
183
210
  this.importGraph.removeFile(normalized);
184
211
  this.compiler.removeFile(normalized);
@@ -188,9 +215,17 @@ class EventraEngine {
188
215
  this.normalizedPathCache.delete(normalized);
189
216
  const nextConfig = config ?? this.lastConfig;
190
217
  const toReanalyze = [...affected].filter((f) => f !== normalized);
218
+ // reanalyzeFiles already resets caches + reindexes all sources,
219
+ // which is what we need after compiler.removeFile rebuilds the program.
191
220
  if (toReanalyze.length > 0) {
192
221
  await this.reanalyzeFiles(toReanalyze, nextConfig);
193
222
  }
223
+ else {
224
+ // Even without dependents, the program changed → caches must be reset
225
+ // so the next call (e.g. runFullAnalysis) sees a clean slate.
226
+ this.resetCaches();
227
+ this.refreshChecker();
228
+ }
194
229
  }
195
230
  getAllEvents() {
196
231
  const events = new Set();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventra_dev/eventra-cli",
3
- "version": "0.3.11",
3
+ "version": "0.3.13",
4
4
  "description": "Static analytics event extraction CLI powered by the TypeScript compiler API",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",