@eventra_dev/eventra-cli 0.3.1 → 0.3.3

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 (35) hide show
  1. package/README.md +2 -13
  2. package/dist/analysis/cache/evaluationCache.js +1 -3
  3. package/dist/analysis/cache/resolvedCallCache.js +1 -3
  4. package/dist/analysis/cache/resolvedExportCache.js +1 -3
  5. package/dist/analysis/cache/returnPropagationCache.js +19 -0
  6. package/dist/analysis/cache/symbolInvalidation.js +8 -2
  7. package/dist/analysis/extractor/extractor.js +5 -13
  8. package/dist/analysis/extractor/propagationExtractor.js +55 -14
  9. package/dist/analysis/resolver/callResolver.js +10 -52
  10. package/dist/analysis/resolver/exportResolver.js +4 -19
  11. package/dist/analysis/resolver/resolver.js +148 -29
  12. package/dist/analysis/scanner/analyzer/propagationAnalyzer.js +19 -48
  13. package/dist/analysis/scanner/analyzer/returnPropagationAnalyzer.js +2 -13
  14. package/dist/analysis/scanner/scanner.js +16 -44
  15. package/dist/analysis/scanner/sinkDetector.js +10 -44
  16. package/dist/analysis/shared/evaluationContext.js +0 -1
  17. package/dist/analysis/symbols/symbolUtils.js +6 -29
  18. package/dist/analysis/symbols/wrapperRegistry.js +25 -25
  19. package/dist/cli/check.js +8 -10
  20. package/dist/cli/init.js +18 -82
  21. package/dist/cli/send.js +7 -5
  22. package/dist/cli/sync.js +4 -39
  23. package/dist/cli/watch.js +42 -40
  24. package/dist/compiler/compilerContext.js +18 -41
  25. package/dist/compiler/documentRegistry.js +5 -15
  26. package/dist/compiler/importGraph.js +4 -8
  27. package/dist/compiler/scheduler.js +1 -3
  28. package/dist/config/config.js +9 -19
  29. package/dist/core/EventraEngine.js +37 -101
  30. package/dist/core/projectScanner.js +7 -6
  31. package/dist/filesystem/processFile.js +1 -4
  32. package/dist/shared/buildSyntheticTsx.js +1 -2
  33. package/package.json +1 -1
  34. package/dist/analysis/cache/semanticDependencyGraph.js +0 -25
  35. package/dist/analysis/pipeline/semanticPass.js +0 -2
package/README.md CHANGED
@@ -9,17 +9,6 @@
9
9
  [![TypeScript](https://img.shields.io/badge/typescript-ready-blue.svg)](https://www.typescriptlang.org/)
10
10
  [![License](https://img.shields.io/npm/l/@eventra_dev/eventra-cli)]()
11
11
 
12
- <p align="center">
13
- <img src="./assets/eventra-icon-animated.svg" width="120">
14
- </p>
15
-
16
- # Eventra CLI
17
-
18
- [![npm version](https://img.shields.io/npm/v/@eventra_dev/eventra-cli.svg)](https://www.npmjs.com/package/@eventra_dev/eventra-cli)
19
- [![npm downloads](https://img.shields.io/npm/dm/@eventra_dev/eventra-cli.svg)](https://www.npmjs.com/package/@eventra_dev/eventra-cli)
20
- [![TypeScript](https://img.shields.io/badge/typescript-ready-blue.svg)](https://www.typescriptlang.org/)
21
- [![License](https://img.shields.io/npm/l/@eventra_dev/eventra-cli)]()
22
-
23
12
  Eventra CLI automatically discovers and tracks event usage across your codebase — even through abstractions like wrappers, propagation chains, variables, and cross-file calls.
24
13
 
25
14
  ---
@@ -28,7 +17,7 @@ Eventra CLI automatically discovers and tracks event usage across your codebase
28
17
 
29
18
  Eventra scans your JavaScript and TypeScript codebase using a TypeScript-powered semantic engine and extracts analytics event usage statically.
30
19
 
31
- It supports wrapper propagation, return propagation, cross-file resolution, and incremental dependency-aware analysis.
20
+ It supports semantic parameter propagation, return propagation, cross-file resolution, and incremental dependency-aware analysis.
32
21
 
33
22
  No runtime instrumentation required.
34
23
 
@@ -150,7 +139,7 @@ function trackFeature(name: string) {
150
139
  trackFeature("click")
151
140
  ```
152
141
 
153
- ##### Wrapper propagation
142
+ ##### Semantic parameter propagation
154
143
 
155
144
  ```ts
156
145
  function trackEvent(props: {
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
- // src/analysis/cache/evaluationCache.ts
3
2
  Object.defineProperty(exports, "__esModule", { value: true });
4
3
  exports.EvaluationCache = void 0;
5
4
  class EvaluationCache {
@@ -14,8 +13,7 @@ class EvaluationCache {
14
13
  this.cache.delete(symbol);
15
14
  }
16
15
  clear() {
17
- this.cache =
18
- new WeakMap();
16
+ this.cache = new WeakMap();
19
17
  }
20
18
  }
21
19
  exports.EvaluationCache = EvaluationCache;
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
- // src/analysis/cache/resolvedCallCache.ts
3
2
  Object.defineProperty(exports, "__esModule", { value: true });
4
3
  exports.ResolvedCallCache = void 0;
5
4
  class ResolvedCallCache {
@@ -14,8 +13,7 @@ class ResolvedCallCache {
14
13
  this.cache.delete(symbol);
15
14
  }
16
15
  clear() {
17
- this.cache =
18
- new WeakMap();
16
+ this.cache = new WeakMap();
19
17
  }
20
18
  }
21
19
  exports.ResolvedCallCache = ResolvedCallCache;
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
- // src/analysis/cache/resolvedExportCache.ts
3
2
  Object.defineProperty(exports, "__esModule", { value: true });
4
3
  exports.ResolvedExportCache = void 0;
5
4
  class ResolvedExportCache {
@@ -14,8 +13,7 @@ class ResolvedExportCache {
14
13
  this.cache.delete(symbol);
15
14
  }
16
15
  clear() {
17
- this.cache =
18
- new WeakMap();
16
+ this.cache = new WeakMap();
19
17
  }
20
18
  }
21
19
  exports.ResolvedExportCache = ResolvedExportCache;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ReturnPropagationCache = void 0;
4
+ class ReturnPropagationCache {
5
+ cache = new WeakMap();
6
+ get(declaration) {
7
+ return this.cache.get(declaration);
8
+ }
9
+ set(declaration, value) {
10
+ this.cache.set(declaration, value);
11
+ }
12
+ delete(declaration) {
13
+ this.cache.delete(declaration);
14
+ }
15
+ clear() {
16
+ this.cache = new WeakMap();
17
+ }
18
+ }
19
+ exports.ReturnPropagationCache = ReturnPropagationCache;
@@ -1,18 +1,24 @@
1
1
  "use strict";
2
- // src/analysis/cache/symbolInvalidation.ts
3
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
4
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
4
  };
6
5
  Object.defineProperty(exports, "__esModule", { value: true });
7
6
  exports.invalidateSourceFileSymbols = invalidateSourceFileSymbols;
8
7
  const typescript_1 = __importDefault(require("typescript"));
9
- function invalidateSourceFileSymbols(sourceFile, checker, evaluationCache, resolvedCallCache, resolvedExportCache) {
8
+ function isFunctionLike(node) {
9
+ return (typescript_1.default.isFunctionDeclaration(node) || typescript_1.default.isMethodDeclaration(node) || typescript_1.default.isArrowFunction(node) || typescript_1.default.isFunctionExpression(node));
10
+ }
11
+ function invalidateSourceFileSymbols(sourceFile, checker, evaluationCache, resolvedCallCache, resolvedExportCache, returnPropagationCache, wrapperRegistry) {
10
12
  function visit(node) {
11
13
  const symbol = checker.getSymbolAtLocation(node);
12
14
  if (symbol) {
13
15
  evaluationCache.delete(symbol);
14
16
  resolvedCallCache.delete(symbol);
15
17
  resolvedExportCache.delete(symbol);
18
+ wrapperRegistry.delete(symbol);
19
+ }
20
+ if (isFunctionLike(node)) {
21
+ returnPropagationCache.delete(node);
16
22
  }
17
23
  typescript_1.default.forEachChild(node, visit);
18
24
  }
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
- // src/analysis/extractor/extractor.ts
3
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
4
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
4
  };
@@ -10,18 +9,14 @@ const resolver_1 = require("../resolver/resolver");
10
9
  const evaluationContext_1 = require("../shared/evaluationContext");
11
10
  const propagationExtractor_1 = require("./propagationExtractor");
12
11
  function isValidEvent(value) {
13
- return (value.length > 0
14
- && value.length < 160
15
- && /^[a-zA-Z0-9:_./-]+$/.test(value));
12
+ return (value.length > 0 && value.length < 160 && /^[a-zA-Z0-9:_./-]+$/.test(value));
16
13
  }
17
- function extractEvents(index, checker, _config, evaluationCache, exportCache, resolvedCallCache, wrapperRegistry) {
14
+ function extractEvents(index, checker, _config, evaluationCache, exportCache, resolvedCallCache, returnPropagationCache, wrapperRegistry) {
18
15
  const events = new Set();
19
- //
20
16
  // direct track sinks
21
- //
22
17
  for (const call of index.trackCalls) {
23
18
  for (const argument of call.trackedArguments) {
24
- const resolved = (0, resolver_1.resolveNodeValue)(argument, checker, (0, evaluationContext_1.createEvaluationContext)(), new Set(), evaluationCache, resolvedCallCache, exportCache);
19
+ const resolved = (0, resolver_1.resolveNodeValue)(argument, checker, (0, evaluationContext_1.createEvaluationContext)(), new Set(), evaluationCache, resolvedCallCache, returnPropagationCache, exportCache);
25
20
  for (const value of resolved.values) {
26
21
  if (isValidEvent(value)) {
27
22
  events.add(value);
@@ -29,12 +24,10 @@ function extractEvents(index, checker, _config, evaluationCache, exportCache, re
29
24
  }
30
25
  }
31
26
  }
32
- //
33
- // wrapper propagation
34
- //
27
+ const propagationVisited = new Set();
35
28
  function visit(node) {
36
29
  if (typescript_1.default.isCallExpression(node)) {
37
- (0, propagationExtractor_1.extractPropagationEvents)(node, checker, wrapperRegistry, resolvedCallCache, evaluationCache, exportCache, events, new Set());
30
+ (0, propagationExtractor_1.extractPropagationEvents)(node, checker, wrapperRegistry, resolvedCallCache, returnPropagationCache, evaluationCache, exportCache, events, propagationVisited);
38
31
  }
39
32
  typescript_1.default.forEachChild(node, visit);
40
33
  }
@@ -42,6 +35,5 @@ function extractEvents(index, checker, _config, evaluationCache, exportCache, re
42
35
  return {
43
36
  events,
44
37
  detectedFunctionWrappers: new Set(index.wrappers.map(wrapper => wrapper.symbol.getName())),
45
- detectedComponentWrappers: new Map(),
46
38
  };
47
39
  }
@@ -1,21 +1,47 @@
1
1
  "use strict";
2
- // src/analysis/extractor/propagationExtractor.ts
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
3
5
  Object.defineProperty(exports, "__esModule", { value: true });
4
6
  exports.extractPropagationEvents = extractPropagationEvents;
7
+ const typescript_1 = __importDefault(require("typescript"));
5
8
  const callResolver_1 = require("../resolver/callResolver");
6
9
  const resolver_1 = require("../resolver/resolver");
7
10
  const evaluationContext_1 = require("../shared/evaluationContext");
8
11
  function isValidEvent(value) {
9
- return (value.length > 0
10
- && value.length < 160
11
- && /^[a-zA-Z0-9:_./-]+$/.test(value));
12
+ return (value.length > 0 && value.length < 160 && /^[a-zA-Z0-9:_./-]+$/.test(value));
12
13
  }
13
- function extractPropagationEvents(call, checker, wrapperRegistry, resolvedCallCache, evaluationCache, exportCache, events, visited) {
14
+ function resolveObjectPath(expression, path) {
15
+ let current = expression;
16
+ for (const segment of path) {
17
+ if (!typescript_1.default.isObjectLiteralExpression(current)) {
18
+ return null;
19
+ }
20
+ let matched = null;
21
+ for (const property of current.properties) {
22
+ if (!typescript_1.default.isPropertyAssignment(property) && !typescript_1.default.isShorthandPropertyAssignment(property)) {
23
+ continue;
24
+ }
25
+ const propertyName = typescript_1.default.isIdentifier(property.name) ? property.name.text : typescript_1.default.isStringLiteral(property.name) ? property.name.text : property.name.getText();
26
+ if (propertyName !== segment) {
27
+ continue;
28
+ }
29
+ matched = typescript_1.default.isShorthandPropertyAssignment(property) ? property.name : property.initializer;
30
+ break;
31
+ }
32
+ if (!matched) {
33
+ return null;
34
+ }
35
+ current = matched;
36
+ }
37
+ return current;
38
+ }
39
+ function extractPropagationEvents(call, checker, wrapperRegistry, resolvedCallCache, returnPropagationCache, evaluationCache, exportCache, events, visited) {
14
40
  const resolved = (0, callResolver_1.resolveFunctionFromCall)(call.expression, checker, resolvedCallCache, exportCache);
15
41
  if (!resolved) {
16
42
  return;
17
43
  }
18
- const semantic = wrapperRegistry.get(resolved, checker, exportCache);
44
+ const semantic = wrapperRegistry.get(resolved);
19
45
  if (!semantic) {
20
46
  return;
21
47
  }
@@ -27,23 +53,38 @@ function extractPropagationEvents(call, checker, wrapperRegistry, resolvedCallCa
27
53
  return;
28
54
  }
29
55
  visited.add(signature);
30
- for (const propagation of semantic.propagations) {
31
- extractPropagation(propagation, call, checker, evaluationCache, resolvedCallCache, exportCache, events);
56
+ try {
57
+ for (const propagation of semantic.propagations) {
58
+ extractPropagation(propagation, call, checker, evaluationCache, resolvedCallCache, returnPropagationCache, exportCache, events);
59
+ }
60
+ }
61
+ finally {
62
+ visited.delete(signature);
32
63
  }
33
64
  }
34
- function extractPropagation(propagation, call, checker, evaluationCache, resolvedCallCache, exportCache, events) {
35
- const argument = call.arguments[propagation.sourceParameterIndex];
65
+ function extractPropagation(propagation, call, checker, evaluationCache, resolvedCallCache, returnPropagationCache, exportCache, events) {
66
+ let argument = call.arguments[propagation.sourceParameterIndex];
36
67
  if (!argument) {
37
68
  return;
38
69
  }
70
+ // wrapper({
71
+ // nested: {
72
+ // event: "x"
73
+ // }
74
+ // })
75
+ if (propagation.propertyPath.length > 0) {
76
+ const resolvedPath = resolveObjectPath(argument, propagation.propertyPath);
77
+ if (!resolvedPath) {
78
+ return;
79
+ }
80
+ argument = resolvedPath;
81
+ }
39
82
  const context = (0, evaluationContext_1.createEvaluationContext)();
40
- const parameterSymbol = checker.getSymbolAtLocation(propagation
41
- .sourceParameter
42
- .name);
83
+ const parameterSymbol = checker.getSymbolAtLocation(propagation.sourceParameter.name);
43
84
  if (parameterSymbol) {
44
85
  context.parameterBindings.set(parameterSymbol, argument);
45
86
  }
46
- const resolved = (0, resolver_1.resolveNodeValue)(propagation.targetNode, checker, context, new Set(), evaluationCache, resolvedCallCache, exportCache);
87
+ const resolved = (0, resolver_1.resolveNodeValue)(propagation.targetNode, checker, context, new Set(), evaluationCache, resolvedCallCache, returnPropagationCache, exportCache);
47
88
  for (const value of resolved.values) {
48
89
  if (isValidEvent(value)) {
49
90
  events.add(value);
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
- // src/analysis/resolver/callResolver.ts
3
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
4
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
4
  };
@@ -8,59 +7,37 @@ exports.resolveFunctionFromCall = resolveFunctionFromCall;
8
7
  const typescript_1 = __importDefault(require("typescript"));
9
8
  const exportResolver_1 = require("./exportResolver");
10
9
  function isFunctionLike(node) {
11
- return (typescript_1.default.isFunctionDeclaration(node)
12
- || typescript_1.default.isMethodDeclaration(node)
13
- || typescript_1.default.isArrowFunction(node)
14
- || typescript_1.default.isFunctionExpression(node));
10
+ return (typescript_1.default.isFunctionDeclaration(node) || typescript_1.default.isMethodDeclaration(node) || typescript_1.default.isArrowFunction(node) || typescript_1.default.isFunctionExpression(node));
15
11
  }
16
12
  function resolveDeclaration(declaration) {
17
- //
18
13
  // function foo() {}
19
- //
20
14
  if (isFunctionLike(declaration)) {
21
15
  return declaration;
22
16
  }
23
- //
24
17
  // const foo = () => {}
25
- //
26
- if (typescript_1.default.isVariableDeclaration(declaration)
27
- && declaration.initializer
28
- && isFunctionLike(declaration.initializer)) {
18
+ if (typescript_1.default.isVariableDeclaration(declaration) && declaration.initializer && isFunctionLike(declaration.initializer)) {
29
19
  return declaration.initializer;
30
20
  }
31
- //
32
21
  // class field = () => {}
33
- //
34
- if (typescript_1.default.isPropertyDeclaration(declaration)
35
- && declaration.initializer
36
- && isFunctionLike(declaration.initializer)) {
22
+ if (typescript_1.default.isPropertyDeclaration(declaration) && declaration.initializer && isFunctionLike(declaration.initializer)) {
37
23
  return declaration.initializer;
38
24
  }
39
25
  return null;
40
26
  }
41
27
  function getExpressionSymbol(expression, checker) {
42
- //
43
28
  // analytics.track()
44
- //
45
29
  if (typescript_1.default.isPropertyAccessExpression(expression)) {
46
30
  return checker.getSymbolAtLocation(expression.name);
47
31
  }
48
- //
49
32
  // analytics?.track()
50
- //
51
33
  if (typescript_1.default.isPropertyAccessChain(expression)) {
52
34
  return checker.getSymbolAtLocation(expression.name);
53
35
  }
54
- //
55
36
  // analytics["track"]()
56
- //
57
- if (typescript_1.default.isElementAccessExpression(expression)
58
- && expression.argumentExpression) {
37
+ if (typescript_1.default.isElementAccessExpression(expression) && expression.argumentExpression) {
59
38
  return checker.getSymbolAtLocation(expression.argumentExpression);
60
39
  }
61
- //
62
40
  // track()
63
- //
64
41
  return checker.getSymbolAtLocation(expression);
65
42
  }
66
43
  function resolveFunctionFromCall(expression, checker, callCache, exportCache) {
@@ -68,32 +45,19 @@ function resolveFunctionFromCall(expression, checker, callCache, exportCache) {
68
45
  if (!symbol) {
69
46
  return null;
70
47
  }
71
- //
72
48
  // export unwrap
73
- //
74
- symbol =
75
- (0, exportResolver_1.resolveExportedSymbol)(symbol, checker, exportCache)
76
- ?? symbol;
77
- //
49
+ symbol = (0, exportResolver_1.resolveExportedSymbol)(symbol, checker, exportCache) ?? symbol;
78
50
  // alias unwrap
79
- //
80
- if (symbol.flags
81
- & typescript_1.default.SymbolFlags.Alias) {
82
- symbol =
83
- checker.getAliasedSymbol(symbol);
51
+ if (symbol.flags & typescript_1.default.SymbolFlags.Alias) {
52
+ symbol = checker.getAliasedSymbol(symbol);
84
53
  }
85
- //
86
54
  // cache
87
- //
88
55
  const cached = callCache.get(symbol);
89
56
  if (cached !== undefined) {
90
57
  return cached;
91
58
  }
92
- //
93
59
  // declaration resolution
94
- //
95
- const declarations = symbol.getDeclarations()
96
- ?? [];
60
+ const declarations = symbol.getDeclarations() ?? [];
97
61
  for (const declaration of declarations) {
98
62
  const resolved = resolveDeclaration(declaration);
99
63
  if (!resolved) {
@@ -102,24 +66,18 @@ function resolveFunctionFromCall(expression, checker, callCache, exportCache) {
102
66
  callCache.set(symbol, resolved);
103
67
  return resolved;
104
68
  }
105
- //
106
69
  // fallback:
107
70
  // type signatures
108
- //
109
- const type = checker.getTypeOfSymbolAtLocation(symbol, declarations[0]
110
- ?? expression);
71
+ const type = checker.getTypeOfSymbolAtLocation(symbol, declarations[0] ?? expression);
111
72
  const signatures = checker.getSignaturesOfType(type, typescript_1.default.SignatureKind.Call);
112
73
  for (const signature of signatures) {
113
74
  const declaration = signature.declaration;
114
- if (declaration
115
- && isFunctionLike(declaration)) {
75
+ if (declaration && isFunctionLike(declaration)) {
116
76
  callCache.set(symbol, declaration);
117
77
  return declaration;
118
78
  }
119
79
  }
120
- //
121
80
  // unresolved
122
- //
123
81
  callCache.set(symbol, null);
124
82
  return null;
125
83
  }
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
- // src/analysis/resolver/exportResolver.ts
3
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
4
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
4
  };
@@ -17,54 +16,40 @@ function isConcreteDeclaration(node) {
17
16
  || typescript_1.default.isPropertyDeclaration(node));
18
17
  }
19
18
  function resolveExportedSymbol(symbol, checker, cache, visited) {
20
- const seen = visited
21
- ?? new Set();
19
+ const seen = visited ?? new Set();
22
20
  const cached = cache.get(symbol);
23
21
  if (cached !== undefined) {
24
22
  return cached;
25
23
  }
26
- //
27
24
  // cycle guard
28
- //
29
25
  if (seen.has(symbol)) {
30
26
  cache.set(symbol, null);
31
27
  return null;
32
28
  }
33
29
  seen.add(symbol);
34
- //
35
30
  // alias unwrap
36
- //
37
- if (symbol.flags
38
- & typescript_1.default.SymbolFlags.Alias) {
31
+ if (symbol.flags & typescript_1.default.SymbolFlags.Alias) {
39
32
  const aliased = checker.getAliasedSymbol(symbol);
40
33
  const resolved = resolveExportedSymbol(aliased, checker, cache, seen);
41
34
  cache.set(symbol, resolved);
42
35
  return resolved;
43
36
  }
44
- //
45
37
  // export unwrap
46
- //
47
38
  const exportSymbol = checker.getExportSymbolOfSymbol(symbol);
48
- if (exportSymbol
49
- && exportSymbol !== symbol) {
39
+ if (exportSymbol && exportSymbol !== symbol) {
50
40
  const resolved = resolveExportedSymbol(exportSymbol, checker, cache, seen);
51
41
  cache.set(symbol, resolved);
52
42
  return resolved;
53
43
  }
54
- //
55
44
  // declaration existence
56
- //
57
- const declarations = symbol.getDeclarations()
58
- ?? [];
45
+ const declarations = symbol.getDeclarations() ?? [];
59
46
  for (const declaration of declarations) {
60
47
  if (isConcreteDeclaration(declaration)) {
61
48
  cache.set(symbol, symbol);
62
49
  return symbol;
63
50
  }
64
51
  }
65
- //
66
52
  // final validation
67
- //
68
53
  const valid = declarations.some(isConcreteDeclaration);
69
54
  if (valid) {
70
55
  cache.set(symbol, symbol);