@agentskit/doc-bridge 1.6.4 → 1.7.45
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 +249 -0
- package/CONTRIBUTING.md +6 -4
- package/action.yml +1 -1
- package/dist/cli/program.js +1139 -294
- package/dist/cli/program.js.map +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/config/index.js +43 -5
- package/dist/config/index.js.map +1 -1
- package/dist/index-BUL0q7s8.d.ts +660 -0
- package/dist/index.d.ts +817 -2134
- package/dist/index.js +1154 -244
- package/dist/index.js.map +1 -1
- package/docs/PRD-enterprise-hardening.md +288 -0
- package/docs/RELEASE.md +22 -8
- package/docs/adr/0001-enterprise-verification-contract.md +35 -0
- package/docs/agent-corpus/INDEX.md +2 -2
- package/docs/agent-corpus/chat.md +2 -2
- package/docs/agent-corpus/cli.md +2 -2
- package/docs/agent-corpus/conformance.md +2 -2
- package/docs/agent-corpus/doc-bridge.md +1 -1
- package/docs/agent-corpus/doctor.md +2 -2
- package/docs/agent-corpus/gates.md +2 -2
- package/docs/agent-corpus/mcp.md +2 -2
- package/docs/agent-corpus/memory.md +2 -2
- package/docs/agent-corpus/query.md +2 -2
- package/docs/knowledge-engine-runbook.md +30 -2
- package/docs/spec/analyzer-plugin-v1.md +24 -0
- package/docs/spec/benchmark-v1.md +36 -0
- package/docs/spec/config-v1.md +156 -0
- package/docs/validation-cycle-plan.md +255 -0
- package/docs/verification-harness.md +37 -4
- package/mcpb/manifest.json +1 -1
- package/package.json +68 -70
- package/scripts/check-ecosystem-upstream.mjs +3 -2
- package/scripts/report-visual-check.mjs +64 -12
- package/scripts/verification-harness.mjs +216 -14
- package/skills/doc-bridge-handoff/scripts/resolve-handoff.mjs +1 -1
- package/src/agents/registry-adapter.ts +31 -7
- package/src/cli/demo.ts +2 -2
- package/src/cli/program.ts +59 -16
- package/src/config/index.ts +2 -0
- package/src/config/load-config.ts +7 -1
- package/src/config/schema.ts +60 -2
- package/src/conformance/documentation-standard-v1.ts +14 -8
- package/src/discovery/documentation.ts +90 -23
- package/src/discovery/repository.ts +147 -19
- package/src/doctor/run-doctor.ts +2 -15
- package/src/federation/llms.ts +72 -20
- package/src/fixes/proposals.ts +4 -3
- package/src/index-builder/human-adapters/fumadocs.ts +1 -1
- package/src/index-builder/watch-index.ts +1 -1
- package/src/index.ts +29 -0
- package/src/lib/bounded-text.ts +15 -10
- package/src/metrics/benchmark.ts +176 -0
- package/src/plugins/contract.ts +89 -0
- package/src/reconciliation/reconcile.ts +181 -5
- package/src/report/html.ts +318 -88
- package/src/rules/engine.ts +15 -2
- package/src/safety/repository.ts +1 -1
- package/src/schemas/knowledge.ts +21 -3
- package/src/validate.ts +7 -1
- package/src/version.ts +1 -1
- package/src/workflow/engine.ts +65 -9
- package/dist/index-DudNuwI5.d.ts +0 -2060
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentskit/doc-bridge",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.45",
|
|
4
4
|
"mcpName": "io.github.AgentsKit-io/doc-bridge",
|
|
5
5
|
"description": "Human↔agent documentation bridge — deterministic handoffs, doc-site links, memory→docs, optional AgentsKit RAG/chat.",
|
|
6
6
|
"type": "module",
|
|
@@ -46,51 +46,6 @@
|
|
|
46
46
|
"src",
|
|
47
47
|
"skills"
|
|
48
48
|
],
|
|
49
|
-
"scripts": {
|
|
50
|
-
"prebuild": "node scripts/sync-version.mjs",
|
|
51
|
-
"build": "tsup",
|
|
52
|
-
"test": "vitest run && pnpm test:cursor-plugin && pnpm test:claude-plugin && pnpm test:copilot-plugin && pnpm test:portable-skill",
|
|
53
|
-
"test:watch": "vitest",
|
|
54
|
-
"coverage": "vitest run --coverage",
|
|
55
|
-
"check:ecosystem-upstream": "node scripts/check-ecosystem-upstream.mjs",
|
|
56
|
-
"check:no-legacy-chat-imports": "node scripts/check-no-legacy-chat-imports.mjs",
|
|
57
|
-
"typecheck": "tsc --noEmit",
|
|
58
|
-
"smoke:packaged": "node scripts/smoke-packaged.mjs",
|
|
59
|
-
"smoke:docsites": "node scripts/smoke-docsites.mjs",
|
|
60
|
-
"smoke:real-docsites": "node scripts/smoke-real-docsites.mjs",
|
|
61
|
-
"smoke:ollama": "node scripts/smoke-ollama.mjs",
|
|
62
|
-
"mcpb:stage": "npm run build && node scripts/build-mcpb.mjs stage",
|
|
63
|
-
"mcpb:validate": "node scripts/build-mcpb.mjs validate",
|
|
64
|
-
"mcpb:smoke": "node scripts/smoke-mcpb.mjs",
|
|
65
|
-
"mcpb:pack": "npm run mcpb:stage && npm run mcpb:smoke && node scripts/build-mcpb.mjs pack",
|
|
66
|
-
"test:mcpb": "node --test scripts/mcpb-contract.test.mjs",
|
|
67
|
-
"test:cursor-plugin": "node --test scripts/cursor-plugin-contract.test.mjs",
|
|
68
|
-
"test:claude-plugin": "node --test scripts/claude-plugin-contract.test.mjs",
|
|
69
|
-
"test:copilot-plugin": "node --test scripts/copilot-plugin-contract.test.mjs",
|
|
70
|
-
"test:portable-skill": "node --test scripts/portable-skill-contract.test.mjs",
|
|
71
|
-
"coverage:badge": "node scripts/update-coverage-badge.mjs",
|
|
72
|
-
"changeset": "changeset",
|
|
73
|
-
"version-packages": "changeset version && node scripts/sync-version.mjs",
|
|
74
|
-
"release": "npm run build && npm test && changeset publish",
|
|
75
|
-
"version": "node scripts/sync-version.mjs && git add package.json src/version.ts action.yml",
|
|
76
|
-
"prepublishOnly": "npm run build && npm test",
|
|
77
|
-
"prepare": "node scripts/prepare.mjs",
|
|
78
|
-
"prepack": "npm run build",
|
|
79
|
-
"docs:dev": "cd apps/docs && next dev",
|
|
80
|
-
"docs:artifacts": "node scripts/build-docs-artifacts.mjs",
|
|
81
|
-
"docs:build": "pnpm build && node bin/ak-docs.js index && pnpm docs:artifacts && cd apps/docs && next build",
|
|
82
|
-
"docs:typecheck": "cd apps/docs && fumadocs-mdx && tsc --noEmit",
|
|
83
|
-
"docs:e2e": "playwright test",
|
|
84
|
-
"report:visual": "node scripts/report-visual-check.mjs",
|
|
85
|
-
"test:verification-harness": "node --test scripts/verification-harness.test.mjs",
|
|
86
|
-
"docs:lighthouse": "lhci autorun",
|
|
87
|
-
"test:readme-standard": "node --test scripts/readme-standard.test.mjs",
|
|
88
|
-
"test:marketplace": "node --test scripts/marketplace-contract.test.mjs",
|
|
89
|
-
"check:marketplace": "node scripts/check-marketplace-contract.mjs",
|
|
90
|
-
"test:docs-artifacts": "node --test scripts/docs-artifacts-contract.test.mjs",
|
|
91
|
-
"check:readme-standard": "node scripts/check-readme-standard.mjs",
|
|
92
|
-
"check:docs": "pnpm check:readme-standard && node bin/ak-docs.js gate run && node bin/ak-docs.js conformance run documentation-standard-v1 --text && pnpm docs:typecheck && pnpm docs:build && pnpm test:docs-artifacts"
|
|
93
|
-
},
|
|
94
49
|
"keywords": [
|
|
95
50
|
"agentskit",
|
|
96
51
|
"documentation",
|
|
@@ -120,10 +75,10 @@
|
|
|
120
75
|
},
|
|
121
76
|
"dependencies": {
|
|
122
77
|
"github-slugger": "^2.0.0",
|
|
123
|
-
"mermaid": "^11.
|
|
78
|
+
"mermaid": "^11.17.2",
|
|
124
79
|
"minimatch": "^10.2.6",
|
|
125
80
|
"typescript": "^6.0.3",
|
|
126
|
-
"zod": "^
|
|
81
|
+
"zod": "^4.4.3"
|
|
127
82
|
},
|
|
128
83
|
"peerDependencies": {
|
|
129
84
|
"@agentskit/adapters": ">=0.12.0 <1",
|
|
@@ -154,30 +109,31 @@
|
|
|
154
109
|
}
|
|
155
110
|
},
|
|
156
111
|
"devDependencies": {
|
|
157
|
-
"@agentskit/chat": "0.4.
|
|
158
|
-
"@agentskit/core": "1.12.
|
|
159
|
-
"@agentskit/ink": "0.10.
|
|
112
|
+
"@agentskit/chat": "0.4.1",
|
|
113
|
+
"@agentskit/core": "1.12.8",
|
|
114
|
+
"@agentskit/ink": "0.10.9",
|
|
160
115
|
"@agentskit/react": "0.7.4",
|
|
161
116
|
"@anthropic-ai/mcpb": "2.1.2",
|
|
162
|
-
"@changesets/cli": "^
|
|
117
|
+
"@changesets/cli": "^3.0.1",
|
|
163
118
|
"@lhci/cli": "^0.15.1",
|
|
164
|
-
"@playwright/test": "1.
|
|
165
|
-
"@tailwindcss/postcss": "4.3.
|
|
166
|
-
"@types/node": "^
|
|
167
|
-
"@types/react": "19.2.
|
|
168
|
-
"@types/react-dom": "19.2.
|
|
169
|
-
"@vitest/coverage-v8": "4.1.
|
|
170
|
-
"esbuild": "^0.28.
|
|
171
|
-
"fumadocs-core": "15.
|
|
172
|
-
"fumadocs-mdx": "
|
|
173
|
-
"fumadocs-ui": "15.
|
|
174
|
-
"lucide-react": "
|
|
175
|
-
"next": "
|
|
176
|
-
"
|
|
177
|
-
"react
|
|
178
|
-
"
|
|
119
|
+
"@playwright/test": "1.62.1",
|
|
120
|
+
"@tailwindcss/postcss": "4.3.3",
|
|
121
|
+
"@types/node": "^26.4.0",
|
|
122
|
+
"@types/react": "19.2.18",
|
|
123
|
+
"@types/react-dom": "19.2.5",
|
|
124
|
+
"@vitest/coverage-v8": "4.1.11",
|
|
125
|
+
"esbuild": "^0.28.2",
|
|
126
|
+
"fumadocs-core": "16.15.4",
|
|
127
|
+
"fumadocs-mdx": "15.4.0",
|
|
128
|
+
"fumadocs-ui": "16.15.4",
|
|
129
|
+
"lucide-react": "1.34.0",
|
|
130
|
+
"next": "16.3.3",
|
|
131
|
+
"proxy-agent": "8.0.2",
|
|
132
|
+
"react": "19.2.8",
|
|
133
|
+
"react-dom": "19.2.8",
|
|
134
|
+
"tailwindcss": "4.3.3",
|
|
179
135
|
"tsup": "^8.5.0",
|
|
180
|
-
"vitest": "^4.1.
|
|
136
|
+
"vitest": "^4.1.11"
|
|
181
137
|
},
|
|
182
138
|
"directories": {
|
|
183
139
|
"doc": "docs",
|
|
@@ -188,5 +144,47 @@
|
|
|
188
144
|
"bugs": {
|
|
189
145
|
"url": "https://github.com/AgentsKit-io/doc-bridge/issues"
|
|
190
146
|
},
|
|
191
|
-
"homepage": "https://doc-bridge.agentskit.io/"
|
|
192
|
-
|
|
147
|
+
"homepage": "https://doc-bridge.agentskit.io/",
|
|
148
|
+
"scripts": {
|
|
149
|
+
"prebuild": "node scripts/sync-version.mjs",
|
|
150
|
+
"build": "tsup",
|
|
151
|
+
"test": "vitest run && pnpm test:cursor-plugin && pnpm test:claude-plugin && pnpm test:copilot-plugin && pnpm test:portable-skill",
|
|
152
|
+
"test:watch": "vitest",
|
|
153
|
+
"coverage": "vitest run --coverage",
|
|
154
|
+
"check:ecosystem-upstream": "node scripts/check-ecosystem-upstream.mjs",
|
|
155
|
+
"check:no-legacy-chat-imports": "node scripts/check-no-legacy-chat-imports.mjs",
|
|
156
|
+
"typecheck": "tsc --noEmit",
|
|
157
|
+
"smoke:packaged": "node scripts/smoke-packaged.mjs",
|
|
158
|
+
"smoke:docsites": "node scripts/smoke-docsites.mjs",
|
|
159
|
+
"smoke:real-docsites": "node scripts/smoke-real-docsites.mjs",
|
|
160
|
+
"smoke:ollama": "node scripts/smoke-ollama.mjs",
|
|
161
|
+
"mcpb:stage": "npm run build && node scripts/build-mcpb.mjs stage",
|
|
162
|
+
"mcpb:validate": "node scripts/build-mcpb.mjs validate",
|
|
163
|
+
"mcpb:smoke": "node scripts/smoke-mcpb.mjs",
|
|
164
|
+
"mcpb:pack": "npm run mcpb:stage && npm run mcpb:smoke && node scripts/build-mcpb.mjs pack",
|
|
165
|
+
"test:mcpb": "node --test scripts/mcpb-contract.test.mjs",
|
|
166
|
+
"test:cursor-plugin": "node --test scripts/cursor-plugin-contract.test.mjs",
|
|
167
|
+
"test:claude-plugin": "node --test scripts/claude-plugin-contract.test.mjs",
|
|
168
|
+
"test:copilot-plugin": "node --test scripts/copilot-plugin-contract.test.mjs",
|
|
169
|
+
"test:portable-skill": "node --test scripts/portable-skill-contract.test.mjs",
|
|
170
|
+
"coverage:badge": "node scripts/update-coverage-badge.mjs",
|
|
171
|
+
"changeset": "changeset",
|
|
172
|
+
"version-packages": "changeset version && node scripts/sync-version.mjs",
|
|
173
|
+
"release": "npm run build && npm test && changeset publish",
|
|
174
|
+
"version": "node scripts/sync-version.mjs && git add package.json src/version.ts action.yml",
|
|
175
|
+
"docs:dev": "cd apps/docs && next dev",
|
|
176
|
+
"docs:artifacts": "node scripts/build-docs-artifacts.mjs",
|
|
177
|
+
"docs:build": "pnpm build && node bin/ak-docs.js index && pnpm docs:artifacts && cd apps/docs && next build",
|
|
178
|
+
"docs:typecheck": "cd apps/docs && fumadocs-mdx && tsc --noEmit",
|
|
179
|
+
"docs:e2e": "playwright test",
|
|
180
|
+
"report:visual": "node scripts/report-visual-check.mjs",
|
|
181
|
+
"test:verification-harness": "node --test scripts/verification-harness.test.mjs",
|
|
182
|
+
"docs:lighthouse": "lhci autorun",
|
|
183
|
+
"test:readme-standard": "node --test scripts/readme-standard.test.mjs",
|
|
184
|
+
"test:marketplace": "node --test scripts/marketplace-contract.test.mjs",
|
|
185
|
+
"check:marketplace": "node scripts/check-marketplace-contract.mjs",
|
|
186
|
+
"test:docs-artifacts": "node --test scripts/docs-artifacts-contract.test.mjs",
|
|
187
|
+
"check:readme-standard": "node scripts/check-readme-standard.mjs",
|
|
188
|
+
"check:docs": "pnpm check:readme-standard && node bin/ak-docs.js gate run && node bin/ak-docs.js conformance run documentation-standard-v1 --text && pnpm docs:typecheck && pnpm docs:build && pnpm test:docs-artifacts"
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -41,8 +41,9 @@ for (const file of requiredFiles) {
|
|
|
41
41
|
if (localDigest !== expectedDigest) {
|
|
42
42
|
throw new Error(`${file} differs from its recorded upstream SHA-256 digest.`)
|
|
43
43
|
}
|
|
44
|
-
const url = `https://raw.githubusercontent.com/${metadata.repository}/${metadata.ref}/${file}`
|
|
45
|
-
|
|
44
|
+
const url = new URL(`https://raw.githubusercontent.com/${metadata.repository}/${metadata.ref}/${file}`)
|
|
45
|
+
if (url.hostname !== 'raw.githubusercontent.com') throw new Error('Unexpected upstream host.')
|
|
46
|
+
const upstream = await fetchText(url.href)
|
|
46
47
|
if (sha256(upstream) !== expectedDigest || upstream !== local) {
|
|
47
48
|
throw new Error(`${file} is stale against ${metadata.repository}@${metadata.ref}. Sync the canonical snapshot and digest.`)
|
|
48
49
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { mkdirSync, writeFileSync } from 'node:fs'
|
|
2
|
-
import {
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import { createHash } from 'node:crypto'
|
|
3
|
+
import { join, relative, resolve } from 'node:path'
|
|
3
4
|
import { pathToFileURL } from 'node:url'
|
|
4
5
|
|
|
5
6
|
import { chromium } from '@playwright/test'
|
|
@@ -15,6 +16,7 @@ const themes = ['light', 'dark']
|
|
|
15
16
|
const failures = []
|
|
16
17
|
const checks = []
|
|
17
18
|
const networkRequests = []
|
|
19
|
+
const clock = () => performance.now()
|
|
18
20
|
|
|
19
21
|
mkdirSync(outputDir, { recursive: true })
|
|
20
22
|
|
|
@@ -92,6 +94,12 @@ const inspect = async (frame, width, height) => frame.evaluate(({ width: viewpor
|
|
|
92
94
|
diagnosticCount: typeof globalThis.__DOC_BRIDGE_DATA__?.diagnosticCount === 'number'
|
|
93
95
|
? globalThis.__DOC_BRIDGE_DATA__.diagnosticCount
|
|
94
96
|
: Number.parseInt(document.querySelector('#finding-count')?.textContent ?? '', 10) || document.querySelectorAll('.finding').length,
|
|
97
|
+
actionableCount: typeof globalThis.__DOC_BRIDGE_DATA__?.actionableCount === 'number'
|
|
98
|
+
? globalThis.__DOC_BRIDGE_DATA__.actionableCount
|
|
99
|
+
: Number.parseInt((document.querySelector('#finding-count')?.textContent ?? '').match(/(\d+) actionable findings/)?.[1] ?? '0', 10),
|
|
100
|
+
confirmedCount: typeof globalThis.__DOC_BRIDGE_DATA__?.confirmedCount === 'number'
|
|
101
|
+
? globalThis.__DOC_BRIDGE_DATA__.confirmedCount
|
|
102
|
+
: Number.parseInt((document.querySelector('#finding-count')?.textContent ?? '').match(/(\d+) confirmed checks/)?.[1] ?? '0', 10),
|
|
95
103
|
unnamedButtonCount: unnamedButtons.length,
|
|
96
104
|
visibleOverflowCount: visibleOverflow.length,
|
|
97
105
|
contrastFailures,
|
|
@@ -99,25 +107,46 @@ const inspect = async (frame, width, height) => frame.evaluate(({ width: viewpor
|
|
|
99
107
|
}, { width, height })
|
|
100
108
|
|
|
101
109
|
const poll = async (read, predicate, label, timeoutMs = interactionTimeoutMs) => {
|
|
102
|
-
const started =
|
|
103
|
-
while (
|
|
104
|
-
if (predicate(await read())) return
|
|
110
|
+
const started = clock()
|
|
111
|
+
while (clock() - started < timeoutMs) {
|
|
112
|
+
if (predicate(await read())) return Math.round(clock() - started)
|
|
105
113
|
await new Promise((resolve) => setTimeout(resolve, 50))
|
|
106
114
|
}
|
|
107
115
|
throw new Error(`${label} did not reach the expected state within ${timeoutMs}ms`)
|
|
108
116
|
}
|
|
109
117
|
|
|
110
118
|
const exercise = async (frame, result) => {
|
|
111
|
-
|
|
112
|
-
|
|
119
|
+
result.interactionDurationsMs ??= []
|
|
120
|
+
const run = async (label, action, verify, browserEvent) => {
|
|
121
|
+
const started = clock()
|
|
113
122
|
try {
|
|
123
|
+
if (browserEvent) await frame.evaluate((type) => {
|
|
124
|
+
document.documentElement.dataset.docBridgeInteractionStartedAt = ''
|
|
125
|
+
window.addEventListener(type, () => {
|
|
126
|
+
document.documentElement.dataset.docBridgeInteractionStartedAt = String(performance.now())
|
|
127
|
+
}, { capture: true, once: true })
|
|
128
|
+
}, browserEvent)
|
|
114
129
|
await Promise.race([
|
|
115
130
|
(async () => { await action(); if (verify) await verify() })(),
|
|
116
131
|
new Promise((_, reject) => setTimeout(() => reject(new Error('interaction timeout')), interactionTimeoutMs)),
|
|
117
132
|
])
|
|
118
|
-
const
|
|
133
|
+
const gestureDurationMs = Math.round(clock() - started)
|
|
134
|
+
const durationMs = browserEvent
|
|
135
|
+
? await frame.evaluate(() => {
|
|
136
|
+
const startedAt = Number(document.documentElement.dataset.docBridgeInteractionStartedAt)
|
|
137
|
+
return Number.isFinite(startedAt) ? Math.round(performance.now() - startedAt) : null
|
|
138
|
+
})
|
|
139
|
+
: gestureDurationMs
|
|
140
|
+
if (durationMs == null) throw new Error(`${browserEvent} event was not observed`)
|
|
141
|
+
result.interactionDurationsMs.push({ label, durationMs, ...(browserEvent ? { gestureDurationMs } : {}) })
|
|
142
|
+
if (browserEvent) {
|
|
143
|
+
const renderTimings = await frame.evaluate(() => JSON.parse(document.documentElement.dataset.docBridgeRenderTimings || '{}'))
|
|
144
|
+
result.renderTimings ??= []
|
|
145
|
+
result.renderTimings.push({ label, ...renderTimings })
|
|
146
|
+
}
|
|
119
147
|
if (durationMs > 2000) result.failures.push(`${label} took ${durationMs}ms`)
|
|
120
148
|
} catch (error) {
|
|
149
|
+
result.interactionDurationsMs.push({ label, durationMs: Math.round(clock() - started), failed: true })
|
|
121
150
|
result.failures.push(`${label}: ${error instanceof Error ? error.message : String(error)}`)
|
|
122
151
|
}
|
|
123
152
|
}
|
|
@@ -134,7 +163,8 @@ const exercise = async (frame, result) => {
|
|
|
134
163
|
if (await load.count()) await load.click()
|
|
135
164
|
else if (result.diagnosticCount > 0 && await frame.locator('.finding-group').count() === 0) throw new Error('finding loader is missing while findings are present')
|
|
136
165
|
}, async () => {
|
|
137
|
-
|
|
166
|
+
const evidenceView = await frame.locator('body').getAttribute('data-report-view') === 'coverage'
|
|
167
|
+
if (result.actionableCount > 0 || (evidenceView && result.confirmedCount > 0)) {
|
|
138
168
|
await poll(() => frame.locator('.finding-group').count(), (count) => count > 0, 'finding groups')
|
|
139
169
|
} else if (await frame.locator('.finding-group').count() !== 0) {
|
|
140
170
|
throw new Error('finding groups rendered despite zero findings')
|
|
@@ -165,7 +195,7 @@ const exercise = async (frame, result) => {
|
|
|
165
195
|
await poll(() => frame.locator('[data-level][aria-pressed="true"]').getAttribute('data-level'), (actual) => actual === expectedLevel, `drill-down ${expectedLevel}`)
|
|
166
196
|
const details = await frame.locator('#details').innerText()
|
|
167
197
|
if (/Select a node in the map/i.test(details)) throw new Error('details panel did not update')
|
|
168
|
-
})
|
|
198
|
+
}, 'dblclick')
|
|
169
199
|
}
|
|
170
200
|
if (await frame.locator('#breadcrumbs [data-breadcrumb-level]').count() < 1) result.failures.push('drill-down did not create usable breadcrumbs')
|
|
171
201
|
await run('breadcrumb back to repository', () => frame.locator('#breadcrumbs [data-breadcrumb-level="overview"]').click(), async () => {
|
|
@@ -189,6 +219,9 @@ const exercise = async (frame, result) => {
|
|
|
189
219
|
let browser
|
|
190
220
|
try {
|
|
191
221
|
browser = await chromium.launch()
|
|
222
|
+
const warmup = await browser.newPage()
|
|
223
|
+
await warmup.goto('about:blank')
|
|
224
|
+
await warmup.close()
|
|
192
225
|
for (const theme of themes) {
|
|
193
226
|
for (const viewport of viewports) {
|
|
194
227
|
const page = await browser.newPage()
|
|
@@ -203,10 +236,12 @@ try {
|
|
|
203
236
|
try {
|
|
204
237
|
await page.setViewportSize({ width: viewport[0], height: viewport[1] })
|
|
205
238
|
await page.emulateMedia({ colorScheme: theme })
|
|
239
|
+
const renderStarted = clock()
|
|
206
240
|
await page.goto(pathToFileURL(reportPath).href, { waitUntil: 'load' })
|
|
207
241
|
const frame = page.frames().find((candidate) => candidate !== page.mainFrame() && candidate.url().endsWith('/report/index.html')) ?? page.mainFrame()
|
|
208
242
|
await frame.locator('#graph').waitFor({ state: 'visible', timeout: interactionTimeoutMs })
|
|
209
243
|
result = await inspect(frame, viewport[0], viewport[1])
|
|
244
|
+
result.firstRenderMs = Math.round(clock() - renderStarted)
|
|
210
245
|
await exercise(frame, result)
|
|
211
246
|
result.pageErrors = pageErrors
|
|
212
247
|
result.consoleErrors = consoleErrors
|
|
@@ -229,8 +264,15 @@ try {
|
|
|
229
264
|
}
|
|
230
265
|
|
|
231
266
|
const status = failures.length || checks.some((check) => check.failures.length) ? 'failed' : humanApproved ? 'passed' : 'pending-human-review'
|
|
267
|
+
const artifacts = checks.map((check) => {
|
|
268
|
+
const path = join(outputDir, `${check.viewport}-${check.theme}.png`)
|
|
269
|
+
return existsSync(path) ? { type: 'screenshot', path: relative(process.cwd(), path), sha256: createHash('sha256').update(readFileSync(path)).digest('hex'), viewport: check.viewport, theme: check.theme } : null
|
|
270
|
+
}).filter(Boolean)
|
|
232
271
|
const result = {
|
|
233
272
|
status,
|
|
273
|
+
capability: 'real-browser',
|
|
274
|
+
artifacts,
|
|
275
|
+
criteria: { 'report-ui': { status: status === 'failed' ? 'failed' : 'passed' } },
|
|
234
276
|
reportPath,
|
|
235
277
|
outputDir,
|
|
236
278
|
viewports,
|
|
@@ -245,6 +287,16 @@ const result = {
|
|
|
245
287
|
: 'Automated checks passed; screenshots require human visual review. Re-run with --human-approved only after review.',
|
|
246
288
|
}
|
|
247
289
|
writeFileSync(join(outputDir, 'result.json'), `${JSON.stringify(result, null, 2)}\n`, 'utf8')
|
|
248
|
-
console.log(JSON.stringify(
|
|
249
|
-
|
|
290
|
+
console.log(JSON.stringify({
|
|
291
|
+
status,
|
|
292
|
+
capability: result.capability,
|
|
293
|
+
artifacts: result.artifacts,
|
|
294
|
+
criteria: result.criteria,
|
|
295
|
+
reportPath: result.reportPath,
|
|
296
|
+
outputDir: result.outputDir,
|
|
297
|
+
viewports: result.viewports,
|
|
298
|
+
themes: result.themes,
|
|
299
|
+
failures: result.failures,
|
|
300
|
+
note: result.note,
|
|
301
|
+
}))
|
|
250
302
|
if (status === 'failed') process.exitCode = 1
|