@oalacea/daemon 0.5.0 → 0.5.1
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 +46 -38
- package/LICENSE +23 -23
- package/README.md +147 -141
- package/agents/deps-analyzer.js +366 -366
- package/agents/detector.js +570 -570
- package/agents/fix-engine.js +305 -305
- package/agents/lighthouse-scanner.js +405 -405
- package/agents/perf-analyzer.js +294 -294
- package/agents/perf-front-analyzer.js +229 -229
- package/agents/test-generator.js +387 -387
- package/agents/test-runner.js +318 -318
- package/bin/Dockerfile +75 -74
- package/bin/cli.js +449 -449
- package/lib/config.js +250 -250
- package/lib/docker.js +207 -207
- package/lib/reporter.js +297 -297
- package/package.json +34 -34
- package/prompts/DEPS_EFFICIENCY.md +558 -558
- package/prompts/E2E.md +491 -491
- package/prompts/EXECUTE.md +1060 -1060
- package/prompts/INTEGRATION_API.md +484 -484
- package/prompts/INTEGRATION_DB.md +425 -425
- package/prompts/PERF_API.md +433 -433
- package/prompts/PERF_DB.md +430 -430
- package/prompts/PERF_FRONT.md +357 -357
- package/prompts/REMEDIATION.md +482 -482
- package/prompts/UNIT.md +260 -260
- package/scripts/dev.js +106 -106
- package/templates/README.md +38 -38
- package/templates/k6/load-test.js +54 -54
- package/templates/playwright/e2e.spec.ts +61 -61
- package/templates/vitest/angular-component.test.ts +38 -38
- package/templates/vitest/api.test.ts +51 -51
- package/templates/vitest/component.test.ts +27 -27
- package/templates/vitest/hook.test.ts +36 -36
- package/templates/vitest/solid-component.test.ts +34 -34
- package/templates/vitest/svelte-component.test.ts +33 -33
- package/templates/vitest/vue-component.test.ts +39 -39
|
@@ -1,229 +1,229 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Daemon - Frontend Performance Analyzer
|
|
3
|
-
*
|
|
4
|
-
* Analyzes frontend performance using Lighthouse:
|
|
5
|
-
* - Core Web Vitals (LCP, FID, CLS)
|
|
6
|
-
* - Performance score
|
|
7
|
-
* - Accessibility
|
|
8
|
-
* - Best Practices
|
|
9
|
-
* - SEO
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
const { execSync } = require('child_process');
|
|
13
|
-
const fs = require('fs');
|
|
14
|
-
const path = require('path');
|
|
15
|
-
|
|
16
|
-
const CONFIG = {
|
|
17
|
-
container: 'daemon-tools',
|
|
18
|
-
docker: 'docker exec',
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Run Lighthouse audit
|
|
23
|
-
*/
|
|
24
|
-
function runLighthouse(url, options = {}) {
|
|
25
|
-
const outputPath = options.output || path.join(process.cwd(), 'lighthouse-report.json');
|
|
26
|
-
const htmlOutput = outputPath.replace('.json', '.html');
|
|
27
|
-
|
|
28
|
-
const cmd = [
|
|
29
|
-
`docker exec ${CONFIG.container} npx lighthouse`,
|
|
30
|
-
url,
|
|
31
|
-
`--output=json --output=html`,
|
|
32
|
-
`--chrome-flags="--headless --no-sandbox --disable-gpu"`,
|
|
33
|
-
`--quiet`,
|
|
34
|
-
options.onlyCategories ? `--only-categories=${options.onlyCategories}` : '',
|
|
35
|
-
options.formFactor ? `--form-factor=${options.formFactor}` : '--throttling-method=devtools',
|
|
36
|
-
options.screenEmulation ? `--screen-emulation=${options.screenEmulation}` : '',
|
|
37
|
-
].filter(Boolean).join(' ');
|
|
38
|
-
|
|
39
|
-
try {
|
|
40
|
-
execSync(cmd, { encoding: 'utf-8', stdio: 'pipe', timeout: 120000 });
|
|
41
|
-
|
|
42
|
-
// Read the JSON report (container writes to /app)
|
|
43
|
-
const reportPath = path.join(process.cwd(), 'lighthouse-report.json');
|
|
44
|
-
if (fs.existsSync(reportPath)) {
|
|
45
|
-
const report = JSON.parse(fs.readFileSync(reportPath, 'utf-8'));
|
|
46
|
-
return { success: true, report, htmlOutput };
|
|
47
|
-
}
|
|
48
|
-
} catch (error) {
|
|
49
|
-
return {
|
|
50
|
-
success: false,
|
|
51
|
-
error: error.message,
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Analyze Lighthouse results
|
|
58
|
-
*/
|
|
59
|
-
function analyzeResults(report) {
|
|
60
|
-
const categories = report.categories || {};
|
|
61
|
-
const audits = report.audits || {};
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
scores: {
|
|
65
|
-
performance: categories.performance?.score || 0,
|
|
66
|
-
accessibility: categories.accessibility?.score || 0,
|
|
67
|
-
bestPractices: categories['best-practices']?.score || 0,
|
|
68
|
-
seo: categories.seo?.score || 0,
|
|
69
|
-
pwa: categories.pwa?.score || 0,
|
|
70
|
-
},
|
|
71
|
-
coreWebVitals: {
|
|
72
|
-
lcp: audits['largest-contentful-paint']?.displayValue || 'N/A',
|
|
73
|
-
fid: audits['max-potential-fid']?.displayValue || 'N/A',
|
|
74
|
-
cls: audits['cumulative-layout-shift']?.displayValue || 'N/A',
|
|
75
|
-
ttfb: audits['time-to-first-byte']?.displayValue || 'N/A',
|
|
76
|
-
fcp: audits['first-contentful-paint']?.displayValue || 'N/A',
|
|
77
|
-
tti: audits['interactive']?.displayValue || 'N/A',
|
|
78
|
-
si: audits['speed-index']?.displayValue || 'N/A',
|
|
79
|
-
},
|
|
80
|
-
opportunities: (opportunities || []).map(item => ({
|
|
81
|
-
id: item.id,
|
|
82
|
-
title: item.title,
|
|
83
|
-
description: item.description,
|
|
84
|
-
score: item.score,
|
|
85
|
-
displayValue: item.displayValue,
|
|
86
|
-
})),
|
|
87
|
-
diagnostics: Object.values(audits)
|
|
88
|
-
.filter(audit => audit.details && audit.details.type === 'table')
|
|
89
|
-
.map(audit => ({
|
|
90
|
-
id: audit.id,
|
|
91
|
-
title: audit.title,
|
|
92
|
-
description: audit.description,
|
|
93
|
-
items: audit.details?.items || [],
|
|
94
|
-
})),
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Get performance opportunities
|
|
100
|
-
*/
|
|
101
|
-
function getOpportunities(report) {
|
|
102
|
-
const opportunities = [];
|
|
103
|
-
|
|
104
|
-
if (!report.audits) return opportunities;
|
|
105
|
-
|
|
106
|
-
const opportunityAudits = [
|
|
107
|
-
'render-blocking-resources',
|
|
108
|
-
'unminified-css',
|
|
109
|
-
'unminified-javascript',
|
|
110
|
-
'unused-css-rules',
|
|
111
|
-
'unused-javascript',
|
|
112
|
-
'modern-image-formats',
|
|
113
|
-
'offscreen-images',
|
|
114
|
-
'scaled-images',
|
|
115
|
-
'efficient-animated-content',
|
|
116
|
-
'text-compression',
|
|
117
|
-
'uses-long-cache-ttl',
|
|
118
|
-
'total-byte-weight',
|
|
119
|
-
'uses-optimized-images',
|
|
120
|
-
];
|
|
121
|
-
|
|
122
|
-
for (const id of opportunityAudits) {
|
|
123
|
-
const audit = report.audits[id];
|
|
124
|
-
if (audit && audit.score !== null && audit.score < 1) {
|
|
125
|
-
opportunities.push({
|
|
126
|
-
id,
|
|
127
|
-
title: audit.title,
|
|
128
|
-
description: audit.description,
|
|
129
|
-
score: audit.score,
|
|
130
|
-
displayValue: audit.displayValue,
|
|
131
|
-
severity: audit.score < 0.5 ? 'high' : 'medium',
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return opportunities.sort((a, b) => a.score - b.score);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* Generate performance recommendations
|
|
141
|
-
*/
|
|
142
|
-
function generateRecommendations(results) {
|
|
143
|
-
const recommendations = [];
|
|
144
|
-
const { scores, coreWebVitals, opportunities } = results;
|
|
145
|
-
|
|
146
|
-
// Performance score recommendations
|
|
147
|
-
if (scores.performance < 0.5) {
|
|
148
|
-
recommendations.push({
|
|
149
|
-
type: 'critical',
|
|
150
|
-
message: `Performance score is ${(scores.performance * 100).toFixed(0)}/100. Immediate optimization required.`,
|
|
151
|
-
});
|
|
152
|
-
} else if (scores.performance < 0.8) {
|
|
153
|
-
recommendations.push({
|
|
154
|
-
type: 'warning',
|
|
155
|
-
message: `Performance score is ${(scores.performance * 100).toFixed(0)}/100. Optimization recommended.`,
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Core Web Vitals recommendations
|
|
160
|
-
if (coreWebVitals.lcp) {
|
|
161
|
-
const lcpValue = parseFloat(coreWebVitals.lcp);
|
|
162
|
-
if (lcpValue > 4000) {
|
|
163
|
-
recommendations.push({
|
|
164
|
-
type: 'critical',
|
|
165
|
-
metric: 'LCP',
|
|
166
|
-
message: `LCP is ${coreWebVitals.lcp} (target: <2.5s). Consider lazy loading, code splitting, or optimizing images.`,
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if (coreWebVitals.cls) {
|
|
172
|
-
const clsValue = parseFloat(coreWebVitals.cls);
|
|
173
|
-
if (clsValue > 0.25) {
|
|
174
|
-
recommendations.push({
|
|
175
|
-
type: 'critical',
|
|
176
|
-
metric: 'CLS',
|
|
177
|
-
message: `CLS is ${coreWebVitals.cls} (target: <0.1). Reserve space for dynamic content.`,
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Opportunity-based recommendations
|
|
183
|
-
for (const opp of opportunities.slice(0, 5)) {
|
|
184
|
-
recommendations.push({
|
|
185
|
-
type: opp.severity === 'high' ? 'warning' : 'info',
|
|
186
|
-
message: `${opp.title}: ${opp.displayValue || 'Needs improvement'}`,
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return recommendations;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Run mobile audit
|
|
195
|
-
*/
|
|
196
|
-
function runMobileAudit(url) {
|
|
197
|
-
return runLighthouse(url, {
|
|
198
|
-
formFactor: 'mobile',
|
|
199
|
-
screenEmulation: 'mobile',
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Run desktop audit
|
|
205
|
-
*/
|
|
206
|
-
function runDesktopAudit(url) {
|
|
207
|
-
return runLighthouse(url, {
|
|
208
|
-
formFactor: 'desktop',
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Run performance-only audit (faster)
|
|
214
|
-
*/
|
|
215
|
-
function runPerformanceOnlyAudit(url) {
|
|
216
|
-
return runLighthouse(url, {
|
|
217
|
-
onlyCategories: 'performance',
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
module.exports = {
|
|
222
|
-
runLighthouse,
|
|
223
|
-
analyzeResults,
|
|
224
|
-
getOpportunities,
|
|
225
|
-
generateRecommendations,
|
|
226
|
-
runMobileAudit,
|
|
227
|
-
runDesktopAudit,
|
|
228
|
-
runPerformanceOnlyAudit,
|
|
229
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Daemon - Frontend Performance Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Analyzes frontend performance using Lighthouse:
|
|
5
|
+
* - Core Web Vitals (LCP, FID, CLS)
|
|
6
|
+
* - Performance score
|
|
7
|
+
* - Accessibility
|
|
8
|
+
* - Best Practices
|
|
9
|
+
* - SEO
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const { execSync } = require('child_process');
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
const CONFIG = {
|
|
17
|
+
container: 'daemon-tools',
|
|
18
|
+
docker: 'docker exec',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Run Lighthouse audit
|
|
23
|
+
*/
|
|
24
|
+
function runLighthouse(url, options = {}) {
|
|
25
|
+
const outputPath = options.output || path.join(process.cwd(), 'lighthouse-report.json');
|
|
26
|
+
const htmlOutput = outputPath.replace('.json', '.html');
|
|
27
|
+
|
|
28
|
+
const cmd = [
|
|
29
|
+
`docker exec ${CONFIG.container} npx lighthouse`,
|
|
30
|
+
url,
|
|
31
|
+
`--output=json --output=html`,
|
|
32
|
+
`--chrome-flags="--headless --no-sandbox --disable-gpu"`,
|
|
33
|
+
`--quiet`,
|
|
34
|
+
options.onlyCategories ? `--only-categories=${options.onlyCategories}` : '',
|
|
35
|
+
options.formFactor ? `--form-factor=${options.formFactor}` : '--throttling-method=devtools',
|
|
36
|
+
options.screenEmulation ? `--screen-emulation=${options.screenEmulation}` : '',
|
|
37
|
+
].filter(Boolean).join(' ');
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
execSync(cmd, { encoding: 'utf-8', stdio: 'pipe', timeout: 120000 });
|
|
41
|
+
|
|
42
|
+
// Read the JSON report (container writes to /app)
|
|
43
|
+
const reportPath = path.join(process.cwd(), 'lighthouse-report.json');
|
|
44
|
+
if (fs.existsSync(reportPath)) {
|
|
45
|
+
const report = JSON.parse(fs.readFileSync(reportPath, 'utf-8'));
|
|
46
|
+
return { success: true, report, htmlOutput };
|
|
47
|
+
}
|
|
48
|
+
} catch (error) {
|
|
49
|
+
return {
|
|
50
|
+
success: false,
|
|
51
|
+
error: error.message,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Analyze Lighthouse results
|
|
58
|
+
*/
|
|
59
|
+
function analyzeResults(report) {
|
|
60
|
+
const categories = report.categories || {};
|
|
61
|
+
const audits = report.audits || {};
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
scores: {
|
|
65
|
+
performance: categories.performance?.score || 0,
|
|
66
|
+
accessibility: categories.accessibility?.score || 0,
|
|
67
|
+
bestPractices: categories['best-practices']?.score || 0,
|
|
68
|
+
seo: categories.seo?.score || 0,
|
|
69
|
+
pwa: categories.pwa?.score || 0,
|
|
70
|
+
},
|
|
71
|
+
coreWebVitals: {
|
|
72
|
+
lcp: audits['largest-contentful-paint']?.displayValue || 'N/A',
|
|
73
|
+
fid: audits['max-potential-fid']?.displayValue || 'N/A',
|
|
74
|
+
cls: audits['cumulative-layout-shift']?.displayValue || 'N/A',
|
|
75
|
+
ttfb: audits['time-to-first-byte']?.displayValue || 'N/A',
|
|
76
|
+
fcp: audits['first-contentful-paint']?.displayValue || 'N/A',
|
|
77
|
+
tti: audits['interactive']?.displayValue || 'N/A',
|
|
78
|
+
si: audits['speed-index']?.displayValue || 'N/A',
|
|
79
|
+
},
|
|
80
|
+
opportunities: (opportunities || []).map(item => ({
|
|
81
|
+
id: item.id,
|
|
82
|
+
title: item.title,
|
|
83
|
+
description: item.description,
|
|
84
|
+
score: item.score,
|
|
85
|
+
displayValue: item.displayValue,
|
|
86
|
+
})),
|
|
87
|
+
diagnostics: Object.values(audits)
|
|
88
|
+
.filter(audit => audit.details && audit.details.type === 'table')
|
|
89
|
+
.map(audit => ({
|
|
90
|
+
id: audit.id,
|
|
91
|
+
title: audit.title,
|
|
92
|
+
description: audit.description,
|
|
93
|
+
items: audit.details?.items || [],
|
|
94
|
+
})),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Get performance opportunities
|
|
100
|
+
*/
|
|
101
|
+
function getOpportunities(report) {
|
|
102
|
+
const opportunities = [];
|
|
103
|
+
|
|
104
|
+
if (!report.audits) return opportunities;
|
|
105
|
+
|
|
106
|
+
const opportunityAudits = [
|
|
107
|
+
'render-blocking-resources',
|
|
108
|
+
'unminified-css',
|
|
109
|
+
'unminified-javascript',
|
|
110
|
+
'unused-css-rules',
|
|
111
|
+
'unused-javascript',
|
|
112
|
+
'modern-image-formats',
|
|
113
|
+
'offscreen-images',
|
|
114
|
+
'scaled-images',
|
|
115
|
+
'efficient-animated-content',
|
|
116
|
+
'text-compression',
|
|
117
|
+
'uses-long-cache-ttl',
|
|
118
|
+
'total-byte-weight',
|
|
119
|
+
'uses-optimized-images',
|
|
120
|
+
];
|
|
121
|
+
|
|
122
|
+
for (const id of opportunityAudits) {
|
|
123
|
+
const audit = report.audits[id];
|
|
124
|
+
if (audit && audit.score !== null && audit.score < 1) {
|
|
125
|
+
opportunities.push({
|
|
126
|
+
id,
|
|
127
|
+
title: audit.title,
|
|
128
|
+
description: audit.description,
|
|
129
|
+
score: audit.score,
|
|
130
|
+
displayValue: audit.displayValue,
|
|
131
|
+
severity: audit.score < 0.5 ? 'high' : 'medium',
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return opportunities.sort((a, b) => a.score - b.score);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Generate performance recommendations
|
|
141
|
+
*/
|
|
142
|
+
function generateRecommendations(results) {
|
|
143
|
+
const recommendations = [];
|
|
144
|
+
const { scores, coreWebVitals, opportunities } = results;
|
|
145
|
+
|
|
146
|
+
// Performance score recommendations
|
|
147
|
+
if (scores.performance < 0.5) {
|
|
148
|
+
recommendations.push({
|
|
149
|
+
type: 'critical',
|
|
150
|
+
message: `Performance score is ${(scores.performance * 100).toFixed(0)}/100. Immediate optimization required.`,
|
|
151
|
+
});
|
|
152
|
+
} else if (scores.performance < 0.8) {
|
|
153
|
+
recommendations.push({
|
|
154
|
+
type: 'warning',
|
|
155
|
+
message: `Performance score is ${(scores.performance * 100).toFixed(0)}/100. Optimization recommended.`,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Core Web Vitals recommendations
|
|
160
|
+
if (coreWebVitals.lcp) {
|
|
161
|
+
const lcpValue = parseFloat(coreWebVitals.lcp);
|
|
162
|
+
if (lcpValue > 4000) {
|
|
163
|
+
recommendations.push({
|
|
164
|
+
type: 'critical',
|
|
165
|
+
metric: 'LCP',
|
|
166
|
+
message: `LCP is ${coreWebVitals.lcp} (target: <2.5s). Consider lazy loading, code splitting, or optimizing images.`,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (coreWebVitals.cls) {
|
|
172
|
+
const clsValue = parseFloat(coreWebVitals.cls);
|
|
173
|
+
if (clsValue > 0.25) {
|
|
174
|
+
recommendations.push({
|
|
175
|
+
type: 'critical',
|
|
176
|
+
metric: 'CLS',
|
|
177
|
+
message: `CLS is ${coreWebVitals.cls} (target: <0.1). Reserve space for dynamic content.`,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Opportunity-based recommendations
|
|
183
|
+
for (const opp of opportunities.slice(0, 5)) {
|
|
184
|
+
recommendations.push({
|
|
185
|
+
type: opp.severity === 'high' ? 'warning' : 'info',
|
|
186
|
+
message: `${opp.title}: ${opp.displayValue || 'Needs improvement'}`,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return recommendations;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Run mobile audit
|
|
195
|
+
*/
|
|
196
|
+
function runMobileAudit(url) {
|
|
197
|
+
return runLighthouse(url, {
|
|
198
|
+
formFactor: 'mobile',
|
|
199
|
+
screenEmulation: 'mobile',
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Run desktop audit
|
|
205
|
+
*/
|
|
206
|
+
function runDesktopAudit(url) {
|
|
207
|
+
return runLighthouse(url, {
|
|
208
|
+
formFactor: 'desktop',
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Run performance-only audit (faster)
|
|
214
|
+
*/
|
|
215
|
+
function runPerformanceOnlyAudit(url) {
|
|
216
|
+
return runLighthouse(url, {
|
|
217
|
+
onlyCategories: 'performance',
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
module.exports = {
|
|
222
|
+
runLighthouse,
|
|
223
|
+
analyzeResults,
|
|
224
|
+
getOpportunities,
|
|
225
|
+
generateRecommendations,
|
|
226
|
+
runMobileAudit,
|
|
227
|
+
runDesktopAudit,
|
|
228
|
+
runPerformanceOnlyAudit,
|
|
229
|
+
};
|