@eventra_dev/eventra-cli 0.3.4 → 0.3.6

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.
@@ -0,0 +1,76 @@
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.analyzeFileRecursive = analyzeFileRecursive;
7
+ const typescript_1 = __importDefault(require("typescript"));
8
+ const propagationAnalyzer_1 = require("../scanner/analyzer/propagationAnalyzer");
9
+ const extractor_1 = require("../extractor/extractor");
10
+ async function analyzeFileRecursive(sourceFile, config, wrapperRegistry, evaluationCache, resolvedCallCache, returnPropagationCache, resolvedExportCache) {
11
+ const checker = wrapperRegistry["checker"];
12
+ const sinks = [];
13
+ const wrappers = [];
14
+ const trackCalls = [];
15
+ // Support empty functionWrappers
16
+ const wrapperNames = new Set(config.functionWrappers?.map(w => w.name) ?? []);
17
+ function visit(node, parentFunction) {
18
+ // Detect calls to configured wrapper functions
19
+ if (typescript_1.default.isCallExpression(node)) {
20
+ const callName = getCallName(node.expression);
21
+ if (wrapperNames.has(callName)) {
22
+ // Track this wrapper call
23
+ sinks.push({ call: node, trackedArguments: [{ index: 0, propertyPath: [] }] });
24
+ trackCalls.push({
25
+ node,
26
+ sourceFile,
27
+ trackedArguments: node.arguments.filter(typescript_1.default.isExpression)
28
+ });
29
+ }
30
+ }
31
+ // Detect and analyze function declarations / expressions / methods
32
+ if (typescript_1.default.isFunctionDeclaration(node) ||
33
+ typescript_1.default.isFunctionExpression(node) ||
34
+ typescript_1.default.isArrowFunction(node) ||
35
+ typescript_1.default.isMethodDeclaration(node)) {
36
+ const localSinks = sinks.filter(s => isNodeInside(s.call, node));
37
+ const wrapper = (0, propagationAnalyzer_1.analyzeWrapperPropagation)(node, checker, localSinks);
38
+ if (wrapper) {
39
+ wrappers.push(wrapper);
40
+ wrapperRegistry.set(wrapper);
41
+ }
42
+ // Recurse into function body
43
+ if (node.body) {
44
+ typescript_1.default.forEachChild(node.body, child => visit(child, node));
45
+ }
46
+ return;
47
+ }
48
+ // Recurse into child nodes
49
+ typescript_1.default.forEachChild(node, child => visit(child, parentFunction));
50
+ }
51
+ visit(sourceFile);
52
+ // Extract events from tracked calls and wrapper metadata
53
+ return (0, extractor_1.extractEvents)({
54
+ fileName: sourceFile.fileName,
55
+ sourceFile,
56
+ sinks,
57
+ trackCalls,
58
+ wrappers,
59
+ }, checker, config, evaluationCache, resolvedExportCache, resolvedCallCache, returnPropagationCache, wrapperRegistry);
60
+ }
61
+ function isNodeInside(child, fn) {
62
+ let current = child;
63
+ while (current) {
64
+ if (current === fn)
65
+ return true;
66
+ current = current.parent;
67
+ }
68
+ return false;
69
+ }
70
+ function getCallName(expr) {
71
+ if (typescript_1.default.isIdentifier(expr))
72
+ return expr.text;
73
+ if (typescript_1.default.isPropertyAccessExpression(expr) || typescript_1.default.isPropertyAccessChain(expr))
74
+ return expr.name.text;
75
+ return "";
76
+ }
@@ -14,9 +14,8 @@ 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
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
17
  const wrapperRegistry_1 = require("../analysis/symbols/wrapperRegistry");
18
+ const recursiveWrapperAnalyzer_1 = require("../analysis/engine/recursiveWrapperAnalyzer");
20
19
  const EMPTY_RESULT = () => ({
21
20
  events: new Set(),
22
21
  detectedFunctionWrappers: new Set(),
@@ -63,7 +62,7 @@ class EventraEngine {
63
62
  }
64
63
  // RE-ANALYZE
65
64
  for (const file of affected) {
66
- this.analyzeFile(file, this.lastConfig);
65
+ await this.analyzeFile(file, this.lastConfig);
67
66
  }
68
67
  });
69
68
  }
@@ -109,19 +108,6 @@ class EventraEngine {
109
108
  }
110
109
  this.importGraph.updateFile(normalized, imports);
111
110
  }
112
- analyzeFile(fileName, config) {
113
- const normalized = this.normalize(fileName);
114
- const source = this.compiler.getSourceFile(normalized);
115
- if (!source) {
116
- this.fileResults.delete(normalized);
117
- return EMPTY_RESULT();
118
- }
119
- const checker = this.compiler.getChecker();
120
- const index = (0, scanner_1.scanSource)(source, checker, this.wrapperRegistry);
121
- const result = (0, extractor_1.extractEvents)(index, checker, config, this.evaluationCache, this.resolvedExportCache, this.resolvedCallCache, this.returnPropagationCache, this.wrapperRegistry);
122
- this.fileResults.set(normalized, result);
123
- return result;
124
- }
125
111
  async preloadFile(fileName, content) {
126
112
  if (!this.isPreloading) {
127
113
  throw new Error("preload phase not active");
@@ -135,9 +121,9 @@ class EventraEngine {
135
121
  const normalized = this.normalize(fileName);
136
122
  await this.scheduler.enqueue(normalized, content);
137
123
  }
138
- scanFile(fileName, config) {
124
+ async scanFile(fileName, config) {
139
125
  this.lastConfig = config;
140
- return this.analyzeFile(fileName, config);
126
+ return await this.analyzeFile(fileName, config);
141
127
  }
142
128
  async removeFile(fileName, config) {
143
129
  const normalized = this.normalize(fileName);
@@ -152,9 +138,20 @@ class EventraEngine {
152
138
  if (file === normalized) {
153
139
  continue;
154
140
  }
155
- this.analyzeFile(file, nextConfig);
141
+ await this.analyzeFile(file, nextConfig);
156
142
  }
157
143
  }
144
+ async analyzeFile(fileName, config) {
145
+ const normalized = this.normalize(fileName);
146
+ const source = this.compiler.getSourceFile(normalized);
147
+ if (!source) {
148
+ this.fileResults.delete(normalized);
149
+ return EMPTY_RESULT();
150
+ }
151
+ const result = await (0, recursiveWrapperAnalyzer_1.analyzeFileRecursive)(source, config, this.wrapperRegistry, this.evaluationCache, this.resolvedCallCache, this.returnPropagationCache, this.resolvedExportCache);
152
+ this.fileResults.set(normalized, result);
153
+ return result;
154
+ }
158
155
  getAllEvents() {
159
156
  const events = new Set();
160
157
  for (const result of this.fileResults.values()) {
@@ -12,9 +12,8 @@ const processFile_1 = require("../filesystem/processFile");
12
12
  const getVirtualFile_1 = require("../filesystem/getVirtualFile");
13
13
  async function getParsedFile(file, cache) {
14
14
  const existing = cache.get(file);
15
- if (existing) {
15
+ if (existing)
16
16
  return existing;
17
- }
18
17
  const raw = await promises_1.default.readFile(file, "utf8");
19
18
  const parsed = await (0, processFile_1.processFile)(file, raw);
20
19
  cache.set(file, parsed);
@@ -32,9 +31,8 @@ async function scanProject(config) {
32
31
  for (const file of files) {
33
32
  try {
34
33
  const raw = await promises_1.default.readFile(file, "utf8");
35
- if (raw.length > 2_000_000) {
34
+ if (raw.length > 2_000_000)
36
35
  continue;
37
- }
38
36
  const parsed = await (0, processFile_1.processFile)(file, raw);
39
37
  cache.set(file, parsed);
40
38
  toScan.add(file);
@@ -60,7 +58,7 @@ async function scanProject(config) {
60
58
  const results = new Map();
61
59
  for (const file of toScan) {
62
60
  try {
63
- const result = engine.scanFile((0, getVirtualFile_1.getVirtualFile)(file), config);
61
+ const result = await engine.scanFile((0, getVirtualFile_1.getVirtualFile)(file), config);
64
62
  results.set(file, result);
65
63
  }
66
64
  catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventra_dev/eventra-cli",
3
- "version": "0.3.4",
3
+ "version": "0.3.6",
4
4
  "description": "Static analytics event extraction CLI powered by the TypeScript compiler API",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",