x-fidelity 3.3.0 → 3.4.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.
- package/CHANGELOG.md +21 -0
- package/dist/core/cli.js +30 -14
- package/dist/core/engine/analyzer.js +6 -2
- package/dist/core/engine/analyzer.test.js +11 -3
- package/dist/core/engine/engineRunner.js +4 -2
- package/dist/demoConfig/node-fullstack.json +4 -2
- package/dist/demoConfig/rules/regexMatch-example-iterative-rule.json +28 -0
- package/dist/demoConfig/rules/sensitiveLogging-iterative-rule.json +2 -1
- package/dist/facts/repoDependencyFacts.js +0 -1
- package/dist/index.js +11 -8
- package/dist/operators/index.js +3 -1
- package/dist/operators/nonStandardDirectoryStructure.js +1 -1
- package/dist/operators/regexMatch.d.ts +10 -0
- package/dist/operators/regexMatch.js +46 -0
- package/dist/operators/regexMatch.test.d.ts +1 -0
- package/dist/operators/regexMatch.test.js +52 -0
- package/dist/types/typeDefs.d.ts +3 -0
- package/dist/utils/logger.js +4 -9
- package/dist/xfidelity +11 -8
- package/package.json +2 -2
- package/src/core/cli.ts +31 -15
- package/src/core/engine/analyzer.test.ts +11 -3
- package/src/core/engine/analyzer.ts +7 -2
- package/src/core/engine/engineRunner.ts +4 -2
- package/src/demoConfig/node-fullstack.json +4 -2
- package/src/demoConfig/rules/regexMatch-example-iterative-rule.json +28 -0
- package/src/demoConfig/rules/sensitiveLogging-iterative-rule.json +2 -1
- package/src/facts/repoDependencyFacts.ts +0 -1
- package/src/index.ts +15 -11
- package/src/operators/index.ts +2 -0
- package/src/operators/nonStandardDirectoryStructure.ts +1 -1
- package/src/operators/regexMatch.test.ts +59 -0
- package/src/operators/regexMatch.ts +51 -0
- package/src/types/typeDefs.ts +4 -1
- package/src/utils/logger.ts +3 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
+
# [3.4.0](https://github.com/zotoio/x-fidelity/compare/v3.3.1...v3.4.0) (2025-02-26)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* add support for all modern regex flags in pattern matching ([847a320](https://github.com/zotoio/x-fidelity/commit/847a32068b68db66c1a9d84090bae4bc918f36d0))
|
|
7
|
+
* correct regex test case to use case-insensitive flag ([96d906c](https://github.com/zotoio/x-fidelity/commit/96d906c23833f482d649df866ca36f28f609bbd6))
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* add regex pattern matching operator with tests ([8ac216f](https://github.com/zotoio/x-fidelity/commit/8ac216f222a28367eba971f0f187fad4173167b3))
|
|
13
|
+
* **operators:** add regexMatch general purpose operator ([8270696](https://github.com/zotoio/x-fidelity/commit/82706966e9a52c089848f785007f415e614b67da))
|
|
14
|
+
|
|
15
|
+
## [3.3.1](https://github.com/zotoio/x-fidelity/compare/v3.3.0...v3.3.1) (2025-02-26)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Bug Fixes
|
|
19
|
+
|
|
20
|
+
* **engine:** node 18 ([6c3e157](https://github.com/zotoio/x-fidelity/commit/6c3e157d0873dbd6148c568f213ee7ad997744bd))
|
|
21
|
+
|
|
1
22
|
# [3.3.0](https://github.com/zotoio/x-fidelity/compare/v3.2.0...v3.3.0) (2025-02-22)
|
|
2
23
|
|
|
3
24
|
|
package/dist/core/cli.js
CHANGED
|
@@ -15,6 +15,18 @@ const prettyjson_1 = __importDefault(require("prettyjson"));
|
|
|
15
15
|
exports.DEMO_CONFIG_PATH = path_1.default.resolve(__dirname, '../demoConfig');
|
|
16
16
|
exports.options = commander_1.program.opts();
|
|
17
17
|
function initCLI() {
|
|
18
|
+
const bannerArt = `\n
|
|
19
|
+
=====================================
|
|
20
|
+
__ __ ________ ______
|
|
21
|
+
| ## | ## | ######## \\######
|
|
22
|
+
\\##\\/ ## ______ | ##__ | ##
|
|
23
|
+
>## ## | \\| ## \\ | ##
|
|
24
|
+
/ ####\\ \\######| ###### | ##
|
|
25
|
+
| ## \\##\\ | ## _| ##_
|
|
26
|
+
| ## | ## | ## | ## \\
|
|
27
|
+
\\## \\## \\## \\######
|
|
28
|
+
-------------------------------------
|
|
29
|
+
`;
|
|
18
30
|
// Ensure logger is initialized
|
|
19
31
|
if (!logger_1.logger || typeof logger_1.logger.info !== 'function') {
|
|
20
32
|
console.error({ msg: 'Logger is not properly initialized' });
|
|
@@ -40,7 +52,7 @@ function initCLI() {
|
|
|
40
52
|
}
|
|
41
53
|
});
|
|
42
54
|
commander_1.program
|
|
43
|
-
.option("-d, --dir <directory>", "
|
|
55
|
+
.option("-d, --dir <directory>", "local git repo directory path to analyze. equivalent of directory argument")
|
|
44
56
|
.option("-a, --archetype <archetype>", "The archetype to use for analysis", "node-fullstack")
|
|
45
57
|
.option("-c, --configServer <configServer>", "The config server URL for fetching remote archetype configurations and rules. This takes precedence over localConfigPath.")
|
|
46
58
|
.option("-o, --openaiEnabled <boolean>", "Enable OpenAI analysis", false)
|
|
@@ -53,7 +65,11 @@ function initCLI() {
|
|
|
53
65
|
.option("-x, --examine <archetype>", "Examine the archetype configuration and rules")
|
|
54
66
|
.version(package_json_1.version, "-v, --version", "Output the version number of xfidelity")
|
|
55
67
|
.helpOption("-h, --help", "Display help for command")
|
|
56
|
-
.
|
|
68
|
+
.summary("CLI for analyzing codebases for architectural fidelity")
|
|
69
|
+
.usage("[directory] [options]")
|
|
70
|
+
.argument('[directory]', 'local git repo directory path to analyze')
|
|
71
|
+
.addHelpText('before', bannerArt)
|
|
72
|
+
.addHelpText('after', '-------------------------------------');
|
|
57
73
|
commander_1.program.parse(process.argv);
|
|
58
74
|
// Resolve paths
|
|
59
75
|
const resolvePath = (inputPath) => {
|
|
@@ -90,18 +106,18 @@ function initCLI() {
|
|
|
90
106
|
if (process.env.NODE_ENV !== 'test')
|
|
91
107
|
commander_1.program.error(`LocalConfigPath does not exist or is invalid: ${error}`);
|
|
92
108
|
}
|
|
93
|
-
const bannerArt = `\n
|
|
94
|
-
=====================================
|
|
95
|
-
|
|
96
|
-
| ## | ## | ######## \\######
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
| ## \\##\\ | ## _| ##_
|
|
101
|
-
| ## | ## | ## | ## \\
|
|
102
|
-
|
|
103
|
-
-------------------------------------
|
|
104
|
-
`;
|
|
109
|
+
// const bannerArt = `\n
|
|
110
|
+
// =====================================
|
|
111
|
+
// __ __ ________ ______
|
|
112
|
+
// | ## | ## | ######## \\######
|
|
113
|
+
// \\##\\/ ## ______ | ##__ | ##
|
|
114
|
+
// >## ## | \\| ## \\ | ##
|
|
115
|
+
// / ####\\ \\######| ###### | ##
|
|
116
|
+
// | ## \\##\\ | ## _| ##_
|
|
117
|
+
// | ## | ## | ## | ## \\
|
|
118
|
+
// \\## \\## \\## \\######
|
|
119
|
+
// -------------------------------------
|
|
120
|
+
// `;
|
|
105
121
|
logger_1.logger.info(bannerArt);
|
|
106
122
|
logger_1.logger.info(`\n${prettyjson_1.default.render({
|
|
107
123
|
version: package_json_1.version,
|
|
@@ -87,22 +87,25 @@ function analyzeCodebase(params) {
|
|
|
87
87
|
const finishMsg = `\n==========================\nCHECKS COMPLETED..\n==========================`;
|
|
88
88
|
logger_1.logger.info(finishMsg);
|
|
89
89
|
const totalFailureCount = (0, utils_1.countRuleFailures)(failures);
|
|
90
|
-
logger_1.logger.info(`${fileData.length} files analyzed. ${totalFailureCount} rule failures.`);
|
|
90
|
+
logger_1.logger.info(`${fileData.length - 1} files analyzed. ${totalFailureCount} rule failures.`);
|
|
91
91
|
const fatalityCount = (0, utils_1.countRuleFailures)(failures, 'fatality');
|
|
92
92
|
const warningCount = (0, utils_1.countRuleFailures)(failures, 'warning');
|
|
93
93
|
const exemptCount = (0, utils_1.countRuleFailures)(failures, 'exempt');
|
|
94
94
|
const errorCount = (0, utils_1.countRuleFailures)(failures, 'error');
|
|
95
95
|
const finishTime = new Date().getTime();
|
|
96
|
+
const memoryUsage = process.memoryUsage();
|
|
97
|
+
logger_1.logger.info('Assemblying result metadata..');
|
|
96
98
|
const resultMetadata = {
|
|
97
99
|
XFI_RESULT: {
|
|
98
100
|
archetype,
|
|
99
101
|
telemetryData,
|
|
102
|
+
memoryUsage,
|
|
100
103
|
repoXFIConfig: repoXFIConfig,
|
|
101
104
|
issueDetails: failures,
|
|
102
105
|
startTime: telemetryData.startTime,
|
|
103
106
|
finishTime: finishTime,
|
|
104
107
|
durationSeconds: (finishTime - telemetryData.startTime) / 1000,
|
|
105
|
-
fileCount: fileData.length,
|
|
108
|
+
fileCount: fileData.length - 1,
|
|
106
109
|
totalIssues: totalFailureCount,
|
|
107
110
|
warningCount: warningCount,
|
|
108
111
|
fatalityCount: fatalityCount,
|
|
@@ -110,6 +113,7 @@ function analyzeCodebase(params) {
|
|
|
110
113
|
exemptCount: exemptCount,
|
|
111
114
|
options: cli_1.options,
|
|
112
115
|
repoPath,
|
|
116
|
+
repoUrl
|
|
113
117
|
}
|
|
114
118
|
};
|
|
115
119
|
// Send telemetry for analysis end
|
|
@@ -106,15 +106,17 @@ describe('analyzeCodebase', () => {
|
|
|
106
106
|
XFI_RESULT: expect.objectContaining({
|
|
107
107
|
archetype: 'node-fullstack',
|
|
108
108
|
repoPath: 'mockRepoPath',
|
|
109
|
-
fileCount:
|
|
109
|
+
fileCount: expect.any(Number),
|
|
110
110
|
totalIssues: expect.any(Number),
|
|
111
111
|
warningCount: expect.any(Number),
|
|
112
112
|
fatalityCount: expect.any(Number),
|
|
113
|
+
errorCount: expect.any(Number),
|
|
113
114
|
exemptCount: expect.any(Number),
|
|
114
115
|
issueDetails: expect.any(Array),
|
|
115
116
|
durationSeconds: expect.any(Number),
|
|
116
117
|
finishTime: expect.any(Number),
|
|
117
118
|
startTime: expect.any(Number),
|
|
119
|
+
memoryUsage: expect.any(Object),
|
|
118
120
|
options: expect.objectContaining({
|
|
119
121
|
archetype: 'node-fullstack',
|
|
120
122
|
configServer: '',
|
|
@@ -132,6 +134,7 @@ describe('analyzeCodebase', () => {
|
|
|
132
134
|
startTime: expect.any(Number),
|
|
133
135
|
userInfo: expect.any(Object),
|
|
134
136
|
}),
|
|
137
|
+
repoUrl: expect.any(String),
|
|
135
138
|
repoXFIConfig: expect.objectContaining({
|
|
136
139
|
sensitiveFileFalsePositives: expect.any(Array),
|
|
137
140
|
})
|
|
@@ -177,7 +180,7 @@ describe('analyzeCodebase', () => {
|
|
|
177
180
|
XFI_RESULT: expect.objectContaining({
|
|
178
181
|
archetype: 'node-fullstack',
|
|
179
182
|
repoPath: 'mockRepoPath',
|
|
180
|
-
fileCount:
|
|
183
|
+
fileCount: expect.any(Number),
|
|
181
184
|
totalIssues: 3,
|
|
182
185
|
warningCount: 0,
|
|
183
186
|
fatalityCount: 0,
|
|
@@ -202,8 +205,10 @@ describe('analyzeCodebase', () => {
|
|
|
202
205
|
durationSeconds: expect.any(Number),
|
|
203
206
|
finishTime: expect.any(Number),
|
|
204
207
|
startTime: expect.any(Number),
|
|
208
|
+
memoryUsage: expect.any(Object),
|
|
205
209
|
options: expect.any(Object),
|
|
206
210
|
telemetryData: expect.any(Object),
|
|
211
|
+
repoUrl: expect.any(String),
|
|
207
212
|
repoXFIConfig: expect.objectContaining({
|
|
208
213
|
sensitiveFileFalsePositives: expect.any(Array)
|
|
209
214
|
})
|
|
@@ -305,7 +310,7 @@ describe('analyzeCodebase', () => {
|
|
|
305
310
|
XFI_RESULT: expect.objectContaining({
|
|
306
311
|
archetype: 'node-fullstack',
|
|
307
312
|
repoPath: 'mockRepoPath',
|
|
308
|
-
fileCount:
|
|
313
|
+
fileCount: expect.any(Number),
|
|
309
314
|
totalIssues: 3,
|
|
310
315
|
warningCount: 0,
|
|
311
316
|
fatalityCount: 3,
|
|
@@ -321,8 +326,11 @@ describe('analyzeCodebase', () => {
|
|
|
321
326
|
durationSeconds: expect.any(Number),
|
|
322
327
|
finishTime: expect.any(Number),
|
|
323
328
|
startTime: expect.any(Number),
|
|
329
|
+
memoryUsage: expect.any(Object),
|
|
324
330
|
options: expect.any(Object),
|
|
325
331
|
telemetryData: expect.any(Object),
|
|
332
|
+
repoUrl: expect.any(String),
|
|
333
|
+
repoXFIConfig: expect.any(Object)
|
|
326
334
|
})
|
|
327
335
|
});
|
|
328
336
|
expect(telemetry_1.sendTelemetry).toHaveBeenCalledTimes(2); // Start and end
|
|
@@ -20,13 +20,15 @@ function runEngineOnFiles(params) {
|
|
|
20
20
|
const msg = `\n==========================\nRUNNING FILE CHECKS..\n==========================`;
|
|
21
21
|
logger_1.logger.info(msg);
|
|
22
22
|
const failures = [];
|
|
23
|
-
|
|
23
|
+
const fileCount = fileData.length;
|
|
24
|
+
for (let i = 0; i < fileCount; i++) {
|
|
25
|
+
const file = fileData[i];
|
|
24
26
|
if (file.fileName === configManager_1.REPO_GLOBAL_CHECK) {
|
|
25
27
|
const msg = `\n==========================\nRUNNING GLOBAL REPO CHECKS..\n==========================`;
|
|
26
28
|
logger_1.logger.info(msg);
|
|
27
29
|
}
|
|
28
30
|
else {
|
|
29
|
-
const msg = `analysing ${file.filePath} ...`;
|
|
31
|
+
const msg = `analysing (${i + 1} of ${fileCount - 1}) ${file.filePath} ...`;
|
|
30
32
|
logger_1.logger.info(msg);
|
|
31
33
|
}
|
|
32
34
|
const facts = {
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"openaiAnalysisTop5-global",
|
|
9
9
|
"openaiAnalysisA11y-global",
|
|
10
10
|
"invalidSystemIdConfigured-iterative",
|
|
11
|
-
"missingRequiredFiles-global"
|
|
11
|
+
"missingRequiredFiles-global",
|
|
12
|
+
"regexMatch-example-iterative"
|
|
12
13
|
],
|
|
13
14
|
"operators": [
|
|
14
15
|
"fileContains",
|
|
@@ -16,7 +17,8 @@
|
|
|
16
17
|
"nonStandardDirectoryStructure",
|
|
17
18
|
"openaiAnalysisHighSeverity",
|
|
18
19
|
"invalidRemoteValidation",
|
|
19
|
-
"missingRequiredFiles"
|
|
20
|
+
"missingRequiredFiles",
|
|
21
|
+
"regexMatch"
|
|
20
22
|
],
|
|
21
23
|
"facts": [
|
|
22
24
|
"repoFilesystemFacts",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "regexMatch-example",
|
|
3
|
+
"conditions": {
|
|
4
|
+
"all": [
|
|
5
|
+
{
|
|
6
|
+
"fact": "fileData",
|
|
7
|
+
"path": "$.filePath",
|
|
8
|
+
"operator": "regexMatch",
|
|
9
|
+
"value": ".*/facts/.*"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"fact": "fileData",
|
|
13
|
+
"path": "$.fileContent",
|
|
14
|
+
"operator": "regexMatch",
|
|
15
|
+
"value": "OpenAI"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
"event": {
|
|
20
|
+
"type": "warning",
|
|
21
|
+
"params": {
|
|
22
|
+
"message": "I'm seeing a pattern here..",
|
|
23
|
+
"details": {
|
|
24
|
+
"suggestion": "wake up and smell the coffee"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -76,7 +76,6 @@ function collectLocalDependencies() {
|
|
|
76
76
|
else {
|
|
77
77
|
logger_1.logger.error('No yarn.lock or package-lock.json found');
|
|
78
78
|
process.exit(1);
|
|
79
|
-
throw new Error('Unsupported package manager');
|
|
80
79
|
}
|
|
81
80
|
logger_1.logger.trace(`collectLocalDependencies: ${(0, utils_1.safeStringify)(result)}`);
|
|
82
81
|
return result;
|
package/dist/index.js
CHANGED
|
@@ -54,7 +54,6 @@ exports.main = main;
|
|
|
54
54
|
const logger_1 = require("./utils/logger");
|
|
55
55
|
const cli_1 = require("./core/cli");
|
|
56
56
|
if (require.main === module) {
|
|
57
|
-
(0, logger_1.initializeLogger)();
|
|
58
57
|
(0, logger_1.setLogLevel)(process.env.XFI_LOG_LEVEL || 'info');
|
|
59
58
|
(0, cli_1.initCLI)();
|
|
60
59
|
}
|
|
@@ -71,7 +70,8 @@ const handleError = (error) => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
71
70
|
archetype: cli_1.options.archetype,
|
|
72
71
|
repoPath: cli_1.options.dir,
|
|
73
72
|
options: cli_1.options,
|
|
74
|
-
errorMessage: error.message
|
|
73
|
+
errorMessage: error.message,
|
|
74
|
+
errorStack: error.stack
|
|
75
75
|
},
|
|
76
76
|
timestamp: new Date().toISOString()
|
|
77
77
|
}, executionLogPrefix);
|
|
@@ -101,38 +101,41 @@ function main() {
|
|
|
101
101
|
localConfigPath: cli_1.options.localConfigPath,
|
|
102
102
|
executionLogPrefix
|
|
103
103
|
});
|
|
104
|
+
const resultString = JSON.stringify(resultMetadata);
|
|
105
|
+
const prettyResult = prettyjson_1.default.render(resultMetadata.XFI_RESULT);
|
|
104
106
|
// if results are found, there were issues found in the codebase
|
|
105
107
|
if (resultMetadata.XFI_RESULT.totalIssues > 0) {
|
|
106
108
|
logger_1.logger.warn(`WARNING: lo-fi attributes detected in codebase. ${resultMetadata.XFI_RESULT.warningCount} are warnings, ${resultMetadata.XFI_RESULT.fatalityCount} are fatal.`);
|
|
107
|
-
logger_1.logger.warn(
|
|
109
|
+
logger_1.logger.warn(resultString);
|
|
108
110
|
if (resultMetadata.XFI_RESULT.errorCount > 0) {
|
|
109
111
|
logger_1.logger.error(outcomeMessage(`THERE WERE ${resultMetadata.XFI_RESULT.errorCount} UNEXPECTED ERRORS!`));
|
|
110
|
-
logger_1.logger.error(`\n${
|
|
112
|
+
logger_1.logger.error(`\n${prettyResult}\n\n`);
|
|
111
113
|
logger_1.logger.error(outcomeMessage(`THERE WERE ${resultMetadata.XFI_RESULT.errorCount} UNEXPECTED ERRORS!`));
|
|
112
114
|
process.exit(1);
|
|
113
115
|
}
|
|
114
116
|
if (resultMetadata.XFI_RESULT.fatalityCount > 0) {
|
|
115
117
|
logger_1.logger.error(outcomeMessage(`THERE WERE ${resultMetadata.XFI_RESULT.fatalityCount} FATAL ERRORS DETECTED TO BE IMMEDIATELY ADDRESSED!`));
|
|
116
|
-
logger_1.logger.error(`\n${
|
|
118
|
+
logger_1.logger.error(`\n${prettyResult}\n\n`);
|
|
117
119
|
logger_1.logger.error(outcomeMessage(`THERE WERE ${resultMetadata.XFI_RESULT.fatalityCount} FATAL ERRORS DETECTED TO BE IMMEDIATELY ADDRESSED!`));
|
|
118
120
|
process.exit(1);
|
|
119
121
|
}
|
|
120
122
|
else {
|
|
121
123
|
logger_1.logger.warn(outcomeMessage('No fatal errors were found, however please review the following warnings.'));
|
|
122
|
-
logger_1.logger.warn(`\n${
|
|
124
|
+
logger_1.logger.warn(`\n${prettyResult}\n\n`);
|
|
123
125
|
logger_1.logger.warn(outcomeMessage('No fatal errors were found, however please review the above warnings.'));
|
|
124
126
|
}
|
|
125
127
|
}
|
|
126
128
|
else {
|
|
127
129
|
logger_1.logger.info(outcomeMessage('SUCCESS! hi-fi codebase detected.'));
|
|
128
|
-
logger_1.logger.info(
|
|
129
|
-
logger_1.logger.info(`\n${
|
|
130
|
+
logger_1.logger.info(resultString);
|
|
131
|
+
logger_1.logger.info(`\n${prettyResult}\n\n`);
|
|
130
132
|
logger_1.logger.info(outcomeMessage('SUCCESS! hi-fi codebase detected.'));
|
|
131
133
|
}
|
|
132
134
|
}
|
|
133
135
|
}
|
|
134
136
|
}
|
|
135
137
|
catch (e) {
|
|
138
|
+
logger_1.logger.error(e, `Error during execution ${JSON.stringify(e.message)} \n ${e.stack}`);
|
|
136
139
|
yield handleError(e).then(() => {
|
|
137
140
|
// give some time async ops to finish if not handled directly
|
|
138
141
|
if (process.env.NODE_ENV !== 'test') {
|
package/dist/operators/index.js
CHANGED
|
@@ -14,6 +14,7 @@ const outdatedFramework_1 = require("./outdatedFramework");
|
|
|
14
14
|
const fileContains_1 = require("./fileContains");
|
|
15
15
|
const nonStandardDirectoryStructure_1 = require("./nonStandardDirectoryStructure");
|
|
16
16
|
const openaiAnalysisHighSeverity_1 = require("./openaiAnalysisHighSeverity");
|
|
17
|
+
const regexMatch_1 = require("./regexMatch");
|
|
17
18
|
const openaiUtils_1 = require("../utils/openaiUtils");
|
|
18
19
|
const logger_1 = require("../utils/logger");
|
|
19
20
|
const pluginRegistry_1 = require("../core/pluginRegistry");
|
|
@@ -23,7 +24,8 @@ function loadOperators(operatorNames) {
|
|
|
23
24
|
const allAvailableOperators = Object.assign({ outdatedFramework: outdatedFramework_1.outdatedFramework,
|
|
24
25
|
fileContains: fileContains_1.fileContains,
|
|
25
26
|
nonStandardDirectoryStructure: nonStandardDirectoryStructure_1.nonStandardDirectoryStructure,
|
|
26
|
-
openaiAnalysisHighSeverity: openaiAnalysisHighSeverity_1.openaiAnalysisHighSeverity
|
|
27
|
+
openaiAnalysisHighSeverity: openaiAnalysisHighSeverity_1.openaiAnalysisHighSeverity,
|
|
28
|
+
regexMatch: regexMatch_1.regexMatch }, Object.fromEntries(pluginRegistry_1.pluginRegistry.getPluginOperators().map(op => [op.name, op])));
|
|
27
29
|
const openAIStatus = (0, openaiUtils_1.getOpenAIStatus)();
|
|
28
30
|
logger_1.logger.info(`Loading operators: ${operatorNames.join(', ')}`);
|
|
29
31
|
const loadedOperators = [];
|
|
@@ -16,7 +16,7 @@ const nonStandardDirectoryStructure = {
|
|
|
16
16
|
if (filePath !== configManager_1.REPO_GLOBAL_CHECK) {
|
|
17
17
|
return false;
|
|
18
18
|
}
|
|
19
|
-
logger_1.logger.
|
|
19
|
+
logger_1.logger.info(`running global directory structure analysis..`);
|
|
20
20
|
const repoPath = cli_1.options.dir || process.cwd();
|
|
21
21
|
let result = false;
|
|
22
22
|
// check the directory structure of the repo against the standard structure
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { OperatorDefn } from '../types/typeDefs';
|
|
2
|
+
/**
|
|
3
|
+
* Operator that tests if a string matches a regular expression pattern
|
|
4
|
+
*
|
|
5
|
+
* @param factValue - The string to test against the regex pattern
|
|
6
|
+
* @param regexPattern - The regex pattern to test against (as a string)
|
|
7
|
+
* @returns true if the string matches the pattern, false otherwise
|
|
8
|
+
*/
|
|
9
|
+
declare const regexMatch: OperatorDefn;
|
|
10
|
+
export { regexMatch };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.regexMatch = void 0;
|
|
4
|
+
const logger_1 = require("../utils/logger");
|
|
5
|
+
/**
|
|
6
|
+
* Operator that tests if a string matches a regular expression pattern
|
|
7
|
+
*
|
|
8
|
+
* @param factValue - The string to test against the regex pattern
|
|
9
|
+
* @param regexPattern - The regex pattern to test against (as a string)
|
|
10
|
+
* @returns true if the string matches the pattern, false otherwise
|
|
11
|
+
*/
|
|
12
|
+
const regexMatch = {
|
|
13
|
+
'name': 'regexMatch',
|
|
14
|
+
'fn': (factValue, regexPattern) => {
|
|
15
|
+
try {
|
|
16
|
+
logger_1.logger.debug(`regexMatch: testing ${factValue} against pattern ${regexPattern}`);
|
|
17
|
+
if (factValue === undefined || factValue === null) {
|
|
18
|
+
logger_1.logger.debug('regexMatch: factValue is undefined or null');
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
if (typeof regexPattern !== 'string') {
|
|
22
|
+
logger_1.logger.debug(`regexMatch: regexPattern is not a string: ${typeof regexPattern}`);
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
// Extract flags if they exist (pattern/flags format)
|
|
26
|
+
let flags = '';
|
|
27
|
+
let pattern = regexPattern;
|
|
28
|
+
// Check if the pattern is in /pattern/flags format
|
|
29
|
+
const regexFormatMatch = /^\/(.+)\/([gimsuyd]*)$/.exec(regexPattern);
|
|
30
|
+
if (regexFormatMatch) {
|
|
31
|
+
pattern = regexFormatMatch[1];
|
|
32
|
+
flags = regexFormatMatch[2];
|
|
33
|
+
logger_1.logger.debug(`regexMatch: extracted pattern "${pattern}" with flags "${flags}"`);
|
|
34
|
+
}
|
|
35
|
+
const regex = new RegExp(pattern, flags);
|
|
36
|
+
const result = regex.test(String(factValue));
|
|
37
|
+
logger_1.logger.debug(`regexMatch: result is ${result}`);
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
logger_1.logger.error(`regexMatch error: ${e}`);
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
exports.regexMatch = regexMatch;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const regexMatch_1 = require("./regexMatch");
|
|
4
|
+
const logger_1 = require("../utils/logger");
|
|
5
|
+
jest.mock('../utils/logger', () => ({
|
|
6
|
+
logger: {
|
|
7
|
+
debug: jest.fn(),
|
|
8
|
+
error: jest.fn()
|
|
9
|
+
},
|
|
10
|
+
}));
|
|
11
|
+
describe('regexMatch', () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
jest.clearAllMocks();
|
|
14
|
+
});
|
|
15
|
+
it('should return true when the string matches the pattern', () => {
|
|
16
|
+
expect(regexMatch_1.regexMatch.fn('hello world', 'hello')).toBe(true);
|
|
17
|
+
expect(regexMatch_1.regexMatch.fn('hello world', 'world$')).toBe(true);
|
|
18
|
+
expect(regexMatch_1.regexMatch.fn('hello world', '^hello')).toBe(true);
|
|
19
|
+
expect(logger_1.logger.debug).toHaveBeenCalledWith(expect.stringContaining('result is true'));
|
|
20
|
+
});
|
|
21
|
+
it('should return false when the string does not match the pattern', () => {
|
|
22
|
+
expect(regexMatch_1.regexMatch.fn('hello world', 'goodbye')).toBe(false);
|
|
23
|
+
expect(regexMatch_1.regexMatch.fn('hello world', '^world')).toBe(false);
|
|
24
|
+
expect(regexMatch_1.regexMatch.fn('hello world', 'hello$')).toBe(false);
|
|
25
|
+
expect(logger_1.logger.debug).toHaveBeenCalledWith(expect.stringContaining('result is false'));
|
|
26
|
+
});
|
|
27
|
+
it('should handle regex flags correctly', () => {
|
|
28
|
+
expect(regexMatch_1.regexMatch.fn('Hello World', '/hello/i')).toBe(true);
|
|
29
|
+
expect(regexMatch_1.regexMatch.fn('Hello\nWorld', '/hello.*world/is')).toBe(true);
|
|
30
|
+
expect(regexMatch_1.regexMatch.fn('Hello World', '/hello world/i')).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
it('should handle undefined or null factValue', () => {
|
|
33
|
+
expect(regexMatch_1.regexMatch.fn(undefined, 'test')).toBe(false);
|
|
34
|
+
expect(regexMatch_1.regexMatch.fn(null, 'test')).toBe(false);
|
|
35
|
+
expect(logger_1.logger.debug).toHaveBeenCalledWith('regexMatch: factValue is undefined or null');
|
|
36
|
+
});
|
|
37
|
+
it('should handle non-string regexPattern', () => {
|
|
38
|
+
expect(regexMatch_1.regexMatch.fn('test', null)).toBe(false);
|
|
39
|
+
expect(regexMatch_1.regexMatch.fn('test', undefined)).toBe(false);
|
|
40
|
+
expect(regexMatch_1.regexMatch.fn('test', 123)).toBe(false);
|
|
41
|
+
expect(logger_1.logger.debug).toHaveBeenCalledWith(expect.stringContaining('regexPattern is not a string'));
|
|
42
|
+
});
|
|
43
|
+
it('should handle invalid regex patterns gracefully', () => {
|
|
44
|
+
expect(regexMatch_1.regexMatch.fn('test', '[')).toBe(false);
|
|
45
|
+
expect(logger_1.logger.error).toHaveBeenCalledWith(expect.stringContaining('regexMatch error'));
|
|
46
|
+
});
|
|
47
|
+
it('should handle non-string factValues by converting them to strings', () => {
|
|
48
|
+
expect(regexMatch_1.regexMatch.fn(123, '123')).toBe(true);
|
|
49
|
+
expect(regexMatch_1.regexMatch.fn(true, 'true')).toBe(true);
|
|
50
|
+
expect(regexMatch_1.regexMatch.fn({ toString: () => 'custom' }, 'custom')).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
});
|
package/dist/types/typeDefs.d.ts
CHANGED
|
@@ -128,6 +128,7 @@ export interface BasicTelemetryMetadata {
|
|
|
128
128
|
telemetryData?: TelemetryData;
|
|
129
129
|
errorMessage?: string;
|
|
130
130
|
options?: any;
|
|
131
|
+
errorStack?: string;
|
|
131
132
|
}
|
|
132
133
|
export interface ExemptionTelemetryMetadata {
|
|
133
134
|
repoUrl: string;
|
|
@@ -168,6 +169,8 @@ export interface ResultMetadata {
|
|
|
168
169
|
telemetryData: TelemetryData;
|
|
169
170
|
options: any;
|
|
170
171
|
repoXFIConfig: RepoXFIConfig;
|
|
172
|
+
memoryUsage: any;
|
|
173
|
+
repoUrl: string;
|
|
171
174
|
};
|
|
172
175
|
}
|
|
173
176
|
export interface TelemetryData {
|
package/dist/utils/logger.js
CHANGED
|
@@ -18,7 +18,7 @@ const maskSensitiveData_1 = require("./maskSensitiveData");
|
|
|
18
18
|
let loggerInstance;
|
|
19
19
|
let loglevel = process.env.XFI_LOG_LEVEL ||
|
|
20
20
|
(process.env.NODE_ENV === 'test' ? 'silent' : 'info');
|
|
21
|
-
let logPrefix;
|
|
21
|
+
let logPrefix = generateLogPrefix();
|
|
22
22
|
// Initialize logger and prefix immediately
|
|
23
23
|
function initializeLogger() {
|
|
24
24
|
logPrefix = generateLogPrefix();
|
|
@@ -29,15 +29,9 @@ function generateLogPrefix() {
|
|
|
29
29
|
}
|
|
30
30
|
function resetLogPrefix() {
|
|
31
31
|
logPrefix = generateLogPrefix();
|
|
32
|
-
if (loggerInstance) {
|
|
33
|
-
loggerInstance = loggerInstance.child({ prefix: logPrefix });
|
|
34
|
-
}
|
|
35
32
|
}
|
|
36
33
|
function setLogPrefix(prefix) {
|
|
37
34
|
logPrefix = prefix;
|
|
38
|
-
if (loggerInstance) {
|
|
39
|
-
loggerInstance = loggerInstance.child({ prefix: logPrefix });
|
|
40
|
-
}
|
|
41
35
|
}
|
|
42
36
|
function getLogPrefix() {
|
|
43
37
|
return logPrefix;
|
|
@@ -54,21 +48,22 @@ function getLogger(force) {
|
|
|
54
48
|
target: 'pino-pretty',
|
|
55
49
|
options: {
|
|
56
50
|
loglevel: loglevel,
|
|
51
|
+
sync: false,
|
|
57
52
|
colorize: true,
|
|
58
53
|
translateTime: 'SYS:yyyy-mm-dd HH:MM:ss.l o',
|
|
59
54
|
ignore: 'pid,hostname',
|
|
60
|
-
messageFormat: '{prefix} - {msg}',
|
|
61
55
|
singleLine: true,
|
|
62
56
|
errorProps: '*'
|
|
63
57
|
}
|
|
64
58
|
});
|
|
65
59
|
const loggerOptions = {
|
|
66
60
|
timestamp: pino_1.default.stdTimeFunctions.isoTime,
|
|
61
|
+
msgPrefix: `${logPrefix} - `,
|
|
67
62
|
level: 'info',
|
|
68
63
|
formatters: {
|
|
69
64
|
level: (label) => ({ level: label }),
|
|
70
65
|
bindings: (bindings) => bindings,
|
|
71
|
-
log: (object) => (Object.assign({
|
|
66
|
+
log: (object) => (Object.assign({}, object))
|
|
72
67
|
},
|
|
73
68
|
serializers: {
|
|
74
69
|
err: (err) => pino_1.default.stdSerializers.err,
|
package/dist/xfidelity
CHANGED
|
@@ -54,7 +54,6 @@ exports.main = main;
|
|
|
54
54
|
const logger_1 = require("./utils/logger");
|
|
55
55
|
const cli_1 = require("./core/cli");
|
|
56
56
|
if (require.main === module) {
|
|
57
|
-
(0, logger_1.initializeLogger)();
|
|
58
57
|
(0, logger_1.setLogLevel)(process.env.XFI_LOG_LEVEL || 'info');
|
|
59
58
|
(0, cli_1.initCLI)();
|
|
60
59
|
}
|
|
@@ -71,7 +70,8 @@ const handleError = (error) => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
71
70
|
archetype: cli_1.options.archetype,
|
|
72
71
|
repoPath: cli_1.options.dir,
|
|
73
72
|
options: cli_1.options,
|
|
74
|
-
errorMessage: error.message
|
|
73
|
+
errorMessage: error.message,
|
|
74
|
+
errorStack: error.stack
|
|
75
75
|
},
|
|
76
76
|
timestamp: new Date().toISOString()
|
|
77
77
|
}, executionLogPrefix);
|
|
@@ -101,38 +101,41 @@ function main() {
|
|
|
101
101
|
localConfigPath: cli_1.options.localConfigPath,
|
|
102
102
|
executionLogPrefix
|
|
103
103
|
});
|
|
104
|
+
const resultString = JSON.stringify(resultMetadata);
|
|
105
|
+
const prettyResult = prettyjson_1.default.render(resultMetadata.XFI_RESULT);
|
|
104
106
|
// if results are found, there were issues found in the codebase
|
|
105
107
|
if (resultMetadata.XFI_RESULT.totalIssues > 0) {
|
|
106
108
|
logger_1.logger.warn(`WARNING: lo-fi attributes detected in codebase. ${resultMetadata.XFI_RESULT.warningCount} are warnings, ${resultMetadata.XFI_RESULT.fatalityCount} are fatal.`);
|
|
107
|
-
logger_1.logger.warn(
|
|
109
|
+
logger_1.logger.warn(resultString);
|
|
108
110
|
if (resultMetadata.XFI_RESULT.errorCount > 0) {
|
|
109
111
|
logger_1.logger.error(outcomeMessage(`THERE WERE ${resultMetadata.XFI_RESULT.errorCount} UNEXPECTED ERRORS!`));
|
|
110
|
-
logger_1.logger.error(`\n${
|
|
112
|
+
logger_1.logger.error(`\n${prettyResult}\n\n`);
|
|
111
113
|
logger_1.logger.error(outcomeMessage(`THERE WERE ${resultMetadata.XFI_RESULT.errorCount} UNEXPECTED ERRORS!`));
|
|
112
114
|
process.exit(1);
|
|
113
115
|
}
|
|
114
116
|
if (resultMetadata.XFI_RESULT.fatalityCount > 0) {
|
|
115
117
|
logger_1.logger.error(outcomeMessage(`THERE WERE ${resultMetadata.XFI_RESULT.fatalityCount} FATAL ERRORS DETECTED TO BE IMMEDIATELY ADDRESSED!`));
|
|
116
|
-
logger_1.logger.error(`\n${
|
|
118
|
+
logger_1.logger.error(`\n${prettyResult}\n\n`);
|
|
117
119
|
logger_1.logger.error(outcomeMessage(`THERE WERE ${resultMetadata.XFI_RESULT.fatalityCount} FATAL ERRORS DETECTED TO BE IMMEDIATELY ADDRESSED!`));
|
|
118
120
|
process.exit(1);
|
|
119
121
|
}
|
|
120
122
|
else {
|
|
121
123
|
logger_1.logger.warn(outcomeMessage('No fatal errors were found, however please review the following warnings.'));
|
|
122
|
-
logger_1.logger.warn(`\n${
|
|
124
|
+
logger_1.logger.warn(`\n${prettyResult}\n\n`);
|
|
123
125
|
logger_1.logger.warn(outcomeMessage('No fatal errors were found, however please review the above warnings.'));
|
|
124
126
|
}
|
|
125
127
|
}
|
|
126
128
|
else {
|
|
127
129
|
logger_1.logger.info(outcomeMessage('SUCCESS! hi-fi codebase detected.'));
|
|
128
|
-
logger_1.logger.info(
|
|
129
|
-
logger_1.logger.info(`\n${
|
|
130
|
+
logger_1.logger.info(resultString);
|
|
131
|
+
logger_1.logger.info(`\n${prettyResult}\n\n`);
|
|
130
132
|
logger_1.logger.info(outcomeMessage('SUCCESS! hi-fi codebase detected.'));
|
|
131
133
|
}
|
|
132
134
|
}
|
|
133
135
|
}
|
|
134
136
|
}
|
|
135
137
|
catch (e) {
|
|
138
|
+
logger_1.logger.error(e, `Error during execution ${JSON.stringify(e.message)} \n ${e.stack}`);
|
|
136
139
|
yield handleError(e).then(() => {
|
|
137
140
|
// give some time async ops to finish if not handled directly
|
|
138
141
|
if (process.env.NODE_ENV !== 'test') {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "x-fidelity",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "cli for opinionated framework adherence checks",
|
|
5
5
|
"main": "dist/index",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"author": "wyvern8 <io@zoto.io>",
|
|
41
41
|
"license": "MIT",
|
|
42
42
|
"engines": {
|
|
43
|
-
"node": ">=
|
|
43
|
+
"node": ">=18.0.0",
|
|
44
44
|
"yarn": ">=1.22.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
package/src/core/cli.ts
CHANGED
|
@@ -11,6 +11,19 @@ export const options = program.opts();
|
|
|
11
11
|
|
|
12
12
|
export function initCLI() {
|
|
13
13
|
|
|
14
|
+
const bannerArt = `\n
|
|
15
|
+
=====================================
|
|
16
|
+
__ __ ________ ______
|
|
17
|
+
| ## | ## | ######## \\######
|
|
18
|
+
\\##\\/ ## ______ | ##__ | ##
|
|
19
|
+
>## ## | \\| ## \\ | ##
|
|
20
|
+
/ ####\\ \\######| ###### | ##
|
|
21
|
+
| ## \\##\\ | ## _| ##_
|
|
22
|
+
| ## | ## | ## | ## \\
|
|
23
|
+
\\## \\## \\## \\######
|
|
24
|
+
-------------------------------------
|
|
25
|
+
`;
|
|
26
|
+
|
|
14
27
|
// Ensure logger is initialized
|
|
15
28
|
if (!logger || typeof logger.info !== 'function') {
|
|
16
29
|
console.error({ msg: 'Logger is not properly initialized' });
|
|
@@ -37,7 +50,7 @@ export function initCLI() {
|
|
|
37
50
|
});
|
|
38
51
|
|
|
39
52
|
program
|
|
40
|
-
.option("-d, --dir <directory>", "
|
|
53
|
+
.option("-d, --dir <directory>", "local git repo directory path to analyze. equivalent of directory argument")
|
|
41
54
|
.option("-a, --archetype <archetype>", "The archetype to use for analysis", "node-fullstack")
|
|
42
55
|
.option("-c, --configServer <configServer>", "The config server URL for fetching remote archetype configurations and rules. This takes precedence over localConfigPath.")
|
|
43
56
|
.option("-o, --openaiEnabled <boolean>", "Enable OpenAI analysis", false)
|
|
@@ -50,8 +63,11 @@ export function initCLI() {
|
|
|
50
63
|
.option("-x, --examine <archetype>", "Examine the archetype configuration and rules")
|
|
51
64
|
.version(version, "-v, --version", "Output the version number of xfidelity")
|
|
52
65
|
.helpOption("-h, --help", "Display help for command")
|
|
53
|
-
.
|
|
54
|
-
|
|
66
|
+
.summary("CLI for analyzing codebases for architectural fidelity")
|
|
67
|
+
.usage("[directory] [options]")
|
|
68
|
+
.argument('[directory]', 'local git repo directory path to analyze')
|
|
69
|
+
.addHelpText('before', bannerArt)
|
|
70
|
+
.addHelpText('after', '-------------------------------------');
|
|
55
71
|
|
|
56
72
|
|
|
57
73
|
program.parse(process.argv);
|
|
@@ -88,18 +104,18 @@ export function initCLI() {
|
|
|
88
104
|
if (process.env.NODE_ENV !== 'test') program.error(`LocalConfigPath does not exist or is invalid: ${error}`);
|
|
89
105
|
}
|
|
90
106
|
|
|
91
|
-
const bannerArt = `\n
|
|
92
|
-
=====================================
|
|
93
|
-
|
|
94
|
-
| ## | ## | ######## \\######
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
| ## \\##\\ | ## _| ##_
|
|
99
|
-
| ## | ## | ## | ## \\
|
|
100
|
-
|
|
101
|
-
-------------------------------------
|
|
102
|
-
`;
|
|
107
|
+
// const bannerArt = `\n
|
|
108
|
+
// =====================================
|
|
109
|
+
// __ __ ________ ______
|
|
110
|
+
// | ## | ## | ######## \\######
|
|
111
|
+
// \\##\\/ ## ______ | ##__ | ##
|
|
112
|
+
// >## ## | \\| ## \\ | ##
|
|
113
|
+
// / ####\\ \\######| ###### | ##
|
|
114
|
+
// | ## \\##\\ | ## _| ##_
|
|
115
|
+
// | ## | ## | ## | ## \\
|
|
116
|
+
// \\## \\## \\## \\######
|
|
117
|
+
// -------------------------------------
|
|
118
|
+
// `;
|
|
103
119
|
|
|
104
120
|
logger.info(bannerArt);
|
|
105
121
|
|
|
@@ -108,15 +108,17 @@ describe('analyzeCodebase', () => {
|
|
|
108
108
|
XFI_RESULT: expect.objectContaining({
|
|
109
109
|
archetype: 'node-fullstack',
|
|
110
110
|
repoPath: 'mockRepoPath',
|
|
111
|
-
fileCount:
|
|
111
|
+
fileCount: expect.any(Number),
|
|
112
112
|
totalIssues: expect.any(Number),
|
|
113
113
|
warningCount: expect.any(Number),
|
|
114
114
|
fatalityCount: expect.any(Number),
|
|
115
|
+
errorCount: expect.any(Number),
|
|
115
116
|
exemptCount: expect.any(Number),
|
|
116
117
|
issueDetails: expect.any(Array),
|
|
117
118
|
durationSeconds: expect.any(Number),
|
|
118
119
|
finishTime: expect.any(Number),
|
|
119
120
|
startTime: expect.any(Number),
|
|
121
|
+
memoryUsage: expect.any(Object),
|
|
120
122
|
options: expect.objectContaining({
|
|
121
123
|
archetype: 'node-fullstack',
|
|
122
124
|
configServer: '',
|
|
@@ -134,6 +136,7 @@ describe('analyzeCodebase', () => {
|
|
|
134
136
|
startTime: expect.any(Number),
|
|
135
137
|
userInfo: expect.any(Object),
|
|
136
138
|
}),
|
|
139
|
+
repoUrl: expect.any(String),
|
|
137
140
|
repoXFIConfig: expect.objectContaining({
|
|
138
141
|
sensitiveFileFalsePositives: expect.any(Array),
|
|
139
142
|
})
|
|
@@ -184,7 +187,7 @@ describe('analyzeCodebase', () => {
|
|
|
184
187
|
XFI_RESULT: expect.objectContaining({
|
|
185
188
|
archetype: 'node-fullstack',
|
|
186
189
|
repoPath: 'mockRepoPath',
|
|
187
|
-
fileCount:
|
|
190
|
+
fileCount: expect.any(Number),
|
|
188
191
|
totalIssues: 3,
|
|
189
192
|
warningCount: 0,
|
|
190
193
|
fatalityCount: 0,
|
|
@@ -209,8 +212,10 @@ describe('analyzeCodebase', () => {
|
|
|
209
212
|
durationSeconds: expect.any(Number),
|
|
210
213
|
finishTime: expect.any(Number),
|
|
211
214
|
startTime: expect.any(Number),
|
|
215
|
+
memoryUsage: expect.any(Object),
|
|
212
216
|
options: expect.any(Object),
|
|
213
217
|
telemetryData: expect.any(Object),
|
|
218
|
+
repoUrl: expect.any(String),
|
|
214
219
|
repoXFIConfig: expect.objectContaining({
|
|
215
220
|
sensitiveFileFalsePositives: expect.any(Array)
|
|
216
221
|
})
|
|
@@ -326,7 +331,7 @@ describe('analyzeCodebase', () => {
|
|
|
326
331
|
XFI_RESULT: expect.objectContaining({
|
|
327
332
|
archetype: 'node-fullstack',
|
|
328
333
|
repoPath: 'mockRepoPath',
|
|
329
|
-
fileCount:
|
|
334
|
+
fileCount: expect.any(Number),
|
|
330
335
|
totalIssues: 3,
|
|
331
336
|
warningCount: 0,
|
|
332
337
|
fatalityCount: 3,
|
|
@@ -342,8 +347,11 @@ describe('analyzeCodebase', () => {
|
|
|
342
347
|
durationSeconds: expect.any(Number),
|
|
343
348
|
finishTime: expect.any(Number),
|
|
344
349
|
startTime: expect.any(Number),
|
|
350
|
+
memoryUsage: expect.any(Object),
|
|
345
351
|
options: expect.any(Object),
|
|
346
352
|
telemetryData: expect.any(Object),
|
|
353
|
+
repoUrl: expect.any(String),
|
|
354
|
+
repoXFIConfig: expect.any(Object)
|
|
347
355
|
})
|
|
348
356
|
});
|
|
349
357
|
expect(sendTelemetry).toHaveBeenCalledTimes(2); // Start and end
|
|
@@ -95,7 +95,7 @@ export async function analyzeCodebase(params: AnalyzeCodebaseParams): Promise<Re
|
|
|
95
95
|
logger.info(finishMsg);
|
|
96
96
|
|
|
97
97
|
const totalFailureCount = countRuleFailures(failures);
|
|
98
|
-
logger.info(`${fileData.length} files analyzed. ${totalFailureCount} rule failures.`);
|
|
98
|
+
logger.info(`${fileData.length -1} files analyzed. ${totalFailureCount} rule failures.`);
|
|
99
99
|
|
|
100
100
|
const fatalityCount = countRuleFailures(failures, 'fatality');
|
|
101
101
|
const warningCount = countRuleFailures(failures, 'warning');
|
|
@@ -103,17 +103,21 @@ export async function analyzeCodebase(params: AnalyzeCodebaseParams): Promise<Re
|
|
|
103
103
|
const errorCount = countRuleFailures(failures, 'error');
|
|
104
104
|
|
|
105
105
|
const finishTime = new Date().getTime();
|
|
106
|
+
const memoryUsage = process.memoryUsage();
|
|
107
|
+
|
|
108
|
+
logger.info('Assemblying result metadata..');
|
|
106
109
|
|
|
107
110
|
const resultMetadata: ResultMetadata = {
|
|
108
111
|
XFI_RESULT: {
|
|
109
112
|
archetype,
|
|
110
113
|
telemetryData,
|
|
114
|
+
memoryUsage,
|
|
111
115
|
repoXFIConfig: repoXFIConfig,
|
|
112
116
|
issueDetails: failures,
|
|
113
117
|
startTime: telemetryData.startTime,
|
|
114
118
|
finishTime: finishTime,
|
|
115
119
|
durationSeconds: (finishTime - telemetryData.startTime) / 1000,
|
|
116
|
-
fileCount: fileData.length,
|
|
120
|
+
fileCount: fileData.length -1,
|
|
117
121
|
totalIssues: totalFailureCount,
|
|
118
122
|
warningCount: warningCount,
|
|
119
123
|
fatalityCount: fatalityCount,
|
|
@@ -121,6 +125,7 @@ export async function analyzeCodebase(params: AnalyzeCodebaseParams): Promise<Re
|
|
|
121
125
|
exemptCount: exemptCount,
|
|
122
126
|
options,
|
|
123
127
|
repoPath,
|
|
128
|
+
repoUrl
|
|
124
129
|
}
|
|
125
130
|
}
|
|
126
131
|
|
|
@@ -10,13 +10,15 @@ export async function runEngineOnFiles(params: RunEngineOnFilesParams): Promise<
|
|
|
10
10
|
const msg = `\n==========================\nRUNNING FILE CHECKS..\n==========================`;
|
|
11
11
|
logger.info(msg);
|
|
12
12
|
const failures: ScanResult[] = [];
|
|
13
|
+
const fileCount = fileData.length;
|
|
13
14
|
|
|
14
|
-
for (
|
|
15
|
+
for (let i=0; i < fileCount; i++) {
|
|
16
|
+
const file = fileData[i]
|
|
15
17
|
if (file.fileName === REPO_GLOBAL_CHECK) {
|
|
16
18
|
const msg = `\n==========================\nRUNNING GLOBAL REPO CHECKS..\n==========================`;
|
|
17
19
|
logger.info(msg);
|
|
18
20
|
} else {
|
|
19
|
-
const msg = `analysing ${file.filePath} ...`
|
|
21
|
+
const msg = `analysing (${i+1} of ${fileCount-1}) ${file.filePath} ...`
|
|
20
22
|
logger.info(msg);
|
|
21
23
|
}
|
|
22
24
|
const facts = {
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"openaiAnalysisTop5-global",
|
|
9
9
|
"openaiAnalysisA11y-global",
|
|
10
10
|
"invalidSystemIdConfigured-iterative",
|
|
11
|
-
"missingRequiredFiles-global"
|
|
11
|
+
"missingRequiredFiles-global",
|
|
12
|
+
"regexMatch-example-iterative"
|
|
12
13
|
],
|
|
13
14
|
"operators": [
|
|
14
15
|
"fileContains",
|
|
@@ -16,7 +17,8 @@
|
|
|
16
17
|
"nonStandardDirectoryStructure",
|
|
17
18
|
"openaiAnalysisHighSeverity",
|
|
18
19
|
"invalidRemoteValidation",
|
|
19
|
-
"missingRequiredFiles"
|
|
20
|
+
"missingRequiredFiles",
|
|
21
|
+
"regexMatch"
|
|
20
22
|
],
|
|
21
23
|
"facts": [
|
|
22
24
|
"repoFilesystemFacts",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "regexMatch-example",
|
|
3
|
+
"conditions": {
|
|
4
|
+
"all": [
|
|
5
|
+
{
|
|
6
|
+
"fact": "fileData",
|
|
7
|
+
"path": "$.filePath",
|
|
8
|
+
"operator": "regexMatch",
|
|
9
|
+
"value": ".*/facts/.*"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"fact": "fileData",
|
|
13
|
+
"path": "$.fileContent",
|
|
14
|
+
"operator": "regexMatch",
|
|
15
|
+
"value": "OpenAI"
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
"event": {
|
|
20
|
+
"type": "warning",
|
|
21
|
+
"params": {
|
|
22
|
+
"message": "I'm seeing a pattern here..",
|
|
23
|
+
"details": {
|
|
24
|
+
"suggestion": "wake up and smell the coffee"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -25,7 +25,6 @@ export async function collectLocalDependencies(): Promise<LocalDependencies[]> {
|
|
|
25
25
|
} else {
|
|
26
26
|
logger.error('No yarn.lock or package-lock.json found');
|
|
27
27
|
process.exit(1);
|
|
28
|
-
throw new Error('Unsupported package manager');
|
|
29
28
|
}
|
|
30
29
|
logger.trace(`collectLocalDependencies: ${safeStringify(result)}`);
|
|
31
30
|
return result;
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { logger,
|
|
2
|
+
import { logger, getLogPrefix, setLogLevel } from './utils/logger';
|
|
3
3
|
import { options, initCLI } from "./core/cli";
|
|
4
4
|
if (require.main === module) {
|
|
5
|
-
initializeLogger()
|
|
6
5
|
setLogLevel(process.env.XFI_LOG_LEVEL || 'info');
|
|
7
6
|
initCLI();
|
|
8
7
|
}
|
|
@@ -23,7 +22,8 @@ const handleError = async (error: Error) => {
|
|
|
23
22
|
archetype: options.archetype,
|
|
24
23
|
repoPath: options.dir,
|
|
25
24
|
options,
|
|
26
|
-
errorMessage: error.message
|
|
25
|
+
errorMessage: error.message,
|
|
26
|
+
errorStack: error.stack
|
|
27
27
|
},
|
|
28
28
|
timestamp: new Date().toISOString()
|
|
29
29
|
}, executionLogPrefix);
|
|
@@ -54,43 +54,47 @@ export async function main() {
|
|
|
54
54
|
executionLogPrefix
|
|
55
55
|
});
|
|
56
56
|
|
|
57
|
+
const resultString = JSON.stringify(resultMetadata);
|
|
58
|
+
const prettyResult = json.render(resultMetadata.XFI_RESULT);
|
|
59
|
+
|
|
57
60
|
// if results are found, there were issues found in the codebase
|
|
58
61
|
if (resultMetadata.XFI_RESULT.totalIssues > 0) {
|
|
59
62
|
logger.warn(`WARNING: lo-fi attributes detected in codebase. ${resultMetadata.XFI_RESULT.warningCount} are warnings, ${resultMetadata.XFI_RESULT.fatalityCount} are fatal.`);
|
|
60
|
-
logger.warn(
|
|
63
|
+
logger.warn(resultString);
|
|
61
64
|
|
|
62
65
|
if (resultMetadata.XFI_RESULT.errorCount > 0) {
|
|
63
66
|
logger.error(outcomeMessage(`THERE WERE ${resultMetadata.XFI_RESULT.errorCount} UNEXPECTED ERRORS!`));
|
|
64
|
-
logger.error(`\n${
|
|
67
|
+
logger.error(`\n${prettyResult}\n\n`);
|
|
65
68
|
logger.error(outcomeMessage(`THERE WERE ${resultMetadata.XFI_RESULT.errorCount} UNEXPECTED ERRORS!`));
|
|
66
69
|
process.exit(1);
|
|
67
70
|
}
|
|
68
71
|
|
|
69
72
|
if (resultMetadata.XFI_RESULT.fatalityCount > 0) {
|
|
70
73
|
logger.error(outcomeMessage(`THERE WERE ${resultMetadata.XFI_RESULT.fatalityCount} FATAL ERRORS DETECTED TO BE IMMEDIATELY ADDRESSED!`));
|
|
71
|
-
logger.error(`\n${
|
|
74
|
+
logger.error(`\n${prettyResult}\n\n`);
|
|
72
75
|
logger.error(outcomeMessage(`THERE WERE ${resultMetadata.XFI_RESULT.fatalityCount} FATAL ERRORS DETECTED TO BE IMMEDIATELY ADDRESSED!`));
|
|
73
76
|
process.exit(1);
|
|
74
77
|
} else {
|
|
75
78
|
logger.warn(outcomeMessage('No fatal errors were found, however please review the following warnings.'));
|
|
76
|
-
logger.warn(`\n${
|
|
79
|
+
logger.warn(`\n${prettyResult}\n\n`);
|
|
77
80
|
logger.warn(outcomeMessage('No fatal errors were found, however please review the above warnings.'));
|
|
78
81
|
}
|
|
79
82
|
} else {
|
|
80
83
|
logger.info(outcomeMessage('SUCCESS! hi-fi codebase detected.'));
|
|
81
|
-
logger.info(
|
|
82
|
-
logger.info(`\n${
|
|
84
|
+
logger.info(resultString);
|
|
85
|
+
logger.info(`\n${prettyResult}\n\n`);
|
|
83
86
|
logger.info(outcomeMessage('SUCCESS! hi-fi codebase detected.'));
|
|
84
87
|
}
|
|
85
88
|
}
|
|
86
89
|
}
|
|
87
90
|
} catch (e: any) {
|
|
91
|
+
logger.error(e, `Error during execution ${JSON.stringify(e.message)} \n ${e.stack}`);
|
|
88
92
|
await handleError(e).then(() => {
|
|
89
93
|
// give some time async ops to finish if not handled directly
|
|
90
94
|
if (process.env.NODE_ENV !== 'test') {
|
|
91
|
-
setTimeout(() => {
|
|
95
|
+
setTimeout(() => { //todo fix this
|
|
92
96
|
process.exit(1);
|
|
93
|
-
},
|
|
97
|
+
}, 3000);
|
|
94
98
|
}
|
|
95
99
|
});
|
|
96
100
|
}
|
package/src/operators/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { outdatedFramework } from './outdatedFramework';
|
|
|
3
3
|
import { fileContains } from './fileContains';
|
|
4
4
|
import { nonStandardDirectoryStructure } from './nonStandardDirectoryStructure';
|
|
5
5
|
import { openaiAnalysisHighSeverity } from './openaiAnalysisHighSeverity';
|
|
6
|
+
import { regexMatch } from './regexMatch';
|
|
6
7
|
import { getOpenAIStatus } from '../utils/openaiUtils';
|
|
7
8
|
import { logger } from '../utils/logger';
|
|
8
9
|
import { pluginRegistry } from '../core/pluginRegistry';
|
|
@@ -14,6 +15,7 @@ async function loadOperators(operatorNames: string[]): Promise<OperatorDefn[]> {
|
|
|
14
15
|
fileContains,
|
|
15
16
|
nonStandardDirectoryStructure,
|
|
16
17
|
openaiAnalysisHighSeverity,
|
|
18
|
+
regexMatch,
|
|
17
19
|
...Object.fromEntries(
|
|
18
20
|
pluginRegistry.getPluginOperators().map(op => [op.name, op])
|
|
19
21
|
)
|
|
@@ -13,7 +13,7 @@ const nonStandardDirectoryStructure: OperatorDefn = {
|
|
|
13
13
|
return false;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
logger.
|
|
16
|
+
logger.info(`running global directory structure analysis..`);
|
|
17
17
|
|
|
18
18
|
const repoPath = options.dir || process.cwd();
|
|
19
19
|
let result = false;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { regexMatch } from './regexMatch';
|
|
2
|
+
import { logger } from '../utils/logger';
|
|
3
|
+
|
|
4
|
+
jest.mock('../utils/logger', () => ({
|
|
5
|
+
logger: {
|
|
6
|
+
debug: jest.fn(),
|
|
7
|
+
error: jest.fn()
|
|
8
|
+
},
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
describe('regexMatch', () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
jest.clearAllMocks();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('should return true when the string matches the pattern', () => {
|
|
17
|
+
expect(regexMatch.fn('hello world', 'hello')).toBe(true);
|
|
18
|
+
expect(regexMatch.fn('hello world', 'world$')).toBe(true);
|
|
19
|
+
expect(regexMatch.fn('hello world', '^hello')).toBe(true);
|
|
20
|
+
expect(logger.debug).toHaveBeenCalledWith(expect.stringContaining('result is true'));
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should return false when the string does not match the pattern', () => {
|
|
24
|
+
expect(regexMatch.fn('hello world', 'goodbye')).toBe(false);
|
|
25
|
+
expect(regexMatch.fn('hello world', '^world')).toBe(false);
|
|
26
|
+
expect(regexMatch.fn('hello world', 'hello$')).toBe(false);
|
|
27
|
+
expect(logger.debug).toHaveBeenCalledWith(expect.stringContaining('result is false'));
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should handle regex flags correctly', () => {
|
|
31
|
+
expect(regexMatch.fn('Hello World', '/hello/i')).toBe(true);
|
|
32
|
+
expect(regexMatch.fn('Hello\nWorld', '/hello.*world/is')).toBe(true);
|
|
33
|
+
expect(regexMatch.fn('Hello World', '/hello world/i')).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('should handle undefined or null factValue', () => {
|
|
37
|
+
expect(regexMatch.fn(undefined, 'test')).toBe(false);
|
|
38
|
+
expect(regexMatch.fn(null, 'test')).toBe(false);
|
|
39
|
+
expect(logger.debug).toHaveBeenCalledWith('regexMatch: factValue is undefined or null');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should handle non-string regexPattern', () => {
|
|
43
|
+
expect(regexMatch.fn('test', null as any)).toBe(false);
|
|
44
|
+
expect(regexMatch.fn('test', undefined as any)).toBe(false);
|
|
45
|
+
expect(regexMatch.fn('test', 123 as any)).toBe(false);
|
|
46
|
+
expect(logger.debug).toHaveBeenCalledWith(expect.stringContaining('regexPattern is not a string'));
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should handle invalid regex patterns gracefully', () => {
|
|
50
|
+
expect(regexMatch.fn('test', '[')).toBe(false);
|
|
51
|
+
expect(logger.error).toHaveBeenCalledWith(expect.stringContaining('regexMatch error'));
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('should handle non-string factValues by converting them to strings', () => {
|
|
55
|
+
expect(regexMatch.fn(123, '123')).toBe(true);
|
|
56
|
+
expect(regexMatch.fn(true, 'true')).toBe(true);
|
|
57
|
+
expect(regexMatch.fn({ toString: () => 'custom' }, 'custom')).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { logger } from '../utils/logger';
|
|
2
|
+
import { OperatorDefn } from '../types/typeDefs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Operator that tests if a string matches a regular expression pattern
|
|
6
|
+
*
|
|
7
|
+
* @param factValue - The string to test against the regex pattern
|
|
8
|
+
* @param regexPattern - The regex pattern to test against (as a string)
|
|
9
|
+
* @returns true if the string matches the pattern, false otherwise
|
|
10
|
+
*/
|
|
11
|
+
const regexMatch: OperatorDefn = {
|
|
12
|
+
'name': 'regexMatch',
|
|
13
|
+
'fn': (factValue: any, regexPattern: string) => {
|
|
14
|
+
try {
|
|
15
|
+
logger.debug(`regexMatch: testing ${factValue} against pattern ${regexPattern}`);
|
|
16
|
+
|
|
17
|
+
if (factValue === undefined || factValue === null) {
|
|
18
|
+
logger.debug('regexMatch: factValue is undefined or null');
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (typeof regexPattern !== 'string') {
|
|
23
|
+
logger.debug(`regexMatch: regexPattern is not a string: ${typeof regexPattern}`);
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Extract flags if they exist (pattern/flags format)
|
|
28
|
+
let flags = '';
|
|
29
|
+
let pattern = regexPattern;
|
|
30
|
+
|
|
31
|
+
// Check if the pattern is in /pattern/flags format
|
|
32
|
+
const regexFormatMatch = /^\/(.+)\/([gimsuyd]*)$/.exec(regexPattern);
|
|
33
|
+
if (regexFormatMatch) {
|
|
34
|
+
pattern = regexFormatMatch[1];
|
|
35
|
+
flags = regexFormatMatch[2];
|
|
36
|
+
logger.debug(`regexMatch: extracted pattern "${pattern}" with flags "${flags}"`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const regex = new RegExp(pattern, flags);
|
|
40
|
+
const result = regex.test(String(factValue));
|
|
41
|
+
|
|
42
|
+
logger.debug(`regexMatch: result is ${result}`);
|
|
43
|
+
return result;
|
|
44
|
+
} catch (e) {
|
|
45
|
+
logger.error(`regexMatch error: ${e}`);
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export { regexMatch };
|
package/src/types/typeDefs.ts
CHANGED
|
@@ -144,7 +144,8 @@ export interface BasicTelemetryMetadata {
|
|
|
144
144
|
repoPath: string;
|
|
145
145
|
telemetryData?: TelemetryData;
|
|
146
146
|
errorMessage?: string;
|
|
147
|
-
options?: any
|
|
147
|
+
options?: any;
|
|
148
|
+
errorStack?: string;
|
|
148
149
|
}
|
|
149
150
|
|
|
150
151
|
export interface ExemptionTelemetryMetadata {
|
|
@@ -191,6 +192,8 @@ export interface ResultMetadata {
|
|
|
191
192
|
telemetryData: TelemetryData;
|
|
192
193
|
options: any;
|
|
193
194
|
repoXFIConfig: RepoXFIConfig;
|
|
195
|
+
memoryUsage: any;
|
|
196
|
+
repoUrl: string;
|
|
194
197
|
};
|
|
195
198
|
}
|
|
196
199
|
|
package/src/utils/logger.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { maskSensitiveData } from './maskSensitiveData';
|
|
|
6
6
|
let loggerInstance: pino.Logger | undefined;
|
|
7
7
|
let loglevel = process.env.XFI_LOG_LEVEL ||
|
|
8
8
|
(process.env.NODE_ENV === 'test' ? 'silent' : 'info');
|
|
9
|
-
let logPrefix: string;
|
|
9
|
+
let logPrefix: string = generateLogPrefix();
|
|
10
10
|
|
|
11
11
|
// Initialize logger and prefix immediately
|
|
12
12
|
export function initializeLogger() {
|
|
@@ -20,16 +20,10 @@ export function generateLogPrefix(): string {
|
|
|
20
20
|
|
|
21
21
|
export function resetLogPrefix(): void {
|
|
22
22
|
logPrefix = generateLogPrefix();
|
|
23
|
-
if (loggerInstance) {
|
|
24
|
-
loggerInstance = loggerInstance.child({ prefix: logPrefix });
|
|
25
|
-
}
|
|
26
23
|
}
|
|
27
24
|
|
|
28
25
|
export function setLogPrefix(prefix: string): void {
|
|
29
26
|
logPrefix = prefix;
|
|
30
|
-
if (loggerInstance) {
|
|
31
|
-
loggerInstance = loggerInstance.child({ prefix: logPrefix });
|
|
32
|
-
}
|
|
33
27
|
}
|
|
34
28
|
|
|
35
29
|
export function getLogPrefix(): string {
|
|
@@ -49,10 +43,10 @@ function getLogger(force?: boolean): pino.Logger {
|
|
|
49
43
|
target: 'pino-pretty',
|
|
50
44
|
options: {
|
|
51
45
|
loglevel: loglevel,
|
|
46
|
+
sync: false,
|
|
52
47
|
colorize: true,
|
|
53
48
|
translateTime: 'SYS:yyyy-mm-dd HH:MM:ss.l o',
|
|
54
49
|
ignore: 'pid,hostname',
|
|
55
|
-
messageFormat: '{prefix} - {msg}',
|
|
56
50
|
singleLine: true,
|
|
57
51
|
errorProps: '*'
|
|
58
52
|
}
|
|
@@ -60,12 +54,12 @@ function getLogger(force?: boolean): pino.Logger {
|
|
|
60
54
|
|
|
61
55
|
const loggerOptions: pino.LoggerOptions = {
|
|
62
56
|
timestamp: pino.stdTimeFunctions.isoTime,
|
|
57
|
+
msgPrefix: `${logPrefix} - `,
|
|
63
58
|
level: 'info',
|
|
64
59
|
formatters: {
|
|
65
60
|
level: (label) => ({ level: label }),
|
|
66
61
|
bindings: (bindings) => bindings,
|
|
67
62
|
log: (object) => ({
|
|
68
|
-
prefix: logPrefix,
|
|
69
63
|
...object
|
|
70
64
|
})
|
|
71
65
|
},
|