@govtechsg/oobee 0.10.76 → 0.10.78-alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/.github/workflows/publish.yml +8 -1
  2. package/INTEGRATION.md +50 -3
  3. package/dist/cli.js +252 -0
  4. package/dist/combine.js +221 -0
  5. package/dist/constants/cliFunctions.js +306 -0
  6. package/dist/constants/common.js +1669 -0
  7. package/dist/constants/constants.js +913 -0
  8. package/dist/constants/errorMeta.json +319 -0
  9. package/dist/constants/itemTypeDescription.js +7 -0
  10. package/dist/constants/oobeeAi.js +121 -0
  11. package/dist/constants/questions.js +151 -0
  12. package/dist/constants/sampleData.js +176 -0
  13. package/dist/crawlers/commonCrawlerFunc.js +428 -0
  14. package/dist/crawlers/crawlDomain.js +613 -0
  15. package/dist/crawlers/crawlIntelligentSitemap.js +135 -0
  16. package/dist/crawlers/crawlLocalFile.js +151 -0
  17. package/dist/crawlers/crawlSitemap.js +303 -0
  18. package/dist/crawlers/custom/escapeCssSelector.js +10 -0
  19. package/dist/crawlers/custom/evaluateAltText.js +11 -0
  20. package/dist/crawlers/custom/extractAndGradeText.js +44 -0
  21. package/dist/crawlers/custom/extractText.js +27 -0
  22. package/dist/crawlers/custom/findElementByCssSelector.js +36 -0
  23. package/dist/crawlers/custom/flagUnlabelledClickableElements.js +963 -0
  24. package/dist/crawlers/custom/framesCheck.js +37 -0
  25. package/dist/crawlers/custom/getAxeConfiguration.js +111 -0
  26. package/dist/crawlers/custom/gradeReadability.js +23 -0
  27. package/dist/crawlers/custom/utils.js +1024 -0
  28. package/dist/crawlers/custom/xPathToCss.js +147 -0
  29. package/dist/crawlers/guards/urlGuard.js +71 -0
  30. package/dist/crawlers/pdfScanFunc.js +276 -0
  31. package/dist/crawlers/runCustom.js +89 -0
  32. package/dist/exclusions.txt +7 -0
  33. package/dist/generateHtmlReport.js +144 -0
  34. package/dist/index.js +62 -0
  35. package/dist/logs.js +84 -0
  36. package/dist/mergeAxeResults.js +1588 -0
  37. package/dist/npmIndex.js +640 -0
  38. package/dist/proxyService.js +360 -0
  39. package/dist/runGenerateJustHtmlReport.js +16 -0
  40. package/dist/screenshotFunc/htmlScreenshotFunc.js +355 -0
  41. package/dist/screenshotFunc/pdfScreenshotFunc.js +645 -0
  42. package/dist/services/s3Uploader.js +127 -0
  43. package/dist/static/ejs/partials/components/allIssues/AllIssues.ejs +9 -0
  44. package/dist/static/ejs/partials/components/allIssues/CategoryBadges.ejs +82 -0
  45. package/dist/static/ejs/partials/components/allIssues/FilterBar.ejs +33 -0
  46. package/dist/static/ejs/partials/components/allIssues/IssuesTable.ejs +41 -0
  47. package/dist/static/ejs/partials/components/header/SiteInfo.ejs +119 -0
  48. package/dist/static/ejs/partials/components/header/aboutScanModal/AboutScanModal.ejs +15 -0
  49. package/dist/static/ejs/partials/components/header/aboutScanModal/ScanConfiguration.ejs +44 -0
  50. package/dist/static/ejs/partials/components/header/aboutScanModal/ScanDetails.ejs +142 -0
  51. package/dist/static/ejs/partials/components/prioritiseIssues/IssueDetailCard.ejs +36 -0
  52. package/dist/static/ejs/partials/components/prioritiseIssues/PrioritiseIssues.ejs +47 -0
  53. package/dist/static/ejs/partials/components/ruleModal/ruleOffcanvas.ejs +196 -0
  54. package/dist/static/ejs/partials/components/scannedPagesSegmentedTabs.ejs +48 -0
  55. package/dist/static/ejs/partials/components/screenshotLightbox.ejs +13 -0
  56. package/dist/static/ejs/partials/components/shared/InfoAlert.ejs +3 -0
  57. package/dist/static/ejs/partials/components/summaryScanAbout.ejs +141 -0
  58. package/dist/static/ejs/partials/components/summaryScanResults.ejs +16 -0
  59. package/dist/static/ejs/partials/components/summaryTable.ejs +20 -0
  60. package/dist/static/ejs/partials/components/summaryWcagCompliance.ejs +94 -0
  61. package/dist/static/ejs/partials/components/topTen.ejs +6 -0
  62. package/dist/static/ejs/partials/components/wcagCompliance/FailedCriteria.ejs +47 -0
  63. package/dist/static/ejs/partials/components/wcagCompliance/WcagCompliance.ejs +16 -0
  64. package/dist/static/ejs/partials/components/wcagCompliance/WcagGaugeBar.ejs +16 -0
  65. package/dist/static/ejs/partials/components/wcagCoverageDetails.ejs +18 -0
  66. package/dist/static/ejs/partials/footer.ejs +24 -0
  67. package/dist/static/ejs/partials/header.ejs +14 -0
  68. package/dist/static/ejs/partials/main.ejs +29 -0
  69. package/dist/static/ejs/partials/scripts/allIssues/AllIssues.ejs +376 -0
  70. package/dist/static/ejs/partials/scripts/bootstrap.ejs +8 -0
  71. package/dist/static/ejs/partials/scripts/categorySummary.ejs +141 -0
  72. package/dist/static/ejs/partials/scripts/decodeUnzipParse.ejs +3 -0
  73. package/dist/static/ejs/partials/scripts/header/SiteInfo.ejs +44 -0
  74. package/dist/static/ejs/partials/scripts/header/aboutScanModal/AboutScanModal.ejs +51 -0
  75. package/dist/static/ejs/partials/scripts/header/aboutScanModal/ScanConfiguration.ejs +127 -0
  76. package/dist/static/ejs/partials/scripts/header/aboutScanModal/ScanDetails.ejs +60 -0
  77. package/dist/static/ejs/partials/scripts/highlightjs.ejs +335 -0
  78. package/dist/static/ejs/partials/scripts/popper.ejs +7 -0
  79. package/dist/static/ejs/partials/scripts/prioritiseIssues/IssueDetailCard.ejs +137 -0
  80. package/dist/static/ejs/partials/scripts/prioritiseIssues/PrioritiseIssues.ejs +214 -0
  81. package/dist/static/ejs/partials/scripts/prioritiseIssues/wcagSvgMap.ejs +861 -0
  82. package/dist/static/ejs/partials/scripts/ruleModal/constants.ejs +957 -0
  83. package/dist/static/ejs/partials/scripts/ruleModal/itemCardRenderer.ejs +353 -0
  84. package/dist/static/ejs/partials/scripts/ruleModal/pageAccordionBuilder.ejs +468 -0
  85. package/dist/static/ejs/partials/scripts/ruleModal/ruleOffcanvas.ejs +306 -0
  86. package/dist/static/ejs/partials/scripts/ruleModal/utilities.ejs +483 -0
  87. package/dist/static/ejs/partials/scripts/scannedPagesSegmentedTabs.ejs +35 -0
  88. package/dist/static/ejs/partials/scripts/screenshotLightbox.ejs +75 -0
  89. package/dist/static/ejs/partials/scripts/summaryScanResults.ejs +14 -0
  90. package/dist/static/ejs/partials/scripts/summaryTable.ejs +78 -0
  91. package/dist/static/ejs/partials/scripts/topTen.ejs +61 -0
  92. package/dist/static/ejs/partials/scripts/utils.ejs +453 -0
  93. package/dist/static/ejs/partials/scripts/wcagCompliance/FailedCriteria.ejs +103 -0
  94. package/dist/static/ejs/partials/scripts/wcagCompliance/WcagGaugeBar.ejs +47 -0
  95. package/dist/static/ejs/partials/scripts/wcagCompliance.ejs +15 -0
  96. package/dist/static/ejs/partials/scripts/wcagCoverageDetails.ejs +75 -0
  97. package/dist/static/ejs/partials/styles/allIssues/AllIssues.ejs +384 -0
  98. package/dist/static/ejs/partials/styles/bootstrap.ejs +12391 -0
  99. package/dist/static/ejs/partials/styles/header/SiteInfo.ejs +121 -0
  100. package/dist/static/ejs/partials/styles/header/aboutScanModal/AboutScanModal.ejs +82 -0
  101. package/dist/static/ejs/partials/styles/header/aboutScanModal/ScanConfiguration.ejs +50 -0
  102. package/dist/static/ejs/partials/styles/header/aboutScanModal/ScanDetails.ejs +149 -0
  103. package/dist/static/ejs/partials/styles/header.ejs +7 -0
  104. package/dist/static/ejs/partials/styles/highlightjs.ejs +54 -0
  105. package/dist/static/ejs/partials/styles/prioritiseIssues/IssueDetailCard.ejs +141 -0
  106. package/dist/static/ejs/partials/styles/prioritiseIssues/PrioritiseIssues.ejs +204 -0
  107. package/dist/static/ejs/partials/styles/ruleModal/ruleOffcanvas.ejs +456 -0
  108. package/dist/static/ejs/partials/styles/scannedPagesSegmentedTabs.ejs +46 -0
  109. package/dist/static/ejs/partials/styles/shared/InfoAlert.ejs +12 -0
  110. package/dist/static/ejs/partials/styles/styles.ejs +1607 -0
  111. package/dist/static/ejs/partials/styles/summaryBootstrap.ejs +12458 -0
  112. package/dist/static/ejs/partials/styles/topTenCard.ejs +44 -0
  113. package/dist/static/ejs/partials/styles/wcagCompliance/FailedCriteria.ejs +59 -0
  114. package/dist/static/ejs/partials/styles/wcagCompliance/WcagGaugeBar.ejs +62 -0
  115. package/dist/static/ejs/partials/styles/wcagCompliance.ejs +36 -0
  116. package/dist/static/ejs/partials/styles/wcagCoverageDetails.ejs +33 -0
  117. package/dist/static/ejs/partials/summaryHeader.ejs +70 -0
  118. package/dist/static/ejs/partials/summaryMain.ejs +49 -0
  119. package/dist/static/ejs/report.ejs +226 -0
  120. package/dist/static/ejs/summary.ejs +47 -0
  121. package/dist/types/types.js +1 -0
  122. package/dist/utils.js +1070 -0
  123. package/examples/oobee-cypress-integration-js/cypress/support/e2e.js +36 -6
  124. package/examples/oobee-cypress-integration-js/cypress.config.js +45 -1
  125. package/examples/oobee-cypress-integration-ts/cypress.config.ts +47 -1
  126. package/examples/oobee-cypress-integration-ts/src/cypress/support/e2e.ts +36 -6
  127. package/examples/oobee-playwright-integration-js/oobee-playwright-demo.js +2 -1
  128. package/examples/oobee-playwright-integration-ts/src/oobee-playwright-demo.ts +2 -1
  129. package/examples/oobee-scan-html-demo.js +51 -0
  130. package/examples/oobee-scan-page-demo.js +40 -0
  131. package/package.json +9 -3
  132. package/src/constants/common.ts +2 -2
  133. package/src/constants/constants.ts +3 -1
  134. package/src/crawlers/crawlDomain.ts +1 -0
  135. package/src/crawlers/runCustom.ts +0 -1
  136. package/src/mergeAxeResults.ts +43 -22
  137. package/src/npmIndex.ts +500 -131
@@ -0,0 +1,144 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ import { compressJsonFileStreaming, writeHTML, flattenAndSortResults, populateScanPagesDetail, getWcagPassPercentage, getProgressPercentage, getIssuesPercentage, itemTypeDescription, oobeeAiHtmlETL, oobeeAiRules, formatAboutStartTime, } from './mergeAxeResults.js';
4
+ import constants, { ScannerTypes, WCAGclauses, a11yRuleShortDescriptionMap, disabilityBadgesMap, a11yRuleLongDescriptionMap, } from './constants/constants.js';
5
+ import { consoleLogger } from './logs.js';
6
+ const ensureCategory = (categoryObj, categoryName) => {
7
+ const rulesRaw = categoryObj?.rules ?? [];
8
+ const rules = Array.isArray(rulesRaw)
9
+ ? rulesRaw
10
+ : Object.entries(rulesRaw).map(([rule, info]) => ({
11
+ rule,
12
+ ...info,
13
+ }));
14
+ rules.forEach((rule) => {
15
+ if (!Array.isArray(rule.pagesAffected) &&
16
+ rule.pagesAffected &&
17
+ typeof rule.pagesAffected === 'object') {
18
+ rule.pagesAffected = Object.entries(rule.pagesAffected).map(([url, pageInfo]) => {
19
+ return pageInfo?.url ? pageInfo : { url, ...pageInfo };
20
+ });
21
+ }
22
+ if (!Array.isArray(rule.pagesAffected)) {
23
+ rule.pagesAffected = [];
24
+ }
25
+ if (typeof rule.totalItems !== 'number') {
26
+ rule.totalItems = rule.pagesAffected.reduce((accumulate, page) => accumulate + (Array.isArray(page.items) ? page.items.length : 0), 0);
27
+ }
28
+ });
29
+ const totals = {
30
+ totalItems: typeof categoryObj?.totalItems === 'number'
31
+ ? categoryObj.totalItems
32
+ : rules.reduce((acc, rr) => acc + (rr.totalItems || 0), 0),
33
+ totalRuleIssues: typeof categoryObj?.totalRuleIssues === 'number' ? categoryObj.totalRuleIssues : rules.length,
34
+ };
35
+ return {
36
+ description: categoryObj?.description || itemTypeDescription[categoryName],
37
+ ...totals,
38
+ rules,
39
+ };
40
+ };
41
+ export const generateHtmlReport = async (resultDir) => {
42
+ try {
43
+ const storagePath = path.resolve(resultDir);
44
+ const scanDataJsonPath = path.join(storagePath, 'scanData.json');
45
+ const scanItemsJsonPath = path.join(storagePath, 'scanItems.json');
46
+ if (!fs.existsSync(scanDataJsonPath)) {
47
+ throw new Error(`Missing file: ${scanDataJsonPath}`);
48
+ }
49
+ if (!fs.existsSync(scanItemsJsonPath)) {
50
+ throw new Error(`Missing file: ${scanItemsJsonPath}`);
51
+ }
52
+ const scanDataB64Path = path.join(storagePath, 'scanData.json.gz.b64');
53
+ const scanItemsB64Path = path.join(storagePath, 'scanItems.json.gz.b64');
54
+ if (!fs.existsSync(scanDataB64Path)) {
55
+ consoleLogger.info('scanData.json.gz.b64 not found — generating from scanData.json');
56
+ await compressJsonFileStreaming(scanDataJsonPath, scanDataB64Path);
57
+ }
58
+ if (!fs.existsSync(scanItemsB64Path)) {
59
+ consoleLogger.info('scanItems.json.gz.b64 not found — generating from scanItems.json');
60
+ await compressJsonFileStreaming(scanItemsJsonPath, scanItemsB64Path);
61
+ }
62
+ const scanData = JSON.parse(await fs.readFile(scanDataJsonPath, 'utf8'));
63
+ const scanItemsAll = JSON.parse(await fs.readFile(scanItemsJsonPath, 'utf8'));
64
+ const { oobeeAppVersion: itemsAppVersion, mustFix = {}, goodToFix = {}, needsReview = {}, passed = {}, } = scanItemsAll;
65
+ const items = {
66
+ mustFix: ensureCategory(mustFix, 'mustFix'),
67
+ goodToFix: ensureCategory(goodToFix, 'goodToFix'),
68
+ needsReview: ensureCategory(needsReview, 'needsReview'),
69
+ passed: ensureCategory(passed, 'passed'),
70
+ };
71
+ const pagesScanned = Array.isArray(scanData.pagesScanned) ? scanData.pagesScanned : [];
72
+ const pagesNotScanned = Array.isArray(scanData.pagesNotScanned) ? scanData.pagesNotScanned : [];
73
+ const allIssues = {
74
+ storagePath,
75
+ oobeeAi: { htmlETL: oobeeAiHtmlETL, rules: oobeeAiRules },
76
+ siteName: (scanData.siteName || (pagesScanned[0]?.pageTitle ?? ''))
77
+ .toString()
78
+ .replace(/^\d+\s*:\s*/, '')
79
+ .trim(),
80
+ startTime: scanData.startTime ? new Date(scanData.startTime) : new Date(),
81
+ endTime: scanData.endTime ? new Date(scanData.endTime) : new Date(),
82
+ urlScanned: scanData.urlScanned || scanData.url || '',
83
+ scanType: scanData.scanType || ScannerTypes.WEBSITE,
84
+ deviceChosen: scanData.deviceChosen || 'Desktop',
85
+ formatAboutStartTime,
86
+ isCustomFlow: (scanData.scanType || '') === ScannerTypes.CUSTOM,
87
+ viewport: scanData.deviceChosen || 'Desktop',
88
+ pagesScanned,
89
+ pagesNotScanned,
90
+ totalPagesScanned: typeof scanData.totalPagesScanned === 'number'
91
+ ? scanData.totalPagesScanned
92
+ : pagesScanned.length,
93
+ totalPagesNotScanned: typeof scanData.totalPagesNotScanned === 'number'
94
+ ? scanData.totalPagesNotScanned
95
+ : pagesNotScanned.length,
96
+ totalItems: 0,
97
+ topFiveMostIssues: Array.isArray(scanData.topFiveMostIssues)
98
+ ? scanData.topFiveMostIssues
99
+ : [],
100
+ topTenPagesWithMostIssues: Array.isArray(scanData.topTenPagesWithMostIssues)
101
+ ? scanData.topTenPagesWithMostIssues
102
+ : [],
103
+ topTenIssues: Array.isArray(scanData.topTenIssues) ? scanData.topTenIssues : [],
104
+ wcagViolations: Array.isArray(scanData.wcagViolations) ? scanData.wcagViolations : [],
105
+ customFlowLabel: scanData.customFlowLabel || '',
106
+ oobeeAppVersion: itemsAppVersion || scanData.oobeeAppVersion || 'dev',
107
+ items,
108
+ cypressScanAboutMetadata: scanData.cypressScanAboutMetadata || {},
109
+ wcagLinks: scanData.wcagLinks || constants.wcagLinks,
110
+ wcagClauses: WCAGclauses,
111
+ a11yRuleShortDescriptionMap,
112
+ disabilityBadgesMap,
113
+ a11yRuleLongDescriptionMap,
114
+ advancedScanOptionsSummaryItems: {
115
+ showIncludeScreenshots: !!scanData.advancedScanOptionsSummaryItems?.showIncludeScreenshots,
116
+ showAllowSubdomains: !!scanData.advancedScanOptionsSummaryItems?.showAllowSubdomains,
117
+ showEnableCustomChecks: !!scanData.advancedScanOptionsSummaryItems?.showEnableCustomChecks,
118
+ showEnableWcagAaa: !!scanData.advancedScanOptionsSummaryItems?.showEnableWcagAaa,
119
+ showSlowScanMode: !!scanData.advancedScanOptionsSummaryItems?.showSlowScanMode,
120
+ showAdhereRobots: !!scanData.advancedScanOptionsSummaryItems?.showAdhereRobots,
121
+ },
122
+ scanPagesDetail: scanData.scanPagesDetail || {
123
+ pagesAffected: [],
124
+ pagesNotAffected: [],
125
+ scannedPagesCount: 0,
126
+ pagesNotScanned: [],
127
+ pagesNotScannedCount: 0,
128
+ },
129
+ };
130
+ flattenAndSortResults(allIssues, allIssues.isCustomFlow);
131
+ populateScanPagesDetail(allIssues);
132
+ allIssues.wcagPassPercentage = getWcagPassPercentage(allIssues.wcagViolations, allIssues.advancedScanOptionsSummaryItems.showEnableWcagAaa);
133
+ allIssues.progressPercentage = getProgressPercentage(allIssues.scanPagesDetail, allIssues.advancedScanOptionsSummaryItems.showEnableWcagAaa);
134
+ allIssues.issuesPercentage = await getIssuesPercentage(allIssues.scanPagesDetail, allIssues.advancedScanOptionsSummaryItems.showEnableWcagAaa, allIssues.advancedScanOptionsSummaryItems?.disableOobee);
135
+ await writeHTML(allIssues, storagePath, 'report', scanDataB64Path, scanItemsB64Path);
136
+ consoleLogger.info(`Report generated at: ${path.join(storagePath, 'report.html')}`);
137
+ return path.join(storagePath, 'report.html');
138
+ }
139
+ catch (err) {
140
+ consoleLogger.error(`generateHtmlReport failed: ${err?.message || err}`);
141
+ throw err;
142
+ }
143
+ };
144
+ export default generateHtmlReport;
package/dist/index.js ADDED
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+ import printMessage from 'print-message';
3
+ import inquirer from 'inquirer';
4
+ import { getVersion, getUserDataTxt, writeToUserDataTxt, listenForCleanUp, cleanUpAndExit, } from './utils.js';
5
+ import { prepareData, messageOptions, getPlaywrightDeviceDetailsObject, getScreenToScan, getClonedProfilesWithRandomToken, } from './constants/common.js';
6
+ import questions from './constants/questions.js';
7
+ import combineRun from './combine.js';
8
+ import { FileTypes } from './constants/constants.js';
9
+ const userData = getUserDataTxt();
10
+ const runScan = async (answers) => {
11
+ const screenToScan = getScreenToScan(answers.deviceChosen, answers.customDevice, answers.viewportWidth);
12
+ answers.playwrightDeviceDetailsObject = getPlaywrightDeviceDetailsObject(answers.deviceChosen, answers.customDevice, answers.viewportWidth);
13
+ if (!answers.nameEmail) {
14
+ answers.nameEmail = `${userData.name}:${userData.email}`;
15
+ }
16
+ answers.fileTypes = FileTypes.All;
17
+ answers.metadata = '{}';
18
+ const data = await prepareData(answers);
19
+ // Executes cleanUp script if error encountered
20
+ listenForCleanUp(data.randomToken);
21
+ data.userDataDirectory = getClonedProfilesWithRandomToken(data.browser, data.randomToken);
22
+ printMessage(['Scanning website...'], messageOptions);
23
+ await combineRun(data, screenToScan);
24
+ // Delete dataset and request queues
25
+ cleanUpAndExit(0, data.randomToken);
26
+ };
27
+ if (userData) {
28
+ printMessage([
29
+ `Oobee (ver ${getVersion()})`,
30
+ 'We recommend using Chrome browser for the best experience.',
31
+ '',
32
+ `Welcome back ${userData.name}!`,
33
+ `(Refer to readme.txt on how to change your profile)`,
34
+ ], {
35
+ // Note that the color is based on kleur NPM package
36
+ border: true,
37
+ borderColor: 'magenta',
38
+ });
39
+ inquirer.prompt(questions).then(async (answers) => {
40
+ await runScan(answers);
41
+ });
42
+ }
43
+ else {
44
+ printMessage([`Oobee (ver ${getVersion()})`, 'We recommend using Chrome browser for the best experience.'], {
45
+ // Note that the color is based on kleur NPM package
46
+ border: true,
47
+ borderColor: 'magenta',
48
+ });
49
+ printMessage([
50
+ `To personalise your experience, we will be collecting your name, email address and app usage data.`,
51
+ `Your information fully complies with GovTech's Privacy Policy.`,
52
+ ], {
53
+ border: false,
54
+ });
55
+ inquirer.prompt(questions).then(async (answers) => {
56
+ const { name, email } = answers;
57
+ answers.nameEmail = `${name}:${email}`;
58
+ await writeToUserDataTxt('name', name);
59
+ await writeToUserDataTxt('email', email);
60
+ await runScan(answers);
61
+ });
62
+ }
package/dist/logs.js ADDED
@@ -0,0 +1,84 @@
1
+ /* eslint-disable no-console */
2
+ /* eslint-disable no-shadow */
3
+ import { createLogger, format, transports } from 'winston';
4
+ import { guiInfoStatusTypes } from './constants/constants.js';
5
+ import path from 'path';
6
+ import { randomUUID } from 'crypto';
7
+ const { combine, timestamp, printf } = format;
8
+ // Sample output
9
+ // {"timestamp":"2020-11-25 17:29:07","level":"error","message":"hello world"}
10
+ const logFormat = printf(({ timestamp, level, message }) => {
11
+ const log = {
12
+ timestamp: `${timestamp}`,
13
+ level: `${level}`,
14
+ message: `${message}`,
15
+ };
16
+ return JSON.stringify(log);
17
+ });
18
+ // transport: storage device for logs
19
+ // Enabled for console and storing into files; Files are overwritten each time
20
+ // All logs in combined.txt, error in errors.txt
21
+ const uuid = randomUUID();
22
+ let basePath;
23
+ if (process.env.OOBEE_LOGS_PATH) {
24
+ basePath = process.env.OOBEE_LOGS_PATH;
25
+ }
26
+ else if (process.platform === 'win32') {
27
+ basePath = path.join(process.env.APPDATA, 'Oobee');
28
+ }
29
+ else if (process.platform === 'darwin') {
30
+ basePath = path.join(process.env.HOME, 'Library', 'Application Support', 'Oobee');
31
+ }
32
+ else {
33
+ basePath = path.join(process.cwd());
34
+ }
35
+ export const errorsTxtPath = path.join(basePath, `${uuid}.txt`);
36
+ const consoleLogger = createLogger({
37
+ silent: !(process.env.RUNNING_FROM_PH_GUI || process.env.OOBEE_VERBOSE),
38
+ format: combine(timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), logFormat),
39
+ transports: [
40
+ new transports.Console({ level: 'info' }),
41
+ new transports.File({
42
+ filename: errorsTxtPath,
43
+ level: 'info',
44
+ handleExceptions: true,
45
+ }),
46
+ ],
47
+ });
48
+ // No display in consoles, this will mostly be used within the interactive script to avoid disrupting the flow
49
+ // Also used in common functions to not link internal information
50
+ // if running from mass scanner, log out errors in console
51
+ const silentLogger = createLogger({
52
+ format: combine(timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), logFormat),
53
+ transports: [
54
+ new transports.File({
55
+ filename: errorsTxtPath,
56
+ level: 'warn',
57
+ handleExceptions: true
58
+ }),
59
+ ].filter(Boolean),
60
+ });
61
+ // guiInfoLogger feeds the gui information via console log and is mainly used for scanning process
62
+ export const guiInfoLog = (status, data) => {
63
+ if (process.env.RUNNING_FROM_PH_GUI || process.env.OOBEE_VERBOSE) {
64
+ switch (status) {
65
+ case guiInfoStatusTypes.COMPLETED:
66
+ console.log('Scan completed');
67
+ silentLogger.info('Scan completed');
68
+ break;
69
+ case guiInfoStatusTypes.SCANNED:
70
+ case guiInfoStatusTypes.SKIPPED:
71
+ case guiInfoStatusTypes.ERROR:
72
+ case guiInfoStatusTypes.DUPLICATE:
73
+ const msg = `crawling::${data.numScanned || 0}::${status}::${data.urlScanned || 'no url provided'}`;
74
+ console.log(msg);
75
+ silentLogger.info(msg);
76
+ break;
77
+ default:
78
+ console.log(`Status provided to gui info log not recognized: ${status}`);
79
+ break;
80
+ }
81
+ }
82
+ };
83
+ consoleLogger.info(`Logger writing to: ${errorsTxtPath}`);
84
+ export { logFormat, consoleLogger, silentLogger };