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

@shayc/open-board-format

v0.1.7

Published

A TypeScript toolkit for Open Board Format — the open standard for Augmentative and Alternative Communication (AAC) boards.

Readme

@shayc/open-board-format

A TypeScript toolkit for Open Board Format — the open standard for Augmentative and Alternative Communication (AAC) boards. Parse, validate, and create OBF boards and OBZ packages, all backed by Zod schemas with full TypeScript types inferred.

npm version CI License: MIT

OBF (.obf) is a JSON file describing a single communication board — buttons, images, sounds, grid layout, metadata. OBZ (.obz) is a ZIP archive bundling one or more .obf boards with their media and a manifest.json.

Install

npm install @shayc/open-board-format

Quick start

import { parseOBF } from "@shayc/open-board-format";

const board = parseOBF(jsonString);
console.log(board.id, board.buttons.length);

parseOBF throws on invalid input; the returned value is a fully typed OBFBoard. For other input shapes (already-parsed object, browser File, OBZ archive), see Overview.

Overview

Two file types; pick the entry point by what you have:

  • OBF is a single board (a JSON object). Use parseOBF for a JSON string, validateOBF for an already-parsed object, loadOBF for a browser File. stringifyOBF serializes back out.
  • OBZ is a package of boards plus media (a ZIP archive). Use loadOBZ for a File, extractOBZ for an ArrayBuffer, createOBZ to build a new one.

Every OBF type ships with a matching *Schema Zod schema (e.g. OBFBoardSchema, OBFManifestSchema), so you can validate inline with safeParse or wire the schema straight into an API contract — the TypeScript types are inferred from those schemas.

Validation preserves unknown fields rather than stripping them, so vendor extensions allowed by the OBF spec survive a parseOBFstringifyOBF round trip.

Requirements

  • Module format: ESM only.
  • Runtime: browser or Node 22+ — works against File, ArrayBuffer, and Blob.

Examples

Extract an OBZ package

import { loadOBZ, extractOBZ } from "@shayc/open-board-format";

// From a File (e.g. drag-and-drop)
const { manifest, boards, resources } = await loadOBZ(file);

// Or from an ArrayBuffer (e.g. fetch response)
const parsed = await extractOBZ(buffer);

const homeBoard = parsed.boards.get("1");
const imageBytes = parsed.resources.get("images/logo.png");

Create an OBZ package

import { createOBZ } from "@shayc/open-board-format";
import type { OBFBoard } from "@shayc/open-board-format";

const boards: OBFBoard[] = [
  {
    format: "open-board-0.1",
    id: "board-1",
    buttons: [{ id: "btn-1", label: "Hello" }],
    grid: { rows: 1, columns: 1, order: [["btn-1"]] },
  },
];

const pngBytes = new Uint8Array(/* ... */);
const resources = new Map([["images/logo.png", pngBytes]]);
const blob = await createOBZ(boards, "board-1", resources);

Validate with Zod directly

import { OBFBoardSchema } from "@shayc/open-board-format";

const result = OBFBoardSchema.safeParse(data);

if (result.success) {
  console.log(result.data.buttons);
} else {
  console.error(result.error.issues);
}

API

OBF (single board)

| Function | Description | | --------------------- | ------------------------------------------------------------ | | parseOBF(json) | Parse a JSON string into a validated OBFBoard | | validateOBF(data) | Validate an unknown object as OBFBoard (throws on failure) | | stringifyOBF(board) | Serialize an OBFBoard to a JSON string | | loadOBF(file) | Load an OBFBoard from a browser File |

OBZ (board package)

| Function | Description | | -------------------------------------------- | ------------------------------------------------------------- | | loadOBZ(file) | Load an OBZ package from a browser File | | extractOBZ(archive) | Extract boards, manifest, and resources from an ArrayBuffer | | createOBZ(boards, rootBoardId, resources?) | Create an OBZ package as a Blob | | parseManifest(json) | Parse a manifest.json string into a validated OBFManifest |

Utilities

| Function | Description | | ---------------- | -------------------------------------------------------- | | isZip(archive) | Check if an ArrayBuffer starts with a ZIP magic number | | zip(entries) | Create a ZIP from a map of paths to buffers | | unzip(archive) | Extract a ZIP into a map of paths to Uint8Array |

Types

| Type | Description | | --------------------- | --------------------------------------------------------------------------- | | OBFBoard | A single communication board | | OBFGrid | Grid layout (rows, columns, order) | | OBFButton | A button on the board | | OBFButtonAction | Button action (spelling or specialty) | | OBFSpellingAction | Spelling action (e.g., +s) | | OBFSpecialtyAction | Specialty action (e.g., :clear) | | OBFLoadBoard | Reference to load another board | | OBFMedia | Common media properties (base for OBFImage and OBFSound) | | OBFImage | An image resource (extends OBFMedia) | | OBFSound | A sound resource (extends OBFMedia) | | OBFSymbolInfo | Symbol set reference | | OBFManifest | OBZ package manifest | | ParsedOBZ | Return type of extractOBZ / loadOBZ{ manifest, boards, resources } | | OBFID | Unique identifier (string, coerced from number) | | OBFFormatVersion | Format version string (e.g., open-board-0.1) | | OBFLicense | Licensing information | | OBFLocaleCode | BCP 47 locale code | | OBFLocalizedStrings | Key-value string translations | | OBFStrings | Multi-locale string translations |

Schemas

Every type above except ParsedOBZ is exported alongside a matching Zod schema with a Schema suffix — OBFBoardOBFBoardSchema, OBFManifestOBFManifestSchema, and so on. Import any of them to validate with safeParse/parse or to compose into your own schemas:

import { OBFButtonSchema, OBFManifestSchema } from "@shayc/open-board-format";

Errors

All failures throw plain Error. The message identifies what failed, typically with one of these prefixes:

  • Invalid OBF: — schema validation rejected an OBF board.
  • Invalid OBZ: — the package was rejected. On read: not a ZIP, missing manifest, or the manifest references a board file not in the archive. On write (createOBZ): rootBoardId matches no board, a board fails validation, two boards map the same media id to conflicting paths, a declared image/sound path has no matching resource, or a resource would overwrite a generated entry.
  • Invalid manifest:manifest.json failed to parse or validate.

When the root cause is a JSON.parse failure, the original error is preserved as error.cause. For finer-grained validation, drop one level down and use the Zod schemas directly with safeParse — the issues array tells you exactly which field failed.

Security

OBZ archives are untrusted input. This library does not enforce limits on entry size or count, and does not sanitize entry paths — if you write extracted resources to disk, validate paths yourself first to avoid directory traversal. For stronger guarantees against zip-bomb-style payloads, run extraction in a sandboxed context (Web Worker, isolated process).

Found a security issue? Open a private advisory at github.com/shayc/open-board-format/security/advisories/new.

Versioning

Semver; see CHANGELOG.md.

Contributing

See CONTRIBUTING.md for development setup (Node 22+, Vitest, the changeset workflow).

Related

License

MIT © Shay Cojocaru