@reticular/speakable 1.0.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/README.md +31 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +2862 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +348 -0
- package/dist/index.js +512 -0
- package/dist/index.js.map +1 -0
- package/package.json +42 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical Announcement Model - Version 1
|
|
3
|
+
*
|
|
4
|
+
* Platform-agnostic representation of accessibility semantics.
|
|
5
|
+
* Deterministic, serializable, suitable for snapshot testing.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Model version for forward compatibility.
|
|
9
|
+
* Breaking changes increment major version.
|
|
10
|
+
*/
|
|
11
|
+
interface ModelVersion {
|
|
12
|
+
major: number;
|
|
13
|
+
minor: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Accessible role following ARIA specification.
|
|
17
|
+
* V1 supports common interactive and structural roles.
|
|
18
|
+
*/
|
|
19
|
+
type AccessibleRole = 'button' | 'link' | 'heading' | 'textbox' | 'checkbox' | 'radio' | 'combobox' | 'listbox' | 'option' | 'list' | 'listitem' | 'article' | 'generic' | 'navigation' | 'main' | 'banner' | 'contentinfo' | 'region' | 'img' | 'complementary' | 'form' | 'search' | 'paragraph' | 'blockquote' | 'code' | 'staticText' | 'table' | 'row' | 'cell' | 'columnheader' | 'rowheader' | 'term' | 'definition' | 'figure' | 'caption' | 'group' | 'dialog' | 'meter' | 'progressbar' | 'status' | 'document' | 'application' | 'separator';
|
|
20
|
+
/**
|
|
21
|
+
* Accessible states and properties.
|
|
22
|
+
* Only includes properties relevant to the element's role.
|
|
23
|
+
*/
|
|
24
|
+
interface AccessibleState {
|
|
25
|
+
/** Whether the element is expanded (e.g., accordion, dropdown) */
|
|
26
|
+
expanded?: boolean;
|
|
27
|
+
/** Whether the element is checked (checkbox, radio, or mixed state) */
|
|
28
|
+
checked?: boolean | 'mixed';
|
|
29
|
+
/** Whether the element is pressed (toggle button) */
|
|
30
|
+
pressed?: boolean | 'mixed';
|
|
31
|
+
/** Whether the element is selected (option, tab, etc.) */
|
|
32
|
+
selected?: boolean;
|
|
33
|
+
/** Whether the element is disabled */
|
|
34
|
+
disabled?: boolean;
|
|
35
|
+
/** Whether the element has invalid input */
|
|
36
|
+
invalid?: boolean;
|
|
37
|
+
/** Whether the element is required */
|
|
38
|
+
required?: boolean;
|
|
39
|
+
/** Whether the element is read-only */
|
|
40
|
+
readonly?: boolean;
|
|
41
|
+
/** Whether the element is busy loading */
|
|
42
|
+
busy?: boolean;
|
|
43
|
+
/** Current page/step/location indicator */
|
|
44
|
+
current?: 'page' | 'step' | 'location' | 'date' | 'time' | 'true' | false;
|
|
45
|
+
/** Whether the element is grabbed for drag-and-drop */
|
|
46
|
+
grabbed?: boolean;
|
|
47
|
+
/** Whether the element is hidden from accessibility tree */
|
|
48
|
+
hidden?: boolean;
|
|
49
|
+
/** Heading level (1-6) */
|
|
50
|
+
level?: number;
|
|
51
|
+
/** Position in set (1-indexed) for lists, tabs, etc. */
|
|
52
|
+
posinset?: number;
|
|
53
|
+
/** Total size of set */
|
|
54
|
+
setsize?: number;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Value for form controls and range widgets.
|
|
58
|
+
*/
|
|
59
|
+
interface AccessibleValue {
|
|
60
|
+
/** Current value */
|
|
61
|
+
current: string | number;
|
|
62
|
+
/** Minimum value (for range widgets) */
|
|
63
|
+
min?: number;
|
|
64
|
+
/** Maximum value (for range widgets) */
|
|
65
|
+
max?: number;
|
|
66
|
+
/** Textual representation (e.g., "50%") */
|
|
67
|
+
text?: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Focusability information.
|
|
71
|
+
*/
|
|
72
|
+
interface FocusInfo {
|
|
73
|
+
/** Whether the element can receive focus */
|
|
74
|
+
focusable: boolean;
|
|
75
|
+
/** Explicit tabindex value (only present if explicitly set) */
|
|
76
|
+
tabindex?: number;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Single node in the accessibility tree.
|
|
80
|
+
*/
|
|
81
|
+
interface AccessibleNode {
|
|
82
|
+
/** ARIA role of the element */
|
|
83
|
+
role: AccessibleRole;
|
|
84
|
+
/** Accessible name (computed via ARIA name algorithm) */
|
|
85
|
+
name: string;
|
|
86
|
+
/** Accessible description (aria-describedby, title, etc.) */
|
|
87
|
+
description?: string;
|
|
88
|
+
/** Value for form controls */
|
|
89
|
+
value?: AccessibleValue;
|
|
90
|
+
/** State properties */
|
|
91
|
+
state: AccessibleState;
|
|
92
|
+
/** Focus information */
|
|
93
|
+
focus: FocusInfo;
|
|
94
|
+
/** Child nodes in the accessibility tree */
|
|
95
|
+
children: AccessibleNode[];
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Root of the Canonical Announcement Model.
|
|
99
|
+
*/
|
|
100
|
+
interface AnnouncementModel {
|
|
101
|
+
/** Model version for forward compatibility */
|
|
102
|
+
version: ModelVersion;
|
|
103
|
+
/** Root node of the accessibility tree */
|
|
104
|
+
root: AccessibleNode;
|
|
105
|
+
/** Metadata about the extraction */
|
|
106
|
+
metadata: {
|
|
107
|
+
/** ISO 8601 timestamp of extraction */
|
|
108
|
+
extractedAt: string;
|
|
109
|
+
/** Optional hash of source HTML for change detection */
|
|
110
|
+
sourceHash?: string;
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Supported roles as a constant array for validation.
|
|
115
|
+
*/
|
|
116
|
+
declare const SUPPORTED_ROLES: readonly AccessibleRole[];
|
|
117
|
+
/**
|
|
118
|
+
* Current model version constant.
|
|
119
|
+
*/
|
|
120
|
+
declare const CURRENT_MODEL_VERSION: ModelVersion;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Validation functions for the Canonical Announcement Model.
|
|
124
|
+
*/
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Validation error class.
|
|
128
|
+
*/
|
|
129
|
+
declare class ValidationError extends Error {
|
|
130
|
+
constructor(message: string);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Validates that a role is supported in V1.
|
|
134
|
+
*
|
|
135
|
+
* @param role - The role to validate
|
|
136
|
+
* @returns true if valid
|
|
137
|
+
* @throws ValidationError if invalid
|
|
138
|
+
*/
|
|
139
|
+
declare function validateRole(role: string): role is AccessibleRole;
|
|
140
|
+
/**
|
|
141
|
+
* Validates accessible state constraints.
|
|
142
|
+
*
|
|
143
|
+
* @param state - The state object to validate
|
|
144
|
+
* @throws ValidationError if invalid
|
|
145
|
+
*/
|
|
146
|
+
declare function validateState(state: AccessibleState): void;
|
|
147
|
+
/**
|
|
148
|
+
* Validates tree structure integrity (no cycles).
|
|
149
|
+
*
|
|
150
|
+
* @param node - The root node to validate
|
|
151
|
+
* @param visited - Set of visited nodes (for cycle detection)
|
|
152
|
+
* @throws ValidationError if cycles detected
|
|
153
|
+
*/
|
|
154
|
+
declare function validateTreeStructure(node: AccessibleNode, visited?: Set<AccessibleNode>): void;
|
|
155
|
+
/**
|
|
156
|
+
* Validates an entire AnnouncementModel.
|
|
157
|
+
*
|
|
158
|
+
* @param model - The model to validate
|
|
159
|
+
* @throws ValidationError if invalid
|
|
160
|
+
*/
|
|
161
|
+
declare function validateModel(model: AnnouncementModel): void;
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* JSON serialization and deserialization for the Canonical Announcement Model.
|
|
165
|
+
*
|
|
166
|
+
* Ensures deterministic output with consistent property ordering.
|
|
167
|
+
*/
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Serialization options.
|
|
171
|
+
*/
|
|
172
|
+
interface SerializationOptions {
|
|
173
|
+
/** Whether to pretty-print the JSON output */
|
|
174
|
+
pretty?: boolean;
|
|
175
|
+
/** Whether to validate the model before serialization */
|
|
176
|
+
validate?: boolean;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Deserialization options.
|
|
180
|
+
*/
|
|
181
|
+
interface DeserializationOptions {
|
|
182
|
+
/** Whether to validate the model after deserialization */
|
|
183
|
+
validate?: boolean;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Serializes an AnnouncementModel to JSON string.
|
|
187
|
+
*
|
|
188
|
+
* Produces deterministic output with consistent property ordering.
|
|
189
|
+
*
|
|
190
|
+
* @param model - The model to serialize
|
|
191
|
+
* @param options - Serialization options
|
|
192
|
+
* @returns JSON string representation
|
|
193
|
+
* @throws ValidationError if validation is enabled and model is invalid
|
|
194
|
+
*/
|
|
195
|
+
declare function serializeModel(model: AnnouncementModel, options?: SerializationOptions): string;
|
|
196
|
+
/**
|
|
197
|
+
* Deserializes a JSON string to an AnnouncementModel.
|
|
198
|
+
*
|
|
199
|
+
* @param json - JSON string to deserialize
|
|
200
|
+
* @param options - Deserialization options
|
|
201
|
+
* @returns Parsed AnnouncementModel
|
|
202
|
+
* @throws SyntaxError if JSON is invalid
|
|
203
|
+
* @throws ValidationError if validation is enabled and model is invalid
|
|
204
|
+
*/
|
|
205
|
+
declare function deserializeModel(json: string, options?: DeserializationOptions): AnnouncementModel;
|
|
206
|
+
/**
|
|
207
|
+
* Creates a new AnnouncementModel with current version and timestamp.
|
|
208
|
+
*
|
|
209
|
+
* @param root - Root accessible node
|
|
210
|
+
* @param sourceHash - Optional hash of source HTML
|
|
211
|
+
* @returns New AnnouncementModel
|
|
212
|
+
*/
|
|
213
|
+
declare function createModel(root: AccessibleNode, sourceHash?: string): AnnouncementModel;
|
|
214
|
+
/**
|
|
215
|
+
* Compares two models for equality (deep comparison).
|
|
216
|
+
*
|
|
217
|
+
* @param a - First model
|
|
218
|
+
* @param b - Second model
|
|
219
|
+
* @returns true if models are equivalent
|
|
220
|
+
*/
|
|
221
|
+
declare function modelsEqual(a: AnnouncementModel, b: AnnouncementModel): boolean;
|
|
222
|
+
/**
|
|
223
|
+
* Clones a model (deep copy).
|
|
224
|
+
*
|
|
225
|
+
* @param model - Model to clone
|
|
226
|
+
* @returns Deep copy of the model
|
|
227
|
+
*/
|
|
228
|
+
declare function cloneModel(model: AnnouncementModel): AnnouncementModel;
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Semantic diff types for comparing accessibility trees.
|
|
232
|
+
*/
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Type of change detected in the diff.
|
|
236
|
+
*/
|
|
237
|
+
type ChangeType = 'added' | 'removed' | 'changed';
|
|
238
|
+
/**
|
|
239
|
+
* JSON path to a node in the tree (e.g., "root.children[0].children[1]").
|
|
240
|
+
*/
|
|
241
|
+
type NodePath = string;
|
|
242
|
+
/**
|
|
243
|
+
* Details about what changed in a node.
|
|
244
|
+
*/
|
|
245
|
+
interface PropertyChange {
|
|
246
|
+
/** Property that changed */
|
|
247
|
+
property: 'role' | 'name' | 'description' | 'value' | 'state' | 'focus';
|
|
248
|
+
/** Old value (undefined for added nodes) */
|
|
249
|
+
oldValue?: unknown;
|
|
250
|
+
/** New value (undefined for removed nodes) */
|
|
251
|
+
newValue?: unknown;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* A single change in the accessibility tree.
|
|
255
|
+
*/
|
|
256
|
+
interface NodeChange {
|
|
257
|
+
/** Type of change */
|
|
258
|
+
type: ChangeType;
|
|
259
|
+
/** JSON path to the node */
|
|
260
|
+
path: NodePath;
|
|
261
|
+
/** The node itself (for added/removed) or property changes (for changed) */
|
|
262
|
+
node?: AccessibleNode;
|
|
263
|
+
/** Specific property changes (only for type='changed') */
|
|
264
|
+
changes?: PropertyChange[];
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Complete diff between two accessibility trees.
|
|
268
|
+
*/
|
|
269
|
+
interface SemanticDiff {
|
|
270
|
+
/** All detected changes */
|
|
271
|
+
changes: NodeChange[];
|
|
272
|
+
/** Summary statistics */
|
|
273
|
+
summary: {
|
|
274
|
+
added: number;
|
|
275
|
+
removed: number;
|
|
276
|
+
changed: number;
|
|
277
|
+
total: number;
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Semantic diff algorithm for accessibility trees.
|
|
283
|
+
*
|
|
284
|
+
* Compares two accessibility trees and identifies:
|
|
285
|
+
* - Added nodes (present in new tree, not in old)
|
|
286
|
+
* - Removed nodes (present in old tree, not in new)
|
|
287
|
+
* - Changed nodes (same path, different properties)
|
|
288
|
+
*/
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Compare two accessibility trees and generate a semantic diff.
|
|
292
|
+
*
|
|
293
|
+
* @param oldTree - The original accessibility tree
|
|
294
|
+
* @param newTree - The updated accessibility tree
|
|
295
|
+
* @returns Semantic diff with all detected changes
|
|
296
|
+
*/
|
|
297
|
+
declare function diffAccessibilityTrees(oldTree: AccessibleNode, newTree: AccessibleNode): SemanticDiff;
|
|
298
|
+
/**
|
|
299
|
+
* Generate a human-readable description of a change.
|
|
300
|
+
*
|
|
301
|
+
* @param change - The change to describe
|
|
302
|
+
* @returns Human-readable description
|
|
303
|
+
*/
|
|
304
|
+
declare function describeChange(change: NodeChange): string;
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Formatting utilities for semantic diff output.
|
|
308
|
+
*
|
|
309
|
+
* Provides JSON and human-readable text formatting for diffs.
|
|
310
|
+
*/
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Format a semantic diff as pretty-printed JSON.
|
|
314
|
+
*
|
|
315
|
+
* @param diff - The semantic diff to format
|
|
316
|
+
* @returns JSON string with 2-space indentation
|
|
317
|
+
*/
|
|
318
|
+
declare function formatDiffAsJSON(diff: SemanticDiff): string;
|
|
319
|
+
/**
|
|
320
|
+
* Format a semantic diff as human-readable text.
|
|
321
|
+
*
|
|
322
|
+
* @param diff - The semantic diff to format
|
|
323
|
+
* @returns Multi-line text description of changes
|
|
324
|
+
*/
|
|
325
|
+
declare function formatDiffAsText(diff: SemanticDiff): string;
|
|
326
|
+
/**
|
|
327
|
+
* Format a semantic diff for CI/CD tools (GitHub Actions, GitLab CI, etc.).
|
|
328
|
+
*
|
|
329
|
+
* Uses a format that's easy to parse and diff-friendly.
|
|
330
|
+
*
|
|
331
|
+
* @param diff - The semantic diff to format
|
|
332
|
+
* @returns CI-friendly text output
|
|
333
|
+
*/
|
|
334
|
+
declare function formatDiffForCI(diff: SemanticDiff): string;
|
|
335
|
+
/**
|
|
336
|
+
* Check if a diff represents a regression (accessibility got worse).
|
|
337
|
+
*
|
|
338
|
+
* Heuristics:
|
|
339
|
+
* - Removed nodes with important roles (button, link, heading, etc.)
|
|
340
|
+
* - Changed nodes that lost accessible names
|
|
341
|
+
* - Changed nodes that became disabled or hidden
|
|
342
|
+
*
|
|
343
|
+
* @param diff - The semantic diff to analyze
|
|
344
|
+
* @returns True if potential regression detected
|
|
345
|
+
*/
|
|
346
|
+
declare function hasAccessibilityRegression(diff: SemanticDiff): boolean;
|
|
347
|
+
|
|
348
|
+
export { type AccessibleNode, type AccessibleRole, type AccessibleState, type AccessibleValue, type AnnouncementModel, CURRENT_MODEL_VERSION, type ChangeType, type DeserializationOptions, type FocusInfo, type ModelVersion, type NodeChange, type NodePath, type PropertyChange, SUPPORTED_ROLES, type SemanticDiff, type SerializationOptions, ValidationError, cloneModel, createModel, describeChange, deserializeModel, diffAccessibilityTrees, formatDiffAsJSON, formatDiffAsText, formatDiffForCI, hasAccessibilityRegression, modelsEqual, serializeModel, validateModel, validateRole, validateState, validateTreeStructure };
|