npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sr-receipts/voiceover

v0.4.0

Published

VoiceOver as a reader session, in process on a Mac, for the sr-receipts Playwright fixture; and a read-only doctor for its prerequisites.

Readme

@sr-receipts/voiceover: VoiceOver on a local Mac

The bundled VoiceOver, driven in process by the Playwright fixture, beside whatever browser Playwright launched: its WebKit, or a branded Chrome, never Safari. Deliberately narrow: macOS 26.6.x, the bundled VoiceOver, English, and @guidepup/guidepup 0.34.0.

Two things live here: the doctor, a read-only check of the prerequisites, and the session itself, src/session.ts, with the launch retry, the sentinel and the hints settled inside it, the Item Chooser walk in src/entry.ts, and the permission probe. A VoiceOver test needs the authorization below, and takes over the Mac while it runs.

A consumer installs this package beside @sr-receipts/playwright, which loads it only for a voiceover project:

npm install --save-dev @sr-receipts/voiceover

One-time human authorization

Do these actions manually, while signed in to the interactive Mac desktop:

  1. Install this package in the consumer, as above. In this repository, npm ci at the root installs it, and the commands below run from packages/voiceover.

  2. Start the bundled VoiceOver once manually (Command-F5), complete Apple's first-run onboarding, then open VoiceOver Utility → General and enable Allow VoiceOver to be controlled with AppleScript. This establishes the local VoiceOver configuration without writing TCC's database.

  3. In System Settings → Privacy & Security → Accessibility, enable the terminal application that will run playwright test. When macOS presents one, also approve the Guidepup helper used by this installation.

  4. In System Settings → Privacy & Security → Automation, allow that terminal (and any presented Guidepup helper) to control VoiceOver, System Events and the browser Playwright launches (Playwright for its WebKit). These rows appear only after the relevant first request; rerun the test and answer the system prompt rather than editing TCC.

  5. In System Settings → Privacy & Security → Full Disk Access, enable the same terminal application. It needs access to VoiceOver's protected group preference directory while Guidepup mounts its preference asset. If the terminal is an IDE-integrated terminal, grant the IDE application instead. Quit and reopen that application after changing Full Disk Access.

  6. Keep the system language list containing English and use the bundled VoiceOver. Do not run this against a manually installed replacement.

  7. Download the asset matched to this package's Guidepup version:

    npx @guidepup/setup install voiceover

    This is the supported asset-install step, not the TCC-bypass setup step. It downloads the VoiceOver preference asset; rerun it after changing Guidepup. It is not a substitute for the manual permissions above.

Never run @guidepup/setup setup for VoiceOver: that setup flow can attempt to write or bypass macOS's TCC database. Nothing here attempts to grant, reset, or bypass TCC permissions.

The grants follow the application that hosts the shell. A playwright test launched from an application without them fails at the start with an EPERM on VoiceOver's group container, and the session refuses to start when its permission probe finds Accessibility or Automation missing, because the reader holds the keyboard once it is talking.

The doctor

npx sr-voiceover-doctor    # in a consumer
npm run doctor             # here

Read-only: platform, macOS version, the bundled VoiceOver, Guidepup and its asset, access to VoiceOver's preferences, AppleScript control, and the language. It writes doctor.json in the directory it was run from. It cannot see Accessibility or Automation; the session probes those itself, with a lone Control press, before starting the reader.

What a run does, and why

  • The launch is retried, on the launch race only. Guidepup terminates and restarts VoiceOver inside start(), and can activate it before macOS has finished launching it: AppleScript -600, "application isn't running". Roughly one start in seven failed this way in the pivot's proof of concept. Five attempts with a ten second settle, every attempt in the transcript; anything else fails on the first attempt. See start in src/session.ts.
  • Web content is entered through the Item Chooser, the sequence Guidepup's own fixture uses, with both loops bounded: ten attempts to open the chooser, five rounds of typing "web content". On macOS 26 the chooser usually speaks only its highlighted entry, "On item ", never the words "Item Chooser", so that phrase is matched. Arrival is the sequence returning and the first item having text.
  • A sentinel is spoken before every command. Guidepup reads VoiceOver's last phrase and decides a command's speech is new by comparing it with the phrase it recorded last, which cannot tell silence from a repeat. Speaking "receipts mark" first makes last phrase something no page says: a silent command leaves it in place and is recorded as silence; a repeated announcement differs from it and is recorded whole. See "Speech" in src/session.ts.
  • Hints are off and stripped. SCRShouldOutputVOInstructions: false, found in VoiceOver's framework strings, not documentation. A hint arrives about a second after the announcement and restarts Guidepup's wait for speech to settle; with hints on, most steps took 8.5 to 15 seconds instead of 5 to 7.5. Any hint that gets through is cut from what is recorded, and a capture that was only a hint is silence. See "Speech" in src/session.ts.
  • capture(action) is Guidepup's own, with the sentinel spoken first. It caught a focus move that a 100 ms poll of last phrase had missed.
  • type is not Guidepup's. Guidepup's type sends one character at a time and waits out the speech after each, about five seconds a key. The session sends every key from one AppleScript, delay apart, inside a capture, so the keys arrive at the asked pace. It answers with the last phrase only: VoiceOver keeps no other, so the speech for earlier keys is gone by the time the capture reads it.

What real runs have established

  • The VoiceOver cursor follows keyboard focus that page moves (locator.focus()), 5 of 5, and one next() continues from there. It does not follow a click.
  • A page-driven announcement (1 row selected...) is heard by capture(), 9 of 9 across two runs, in about 5 seconds each.
  • A Control press after the sentinel made VoiceOver drop the next announcement (2026-09-26, Playwright's WebKit): the first run of the refactored session recorded every page action and typed character as silence. The sentinel is now output alone. See "Speech" in src/session.ts.
  • readTableColumnHeader (VO-C) has answered with VoiceOver's own startup greeting in 3 of 11 uses, and once moved the cursor out of the table. The cause is unknown. Keep it out of anything whose later steps depend on where the cursor is.
  • moveToNext does not stop at a row's last cell: it wraps to the first cell of the next row.
  • An entry costs 15 to 25 seconds with whole-announcement capture, against 9 to 12 with first-phrase capture.