Observed since 20 Oct, 21:54 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
Read, write and validate Open Cross Stitch (.OXS) files
Read, write and validate Open Cross Stitch (.oxs) files.
Pure TypeScript, ESM, no DOM or Node-specific APIs.
npm install @008h/oxs
import { parseOxs, serializeOxs, validateOxs } from '@008h/oxs';
const doc = parseOxs(xmlString);
console.log(doc.properties.chartWidth, doc.properties.chartHeight);
for (const d of validateOxs(doc)) {
console.warn(d.severity, d.code, d.message);
}
const xml = serializeOxs(doc);
serializeOxs(doc, { sort: true }) orders stitches by x, y and palette index,
which gives stable output for diffs and golden files.
Documents are immutable: build a new one instead of changing it in place.
import { createDocument, serializeOxs } from '@008h/oxs';
const empty = createDocument({ width: 8, height: 8, title: 'Heart', author: 'me' });
const doc = {
...empty,
palette: [
...empty.palette,
{ index: 1, number: '321', name: 'Red', color: 'C93756', blends: [], extra: [] },
],
fullStitches: [
{ x: 3, y: 2, palindex: 1, extra: [] },
{ x: 4, y: 2, palindex: 1, extra: [] },
],
};
const xml = serializeOxs(doc);
palette[0] (index 0) is the cloth colour. createDocument adds it for you.
import { chartBounds, paletteUsage, paletteCount } from '@008h/oxs';
chartBounds(doc); // { minX, maxX, minY, maxY } of the stitches, or null if empty
paletteUsage(doc); // Set of palette indexes that are actually used
paletteCount(doc); // number of palette entries, not counting the cloth
toDense turns a document into maps keyed by cell, which is convenient for
editors. fromDense converts back against the original document, reusing
unchanged elements so that unknown attributes survive.
import { toDense, fromDense, cellKey } from '@008h/oxs';
const dense = toDense(doc);
const edited = {
...dense,
fullStitches: new Map(dense.fullStitches).set(cellKey(5, 5), 1),
};
const next = fromDense(edited, doc);
validateOxs does not throw on odd input. It returns diagnostics, each with a
severity (error, warn or info), a stable code, a message and
usually a path. It currently reports:
| Code | Severity | Meaning |
|---|---|---|
OXS003_OUT_OF_BOUNDS | info | a stitch or ornament lies outside the chart size |
OXS004_UNKNOWN_PALINDEX | warn | a stitch references a palette index that does not exist |
OXS007_FOREIGN_ELEMENT | info | an unknown element was kept verbatim |
OXS009_CLOTH_INDEX_MISSING | info | the palette has no entry at index 0 (cloth) |
Out-of-bounds coordinates are legal in OXS, so they are reported as info,
never error. The DiagnosticCode type lists a few more codes that are
reserved for later and not reported yet.
Malformed XML makes parseOxs (and readXml) throw XmlParseError:
import { parseOxs, XmlParseError } from '@008h/oxs';
try {
parseOxs(text);
} catch (e) {
if (e instanceof XmlParseError) console.error('not valid XML:', e.message);
else throw e;
}
Unknown attributes are kept in extra and unknown elements in foreign, so a
parse → serialize round trip does not drop data written by other software.
The OXS specification does not define the coordinate origin. Based on
measurements of real-world files, this library treats coordinates as
0-based and keeps them exactly as written in the file: parsing and
serializing never shift values. Out-of-bounds coordinates are therefore legal
in the IR and reported by validateOxs instead. Use detectOrigin,
toZeroBased and fromZeroBased to interpret a file's origin.
Details and evidence: docs/OXS-DIALECT.md.
OXS is a file format published by Ursa Software. This project is an independent implementation and is not affiliated with or endorsed by Ursa Software.
MIT
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 0.1.1Registry latest | 10 Oct 2026 | 5 | |
| 0.1.0 | 10 Oct 2026 | 5 | |
| 0.0.0-stage | 10 Oct 2026 | 0 |
This describes our collected data; it is not a security assessment.
Observed since 20 Oct, 21:54 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:67bcd4fcc8090f8e89829bde1f2203e57f86e9208aa0e4ac96582f3ada5fdd17