Catalog npm
Back to packageRELEASE DETAILS

0.1.0

npm

@008h/oxs

Read, write and validate Open Cross Stitch (.OXS) files

cross-stitchembroideryoxs
Published10 Oct 2026
Direct dependencies5
Transitive dependenciesNot calculated

README

Current package documentation

oxs

Read, write and validate Open Cross Stitch (.oxs) files. Pure TypeScript, ESM, no DOM or Node-specific APIs.

Install

npm install @008h/oxs

Usage

Read, validate, write

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.

Create a chart

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.

Inspect

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

Edit as a grid

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);

Validation

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:

CodeSeverityMeaning
OXS003_OUT_OF_BOUNDSinfoa stitch or ornament lies outside the chart size
OXS004_UNKNOWN_PALINDEXwarna stitch references a palette index that does not exist
OXS007_FOREIGN_ELEMENTinfoan unknown element was kept verbatim
OXS009_CLOTH_INDEX_MISSINGinfothe 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.

Errors

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;
}

Preservation

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.

Coordinates

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.

Disclaimer

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.

License

MIT

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Declared dependencies 5

Native requirements
5 direct targetsNot calculated transitive count
Package / targetRequirementRelationship
fast-xml-parser
Environment
{
  "local_name": "fast-xml-parser",
  "alias": false,
  "peer_metadata": null
}
^4.5.0runtime
vite
Environment
{
  "local_name": "vite",
  "alias": false,
  "peer_metadata": null
}
^7.3.1development
vitest
Environment
{
  "local_name": "vitest",
  "alias": false,
  "peer_metadata": null
}
^4.1.9development
fast-check
Environment
{
  "local_name": "fast-check",
  "alias": false,
  "peer_metadata": null
}
^3.23.0development
typescript
Environment
{
  "local_name": "typescript",
  "alias": false,
  "peer_metadata": null
}
^5.9.3development

Distribution files

1 collected
Distribution 1

Source distribution

Source file
Checksums from source
{
  "shasum": "e5c6b791900e4b9c0f1ad80bd450b2f0e0dfe4dc",
  "tarball": "https://registry.npmjs.org/@008h/oxs/-/oxs-0.1.0.tgz",
  "fileCount": 36,
  "integrity": "sha512-JO9FOz6oKMlSzSnQxzlKb5IWilDGTDIeCw+ivyIVZQmWM17yCSjiyK4eSnRcnskrTKdgsVxdUuLZ7ZuT9CIgqw==",
  "signatures": [
    {
      "sig": "MEUCIGSo+Of+EvwiX2t914mbIStRUFA1jn/1ira7erp45pirAiEAxB9mnoPWSvUNxLlBVsUy7HwWNh8kh/2TjHOD6l4Z1Pc=",
      "keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"
    },
    {
      "sig": "MEUCIAgUeWEpRpTHQqI8E4q5shv1/Fhcg6wBAsBxluh6kw5NAiEApuMrZNvCxT9XNmp4cF8UhJHYSA9P/CwCwnz77XIE4rI=",
      "keyid": "SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"
    }
  ],
  "unpackedSize": 88457
}

Repository

PENDING
008h/oxs

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 21:54 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package_version:79dccb9231c85eee72d1d395fb6ba57c8a9204909ccfc09d19084f568fbdb5e7

Other collected versions

0.1.110 Oct0.0.0-stage10 OctView all collected versions →