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 🙏

© 2025 – Pkg Stats / Ryan Hefner

comapeocat

v1.1.0

Published

A JavaScript library for reading and writing CoMapeo Categories archives (`.comapeocat`). These files package category definitions, custom fields, icons, and translations for use in [CoMapeo](https://www.comapeo.app) applications.

Readme

CoMapeo Categories File Utilities comapeocat

npm version Build Status

A JavaScript library for reading and writing CoMapeo Categories files (.comapeocat). These files package category definitions, custom fields, icons, and translations for use in CoMapeo applications.

Features

  • Read .comapeocat files and access categories, fields, icons, and translations
  • Write .comapeocat files with full validation
  • Validate category files against the specification
  • CLI tool for creating and linting category files

Contents

Quick Start

Build a .comapeocat file from a directory of JSON files and icons:

npx comapeocat build --output mycategories.comapeocat

Extract messages for translation:

npx comapeocat messages --output messages/en.json

CLI Usage

Installation

Install the CLI globally for offline-use and convenience (you can always use npx comapeocat without installing):

npm install -g comapeocat

Commands

npx comapeocat build [inputDir]

Build a .comapeocat file from a directory containing JSON files and icons.

Arguments:

  • [inputDir] - Directory containing categories, fields, categorySelection, icons, and messages (default: current directory)

Options:

  • -o, --output <file> - Output file path (default: stdout)
  • --name <name> - Name of the category set (overrides metadata.json)
  • --version <version> - Version of the category set (overrides metadata.json)
  • --addCategoryIdTags - Add a categoryId tag to each category's addTags property

Directory structure:

inputDir/
├── categories/
│   ├── tree.json
│   └── river.json
├── fields/
│   ├── species.json
│   └── height.json
├── icons/
│   ├── tree.svg
│   └── river.svg
├── messages/
│   ├── en.json
│   └── es.json
├── categorySelection.json
└── metadata.json

Example:

# Build from current directory to stdout
npx comapeocat build

# Build to a specific file
npx comapeocat build ./my_categories --output output.comapeocat

# Override metadata
npx comapeocat build --name "My Categories" --version "1.0.0" --output output.comapeocat

# Add categoryId tags to categories
npx comapeocat build --addCategoryIdTags --output output.comapeocat

npx comapeocat lint [inputDir]

Lint category and field JSON files to validate against schemas and check references.

Arguments:

  • [inputDir] - Directory containing categories and fields (default: current directory)

Example:

# Lint files in current directory
npx comapeocat lint

npx comapeocat validate [file]

Validate a .comapeocat archive file.

Arguments:

  • [file] - Path to the .comapeocat file (required)

Example:

# Validate a specific .comapeocat file
npx comapeocat validate mycategories.comapeocat

npx comapeocat messages [inputDir]

Extract translatable messages from categories and fields for a given language.

Arguments:

  • [inputDir] - Directory containing categories and fields (default: current directory)

Options:

  • -o, --output <file> - Output file path (default: stdout)

Example:

comapeocat messages --output messages/en.json

This creates a messages/<lang>.json file with all translatable strings extracted from categories and fields. The filename should be a valid BCP 47 language code (e.g., en, es-PE, fr) and the file should be placed in a messages subdirectory of the input directory to be picked up by the build command.

VSCode settings for JSON Schema Validation

To enable JSON schema validation in VSCode for the various JSON files used in a .comapeocat project, add the following to your workspace settings (.vscode/settings.json):

{
	"json.schemas": [
		{
			"fileMatch": ["categories/**/*.json", "presets/**/*.json"],
			"url": "./node_modules/comapeocat/dist/schema/category.json"
		},
		{
			"fileMatch": ["fields/**/*.json"],
			"url": "./node_modules/comapeocat/dist/schema/field.json"
		},
		{
			"fileMatch": ["messages/*.json"],
			"url": "./node_modules/comapeocat/dist/schema/messages.json"
		},
		{
			"fileMatch": ["categorySelection.json"],
			"url": "./node_modules/comapeocat/dist/schema/categorySelection.json"
		},
		{
			"fileMatch": ["metadata.json"],
			"url": "./node_modules/comapeocat/dist/schema/metadata.json"
		}
	]
}

API Reference

Installation

npm install comapeocat

Quick Start

Reading a Categories File

import { Reader } from 'comapeocat'

const reader = new Reader('path/to/categories.comapeocat')

// Wait for the file to be opened
await reader.opened()

// Read categories
const categories = await reader.categories()
for (const [id, category] of categories) {
	console.log(id, category.name, category.appliesTo)
}

// Read fields
const fields = await reader.fields()
const species = fields.get('species')
console.log(species.label, species.type)

// Read icons
for await (const { name, iconXml } of reader.icons()) {
	console.log(name, iconXml)
}

// Read translations
for await (const { lang, translations } of reader.translations()) {
	console.log(lang, translations)
}

// Validate the file
await reader.validate()

// Always close when done
await reader.close()

Writing a Categories File

import { Writer } from 'comapeocat'
import { createWriteStream } from 'fs'
import { pipeline } from 'stream/promises'

const writer = new Writer()

// Add categories
writer.addCategory('tree', {
	name: 'Tree',
	appliesTo: ['observation'],
	tags: { natural: 'tree' },
	fields: ['species', 'height'],
	icon: 'tree',
	color: '#228B22',
})

// Add fields
writer.addField('species', {
	type: 'text',
	tagKey: 'species',
	label: 'Species',
	appearance: 'singleline',
})

writer.addField('height', {
	type: 'number',
	tagKey: 'height',
	label: 'Height (meters)',
})

// Add icons (async)
await writer.addIcon('tree', '<svg>...</svg>')

// Add translations (async)
await writer.addTranslations('es', {
	category: {
		tree: { name: 'Árbol' },
	},
	field: {
		species: { label: 'Especie' },
	},
})

// Set category selection
writer.setCategorySelection({
	observation: ['tree'],
	track: [],
})

// Set metadata
writer.setMetadata({
	name: 'Forest Monitoring',
	version: '1.0.0',
	builderName: 'comapeocat',
	builderVersion: '1.0.0',
})

// Finalize and write
writer.finish()
await pipeline(writer.outputStream, createWriteStream('output.comapeocat'))

Reader

new Reader(filepath)

Creates a new reader for a .comapeocat file.

  • filepath: string | ZipFile - Path to the file or an open yauzl ZipFile instance

async reader.opened()

Returns a promise that resolves when the file has been successfully opened and validated.

async reader.categories()

Returns a Promise<Map<string, Category>> of all categories in the file.

async reader.fields()

Returns a Promise<Map<string, Field>> of all fields in the file.

async reader.categorySelection()

Returns a Promise<CategorySelection> - the category selection object mapping document types to category IDs.

async reader.metadata()

Returns a Promise<Metadata> - the metadata object containing:

  • name - Human-readable name for the category set
  • version - Version identifier (optional)
  • buildDateValue - Build timestamp in milliseconds
  • builderName - Name of the tool used to build the archive (optional)
  • builderVersion - Version of the tool used to build the archive (optional)

reader.supportedFileVersion()

Returns the supported file version string (e.g., "1.0").

  • Returns: string - The supported file version
const reader = new Reader('path/to/categories.comapeocat')
const supportedVersion = reader.supportedFileVersion()
console.log('Supported version:', supportedVersion) // "1.0"

async reader.fileVersion()

Returns the actual file version string from the archive.

  • Returns: Promise<string> - The file version (e.g., "1.0", "1.5")
const reader = new Reader('path/to/categories.comapeocat')
await reader.opened()
const fileVersion = await reader.fileVersion()
console.log('File version:', fileVersion)

async reader.iconNames()

Returns a Promise<Set<string>> of all icon names (without .svg extension).

async reader.getIcon(iconId)

Returns the SVG XML content of an icon by its ID, or null if the icon doesn't exist.

  • iconId: string - Icon ID (without .svg extension)
  • Returns: Promise<string | null> - SVG XML content or null
const iconXml = await reader.getIcon('tree')
if (iconXml) {
	console.log('Icon found:', iconXml)
}

async *reader.icons()

Returns an async generator that yields { name, iconXml } objects.

for await (const { name, iconXml } of reader.icons()) {
	// Process each icon
}

async *reader.translations()

Returns an async generator that yields { lang, translations } objects.

for await (const { lang, translations } of reader.translations()) {
	// Process each translation
}

async reader.validate()

Validates the file structure and all references. Throws errors if validation fails.

async reader.close()

Closes the underlying zip file.

Writer

new Writer(options?)

Creates a new writer.

  • options.highWaterMark: number - Stream high water mark (default: 1MB)

writer.addCategory(id, category) (synchronous)

Adds a category definition. Throws if called after finish().

writer.addField(id, field) (synchronous)

Adds a field definition. Throws if called after finish().

async writer.addIcon(id, svg)

Adds an SVG icon. Returns a promise that resolves when the icon is added. Throws if SVG is invalid or if called after finish().

async writer.addTranslations(lang, translations)

Adds translations for a language. Returns a promise that resolves when translations are added. Throws if called after finish().

writer.setCategorySelection(categorySelection) (synchronous)

Sets the category selection object mapping document types to category IDs. Throws if called after finish().

writer.setMetadata(metadata) (synchronous)

Sets the metadata (required). Throws if called after finish().

  • metadata.name: string (required) - Human-readable name for the category set (max 100 characters)
  • metadata.version: string (optional) - Version identifier (max 20 characters)
  • metadata.builderName: string (optional) - Name of the tool used to build the archive (max 100 characters)
  • metadata.builderVersion: string (optional) - Version of the tool (max 20 characters)

writer.finish() (synchronous)

Finalizes the archive. Must be called before reading from outputStream. Validates all references and throws if validation fails. After calling this, no more data can be added.

writer.outputStream (property)

Readable stream containing the .comapeocat file data. Only readable after calling finish().

File Format Specification

The .comapeocat file format is a ZIP archive containing JSON configuration files and SVG icons. See the full specification for details.

Required Files

  • VERSION - Format version (e.g., "1.0")
  • categories.json - Category definitions
  • categorySelection.json - Category selection for each document type
  • metadata.json - Package metadata

Optional Files

  • fields.json - Field definitions
  • icons/*.svg - Icon files
  • translations/*.json - Translation files

Validation

Both Reader and Writer perform comprehensive validation:

  • Schema validation - All JSON files are validated against their schemas
  • Reference validation - Field and icon references are checked
  • Version validation - File format version is checked
  • SVG validation - Icon files are validated and sanitized

Error messages include file names and specific issues to help debug problems.

License

MIT

Contributing

Issues and pull requests welcome at github.com/digidem/comapeocat.