@@ -10,6 +10,93 @@ function coerceScore(value) {
1010 return 0 ;
1111}
1212
13+ /**
14+ * Produce a compact per-URL code quality summary suitable for the top_urls array.
15+ * Avoids embedding full audit item payloads to keep the report JSON size manageable.
16+ *
17+ * @param {object|null } audits - raw code_quality_audits from a URL scan result
18+ * @returns {object|null }
19+ */
20+ function summarizeCodeQualityAudits ( audits ) {
21+ if ( ! audits ) return null ;
22+ return {
23+ deprecated_apis_passing : audits . deprecated_apis ?. passing ?? null ,
24+ deprecated_apis_count : audits . deprecated_apis ?. items ?. length ?? 0 ,
25+ errors_in_console_passing : audits . errors_in_console ?. passing ?? null ,
26+ errors_in_console_count : audits . errors_in_console ?. count ?? 0 ,
27+ no_document_write_passing : audits . no_document_write ?. passing ?? null ,
28+ vulnerable_libraries_passing : audits . vulnerable_libraries ?. passing ?? null ,
29+ vulnerable_libraries_count : audits . vulnerable_libraries ?. items ?. length ?? 0 ,
30+ vulnerable_library_names : ( audits . vulnerable_libraries ?. items ?? [ ] )
31+ . map ( ( item ) => item . library )
32+ . filter ( Boolean ) ,
33+ js_libraries : ( audits . js_libraries ?. items ?? [ ] ) . map ( ( l ) => l . name ) . filter ( Boolean )
34+ } ;
35+ }
36+
37+ /**
38+ * Aggregate code quality audit results across all successfully scanned URLs.
39+ *
40+ * @param {Array } urlResults - normalized URL scan results
41+ * @returns {object }
42+ */
43+ export function buildCodeQualitySummary ( urlResults = [ ] ) {
44+ const successful = urlResults . filter (
45+ ( r ) => r ?. scan_status === 'success' && r . code_quality_audits != null
46+ ) ;
47+
48+ const auditUrls = {
49+ deprecated_apis : [ ] ,
50+ console_errors : [ ] ,
51+ document_write : [ ] ,
52+ vulnerable_libraries : [ ]
53+ } ;
54+
55+ const jsLibraryCounts = { } ;
56+ const vulnerableLibraryCounts = { } ;
57+
58+ for ( const result of successful ) {
59+ const qa = result . code_quality_audits ;
60+
61+ if ( qa . deprecated_apis ?. passing === false ) {
62+ auditUrls . deprecated_apis . push ( result . url ) ;
63+ }
64+ if ( qa . errors_in_console ?. passing === false ) {
65+ auditUrls . console_errors . push ( result . url ) ;
66+ }
67+ if ( qa . no_document_write ?. passing === false ) {
68+ auditUrls . document_write . push ( result . url ) ;
69+ }
70+ if ( qa . vulnerable_libraries ?. passing === false ) {
71+ auditUrls . vulnerable_libraries . push ( result . url ) ;
72+ for ( const lib of qa . vulnerable_libraries . items ?? [ ] ) {
73+ if ( lib . library ) {
74+ if ( ! vulnerableLibraryCounts [ lib . library ] ) {
75+ vulnerableLibraryCounts [ lib . library ] = { count : 0 , severity : lib . severity } ;
76+ }
77+ vulnerableLibraryCounts [ lib . library ] . count += 1 ;
78+ }
79+ }
80+ }
81+ for ( const lib of qa . js_libraries ?. items ?? [ ] ) {
82+ if ( lib . name ) {
83+ jsLibraryCounts [ lib . name ] = ( jsLibraryCounts [ lib . name ] ?? 0 ) + 1 ;
84+ }
85+ }
86+ }
87+
88+ return {
89+ total_scanned : successful . length ,
90+ urls_with_deprecated_apis : auditUrls . deprecated_apis . length ,
91+ urls_with_console_errors : auditUrls . console_errors . length ,
92+ urls_with_document_write : auditUrls . document_write . length ,
93+ urls_with_vulnerable_libraries : auditUrls . vulnerable_libraries . length ,
94+ js_library_counts : jsLibraryCounts ,
95+ vulnerable_library_counts : vulnerableLibraryCounts ,
96+ audit_urls : auditUrls
97+ } ;
98+ }
99+
13100function normalizeTopUrls ( urlResults = [ ] , dotgovLookup = null ) {
14101 return urlResults
15102 . map ( ( result ) => {
@@ -29,6 +116,7 @@ function normalizeTopUrls(urlResults = [], dotgovLookup = null) {
29116 core_web_vitals_status : result . core_web_vitals_status ?? 'unknown' ,
30117 lcp_value_ms : typeof result . lcp_value_ms === 'number' ? result . lcp_value_ms : null ,
31118 detected_technologies : result . detected_technologies ?? null ,
119+ code_quality_summary : summarizeCodeQualityAudits ( result . code_quality_audits ) ,
32120 lighthouse_scores :
33121 result . scan_status === 'success'
34122 ? {
@@ -86,6 +174,8 @@ export function buildDailyReport({
86174 ) ;
87175 techSummary . required_links_summary = buildRequiredLinksSummary ( requiredLinks ?? { } ) ;
88176
177+ const codeQualitySummary = buildCodeQualitySummary ( urlResults ) ;
178+
89179 const sourceDataDate = urlResults . reduce ( ( latest , result ) => {
90180 const candidate = result ?. source_date ;
91181 if ( ! candidate ) {
@@ -121,6 +211,7 @@ export function buildDailyReport({
121211 source_data_date : sourceDataDate ,
122212 top_urls : topUrls ,
123213 tech_summary : techSummary ,
214+ code_quality_summary : codeQualitySummary ,
124215 trend_window_days : historyWindow ?. window_days ?? 30 ,
125216 history_series : historySeries ,
126217 generated_at : runMetadata . generated_at ,
0 commit comments