codemetrics-ai 0.2.0

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,444 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/sean-m-cooper/CodeMetrics.AI/shared/scorecard-schema/evidence.schema.v3.json",
4
+ "title": "CodeMetrics.AI Evidence v3",
5
+ "type": "object",
6
+ "required": [
7
+ "schemaVersion",
8
+ "generatedAtUtc",
9
+ "tool",
10
+ "subject",
11
+ "filters",
12
+ "population",
13
+ "dimensions",
14
+ "analysis"
15
+ ],
16
+ "additionalProperties": false,
17
+ "properties": {
18
+ "schemaVersion": {
19
+ "const": 3
20
+ },
21
+ "generatedAtUtc": {
22
+ "type": "string",
23
+ "format": "date-time"
24
+ },
25
+ "tool": {
26
+ "type": "object",
27
+ "required": [
28
+ "name",
29
+ "version",
30
+ "ecosystem"
31
+ ],
32
+ "additionalProperties": false,
33
+ "properties": {
34
+ "name": {
35
+ "type": "string",
36
+ "minLength": 1
37
+ },
38
+ "version": {
39
+ "type": "string",
40
+ "minLength": 1
41
+ },
42
+ "ecosystem": {
43
+ "enum": [
44
+ "dotnet",
45
+ "javascript-typescript",
46
+ "python",
47
+ "rust"
48
+ ]
49
+ }
50
+ }
51
+ },
52
+ "subject": {
53
+ "type": "object",
54
+ "required": [
55
+ "root",
56
+ "entryPoint"
57
+ ],
58
+ "additionalProperties": false,
59
+ "properties": {
60
+ "root": {
61
+ "type": "string",
62
+ "minLength": 1
63
+ },
64
+ "entryPoint": {
65
+ "type": "string",
66
+ "minLength": 1
67
+ },
68
+ "name": {
69
+ "type": "string"
70
+ },
71
+ "variant": {
72
+ "type": "string"
73
+ }
74
+ }
75
+ },
76
+ "filters": {
77
+ "type": "object",
78
+ "required": [
79
+ "totalUnits",
80
+ "analyzedUnits",
81
+ "skipped"
82
+ ],
83
+ "additionalProperties": false,
84
+ "properties": {
85
+ "totalUnits": {
86
+ "type": "integer",
87
+ "minimum": 0
88
+ },
89
+ "analyzedUnits": {
90
+ "type": "integer",
91
+ "minimum": 0
92
+ },
93
+ "skipped": {
94
+ "type": "array",
95
+ "items": {
96
+ "type": "object",
97
+ "required": [
98
+ "name",
99
+ "reason"
100
+ ],
101
+ "additionalProperties": false,
102
+ "properties": {
103
+ "name": {
104
+ "type": "string",
105
+ "minLength": 1
106
+ },
107
+ "reason": {
108
+ "type": "string",
109
+ "minLength": 1
110
+ }
111
+ }
112
+ }
113
+ }
114
+ }
115
+ },
116
+ "population": {
117
+ "type": "object",
118
+ "required": [
119
+ "types",
120
+ "members"
121
+ ],
122
+ "additionalProperties": false,
123
+ "properties": {
124
+ "types": {
125
+ "type": "integer",
126
+ "minimum": 0
127
+ },
128
+ "members": {
129
+ "type": "integer",
130
+ "minimum": 0
131
+ }
132
+ }
133
+ },
134
+ "dimensions": {
135
+ "type": "object",
136
+ "required": [
137
+ "codeQuality",
138
+ "maintainability",
139
+ "errorHandling",
140
+ "performanceAsync",
141
+ "security",
142
+ "testing",
143
+ "documentation",
144
+ "dependencyManagement",
145
+ "architecture"
146
+ ],
147
+ "additionalProperties": false,
148
+ "properties": {
149
+ "codeQuality": {
150
+ "$ref": "#/$defs/dimensionResult"
151
+ },
152
+ "maintainability": {
153
+ "$ref": "#/$defs/dimensionResult"
154
+ },
155
+ "errorHandling": {
156
+ "$ref": "#/$defs/dimensionResult"
157
+ },
158
+ "performanceAsync": {
159
+ "$ref": "#/$defs/dimensionResult"
160
+ },
161
+ "security": {
162
+ "$ref": "#/$defs/dimensionResult"
163
+ },
164
+ "testing": {
165
+ "$ref": "#/$defs/dimensionResult"
166
+ },
167
+ "documentation": {
168
+ "$ref": "#/$defs/dimensionResult"
169
+ },
170
+ "dependencyManagement": {
171
+ "$ref": "#/$defs/dimensionResult"
172
+ },
173
+ "architecture": {
174
+ "$ref": "#/$defs/dimensionResult"
175
+ }
176
+ }
177
+ },
178
+ "analysis": {
179
+ "type": "object",
180
+ "additionalProperties": false,
181
+ "required": [
182
+ "status",
183
+ "ruleset",
184
+ "calibration",
185
+ "configurationFingerprint",
186
+ "diagnostics",
187
+ "suppressions"
188
+ ],
189
+ "properties": {
190
+ "status": {
191
+ "enum": [
192
+ "complete",
193
+ "incomplete"
194
+ ]
195
+ },
196
+ "ruleset": {
197
+ "type": "string",
198
+ "minLength": 1
199
+ },
200
+ "calibration": {
201
+ "enum": [
202
+ "baseline",
203
+ "uncalibrated",
204
+ "calibrated"
205
+ ]
206
+ },
207
+ "configurationFingerprint": {
208
+ "type": "string",
209
+ "minLength": 1
210
+ },
211
+ "diagnostics": {
212
+ "type": "array",
213
+ "items": {
214
+ "type": "object",
215
+ "required": [
216
+ "kind",
217
+ "message"
218
+ ],
219
+ "properties": {
220
+ "kind": {
221
+ "type": "string"
222
+ },
223
+ "message": {
224
+ "type": "string"
225
+ },
226
+ "project": {
227
+ "type": "string"
228
+ }
229
+ },
230
+ "additionalProperties": false
231
+ }
232
+ },
233
+ "suppressions": {
234
+ "type": "array",
235
+ "items": {
236
+ "type": "object",
237
+ "required": [
238
+ "file",
239
+ "line",
240
+ "categories",
241
+ "status"
242
+ ],
243
+ "properties": {
244
+ "file": {
245
+ "type": "string"
246
+ },
247
+ "line": {
248
+ "type": "integer",
249
+ "minimum": 1
250
+ },
251
+ "categories": {
252
+ "type": "array",
253
+ "items": {
254
+ "type": "string"
255
+ }
256
+ },
257
+ "reason": {
258
+ "type": [
259
+ "string",
260
+ "null"
261
+ ]
262
+ },
263
+ "status": {
264
+ "enum": [
265
+ "declared",
266
+ "applied"
267
+ ]
268
+ }
269
+ },
270
+ "additionalProperties": false
271
+ }
272
+ },
273
+ "runId": {
274
+ "type": "string",
275
+ "format": "uuid"
276
+ },
277
+ "auditId": {
278
+ "type": "string",
279
+ "format": "uuid"
280
+ }
281
+ }
282
+ }
283
+ },
284
+ "$defs": {
285
+ "dimensionResult": {
286
+ "type": "object",
287
+ "required": [
288
+ "status",
289
+ "basis",
290
+ "findings"
291
+ ],
292
+ "properties": {
293
+ "status": {
294
+ "enum": [
295
+ "scored",
296
+ "skipped",
297
+ "failed"
298
+ ]
299
+ },
300
+ "score": {
301
+ "type": "number",
302
+ "minimum": 0,
303
+ "maximum": 10
304
+ },
305
+ "basis": {
306
+ "type": "string",
307
+ "minLength": 1
308
+ },
309
+ "findings": {
310
+ "type": "array",
311
+ "items": {
312
+ "$ref": "#/$defs/finding"
313
+ }
314
+ },
315
+ "scope": {
316
+ "type": "object",
317
+ "additionalProperties": false,
318
+ "required": [
319
+ "id",
320
+ "coverage",
321
+ "includes",
322
+ "excludes"
323
+ ],
324
+ "properties": {
325
+ "id": {
326
+ "type": "string",
327
+ "minLength": 1
328
+ },
329
+ "coverage": {
330
+ "enum": [
331
+ "partial",
332
+ "unsupported"
333
+ ]
334
+ },
335
+ "includes": {
336
+ "type": "array",
337
+ "items": {
338
+ "type": "string",
339
+ "minLength": 1
340
+ }
341
+ },
342
+ "excludes": {
343
+ "type": "array",
344
+ "items": {
345
+ "type": "string",
346
+ "minLength": 1
347
+ }
348
+ }
349
+ }
350
+ }
351
+ },
352
+ "additionalProperties": true,
353
+ "if": {
354
+ "properties": {
355
+ "status": {
356
+ "const": "scored"
357
+ }
358
+ },
359
+ "required": [
360
+ "status"
361
+ ]
362
+ },
363
+ "then": {
364
+ "required": [
365
+ "score"
366
+ ]
367
+ },
368
+ "else": {
369
+ "not": {
370
+ "required": [
371
+ "score"
372
+ ]
373
+ }
374
+ }
375
+ },
376
+ "finding": {
377
+ "type": "object",
378
+ "required": [
379
+ "category",
380
+ "severity",
381
+ "message",
382
+ "ruleId",
383
+ "fingerprint",
384
+ "confidence",
385
+ "observations"
386
+ ],
387
+ "additionalProperties": false,
388
+ "properties": {
389
+ "category": {
390
+ "type": "string",
391
+ "minLength": 1
392
+ },
393
+ "severity": {
394
+ "enum": [
395
+ "info",
396
+ "warning",
397
+ "error"
398
+ ]
399
+ },
400
+ "file": {
401
+ "type": "string"
402
+ },
403
+ "line": {
404
+ "type": "integer",
405
+ "minimum": 0
406
+ },
407
+ "project": {
408
+ "type": "string"
409
+ },
410
+ "type": {
411
+ "type": "string"
412
+ },
413
+ "member": {
414
+ "type": "string"
415
+ },
416
+ "package": {
417
+ "type": "string"
418
+ },
419
+ "confidence": {
420
+ "enum": [
421
+ "high",
422
+ "medium",
423
+ "low"
424
+ ]
425
+ },
426
+ "message": {
427
+ "type": "string",
428
+ "minLength": 1
429
+ },
430
+ "ruleId": {
431
+ "type": "string",
432
+ "minLength": 1
433
+ },
434
+ "fingerprint": {
435
+ "type": "string",
436
+ "pattern": "^[0-9a-f]{64}$"
437
+ },
438
+ "observations": {
439
+ "type": "object"
440
+ }
441
+ }
442
+ }
443
+ }
444
+ }
@@ -0,0 +1,5 @@
1
+ export { defaultEvidencePath, defaultMetricsPath, dimensionKeys, ecosystemId, metricsCsvHeader, type DimensionKey, } from "./scorecard-contract.js";
2
+ export { analyze } from "./analyzer.js";
3
+ export { compare, gate, sarif, validateEvidence, readEvidence } from "./evidence-tools.js";
4
+ export type { Evidence, Finding, Dimension } from "./evidence.js";
5
+ export { readCompatibleEvidence, inspectEvidence, type LegacyEvidence } from "./evidence-reader.js";
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { defaultEvidencePath, defaultMetricsPath, dimensionKeys, ecosystemId, metricsCsvHeader, } from "./scorecard-contract.js";
2
+ export { analyze } from "./analyzer.js";
3
+ export { compare, gate, sarif, validateEvidence, readEvidence } from "./evidence-tools.js";
4
+ export { readCompatibleEvidence, inspectEvidence } from "./evidence-reader.js";
package/dist/io.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare function write(file: string, value: unknown): void;
2
+ export declare function distinctOutputs(inputs: (string | undefined)[], outputs: (string | undefined)[]): void;
package/dist/io.js ADDED
@@ -0,0 +1,21 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { randomUUID } from "node:crypto";
4
+ export function write(file, value) {
5
+ fs.mkdirSync(path.dirname(path.resolve(file)), { recursive: true });
6
+ const temporary = file + "." + randomUUID() + ".tmp";
7
+ try {
8
+ fs.writeFileSync(temporary, typeof value === "string" ? value : JSON.stringify(value, null, 2) + "\n");
9
+ fs.renameSync(temporary, file);
10
+ }
11
+ finally {
12
+ if (fs.existsSync(temporary))
13
+ fs.unlinkSync(temporary);
14
+ }
15
+ }
16
+ export function distinctOutputs(inputs, outputs) {
17
+ const normalize = (file) => process.platform === "win32" ? path.resolve(file).toLowerCase() : path.resolve(file);
18
+ const targets = outputs.filter((file) => !!file).map(normalize);
19
+ if (new Set(targets).size !== targets.length || inputs.some(file => file && targets.includes(normalize(file))))
20
+ throw new Error("Output paths must be distinct and must not overwrite inputs.");
21
+ }
@@ -0,0 +1,26 @@
1
+ import ts from "typescript";
2
+ import { type Finding } from "./evidence.js";
3
+ export interface Metric {
4
+ project: string;
5
+ file: string;
6
+ type: string;
7
+ member: string;
8
+ kind: "function" | "component" | "hook" | "method";
9
+ line: number;
10
+ complexity: number;
11
+ sourceLines: number;
12
+ executableLines: number;
13
+ halsteadVolume: number;
14
+ maintainabilityIndex: number;
15
+ coupling: number;
16
+ inheritance: number;
17
+ }
18
+ export declare function maintainability(volume: number, complexity: number, lines: number): number;
19
+ export declare function analyzeFile(source: ts.SourceFile, checker: ts.TypeChecker, project: string, root: string): {
20
+ metrics: Metric[];
21
+ findings: {
22
+ dimension: "codeQuality" | "performanceAsync";
23
+ finding: Finding;
24
+ }[];
25
+ reactSupported: boolean;
26
+ };
@@ -0,0 +1,153 @@
1
+ import path from "node:path";
2
+ import ts from "typescript";
3
+ import { hash } from "./evidence.js";
4
+ function isFunction(node) {
5
+ return ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node) ||
6
+ ts.isMethodDeclaration(node) || ts.isConstructorDeclaration(node) || ts.isGetAccessorDeclaration(node) || ts.isSetAccessorDeclaration(node);
7
+ }
8
+ function nameOf(node, source) {
9
+ if (node.name)
10
+ return node.name.getText(source);
11
+ if (ts.isConstructorDeclaration(node))
12
+ return "constructor";
13
+ if (ts.isVariableDeclaration(node.parent) || ts.isPropertyAssignment(node.parent))
14
+ return node.parent.name.getText(source);
15
+ if (ts.isCallExpression(node.parent))
16
+ return `${node.parent.expression.getText(source)}:callback${node.parent.arguments.indexOf(node)}`;
17
+ return "anonymous";
18
+ }
19
+ function ownVisit(node, visit) {
20
+ if (isFunction(node) || ts.isTypeNode(node) || ts.isInterfaceDeclaration(node) || ts.isTypeAliasDeclaration(node))
21
+ return;
22
+ visit(node);
23
+ ts.forEachChild(node, child => ownVisit(child, visit));
24
+ }
25
+ export function maintainability(volume, complexity, lines) {
26
+ return Math.round(Math.max(0, (171 - 5.2 * Math.log(Math.max(volume, 1)) - 0.23 * complexity - 16.2 * Math.log(Math.max(lines, 1))) * 100 / 171));
27
+ }
28
+ export function analyzeFile(source, checker, project, root) {
29
+ const file = path.relative(root, source.fileName).replaceAll("\\", "/");
30
+ const metrics = [];
31
+ const findings = [];
32
+ const importCount = new Set(source.statements.filter(ts.isImportDeclaration).map(node => node.moduleSpecifier.getText(source))).size;
33
+ const identities = new Map();
34
+ function emit(category, dimension, node, member, message, observations, confidence = "high") {
35
+ const ruleId = `javascript-typescript/${dimension}/${category}`;
36
+ const identity = `${ruleId}|${file}|${project}|${member}`;
37
+ const occurrence = identities.get(identity) ?? 0;
38
+ identities.set(identity, occurrence + 1);
39
+ findings.push({ dimension, finding: { category, ruleId, fingerprint: hash(`${identity}|${occurrence}`),
40
+ severity: "warning", confidence, file, line: source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1,
41
+ project, member, message, observations } });
42
+ }
43
+ function visit(node, parentName = "") {
44
+ if (isFunction(node) && node.body) {
45
+ const name = nameOf(node, source);
46
+ const member = parentName ? `${parentName}/${name}` : name;
47
+ const parentClass = ts.isClassDeclaration(node.parent) || ts.isClassExpression(node.parent) ? node.parent : undefined;
48
+ let complexity = 1, executableLines = 0, hasJsx = false;
49
+ ownVisit(node.body, child => {
50
+ if (ts.isIfStatement(child) || ts.isForStatement(child) || ts.isForInStatement(child) || ts.isForOfStatement(child) ||
51
+ ts.isWhileStatement(child) || ts.isDoStatement(child) || ts.isCatchClause(child) || ts.isCaseClause(child) || ts.isConditionalExpression(child))
52
+ complexity++;
53
+ if (ts.isBinaryExpression(child) && [ts.SyntaxKind.AmpersandAmpersandToken, ts.SyntaxKind.BarBarToken, ts.SyntaxKind.QuestionQuestionToken].includes(child.operatorToken.kind))
54
+ complexity++;
55
+ if (ts.isStatement(child) && !ts.isBlock(child) && !ts.isEmptyStatement(child))
56
+ executableLines++;
57
+ if (ts.isJsxElement(child) || ts.isJsxSelfClosingElement(child) || ts.isJsxFragment(child))
58
+ hasJsx = true;
59
+ if (ts.isCallExpression(child)) {
60
+ const symbol = checker.getSymbolAtLocation(ts.isPropertyAccessExpression(child.expression) ? child.expression.name : child.expression);
61
+ const declarations = symbol?.declarations ?? [];
62
+ const reactHook = declarations.some(declaration => {
63
+ let cursor = declaration;
64
+ while (cursor && !ts.isImportDeclaration(cursor))
65
+ cursor = cursor.parent;
66
+ return cursor && ts.isStringLiteral(cursor.moduleSpecifier) && cursor.moduleSpecifier.text === "react";
67
+ });
68
+ const hookName = declarations.find(ts.isImportSpecifier);
69
+ const calledName = hookName ? (hookName.propertyName ?? hookName.name).text : ts.isPropertyAccessExpression(child.expression) ? child.expression.name.text : "";
70
+ const namespaceSymbol = ts.isPropertyAccessExpression(child.expression) ? checker.getSymbolAtLocation(child.expression.expression) : undefined;
71
+ const reactNamespace = namespaceSymbol?.declarations?.some(declaration => {
72
+ let cursor = declaration;
73
+ while (cursor && !ts.isImportDeclaration(cursor))
74
+ cursor = cursor.parent;
75
+ return cursor && ts.isStringLiteral(cursor.moduleSpecifier) && cursor.moduleSpecifier.text === "react";
76
+ });
77
+ if ((reactHook || reactNamespace) && /^use[A-Z]/.test(calledName)) {
78
+ let ancestor = child.parent;
79
+ while (ancestor && ancestor !== node.body) {
80
+ if (ts.isIfStatement(ancestor) || ts.isConditionalExpression(ancestor) || ts.isForStatement(ancestor) || ts.isForOfStatement(ancestor) || ts.isForInStatement(ancestor) || ts.isWhileStatement(ancestor) || ts.isDoStatement(ancestor) ||
81
+ ts.isBinaryExpression(ancestor) && [ts.SyntaxKind.AmpersandAmpersandToken, ts.SyntaxKind.BarBarToken, ts.SyntaxKind.QuestionQuestionToken].includes(ancestor.operatorToken.kind)) {
82
+ emit("conditionalHook", "performanceAsync", child, member, `${calledName} is called within a conditional or loop.`, { hook: calledName });
83
+ break;
84
+ }
85
+ ancestor = ancestor.parent;
86
+ }
87
+ if (["useEffect", "useLayoutEffect"].includes(calledName)) {
88
+ const callback = child.arguments[0];
89
+ if (callback && isFunction(callback) && callback.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.AsyncKeyword))
90
+ emit("asyncEffectCallback", "performanceAsync", child, member, "An effect callback is async and returns a Promise instead of cleanup or undefined.", { hook: calledName });
91
+ if (child.arguments.length < 2)
92
+ emit("effectWithoutDependencies", "performanceAsync", child, member, "Effect runs after every render; verify that this is intentional.", { hook: calledName }, "low");
93
+ }
94
+ }
95
+ }
96
+ });
97
+ if (!ts.isBlock(node.body))
98
+ executableLines++;
99
+ const operators = [], operands = [], sourceLineSet = new Set();
100
+ function tokens(current) {
101
+ if (current !== node && (isFunction(current) || ts.isTypeNode(current)))
102
+ return;
103
+ // Markup contributes source lines, but not Halstead operators or operands.
104
+ if (ts.isJsxText(current))
105
+ return;
106
+ const children = current.getChildren(source);
107
+ if (children.length) {
108
+ children.forEach(tokens);
109
+ return;
110
+ }
111
+ if (current.kind === ts.SyntaxKind.EndOfFileToken || current.getWidth(source) === 0)
112
+ return;
113
+ const text = current.getText(source);
114
+ if (!["{", "}", ";"].includes(text))
115
+ sourceLineSet.add(source.getLineAndCharacterOfPosition(current.getStart(source)).line);
116
+ if (ts.isIdentifier(current) || ts.isLiteralExpression(current))
117
+ operands.push(text);
118
+ else
119
+ operators.push(text);
120
+ }
121
+ tokens(node);
122
+ const vocabulary = new Set(operators).size + new Set(operands).size;
123
+ const volume = vocabulary > 1 ? (operators.length + operands.length) * Math.log2(vocabulary) : 0;
124
+ let inheritance = 0;
125
+ if (parentClass) {
126
+ const seen = new Set();
127
+ let current = checker.getTypeAtLocation(parentClass);
128
+ while (current && !seen.has(current)) {
129
+ seen.add(current);
130
+ current = current.getBaseTypes()?.[0];
131
+ if (current)
132
+ inheritance++;
133
+ }
134
+ }
135
+ const kind = parentClass ? "method" : /^use[A-Z]/.test(name) ? "hook" : hasJsx && /^[A-Z]/.test(name) ? "component" : "function";
136
+ const type = parentClass?.name?.text ?? (parentName || name);
137
+ const metric = { project, file, type, member, kind, line: source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1,
138
+ complexity, sourceLines: Math.max(1, sourceLineSet.size), executableLines, halsteadVolume: volume,
139
+ maintainabilityIndex: maintainability(volume, complexity, Math.max(1, sourceLineSet.size)), coupling: importCount, inheritance };
140
+ metrics.push(metric);
141
+ if (complexity > 10)
142
+ emit("highFunctionComplexity", "codeQuality", node, member, `${member} has cyclomatic complexity ${complexity} (threshold: 10).`, { measured: complexity, threshold: 10, metric: "cyclomaticComplexity", kind });
143
+ ts.forEachChild(node, child => visit(child, member));
144
+ return;
145
+ }
146
+ if (!ts.isTypeNode(node))
147
+ ts.forEachChild(node, child => visit(child, parentName));
148
+ }
149
+ visit(source);
150
+ const reactSupported = source.statements.some(statement => ts.isImportDeclaration(statement) && !statement.importClause?.isTypeOnly &&
151
+ ts.isStringLiteral(statement.moduleSpecifier) && statement.moduleSpecifier.text === "react");
152
+ return { metrics, findings, reactSupported };
153
+ }
@@ -0,0 +1,6 @@
1
+ export declare const ecosystemId = "javascript-typescript";
2
+ export declare const defaultMetricsPath = ".scorecard/javascript-typescript/metrics.csv";
3
+ export declare const defaultEvidencePath = ".scorecard/javascript-typescript/evidence.json";
4
+ export declare const dimensionKeys: readonly ["codeQuality", "maintainability", "errorHandling", "performanceAsync", "security", "testing", "documentation", "dependencyManagement", "architecture"];
5
+ export type DimensionKey = (typeof dimensionKeys)[number];
6
+ export declare const metricsCsvHeader = "Scope,Project,Namespace,Type,Member,Maintainability Index,Cyclomatic Complexity,Depth of Inheritance,Class Coupling,Lines of Source code,Lines of Executable code";
@@ -0,0 +1,15 @@
1
+ export const ecosystemId = "javascript-typescript";
2
+ export const defaultMetricsPath = ".scorecard/javascript-typescript/metrics.csv";
3
+ export const defaultEvidencePath = ".scorecard/javascript-typescript/evidence.json";
4
+ export const dimensionKeys = [
5
+ "codeQuality",
6
+ "maintainability",
7
+ "errorHandling",
8
+ "performanceAsync",
9
+ "security",
10
+ "testing",
11
+ "documentation",
12
+ "dependencyManagement",
13
+ "architecture",
14
+ ];
15
+ export const metricsCsvHeader = "Scope,Project,Namespace,Type,Member,Maintainability Index,Cyclomatic Complexity,Depth of Inheritance,Class Coupling,Lines of Source code,Lines of Executable code";