{"_id":"cross-stitch","_rev":"14-260fe305ef5d619b428cad0205f549d5","name":"cross-stitch","dist-tags":{"latest":"2.0.0"},"versions":{"0.5.1":{"name":"cross-stitch","version":"0.5.1","keywords":[],"author":{"name":"neilcochran","email":"dev.neil.cochran@pm.me"},"license":"MIT","_id":"cross-stitch@0.5.1","maintainers":[{"name":"neilcochran","email":"dev.neil.cochran@pm.me"}],"dist":{"shasum":"58a9e0332c11e2dd58e83d7e1dddaf03925981dc","tarball":"https://registry.npmjs.org/cross-stitch/-/cross-stitch-0.5.1.tgz","fileCount":717,"integrity":"sha512-wrP5frtZ+GfvMdjZ+2IVt5uWWTK0ov4WMP5kt/p/lul+7Fz7EedmP0xozHCGbCdniSdu2K8vlAKQmzZxiw64pQ==","signatures":[{"sig":"MEYCIQC/fCOAU4xAFikAqEtsK5QoAYDaukX1Mzun1yILdO9RtQIhAJttr7HYnWta5yFggS/4mxhttEvZxkTUFVfraf2zk4az","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96610577},"main":"dist/index.js","types":"./dist/index.d.ts","gitHead":"20eb9a6b3ec11b288546fee2eaa78c2534f21332","scripts":{"dev":"tsc-watch -p tsconfig.json --onSuccess \"node ./dist/index.js\"","doc":"typedoc --includeVersion --out docs src/","lint":"eslint --fix --ext .ts \"src/**/*\"","test":"jest","build":"npm run lint && npm run prettier:write && tsc","start":"node dist/index.js","prettier:check":"prettier . --check","prettier:write":"prettier . --write"},"_npmUser":{"name":"neilcochran","email":"dev.neil.cochran@pm.me"},"_npmVersion":"10.8.1","description":"This repository defines a json schema to represent cross stitch patterns and provides tools to work with them.","directories":{},"_nodeVersion":"20.16.0","dependencies":{"deep-equal":"^2.2.3"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.3.1","eslint":"^8.31.0","ts-jest":"^29.0.3","ts-node":"^10.9.1","typedoc":"^0.23.24","prettier":"3.3.3","tsc-watch":"^6.0.0","typescript":"^4.9.4","@types/jest":"^29.2.5","@types/node":"^18.11.18","@types/deep-equal":"^1.0.4","eslint-plugin-tsdoc":"^0.2.17","eslint-config-prettier":"^9.1.0","@typescript-eslint/parser":"^5.48.1","@typescript-eslint/eslint-plugin":"^5.48.1"},"_npmOperationalInternal":{"tmp":"tmp/cross-stitch_0.5.1_1724177698121_0.8120991013779737","host":"s3://npm-registry-packages"}},"1.0.0":{"name":"cross-stitch","version":"1.0.0","keywords":[],"author":{"name":"neilcochran","email":"dev.neil.cochran@pm.me"},"license":"MIT","_id":"cross-stitch@1.0.0","maintainers":[{"name":"neilcochran","email":"dev.neil.cochran@pm.me"}],"dist":{"shasum":"8495e768648c25ab1670ecc2273be3f307ecd440","tarball":"https://registry.npmjs.org/cross-stitch/-/cross-stitch-1.0.0.tgz","fileCount":739,"integrity":"sha512-nmgcD3hBXXzpSV8030b0rlpaRC2cEQkYAQDH5CgWsQ7qEe0H30i2s6yAsJPA3lo60B1ZHrrSxNGUEzfzGDEWZw==","signatures":[{"sig":"MEQCIAdrj1bVYgZW7BFly7fZgslSCm09/bqjQohZgNQ+NoJ5AiB4giQTz7xKF9reWiKdE0dzoEYOmEKnuQpJ5nmPmggP5Q==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":96691697},"main":"dist/index.js","types":"./dist/index.d.ts","gitHead":"0747b6ea323c1588c23867688b5a61b1f7404597","scripts":{"dev":"tsc-watch -p tsconfig.json --onSuccess \"node ./dist/index.js\"","doc":"typedoc --includeVersion --out docs src/","lint":"eslint --fix --ext .ts \"src/**/*\"","test":"jest","build":"npm run lint && npm run prettier:write && tsc","start":"node dist/index.js","prettier:check":"prettier . --check","prettier:write":"prettier . --write"},"_npmUser":{"name":"neilcochran","email":"dev.neil.cochran@pm.me"},"_npmVersion":"10.8.1","description":"This repository defines a json schema to represent cross stitch patterns and provides tools to work with them.","directories":{},"_nodeVersion":"20.16.0","dependencies":{"deep-equal":"^2.2.3"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.3.1","eslint":"^8.31.0","ts-jest":"^29.0.3","ts-node":"^10.9.1","typedoc":"^0.23.24","prettier":"3.3.3","tsc-watch":"^6.0.0","typescript":"^4.9.4","@types/jest":"^29.2.5","@types/node":"^18.11.18","@types/deep-equal":"^1.0.4","eslint-plugin-tsdoc":"^0.2.17","eslint-config-prettier":"^9.1.0","@typescript-eslint/parser":"^5.48.1","@typescript-eslint/eslint-plugin":"^5.48.1"},"_npmOperationalInternal":{"tmp":"tmp/cross-stitch_1.0.0_1724189953910_0.6555958739841377","host":"s3://npm-registry-packages"}},"1.1.0":{"name":"cross-stitch","version":"1.1.0","keywords":[],"author":{"name":"neilcochran","email":"dev.neil.cochran@pm.me"},"license":"MIT","_id":"cross-stitch@1.1.0","maintainers":[{"name":"neilcochran","email":"dev.neil.cochran@pm.me"}],"dist":{"shasum":"2eff110803ba043215d02a1df9608ba1b40280d9","tarball":"https://registry.npmjs.org/cross-stitch/-/cross-stitch-1.1.0.tgz","fileCount":718,"integrity":"sha512-jzCoAZBk9XpP2a59QXFBCZPJ/uZY94sFk7vAV50/HcnEM18b8DY0t3CY2mGqgaHWmsMRElx/zzKPR2kbw1V3GQ==","signatures":[{"sig":"MEYCIQDbtK3oNfP/Uh6GzaN2vpPfqYSYi/KZ5t9GhKzMEoaSvwIhAJkZkWV3UurEl9/zkI6/HvUR9ApXr3QElmSV4BurZg8c","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":97973305},"main":"dist/index.js","types":"./dist/index.d.ts","gitHead":"80d0040b9ad626b67bd8b59dd074c5df77cfc1b9","scripts":{"dev":"tsc-watch -p tsconfig.json --onSuccess \"node ./dist/index.js\"","doc":"typedoc --includeVersion --out docs src/","lint":"eslint --fix --ext .ts \"src/**/*\"","test":"jest","build":"npm run lint && npm run prettier:write && tsc","start":"node dist/index.js","prettier:check":"prettier . --check","prettier:write":"prettier . --write"},"_npmUser":{"name":"neilcochran","email":"dev.neil.cochran@pm.me"},"_npmVersion":"10.8.1","description":"This repository defines a json schema to represent cross stitch patterns and provides javascript/typescript tools to work with them.","directories":{},"_nodeVersion":"20.16.0","dependencies":{"deep-equal":"^2.2.3"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.3.1","eslint":"^8.31.0","ts-jest":"^29.0.3","ts-node":"^10.9.1","typedoc":"^0.23.24","prettier":"3.3.3","tsc-watch":"^6.0.0","typescript":"^4.9.4","@types/jest":"^29.2.5","@types/node":"^18.11.18","@types/deep-equal":"^1.0.4","eslint-plugin-tsdoc":"^0.2.17","eslint-config-prettier":"^9.1.0","@typescript-eslint/parser":"^5.48.1","@typescript-eslint/eslint-plugin":"^5.48.1"},"_npmOperationalInternal":{"tmp":"tmp/cross-stitch_1.1.0_1724276347111_0.31746409388138375","host":"s3://npm-registry-packages"}},"1.1.1":{"name":"cross-stitch","version":"1.1.1","keywords":[],"author":{"name":"neilcochran","email":"dev.neil.cochran@pm.me"},"license":"MIT","_id":"cross-stitch@1.1.1","maintainers":[{"name":"neilcochran","email":"dev.neil.cochran@pm.me"}],"dist":{"shasum":"5be84f47eaa2969124bc5fc3e943513d2b9dc834","tarball":"https://registry.npmjs.org/cross-stitch/-/cross-stitch-1.1.1.tgz","fileCount":718,"integrity":"sha512-kR5FgWxcZAsgGYba2jnA5+9H0TcJTZKIgd9ef8ejfKY7ipentLV2ypf9EcnuWlPO+l66qy+uBaZ9H3XX+zWG3w==","signatures":[{"sig":"MEQCIFqOobJF9gir8jOXLl2fPeOL+orK6CbcC+qKeEvw+ZucAiBfeEOVVtPBkeZ16XNHV5vwaXBtWrr8Ypn5TXjFxfYmAg==","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":97974179},"main":"dist/index.js","types":"./dist/index.d.ts","gitHead":"8840ca0844c77d6039f2e99d3349f27a267a7fd1","scripts":{"dev":"tsc-watch -p tsconfig.json --onSuccess \"node ./dist/index.js\"","doc":"typedoc --includeVersion --out docs src/","lint":"eslint --fix --ext .ts \"src/**/*\"","test":"jest","build":"npm run lint && npm run prettier:write && tsc","start":"node dist/index.js","prettier:check":"prettier . --check","prettier:write":"prettier . --write"},"_npmUser":{"name":"neilcochran","email":"dev.neil.cochran@pm.me"},"_npmVersion":"10.8.1","description":"This repository defines a json schema to represent cross stitch patterns and provides javascript/typescript tools to work with them.","directories":{},"_nodeVersion":"20.16.0","dependencies":{"deep-equal":"^2.2.3"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.3.1","eslint":"^8.31.0","ts-jest":"^29.0.3","ts-node":"^10.9.1","typedoc":"^0.23.24","prettier":"3.3.3","tsc-watch":"^6.0.0","typescript":"^4.9.4","@types/jest":"^29.2.5","@types/node":"^18.11.18","@types/deep-equal":"^1.0.4","eslint-plugin-tsdoc":"^0.2.17","eslint-config-prettier":"^9.1.0","@typescript-eslint/parser":"^5.48.1","@typescript-eslint/eslint-plugin":"^5.48.1"},"_npmOperationalInternal":{"tmp":"tmp/cross-stitch_1.1.1_1724277120299_0.3339086259703741","host":"s3://npm-registry-packages"}},"1.1.2":{"name":"cross-stitch","version":"1.1.2","keywords":["cross stitch","cross-stitch","json"],"author":{"name":"neilcochran","email":"dev.neil.cochran@pm.me"},"license":"MIT","_id":"cross-stitch@1.1.2","maintainers":[{"name":"neilcochran","email":"dev.neil.cochran@pm.me"}],"dist":{"shasum":"b12133227c36656d04ba80a1970dfdab3d397141","tarball":"https://registry.npmjs.org/cross-stitch/-/cross-stitch-1.1.2.tgz","fileCount":718,"integrity":"sha512-qEjXP6UISfBFJHUSyftdocDG/grbBN8j9WOA3ufvwEEl5dGur5tnT0K7xEPqPifJyu6hKLneVzrZz/cZIqdLdA==","signatures":[{"sig":"MEYCIQCPLuTz6i4dPuQOTuGKDD5kz6uejyy63tN7FhnYqeNDbwIhALPETU8CCHmTFChSwl24B/iBmw0eechUtwXi09YgDE7T","keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA"}],"unpackedSize":97974299},"main":"dist/index.js","types":"./dist/index.d.ts","gitHead":"5b87cbe2a69910c6431e4de7aa98437c6f5ec952","scripts":{"dev":"tsc-watch -p tsconfig.json --onSuccess \"node ./dist/index.js\"","doc":"typedoc --includeVersion --out docs src/","lint":"eslint --fix --ext .ts \"src/**/*\"","test":"jest","build":"npm run lint && npm run prettier:write && tsc","start":"node dist/index.js","prettier:check":"prettier . --check","prettier:write":"prettier . --write"},"_npmUser":{"name":"neilcochran","email":"dev.neil.cochran@pm.me"},"_npmVersion":"10.8.1","description":"This repository defines a json schema to represent cross stitch patterns and provides javascript/typescript tools to work with them.","directories":{},"_nodeVersion":"20.16.0","dependencies":{"deep-equal":"^2.2.3"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.3.1","eslint":"^8.31.0","ts-jest":"^29.0.3","ts-node":"^10.9.1","typedoc":"^0.23.24","prettier":"3.3.3","tsc-watch":"^6.0.0","typescript":"^4.9.4","@types/jest":"^29.2.5","@types/node":"^18.11.18","@types/deep-equal":"^1.0.4","eslint-plugin-tsdoc":"^0.2.17","eslint-config-prettier":"^9.1.0","@typescript-eslint/parser":"^5.48.1","@typescript-eslint/eslint-plugin":"^5.48.1"},"_npmOperationalInternal":{"tmp":"tmp/cross-stitch_1.1.2_1724277989141_0.6858594583642146","host":"s3://npm-registry-packages"}},"2.0.0":{"name":"cross-stitch","version":"2.0.0","description":"This repository defines a json schema to represent cross stitch patterns and provides javascript/typescript tools to work with them.","author":{"name":"neilcochran","email":"dev.neil.cochran@pm.me"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/neilcochran/cross-stitch.git"},"main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./schema":{"types":"./dist/schema/index.d.ts","default":"./dist/schema/index.js"},"./dmc":{"types":"./dist/data/dmc.d.ts","default":"./dist/data/dmc.js"},"./schema.json":"./cross-stitch.schema.json"},"scripts":{"lint":"eslint --fix --ext .ts \"src/**/*\"","prettier:check":"prettier . --check","prettier:write":"prettier . --write","build":"npm run lint && npm run prettier:write && tsc -p tsconfig.build.json","typecheck":"tsc -p tsconfig.json","dev":"tsc-watch -p tsconfig.build.json --onSuccess \"node ./dist/index.js\"","start":"node dist/index.js","test":"jest","doc":"typedoc --includeVersion --out docs src/","schema":"ts-node scripts/generate-schema.ts","lint:check":"eslint --ext .ts \"src/**/*\""},"keywords":["cross stitch","cross-stitch","json"],"devDependencies":{"@types/jest":"^29.2.5","@types/node":"^18.11.18","@typescript-eslint/eslint-plugin":"^8.61.0","@typescript-eslint/parser":"^8.61.0","eslint":"^8.57.1","eslint-config-prettier":"^9.1.0","eslint-plugin-tsdoc":"^0.2.17","jest":"^29.3.1","prettier":"3.3.3","ts-jest":"^29.4.11","ts-node":"^10.9.1","tsc-watch":"^6.0.0","typedoc":"^0.28.19","typescript":"^5.9.3"},"dependencies":{"zod":"^4.4.3"},"_id":"cross-stitch@2.0.0","gitHead":"a0c1327c758b8614b9dbc41b28feaa1fab657032","bugs":{"url":"https://github.com/neilcochran/cross-stitch/issues"},"homepage":"https://github.com/neilcochran/cross-stitch#readme","_nodeVersion":"22.22.3","_npmVersion":"11.5.2","dist":{"integrity":"sha512-Skiu+aoWpf415ijrXds5WRcZw/KnWN5EQ9b381DFmZHB4l2RwA+bn1tAR1ZZZAN9BvjxTOn/8hddhpoXEWULOQ==","shasum":"c567ca9b0cf0055897427f51228c88e8cd2055db","tarball":"https://registry.npmjs.org/cross-stitch/-/cross-stitch-2.0.0.tgz","fileCount":69,"unpackedSize":320187,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/cross-stitch@2.0.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCID6BbCXr4Ekk+5TfRrZ9rRgbsCvlXveYUMJIktBlCZNtAiEA72IsD51BUzpr7MFH8lkHC/RB5ZlkxNe98/GGJbH0+mY="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:d8e6d7b4-7d8f-4b5d-9003-36a71d815ac2"}},"directories":{},"maintainers":[{"name":"neilcochran","email":"dev.neil.cochran@pm.me"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/cross-stitch_2.0.0_1781747287310_0.8454236204512879"},"_hasShrinkwrap":false}},"time":{"created":"2024-08-20T18:14:58.121Z","modified":"2026-06-18T01:48:07.693Z","0.5.1":"2024-08-20T18:14:58.394Z","1.0.0":"2024-08-20T21:39:14.270Z","1.1.0":"2024-08-21T21:39:07.375Z","1.1.1":"2024-08-21T21:52:00.626Z","1.1.2":"2024-08-21T22:06:29.390Z","1.1.3":"2026-05-11T22:17:07.042Z","1.1.4":"2026-05-11T22:48:48.947Z","1.1.5":"2026-05-11T22:55:28.996Z","1.1.6":"2026-05-11T22:57:02.259Z","1.1.7":"2026-05-11T23:52:17.867Z","2.0.0":"2026-06-18T01:48:07.442Z"},"author":{"name":"neilcochran","email":"dev.neil.cochran@pm.me"},"license":"MIT","keywords":["cross stitch","cross-stitch","json"],"description":"This repository defines a json schema to represent cross stitch patterns and provides javascript/typescript tools to work with them.","maintainers":[{"name":"neilcochran","email":"dev.neil.cochran@pm.me"}],"readme":"# **cross-stitch**\n\nThis repository defines a schema to represent cross stitch patterns and provides javascript/typescript tools to work with them.\n\nFor full code documentation, please visit [`this`](https://neilcochran.github.io/cross-stitch/) page.\n\n## Installation\n\nInstall via `npm` using the below terminal command\n\n```\n    npm install cross-stitch\n```\n\n## Usage\n\n`cross-stitch` validates and serializes patterns through a small set of functions. The schemas underneath are built with [Zod](https://zod.dev) and are published on the `cross-stitch/schema` subpath (see [the Zod schemas](#advanced-the-zod-schemas) below), but you do not need to use Zod directly for everyday work. Parsing never throws, and `encodePattern` throws only on an invalid pattern (`encodePatternSafe` is the non-throwing variant). Counts and dimensions are derived on demand and are not stored on the pattern.\n\n```ts\nimport { parsePattern, parsePatternJson, encodePattern, calculateTotals, calculateDimensions } from 'cross-stitch';\n\n// Validate an in-memory value\nconst result = parsePattern(value);\nif (result.success) {\n    const pattern = result.data; // fully typed CrossStitchPattern\n} else {\n    console.error(result.issues); // every problem found, as typed PatternIssue objects\n}\n\n// Validate a JSON string. Malformed JSON is reported as an issue, never thrown.\nconst parsed = parsePatternJson(jsonString);\n\nif (result.success) {\n    // Serialize a validated pattern back to a JSON string\n    const json = encodePattern(result.data); // or encodePatternSafe(...) for a non-throwing result\n\n    // Counts and dimensions are derived on demand\n    const totals = calculateTotals(result.data);\n    const { stitchWidth, stitchHeight, offsetX, offsetY } = calculateDimensions(result.data);\n}\n```\n\nEach validation problem is a typed `PatternIssue`, discriminated on `kind`. Cross-stitch-specific variants carry structured data (`unknown-color-reference` with the offending `colorId`, `duplicate-color-id`, `unreachable-placement` with its `angle` and `placement`, and the segment-span rules), and a `schema-violation` catch-all covers structural failures. Every issue also carries a `path` and a human-readable `message`.\n\nWhen you build a pattern by hand instead of parsing one, type it as `CrossStitchPatternInput` - the pre-validation shape that accepts plain numbers for ids and coordinates and lets defaulted fields be omitted - then parse it:\n\n```ts\nimport { parsePattern } from 'cross-stitch';\nimport type { CrossStitchPatternInput } from 'cross-stitch';\n\nconst draft: CrossStitchPatternInput = {\n    version: 1,\n    colors: [{ id: 0, name: 'Blue', symbol: '@', strands: [{ brand: 'DMC', code: '825', name: 'Dark Blue' }] }],\n    stitches: [{ kind: 'full', colorId: 0, x: 0, y: 0 }]\n};\nconst result = parsePattern(draft);\n```\n\n### Advanced: the Zod schemas\n\nEvery schema is published on the `cross-stitch/schema` subpath (`CrossStitchPattern`, `CrossStitchPatternJson`, `Stitch`, `Color`, and the rest), kept off the root import so everyday use never pulls in Zod. Reach for them to compose schemas (`.pick`, `.extend`) or to read raw Zod issues; they are the same validators the functions above are built on.\n\n```ts\nimport { z } from 'zod';\nimport { CrossStitchPattern, CrossStitchPatternJson } from 'cross-stitch/schema';\n\nconst result = CrossStitchPattern.safeParse(value); // Zod's native result, with result.error.issues\nif (result.success) {\n    const json = z.encode(CrossStitchPatternJson, result.data); // or z.safeEncode(...) to avoid throwing\n}\n```\n\nThe full DMC floss palette is published on a separate subpath so you only load it if you need it:\n\n```ts\nimport { dmcFloss } from 'cross-stitch/dmc';\n\nconst orangeSpice = dmcFloss['721']; // e.g. { brand: 'DMC', code: '721', name: 'Orange Spice - Medium', strandCount: 1, hex: '#f27842' }\n```\n\nNot every entry has a `hex`; it is present only where DMC publishes one.\n\n### JSON Schema\n\nA JSON Schema for the pattern document is generated from the Zod schema and published as [`cross-stitch.schema.json`](cross-stitch.schema.json). It is shipped in the package (importable as `cross-stitch/schema.json`) and carries the canonical `$id` `https://raw.githubusercontent.com/neilcochran/cross-stitch/master/cross-stitch.schema.json`, so non-JavaScript tooling can reference it to validate or annotate pattern documents.\n\nIt validates document **structure** only. The semantic rules - three-quarter reachability, unique color ids and symbols, every stitch referencing a color that exists, and the back / long span limits - have no JSON Schema representation and are enforced only by the Zod schema and the `parse*` functions. Regenerate it with `npm run schema`.\n\n## Versions\n\nView all versions of in the <a href=\"/CHANGELOG.md\">CHANGELOG.md</a>\n\n## License\n\nThis project is licensed under the MIT License - see the <a href=\"/LICENSE.md\">LICENSE.md</a> file for details\n\n<br/>\n\n## **CrossStitchPattern Schema:**\n\nThis section documents the pattern shape. See a full example [below](#full-schema-example).\n\n```json\n{\n    \"schemaVersion\": 1,\n    \"metadata\": {},\n    \"fabric\": {},\n    \"colors\": [],\n    \"stitches\": []\n}\n```\n\n-   `schemaVersion` - The version of the cross-stitch document format. Must be the number `1`.\n\n-   `metadata` - An optional [`Metadata`](#metadata-schema) object holding descriptive, non-structural information about the pattern.\n\n-   `fabric` - An optional [`Fabric`](#fabric-schema) object describing the fabric the pattern is stitched on.\n\n-   `colors` - An array of [`Color`](#color-schema) objects defining the palette used in the pattern.\n\n-   `stitches` - An array of [`Stitch`](#stitch-schema) objects defining every stitch in the pattern. Each stitch is tagged with a `kind`.\n\nBeyond per-field validation, a complete pattern must satisfy three whole-pattern rules: color `id` values are unique, color `symbol` values are unique, and every stitch's `colorId` refers to a color that exists.\n\n<hr/>\n<br/>\n\n### **Metadata Schema:**\n\nOptional, descriptive information about the pattern. Every field is optional.\n\n```json\n{\n    \"title\": \"Tiny Sampler\",\n    \"author\": \"Jane Stitcher\",\n    \"copyright\": \"(c) 2024 Jane Stitcher\",\n    \"notes\": \"A contrived example.\"\n}\n```\n\n-   `title` - The pattern title.\n\n-   `author` - The pattern author or designer.\n\n-   `copyright` - A copyright or license statement.\n\n-   `notes` - Free-form notes or comments about the pattern.\n\n<hr/>\n<br/>\n\n### **Fabric Schema:**\n\nDescribes the fabric the pattern is worked on.\n\n```json\n{\n    \"count\": 14,\n    \"hex\": \"#f5f5dc\",\n    \"kind\": \"aida\"\n}\n```\n\n-   `count` - The fabric count in stitches per inch (for example `14` for 14-count Aida). A positive integer.\n\n-   `hex` - An optional fabric color as a `#rrggbb` hexadecimal string.\n\n-   `kind` - An optional fabric type, such as `aida`, `evenweave`, or `linen`.\n\n<hr/>\n<br/>\n\n### **Color Schema:**\n\nA Color represents a color used in the pattern. The color is made up of one or more strands of [`Floss`](#floss-schema). Each floss strand can be a different color/brand, allowing blended colors to be defined.\n\n```json\n{\n    \"id\": 1,\n    \"name\": \"Burnt Orange\",\n    \"symbol\": \"@\",\n    \"strands\": []\n}\n```\n\n-   `id` - A non-negative integer identifier referenced by stitches to select this color. Must be unique within the pattern.\n\n-   `name` - A name for the overall color (since it could be a blend).\n\n-   `symbol` - A single printable ASCII character (codes 33 to 126) used to represent the color on the chart. Must be unique within the pattern.\n\n-   `strands` - An array of one or more [`Floss`](#floss-schema) objects defining the strands that make up the color (its thread composition).\n\n-   `hex` - The authoritative display color as a `#rrggbb` hexadecimal string. When present, render this; the strands describe how the color is achieved. Optional.\n\n<hr/>\n<br/>\n\n### **Floss Schema:**\n\nThis represents floss of a single color and brand, and by default, a single strand. If more than one strand of the same floss is needed, `count` can be increased.\n\n```json\n{\n    \"brand\": \"DMC\",\n    \"code\": \"721\",\n    \"name\": \"Orange Spice - Medium\",\n    \"strandCount\": 2,\n    \"hex\": \"#f27842\"\n}\n```\n\n-   `brand` - The name of the brand. See a list of supported brands [`here`](#supported-brand-values).\n\n-   `code` - A string representing the brand code for the color. This is often a number, but can be a string like `Ecru` or `Blanc`.\n\n-   `name` - The brand's name for the color.\n\n-   `strandCount` - A positive integer giving the number of strands of this floss to use in the color. If not given, defaults to `1`.\n\n-   `hex` - An optional color value as a `#rrggbb` hexadecimal string.\n\n<hr/>\n<br/>\n\n## **Stitch Schema:**\n\nEvery entry in `stitches` is one of six kinds, discriminated by its `kind` field. All stitches carry a `colorId` referencing a [`Color`](#color-schema).\n\nCoordinates use a lower-left origin. Cell-anchored stitches (`full`, `half`, `quarter`, `three-quarter`) sit on a grid square and take whole-integer `x` / `y` for the lower-left corner of that square. Segment stitches (`back`, `long`) take `from` and `to` points whose coordinates may also use half-step (`0.5`) values. No finer fraction is accepted, and all coordinates are non-negative.\n\n<hr/>\n<br/>\n\n### **Full Stitch Schema:**\n\nA full stitch covers a single square on the pattern in an 'X' shape. It is the combination of 2 opposing half stitches.\n\n```json\n{\n    \"kind\": \"full\",\n    \"colorId\": 1,\n    \"x\": 10,\n    \"y\": 20\n}\n```\n\n-   `kind` - The literal `\"full\"`.\n\n-   `x` - The x coordinate of the lower left corner of the square.\n\n-   `y` - The y coordinate of the lower left corner of the square.\n\n**Example:**\n\n![FullStitch image](https://github.com/neilcochran/cross-stitch/blob/master/images/full-stitch.png)\n\n```json\n{\n    \"kind\": \"full\",\n    \"colorId\": 1,\n    \"x\": 1,\n    \"y\": 1\n}\n```\n\n<hr/>\n<br/>\n\n### **Half Stitch Schema:**\n\nA half stitch is one diagonal across a grid square. It comes in two forms named for the corners they connect. A `tl-br` half goes from the top-left corner to the bottom-right corner. A `bl-tr` half goes from the bottom-left corner to the top-right corner. These are the only two valid values for `angle`.\n\n```json\n{\n    \"kind\": \"half\",\n    \"colorId\": 1,\n    \"x\": 10,\n    \"y\": 20,\n    \"angle\": \"tl-br\"\n}\n```\n\n-   `kind` - The literal `\"half\"`.\n\n-   `x` - The x coordinate of the lower left corner of the square.\n\n-   `y` - The y coordinate of the lower left corner of the square.\n\n-   `angle` - The half-stitch diagonal: `tl-br` or `bl-tr`.\n\n**Examples:**\n\n**Half Stitch tl-br**\n\n![HalfStitch tl-br image](https://github.com/neilcochran/cross-stitch/blob/master/images/half-45.png)\n\n```json\n{\n    \"kind\": \"half\",\n    \"colorId\": 1,\n    \"x\": 1,\n    \"y\": 1,\n    \"angle\": \"tl-br\"\n}\n```\n\n<hr/>\n<br/>\n\n**Half Stitch bl-tr**\n\n![HalfStitch bl-tr image](https://github.com/neilcochran/cross-stitch/blob/master/images/half-135.png)\n\n```json\n{\n    \"kind\": \"half\",\n    \"colorId\": 1,\n    \"x\": 1,\n    \"y\": 1,\n    \"angle\": \"bl-tr\"\n}\n```\n\n<hr/>\n<br/>\n\n### **Quarter Stitch Schema:**\n\nA quarter stitch spans a quarter of a grid square and can be located in the `top-right`, `bottom-right`, `bottom-left`, or `top-left` quadrant of the square as indicated by `placement`. A quarter stitch is a half stitch cut in half: one end is always at the center of the square, and the other extends to the corner indicated by `placement`.\n\n```json\n{\n    \"kind\": \"quarter\",\n    \"colorId\": 1,\n    \"x\": 10,\n    \"y\": 20,\n    \"placement\": \"top-right\"\n}\n```\n\n-   `kind` - The literal `\"quarter\"`.\n\n-   `x` - The x coordinate of the lower left corner of the square.\n\n-   `y` - The y coordinate of the lower left corner of the square.\n\n-   `placement` - The corner of the square the quarter stitch reaches: `top-right`, `bottom-right`, `bottom-left`, or `top-left`.\n\n**Examples:**\n\n**Quarter Stitch Top Right**\n\n![QuarterStitch top right image](https://github.com/neilcochran/cross-stitch/blob/master/images/quarter-top-right.png)\n\n```json\n{\n    \"kind\": \"quarter\",\n    \"colorId\": 1,\n    \"x\": 1,\n    \"y\": 1,\n    \"placement\": \"top-right\"\n}\n```\n\n<hr/>\n<br/>\n\n**Quarter Stitch Bottom Right**\n\n![QuarterStitch bottom right image](https://github.com/neilcochran/cross-stitch/blob/master/images/quarter-bottom-right.png)\n\n```json\n{\n    \"kind\": \"quarter\",\n    \"colorId\": 1,\n    \"x\": 1,\n    \"y\": 1,\n    \"placement\": \"bottom-right\"\n}\n```\n\n<hr/>\n<br/>\n\n**Quarter Stitch Bottom Left**\n\n![QuarterStitch bottom left image](https://github.com/neilcochran/cross-stitch/blob/master/images/quarter-bottom-left.png)\n\n```json\n{\n    \"kind\": \"quarter\",\n    \"colorId\": 1,\n    \"x\": 1,\n    \"y\": 1,\n    \"placement\": \"bottom-left\"\n}\n```\n\n<hr/>\n<br/>\n\n**Quarter Stitch Top Left**\n\n![QuarterStitch top left image](https://github.com/neilcochran/cross-stitch/blob/master/images/quarter-top-left.png)\n\n```json\n{\n    \"kind\": \"quarter\",\n    \"colorId\": 1,\n    \"x\": 1,\n    \"y\": 1,\n    \"placement\": \"top-left\"\n}\n```\n\n<hr/>\n<br/>\n\n### **Three Quarter Stitch Schema:**\n\nA three quarter stitch is a [half stitch](#half-stitch-schema) plus a [quarter stitch](#quarter-stitch-schema) in the same square, so both an `angle` and a `placement` are given. The quarter can only reach the two corners the half does not occupy. A `tl-br` half occupies the top-left and bottom-right corners, so its quarter is `top-right` or `bottom-left`. A `bl-tr` half occupies the bottom-left and top-right corners, so its quarter is `top-left` or `bottom-right`.\n\n```json\n{\n    \"kind\": \"three-quarter\",\n    \"colorId\": 1,\n    \"x\": 10,\n    \"y\": 20,\n    \"angle\": \"tl-br\",\n    \"placement\": \"top-right\"\n}\n```\n\n-   `kind` - The literal `\"three-quarter\"`.\n\n-   `x` - The x coordinate of the lower left corner of the square.\n\n-   `y` - The y coordinate of the lower left corner of the square.\n\n-   `angle` - The half-stitch diagonal: `tl-br` or `bl-tr`. See the [`Half Stitch`](#half-stitch-schema) schema.\n\n-   `placement` - The corner the quarter stitch reaches. Must be reachable for the given `angle` (see above). See the [`Quarter Stitch`](#quarter-stitch-schema) schema.\n\n**Examples:**\n\n**Three Quarter Stitch Top Right** (`tl-br` + `top-right`)\n\n![ThreeQuarterStitch top right image](https://github.com/neilcochran/cross-stitch/blob/master/images/three-quarter-top-right.png)\n\n```json\n{\n    \"kind\": \"three-quarter\",\n    \"colorId\": 1,\n    \"x\": 1,\n    \"y\": 1,\n    \"angle\": \"tl-br\",\n    \"placement\": \"top-right\"\n}\n```\n\n<hr/>\n<br/>\n\n**Three Quarter Stitch Bottom Left** (`tl-br` + `bottom-left`)\n\n![ThreeQuarterStitch bottom left image](https://github.com/neilcochran/cross-stitch/blob/master/images/three-quarter-bottom-left.png)\n\n```json\n{\n    \"kind\": \"three-quarter\",\n    \"colorId\": 1,\n    \"x\": 1,\n    \"y\": 1,\n    \"angle\": \"tl-br\",\n    \"placement\": \"bottom-left\"\n}\n```\n\n<hr/>\n<br/>\n\n**Three Quarter Stitch Top Left** (`bl-tr` + `top-left`)\n\n![ThreeQuarterStitch top left image](https://github.com/neilcochran/cross-stitch/blob/master/images/three-quarter-top-left.png)\n\n```json\n{\n    \"kind\": \"three-quarter\",\n    \"colorId\": 1,\n    \"x\": 1,\n    \"y\": 1,\n    \"angle\": \"bl-tr\",\n    \"placement\": \"top-left\"\n}\n```\n\n<hr/>\n<br/>\n\n**Three Quarter Stitch Bottom Right** (`bl-tr` + `bottom-right`)\n\n![ThreeQuarterStitch bottom right image](https://github.com/neilcochran/cross-stitch/blob/master/images/three-quarter-bottom-right.png)\n\n```json\n{\n    \"kind\": \"three-quarter\",\n    \"colorId\": 1,\n    \"x\": 1,\n    \"y\": 1,\n    \"angle\": \"bl-tr\",\n    \"placement\": \"bottom-right\"\n}\n```\n\n<hr/>\n<br/>\n\n### **Back Stitch Schema:**\n\nBack stitches can go laterally, vertically, or diagonally. A back stitch may span at most one grid space in each direction; half-step (`0.5`) coordinates are supported. A segment longer than one space must be split into multiple back stitches (or use a [`Long Stitch`](#long-stitch-schema)).\n\n```json\n{\n    \"kind\": \"back\",\n    \"colorId\": 1,\n    \"from\": { \"x\": 0, \"y\": 0 },\n    \"to\": { \"x\": 1, \"y\": 0 }\n}\n```\n\n-   `kind` - The literal `\"back\"`.\n\n-   `from` - The start point of the stitch as `{ x, y }`.\n\n-   `to` - The end point of the stitch as `{ x, y }`.\n\n**Examples:**\n\n**Back Stitch Lateral**\n\n![BackStitch lateral image](https://github.com/neilcochran/cross-stitch/blob/master/images/back-stitch-lateral.png)\n\nRed:\n\n```json\n{\n    \"kind\": \"back\",\n    \"colorId\": 1,\n    \"from\": { \"x\": 1, \"y\": 1 },\n    \"to\": { \"x\": 2, \"y\": 1 }\n}\n```\n\nGreen:\n\n```json\n{\n    \"kind\": \"back\",\n    \"colorId\": 1,\n    \"from\": { \"x\": 1, \"y\": 2 },\n    \"to\": { \"x\": 1.5, \"y\": 2 }\n}\n```\n\n<hr/>\n<br/>\n\n**Back Stitch Vertical**\n\n![BackStitch vertical image](https://github.com/neilcochran/cross-stitch/blob/master/images/back-stitch-vertical.png)\n\nRed:\n\n```json\n{\n    \"kind\": \"back\",\n    \"colorId\": 1,\n    \"from\": { \"x\": 2, \"y\": 1 },\n    \"to\": { \"x\": 2, \"y\": 2 }\n}\n```\n\nGreen:\n\n```json\n{\n    \"kind\": \"back\",\n    \"colorId\": 1,\n    \"from\": { \"x\": 1, \"y\": 1 },\n    \"to\": { \"x\": 1, \"y\": 1.5 }\n}\n```\n\n<hr/>\n<br/>\n\n**Back Stitch Diagonal**\n\n![BackStitch diagonal image](https://github.com/neilcochran/cross-stitch/blob/master/images/back-stitch-diagonal.png)\n\nRed:\n\n```json\n{\n    \"kind\": \"back\",\n    \"colorId\": 1,\n    \"from\": { \"x\": 1, \"y\": 2 },\n    \"to\": { \"x\": 2, \"y\": 1 }\n}\n```\n\nGreen:\n\n```json\n{\n    \"kind\": \"back\",\n    \"colorId\": 1,\n    \"from\": { \"x\": 0, \"y\": 1 },\n    \"to\": { \"x\": 0.5, \"y\": 1.5 }\n}\n```\n\n<hr/>\n<br/>\n\n### **Long Stitch Schema:**\n\nLong stitches span more than one space. They can move laterally, vertically, or diagonally just like back stitches, and they also support half-step (`0.5`) coordinates, but they have no maximum length.\n\n```json\n{\n    \"kind\": \"long\",\n    \"colorId\": 1,\n    \"from\": { \"x\": 0, \"y\": 0 },\n    \"to\": { \"x\": 5, \"y\": 2 }\n}\n```\n\n-   `kind` - The literal `\"long\"`.\n\n-   `from` - The start point of the stitch as `{ x, y }`.\n\n-   `to` - The end point of the stitch as `{ x, y }`.\n\n**Examples:**\n\n![LongStitch image](https://github.com/neilcochran/cross-stitch/blob/master/images/long-stitch.png)\n\nRed:\n\n```json\n{\n    \"kind\": \"long\",\n    \"colorId\": 1,\n    \"from\": { \"x\": 0, \"y\": 3 },\n    \"to\": { \"x\": 2.5, \"y\": 0 }\n}\n```\n\nGreen:\n\n```json\n{\n    \"kind\": \"long\",\n    \"colorId\": 1,\n    \"from\": { \"x\": 0, \"y\": 3 },\n    \"to\": { \"x\": 3, \"y\": 3 }\n}\n```\n\n<hr/>\n<br/>\n\n### **Totals and Dimensions:**\n\nStitch counts and pattern size are not stored in the schema. Derive them from a validated pattern with the exported helpers:\n\n-   `calculateTotals(pattern)` returns the overall stitch counts and a per-color breakdown (`{ total, byColor }`). `total` is a count object with one entry per stitch kind, keyed by camelCase name (`full`, `half`, `quarter`, `threeQuarter`, `back`, `long`), so counts read as `total.threeQuarter`. Each `byColor` entry is `{ colorId, counts }`, where `counts` is a count object of the same shape, so per-color counts read as `byColor[0].counts.threeQuarter`.\n\n-   `calculateDimensions(pattern)` returns `{ stitchWidth, stitchHeight, offsetX, offsetY }` in whole stitches: the width and height of the stitched area's bounding box, plus the lower-left offset of that box (both offsets are `0` when the pattern is anchored at the origin or has no stitches).\n\n-   `stitchBounds(stitch)` returns the axis-aligned bounding box (`{ minX, minY, maxX, maxY }`) of a single stitch, normalizing the cell-anchored and segment shapes into one position-and-extent value.\n\n<hr/>\n<br/>\n\n### **Full Schema Example:**\n\nThe image below shows a tiny 3x3 pattern that uses every stitch kind. Here is the JSON that describes it:\n\n![full pattern example image](https://github.com/neilcochran/cross-stitch/blob/master/images/full-pattern-example.png)\n\n```json\n{\n    \"schemaVersion\": 1,\n    \"metadata\": {\n        \"title\": \"Tiny Sampler\",\n        \"notes\": \"A tiny 3x3 example using every stitch kind.\"\n    },\n    \"fabric\": {\n        \"count\": 14,\n        \"kind\": \"aida\"\n    },\n    \"colors\": [\n        {\n            \"id\": 0,\n            \"name\": \"Dark Blue\",\n            \"symbol\": \"@\",\n            \"strands\": [\n                {\n                    \"brand\": \"DMC\",\n                    \"code\": \"825\",\n                    \"name\": \"Dark Blue\",\n                    \"strandCount\": 2\n                }\n            ]\n        },\n        {\n            \"id\": 1,\n            \"name\": \"Orange Blend\",\n            \"symbol\": \"&\",\n            \"strands\": [\n                {\n                    \"brand\": \"DMC\",\n                    \"code\": \"721\",\n                    \"name\": \"Orange Spice\",\n                    \"strandCount\": 1\n                },\n                {\n                    \"brand\": \"DMC\",\n                    \"code\": \"947\",\n                    \"name\": \"Burnt Orange\",\n                    \"strandCount\": 1\n                }\n            ]\n        }\n    ],\n    \"stitches\": [\n        { \"kind\": \"full\", \"colorId\": 0, \"x\": 0, \"y\": 1 },\n        { \"kind\": \"three-quarter\", \"colorId\": 0, \"x\": 2, \"y\": 1, \"angle\": \"tl-br\", \"placement\": \"top-right\" },\n        { \"kind\": \"half\", \"colorId\": 1, \"x\": 1, \"y\": 1, \"angle\": \"bl-tr\" },\n        { \"kind\": \"quarter\", \"colorId\": 1, \"x\": 2, \"y\": 0, \"placement\": \"bottom-right\" },\n        { \"kind\": \"back\", \"colorId\": 1, \"from\": { \"x\": 0, \"y\": 0 }, \"to\": { \"x\": 1, \"y\": 0 } },\n        { \"kind\": \"long\", \"colorId\": 1, \"from\": { \"x\": 0, \"y\": 3 }, \"to\": { \"x\": 3, \"y\": 2 } }\n    ]\n}\n```\n\n### **Supported brand values:**\n\n-   Anchor\n-   Appletons\n-   Cosmo\n-   DMC\n-   J&P Coats\n-   Kreinik\n-   Madeira\n-   Presencia\n-   Sullivans\n-   Unbranded\n\n<hr/>\n","readmeFilename":"README.md","homepage":"https://github.com/neilcochran/cross-stitch#readme","repository":{"type":"git","url":"git+https://github.com/neilcochran/cross-stitch.git"},"bugs":{"url":"https://github.com/neilcochran/cross-stitch/issues"}}