@orkestrel/scaffold 0.0.104 → 0.0.105

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.
@@ -180,7 +180,7 @@ for a missing or inherited key, and for a member carrying neither. Per-key tuple
180
180
  claim the narrowing carries rather than something the reading checks, so a caller relying on it
181
181
  establishes the pairing between an event and the recorder stored under that event first;
182
182
  `createRecorders` establishes it by wiring each recorder to exactly the event it stores that recorder
183
- under. Every hostile read is contained, so a value whose keys or getters throw answers `false`
183
+ under. Every read of untrusted input is contained, so a value whose keys or getters throw answers `false`
184
184
  instead of propagating.
185
185
 
186
186
  #### Helpers
@@ -277,7 +277,7 @@ markup or from a tag and its classes; `createHarness` mounts a whole statechart
277
277
  back the root it mounted; `buildContrast`, `buildEscapes`, and `buildCensus` each build
278
278
  a detached control the caller appends where it is reading; `clearStorage` takes nothing at all, and
279
279
  `removeDatabase` takes a database name. The predicates, the element readers, and the describers name
280
- a node the caller already has — `isRendered`, `isReachable`, `readHit`, `readText`, `readRole`,
280
+ a node the caller already has — `isRendered`, `isReachable`, `supportsSegmentedFocus`, `readHit`, `readText`, `readRole`,
281
281
  `readName`, `readStates`, `readProtocolPoint`, `readProtocolNode`, `readAccessibilityNode`,
282
282
  `readActiveElement`, `countElements`, `containsNode`, `readCensus`, `describeTree`, `describeFocus`, `extractOrphans`,
283
283
  `readRows`, `readStyle`, `readToken`, `readPixels`, `readContrast`, `readColorContrast`,
@@ -335,24 +335,27 @@ a union's arms escaped as `\|`.
335
335
 
336
336
  A `Shape` cell holds the constant's declared type.
337
337
 
338
- | API | Kind | Shape | Summary |
339
- | -------------------- | ----- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------- |
340
- | `ACCESSIBLE_ROLES` | const | `readonly string[]` | Names the interactive ARIA roles a bare accessible name is searched across. |
341
- | `CANVAS_COLOR` | const | `Color` | Names the color a browser paints an unstyled document with: opaque white. |
342
- | `CAPTURE_PANE` | const | `string` | Names the attribute marking the runner's tester pane, and the rule that sizes it, while a frame is staged. |
343
- | `CAPTURE_STAGINGS` | const | `number` | Bounds the restagings one capture takes before it refuses a document whose height never settles. |
344
- | `CONTENT_ROLES` | const | `readonly string[]` | Names the roles whose accessible name is the text a reader can see inside them. |
345
- | `FIELD_ROLES` | const | `Readonly<Record<string, string>>` | Names the role each `input` type carries. |
346
- | `FOCUSABLE_SELECTOR` | const | `string` | Names what sequential keyboard navigation can reach, before disabled and unrendered elements go. |
347
- | `HEADER_ROLES` | const | `Readonly<Record<string, string>>` | Names the role a `th` carries for the header axis its `scope` names. |
348
- | `IMPLICIT_ROLES` | const | `Readonly<Record<string, string>>` | Names the role each listed tag carries in the accessibility tree when it declares none of its own. |
349
- | `MEDIA_STAGE` | const | `string` | Names the tester root's attribute holding the media readings observed before the first stage. |
350
- | `POINTER_HOLD` | const | `string` | Names the tester root's attribute holding the pressed pointer's page coordinates. |
338
+ | API | Kind | Shape | Summary |
339
+ | ----------------------- | ----- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------- |
340
+ | `ACCESSIBLE_ROLES` | const | `readonly string[]` | Names the interactive ARIA roles a bare accessible name is searched across. |
341
+ | `CANVAS_COLOR` | const | `Color` | Names the color a browser paints an unstyled document with: opaque white. |
342
+ | `CAPTURE_PANE` | const | `string` | Names the attribute marking the runner's tester pane, and the rule that sizes it, while a frame is staged. |
343
+ | `CAPTURE_STAGINGS` | const | `number` | Bounds the restagings one capture takes before it refuses a document whose height never settles. |
344
+ | `CONTENT_ROLES` | const | `readonly string[]` | Names the roles whose accessible name is the text a reader can see inside them. |
345
+ | `FIELD_ROLES` | const | `Readonly<Record<string, string>>` | Names the role each `input` type carries. |
346
+ | `FOCUSABLE_SELECTOR` | const | `string` | Names what sequential keyboard navigation can reach, before disabled and unrendered elements go. |
347
+ | `SEGMENTED_INPUT_TYPES` | const | `readonly string[]` | Names the native input types whose editable segments share one focused element. |
348
+ | `FOCUS_SEGMENT_LIMIT` | const | `number` | Bounds consecutive focus landings and the Tab presses budgeted per segmented input. |
349
+ | `HEADER_ROLES` | const | `Readonly<Record<string, string>>` | Names the role a `th` carries for the header axis its `scope` names. |
350
+ | `IMPLICIT_ROLES` | const | `Readonly<Record<string, string>>` | Names the role each listed tag carries in the accessibility tree when it declares none of its own. |
351
+ | `MEDIA_STAGE` | const | `string` | Names the tester root's attribute holding the media readings observed before the first stage. |
352
+ | `POINTER_HOLD` | const | `string` | Names the tester root's attribute holding the pressed pointer's page coordinates. |
351
353
 
352
354
  #### Helpers
353
355
 
354
356
  | API | Kind | Signature | Summary |
355
357
  | -------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
358
+ | `supportsSegmentedFocus` | function | `(element: Element) => boolean` | Determines whether a native input exposes separately editable segments under one focused element. |
356
359
  | `resolveAccessible` | function | `(name: string) => HTMLElement` / `(role: string, name: string) => HTMLElement` | Resolves one visible, focus-reachable interactive element by its exact accessible name. A wholly-off-viewport target is scrolled into view before reachability is measured. |
357
360
  | `resolveAccessibleWithin` | function | `(region: string, role: string, name: string) => HTMLElement` | Resolves one human-reachable control by role and accessible-name text inside a named region. |
358
361
  | `resolveRendered` | function | `(first: string, second?: string) => HTMLElement` | Resolves one rendered, focus-reachable interactive element without requiring it to intersect the viewport yet. |
@@ -537,12 +540,21 @@ resolution on its own, and `holdAccessibleWithin` and `traverseAccessibleWithin`
537
540
  resolves the way `holdAccessible` and `traverseAccessible` drive a document-wide one, so a twin of
538
541
  the same name in another region is left alone.
539
542
 
540
- `traverseAccessible` charges a step only when focus actually lands on an element, ends when focus
541
- revisits one — that is a complete cycle of the tab order — and re-resolves the target by name on
542
- every step, because a framework may replace the node between resolution and focus arrival. Its hard
543
- cap is three times the page's candidate count plus ten, including disabled controls and elements
544
- with `tabindex="-1"`, so a page whose focus never settles fails instead of hanging.
545
- `driveTraversal` is that loop over any resolver, and `driveHold` is the one pointer drive every hold
543
+ `traverseAccessible` charges a step only when focus actually lands on an element and re-resolves
544
+ the target by name on every step, because a framework may replace the node between resolution and
545
+ focus arrival. Consecutive landings on the same native `date`, `time`, `datetime-local`, `month`, or
546
+ `week` input pass through its editable segments and count that control once, up to
547
+ `FOCUS_SEGMENT_LIMIT` landings. A longer run or any other revisit ends the traversal, including a
548
+ consecutive repeat on a button in a focus trap. Any different landing starts a separate run.
549
+ The `supportsSegmentedFocus` predicate is true for an input whose `type` is in `SEGMENTED_INPUT_TYPES`,
550
+ checked against the element's own document's `HTMLInputElement`, so a same-origin frame's input
551
+ counts. It is false where the document has no window. The hard cap is `FOCUS_SEGMENT_LIMIT` times
552
+ the page's candidate count plus ten, including disabled controls and elements with `tabindex="-1"`.
553
+ The limit budgets ten presses per candidate. Chromium measured nine landings on a `datetime-local`
554
+ input with `step="0.001"`: year, month, day, hour, minute, second, millisecond, and meridiem segments,
555
+ plus the picker indicator. The budget leaves one spare press per input, so a page of segmented
556
+ inputs can finish while a page whose focus never settles still fails.
557
+ `driveTraversal` implements this forward Tab traversal for any resolver. `driveHold` is the one pointer drive every hold
546
558
  verb shares, releasing the pointer before refusing when the frame wait or the pressed-state read
547
559
  fails, as well as when the press misses; each verb supplies its resolver and the name its refusals
548
560
  voice.
@@ -604,7 +616,7 @@ answers as the scope's scheme. The element's own and inherited custom properties
604
616
  property that does not inherit included, and an `input`, an SVG element, or any other element
605
617
  resolves like any other. A custom property is named by letters, digits, `_`, and `-` after its `--`,
606
618
  and a name the scope leaves undeclared is not carried, so its `var()` fallback still applies. The
607
- scope element is never written: the probe is new, so it has no earlier style to start a transition
619
+ scope element is never written: the probe is freshly created, so it has no earlier style to start a transition
608
620
  from, a transition running on the scope keeps running and `currentColor` reads the color the scope
609
621
  shows at that moment, and the scope's geometry and scroll position stay as they were. No attribute,
610
622
  style, or child of the scope changes, so a `MutationObserver` on it records nothing. A disconnected
@@ -663,7 +675,7 @@ unreadable surface too.
663
675
  color it actually sits on. When no layer paints it hands that floor straight back.
664
676
 
665
677
  `readRing` reads and never acts. Focus arrives through the published verbs, and this measures what
666
- the browser painted once it landed: the `outline` the cascade declares, and the first color in a
678
+ the browser painted after it landed: the `outline` the cascade declares, and the first color in a
667
679
  `box-shadow`. A control that is not matching `:focus-visible`, a control left the browser's own
668
680
  `outline-style: auto` ring, and a focus style that only changes the control's own fill all report
669
681
  `undefined` — in each case no measurement taken here would be about focus. `worn` names the element
@@ -720,7 +732,7 @@ where that distinction matters.
720
732
 
721
733
  `stagePane` unscales the runner's tester and lifts it to the window's origin, because a frame shot
722
734
  through the runner's fitting scale is a thumbnail of the surface. That couples it to Vitest's own
723
- tester layout, which is contract rather than accident: `vitest@4.1.11` is the version behind the
735
+ tester layout, which is contract rather than accident: `vitest@4.1.12` is the version behind the
724
736
  `iframe[data-vitest]` selector and the `--tester-transform`, `--tester-margin-left`,
725
737
  `--viewport-width`, and `--viewport-height` custom properties it writes. A release that renames any
726
738
  of them reddens the size check rather than writing a wrong frame. Always hand the pane back with
@@ -909,7 +921,7 @@ A `Shape` cell holds the constant's declared type.
909
921
  | ------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
910
922
  | `readInventory` | function | `(root: URL \| string, targets: readonly string[], options?: InventoryOptions) => Readonly<Record<string, string>>` | Reads files from selected targets below a root directory. |
911
923
  | `resolveContained` | function | `(root: string, target: string) => string \| undefined` | Resolves a target that stays below a root directory. |
912
- | `requireContained` | function | `(root: string, target: string) => string` | Resolves a target that stays below a root directory, refusing an escape. |
924
+ | `requireContained` | function | `(root: string, target: string) => string` | Resolves a target that stays below a root directory, refusing a path outside it. |
913
925
  | `hasScratchPath` | function | `(root: string, target: string) => boolean` | Reports whether a contained entry exists under a scratch directory without following its final link. |
914
926
  | `isExcluded` | function | `(key: string, exclusions: readonly string[]) => boolean` | Reports whether a root-relative key matches an exclusion. |
915
927
  | `readIdentity` | function | `(status: Stats) => ScratchIdentity` | Reads the identity of one allocated directory off a host status. |
@@ -948,10 +960,10 @@ delete `resolveContained` and import `resolveContainedPath` from `@orkestrel/sca
948
960
  package already carries as a `devDependency`, rather than adding a third variant.
949
961
 
950
962
  `requireContained` is that same resolution with the refusal every contained scratch operation makes
951
- of an escape: it throws `Path outside scratch directory: <target>` where `resolveContained` answers
963
+ of a path outside the root: it throws `Path outside scratch directory: <target>` where `resolveContained` answers
952
964
  `undefined`. Every `ScratchInterface` member and the `files` seeding pass through it, so the check
953
- and its one message are stated once rather than at each member. Read `resolveContained` where an
954
- escape is an answer the caller handles rather than a refusal it wants raised.
965
+ and its one message are stated once rather than at each member. Read `resolveContained` where
966
+ a path outside the root is an answer the caller handles rather than a refusal it wants raised.
955
967
 
956
968
  `isExcluded` is the exclusion rule itself, and `readInventory` applies it to a named target and a
957
969
  walked entry alike. An exclusion matches whole segments of a root-relative key, so it drops the key
@@ -962,9 +974,9 @@ is exported because a consumer walking its own tree wants that rule rather than
962
974
  what `exclude` means.
963
975
 
964
976
  `matchesIdentity` is the comparison `destroy()` makes before it removes anything: whether the
965
- identity read from the allocated path now is the identity recorded when the directory was allocated.
977
+ identity read from the allocated path is the identity recorded when the directory was allocated.
966
978
  All three fields are compared because none of them alone identifies an allocation. A device is
967
- shared by every directory on one filesystem, an index node is reused once its directory is removed,
979
+ shared by every directory on one filesystem, an index node is reused after its directory is removed,
968
980
  and a creation time repeats within the host's timestamp resolution. It is exported so a fixture that
969
981
  manages its own directory can make the same check rather than trusting a path. `readIdentity` reads
970
982
  that triple off a `node:fs` `Stats`, and it is the one reading `createScratch` takes at allocation,
@@ -1023,7 +1035,7 @@ that names the mechanism it needs: an unprivileged Windows host answers `support
1023
1035
  `true` through a junction while answering `supportsFileLinks` `false`, so a fixture that reads a
1024
1036
  file through a link asks the second question rather than the first. `supportsMode` answers whether a
1025
1037
  permission bit is stored, which is narrower than whether it is enforced — a POSIX host running as
1026
- uid `0` stores every bit faithfully and bypasses the access check those bits describe, so a proof
1038
+ uid `0` stores every bit faithfully and is exempt from the access check those bits describe, so a proof
1027
1039
  that needs a refusal probes that refusal itself.
1028
1040
 
1029
1041
  #### Factories
@@ -1045,7 +1057,7 @@ starts the generated directory name, and defaults to `orkestrel-test-`; allocati
1045
1057
  contains `/` or `\`, which is what stops a prefix steering the allocation out of its parent. Nothing
1046
1058
  else is refused: a fragment carrying no separator is one path segment, so `release-0..2-` allocates.
1047
1059
  `files` seeds files on allocation, keyed by path below the scratch directory; allocation removes the
1048
- directory it recently made and rethrows when a key escapes or the host refuses a write.
1060
+ directory it recently made and rethrows when a key resolves outside it or the host refuses a write.
1049
1061
 
1050
1062
  `createLoopback` takes a `node:net` `Server` — `node:http`'s and `node:https`'s both extend it — and
1051
1063
  never constructs one. It listens on port `0` at `127.0.0.1`, waits for the `listening` event, and
@@ -1460,7 +1472,7 @@ These hold across `src/core`, `src/browser`, `src/server`, and this guide.
1460
1472
  nor a directory, or when a target resolves outside the root. A missing target surfaces the host's
1461
1473
  own `ENOENT` rather than a message from this package. It skips a symlink met while walking rather
1462
1474
  than following it. A target may be written relative to the root or as an absolute path inside it,
1463
- and one that escapes is refused either way.
1475
+ and one that resolves outside the root is refused either way.
1464
1476
 
1465
1477
  A link in the **middle** of a named target is a separate refusal, and the only one that reaches
1466
1478
  it: the symbolic-link check reads the final segment, which such a link is not. The named target is
@@ -1491,13 +1503,13 @@ These hold across `src/core`, `src/browser`, `src/server`, and this guide.
1491
1503
  case are the same file varies by filesystem, so the suite probes the running host and asserts
1492
1504
  what the probe returned instead of assuming either answer.
1493
1505
 
1494
- 7. **`createScratch` refuses a lexical escape, not a symbolic link.** It allocates with
1506
+ 7. **`createScratch` refuses a path lexically outside its directory, not a symbolic link.** It allocates with
1495
1507
  `mkdtempSync` below `parent`, which creates the directory at mode `0700` on a host that applies
1496
1508
  POSIX permission bits. The suite gates that assertion on `supportsMode`, so the mode is proven
1497
1509
  where the probe answers `true` and the case is skipped where it answers `false`. A Windows host
1498
1510
  answers `false` and reads the same allocation back as `0666`, so the bits describe nothing it
1499
1511
  applies. Every member that takes a target — `write`, `read`, `has`, `names`, `ensure`, `link`,
1500
- and `remove` — throws when that target lexically escapes the allocated directory, and a failed
1512
+ and `remove` — throws when that target lexically resolves outside the allocated directory, and a failed
1501
1513
  seed removes the directory before rethrowing. `remove` adds a refusal the others do not need: a
1502
1514
  target naming the allocation itself, lexically or through an intermediate symbolic link. The
1503
1515
  lexical half compares paths. The physical half reads only the final entry with `lstat` and
@@ -1594,7 +1606,7 @@ These hold across `src/core`, `src/browser`, `src/server`, and this guide.
1594
1606
  lists every member with the reading it breaks. Every member makes a naive read throw or violates
1595
1607
  a naive structural assumption; a total guard survives every member without throwing. Each has a
1596
1608
  direct probe for that failure. The negative control keeps an inert value from entering the corpus
1597
- under a hostile name. Whether it accepts or refuses one is that guard's own contract. Membership
1609
+ under a name suggesting untrusted input. Whether it accepts or refuses one is that guard's own contract. Membership
1598
1610
  may grow in a release, so consumers loop over the whole array, assert their guard's expected
1599
1611
  answer per index, and attribute each failure by that index instead of naming or counting members
1600
1612
  locally.
@@ -1604,7 +1616,7 @@ These hold across `src/core`, `src/browser`, `src/server`, and this guide.
1604
1616
  into a description of the markup. `build` creates a node, `mount` attaches one, `render` does
1605
1617
  both, `clearStorage` takes nothing at all, and `removeDatabase` takes a database name. The
1606
1618
  predicates, the element readers, and the describers do take a node —
1607
- `isRendered`, `isReachable`, `readHit`, `readText`, `readRole`, `readName`, `readStates`,
1619
+ `isRendered`, `isReachable`, `supportsSegmentedFocus`, `readHit`, `readText`, `readRole`, `readName`, `readStates`,
1608
1620
  `readCensus`, `describeTree`, `describeFocus`, `extractOrphans`, `readRows`, `readStyle`,
1609
1621
  `readToken`, `readPixels`, `readContrast`, `readColorContrast`, `readLayers`, `readBackdrop`,
1610
1622
  and `readRing` — and each is a reader of a node the caller already has rather than a verb that
@@ -1707,7 +1719,7 @@ These hold across `src/core`, `src/browser`, `src/server`, and this guide.
1707
1719
  stopped, and `steps` and `output` hand out snapshots. There is no shared instance: a file that
1708
1720
  needs one journal per scenario creates one per scenario.
1709
1721
  18. **The capture layer depends on the Vitest runner's own tester layout, deliberately.** A
1710
- screenshot is taken off the page the runner painted, and `vitest@4.1.11` lays its tester out
1722
+ screenshot is taken off the page the runner painted, and `vitest@4.1.12` lays its tester out
1711
1723
  inside a smaller page and fits it by scaling the pane the tester sits in, so a frame shot through
1712
1724
  that scale is a thumbnail of the surface. `stagePane` therefore reaches into that layout: the
1713
1725
  `iframe[data-vitest]` selector and the `--tester-transform`, `--tester-margin-left`,
@@ -1777,15 +1789,15 @@ that applies POSIX permission bits, and the suite asserts that mode where `suppo
1777
1789
  answers `false` the mode protects nothing, and the allocation carries only the access its parent
1778
1790
  directory already gives. The mode does not keep out a sibling test worker or the code under test on
1779
1791
  any host, because both run as the same uid, and they are the population that would create a link
1780
- here. Its containment check is lexical: it refuses a relative path that escapes the allocated
1792
+ here. Its containment check is lexical: it refuses a relative path that resolves outside the allocated
1781
1793
  directory, which is the accident that actually happens — a test writing `../foo`. It does not walk
1782
1794
  the path's segments for symbolic links, because per-segment walking is sandbox behavior and this is
1783
1795
  not a sandbox.
1784
1796
 
1785
1797
  So a link inside the allocation was created by the test process or by the code the test drives, and
1786
1798
  handing `scratch.path` to the code under test is the ordinary use of this helper. `link` is this
1787
- package's own entry in that population: it creates a symbolic link on request, it refuses only an
1788
- escaping target, and its source may name anything. The source stays unchecked except on the fallback
1799
+ package's own entry in that population: it creates a symbolic link on request, it refuses only a
1800
+ target outside its directory, and its source may name anything. The source stays unchecked except on the fallback
1789
1801
  path, which stats it to decide whether a junction can point there. [Traversal](#traversal) states
1790
1802
  what each member does with a link it meets, and a contained path reaching outside the allocation
1791
1803
  through one is the result. This helper does not defend against that.
@@ -1829,7 +1841,7 @@ a time in the following table, and some of them ship while others do not.
1829
1841
 
1830
1842
  A member is one implementation group carried by one package, under whatever name that package
1831
1843
  spells it and whether it exports the helper or declares it inside a test file. Repeated calls routed
1832
- through one shared implementation stay one member, and a set of adversarial values fed through one
1844
+ through one shared implementation stay one member, and a set of unexpected values fed through one
1833
1845
  totality loop is one member rather than one per value. Read the first column as the group rather
1834
1846
  than as an export: **nothing in this section is importable**, and the only names you can install are
1835
1847
  in [Surface](#surface).
@@ -1841,12 +1853,12 @@ or when a consumer appears the ruling did not consider.
1841
1853
  | Candidate | Ruling | Why |
1842
1854
  | ------------------------------------------------------------------------------------------------ | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1843
1855
  | A recorder map over an emitter's events, with its map, event-map, subscriber, and totality types | Ships | It ships as `createRecorders`, with `RecorderMap` and `EventSourceInterface` beside it. Inference is what the earlier refusal turned on, and the shape improves it without settling it: a source parameter typed `EventSourceInterface<TMap>` is an inference site, so a call against one names no type argument, while a concrete class supplies none and the call names both. A keying limit survives that: `TName` derives from the events array's element type, so an array declared with a wider union than its contents keys the map past the events actually listed, and [Bounds a shipped helper carries](#bounds-a-shipped-helper-carries) states what to pass instead. A published signature still cannot import a consumer's event map, so the interface asks for the subscribe half alone and the consumer's own map is what it is instantiated with. |
1844
- | Hostile guard-input sets | Ships | `form`, `table`, and `supervisor` each feed one adversarial set through total readers, and the set is a mechanism rather than a policy: a guard's own contract decides the answers, not the corpus. It ships as `createHostileValues`; its members do not each become a factory, and every member carries a naive-reader negative control. |
1856
+ | Unexpected guard-input sets | Ships | `form`, `table`, and `supervisor` each feed one unexpected-value set through total readers, and the set is a mechanism rather than a policy: a guard's own contract decides the answers, not the corpus. It ships as `createHostileValues`; its members do not each become a factory, and every member carries a naive-reader negative control. |
1845
1857
  | Raw invocation — `invokeRaw` | Ships | It ships as `invokeUnchecked`, with `readProperty` beside it for the read. Native `Reflect.apply` still makes the call; what these add is the boundary — a callability refusal before the call, a target refusal before the read, and one named place where an unchecked runtime result meets the type its caller claims. The claim stays the caller's, and so does the guard that narrows what came back. Without them a consumer that bans `as` cannot drive a foreign object at all. |
1846
1858
  | Condition polling — wall-clock predicate loops | Ships | The wait-family contract states the distinction: the no-polling architecture law governs a product's idle wakeup, and a test instrument waiting on a fact another process produces has no event to park on. It ships as `waitForCondition`. `retryUntil` ships on the same reading, because retrying a real operation is not re-reading a predicate; and where an event does exist, `waitForEvent` is the door. |
1847
1859
  | Deep nesting beyond a guard's cap | Refused | `table` builds a record chain and `supervisor` builds nested arrays. The two nest different containers, so one shared factory needs a selector argument that changes the construction algorithm — a mode switch rather than a mechanism. |
1848
1860
  | Canonical wire fixpoint assertions | Refused | `form` and `table` each serialize, parse untrusted JSON, serialize again, and compare exact bytes. The comparison is an assertion over the consumer's own codecs rather than a reusable mechanism, so the shape stays consumer-local and [Prove a wire fixpoint](#prove-a-wire-fixpoint) publishes the pattern instead. |
1849
- | Numeric corpora, hostile-key tables, and deep-freeze | Refused | A numeric corpus or a hostile-object table is test policy — what a given suite decided to check — rather than a mechanism, and one factory covering the variants would need a mode argument. `createHostileValues` ships because a guard's totality is a property of the guard; these encode a decision about coverage. |
1861
+ | Numeric corpora, unexpected-key tables, and deep-freeze | Refused | A numeric corpus or an unexpected-object table is test policy — what a given suite decided to check — rather than a mechanism, and one factory covering the variants would need a mode argument. `createHostileValues` ships because a guard's totality is a property of the guard; these encode a decision about coverage. |
1850
1862
  | Clearing web storage between tests | Ships | Emptying local and session storage together is one mechanism, and the `afterEach` hook that must run after a failed test too is where every browser suite needs it. It ships as `clearStorage`. |
1851
1863
  | Class-ancestry orphan detection | Ships | A rendered element carrying a child class with no container class above it is a real invariant a stylesheet cannot state, and the check is mechanism when the class names are parameters rather than one framework's. It ships as `extractOrphans(root, child, parent)`. |
1852
1864
  | A DOM element builder | Ships | It ships as `build` for the element and `mount` for the attachment, and `render` widened to take a tag and its class list as well as markup. A class list, a text, and an attribute map are what a fixture actually varies, and expressing that variation through markup means assembling a string. Nothing here assembles a tree one call at a time: a fixture with children is still written as markup. |
@@ -1942,8 +1954,11 @@ the helper rather than to the host, and each names what to reach for instead.
1942
1954
  host stands for what it holds, and focus in a frame from another origin throws, because the page
1943
1955
  cannot name a control there. A hidden or disabled stop is returned like any other. A consumer that
1944
1956
  refuses one throws from the predicate it passes. The walk ends where focus returns to the body or
1945
- revisits a stop, and throws only when it outlasts the count `countElements` reads, which includes
1946
- open shadow roots and same-origin frames. A control inside a same-origin frame belongs to the
1957
+ revisits a stop, except that consecutive landings on the same native segmented input pass through
1958
+ its segments and contribute one stop, up to `FOCUS_SEGMENT_LIMIT` landings including the initial
1959
+ focused element. A longer run or a consecutive repeat on any other element ends the walk.
1960
+ Any different landing starts a separate run. The press cap is `FOCUS_SEGMENT_LIMIT` times the count `countElements` reads, which includes
1961
+ open shadow roots and same-origin frames; exceeding it throws. A control inside a same-origin frame belongs to the
1947
1962
  frame's window, so it fails this window's `instanceof HTMLElement`. `collectTabStops` is the same
1948
1963
  walk bounded to a region by `containsNode`, which places a control inside an open shadow root or a
1949
1964
  same-origin frame in the region that holds its host or frame element. The start element is the
@@ -1961,7 +1976,7 @@ the helper rather than to the host, and each names what to reach for instead.
1961
1976
  `parkPointer`, so a held press stays held and only loses its hover and pressed paint; `pointer: false`
1962
1977
  leaves the pointer, its hover, and that paint where they are. It waits out every finite animation
1963
1978
  in the document, root element included, then pauses each endless one at its first frame and
1964
- resolves to the resume. Call the resume from a `finally` block once the frame is shot. A settle
1979
+ resolves to the resume. Call the resume from a `finally` block after the frame is shot. A settle
1965
1980
  whose wait exhausts throws before it holds anything, so it owes no resume.
1966
1981
  - **`readShotPixel` reads a shot at the runner's scale.** The position is a fraction of the shot,
1967
1982
  and a fraction of `1` reads its last row or column. Where the runner scales the tester, the shot's
@@ -2028,7 +2043,7 @@ the helper rather than to the host, and each names what to reach for instead.
2028
2043
  It resolves the reply since 0.0.27, where 0.0.26 resolved nothing. A callback typed to return
2029
2044
  `void | Promise<void>`, such as a teardown or an `onTestFinished` hook, that returns
2030
2045
  `sendProtocol(…)` directly no longer type-checks. Write `async () => { await sendProtocol(…) }`.
2031
- - **`readProtocolPoint` reads the layout now, and `readProtocolNode` hands back an id to use at
2046
+ - **`readProtocolPoint` reads the layout at the call, and `readProtocolNode` hands back an id to use at
2032
2047
  once.** The point is the layout at the call, so a box that moves afterwards moves the point it
2033
2048
  named. Each frame on the way up adds its border and its padding, because its content starts inside
2034
2049
  both. An element no document holds is refused, because its window still names the tester frame and
@@ -2046,7 +2061,7 @@ the helper rather than to the host, and each names what to reach for instead.
2046
2061
  handler took and destroys it before destroying the loopback.
2047
2062
  - **`destroyScratch`'s behavior under a permission hold is unproven where the hold cannot bind.** The
2048
2063
  retry path is proven against a real host refusal, and a host that produces no such refusal cannot
2049
- exercise it: a container running as uid `0` bypasses the access check the mode bits describe, so
2064
+ exercise it: a container running as uid `0` is exempt from the access check the mode bits describe, so
2050
2065
  the suite reads a runtime probe and skips that case rather than asserting either answer. Read
2051
2066
  `supportsMode` for the narrower question of whether the bits are stored at all.
2052
2067
  - **`createRecorders` keys its map from the events array's declared element type.** An array declared
@@ -2062,7 +2077,7 @@ the helper rather than to the host, and each names what to reach for instead.
2062
2077
  the quota, and the grant are.
2063
2078
  - **`readProperty`'s `TypeError` names the target, never the read.** It refuses a target that is
2064
2079
  neither an object nor a function before it reads anything, and a getter that throws on an accepted
2065
- target hands that throw straight to the caller. Wrap the call in `captureError` where a hostile
2080
+ target hands that throw straight to the caller. Wrap the call in `captureError` where an untrusted
2066
2081
  getter is the subject.
2067
2082
  - **`readPixels` reports a measured contribution rather than a parsed length.** A resolved value
2068
2083
  carrying no leading number — `'auto'`, `'none'`, `''` — reads as `0`, because none of them
@@ -2513,9 +2528,9 @@ for (const [index, value] of values.entries()) {
2513
2528
  ```
2514
2529
 
2515
2530
  The corpus is the positive proof input. Keep a negative control for every member too: perform the
2516
- naive read or the naive structural reading that member is meant to break, and prove it answers the
2517
- way the member's own hostility says. Without that control, an inert value can make the totality loop
2518
- look stronger without exercising another hostile boundary.
2531
+ naive read or the naive structural reading that member is meant to break, and prove it fails the
2532
+ way the member's structure dictates. Without that control, an inert value can make the totality loop
2533
+ look stronger without exercising another untrusted-input boundary.
2519
2534
 
2520
2535
  This package's own suite carries one control per member, in corpus order, and each names the reading
2521
2536
  that member breaks:
@@ -2855,7 +2870,7 @@ readInventory(root, ['src/core/index.ts'], { extensions: ['.ts'], exclude: ['src
2855
2870
  ### Own a temporary directory
2856
2871
 
2857
2872
  This demonstration builds a scratch directory seeded with a file, writes and reads inside it,
2858
- refuses an escaping write, and nests one allocation inside another.
2873
+ refuses a write outside its directory, and nests one allocation inside another.
2859
2874
 
2860
2875
  ```ts
2861
2876
  import { createScratch } from '@orkestrel/test/server'
@@ -3114,9 +3129,9 @@ jar.read('session') // undefined
3114
3129
  jar.header // 'theme=dark'
3115
3130
  ```
3116
3131
 
3117
- ### Refuse an escaping path in your own fixture
3132
+ ### Refuse a path outside the root in your own fixture
3118
3133
 
3119
- `readInventory` and `createScratch` refuse an escape with this predicate. Reach for it when a
3134
+ `readInventory` and `createScratch` refuse a path outside the root with this predicate. Reach for it when a
3120
3135
  fixture of your own resolves a caller-supplied path below a root.
3121
3136
 
3122
3137
  ```ts
@@ -3808,7 +3823,7 @@ finds beside it, because a picture does not show how far a pane scrolls.
3808
3823
  ### Measure a document's content edge
3809
3824
 
3810
3825
  A capture stages the pane at the height the document needs, and the body's box cannot answer for
3811
- that height once the pane is taller than the document. In the following fence the document holds
3826
+ that height after the pane grows taller than the document. In the following fence the document holds
3812
3827
  1600 rows of fixed content and nothing is bound to the viewport.
3813
3828
 
3814
3829
  ```ts
@@ -3885,7 +3900,7 @@ picking one of them.
3885
3900
  is either slower than it needs to be or shorter than the slowest host, and it reports nothing when
3886
3901
  it fails. Name the fact instead, and let `waitForCondition`, `retryUntil`, or `waitForEvent` decide
3887
3902
  when it holds.
3888
- - **Let `readInventory` refuse.** A symlinked root or an escaping target is an error, not a
3903
+ - **Let `readInventory` refuse.** A symlinked root or a target outside the root is an error, not a
3889
3904
  filtered result, so a misconfigured walk fails loudly instead of returning a short map.
3890
3905
  - **Reach for `parent` only when the allocation must be somewhere named.** The default keeps it out
3891
3906
  of the repository, and a path inside a package tree is walked by every tool that reads that tree.
@@ -3944,7 +3959,7 @@ Each entry names the contracts its file proves. The test names carry the cases.
3944
3959
  with every remaining handler still run, both together aggregated in run order, a handler added
3945
3960
  during a run kept for the next call, the count reset before the handlers run, and a `destroy()`
3946
3961
  that is called empty and called twice. `createHostileValues` proves a naive-reader failure for
3947
- every member, frozen and fresh membership, and one total guard's benign and hostile answers with
3962
+ every member, frozen and fresh membership, and one total guard's answers for ordinary and unexpected inputs with
3948
3963
  loop-index attribution.
3949
3964
  - [`tests/src/browser/helpers.test.ts`](../tests/src/browser/helpers.test.ts),
3950
3965
  [`tests/src/browser/journeys/helpers.test.ts`](../tests/src/browser/journeys/helpers.test.ts),
@@ -4021,10 +4036,14 @@ Each entry names the contracts its file proves. The test names carry the cases.
4021
4036
  Each acting verb takes its happy path and every voice it owns, including both
4022
4037
  region-scoped refusals and both native-disclosure ones; `clickAccessibleWithin` also takes a
4023
4038
  glyph-captioned control inside a region a glyph-carrying heading labels, which is the loose match
4024
- proving unchanged. `traverseAccessible` takes a Tab-reachable
4025
- target; as the cap control, a lone target whose own focus handler blurs it, so focus never lands
4039
+ proving unchanged. `traverseAccessible` takes a Tab-reachable target, a crossing for each type in
4040
+ `SEGMENTED_INPUT_TYPES`, and a page of fractional-second `datetime-local` inputs that traversal
4041
+ and census both cross with spare presses for another landing per input. As the cap control, it
4042
+ takes a lone target whose own focus handler blurs it, so focus never lands
4026
4043
  and the cap fails with an empty trail; and, as the cycle control, the same self-blurring target
4027
4044
  behind a reachable decoy, so the traversal completes one cycle and reports the decoy in its trail.
4045
+ `driveTraversal` takes a non-modal button trap with a resolvable target and refuses after the
4046
+ repeated landing, plus a segmented input trap that refuses within its consecutive landing budget.
4028
4047
  The page readers take their own inputs: `readPerception` takes one named region including its
4029
4048
  visually hidden text, a region a glyph-carrying heading labels through `aria-labelledby`, a painted
4030
4049
  region an `aria-hidden` attribute withholds, and its not-visible and ambiguous refusals, `readPage`
@@ -4041,8 +4060,16 @@ Each entry names the contracts its file proves. The test names carry the cases.
4041
4060
  frame holding focus with nothing inside, and a frame from another origin refused;
4042
4061
  `countElements` takes the shadow and frame content a plain query misses; `containsNode` takes a
4043
4062
  region's own content, a shadow root's and a frame's, and a detached node left outside;
4044
- `stepFocus` takes one press per landing, none past the consumer's last read, a limit of zero, and
4045
- a landing on nothing. `walkFocus` and `collectTabStops` take the buttons of an open shadow root
4063
+ `stepFocus` takes one press per landing, none past the consumer's last read, a limit of zero,
4064
+ a landing on nothing, repeated segment landings for each type in `SEGMENTED_INPUT_TYPES`, and
4065
+ the measured nine landings of a fractional-second `datetime-local` input with a spare press in
4066
+ its budget. `supportsSegmentedFocus` takes the frozen type list, every listed type beside text
4067
+ and number inputs, a button and a non-input carrying `type="date"`, an input from a same-origin
4068
+ frame, and a document with no window. `walkFocus` takes a date input counted once before the
4069
+ following control, a single-button modal trap that returns after one press, and a lone segmented
4070
+ input trap that returns within its consecutive landing budget. `collectTabStops` takes a region
4071
+ holding a date input with every stop listed and a trapped date input returned within that budget.
4072
+ `walkFocus` and `collectTabStops` take the buttons of an open shadow root
4046
4073
  and of a same-origin frame between two light controls, a start element inside a shadow root, a
4047
4074
  start element immediately before the region that reaches the region's first stop, and
4048
4075
  focus in a foreign frame refused; `readProtocolPoint` takes a padded frame beside a bordered one,
@@ -4206,8 +4233,8 @@ Each entry names the contracts its file proves. The test names carry the cases.
4206
4233
  or drag event whose `{ composed: false }` override keeps it inside the root.
4207
4234
  - [`tests/src/server/helpers.test.ts`](../tests/src/server/helpers.test.ts) — the `readInventory` and
4208
4235
  wait-family contracts, and each pure leaf against its own inputs. `resolveContained` takes
4209
- contained relative and absolute targets and both spellings of an escape, and `requireContained`
4210
- takes the same contained pair and each escape refused with the message naming the target it was
4236
+ contained relative and absolute targets and both spellings of a path outside the root, and `requireContained`
4237
+ takes the same contained pair and each path outside the root refused with the message naming the target it was
4211
4238
  given. `readIdentity` takes a real
4212
4239
  allocation's three fields, that allocation matching itself across a `stat` and an `lstat`, and a
4213
4240
  second allocation reading as a different identity. `readErrorCode` takes a real `ENOENT` off the
@@ -4248,14 +4275,14 @@ Each entry names the contracts its file proves. The test names carry the cases.
4248
4275
  leaves a moved allocation alone. Then one group per subject.
4249
4276
  `destruction` takes `write`, `read`, `has`, `names`, `ensure`, `link`, and `remove` after
4250
4277
  `destroy()`, with `write`, `ensure`, and `link` also proven not to rebuild the allocation root, and
4251
- `remove` proving its root and escape refusals answer before the destroyed-allocation one.
4278
+ `remove` proving its root and path-containment refusals answer before the destroyed-allocation one.
4252
4279
  `names` takes its sorted output, including the population that discriminates a dropped `.sort()`.
4253
4280
  `ensure` takes an empty directory, every missing parent, and a repeated call. `link` takes
4254
4281
  traversal through a planted link, the final segment `has` reports rather than follows, and the
4255
4282
  `EEXIST` an occupied final segment throws. `remove` takes a file beside a kept
4256
4283
  sibling, an empty directory, a populated subtree, a missing target, an ancestor link back to the
4257
4284
  allocation with every seeded file read back afterwards, a final link whose destination is read back
4258
- afterwards, a sibling directory reached through that same ancestor link, an escaping target with
4285
+ afterwards, a sibling directory reached through that same ancestor link, a target outside the root with
4259
4286
  the file outside left intact, the root refused as `''`, as `'.'`, and as its absolute path, a
4260
4287
  foreign directory swapped onto the allocated path that `remove('')` refuses, and that same swap
4261
4288
  under `destroy()`, which removes nothing either. `parent` and `prefix` take their own refusals.