@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
package/src/index.ts DELETED
@@ -1,49 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { Command } from "commander";
4
-
5
- const program = new Command();
6
-
7
- program
8
- .name("eventra")
9
- .description("Eventra CLI")
10
- .version("0.0.1");
11
-
12
- program
13
- .command("init")
14
- .action(async () => {
15
- const { init } = await import("./commands/init");
16
- await init();
17
- });
18
-
19
- program
20
- .command("sync")
21
- .action(async () => {
22
- const { sync } = await import("./commands/sync");
23
- await sync();
24
- });
25
-
26
- program
27
- .command("check")
28
- .option("--fix", "auto fix issues")
29
- .action(async (opts) => {
30
- const { check } = await import("./commands/check");
31
- await check({ fix: opts.fix });
32
- });
33
-
34
- program
35
- .command("watch")
36
- .description("Watch files and detect events in real-time")
37
- .action(async () => {
38
- const { watch } = await import("./commands/watch");
39
- await watch();
40
- });
41
-
42
- program
43
- .command("send")
44
- .action(async () => {
45
- const { send } = await import("./commands/send");
46
- await send();
47
- });
48
-
49
- program.parse();
package/src/types.ts DELETED
@@ -1,35 +0,0 @@
1
- export type ScanMode = "strict" | "hybrid" | "discovery";
2
-
3
- export type ScanResult = {
4
- events: Set<string>;
5
- detectedFunctionWrappers: Set<string>;
6
- detectedComponentWrappers: Map<string, string>;
7
- };
8
-
9
- export type ParseResult = {
10
- code: string;
11
- deps: string[];
12
- };
13
-
14
- export type ComponentWrapper = {
15
- name: string;
16
- prop: string;
17
- };
18
-
19
- export type FunctionWrapper = {
20
- name: string;
21
- event?: string;
22
- };
23
-
24
- export type EventraConfig = {
25
- apiKey?: string;
26
- endpoint?: string;
27
- events: string[];
28
- mode?: ScanMode;
29
- wrappers: ComponentWrapper[];
30
- functionWrappers: FunctionWrapper[];
31
- sync: {
32
- include: string[];
33
- exclude: string[];
34
- };
35
- };
@@ -1,53 +0,0 @@
1
- import fs from "fs-extra";
2
- import path from "path";
3
- import { EventraConfig } from "../types";
4
-
5
- export const CONFIG_NAME = "eventra.json";
6
-
7
- export function normalizeConfig(
8
- config: Partial<EventraConfig>
9
- ): EventraConfig {
10
- return {
11
- apiKey: config.apiKey ?? "",
12
- events: config.events ?? [],
13
- endpoint: config.endpoint ?? "",
14
- wrappers: config.wrappers ?? [],
15
- functionWrappers: config.functionWrappers ?? [],
16
- sync: config.sync ?? {
17
- include: [
18
- "**/*.{ts,tsx,js,jsx,vue,svelte,astro}"
19
- ],
20
- exclude: [
21
- "node_modules",
22
- "dist",
23
- ".next",
24
- ".git"
25
- ]
26
- }
27
- };
28
- }
29
-
30
- export async function loadConfig(): Promise<EventraConfig | null> {
31
- const configPath = path.join(process.cwd(), CONFIG_NAME);
32
-
33
- if (!(await fs.pathExists(configPath))) {
34
- return null;
35
- }
36
-
37
- try {
38
- const config = await fs.readJSON(configPath);
39
- return normalizeConfig(config);
40
- } catch {
41
- return null;
42
- }
43
- }
44
-
45
- export async function saveConfig(config: EventraConfig) {
46
- const configPath = path.join(process.cwd(), CONFIG_NAME);
47
-
48
- const normalized = normalizeConfig(config);
49
-
50
- await fs.writeJSON(configPath, normalized, {
51
- spaces: 2
52
- });
53
- }
@@ -1,5 +0,0 @@
1
- export function getVirtualFile(file: string) {
2
- return /\.(vue|svelte|astro|html)$/i.test(file)
3
- ? file + ".tsx"
4
- : file;
5
- }
@@ -1,29 +0,0 @@
1
- import path from "path";
2
- import { parseUniversal } from "./universal";
3
-
4
- function extractImports(content: string, file: string) {
5
- return parseUniversal(content, file).deps;
6
- }
7
-
8
- export function processFile(file: string, content: string) {
9
- const ext = path.extname(file);
10
-
11
- if (
12
- ext === ".ts" ||
13
- ext === ".tsx" ||
14
- ext === ".js" ||
15
- ext === ".jsx"
16
- ) {
17
- return {
18
- content,
19
- deps: extractImports(content, file),
20
- };
21
- }
22
-
23
- const parsed = parseUniversal(content, file);
24
-
25
- return {
26
- content: parsed.code || "",
27
- deps: [...new Set(parsed.deps.filter(Boolean))],
28
- };
29
- }
@@ -1,249 +0,0 @@
1
- import path from "path";
2
- import { ParseResult } from "../types";
3
-
4
- /* ---------------------------------- */
5
- /* COMMENT STRIP (SAFE) */
6
- /* ---------------------------------- */
7
- function stripComments(input: string): string {
8
- return input
9
- .replace(/<!--[\s\S]*?-->/g, "")
10
- .replace(/\/\*[\s\S]*?\*\//g, "");
11
- }
12
-
13
- /* ---------------------------------- */
14
- /* TRACK DETECTOR */
15
- /* ---------------------------------- */
16
- function looksLikeTracking(code: string) {
17
- return /\btrack\s*\(|\.track\s*\(/.test(code);
18
- }
19
-
20
- /* ---------------------------------- */
21
- /* SAFE {} PARSER (JSX / TEMPLATES) */
22
- /* ---------------------------------- */
23
- function extractBracedExpressions(input: string): string[] {
24
- const result: string[] = [];
25
-
26
- let depth = 0;
27
- let start = -1;
28
- let inString: string | null = null;
29
-
30
- for (let i = 0; i < input.length; i++) {
31
- const char = input[i];
32
-
33
- // handle strings
34
- if (inString) {
35
- if (char === inString && input[i - 1] !== "\\") {
36
- inString = null;
37
- }
38
- continue;
39
- }
40
-
41
- if (char === '"' || char === "'" || char === "`") {
42
- inString = char;
43
- continue;
44
- }
45
-
46
- if (char === "{") {
47
- if (depth === 0) start = i + 1;
48
- depth++;
49
- } else if (char === "}") {
50
- depth--;
51
- if (depth === 0 && start !== -1) {
52
- const expr = input.slice(start, i);
53
-
54
- if (looksLikeTracking(expr)) {
55
- result.push(expr);
56
- }
57
-
58
- start = -1;
59
- }
60
- }
61
- }
62
-
63
- return result;
64
- }
65
-
66
- /* ---------------------------------- */
67
- /* HTML HANDLERS */
68
- /* ---------------------------------- */
69
- function extractHTMLHandlers(input: string): string[] {
70
- const result: string[] = [];
71
-
72
- const regex = /\bon\w+\s*=\s*["']([^"']+)["']/g;
73
-
74
- for (const m of input.matchAll(regex)) {
75
- if (looksLikeTracking(m[1])) {
76
- result.push(m[1]);
77
- }
78
- }
79
-
80
- return result;
81
- }
82
-
83
- /* ---------------------------------- */
84
- /* VUE HANDLERS */
85
- /* ---------------------------------- */
86
- function extractVueHandlers(input: string): string[] {
87
- const result: string[] = [];
88
-
89
- const regex = /(?:@|v-on:)[\w:-]+\s*=\s*["']([^"']+)["']/g;
90
-
91
- for (const m of input.matchAll(regex)) {
92
- if (looksLikeTracking(m[1])) {
93
- result.push(m[1]);
94
- }
95
- }
96
-
97
- return result;
98
- }
99
-
100
- /* ---------------------------------- */
101
- /* SVELTE HANDLERS */
102
- /* ---------------------------------- */
103
- function extractSvelteHandlers(input: string): string[] {
104
- const result: string[] = [];
105
-
106
- const regex = /\bon:[\w]+\s*=\s*{([\s\S]*?)}/g;
107
-
108
- for (const m of input.matchAll(regex)) {
109
- const expr = m[1];
110
-
111
- const nested = extractBracedExpressions(`{${expr}}`);
112
- result.push(...nested);
113
- }
114
-
115
- return result;
116
- }
117
-
118
- /* ---------------------------------- */
119
- /* SCRIPT EXTRACTION */
120
- /* ---------------------------------- */
121
- function extractScripts(input: string, file: string) {
122
- const scripts: string[] = [];
123
- const deps: string[] = [];
124
-
125
- // <script src="">
126
- const srcRegex =
127
- /<script\b[^>]*?\bsrc=["'](.+?)["'][^>]*>/gi;
128
-
129
- for (const m of input.matchAll(srcRegex)) {
130
- const src = m[1];
131
-
132
- const resolved = src.startsWith("/")
133
- ? path.resolve(process.cwd(), "." + src)
134
- : path.resolve(path.dirname(file), src);
135
-
136
- deps.push(resolved);
137
- }
138
-
139
- // inline <script>
140
- const scriptRegex =
141
- /<script\b[^>]*>([\s\S]*?)<\/script>/gi;
142
-
143
- for (const m of input.matchAll(scriptRegex)) {
144
- if (m[1]?.trim()) {
145
- scripts.push(m[1]);
146
- }
147
- }
148
-
149
- const noScripts = input.replace(scriptRegex, "");
150
-
151
- return { scripts, deps, noScripts };
152
- }
153
-
154
- /* ---------------------------------- */
155
- /* STATIC IMPORTS */
156
- /* ---------------------------------- */
157
- function extractStaticImports(input: string, file: string) {
158
- const deps: string[] = [];
159
-
160
- const regex =
161
- /import\s+(?:.+?\s+from\s+)?["'](.+?)["']/g;
162
-
163
- for (const m of input.matchAll(regex)) {
164
- const mod = m[1];
165
-
166
- if (!mod.startsWith(".") && !mod.startsWith("/")) continue;
167
-
168
- const resolved = path.resolve(
169
- path.dirname(file),
170
- mod
171
- );
172
-
173
- deps.push(resolved);
174
- }
175
-
176
- return deps;
177
- }
178
-
179
- /* ---------------------------------- */
180
- /* DYNAMIC IMPORTS */
181
- /* ---------------------------------- */
182
- function extractDynamicImports(input: string, file: string) {
183
- const deps: string[] = [];
184
- const calls: string[] = [];
185
-
186
- const regex = /import\(\s*["'](.+?)["']\s*\)/g;
187
-
188
- for (const m of input.matchAll(regex)) {
189
- const mod = m[1];
190
-
191
- const resolved = mod.startsWith("/")
192
- ? path.resolve(process.cwd(), "." + mod)
193
- : path.resolve(path.dirname(file), mod);
194
-
195
- deps.push(resolved);
196
-
197
- if (looksLikeTracking(mod)) {
198
- calls.push(`import("${mod}")`);
199
- }
200
- }
201
-
202
- return { deps, calls };
203
- }
204
-
205
- /* ---------------------------------- */
206
- /* MAIN */
207
- /* ---------------------------------- */
208
- export function parseUniversal(
209
- content: string,
210
- file: string
211
- ): ParseResult {
212
- const parts: string[] = [];
213
- const deps: string[] = [];
214
-
215
- const clean = stripComments(content);
216
-
217
- /* ---------- ASTRO FRONTMATTER ---------- */
218
- const fm = clean.match(/^\s*---([\s\S]*?)---/);
219
- if (fm?.[1]) {
220
- parts.push(fm[1]);
221
- }
222
-
223
- /* ---------- SCRIPTS ---------- */
224
- const { scripts, deps: scriptDeps, noScripts } =
225
- extractScripts(clean, file);
226
-
227
- parts.push(...scripts);
228
- deps.push(...scriptDeps);
229
-
230
- /* ---------- HANDLERS ---------- */
231
- parts.push(...extractHTMLHandlers(noScripts));
232
- parts.push(...extractVueHandlers(noScripts));
233
- parts.push(...extractSvelteHandlers(noScripts));
234
-
235
- /* ---------- JSX / TEMPLATE ---------- */
236
- parts.push(...extractBracedExpressions(clean));
237
-
238
- /* ---------- IMPORTS ---------- */
239
- deps.push(...extractStaticImports(clean, file));
240
-
241
- const dyn = extractDynamicImports(clean, file);
242
- deps.push(...dyn.deps);
243
- parts.push(...dyn.calls);
244
-
245
- return {
246
- code: parts.length ? parts.join("\n") : content,
247
- deps: [...new Set(deps.filter(Boolean))],
248
- };
249
- }
@@ -1,233 +0,0 @@
1
- /* eslint-disable */
2
-
3
- declare function track(event: string): void;
4
-
5
- const express = (...args: any[]) => ({
6
- get: (...args: any[]) => {},
7
- post: (...args: any[]) => {},
8
- put: (...args: any[]) => {},
9
- delete: (...args: any[]) => {},
10
- use: (...args: any[]) => {},
11
- listen: (...args: any[]) => {}
12
- });
13
-
14
- const app = express();
15
-
16
- // =========================
17
- // BASIC
18
- // =========================
19
-
20
- track("express_event");
21
-
22
- // =========================
23
- // VARIABLES
24
- // =========================
25
-
26
- const eventName = "variable_event";
27
- track(eventName);
28
-
29
- let anotherEvent;
30
- anotherEvent = "assigned_event";
31
- track(anotherEvent);
32
-
33
- // =========================
34
- // TEMPLATE
35
- // =========================
36
-
37
- track(`template_event`);
38
-
39
- const id = "123";
40
- track(`template_${id}`); // dynamic
41
-
42
- // =========================
43
- // MIDDLEWARE
44
- // =========================
45
-
46
- app.use(() => {
47
- track("middleware_event");
48
- });
49
-
50
- // =========================
51
- // ROUTES
52
- // =========================
53
-
54
- app.get("/", () => {
55
- track("get_event");
56
- });
57
-
58
- app.post("/users", () => {
59
- track("post_event");
60
- });
61
-
62
- app.put("/users", () => {
63
- track("put_event");
64
- });
65
-
66
- app.delete("/users", () => {
67
- track("delete_event");
68
- });
69
-
70
- // =========================
71
- // NESTED
72
- // =========================
73
-
74
- app.get("/nested", () => {
75
- if (true) {
76
- track("nested_event");
77
- }
78
- });
79
-
80
- // =========================
81
- // ASYNC
82
- // =========================
83
-
84
- app.get("/async", async () => {
85
- track("async_event");
86
- });
87
-
88
- // =========================
89
- // ARROW HANDLER
90
- // =========================
91
-
92
- const handler = () => {
93
- track("arrow_event");
94
- };
95
-
96
- app.get("/arrow", handler);
97
-
98
- // =========================
99
- // FUNCTION DECLARATION
100
- // =========================
101
-
102
- function service() {
103
- track("function_event");
104
- }
105
-
106
- // =========================
107
- // CLASS
108
- // =========================
109
-
110
- class Service {
111
- run() {
112
- track("class_event");
113
- }
114
- }
115
-
116
- // =========================
117
- // LISTEN
118
- // =========================
119
-
120
- app.listen(3000, () => {
121
- track("listen_event");
122
- });
123
-
124
- // =========================
125
- // OBJECT WRAPPER
126
- // =========================
127
-
128
- const analytics = {
129
- track
130
- };
131
-
132
- analytics.track("object_event");
133
-
134
- // nested object
135
- const analyticsDeep = {
136
- events: {
137
- track
138
- }
139
- };
140
-
141
- analyticsDeep.events.track("nested_object_event");
142
-
143
- // =========================
144
- // CONDITIONALS
145
- // =========================
146
-
147
- if (true) {
148
- track("if_event");
149
- } else {
150
- track("else_event");
151
- }
152
-
153
- // ternary
154
- true
155
- ? track("ternary_true")
156
- : track("ternary_false");
157
-
158
- // =========================
159
- // ARRAYS
160
- // =========================
161
-
162
- [
163
- () => track("array_1"),
164
- () => track("array_2")
165
- ];
166
-
167
- // =========================
168
- // LOOP
169
- // =========================
170
-
171
- for (let i = 0; i < 1; i++) {
172
- track("loop_event");
173
- }
174
-
175
- // =========================
176
- // TRY / CATCH
177
- // =========================
178
-
179
- try {
180
- track("try_event");
181
- } catch {
182
- track("catch_event");
183
- }
184
-
185
- // =========================
186
- // RETURN
187
- // =========================
188
-
189
- function returnTest() {
190
- return track("return_event");
191
- }
192
-
193
- // =========================
194
- // PARAM DEFAULT
195
- // =========================
196
-
197
- function withDefault(e = "default_event") {
198
- track(e);
199
- }
200
-
201
- // =========================
202
- // INLINE OBJECT
203
- // =========================
204
-
205
- track({
206
- name: "object_payload_event"
207
- } as any);
208
-
209
- // =========================
210
- // OPTIONAL CHAINING
211
- // =========================
212
-
213
- analytics?.track?.("optional_chain_event");
214
-
215
- // =========================
216
- // EDGE CASES
217
- // =========================
218
-
219
- // not first argument (should be ignored usually)
220
- // @ts-ignore
221
- track("real_event", "extra");
222
-
223
- // no args
224
- // @ts-ignore
225
- track();
226
-
227
- // dynamic identifier (should be dynamic)
228
- const dynamicEvent = Math.random() > 0.5 ? "a" : "b";
229
- track(dynamicEvent);
230
-
231
- // =========================
232
- // END
233
- // =========================