@aiready/core 0.24.14 → 0.24.15
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/chunk-OT6FOHL4.mjs +304 -0
- package/dist/index.js +19 -4
- package/dist/index.mjs +21 -6
- package/dist/typescript-parser-RMNCTHRD.mjs +7 -0
- package/package.json +1 -1
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ParseError
|
|
3
|
+
} from "./chunk-U3IY2CFC.mjs";
|
|
4
|
+
|
|
5
|
+
// src/parsers/typescript-parser.ts
|
|
6
|
+
import { parse } from "@typescript-eslint/typescript-estree";
|
|
7
|
+
var TypeScriptParser = class {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.language = "typescript" /* TypeScript */;
|
|
10
|
+
this.extensions = [".ts", ".tsx", ".js", ".jsx"];
|
|
11
|
+
}
|
|
12
|
+
async initialize() {
|
|
13
|
+
}
|
|
14
|
+
canHandle(filePath) {
|
|
15
|
+
return this.extensions.some((ext) => filePath.endsWith(ext));
|
|
16
|
+
}
|
|
17
|
+
async getAST(code, filePath) {
|
|
18
|
+
try {
|
|
19
|
+
return parse(code, {
|
|
20
|
+
filePath,
|
|
21
|
+
loc: true,
|
|
22
|
+
range: true,
|
|
23
|
+
tokens: true,
|
|
24
|
+
comment: true,
|
|
25
|
+
jsx: filePath.endsWith("x")
|
|
26
|
+
});
|
|
27
|
+
} catch (error) {
|
|
28
|
+
const err = error;
|
|
29
|
+
throw new ParseError(err.message || "Unknown error", filePath, {
|
|
30
|
+
line: err.lineNumber || 1,
|
|
31
|
+
column: err.column || 0
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
parse(code, filePath) {
|
|
36
|
+
try {
|
|
37
|
+
const ast = parse(code, {
|
|
38
|
+
filePath,
|
|
39
|
+
loc: true,
|
|
40
|
+
range: true,
|
|
41
|
+
tokens: true,
|
|
42
|
+
comment: true,
|
|
43
|
+
jsx: filePath.endsWith("x")
|
|
44
|
+
});
|
|
45
|
+
const imports = this.extractImports(ast);
|
|
46
|
+
const exports = this.extractExports(ast, code, filePath);
|
|
47
|
+
return {
|
|
48
|
+
exports,
|
|
49
|
+
imports,
|
|
50
|
+
language: this.language
|
|
51
|
+
};
|
|
52
|
+
} catch (error) {
|
|
53
|
+
throw new ParseError(error.message, filePath, {
|
|
54
|
+
line: error.lineNumber || 1,
|
|
55
|
+
column: error.column || 0
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
getNamingConventions() {
|
|
60
|
+
return {
|
|
61
|
+
variablePattern: /^[a-z][a-zA-Z0-9]*$/,
|
|
62
|
+
functionPattern: /^[a-z][a-zA-Z0-9]*$/,
|
|
63
|
+
classPattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
64
|
+
constantPattern: /^[A-Z][A-Z0-9_]*$/,
|
|
65
|
+
typePattern: /^[A-Z][a-zA-Z0-9]*$/,
|
|
66
|
+
interfacePattern: /^I?[A-Z][a-zA-Z0-9]*$/
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
analyzeMetadata(node, code) {
|
|
70
|
+
if (!code) return {};
|
|
71
|
+
return {
|
|
72
|
+
isPure: this.isLikelyPure(node),
|
|
73
|
+
hasSideEffects: !this.isLikelyPure(node)
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
extractImports(ast) {
|
|
77
|
+
const imports = [];
|
|
78
|
+
for (const node of ast.body) {
|
|
79
|
+
if (node.type === "ImportDeclaration") {
|
|
80
|
+
const specifiers = [];
|
|
81
|
+
let isTypeOnly = false;
|
|
82
|
+
if (node.importKind === "type") {
|
|
83
|
+
isTypeOnly = true;
|
|
84
|
+
}
|
|
85
|
+
for (const spec of node.specifiers) {
|
|
86
|
+
if (spec.type === "ImportSpecifier") {
|
|
87
|
+
const imported = spec.imported;
|
|
88
|
+
const name = imported.type === "Identifier" ? imported.name : imported.value;
|
|
89
|
+
specifiers.push(name);
|
|
90
|
+
} else if (spec.type === "ImportDefaultSpecifier") {
|
|
91
|
+
specifiers.push("default");
|
|
92
|
+
} else if (spec.type === "ImportNamespaceSpecifier") {
|
|
93
|
+
specifiers.push("*");
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
imports.push({
|
|
97
|
+
source: node.source.value,
|
|
98
|
+
specifiers,
|
|
99
|
+
isTypeOnly,
|
|
100
|
+
loc: node.loc ? {
|
|
101
|
+
start: {
|
|
102
|
+
line: node.loc.start.line,
|
|
103
|
+
column: node.loc.start.column
|
|
104
|
+
},
|
|
105
|
+
end: { line: node.loc.end.line, column: node.loc.end.column }
|
|
106
|
+
} : void 0
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return imports;
|
|
111
|
+
}
|
|
112
|
+
extractExports(ast, code, filePath) {
|
|
113
|
+
const exports = [];
|
|
114
|
+
for (const node of ast.body) {
|
|
115
|
+
if (node.type === "ExportNamedDeclaration") {
|
|
116
|
+
if (node.declaration) {
|
|
117
|
+
const declaration = node.declaration;
|
|
118
|
+
if ((declaration.type === "FunctionDeclaration" || declaration.type === "TSDeclareFunction") && declaration.id) {
|
|
119
|
+
exports.push(
|
|
120
|
+
this.createExport(
|
|
121
|
+
declaration.id.name,
|
|
122
|
+
"function",
|
|
123
|
+
node,
|
|
124
|
+
// Pass the outer ExportNamedDeclaration
|
|
125
|
+
code,
|
|
126
|
+
filePath
|
|
127
|
+
)
|
|
128
|
+
);
|
|
129
|
+
} else if (declaration.type === "ClassDeclaration" && declaration.id) {
|
|
130
|
+
exports.push(
|
|
131
|
+
this.createExport(
|
|
132
|
+
declaration.id.name,
|
|
133
|
+
"class",
|
|
134
|
+
node,
|
|
135
|
+
// Pass the outer ExportNamedDeclaration
|
|
136
|
+
code,
|
|
137
|
+
filePath
|
|
138
|
+
)
|
|
139
|
+
);
|
|
140
|
+
} else if (declaration.type === "TSTypeAliasDeclaration") {
|
|
141
|
+
exports.push(
|
|
142
|
+
this.createExport(
|
|
143
|
+
declaration.id.name,
|
|
144
|
+
"type",
|
|
145
|
+
node,
|
|
146
|
+
// Pass the outer ExportNamedDeclaration
|
|
147
|
+
code,
|
|
148
|
+
filePath
|
|
149
|
+
)
|
|
150
|
+
);
|
|
151
|
+
} else if (declaration.type === "TSInterfaceDeclaration") {
|
|
152
|
+
exports.push(
|
|
153
|
+
this.createExport(
|
|
154
|
+
declaration.id.name,
|
|
155
|
+
"interface",
|
|
156
|
+
node,
|
|
157
|
+
// Pass the outer ExportNamedDeclaration
|
|
158
|
+
code,
|
|
159
|
+
filePath
|
|
160
|
+
)
|
|
161
|
+
);
|
|
162
|
+
} else if (declaration.type === "VariableDeclaration") {
|
|
163
|
+
for (const decl of declaration.declarations) {
|
|
164
|
+
if (decl.id.type === "Identifier") {
|
|
165
|
+
exports.push(
|
|
166
|
+
this.createExport(
|
|
167
|
+
decl.id.name,
|
|
168
|
+
"const",
|
|
169
|
+
node,
|
|
170
|
+
code,
|
|
171
|
+
filePath,
|
|
172
|
+
decl.init
|
|
173
|
+
)
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
} else if (node.type === "ExportDefaultDeclaration") {
|
|
180
|
+
exports.push(
|
|
181
|
+
this.createExport("default", "default", node, code, filePath)
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return exports;
|
|
186
|
+
}
|
|
187
|
+
createExport(name, type, node, code, filePath, initializer) {
|
|
188
|
+
const documentation = this.extractDocumentation(node, code);
|
|
189
|
+
let methodCount;
|
|
190
|
+
let propertyCount;
|
|
191
|
+
let parameters;
|
|
192
|
+
let isPrimitive = false;
|
|
193
|
+
let isTyped = false;
|
|
194
|
+
if (initializer) {
|
|
195
|
+
if (initializer.type === "Literal" || initializer.type === "TemplateLiteral" && initializer.expressions.length === 0) {
|
|
196
|
+
isPrimitive = true;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
let structNode = node.type === "ExportNamedDeclaration" && node.declaration ? node.declaration : node.type === "ExportDefaultDeclaration" ? node.declaration : node;
|
|
200
|
+
if (!structNode) structNode = node;
|
|
201
|
+
if (filePath.endsWith(".ts") || filePath.endsWith(".tsx")) {
|
|
202
|
+
if (structNode.type === "TSTypeAliasDeclaration" || structNode.type === "TSInterfaceDeclaration" || structNode.type === "TSEnumDeclaration") {
|
|
203
|
+
isTyped = true;
|
|
204
|
+
} else if (structNode.type === "FunctionDeclaration" || structNode.type === "TSDeclareFunction") {
|
|
205
|
+
const func = structNode;
|
|
206
|
+
const hasReturnType = !!func.returnType;
|
|
207
|
+
const allParamsTyped = func.params.length === 0 || func.params.every((p) => !!p.typeAnnotation);
|
|
208
|
+
isTyped = hasReturnType && allParamsTyped;
|
|
209
|
+
} else if (structNode.type === "VariableDeclaration") {
|
|
210
|
+
const variable = structNode;
|
|
211
|
+
isTyped = variable.declarations.every(
|
|
212
|
+
(d) => !!d.id.typeAnnotation || !!d.init
|
|
213
|
+
);
|
|
214
|
+
} else if (structNode.type === "ClassDeclaration") {
|
|
215
|
+
isTyped = true;
|
|
216
|
+
}
|
|
217
|
+
} else if (filePath.endsWith(".js") || filePath.endsWith(".jsx")) {
|
|
218
|
+
isTyped = false;
|
|
219
|
+
}
|
|
220
|
+
if (structNode.type === "ClassDeclaration" || structNode.type === "TSInterfaceDeclaration") {
|
|
221
|
+
const body = structNode.type === "ClassDeclaration" ? structNode.body.body : structNode.body.body;
|
|
222
|
+
methodCount = body.filter(
|
|
223
|
+
(m) => m.type === "MethodDefinition" || m.type === "TSMethodSignature"
|
|
224
|
+
).length;
|
|
225
|
+
propertyCount = body.filter(
|
|
226
|
+
(m) => m.type === "PropertyDefinition" || m.type === "TSPropertySignature"
|
|
227
|
+
).length;
|
|
228
|
+
if (structNode.type === "ClassDeclaration") {
|
|
229
|
+
const constructor = body.find(
|
|
230
|
+
(m) => m.type === "MethodDefinition" && m.kind === "constructor"
|
|
231
|
+
);
|
|
232
|
+
if (constructor && constructor.value && constructor.value.params) {
|
|
233
|
+
parameters = constructor.value.params.map((p) => {
|
|
234
|
+
if (p.type === "Identifier") return p.name;
|
|
235
|
+
if (p.type === "TSParameterProperty" && p.parameter.type === "Identifier") {
|
|
236
|
+
return p.parameter.name;
|
|
237
|
+
}
|
|
238
|
+
return void 0;
|
|
239
|
+
}).filter((p) => !!p);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
if (!parameters && (structNode.type === "FunctionDeclaration" || structNode.type === "TSDeclareFunction" || structNode.type === "MethodDefinition")) {
|
|
244
|
+
const funcNode = structNode.type === "MethodDefinition" ? structNode.value : structNode;
|
|
245
|
+
if (funcNode && funcNode.params) {
|
|
246
|
+
parameters = funcNode.params.map((p) => {
|
|
247
|
+
if (p.type === "Identifier") return p.name;
|
|
248
|
+
return void 0;
|
|
249
|
+
}).filter((p) => !!p);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return {
|
|
253
|
+
name,
|
|
254
|
+
type,
|
|
255
|
+
isPrimitive,
|
|
256
|
+
loc: node.loc ? {
|
|
257
|
+
start: { line: node.loc.start.line, column: node.loc.start.column },
|
|
258
|
+
end: { line: node.loc.end.line, column: node.loc.end.column }
|
|
259
|
+
} : void 0,
|
|
260
|
+
documentation,
|
|
261
|
+
methodCount,
|
|
262
|
+
propertyCount,
|
|
263
|
+
parameters,
|
|
264
|
+
isPure: this.isLikelyPure(node),
|
|
265
|
+
hasSideEffects: !this.isLikelyPure(node),
|
|
266
|
+
isTyped
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
extractDocumentation(node, code) {
|
|
270
|
+
if (node.range) {
|
|
271
|
+
const start = node.range[0];
|
|
272
|
+
const precedingCode = code.substring(0, start);
|
|
273
|
+
const jsdocMatch = precedingCode.match(/\/\*\*([\s\S]*?)\*\/\s*$/);
|
|
274
|
+
if (jsdocMatch) {
|
|
275
|
+
return {
|
|
276
|
+
content: jsdocMatch[1].trim(),
|
|
277
|
+
type: "jsdoc"
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return void 0;
|
|
282
|
+
}
|
|
283
|
+
isLikelyPure(node) {
|
|
284
|
+
const sn = node.type === "ExportNamedDeclaration" && node.declaration ? node.declaration : node.type === "ExportDefaultDeclaration" ? node.declaration : node;
|
|
285
|
+
if (!sn) return false;
|
|
286
|
+
if (sn.type === "VariableDeclaration" && sn.kind === "const") return true;
|
|
287
|
+
if (sn.type === "FunctionDeclaration" || sn.type === "MethodDefinition") {
|
|
288
|
+
const body = sn.type === "MethodDefinition" ? sn.value.body : sn.body;
|
|
289
|
+
if (body && body.type === "BlockStatement") {
|
|
290
|
+
const bodyContent = JSON.stringify(body);
|
|
291
|
+
if (bodyContent.includes('"name":"console"') || bodyContent.includes('"name":"process"') || bodyContent.includes('"name":"fs"') || bodyContent.includes('"name":"fetch"') || bodyContent.includes('"name":"axios"')) {
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
export {
|
|
303
|
+
TypeScriptParser
|
|
304
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -362,7 +362,7 @@ var init_typescript_parser = __esm({
|
|
|
362
362
|
const body = sn.type === "MethodDefinition" ? sn.value.body : sn.body;
|
|
363
363
|
if (body && body.type === "BlockStatement") {
|
|
364
364
|
const bodyContent = JSON.stringify(body);
|
|
365
|
-
if (bodyContent.includes('"name":"console"') || bodyContent.includes('"name":"process"') || bodyContent.includes('"
|
|
365
|
+
if (bodyContent.includes('"name":"console"') || bodyContent.includes('"name":"process"') || bodyContent.includes('"name":"fs"') || bodyContent.includes('"name":"fetch"') || bodyContent.includes('"name":"axios"')) {
|
|
366
366
|
return false;
|
|
367
367
|
}
|
|
368
368
|
return true;
|
|
@@ -4834,7 +4834,7 @@ function inferPatternType(keyword, name) {
|
|
|
4834
4834
|
if (n.includes("validate") || n.includes("schema")) return "validator";
|
|
4835
4835
|
if (n.includes("util") || n.includes("helper")) return "utility";
|
|
4836
4836
|
if (keyword === "class") return "class-method";
|
|
4837
|
-
if (
|
|
4837
|
+
if (name.match(/^[A-Z]/)) return "component";
|
|
4838
4838
|
if (keyword === "function" || keyword === "def") return "function";
|
|
4839
4839
|
return "unknown";
|
|
4840
4840
|
}
|
|
@@ -4900,7 +4900,14 @@ function extractCodeBlocks(file, content) {
|
|
|
4900
4900
|
patternType: inferPatternType(type, name)
|
|
4901
4901
|
});
|
|
4902
4902
|
}
|
|
4903
|
-
return blocks
|
|
4903
|
+
return blocks.filter((block) => {
|
|
4904
|
+
const code = block.code.trim();
|
|
4905
|
+
if (/^export\s+\{[^}]+\}\s*(from\s+['"][^'"]+['"])?\s*;?\s*$/.test(code))
|
|
4906
|
+
return false;
|
|
4907
|
+
if (/^export\s+\*\s+from\s+/.test(code)) return false;
|
|
4908
|
+
if (/^export\s+\*\s+as\s+\w+\s+from\s+/.test(code)) return false;
|
|
4909
|
+
return true;
|
|
4910
|
+
});
|
|
4904
4911
|
}
|
|
4905
4912
|
function extractBlocksPython(file, content) {
|
|
4906
4913
|
const blocks = [];
|
|
@@ -6332,7 +6339,15 @@ function calculateChangeAmplification(params) {
|
|
|
6332
6339
|
recommendations: []
|
|
6333
6340
|
};
|
|
6334
6341
|
}
|
|
6335
|
-
const hotspots = files.map((f) =>
|
|
6342
|
+
const hotspots = files.map((f) => {
|
|
6343
|
+
const lowerFile = f.file.toLowerCase();
|
|
6344
|
+
const isSharedInfra = lowerFile.endsWith("logger.ts") || lowerFile.endsWith("logger.js") || lowerFile.endsWith("constants.ts") || lowerFile.endsWith("constants.js") || lowerFile.endsWith("types.ts") || lowerFile.endsWith("types.js") || lowerFile.endsWith("index.ts") || lowerFile.endsWith("index.js");
|
|
6345
|
+
const fanInWeight = isSharedInfra ? 0.1 : 0.5;
|
|
6346
|
+
return {
|
|
6347
|
+
...f,
|
|
6348
|
+
amplificationFactor: f.fanOut + f.fanIn * fanInWeight
|
|
6349
|
+
};
|
|
6350
|
+
}).sort((a, b) => b.amplificationFactor - a.amplificationFactor);
|
|
6336
6351
|
const maxAmplification = hotspots[0].amplificationFactor;
|
|
6337
6352
|
const avgAmplification = hotspots.reduce((sum, h) => sum + h.amplificationFactor, 0) / hotspots.length;
|
|
6338
6353
|
const avgPenalty = Math.log2(avgAmplification + 1) * 15;
|
package/dist/index.mjs
CHANGED
|
@@ -55,7 +55,7 @@ import {
|
|
|
55
55
|
} from "./chunk-LRM26BOB.mjs";
|
|
56
56
|
import {
|
|
57
57
|
TypeScriptParser
|
|
58
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-OT6FOHL4.mjs";
|
|
59
59
|
import {
|
|
60
60
|
PythonParser
|
|
61
61
|
} from "./chunk-QY5YG2AZ.mjs";
|
|
@@ -922,11 +922,11 @@ var ParserFactory = class _ParserFactory {
|
|
|
922
922
|
Object.entries(LANGUAGE_EXTENSIONS).map(([ext, lang]) => [ext, lang])
|
|
923
923
|
);
|
|
924
924
|
this.registerLazyParser("typescript" /* TypeScript */, async () => {
|
|
925
|
-
const { TypeScriptParser: TypeScriptParser2 } = await import("./typescript-parser-
|
|
925
|
+
const { TypeScriptParser: TypeScriptParser2 } = await import("./typescript-parser-RMNCTHRD.mjs");
|
|
926
926
|
return new TypeScriptParser2();
|
|
927
927
|
});
|
|
928
928
|
this.registerLazyParser("javascript" /* JavaScript */, async () => {
|
|
929
|
-
const { TypeScriptParser: TypeScriptParser2 } = await import("./typescript-parser-
|
|
929
|
+
const { TypeScriptParser: TypeScriptParser2 } = await import("./typescript-parser-RMNCTHRD.mjs");
|
|
930
930
|
return new TypeScriptParser2();
|
|
931
931
|
});
|
|
932
932
|
this.registerLazyParser("python" /* Python */, async () => {
|
|
@@ -1865,7 +1865,7 @@ function inferPatternType(keyword, name) {
|
|
|
1865
1865
|
if (n.includes("validate") || n.includes("schema")) return "validator";
|
|
1866
1866
|
if (n.includes("util") || n.includes("helper")) return "utility";
|
|
1867
1867
|
if (keyword === "class") return "class-method";
|
|
1868
|
-
if (
|
|
1868
|
+
if (name.match(/^[A-Z]/)) return "component";
|
|
1869
1869
|
if (keyword === "function" || keyword === "def") return "function";
|
|
1870
1870
|
return "unknown";
|
|
1871
1871
|
}
|
|
@@ -1931,7 +1931,14 @@ function extractCodeBlocks(file, content) {
|
|
|
1931
1931
|
patternType: inferPatternType(type, name)
|
|
1932
1932
|
});
|
|
1933
1933
|
}
|
|
1934
|
-
return blocks
|
|
1934
|
+
return blocks.filter((block) => {
|
|
1935
|
+
const code = block.code.trim();
|
|
1936
|
+
if (/^export\s+\{[^}]+\}\s*(from\s+['"][^'"]+['"])?\s*;?\s*$/.test(code))
|
|
1937
|
+
return false;
|
|
1938
|
+
if (/^export\s+\*\s+from\s+/.test(code)) return false;
|
|
1939
|
+
if (/^export\s+\*\s+as\s+\w+\s+from\s+/.test(code)) return false;
|
|
1940
|
+
return true;
|
|
1941
|
+
});
|
|
1935
1942
|
}
|
|
1936
1943
|
function extractBlocksPython(file, content) {
|
|
1937
1944
|
const blocks = [];
|
|
@@ -3355,7 +3362,15 @@ function calculateChangeAmplification(params) {
|
|
|
3355
3362
|
recommendations: []
|
|
3356
3363
|
};
|
|
3357
3364
|
}
|
|
3358
|
-
const hotspots = files.map((f) =>
|
|
3365
|
+
const hotspots = files.map((f) => {
|
|
3366
|
+
const lowerFile = f.file.toLowerCase();
|
|
3367
|
+
const isSharedInfra = lowerFile.endsWith("logger.ts") || lowerFile.endsWith("logger.js") || lowerFile.endsWith("constants.ts") || lowerFile.endsWith("constants.js") || lowerFile.endsWith("types.ts") || lowerFile.endsWith("types.js") || lowerFile.endsWith("index.ts") || lowerFile.endsWith("index.js");
|
|
3368
|
+
const fanInWeight = isSharedInfra ? 0.1 : 0.5;
|
|
3369
|
+
return {
|
|
3370
|
+
...f,
|
|
3371
|
+
amplificationFactor: f.fanOut + f.fanIn * fanInWeight
|
|
3372
|
+
};
|
|
3373
|
+
}).sort((a, b) => b.amplificationFactor - a.amplificationFactor);
|
|
3359
3374
|
const maxAmplification = hotspots[0].amplificationFactor;
|
|
3360
3375
|
const avgAmplification = hotspots.reduce((sum, h) => sum + h.amplificationFactor, 0) / hotspots.length;
|
|
3361
3376
|
const avgPenalty = Math.log2(avgAmplification + 1) * 15;
|