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

macos-events

v0.0.1

Published

Native macOS event streams and automation primitives for Node.js

Downloads

19

Readme

macos-events

Native macOS desktop automation primitives for TypeScript and JavaScript.

Use macos-events to build window managers, desktop controllers, hotkey/input tools, status agents, and other local automation software in JS runtimes.

The project is currently macOS-only and early-stage. It exposes window and display discovery, Accessibility-based window control, permission checks, and native event streams for windows, global input, apps, displays, spaces, sleep/wake, and session lock state.

Requirements

  • macOS 12+
  • Node.js 18.17+
  • Xcode Command Line Tools
  • Accessibility permission for window control and Accessibility window events
  • Input Monitoring permission for global mouse, keyboard, scroll, and tablet events

Install / Build

npm install

The install script builds src/native/addon.mm into build/Release/macos_events.node with node-gyp.

Useful scripts:

npm run build          # build native addon and compiled JavaScript
npm run typecheck      # type-check TypeScript sources
npm test               # run the smoke test
npm run playground     # inspect windows/screens and stream events

Usage

import * as wm from "macos-events";

// if the process doesn't have accessibilityPermission, then request it
if (!wm.hasAccessibilityPermission()) wm.requestAccessibilityPermission();

// get the focused window, then set the size of it
const focused = wm.getFocusedWindow();
if (focused) {
  wm.setWindowFrame(focused.id, { x: 0, y: 0, width: 960, height: 1080 });
}

// logs the name of the window when it's focused
wm.addEventListener("window-focused", (event) => {
  console.log(event.window.appName, event.window.title);
});

Permissions

macOS treats window automation and global input observation as separate privacy domains.

| Permission | Used for | Backing API | | ---------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | Accessibility | getFocusedWindow(), focusWindow(), setWindowFrame(), and window events | Accessibility API via AXUIElement | | Input Monitoring | Mouse, keyboard, scroll, and tablet events | Quartz Event Services listen-only event taps |

requestAccessibilityPermission() opens the system Accessibility prompt by calling AXIsProcessTrustedWithOptions. requestInputMonitoringPermission() opens the Input Monitoring prompt through CGRequestListenEventAccess. In both cases, macOS grants permission to the terminal or app hosting the process, not to this package directly.

API

hasAccessibilityPermission(): boolean
requestAccessibilityPermission(): boolean
hasInputMonitoringPermission(): boolean
requestInputMonitoringPermission(): boolean

listScreens(): ScreenInfo[]
listWindows(): WindowInfo[]
getFocusedWindow(): WindowInfo | null
focusWindow(id: number): boolean
setWindowFrame(id: number, frame: Rect): boolean

addEventListener<T extends EventType>(type: T, listener: EventListener<T>): () => void
removeEventListener<T extends EventType>(type: T, listener: EventListener<T>): void

Windows are identified by their CoreGraphics window ID. listWindows() returns visible, non-desktop windows. setWindowFrame() requires a positive width and height. Window control methods throw if the target window cannot be found or macOS rejects the Accessibility operation.

addEventListener() starts the relevant native event stream on first subscription and stops it when the last listener for that stream is removed. It returns an unsubscribe function.

Events

Window events require Accessibility permission.

| Event | Description | | ---------------------- | ---------------------------------------- | | window-created | A window was created by an observed app. | | window-destroyed | A window was closed or destroyed. | | window-moved | A window position changed. | | window-resized | A window size changed. | | window-minimized | A window was minimized. | | window-restored | A minimized window was restored. | | window-focused | The focused window changed. | | window-title-changed | A window title changed. |

Input events require Input Monitoring permission and are delivered through a listen-only CGEventTapCreate event tap. The addon observes input; it does not intercept or mutate it.

| Event | Description | | --------------------- | ------------------------------------------------------ | | mouse-down | Any mouse button was pressed. | | mouse-up | Any mouse button was released. | | mouse-moved | The pointer moved without a button drag. | | mouse-dragged | The pointer moved while a button was held. | | left-mouse-down | The left mouse button was pressed. | | left-mouse-up | The left mouse button was released. | | left-mouse-dragged | The pointer moved while the left button was held. | | right-mouse-down | The right mouse button was pressed. | | right-mouse-up | The right mouse button was released. | | right-mouse-dragged | The pointer moved while the right button was held. | | other-mouse-down | A non-left/right mouse button was pressed. | | other-mouse-up | A non-left/right mouse button was released. | | other-mouse-dragged | The pointer moved while another mouse button was held. | | scroll | A scroll wheel or trackpad scroll occurred. | | key-down | A keyboard key was pressed. | | key-up | A keyboard key was released. | | flags-changed | Modifier key state changed. | | tablet-point | Tablet pointer data changed. | | tablet-proximity | Tablet proximity state changed. |

System events do not use either explicit prompt helper in this package.

| Event | Description | | ----------------------- | -------------------------------------- | | app-launched | An app started. | | app-terminated | An app exited. | | app-activated | An app became active. | | app-deactivated | An app stopped being active. | | app-hidden | An app was hidden. | | app-unhidden | A hidden app was shown. | | display-added | A display was connected. | | display-removed | A display was disconnected. | | display-moved | A display frame origin changed. | | display-resized | A display mode or size changed. | | display-enabled | A display was enabled. | | display-disabled | A display was disabled. | | display-reconfigured | A display changed in any reported way. | | active-space-changed | The active macOS Space changed. | | screens-did-sleep | Displays went to sleep. | | screens-did-wake | Displays woke from sleep. | | system-will-sleep | The system is about to sleep. | | system-did-wake | The system woke from sleep. | | system-will-power-off | The system is about to power off. | | session-did-lock | The user session locked. | | session-did-unlock | The user session unlocked. |

Event Shapes

wm.addEventListener("window-focused", (event) => {
  console.log(event.window.id, event.window.appName, event.window.title);
});

wm.addEventListener("key-down", (event) => {
  console.log(event.keyCode, event.flags, event.repeat);
});

wm.addEventListener("display-reconfigured", (event) => {
  console.log(event.displayId, event.flags);
});

Window events include a window snapshot. Input events share one shape across mouse, keyboard, scroll, and tablet events. System events include app, displayId, or flags when relevant.

Playground

npm run playground

The playground prints the current Accessibility trust state, screens, focused window, and visible windows, then streams native window, input, and system events until stopped with Ctrl+C.

macOS Notes

Window enumeration uses public CoreGraphics APIs. Window control uses Accessibility APIs and maps Accessibility windows to CoreGraphics IDs with _AXUIElementGetWindow, a private macOS symbol commonly used by window-management tools. This is practical for local automation and window-manager tooling, but it is not App Store-safe.

Window events use native Accessibility observers rather than polling. System events use NSWorkspace notifications, distributed lock/unlock notifications, and CoreGraphics display reconfiguration callbacks.

Roadmap

I'd be cool to, at some point, expand this package to Linux and Windows, but currently macOS is the priority

TODO

  • [ ] Fix deprecated warnings
  • [ ] publish to npm + edit the readme to include npm install commands