lighthouse 13.4.1 → 13.5.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/build-tracker.config.js +1 -1
- package/cli/test/smokehouse/lighthouse-runners/bundle.js +3 -3
- package/cli/test/smokehouse/lighthouse-runners/devtools-mcp.js +2 -1
- package/core/audits/accessibility/accesskeys.js +1 -1
- package/core/audits/accessibility/aria-allowed-attr.js +1 -1
- package/core/audits/accessibility/aria-allowed-role.js +1 -1
- package/core/audits/accessibility/aria-command-name.js +1 -1
- package/core/audits/accessibility/aria-conditional-attr.js +1 -1
- package/core/audits/accessibility/aria-deprecated-role.js +1 -1
- package/core/audits/accessibility/aria-dialog-name.js +1 -1
- package/core/audits/accessibility/aria-hidden-body.js +1 -1
- package/core/audits/accessibility/aria-hidden-focus.js +1 -1
- package/core/audits/accessibility/aria-input-field-name.js +1 -1
- package/core/audits/accessibility/aria-meter-name.js +1 -1
- package/core/audits/accessibility/aria-progressbar-name.js +1 -1
- package/core/audits/accessibility/aria-prohibited-attr.js +1 -1
- package/core/audits/accessibility/aria-required-attr.js +1 -1
- package/core/audits/accessibility/aria-required-children.js +1 -1
- package/core/audits/accessibility/aria-required-parent.js +1 -1
- package/core/audits/accessibility/aria-roles.js +1 -1
- package/core/audits/accessibility/aria-text.js +1 -1
- package/core/audits/accessibility/aria-toggle-field-name.js +1 -1
- package/core/audits/accessibility/aria-tooltip-name.js +1 -1
- package/core/audits/accessibility/aria-treeitem-name.js +1 -1
- package/core/audits/accessibility/aria-valid-attr-value.js +1 -1
- package/core/audits/accessibility/aria-valid-attr.js +1 -1
- package/core/audits/accessibility/autocomplete-valid.js +1 -1
- package/core/audits/accessibility/button-name.js +1 -1
- package/core/audits/accessibility/bypass.js +1 -1
- package/core/audits/accessibility/color-contrast.js +1 -1
- package/core/audits/accessibility/definition-list.js +1 -1
- package/core/audits/accessibility/dlitem.js +1 -1
- package/core/audits/accessibility/document-title.js +1 -1
- package/core/audits/accessibility/duplicate-id-aria.js +1 -1
- package/core/audits/accessibility/empty-heading.js +1 -1
- package/core/audits/accessibility/form-field-multiple-labels.js +1 -1
- package/core/audits/accessibility/frame-title.js +1 -1
- package/core/audits/accessibility/heading-order.js +1 -1
- package/core/audits/accessibility/html-has-lang.js +1 -1
- package/core/audits/accessibility/html-lang-valid.js +1 -1
- package/core/audits/accessibility/html-xml-lang-mismatch.js +1 -1
- package/core/audits/accessibility/identical-links-same-purpose.js +1 -1
- package/core/audits/accessibility/image-alt.js +1 -1
- package/core/audits/accessibility/image-redundant-alt.js +1 -1
- package/core/audits/accessibility/input-button-name.js +1 -1
- package/core/audits/accessibility/input-image-alt.js +1 -1
- package/core/audits/accessibility/label-content-name-mismatch.js +1 -1
- package/core/audits/accessibility/label.js +1 -1
- package/core/audits/accessibility/landmark-one-main.js +1 -1
- package/core/audits/accessibility/link-in-text-block.js +1 -1
- package/core/audits/accessibility/link-name.js +1 -1
- package/core/audits/accessibility/list.js +1 -1
- package/core/audits/accessibility/listitem.js +1 -1
- package/core/audits/accessibility/meta-refresh.js +1 -1
- package/core/audits/accessibility/meta-viewport.js +1 -1
- package/core/audits/accessibility/object-alt.js +1 -1
- package/core/audits/accessibility/presentation-role-conflict.js +1 -1
- package/core/audits/accessibility/select-name.js +1 -1
- package/core/audits/accessibility/skip-link.js +1 -1
- package/core/audits/accessibility/svg-img-alt.js +1 -1
- package/core/audits/accessibility/tabindex.js +1 -1
- package/core/audits/accessibility/table-duplicate-name.js +1 -1
- package/core/audits/accessibility/table-fake-caption.js +1 -1
- package/core/audits/accessibility/target-size.js +1 -1
- package/core/audits/accessibility/td-has-header.js +1 -1
- package/core/audits/accessibility/td-headers-attr.js +1 -1
- package/core/audits/accessibility/th-has-data-cells.js +1 -1
- package/core/audits/accessibility/valid-lang.js +1 -1
- package/core/audits/accessibility/video-caption.js +1 -1
- package/core/audits/agentic/ard-schema.d.ts +19 -0
- package/core/audits/agentic/ard-schema.js +135 -0
- package/core/audits/baseline.d.ts +23 -10
- package/core/audits/deprecations.js +1 -2
- package/core/audits/webmcp-form-coverage.js +3 -1
- package/core/audits/webmcp-registered-tools.d.ts +2 -0
- package/core/audits/webmcp-registered-tools.js +16 -1
- package/core/audits/webmcp-schema-validity.js +1 -1
- package/core/computed/metrics/interactive.js +1 -1
- package/core/computed/metrics/lantern-metric.js +1 -1
- package/core/computed/trace-engine-result.d.ts +3 -3
- package/core/computed/trace-engine-result.js +4 -4
- package/core/computed/unused-javascript-summary.d.ts +0 -1
- package/core/computed/unused-javascript-summary.js +0 -1
- package/core/config/agentic-browsing-config.d.ts +2 -0
- package/core/config/agentic-browsing-config.js +13 -1
- package/core/config/default-config.js +13 -1
- package/core/config/validation.js +6 -0
- package/core/gather/driver/execution-context.js +0 -2
- package/core/gather/driver/navigation.d.ts +1 -0
- package/core/gather/driver/navigation.js +35 -7
- package/core/gather/driver/wait-for-condition.d.ts +5 -7
- package/core/gather/driver/wait-for-condition.js +15 -9
- package/core/gather/fetcher.d.ts +9 -2
- package/core/gather/fetcher.js +30 -4
- package/core/gather/gatherers/accessibility.js +1 -3
- package/core/gather/gatherers/agentic/ard.d.ts +30 -0
- package/core/gather/gatherers/agentic/ard.js +125 -0
- package/core/gather/gatherers/anchor-elements.js +0 -2
- package/core/gather/gatherers/full-page-screenshot.js +0 -4
- package/core/gather/gatherers/iframe-elements.js +0 -2
- package/core/gather/gatherers/image-elements.js +1 -13
- package/core/gather/gatherers/inputs.js +0 -2
- package/core/gather/gatherers/link-elements.js +0 -2
- package/core/gather/gatherers/meta-elements.js +0 -3
- package/core/gather/gatherers/seo/robots-txt.d.ts +1 -0
- package/core/gather/gatherers/seo/robots-txt.js +3 -0
- package/core/gather/gatherers/stacks.d.ts +1 -1
- package/core/gather/gatherers/stacks.js +1 -3
- package/core/gather/gatherers/trace-elements.js +1 -3
- package/core/gather/gatherers/viewport-dimensions.js +0 -2
- package/core/gather/navigation-runner.d.ts +3 -1
- package/core/gather/navigation-runner.js +6 -3
- package/core/gather/session.js +1 -0
- package/core/index.cjs +5 -5
- package/core/index.d.cts +5 -5
- package/core/lib/arbitrary-equality-map.d.ts +2 -2
- package/core/lib/arbitrary-equality-map.js +1 -1
- package/core/lib/baseline/web-features-data.json +52 -28
- package/core/lib/baseline/web-features-metadata.json +1 -1
- package/core/lib/deprecation-description.js +1 -1
- package/core/lib/page-functions.d.ts +0 -3
- package/core/lib/page-functions.js +0 -3
- package/core/lib/rect-helpers.js +0 -8
- package/core/runner.js +21 -0
- package/core/user-flow.d.ts +3 -0
- package/core/user-flow.js +12 -0
- package/dist/report/bundle.esm.js +10 -3
- package/dist/report/flow.js +20 -13
- package/dist/report/standalone.js +10 -3
- package/package.json +19 -14
- package/report/assets/styles.css +7 -0
- package/report/renderer/components.js +1 -1
- package/report/renderer/details-renderer.js +7 -5
- package/report/renderer/drop-down-menu.d.ts +2 -2
- package/report/renderer/drop-down-menu.js +1 -1
- package/report/renderer/report-renderer.js +24 -3
- package/report/renderer/report-utils.js +1 -1
- package/report/renderer/snippet-renderer.d.ts +4 -4
- package/report/renderer/snippet-renderer.js +3 -3
- package/report/renderer/text-encoding.js +2 -2
- package/shared/localization/locales/ar-XB.json +111 -72
- package/shared/localization/locales/ar.json +113 -74
- package/shared/localization/locales/bg.json +112 -73
- package/shared/localization/locales/ca.json +112 -73
- package/shared/localization/locales/cs.json +112 -73
- package/shared/localization/locales/da.json +112 -73
- package/shared/localization/locales/de.json +112 -73
- package/shared/localization/locales/el.json +112 -73
- package/shared/localization/locales/en-GB.json +111 -72
- package/shared/localization/locales/en-US.json +102 -66
- package/shared/localization/locales/en-XA.json +111 -72
- package/shared/localization/locales/en-XL.json +102 -66
- package/shared/localization/locales/es-419.json +112 -73
- package/shared/localization/locales/es.json +112 -73
- package/shared/localization/locales/fi.json +112 -73
- package/shared/localization/locales/fil.json +112 -73
- package/shared/localization/locales/fr.json +112 -73
- package/shared/localization/locales/he.json +112 -73
- package/shared/localization/locales/hi.json +112 -73
- package/shared/localization/locales/hr.json +112 -73
- package/shared/localization/locales/hu.json +112 -73
- package/shared/localization/locales/id.json +112 -73
- package/shared/localization/locales/it.json +113 -74
- package/shared/localization/locales/ja.json +112 -73
- package/shared/localization/locales/ko.json +112 -73
- package/shared/localization/locales/lt.json +112 -73
- package/shared/localization/locales/lv.json +112 -73
- package/shared/localization/locales/nl.json +112 -73
- package/shared/localization/locales/no.json +112 -73
- package/shared/localization/locales/pl.json +112 -73
- package/shared/localization/locales/pt-PT.json +112 -73
- package/shared/localization/locales/pt.json +112 -73
- package/shared/localization/locales/ro.json +112 -73
- package/shared/localization/locales/ru.json +112 -73
- package/shared/localization/locales/sk.json +112 -73
- package/shared/localization/locales/sl.json +112 -73
- package/shared/localization/locales/sr-Latn.json +112 -73
- package/shared/localization/locales/sr.json +112 -73
- package/shared/localization/locales/sv.json +124 -85
- package/shared/localization/locales/ta.json +112 -73
- package/shared/localization/locales/te.json +112 -73
- package/shared/localization/locales/th.json +112 -73
- package/shared/localization/locales/tr.json +113 -74
- package/shared/localization/locales/uk.json +112 -73
- package/shared/localization/locales/vi.json +112 -73
- package/shared/localization/locales/zh-HK.json +112 -73
- package/shared/localization/locales/zh-TW.json +113 -74
- package/shared/localization/locales/zh.json +113 -74
- package/shared/util.d.ts +6 -0
- package/shared/util.js +9 -0
- package/third-party/ard/LICENSE +201 -0
- package/third-party/ard/README.md +27 -0
- package/third-party/ard/ard-test.d.ts +2 -0
- package/third-party/ard/ard-test.js +358 -0
- package/third-party/ard/ard.d.ts +34 -0
- package/third-party/ard/ard.js +204 -0
- package/third-party/ard/schema-validator.d.ts +20 -0
- package/third-party/ard/schema-validator.js +1700 -0
- package/third-party/ard/spec/schemas/ai-catalog.schema.json +292 -0
- package/tsconfig.json +1 -0
- package/types/artifacts.d.ts +14 -0
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import log from 'lighthouse-logger';
|
|
8
|
+
import validateAiCatalog from './schema-validator.js';
|
|
9
|
+
|
|
10
|
+
// Strict URN Regex matching urn:air:<publisher>:<namespace>:<agent-name>
|
|
11
|
+
const URN_REGEX = /^urn:air:([a-zA-Z0-9.-]+)(?::([a-zA-Z0-9._:-]+))?:([a-zA-Z0-9._-]+)$/;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {{
|
|
15
|
+
* element: string,
|
|
16
|
+
* message: string,
|
|
17
|
+
* }} ValidationError
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
class ConformanceTester {
|
|
21
|
+
constructor() {
|
|
22
|
+
/** @type {ValidationError[]} */
|
|
23
|
+
this.errors = [];
|
|
24
|
+
/** @type {ValidationError[]} */
|
|
25
|
+
this.warnings = [];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {string} message
|
|
30
|
+
* @param {string} [element='Root']
|
|
31
|
+
*/
|
|
32
|
+
add_error(message, element = 'Root') {
|
|
33
|
+
this.errors.push({element, message});
|
|
34
|
+
log.verbose('ARD', `Validation Error [${element}]: ${message}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @param {string} message
|
|
39
|
+
* @param {string} [element='Root']
|
|
40
|
+
*/
|
|
41
|
+
add_warning(message, element = 'Root') {
|
|
42
|
+
this.warnings.push({element, message});
|
|
43
|
+
log.verbose('ARD', `Validation Warning [${element}]: ${message}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** @param {any} manifest_data */
|
|
47
|
+
run_json_schema_validation(manifest_data) {
|
|
48
|
+
try {
|
|
49
|
+
const valid = validateAiCatalog(manifest_data);
|
|
50
|
+
|
|
51
|
+
if (valid) {
|
|
52
|
+
log.verbose('ARD', 'Strict JSON Schema validation passed.');
|
|
53
|
+
return true;
|
|
54
|
+
} else {
|
|
55
|
+
const errors = /** @type {any} */ (validateAiCatalog).errors;
|
|
56
|
+
const e = errors?.[0];
|
|
57
|
+
const propertyPath = e?.instancePath ? e.instancePath.replace(/^\//, '').replace(/\//g, '.') : 'root';
|
|
58
|
+
let msg = e?.message || 'unknown schema error';
|
|
59
|
+
if (e?.keyword === 'required') {
|
|
60
|
+
msg = `'${e.params.missingProperty}' is a required property`;
|
|
61
|
+
}
|
|
62
|
+
this.add_error(`JSON Schema Validation Failed: ${msg} at path '${propertyPath}'`, 'Root');
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
} catch (e) {
|
|
66
|
+
this.add_warning(`Failed to run JSON Schema validator: ${e}`, 'Root');
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @param {string} raw_content
|
|
73
|
+
* @param {string} source_label
|
|
74
|
+
*/
|
|
75
|
+
validate_manifest(raw_content, source_label) {
|
|
76
|
+
log.verbose('ARD', `Validating Manifest: ${source_label}`);
|
|
77
|
+
|
|
78
|
+
// 1. Basic JSON Parsing
|
|
79
|
+
let data;
|
|
80
|
+
try {
|
|
81
|
+
data = JSON.parse(raw_content);
|
|
82
|
+
log.verbose('ARD', 'Manifest parsed successfully as valid JSON.');
|
|
83
|
+
} catch (e) {
|
|
84
|
+
this.add_error(`Malformed JSON in manifest: ${e}`, 'Root');
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 2. Strict JSON Schema Validation
|
|
89
|
+
this.run_json_schema_validation(data);
|
|
90
|
+
|
|
91
|
+
// 3. Custom Semantic and Protocol-Specific Validation
|
|
92
|
+
log.verbose('ARD', 'Running custom semantic checks...');
|
|
93
|
+
|
|
94
|
+
const spec_ver = data["specVersion"];
|
|
95
|
+
if (!spec_ver) {
|
|
96
|
+
this.add_error("Missing required 'specVersion' root property.", 'Root');
|
|
97
|
+
} else if (spec_ver !== "1.0") {
|
|
98
|
+
this.add_warning(`Unrecognized 'specVersion': ${spec_ver}. Expected '1.0'.`, 'Root');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const entries = data["entries"];
|
|
102
|
+
if (entries === undefined || entries === null) {
|
|
103
|
+
this.add_error("Missing required 'entries' array.", 'Root');
|
|
104
|
+
return false;
|
|
105
|
+
} else if (!Array.isArray(entries)) {
|
|
106
|
+
this.add_error("'entries' must be a JSON array.", 'Root');
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
log.verbose('ARD', `Found ${entries.length} entries to validate.`);
|
|
111
|
+
entries.forEach((entry, idx) => {
|
|
112
|
+
const label = entry["displayName"] || entry["identifier"] || `Entry #${idx}`;
|
|
113
|
+
|
|
114
|
+
// Required properties
|
|
115
|
+
const ident = entry["identifier"];
|
|
116
|
+
if (!ident) {
|
|
117
|
+
this.add_error("Missing required 'identifier'.", label);
|
|
118
|
+
} else {
|
|
119
|
+
// URN pattern checks
|
|
120
|
+
const match = URN_REGEX.exec(ident);
|
|
121
|
+
if (!match) {
|
|
122
|
+
this.add_error(`Identifier '${ident}' does not match RFC 8141 URN pattern 'urn:air:<publisher>:<namespace>:<agent-name>'.`, label);
|
|
123
|
+
} else {
|
|
124
|
+
const publisher = match[1];
|
|
125
|
+
const name = match[3];
|
|
126
|
+
log.verbose('ARD', `[${label}] Valid URN format. Publisher: '${publisher}', Name: '${name}'.`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const disp_name = entry["displayName"];
|
|
131
|
+
if (!disp_name) {
|
|
132
|
+
this.add_error("Missing required 'displayName'.", label);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const media_type = entry["type"];
|
|
136
|
+
if (!media_type) {
|
|
137
|
+
this.add_error("Missing required 'type' (mediaType).", label);
|
|
138
|
+
} else {
|
|
139
|
+
const valid_types = [
|
|
140
|
+
"application/ai-catalog+json",
|
|
141
|
+
"application/agent-card+json",
|
|
142
|
+
"application/a2a-agent-card+json",
|
|
143
|
+
"application/mcp-server-card+json",
|
|
144
|
+
"application/agent-skills+zip",
|
|
145
|
+
"application/agent-skills+gzip",
|
|
146
|
+
"text/markdown; profile=\"urn:air:agent-skills\"",
|
|
147
|
+
"application/ai-registry",
|
|
148
|
+
"application/ai-registry+json"
|
|
149
|
+
];
|
|
150
|
+
if (!valid_types.includes(media_type)) {
|
|
151
|
+
this.add_warning(`Media type '${media_type}' is not one of standard discovery types: ${valid_types}.`, label);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Strict Value-or-Reference checks
|
|
156
|
+
const has_url = entry["url"] !== undefined;
|
|
157
|
+
const has_data = entry["data"] !== undefined;
|
|
158
|
+
if (has_url && has_data) {
|
|
159
|
+
this.add_error("Constraint violation: both 'url' and 'data' are provided. MUST provide exactly one.", label);
|
|
160
|
+
} else if (!has_url && !has_data) {
|
|
161
|
+
this.add_error("Constraint violation: neither 'url' nor 'data' is provided. MUST provide exactly one.", label);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Custom constraints for representativeQueries
|
|
165
|
+
const queries = entry["representativeQueries"];
|
|
166
|
+
if (queries === undefined || queries === null) {
|
|
167
|
+
this.add_warning("Missing 'representativeQueries'. 2 to 5 queries are recommended for vector index embedding.", label);
|
|
168
|
+
} else if (!Array.isArray(queries)) {
|
|
169
|
+
this.add_error("'representativeQueries' must be an array of strings.", label);
|
|
170
|
+
} else {
|
|
171
|
+
if (queries.length < 2 || queries.length > 5) {
|
|
172
|
+
this.add_warning(`'representativeQueries' array has size ${queries.length}. 2 to 5 queries are recommended for vector index embedding.`, label);
|
|
173
|
+
}
|
|
174
|
+
for (const q of queries) {
|
|
175
|
+
if (typeof q !== 'string') {
|
|
176
|
+
this.add_error(`Query '${q}' is not a string.`, label);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Progressive trust checks
|
|
182
|
+
const trust = entry["trustManifest"];
|
|
183
|
+
if (trust !== undefined && trust !== null) {
|
|
184
|
+
if (typeof trust !== 'object' || Array.isArray(trust)) {
|
|
185
|
+
this.add_error("'trustManifest' must be a JSON object.", label);
|
|
186
|
+
} else {
|
|
187
|
+
const trust_id = trust["identity"];
|
|
188
|
+
if (!trust_id) {
|
|
189
|
+
this.add_error("'trustManifest' is missing required 'identity' field.", label);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// Top-level deprecated property check
|
|
196
|
+
if ("collections" in data) {
|
|
197
|
+
this.add_error("Deprecated field check: Found 'collections' array at root. Top-level collections were REMOVED in ADR-0003. Catalog hierarchies MUST be modeled inside 'entries' using 'type: application/ai-catalog+json'.", 'Root');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return this.errors.length === 0;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export { ConformanceTester };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function validate(data: any, { instancePath, parentData, parentDataProperty, rootData, dynamicAnchors }?: {
|
|
2
|
+
instancePath?: string | undefined;
|
|
3
|
+
rootData?: any;
|
|
4
|
+
dynamicAnchors?: {} | undefined;
|
|
5
|
+
}): boolean;
|
|
6
|
+
export namespace validate {
|
|
7
|
+
namespace evaluated {
|
|
8
|
+
let props: boolean;
|
|
9
|
+
let dynamicProps: boolean;
|
|
10
|
+
let dynamicItems: boolean;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export default validate20;
|
|
14
|
+
declare function validate20(data: any, { instancePath, parentData, parentDataProperty, rootData, dynamicAnchors }?: {
|
|
15
|
+
instancePath?: string | undefined;
|
|
16
|
+
rootData?: any;
|
|
17
|
+
dynamicAnchors?: {} | undefined;
|
|
18
|
+
}): boolean;
|
|
19
|
+
declare namespace validate20 { }
|
|
20
|
+
//# sourceMappingURL=schema-validator.d.ts.map
|