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.
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.
npm install @shayc/open-board-formatimport { 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.
Two file types; pick the entry point by what you have:
- OBF is a single board (a JSON object). Use
parseOBFfor a JSON string,validateOBFfor an already-parsed object,loadOBFfor a browserFile.stringifyOBFserializes back out. - OBZ is a package of boards plus media (a ZIP archive). Use
loadOBZfor aFile,extractOBZfor anArrayBuffer,createOBZto 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 parseOBF → stringifyOBF round trip.
- Module format: ESM only.
- Runtime: browser or Node 22+ — works against
File,ArrayBuffer, andBlob.
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");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);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);
}| 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 |
| 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 |
| 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 |
| 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 |
Every type above except ParsedOBZ is exported alongside a matching Zod schema with a Schema suffix — OBFBoard → OBFBoardSchema, OBFManifest → OBFManifestSchema, 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";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):rootBoardIdmatches no board, a board fails validation, two boards map the same media id to conflicting paths, a declared image/soundpathhas no matching resource, or a resource would overwrite a generated entry.Invalid manifest:—manifest.jsonfailed 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.
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.
Semver; see CHANGELOG.md.
See CONTRIBUTING.md for development setup (Node 22+, Vitest, the changeset workflow).
- Open Board Format specification — the official standard and format documentation.
- AAC Board AI — an offline-first AAC web app built on this package, using on-device browser AI for grammar, tone, and translation (live app).
MIT © Shay Cojocaru