Skip to content

iamEvanYT/objcjs-extra

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

objcjs-extra

Hand-written FFI bindings for macOS C frameworks that supplement objc-js and objcjs-types.

objc-js auto-generates bindings for Objective-C classes, protocols, and enums. Pure-C frameworks like CoreFoundation, CoreGraphics, and the Accessibility API have no ObjC metadata to generate from, so this package provides them manually.

Bun & Node.js — This package works with both Bun (bun:ffi) and Node.js (koffi). The runtime is detected automatically at import time.

Install

Bun:

bun add objcjs-extra objc-js objcjs-types

Node.js:

npm install objcjs-extra objc-js objcjs-types koffi

On Node.js, the koffi package (>= 2.0) is required for FFI support. It is listed as an optional dependency and must be installed explicitly.

Runtime Architecture

All framework modules import from a unified FFI abstraction layer (src/modules/ffi.ts) instead of bun:ffi directly. This module:

  • Detects the runtime (globalThis.Bun) and selects the appropriate backend
  • On Bun, delegates to bun:ffi (dlopen, ptr, read, JSCallback)
  • On Node.js, delegates to koffi (koffi.load, koffi.decode, koffi.register)
  • Exports a unified API: dlopen, ptr, read, FFIType, JSCallback, createCallback

Framework bindings are portable across both runtimes with no code changes needed.

Frameworks

Each framework is a separate entry point, loaded lazily on first use.

Framework Description
CoreFoundation CF memory management, CFString, CFData, CFRunLoop, CFMachPort
CoreGraphics Quartz Event API, window list/capture, display management, bitmap contexts, color spaces
ApplicationServices AXUIElement accessibility API, AXObserver, AXValue
Security Keychain Services, Authorization, code signing, certificates, SecRandom
CoreServices FSEvents (file watching), Launch Services (open apps/URLs)
IOKit Service matching, registry properties, power assertions (prevent sleep)
CoreText Font creation/metrics, font collections, descriptors, line layout
ImageIO CGImageSource (read), CGImageDestination (write), format queries, EXIF metadata
CoreAudio Audio device enumeration, volume/mute control, sample rate, HAL properties
Network Endpoints, parameters, connections, listeners, dispatch queues
CoreMedia CMTime (pure JS arithmetic), CMSampleBuffer, CMFormatDescription
Accelerate vDSP (float/double vector ops), CBLAS (matrix multiply, norms)

CoreFoundation

Memory management, CFString, CFData, CFRunLoop, and CFMachPort.

import {
  CFStringCreateWithJSString,
  CFStringGetJSString,
  CFRelease,
  CFRunLoopGetCurrent,
  CFRunLoopRun
} from "objcjs-extra/CoreFoundation";

CoreGraphics

Synthetic mouse, keyboard, and scroll-wheel events via the Quartz Event API. Also includes window list/capture, display management, bitmap contexts, and color spaces.

import {
  CGEventCreateKeyboardEvent,
  CGEventPost,
  CGEventTapLocation,
  CGEventSourceCreate,
  CGEventSourceStateID
} from "objcjs-extra/CoreGraphics";

const source = CGEventSourceCreate(CGEventSourceStateID.CombinedSessionState);
const keyDown = CGEventCreateKeyboardEvent(source, 0x00, true); // 'a' key
CGEventPost(CGEventTapLocation.SessionEventTap, keyDown);

ApplicationServices

macOS Accessibility API (AXUIElement) for UI inspection and automation.

import {
  AXIsProcessTrusted,
  AXUIElementCreateApplication,
  AXUIElementCopyAttributeNames,
  AXUIElementCopyAttributeString,
  AXUIElementPerformAction,
  kAXTitleAttribute,
  kAXPressAction,
  kAXFocusedWindowAttribute
} from "objcjs-extra/ApplicationServices";

if (!AXIsProcessTrusted()) {
  console.error("Enable Accessibility in System Settings > Privacy & Security");
  process.exit(1);
}

const app = AXUIElementCreateApplication(pid);
const title = AXUIElementCopyAttributeString(app, kAXTitleAttribute);
console.log("App title:", title);

Includes typed convenience helpers (AXUIElementCopyAttributeString, AXUIElementCopyAttributePoint, etc.), AXValue boxing/unboxing for CGPoint/CGSize/CGRect/CFRange, and AXObserver for watching accessibility notifications.

Security

Keychain Services, Authorization, code signing, certificates, and secure random number generation.

import {
  SecItemAdd,
  SecItemCopyMatching,
  SecItemUpdate,
  SecItemDelete,
  SecRandomBytes,
  SecCodeCopySelf,
  SecCodeCheckValidity,
  errSecSuccess,
  errSecItemNotFound
} from "objcjs-extra/Security";

// Generate 32 bytes of cryptographically secure random data
const randomData = SecRandomBytes(32);

CoreServices

FSEvents for file system monitoring and Launch Services for opening apps/URLs.

import {
  createFSEventStream,
  FSEventStreamStart,
  FSEventStreamStop,
  FSEventStreamRelease,
  getApplicationURLsForBundleId,
  kFSEventStreamCreateFlagFileEvents,
  kFSEventStreamCreateFlagNoDefer
} from "objcjs-extra/CoreServices";

// Watch a directory for file-level changes
const stream = createFSEventStream(
  ["/path/to/watch"],
  (paths, flags, ids) => {
    console.log("Changed:", paths);
  },
  kFSEventStreamCreateFlagFileEvents | kFSEventStreamCreateFlagNoDefer
);

// Find applications by bundle identifier
const urls = getApplicationURLsForBundleId("com.apple.Safari");

IOKit

Service matching, registry property queries, and power management assertions.

import {
  IOServiceMatching,
  IOServiceGetMatchingService,
  IORegistryEntryCreateCFProperty,
  IOObjectRelease,
  preventSleep,
  preventDisplaySleep,
  kIOMasterPortDefault
} from "objcjs-extra/IOKit";

// Prevent the system from sleeping
const assertion = preventSleep("Long-running operation");
// ... do work ...
assertion.release();

// Prevent just the display from sleeping
const displayAssertion = preventDisplaySleep("Presentation mode");
// ... do work ...
displayAssertion.release();

CoreText

Font creation, metrics, descriptors, font collections, and line layout.

import {
  CTFontCreateWithName,
  CTFontCopyFamilyName,
  CTFontGetSize,
  CTFontGetAscent,
  CTFontGetDescent,
  CTFontGetLeading,
  getAvailableFontFamilies
} from "objcjs-extra/CoreText";

// List all available font families on the system
const families = getAvailableFontFamilies();
console.log("Available fonts:", families.length);

ImageIO

Read and write image data in various formats (JPEG, PNG, TIFF, HEIF, etc.) via CGImageSource and CGImageDestination.

import {
  CGImageSourceCreateWithData,
  CGImageSourceGetCount,
  CGImageSourceGetStatus,
  CGImageSourceCopyPropertiesAtIndex,
  CGImageDestinationCreateWithData,
  CGImageDestinationAddImage,
  CGImageDestinationFinalize,
  CGImageSourceStatus
} from "objcjs-extra/ImageIO";

CoreAudio

Audio Hardware Abstraction Layer (HAL) for device enumeration, volume control, mute state, and property listeners.

import {
  getDefaultOutputDevice,
  getDefaultInputDevice,
  getAudioDevices,
  getDeviceName,
  getDeviceVolume,
  setDeviceVolume,
  getDeviceMute,
  setDeviceMute
} from "objcjs-extra/CoreAudio";

// Get the default output device and its volume
const device = getDefaultOutputDevice();
const name = getDeviceName(device);
const volume = getDeviceVolume(device);
console.log(`${name}: volume ${(volume * 100).toFixed(0)}%`);

// Set volume to 50%
setDeviceVolume(device, 0.5);

Network

macOS Network.framework for endpoint, parameter, connection, and listener management.

import {
  nwEndpointCreateHost,
  nwEndpointGetHostname,
  nwEndpointGetPort,
  nwParametersCreateSecureTCP,
  nwConnectionCreate,
  nwConnectionStart,
  nwListenerCreateWithPort,
  nwListenerStart,
  nwRelease
} from "objcjs-extra/Network";

Note: Many Network.framework APIs use Objective-C block-based callbacks (e.g. state change handlers, send/receive). These blocks are not simple C function pointers, so JSCallback cannot be used for them. For full async Network usage, consider using an ObjC bridge or Swift helper library.

CoreMedia

CMTime arithmetic (pure JS), CMSampleBuffer, and CMFormatDescription.

import {
  CMTimeMake,
  CMTimeMakeWithSeconds,
  CMTimeGetSeconds,
  CMTimeAdd,
  CMTimeSubtract,
  CMTimeCompare,
  CMTimeIsValid,
  CMSampleBufferGetNumSamples,
  CMFormatDescriptionGetMediaType,
  fourCCToString,
  kCMTimeZero
} from "objcjs-extra/CoreMedia";

// CMTime is implemented in pure JS — no FFI overhead
const time1 = CMTimeMake(3000, 600); // 5.0 seconds
const time2 = CMTimeMakeWithSeconds(2.5, 600);
const sum = CMTimeAdd(time1, time2);
console.log("Total:", CMTimeGetSeconds(sum), "seconds");

Note: CMTime is a 24-byte value type. Because FFI cannot return structs by value, the CMTime API is implemented in pure JavaScript with buffer serialization helpers for native interop.

Accelerate

High-performance vector and matrix operations via vDSP and CBLAS.

import {
  vaddD,
  vsubD,
  vmulD,
  vdivD,
  dotProductD,
  meanD,
  normalizeD,
  vaddF,
  vmulF,
  dgemm,
  dnrm2,
  CblasRowMajor,
  CblasNoTrans
} from "objcjs-extra/Accelerate";

// Vector addition (double-precision)
const a = new Float64Array([1.0, 2.0, 3.0]);
const b = new Float64Array([4.0, 5.0, 6.0]);
const sum = vaddD(a, b); // Float64Array [5.0, 7.0, 9.0]

// Dot product
const dot = dotProductD(a, b); // 32.0

// Matrix multiply (CBLAS dgemm)
const A = new Float64Array([1, 2, 3, 4, 5, 6]); // 2x3
const B = new Float64Array([7, 8, 9, 10, 11, 12]); // 3x2
const C = dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, 2, 2, 3, 1.0, A, B, 0.0);

Usage with objc-js

objcjs-extra is designed to work alongside objc-js. Use objc-js for Objective-C APIs (NSWorkspace, NSRunningApplication, etc.) and objcjs-extra for the underlying C APIs:

import { objc } from "objc-js";
import {
  CFRunLoopGetCurrent,
  CFRunLoopAddSource,
  CFRunLoopRun,
  kCFRunLoopDefaultMode
} from "objcjs-extra/CoreFoundation";
import {
  AXObserverCreate,
  AXObserverGetRunLoopSource,
  AXObserverAddNotification,
  AXUIElementCreateApplication,
  kAXFocusedWindowChangedNotification
} from "objcjs-extra/ApplicationServices";

const workspace = objc.classes.NSWorkspace.sharedWorkspace();
const frontApp = workspace.frontmostApplication();
const pid = frontApp.processIdentifier();

const observer = AXObserverCreate(pid, (obs, el, notification) => {
  console.log("Notification:", notification);
});

const appElement = AXUIElementCreateApplication(pid);
AXObserverAddNotification(observer, appElement, kAXFocusedWindowChangedNotification);

const source = AXObserverGetRunLoopSource(observer);
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
CFRunLoopRun();

Building

bun run build   # or: npx tsc

About

Hand-written FFI bindings for macOS C frameworks that supplement objc-js and objcjs-types

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors