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.
- Read
.comapeocatfiles and access categories, fields, icons, and translations - Write
.comapeocatfiles with full validation - Validate category files against the specification
- CLI tool for creating and linting category files
- Quick Start
- CLI Usage
- VSCode settings for JSON Schema Validation
- API Reference
- Installation
- Quick Start
- Reader
- Writer
new Writer(options?)writer.addCategory(id, category)(synchronous)writer.addField(id, field)(synchronous)async writer.addIcon(id, svg)async writer.addTranslations(lang, translations)writer.setCategorySelection(categorySelection)(synchronous)writer.setMetadata(metadata)(synchronous)writer.finish()(synchronous)writer.outputStream(property)
- File Format Specification
- Validation
- License
- Contributing
Build a .comapeocat file from a directory of JSON files and icons:
npx comapeocat build --output mycategories.comapeocatExtract messages for translation:
npx comapeocat messages --output messages/en.jsonInstall the CLI globally for offline-use and convenience (you can always use npx comapeocat without installing):
npm install -g comapeocatBuild 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 acategoryIdtag to each category'saddTagsproperty
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.comapeocatLint 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 lintValidate a .comapeocat archive file.
Arguments:
[file]- Path to the.comapeocatfile (required)
Example:
# Validate a specific .comapeocat file
npx comapeocat validate mycategories.comapeocatExtract 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.jsonThis 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.
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"
}
]
}npm install comapeocatimport { 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()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'))Creates a new reader for a .comapeocat file.
- filepath:
string|ZipFile- Path to the file or an open yauzl ZipFile instance
Returns a promise that resolves when the file has been successfully opened and validated.
Returns a Promise<Map<string, Category>> of all categories in the file.
Returns a Promise<Map<string, Field>> of all fields in the file.
Returns a Promise<CategorySelection> - the category selection object mapping document types to category IDs.
Returns a Promise<Metadata> - the metadata object containing:
name- Human-readable name for the category setversion- Version identifier (optional)buildDateValue- Build timestamp in millisecondsbuilderName- Name of the tool used to build the archive (optional)builderVersion- Version of the tool used to build the archive (optional)
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"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)Returns a Promise<Set<string>> of all icon names (without .svg extension).
Returns the SVG XML content of an icon by its ID, or null if the icon doesn't exist.
- iconId:
string- Icon ID (without.svgextension) - Returns:
Promise<string | null>- SVG XML content or null
const iconXml = await reader.getIcon('tree')
if (iconXml) {
console.log('Icon found:', iconXml)
}Returns an async generator that yields { name, iconXml } objects.
for await (const { name, iconXml } of reader.icons()) {
// Process each icon
}Returns an async generator that yields { lang, translations } objects.
for await (const { lang, translations } of reader.translations()) {
// Process each translation
}Validates the file structure and all references. Throws errors if validation fails.
Closes the underlying zip file.
Creates a new writer.
- options.highWaterMark:
number- Stream high water mark (default: 1MB)
Adds a category definition. Throws if called after finish().
Adds a field definition. Throws if called after finish().
Adds an SVG icon. Returns a promise that resolves when the icon is added. Throws if SVG is invalid or if called after finish().
Adds translations for a language. Returns a promise that resolves when translations are added. Throws if called after finish().
Sets the category selection object mapping document types to category IDs. Throws if called after finish().
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)
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.
Readable stream containing the .comapeocat file data. Only readable after calling finish().
The .comapeocat file format is a ZIP archive containing JSON configuration files and SVG icons. See the full specification for details.
VERSION- Format version (e.g., "1.0")categories.json- Category definitionscategorySelection.json- Category selection for each document typemetadata.json- Package metadata
fields.json- Field definitionsicons/*.svg- Icon filestranslations/*.json- Translation files
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.
MIT
Issues and pull requests welcome at github.com/digidem/comapeocat.