Skip to content

Commit 7edebca

Browse files
committed
refactor(plugin-a11y): rename the injected script's internals off "agent"
The injected half is now the page script, talking to the panel over the in-page channel, so "agent" stays reserved for coding agents. Internal types, the wire message (a11y:page-script-ready), and UI copy follow; the public export a11yAgentBundlePath keeps its name.
1 parent f88a023 commit 7edebca

19 files changed

Lines changed: 131 additions & 131 deletions

plugins/a11y/README.md

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
# @devframes/plugin-a11y
22

33
> [!WARNING] Experimental
4-
> This plugin is experimental and may change without a major version bump until
4+
> This devframe is experimental and may change without a major version bump until
55
> it stabilizes.
66
77
An accessibility inspector built on [devframe](../../packages/devframe). It runs
8-
[axe-core](https://github.com/dequelabs/axe-core) against a host application and
8+
[axe-core](https://github.com/dequelabs/axe-core) against a user app and
99
surfaces the violations in a [Solid](https://www.solidjs.com/) panel:
1010

1111
- **Route-aware tracking** — buckets violations by `location.pathname` and tracks
12-
them as you navigate the app (History-API patched, framework-neutral), persisted
12+
them as you navigate the user app (History-API patched, framework-neutral), persisted
1313
in `sessionStorage` so history survives reloads within a tab session.
1414
- **Dashboard + grouped violations** — a Dashboard tab (totals, severity
1515
breakdown, per-route inventory, scan controls) and a Violations tab listing
@@ -28,7 +28,7 @@ surfaces the violations in a [Solid](https://www.solidjs.com/) panel:
2828
- **Console logging** — newly-appeared violations are logged (deduped) to the
2929
browser console.
3030

31-
The scan + highlight loop works the same whether the plugin runs as a live dev
31+
The scan + highlight loop works the same whether the devframe runs as a live dev
3232
server or as a baked static build.
3333

3434
## How it works
@@ -37,28 +37,28 @@ Three pieces, two of them browser-side:
3737

3838
| Piece | Runs in | Role |
3939
|-------|---------|------|
40-
| **Agent** (`src/inject`) | the host app's page | runs axe-core, tracks routes, broadcasts the aggregate state, draws the preview + pinned rings |
40+
| **Page script** (`src/inject`) | the user app's page | runs axe-core, tracks routes, broadcasts the aggregate state, draws the preview + pinned rings |
4141
| **Panel** (`src/spa`) | the devtools iframe | Solid SPA: Dashboard + grouped violations, fires preview/pin/rescan |
42-
| **Node** (`src/index.ts`, `src/node`, `src/rpc`) | the devframe backend | `get-config` RPC (impact taxonomy + runtime config) — live in dev, baked in a static build |
42+
| **Node** (`src/index.ts`, `src/node`, `src/rpc`) | the node side | `get-config` RPC (impact taxonomy + runtime config) — live in dev, baked in a static build |
4343

44-
The agent and panel talk over a same-origin
45-
[`BroadcastChannel`](src/shared/protocol.ts), not the devframe RPC backend. That
44+
The page script and panel talk over the in-page channel (a same-origin
45+
[`BroadcastChannel`](src/shared/protocol.ts)), not the devframe RPC backend. That
4646
is what keeps the live loop working in **both modes**: neither half needs a
4747
server to reach the other, only a shared browser origin (host page + panel
48-
iframe). The agent owns the authoritative route → report map and broadcasts the
48+
iframe). The page script owns the authoritative route → report map and broadcasts the
4949
whole aggregate on every change, so the panel stays a pure render of it. devframe
5050
RPC carries the data model on top — `get-config` is a `static` function, so it
5151
resolves over WebSocket in dev and from the baked dump in a static build; the
52-
panel forwards its runtime-config slice to the agent over the channel, keeping the
53-
agent itself free of any RPC dependency.
52+
panel forwards its runtime-config slice to the page script over the channel, keeping the
53+
page script itself free of any RPC dependency.
5454

55-
devframe deliberately provides no access to the host application's DOM, so the
56-
agent is the author-provided bridge into the page being checked. In a hub, the
57-
agent is the a11y dock's **client script**: attach `a11yAgentBundlePath` as the
55+
devframe deliberately provides no access to the user app's DOM, so the
56+
page script is the author-provided bridge into the user app's page. In a hub, the
57+
page script is the a11y dock's **client script**: attach `a11yAgentBundlePath` as the
5858
dock's `clientScript` (resolved to an importable URL — `/@fs/…` under Vite, or a
5959
statically-served path) and the hub's client runtime (`createDevframeClientHost`
6060
from `@devframes/hub/client`) imports it into the host page and calls its
61-
default export with the client-script context. Booted that way, the agent also
61+
default export with the client-script context. Booted that way, the page script also
6262
mirrors the active route's scan into the hub's **messages feed** — a summary entry
6363
driven through the loading → idle lifecycle plus one entry per violated rule,
6464
carrying the impact-mapped level, WCAG tags as labels, and the first offending
@@ -73,7 +73,7 @@ shows it (no hub context, so the feed mirror simply stays off).
7373
## Configuration
7474

7575
Pass options to `createA11yDevframe()` (surfaced through `get-config`, so they
76-
reach both the panel and the agent):
76+
reach both the panel and the page script):
7777

7878
```ts
7979
createA11yDevframe({
@@ -93,7 +93,7 @@ The demo serves an intentionally-broken host page and the panel from **one
9393
origin** so they share the channel.
9494

9595
```sh
96-
pnpm -C plugins/a11y build # build the panel + the agent bundle
96+
pnpm -C plugins/a11y build # build the panel + the page-script bundle
9797
pnpm -C plugins/a11y demo # dev: live WebSocket RPC → http://localhost:4477/
9898

9999
pnpm -C plugins/a11y cli:build # bake the static deploy (dist/static)
@@ -104,7 +104,7 @@ Open the URL, then hover any row in the panel — the matching element in the pa
104104
gets a focus ring (and scrolls into view if it's off-screen). Both demo modes
105105
behave identically; the panel's `websocket` / `static` tag is the only tell.
106106

107-
Standalone, without a host app:
107+
Standalone, without a user app:
108108

109109
```sh
110110
pnpx @devframes/plugin-a11y # the published package, panel only, at /__devframes_plugin_a11y/
@@ -115,13 +115,13 @@ pnpm -C plugins/a11y dev # from source: same, at /__devframes_plugin_a11
115115

116116
| Path | Export | Purpose |
117117
|------|--------|---------|
118-
| `src/index.ts` | `.` | `createA11yDevframe()` (also the default export); `a11yAgentBundlePath` — the agent module a hub attaches as this dock's client script |
118+
| `src/index.ts` | `.` | `createA11yDevframe()` (also the default export); `a11yAgentBundlePath` — the page-script module a hub attaches as this dock's client script |
119119
| `src/node/index.ts` | `/node` | `setupA11y(ctx, options?)` — registers the RPC functions with the runtime config |
120120
| `src/cli.ts` | `/cli` | `createA11yCli()` — backs the `devframes_plugin_a11y` bin |
121121
| `src/client/index.ts` | `/client` | `connectA11y()` — typed browser RPC client wrapper |
122122
| `src/rpc/` || `get-config` static RPC + the type-safe client registry |
123-
| `src/shared/protocol.ts` || the agent ↔ panel `BroadcastChannel` contract |
124-
| `src/inject/` || the host-page agent (axe scan, highlight overlay, hub messages mirror) → `dist/inject/inject.js` |
123+
| `src/shared/protocol.ts` || the page script ↔ panel in-page channel (`BroadcastChannel`) contract |
124+
| `src/inject/` || the page script (axe scan, highlight overlay, hub messages mirror) → `dist/inject/inject.js` |
125125
| `src/spa/` || the Solid panel SPA → `assets-pkg/dist` (ships in `@devframes/plugin-a11y--assets`) |
126126
| `demo/` || same-origin host page + server (dev + static modes) |
127127
| `tests/` || dev-server RPC + static-build dump |

plugins/a11y/demo/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ <h2>This week's roasts</h2>
160160
<iframe class="df-dock__frame" title="A11y Inspector panel" src="/__devframes_plugin_a11y/"></iframe>
161161
</aside>
162162

163-
<!-- The injected a11y agent: scans this page and answers the panel. -->
163+
<!-- The a11y page script: scans this page and answers the panel. -->
164164
<script type="module" src="/__df-inject/inject.js"></script>
165165
</body>
166166
</html>

plugins/a11y/demo/server.mjs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
/**
33
* Same-origin demo host for the a11y inspector.
44
*
5-
* Serves three things off one origin so the injected agent (host page) and the
5+
* Serves three things off one origin so the page script (host page) and the
66
* panel (devtools iframe) share a BroadcastChannel:
77
*
88
* GET / → the demo page (intentional a11y bugs)
9-
* GET /__df-inject/inject.js → the injected agent bundle
9+
* GET /__df-inject/inject.js → the page script bundle
1010
* GET /__devframes_plugin_a11y/** → the Solid panel SPA
1111
*
1212
* Two modes prove the plugin works either way:
@@ -76,7 +76,7 @@ async function main() {
7676
return demoHtml
7777
})
7878

79-
// 2. The injected agent bundle.
79+
// 2. The page script bundle.
8080
mountStaticHandler(app, '/__df-inject/', injectDir)
8181

8282
if (mode === 'dev') {

plugins/a11y/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "@devframes/plugin-a11y",
33
"type": "module",
44
"version": "0.9.5",
5-
"description": "Devframe plugin that audits the host app with axe and highlights accessibility issues in the page.",
5+
"description": "Built-in devframe that audits the user app with axe and highlights accessibility issues in the page.",
66
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
77
"license": "MIT",
88
"homepage": "https://github.com/devframes/devframe#readme",

plugins/a11y/src/index.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@ const BASE_PATH = '/__devframes_plugin_a11y/'
1212
// package, served on demand through devframe's remote-assets back-proxy. The
1313
// definition's `importMetaUrl` (below) supplies the default `resolveFrom`, so a
1414
// locally installed copy (a workspace link here) is served with zero network.
15-
// The host-page agent bundle (`dist/inject`, below) stays here.
15+
// The page-script bundle (`dist/inject`, below) stays here.
1616
const distDir: RemoteAssets = {
1717
package: `${pkg.name}--assets`,
1818
version: pkg.version,
1919
}
2020

2121
/**
22-
* Absolute path to the built in-page **agent** module (`dist/inject/inject.js`)
23-
* — the dock **client script** the hub runtime imports into the host page to
24-
* scan it (its default export boots the agent; importing it does too).
22+
* Absolute path to the built **page script** module (`dist/inject/inject.js`)
23+
* — the dock **client script** the client runtime imports into the host page to
24+
* scan it (its default export boots the page script; importing it does too).
2525
*
2626
* A hub attaches this as the a11y dock's `clientScript`, resolved to a URL the
2727
* page can import: `/@fs/${a11yAgentBundlePath}` for a Vite host, or a
@@ -69,8 +69,8 @@ export interface A11yDevframeOptions {
6969
/**
7070
* Build a {@link DevframeDefinition} for the a11y inspector. The same
7171
* definition runs standalone (`/cli`, `/build`) and mounts into a host
72-
* (`/vite`, hub). The panel talks to the in-page agent over a same-origin
73-
* BroadcastChannel, so the scan/highlight loop works identically in dev
72+
* (`/vite`, hub). The panel talks to the page script over the in-page channel
73+
* (a same-origin BroadcastChannel), so the scan/highlight loop works identically in dev
7474
* (live WebSocket RPC) and in a baked static build.
7575
*
7676
* @experimental This plugin is experimental and may change without a major

plugins/a11y/src/inject/index.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* The a11y inspector **agent** — injected into the host application's page.
2+
* The a11y inspector **page script** — injected into the user app's page.
33
*
44
* It runs axe-core against the live DOM, tracks violations per route, broadcasts
55
* the whole {@link A11yState} aggregate to the panel, and draws transient +
@@ -16,11 +16,11 @@
1616
import type {
1717
A11yMessage,
1818
A11yState,
19-
AgentConfig,
19+
PageScriptConfig,
2020
PinTarget,
2121
ScanReport,
2222
} from '../shared/protocol.ts'
23-
import type { A11yAgentContext } from './messages.ts'
23+
import type { A11yPageScriptContext } from './messages.ts'
2424
import type { PinInfo } from './overlay.ts'
2525
import {
2626
A11Y_CHANNEL,
@@ -34,7 +34,7 @@ import { resolveElement, scan } from './scanner.ts'
3434

3535
const GLOBAL_FLAG = '__DF_A11Y_AGENT__'
3636

37-
function start(context?: A11yAgentContext) {
37+
function start(context?: A11yPageScriptContext) {
3838
const w = window as unknown as Record<string, unknown>
3939
if (w[GLOBAL_FLAG])
4040
return
@@ -46,7 +46,7 @@ function start(context?: A11yAgentContext) {
4646

4747
// Booted as a hub dock client script — mirror the active route's scan into
4848
// the hub's messages feed. Standalone boots have no context and skip it.
49-
const config: AgentConfig = { logIssues: true, autoScan: true }
49+
const config: PageScriptConfig = { logIssues: true, autoScan: true }
5050

5151
const reporter = context?.messages
5252
? createMessagesReporter(context.messages, {
@@ -251,7 +251,7 @@ function start(context?: A11yAgentContext) {
251251
const message = event.data
252252
switch (message.type) {
253253
case 'a11y:panel-ready':
254-
post({ type: 'a11y:agent-ready', url: location.href, route: activeRoute })
254+
post({ type: 'a11y:page-script-ready', url: location.href, route: activeRoute })
255255
if (routes.size > 0)
256256
broadcastState()
257257
else
@@ -312,7 +312,7 @@ function start(context?: A11yAgentContext) {
312312
}
313313
})
314314

315-
function applyConfig(next: AgentConfig) {
315+
function applyConfig(next: PageScriptConfig) {
316316
config.logIssues = next.logIssues
317317
config.axeTags = next.axeTags
318318
config.axeRunOptions = next.axeRunOptions
@@ -327,7 +327,7 @@ function start(context?: A11yAgentContext) {
327327
bindInteractions()
328328

329329
// Announce ourselves and run the first scan once the page has settled.
330-
post({ type: 'a11y:agent-ready', url: location.href, route: activeRoute })
330+
post({ type: 'a11y:page-script-ready', url: location.href, route: activeRoute })
331331
if (routes.size > 0)
332332
broadcastState()
333333
if (document.readyState === 'complete')
@@ -347,16 +347,16 @@ function findRule(report: ScanReport | null, nodeId: string) {
347347
* Client-script entry the hub runtime calls after importing this module,
348348
* passing its `DockClientScriptContext`. The live scan/highlight loop rides
349349
* the same-origin BroadcastChannel either way; when the context carries a
350-
* `messages` client (duck-typed — see {@link A11yAgentContext}), the agent
350+
* `messages` client (duck-typed — see {@link A11yPageScriptContext}), the page script
351351
* additionally mirrors each scan into the hub's messages feed. `start()` is
352352
* idempotent.
353353
*/
354-
export default function runA11yAgent(context?: A11yAgentContext): void {
354+
export default function runA11yPageScript(context?: A11yPageScriptContext): void {
355355
start(context)
356356
}
357357

358358
// Also self-boot so a plain `<script type="module" src=".../inject.js">`
359-
// (the standalone demo, any non-hub host) starts the agent on load — deferred
359+
// (the standalone demo, any non-hub host page) starts the page script on load — deferred
360360
// one macrotask so a hub host that imports this module calls the default
361361
// export (microtask-chained after the import) first, letting the context-ful
362362
// boot win the `__DF_A11Y_AGENT__` guard.

plugins/a11y/src/inject/messages.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
/**
2-
* Mirrors scan results into the hub's messages feed when the agent is booted
3-
* as a dock **client script** (`createDevframeClientHost()` calls the agent's
2+
* Mirrors scan results into the hub's messages feed when the page script is booted
3+
* as a dock **client script** (`createDevframeClientHost()` calls the page script's
44
* default export with its `DockClientScriptContext`).
55
*
66
* The hub context is consumed by duck-typing a minimal structural slice, so
7-
* the agent keeps no build- or runtime dependency on `@devframes/hub` and the
7+
* the page script keeps no build- or runtime dependency on `@devframes/hub` and the
88
* bundle stays self-contained — a standalone `<script type="module">` boot
99
* simply has no messages client and skips the feed. Mirrors the pattern used
1010
* by the terminals and code-server plugins for `ctx.terminals`.
@@ -13,7 +13,7 @@ import type { Impact, ScanReport } from '../shared/protocol.ts'
1313
import { A11Y_DEFAULT_DOCK_ID } from '../shared/protocol.ts'
1414

1515
/**
16-
* Structural slice of the hub's `DevframeMessageAction` the agent emits — a
16+
* Structural slice of the hub's `DevframeMessageAction` the page script emits — a
1717
* labeled control that, when clicked in the messages panel, activates a dock
1818
* (deep-linking via `params`). Kept as a discriminated union so future action
1919
* kinds can be added without reshaping the field.
@@ -25,7 +25,7 @@ interface HubMessageAction {
2525
activate: { dockId: string, params?: Record<string, unknown> }
2626
}
2727

28-
/** Structural slice of the hub's `DevframeMessageEntryInput` the agent emits. */
28+
/** Structural slice of the hub's `DevframeMessageEntryInput` the page script emits. */
2929
export interface HubMessageInput {
3030
id?: string
3131
message: string
@@ -47,17 +47,17 @@ export interface HubMessageInput {
4747
status?: 'loading' | 'idle'
4848
}
4949

50-
/** Structural slice of the hub's `DevframeMessagesClient` the agent calls. */
50+
/** Structural slice of the hub's `DevframeMessagesClient` the page script calls. */
5151
export interface HubMessagesClient {
5252
add: (input: HubMessageInput) => Promise<unknown>
5353
remove: (id: string) => Promise<void>
5454
}
5555

5656
/**
57-
* Structural slice of the hub's `DockClientScriptContext` the agent accepts.
58-
* Every field is optional so any argument — or none — boots the agent.
57+
* Structural slice of the hub's `DockClientScriptContext` the page script accepts.
58+
* Every field is optional so any argument — or none — boots the page script.
5959
*/
60-
export interface A11yAgentContext {
60+
export interface A11yPageScriptContext {
6161
messages?: HubMessagesClient
6262
}
6363

@@ -87,7 +87,7 @@ export interface MessagesReporter {
8787
export interface MessagesReporterOptions {
8888
/**
8989
* Resolve the current bounding box of a violating element from its axe
90-
* target selectors — the agent supplies a live-DOM implementation. A stale
90+
* target selectors — the page script supplies a live-DOM implementation. A stale
9191
* box is fine: each re-scan refreshes the entry.
9292
*/
9393
resolveBoundingBox?: (target: string[]) => { x: number, y: number, width: number, height: number } | undefined

plugins/a11y/src/inject/vite.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { fileURLToPath } from 'node:url'
22
import { defineConfig } from 'vite'
33

4-
// Builds the host-page agent into a single self-contained ES module
4+
// Builds the page script into a single self-contained ES module
55
// (`dist/inject/inject.js`) with axe-core bundled in. Loaded by the host app
66
// via `<script type="module" src=".../inject.js">`, so it must not rely on a
77
// chunk graph or an import map.

plugins/a11y/src/rpc/functions/get-config.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { AgentConfig } from '../../shared/protocol.ts'
1+
import type { PageScriptConfig } from '../../shared/protocol.ts'
22
import { defineRpcFunction } from 'devframe'
33
import { A11Y_CHANNEL, A11Y_DEFAULT_DOCK_ID, A11Y_NODE_ATTR, IMPACT_ORDER } from '../../shared/protocol.ts'
44

@@ -44,12 +44,12 @@ export interface A11yRuntimeConfig {
4444
* Build the `get-config` RPC function from author options. Declared `static`,
4545
* so the value resolves live over WebSocket in dev and is baked into the RPC
4646
* dump for static builds — the panel's legend + runtime config render the same
47-
* in both modes. The panel forwards the `agent` slice to the in-page agent over
48-
* the BroadcastChannel, keeping the agent free of any RPC dependency.
47+
* in both modes. The panel forwards the `pageScript` slice to the page script over
48+
* the BroadcastChannel, keeping the page script free of any RPC dependency.
4949
*/
5050
export function createGetConfig(options: A11yRuntimeConfig = {}) {
5151
const dockId = options.dockId ?? A11Y_DEFAULT_DOCK_ID
52-
const agent: AgentConfig = {
52+
const pageScript: PageScriptConfig = {
5353
logIssues: options.logIssues ?? true,
5454
autoScan: options.autoScan ?? true,
5555
axeTags: options.axe?.tags,
@@ -66,7 +66,7 @@ export function createGetConfig(options: A11yRuntimeConfig = {}) {
6666
docsBase: 'https://dequeuniversity.com/rules/axe/',
6767
dockId,
6868
defaultHighlight: options.defaultHighlight ?? false,
69-
agent,
69+
pageScript,
7070
impacts: IMPACT_ORDER.map(id => ({ id, ...IMPACT_COPY[id] })),
7171
}),
7272
})

0 commit comments

Comments
 (0)