@a11y-oracle/ 核心引擎
A11y-Oracle 的框架不可知访问引擎. 提供两个主要的API:
SpeechEngine——通过CDP来读取浏览器的"无障碍树"并生成标准化的语音输出.A11yOrchestrator——统一管弦乐器,将语音,键盘调度,焦点指标分析合并为一pressKey()电话.
这个软件包是Playwright和Cypress插件所建立的基础. 也可以直接与任何CDP兼容的客户端使用.
安装
npm install @a11y-oracle/core-engine
使用量
语音引擎( 只有语音)
语音引擎通过一个 CDPSessionLike 接口,任何 CDP 客户端都可以满足 :
import { SpeechEngine } from '@a11y-oracle/core-engine';
// Works with Playwright's CDPSession
const cdpSession = await page.context().newCDPSession(page);
const engine = new SpeechEngine(cdpSession);
await engine.enable();
// Get speech for the currently focused element
const result = await engine.getSpeech();
console.log(result?.speech); // "Products, button, collapsed"
// Get speech for every visible element
const all = await engine.getFullTreeSpeech();
const nav = all.find(r => r.speech.includes('navigation landmark'));
// Clean up
await engine.disable();
A11y Orchestrator(统一州)
管弦乐团协调语音引擎,键盘引擎,以及焦点分析器:
import { A11yOrchestrator } from '@a11y-oracle/core-engine';
const cdpSession = await page.context().newCDPSession(page);
const orchestrator = new A11yOrchestrator(cdpSession);
await orchestrator.enable();
// Press a key and get unified state
const state = await orchestrator.pressKey('Tab');
console.log(state.speech); // "Products, button, collapsed"
console.log(state.focusedElement?.tag); // "BUTTON"
console.log(state.focusIndicator.meetsWCAG_AA); // true
// Get state without pressing a key
const current = await orchestrator.getState();
// Tab order extraction
const report = await orchestrator.traverseTabOrder();
console.log(report.totalCount); // 12
// Keyboard trap detection (WCAG 2.1.2)
const result = await orchestrator.traverseSubTree('#modal', 20);
console.log(result.isTrapped); // false
await orchestrator.disable();
配置选项
const engine = new SpeechEngine(cdpSession, {
// Include "landmark" suffix on landmark roles (default: true)
// true: "Main, navigation landmark"
// false: "Main, navigation"
includeLandmarks: true,
// Include accessible descriptions in output (default: false)
// true: "Submit, button, Submits the form"
// false: "Submit, button"
includeDescription: false,
});
const orchestrator = new A11yOrchestrator(cdpSession, {
// All SpeechEngine options, plus:
// Milliseconds to wait after key press for focus/CSS to settle (default: 50)
focusSettleMs: 50,
});
API 参考
SpeechEngine
语音引擎. 所有方法都是协同的,通过CDP会话运行.
constructor(cdp: CDPSessionLike, options?: SpeechEngineOptions)
创建新引擎实例 .
cdp- 任何执行CDPSessionLike接口.options.includeLandmarks——将"地标"附录为地标性角色. 默认true。 。 。 .options.includeDescription- 包含aria-describedby语句。 默认false。 。 。 .
enable(): Promise<void>
启用 CDP 无障碍域 。 一定要在任何其他方法之前叫出来.
disable(): Promise<void>
禁用 CDP 无障碍域 。 完成后调用以释放浏览器资源 .
getSpeech(): Promise<SpeechResult | null>
获取当前焦点元素的语音输出 .
回返 null 如果没有元素有焦点或焦点元素被忽略(例如, role="presentation") (中文(简体) ).
const result = await engine.getSpeech();
if (result) {
console.log(result.speech); // "Products, button, collapsed"
console.log(result.name); // "Products"
console.log(result.role); // "button"
console.log(result.states); // ["collapsed"]
console.log(result.rawNode); // Full CDP AXNode object
}
getFullTreeSpeech(): Promise<SpeechResult[]>
获得无障碍树上所有非受辱非寂静节点的演说. 用于坚持地标,标题,或结构元素没有焦点.
const all = await engine.getFullTreeSpeech();
const headings = all.filter(r => r.role.includes('heading'));
const landmarks = all.filter(r => r.role.includes('landmark'));
computeSpeech(node: AXNode): SpeechResult | null
计算单个 AX 节点的语音 。 回返 null 被忽略或沉默的节点.
findFocusedNode(nodes: AXNode[]): AXNode | null
查找平坦 AXTree 阵列中最具体的焦点节点. 当多个节点报告时 focused: true (例如, RootWebArea (单位:千美元) menuitem),最深的节点被还原.
A11yOrchestrator
协调三个分机的统一指挥员:
| 发动机 | 责任 |
|---|---|
SpeechEngine | AXTree 到语音字符串 |
KeyboardEngine | CDP 密钥发送 + document.activeElement |
FocusAnalyzer | CSS 焦点指标 + 标签命令 + 陷阱检测 |
constructor(cdp: CDPSessionLike, options?: A11yOrchestratorOptions)
创建一个新的管弦乐团.
cdp任何与协调委员会兼容的会议.options.includeLandmarks——将"地标"附录为地标性角色. 默认true。 。 。 .options.includeDescription- 包括说明案文。 默认false。 。 。 .options.focusSettleMs——按键后延迟焦点/CSS结算. 默认50。 。 。 .
enable(): Promise<void>
启用 CDP 无障碍域 。 必须先用其他方法.
disable(): Promise<void>
禁用 CDP 无障碍域 .
pressKey(key: string, modifiers?: ModifierKeys): Promise<A11yState>
调出一个关键媒体 并返回统一无障碍状态.
- 发送
keyDown+ 键keyUp通过CDPInput.dispatchKeyEvent - 等着
focusSettleMs支助处过渡和重点活动 - 并行收集语音、重点要素和重点指标
const state = await orchestrator.pressKey('Tab');
// state.speech → "Products, button, collapsed"
// state.focusedElement → { tag: 'BUTTON', id: 'products-btn', ... }
// state.focusIndicator → { isVisible: true, contrastRatio: 12.5, meetsWCAG_AA: true }
// With modifier keys
const prev = await orchestrator.pressKey('Tab', { shift: true });
getState(): Promise<A11yState>
获取当前统一状态而不按键 。 方案重点改变后有用.
await page.focus('#my-button');
const state = await orchestrator.getState();
traverseTabOrder(): Promise<TabOrderReport>
在 DOM 标签顺序中提取所有可制表元素 .
const report = await orchestrator.traverseTabOrder();
console.log(report.totalCount); // 12
console.log(report.entries[0].tag); // "A"
console.log(report.entries[0].id); // "home-link"
traverseSubTree(selector: string, maxTabs?: number): Promise<TraversalResult>
检测容器是否夹住键盘焦点(WCAG 2.1.2).
聚焦容器中的第一个可制表元素,按下标签至 maxTabs 时间(默认值50),并检查焦点是否逃脱.
const result = await orchestrator.traverseSubTree('#modal-container', 20);
if (result.isTrapped) {
console.log('Keyboard trap detected!');
console.log(`Focus visited ${result.visitedElements.length} elements`);
} else {
console.log(`Focus escaped to: ${result.escapeElement?.tag}`);
}
类型
CDPSessionLike
引擎和测试框架之间的抽象边界:
interface CDPSessionLike {
send(method: 'Accessibility.enable'): Promise<void>;
send(method: 'Accessibility.disable'): Promise<void>;
send(
method: 'Accessibility.getFullAXTree',
params?: { depth?: number; frameId?: string }
): Promise<{ nodes: AXNode[] }>;
send(method: string, params?: Record<string, unknown>): Promise<unknown>;
}
两个剧作家的 CDPSession 财务报告和审定财务报表 chrome-remote-interface 客户端无需适配器即可满足此接口.
SpeechResult
归来 getSpeech() 财务报告和审定财务报表 getFullTreeSpeech()数字 :
interface SpeechResult {
speech: string; // "Products, button, collapsed"
name: string; // "Products"
role: string; // "button"
states: string[]; // ["collapsed"]
rawNode: Protocol.Accessibility.AXNode;
}
A11yState
归来 pressKey() 财务报告和审定财务报表 getState()数字 :
interface A11yState {
speech: string; // "Products, button, collapsed"
speechResult: SpeechResult | null; // Full speech result with raw AXNode
focusedElement: A11yFocusedElement | null; // DOM info
focusIndicator: A11yFocusIndicator; // CSS analysis
}
A11yFocusedElement
interface A11yFocusedElement {
tag: string; // "BUTTON"
id: string; // "submit-btn"
className: string; // "btn primary"
textContent: string; // "Submit"
role: string; // "button"
ariaLabel: string; // "Submit form"
tabIndex: number; // 0
rect: { x: number; y: number; width: number; height: number };
}
A11yFocusIndicator
interface A11yFocusIndicator {
isVisible: boolean; // true if outline or box-shadow detected
contrastRatio: number | null; // null if colors unparseable
meetsWCAG_AA: boolean; // true if visible and contrast >= 3.0
}
ModifierKeys
interface ModifierKeys {
shift?: boolean;
ctrl?: boolean;
alt?: boolean;
meta?: boolean;
}
TabOrderReport
interface TabOrderReport {
entries: TabOrderEntry[];
totalCount: number;
}
TabOrderEntry
interface TabOrderEntry {
index: number; // Position in tab order (0-based)
tag: string; // "BUTTON"
id: string; // "submit-btn"
textContent: string;
tabIndex: number;
role: string;
rect: { x: number; y: number; width: number; height: number };
}
TraversalResult
interface TraversalResult {
isTrapped: boolean; // true if focus never escaped
tabCount: number; // Total Tab presses attempted
visitedElements: TabOrderEntry[]; // Elements that received focus
escapeElement: TabOrderEntry | null; // First element outside container
}
角色绘图
这个 ROLE_TO_SPEECH 记录50+ CDP 角色值到语音字符串。 作用分为四类:
互动作用
| CDP 作用 | 发言 |
|---|---|
button | button |
link | link |
checkbox | checkbox |
radio | radio button |
textbox | edit text |
combobox | combo box |
slider | slider |
switch | switch |
tab | tab |
menuitem | menu item |
menuitemcheckbox | menu item checkbox |
menuitemradio | menu item radio |
option | option |
searchbox | search text |
spinbutton | spin button |
地标角色
地标在 includeLandmarks 实值 true数字 :
| CDP 作用 | 发言 |
|---|---|
navigation | navigation landmark |
main | main landmark |
banner | banner landmark |
contentinfo | content info landmark |
complementary | complementary landmark |
search | search landmark |
region | region landmark |
form | form landmark |
结构作用
| CDP 作用 | 发言 |
|---|---|
heading | heading |
list | list |
listitem | list item |
img | image |
table | table |
row | row |
cell | cell |
dialog | dialog |
alert | alert |
menu | menu |
menubar | menu bar |
toolbar | toolbar |
tree | tree |
treeitem | tree item |
tablist | tab list |
tabpanel | tab panel |
progressbar | progress bar |
tooltip | tooltip |
寂静的角色
这些角色不会产生语音输出 :
generic, (中文(简体) ), none, (中文(简体) ), presentation, (中文(简体) ), StaticText, (中文(简体) ), InlineTextBox, (中文(简体) ), LineBreak, (中文(简体) ), RootWebArea, (中文(简体) ), WebArea, (中文(简体) ), paragraph, (中文(简体) ), DescriptionListDetail, (中文(简体) ), DescriptionListTerm, (中文(简体) ), DescriptionList
未知角色通过正即,以与新的ARIA角色进行前向相容.
国家测绘
这个 STATE_MAPPINGS 数组定义了布尔 ARIA 属性如何翻译为口语字符串 。 各国按下列顺序出现在产出中:
| CDP 财产 | true | false |
|---|---|---|
expanded | expanded | collapsed |
checked | checked | not checked |
selected | selected | (安静地) |
pressed | pressed | not pressed |
disabled | dimmed | (安静地) |
required | required | (安静地) |
invalid | invalid | (安静地) |
readonly | read only | (安静地) |
multiselectable | multi selectable | (安静地) |
标题级别是特殊情况: level 属性生成 level N (例如, level 2) (中文(简体) ).
当存在多个状态时,它们按上方的固定顺序合并:
"Submit, button, expanded, required"
"Email, edit text, invalid, required"
出口
// Main classes
export { SpeechEngine } from '@a11y-oracle/core-engine';
export { A11yOrchestrator } from '@a11y-oracle/core-engine';
// Core types
export type {
CDPSessionLike,
SpeechResult,
SpeechEngineOptions,
A11yState,
A11yFocusedElement,
A11yFocusIndicator,
A11yOrchestratorOptions,
} from '@a11y-oracle/core-engine';
// Data (for advanced customization)
export { ROLE_TO_SPEECH, LANDMARK_ROLES } from '@a11y-oracle/core-engine';
export { STATE_MAPPINGS, extractStates } from '@a11y-oracle/core-engine';
export type { StateMapping, AXNodeProperty } from '@a11y-oracle/core-engine';
// Re-exports from sub-engines
export type { ModifierKeys, FocusedElementInfo, KeyDefinition } from '@a11y-oracle/core-engine';
export { KEY_DEFINITIONS } from '@a11y-oracle/core-engine';
export type { FocusIndicator, TabOrderEntry, TabOrderReport, TraversalResult } from '@a11y-oracle/core-engine';