@eventra_dev/eventra-cli 0.3.6 → 0.3.10

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.
@@ -14,11 +14,12 @@ class CompilerContext {
14
14
  registry = new documentRegistry_1.DocumentRegistry();
15
15
  compilerOptions;
16
16
  host;
17
- builder;
17
+ program = typescript_1.default.createProgram([], {});
18
18
  rootNames = new Set();
19
19
  constructor(rootDir) {
20
20
  this.rootDir = rootDir;
21
- const configPath = typescript_1.default.findConfigFile(rootDir, typescript_1.default.sys.fileExists, "tsconfig.json");
21
+ const localTsconfig = path_1.default.join(rootDir, "tsconfig.json");
22
+ const configPath = fs_1.default.existsSync(localTsconfig) ? localTsconfig : undefined;
22
23
  let compilerOptions = {
23
24
  target: typescript_1.default.ScriptTarget.ESNext,
24
25
  module: typescript_1.default.ModuleKind.ESNext,
@@ -33,7 +34,7 @@ class CompilerContext {
33
34
  esModuleInterop: true,
34
35
  resolveJsonModule: true,
35
36
  allowNonTsExtensions: true,
36
- incremental: true,
37
+ incremental: false,
37
38
  };
38
39
  if (configPath) {
39
40
  const config = typescript_1.default.readConfigFile(configPath, typescript_1.default.sys.readFile);
@@ -52,30 +53,59 @@ class CompilerContext {
52
53
  this.compilerOptions = compilerOptions;
53
54
  this.host = typescript_1.default.createCompilerHost(this.compilerOptions, true);
54
55
  this.patchCompilerHost();
55
- this.builder = typescript_1.default.createEmitAndSemanticDiagnosticsBuilderProgram([...this.rootNames], this.compilerOptions, this.host);
56
+ this.rebuildProgram();
56
57
  }
57
58
  normalize(fileName) {
58
59
  return this.registry.normalize(fileName);
59
60
  }
61
+ resolveLanguageVersion(languageVersionOrOptions) {
62
+ if (typeof languageVersionOrOptions === "object") {
63
+ return languageVersionOrOptions.languageVersion;
64
+ }
65
+ return languageVersionOrOptions;
66
+ }
67
+ attachVersion(sourceFile, fileName) {
68
+ const normalized = this.normalize(fileName);
69
+ sourceFile.version =
70
+ this.registry.getVersion(normalized);
71
+ return sourceFile;
72
+ }
73
+ createSourceFileFromRegistry(fileName, languageVersion) {
74
+ const normalized = this.normalize(fileName);
75
+ const snapshot = this.registry.getSnapshot(normalized);
76
+ if (!snapshot) {
77
+ return undefined;
78
+ }
79
+ const sourceFile = typescript_1.default.createSourceFile(normalized, snapshot.getText(0, snapshot.getLength()), languageVersion, true, this.getScriptKind(normalized));
80
+ return this.attachVersion(sourceFile, normalized);
81
+ }
60
82
  patchCompilerHost() {
61
- const originalGetSourceFile = this.host.getSourceFile;
62
- this.host.getSourceFile = (fileName, languageVersion, onError, shouldCreateNewSourceFile) => {
83
+ const originalGetSourceFile = this.host.getSourceFile.bind(this.host);
84
+ this.host.getSourceFile = (fileName, languageVersionOrOptions, onError, shouldCreateNewSourceFile) => {
85
+ const languageVersion = this.resolveLanguageVersion(languageVersionOrOptions);
63
86
  const normalized = this.normalize(fileName);
64
87
  this.registry.ensure(normalized);
65
- const snapshot = this.registry.getSnapshot(normalized);
66
- if (!snapshot) {
67
- return originalGetSourceFile(normalized, languageVersion, onError, shouldCreateNewSourceFile);
88
+ const fromRegistry = this.createSourceFileFromRegistry(normalized, languageVersion);
89
+ if (fromRegistry) {
90
+ return fromRegistry;
91
+ }
92
+ const fromDisk = originalGetSourceFile(normalized, languageVersionOrOptions, onError, shouldCreateNewSourceFile);
93
+ if (!fromDisk) {
94
+ return undefined;
95
+ }
96
+ if (!this.registry.has(normalized)) {
97
+ this.registry.update(normalized, fromDisk.getFullText());
68
98
  }
69
- return typescript_1.default.createSourceFile(normalized, snapshot.getText(0, snapshot.getLength()), languageVersion, true, this.getScriptKind(normalized));
99
+ return this.attachVersion(fromDisk, normalized);
70
100
  };
71
101
  this.host.readFile = (fileName) => {
72
102
  const normalized = this.normalize(fileName);
73
103
  this.registry.ensure(normalized);
74
- return (this.registry.getContent(normalized) ?? fs_1.default.readFileSync(normalized, "utf8"));
104
+ return this.registry.getContent(normalized) ?? fs_1.default.readFileSync(normalized, "utf8");
75
105
  };
76
106
  this.host.fileExists = (fileName) => {
77
107
  const normalized = this.normalize(fileName);
78
- return (this.registry.has(normalized) || fs_1.default.existsSync(normalized));
108
+ return this.registry.has(normalized) || fs_1.default.existsSync(normalized);
79
109
  };
80
110
  this.host.getCanonicalFileName = (f) => this.normalize(f);
81
111
  this.host.useCaseSensitiveFileNames = () => true;
@@ -83,64 +113,62 @@ class CompilerContext {
83
113
  this.host.getNewLine = () => "\n";
84
114
  }
85
115
  getScriptKind(fileName) {
86
- if (fileName.endsWith(".tsx")) {
116
+ if (fileName.endsWith(".tsx"))
87
117
  return typescript_1.default.ScriptKind.TSX;
88
- }
89
- if (fileName.endsWith(".ts")) {
118
+ if (fileName.endsWith(".ts"))
90
119
  return typescript_1.default.ScriptKind.TS;
91
- }
92
- if (fileName.endsWith(".jsx")) {
120
+ if (fileName.endsWith(".jsx"))
93
121
  return typescript_1.default.ScriptKind.JSX;
94
- }
95
- if (fileName.endsWith(".js")) {
122
+ if (fileName.endsWith(".js"))
96
123
  return typescript_1.default.ScriptKind.JS;
97
- }
98
- if (fileName.endsWith(".json")) {
124
+ if (fileName.endsWith(".json"))
99
125
  return typescript_1.default.ScriptKind.JSON;
100
- }
101
126
  return typescript_1.default.ScriptKind.Unknown;
102
127
  }
103
- rebuild() {
104
- this.builder = typescript_1.default.createEmitAndSemanticDiagnosticsBuilderProgram([...this.rootNames], this.compilerOptions, this.host, this.builder);
128
+ rebuildProgram() {
129
+ this.program = typescript_1.default.createProgram([...this.rootNames], this.compilerOptions, this.host);
130
+ }
131
+ /** Register file content without rebuilding the TS program (batch via {@link flushUpdates}). */
132
+ stageFile(fileName, content) {
133
+ const normalized = this.normalize(fileName);
134
+ this.registry.update(normalized, content);
135
+ this.rootNames.add(normalized);
136
+ }
137
+ flushUpdates() {
138
+ this.rebuildProgram();
105
139
  }
106
140
  async updateFile(fileName, content) {
107
141
  const normalized = this.normalize(fileName);
108
142
  const processed = await (0, processFile_1.processFile)(normalized, content);
109
- this.registry.update(processed.fileName, processed.content);
110
- this.rootNames.add(processed.fileName);
111
- this.rebuild();
143
+ this.stageFile(processed.fileName, processed.content);
144
+ this.rebuildProgram();
112
145
  }
113
146
  removeFile(fileName) {
114
147
  const normalized = this.normalize(fileName);
115
148
  this.registry.remove(normalized);
116
149
  this.rootNames.delete(normalized);
117
- this.rebuild();
118
- }
119
- invalidate() {
120
- this.registry.invalidate();
121
- this.rebuild();
150
+ this.rebuildProgram();
122
151
  }
123
152
  getProgram() {
124
- return this.builder.getProgram();
153
+ return this.program;
125
154
  }
126
155
  getChecker() {
127
- return this.builder.getProgram().getTypeChecker();
156
+ return this.program.getTypeChecker();
128
157
  }
129
158
  getSourceFile(fileName) {
130
- return this.builder.getProgram().getSourceFile(this.normalize(fileName));
159
+ return this.program.getSourceFile(this.normalize(fileName));
131
160
  }
132
161
  getSemanticDiagnostics(fileName) {
133
- const program = this.builder.getProgram();
134
162
  if (!fileName) {
135
- return typescript_1.default.getPreEmitDiagnostics(program);
163
+ return typescript_1.default.getPreEmitDiagnostics(this.program);
136
164
  }
137
165
  const sourceFile = this.getSourceFile(fileName);
138
166
  if (!sourceFile) {
139
167
  return [];
140
168
  }
141
169
  return [
142
- ...program.getSemanticDiagnostics(sourceFile),
143
- ...program.getSyntacticDiagnostics(sourceFile),
170
+ ...this.program.getSemanticDiagnostics(sourceFile),
171
+ ...this.program.getSyntacticDiagnostics(sourceFile),
144
172
  ];
145
173
  }
146
174
  resolveModule(moduleName, fromFile) {
@@ -162,20 +190,14 @@ class CompilerContext {
162
190
  continue;
163
191
  }
164
192
  const resolved = this.resolveModule(specifier.text, source.fileName);
165
- if (!resolved) {
166
- continue;
193
+ if (resolved) {
194
+ modules.add(this.normalize(resolved));
167
195
  }
168
- modules.add(this.normalize(resolved));
169
196
  }
170
- return [
171
- ...modules,
172
- ];
197
+ return [...modules];
173
198
  }
174
199
  getAllSourceFiles() {
175
- return this.builder.getProgram().getSourceFiles().filter(sf => !sf.isDeclarationFile);
176
- }
177
- getRealFileName(fileName) {
178
- return fileName;
200
+ return this.program.getSourceFiles().filter((sf) => !sf.isDeclarationFile);
179
201
  }
180
202
  }
181
203
  exports.CompilerContext = CompilerContext;
@@ -13,10 +13,21 @@ exports.CONFIG_NAME = "eventra.json";
13
13
  const DEFAULT_INCLUDE = ["**/*.{ts,tsx,js,jsx}"];
14
14
  const DEFAULT_EXCLUDE = ["node_modules", "dist", ".next", ".git"];
15
15
  function normalizeConfig(config) {
16
+ const functionWrappers = (config.functionWrappers ?? [])
17
+ .filter((w) => w?.name)
18
+ .map((w) => ({ name: w.name }));
19
+ const seen = new Set();
20
+ const uniqueWrappers = functionWrappers.filter((w) => {
21
+ if (seen.has(w.name))
22
+ return false;
23
+ seen.add(w.name);
24
+ return true;
25
+ });
16
26
  return {
17
27
  apiKey: config.apiKey ?? "",
18
28
  endpoint: config.endpoint ?? "",
19
- events: config.events ?? [],
29
+ events: [...(config.events ?? [])].sort(),
30
+ functionWrappers: uniqueWrappers.sort((a, b) => a.name.localeCompare(b.name)),
20
31
  sync: {
21
32
  include: config.sync?.include ?? DEFAULT_INCLUDE,
22
33
  exclude: config.sync?.exclude ?? DEFAULT_EXCLUDE,
@@ -25,7 +36,7 @@ function normalizeConfig(config) {
25
36
  }
26
37
  async function loadConfig() {
27
38
  const configPath = path_1.default.join(process.cwd(), exports.CONFIG_NAME);
28
- if (!await fs_extra_1.default.pathExists(configPath)) {
39
+ if (!(await fs_extra_1.default.pathExists(configPath))) {
29
40
  return null;
30
41
  }
31
42
  try {
@@ -14,8 +14,9 @@ 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 analyzeFile_1 = require("../analysis/engine/analyzeFile");
17
18
  const wrapperRegistry_1 = require("../analysis/symbols/wrapperRegistry");
18
- const recursiveWrapperAnalyzer_1 = require("../analysis/engine/recursiveWrapperAnalyzer");
19
+ const processFile_1 = require("../filesystem/processFile");
19
20
  const EMPTY_RESULT = () => ({
20
21
  events: new Set(),
21
22
  detectedFunctionWrappers: new Set(),
@@ -29,11 +30,13 @@ class EventraEngine {
29
30
  returnPropagationCache = new returnPropagationCache_1.ReturnPropagationCache();
30
31
  resolvedExportCache = new resolvedExportCache_1.ResolvedExportCache();
31
32
  wrapperRegistry;
33
+ fileIndices = new Map();
32
34
  fileResults = new Map();
33
35
  normalizedPathCache = new Map();
34
36
  isPreloading = false;
35
37
  lastConfig = {
36
38
  events: [],
39
+ functionWrappers: [],
37
40
  sync: {
38
41
  include: [],
39
42
  exclude: [],
@@ -43,7 +46,7 @@ class EventraEngine {
43
46
  this.compiler = new compilerContext_1.CompilerContext(rootDir);
44
47
  this.wrapperRegistry = new wrapperRegistry_1.WrapperRegistry(this.compiler.getChecker(), this.resolvedExportCache);
45
48
  this.scheduler = new scheduler_1.Scheduler(async (updates) => {
46
- // APPLY UPDATES
49
+ const affected = new Set();
47
50
  for (const [file, content] of updates) {
48
51
  const existing = this.compiler.getSourceFile(file);
49
52
  if (existing) {
@@ -51,27 +54,17 @@ class EventraEngine {
51
54
  }
52
55
  await this.compiler.updateFile(file, content);
53
56
  this.updateImportGraph(file);
54
- }
55
- // COLLECT AFFECTED FILES
56
- const affected = new Set();
57
- for (const file of updates.keys()) {
58
- const dependents = this.importGraph.collectDependents(file);
59
- for (const dependent of dependents) {
57
+ affected.add(file);
58
+ for (const dependent of this.importGraph.collectDependents(file)) {
60
59
  affected.add(dependent);
61
60
  }
62
61
  }
63
- // RE-ANALYZE
64
- for (const file of affected) {
65
- await this.analyzeFile(file, this.lastConfig);
66
- }
62
+ await this.reanalyzeFiles([...affected], this.lastConfig);
67
63
  });
68
64
  }
69
65
  beginPreload() {
70
66
  this.isPreloading = true;
71
67
  }
72
- endPreload() {
73
- this.isPreloading = false;
74
- }
75
68
  normalize(fileName) {
76
69
  const cached = this.normalizedPathCache.get(fileName);
77
70
  if (cached) {
@@ -113,45 +106,92 @@ class EventraEngine {
113
106
  throw new Error("preload phase not active");
114
107
  }
115
108
  const normalized = this.normalize(fileName);
116
- await this.compiler.updateFile(normalized, content);
117
- this.updateImportGraph(normalized);
109
+ const processed = await (0, processFile_1.processFile)(normalized, content);
110
+ this.compiler.stageFile(processed.fileName, processed.content);
111
+ }
112
+ endPreload() {
113
+ this.isPreloading = false;
114
+ this.compiler.flushUpdates();
115
+ for (const sourceFile of this.compiler.getAllSourceFiles()) {
116
+ this.updateImportGraph(sourceFile.fileName);
117
+ }
118
118
  }
119
119
  async updateFile(fileName, content, config) {
120
120
  this.lastConfig = config;
121
121
  const normalized = this.normalize(fileName);
122
122
  await this.scheduler.enqueue(normalized, content);
123
123
  }
124
+ refreshChecker() {
125
+ this.wrapperRegistry.setChecker(this.compiler.getChecker());
126
+ }
127
+ async runFullAnalysis(fileNames, config) {
128
+ this.lastConfig = config;
129
+ this.refreshChecker();
130
+ const normalized = fileNames.map((f) => this.normalize(f));
131
+ for (const file of normalized) {
132
+ this.indexFile(file, config);
133
+ }
134
+ for (const file of normalized) {
135
+ this.extractFile(file, config);
136
+ }
137
+ }
138
+ async reanalyzeFiles(fileNames, config) {
139
+ this.lastConfig = config;
140
+ this.refreshChecker();
141
+ const normalized = fileNames.map((f) => this.normalize(f));
142
+ for (const file of normalized) {
143
+ this.indexFile(file, config);
144
+ }
145
+ for (const file of normalized) {
146
+ this.extractFile(file, config);
147
+ }
148
+ }
149
+ indexFile(fileName, _config) {
150
+ const normalized = this.normalize(fileName);
151
+ const source = this.compiler.getSourceFile(normalized);
152
+ if (!source) {
153
+ this.fileIndices.delete(normalized);
154
+ return;
155
+ }
156
+ const index = (0, analyzeFile_1.indexSourceFile)(source, this.compiler.getChecker(), this.wrapperRegistry);
157
+ this.fileIndices.set(normalized, index);
158
+ }
159
+ extractFile(fileName, config) {
160
+ const normalized = this.normalize(fileName);
161
+ const index = this.fileIndices.get(normalized);
162
+ if (!index) {
163
+ this.fileResults.delete(normalized);
164
+ return EMPTY_RESULT();
165
+ }
166
+ const result = (0, analyzeFile_1.extractFromIndex)(index, config, this.compiler.getChecker(), this.wrapperRegistry, this.evaluationCache, this.resolvedCallCache, this.returnPropagationCache, this.resolvedExportCache);
167
+ this.fileResults.set(normalized, result);
168
+ return result;
169
+ }
124
170
  async scanFile(fileName, config) {
125
171
  this.lastConfig = config;
126
- return await this.analyzeFile(fileName, config);
172
+ this.refreshChecker();
173
+ this.indexFile(fileName, config);
174
+ return this.extractFile(fileName, config);
127
175
  }
128
176
  async removeFile(fileName, config) {
129
177
  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
+ }
130
182
  const affected = this.importGraph.collectDependents(normalized);
131
183
  this.importGraph.removeFile(normalized);
132
184
  this.compiler.removeFile(normalized);
185
+ this.fileIndices.delete(normalized);
133
186
  this.fileResults.delete(normalized);
134
187
  this.normalizedPathCache.delete(fileName);
135
188
  this.normalizedPathCache.delete(normalized);
136
189
  const nextConfig = config ?? this.lastConfig;
137
- for (const file of affected) {
138
- if (file === normalized) {
139
- continue;
140
- }
141
- await this.analyzeFile(file, nextConfig);
190
+ const toReanalyze = [...affected].filter((f) => f !== normalized);
191
+ if (toReanalyze.length > 0) {
192
+ await this.reanalyzeFiles(toReanalyze, nextConfig);
142
193
  }
143
194
  }
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
- }
155
195
  getAllEvents() {
156
196
  const events = new Set();
157
197
  for (const result of this.fileResults.values()) {
@@ -159,9 +199,21 @@ class EventraEngine {
159
199
  events.add(event);
160
200
  }
161
201
  }
162
- return [
163
- ...events,
164
- ].sort();
202
+ return [...events].sort();
203
+ }
204
+ getAllFunctionWrappers() {
205
+ const wrappers = new Set();
206
+ for (const result of this.fileResults.values()) {
207
+ for (const name of result.detectedFunctionWrappers) {
208
+ if (name && name !== "anonymous" && !name.startsWith("__")) {
209
+ wrappers.add(name);
210
+ }
211
+ }
212
+ }
213
+ return [...wrappers].sort();
214
+ }
215
+ getScanResult(fileName) {
216
+ return this.fileResults.get(this.normalize(fileName)) ?? EMPTY_RESULT();
165
217
  }
166
218
  }
167
219
  exports.EventraEngine = EventraEngine;
@@ -7,9 +7,11 @@ exports.scanProject = scanProject;
7
7
  const fast_glob_1 = __importDefault(require("fast-glob"));
8
8
  const promises_1 = __importDefault(require("fs/promises"));
9
9
  const path_1 = __importDefault(require("path"));
10
+ const eventraSdk_1 = require("../analysis/sdk/eventraSdk");
10
11
  const EventraEngine_1 = require("./EventraEngine");
11
12
  const processFile_1 = require("../filesystem/processFile");
12
13
  const getVirtualFile_1 = require("../filesystem/getVirtualFile");
14
+ const SDK_TYPES_FILE = "__eventra_sdk_types__.d.ts";
13
15
  async function getParsedFile(file, cache) {
14
16
  const existing = cache.get(file);
15
17
  if (existing)
@@ -27,7 +29,6 @@ async function scanProject(config) {
27
29
  const engine = new EventraEngine_1.EventraEngine(process.cwd());
28
30
  const cache = new Map();
29
31
  const toScan = new Set();
30
- // COLLECT
31
32
  for (const file of files) {
32
33
  try {
33
34
  const raw = await promises_1.default.readFile(file, "utf8");
@@ -37,32 +38,32 @@ async function scanProject(config) {
37
38
  cache.set(file, parsed);
38
39
  toScan.add(file);
39
40
  for (const dep of parsed.dependencies) {
40
- toScan.add(path_1.default.resolve(path_1.default.dirname(file), dep));
41
+ if (dep.startsWith(".") || dep.startsWith("/")) {
42
+ toScan.add(path_1.default.resolve(path_1.default.dirname(file), dep));
43
+ }
41
44
  }
42
45
  }
43
- catch {
46
+ catch (err) {
47
+ console.error(`skip: ${file}`, err instanceof Error ? err.message : err);
44
48
  }
45
49
  }
46
- // PRELOAD
50
+ const virtualFiles = [SDK_TYPES_FILE, ...toScan].map((f) => (0, getVirtualFile_1.getVirtualFile)(f));
47
51
  engine.beginPreload();
52
+ await engine.preloadFile(SDK_TYPES_FILE, eventraSdk_1.EVENTRA_SDK_SHIM);
48
53
  for (const file of toScan) {
49
54
  try {
50
55
  const parsed = await getParsedFile(file, cache);
51
56
  await engine.preloadFile((0, getVirtualFile_1.getVirtualFile)(file), parsed.content);
52
57
  }
53
- catch {
58
+ catch (err) {
59
+ console.error(`preload skip: ${file}`, err instanceof Error ? err.message : err);
54
60
  }
55
61
  }
56
62
  engine.endPreload();
57
- // SCAN
63
+ await engine.runFullAnalysis(virtualFiles, config);
58
64
  const results = new Map();
59
65
  for (const file of toScan) {
60
- try {
61
- const result = await engine.scanFile((0, getVirtualFile_1.getVirtualFile)(file), config);
62
- results.set(file, result);
63
- }
64
- catch {
65
- }
66
+ results.set(file, engine.getScanResult((0, getVirtualFile_1.getVirtualFile)(file)));
66
67
  }
67
68
  return {
68
69
  engine,
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildConfigFromScan = buildConfigFromScan;
4
+ exports.persistScanResults = persistScanResults;
5
+ const config_1 = require("../config/config");
6
+ function buildConfigFromScan(config, engine) {
7
+ const functionWrappers = engine
8
+ .getAllFunctionWrappers()
9
+ .map((name) => ({ name }));
10
+ return {
11
+ ...config,
12
+ events: engine.getAllEvents(),
13
+ functionWrappers,
14
+ };
15
+ }
16
+ async function persistScanResults(config, engine) {
17
+ await (0, config_1.saveConfig)(buildConfigFromScan(config, engine));
18
+ }
package/dist/index.js CHANGED
@@ -33,13 +33,17 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  return result;
34
34
  };
35
35
  })();
36
+ var __importDefault = (this && this.__importDefault) || function (mod) {
37
+ return (mod && mod.__esModule) ? mod : { "default": mod };
38
+ };
36
39
  Object.defineProperty(exports, "__esModule", { value: true });
37
40
  const commander_1 = require("commander");
41
+ const package_json_1 = __importDefault(require("../package.json"));
38
42
  const program = new commander_1.Command();
39
43
  program
40
44
  .name("eventra")
41
45
  .description("Eventra CLI")
42
- .version("0.0.1");
46
+ .version(package_json_1.default.version);
43
47
  program
44
48
  .command("init")
45
49
  .action(async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventra_dev/eventra-cli",
3
- "version": "0.3.6",
3
+ "version": "0.3.10",
4
4
  "description": "Static analytics event extraction CLI powered by the TypeScript compiler API",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -25,7 +25,7 @@
25
25
  "scripts": {
26
26
  "build": "tsc -p tsconfig.json",
27
27
  "dev": "tsx src/index.ts",
28
- "test": "tsx tests/run.ts",
28
+ "test": "pnpm run clean && pnpm run build && tsx tests/run.ts",
29
29
  "clean": "rm -rf dist"
30
30
  },
31
31
  "dependencies": {
@@ -1,76 +0,0 @@
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
- }