{"_id":"@a-company/atelier-schema","_rev":"5-d7160415b1574a4b67acac228e18b0c7","name":"@a-company/atelier-schema","dist-tags":{"latest":"0.28.0"},"versions":{"0.25.1":{"name":"@a-company/atelier-schema","version":"0.25.1","_id":"@a-company/atelier-schema@0.25.1","maintainers":[{"name":"ascend42","email":"ascend@a-company.org"}],"dist":{"shasum":"e134cfa443eccc1872851bfb2379b68e96705de7","tarball":"https://registry.npmjs.org/@a-company/atelier-schema/-/atelier-schema-0.25.1.tgz","fileCount":8,"integrity":"sha512-inp/pWuiQvXWvC4Un7JrnEA5raiJYcU7GSN+sZuFBffjFNbdZO1akTaKneqKZff56IUJLpsSzKc+VWZsjOcNKA==","signatures":[{"sig":"MEYCIQCqPBgfUmSnvNlVeM/6y0Mp45NdXN0PX5b4hf1iEZeWNAIhAIamXHRuDQF6/QZ0V3V5DQOO9vbnh/NB/Erv/OrtH+Fo","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":718899},"main":"./dist/index.cjs","type":"module","_from":"file:a-company-atelier-schema-0.25.1.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"test":"vitest run","build":"tsup","clean":"rm -rf dist","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ascend42","email":"ascend@a-company.org"},"_resolved":"/private/var/folders/gq/nt2kdpsj39dc8vpq6cfzx8gw0000gn/T/d95f767cbc9d0e2ea0667a6d9e8407e5/a-company-atelier-schema-0.25.1.tgz","_integrity":"sha512-inp/pWuiQvXWvC4Un7JrnEA5raiJYcU7GSN+sZuFBffjFNbdZO1akTaKneqKZff56IUJLpsSzKc+VWZsjOcNKA==","_npmVersion":"11.7.0","description":"Zod validation schemas with AI-readable error messages","directories":{},"_nodeVersion":"24.12.0","dependencies":{"zod":"^3.24.0","yaml":"^2.7.0","@a-company/atelier-types":"0.25.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.4.0","vitest":"^3.0.0","typescript":"^5.7.0"},"_npmOperationalInternal":{"tmp":"tmp/atelier-schema_0.25.1_1771887812071_0.17973594185976416","host":"s3://npm-registry-packages-npm-production"}},"0.25.2":{"name":"@a-company/atelier-schema","version":"0.25.2","_id":"@a-company/atelier-schema@0.25.2","maintainers":[{"name":"ascend42","email":"ascend@a-company.org"}],"dist":{"shasum":"70f78198a174528f94730ea3a5642281f4c0aff3","tarball":"https://registry.npmjs.org/@a-company/atelier-schema/-/atelier-schema-0.25.2.tgz","fileCount":8,"integrity":"sha512-17GriXExzmbd/R3BosdR92IsX2pDpb6kVbIyqlL59jD7c4uqpDNube10ixA4xYfPhb30JRN9RZQ4IY1vebYsuQ==","signatures":[{"sig":"MEUCIGrJ691P1ZllvWwXMvq1lUIX3PWC3NVZwb3rvJI48xPnAiEAzCX3NY3V4GRnj9kPg+X0d5f4ES+cP7SE/6UN1yVhfR0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":719907},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"gitHead":"a014974918c3dd3907ef5e1ee3ef85bc3a227d31","scripts":{"test":"vitest run","build":"tsup","clean":"rm -rf dist","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ascend42","email":"ascend@a-company.org"},"_npmVersion":"11.7.0","description":"Zod validation schemas with AI-readable error messages","directories":{},"_nodeVersion":"24.12.0","dependencies":{"zod":"^3.24.0","yaml":"^2.7.0","@a-company/atelier-types":"workspace:*"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.4.0","vitest":"^3.0.0","typescript":"^5.7.0"},"_npmOperationalInternal":{"tmp":"tmp/atelier-schema_0.25.2_1772560073187_0.4426000837914523","host":"s3://npm-registry-packages-npm-production"}},"0.25.3":{"name":"@a-company/atelier-schema","version":"0.25.3","_id":"@a-company/atelier-schema@0.25.3","maintainers":[{"name":"ascend42","email":"ascend@a-company.org"}],"dist":{"shasum":"dce7cce23a9f8a776b30f1d73f79b2494f64624b","tarball":"https://registry.npmjs.org/@a-company/atelier-schema/-/atelier-schema-0.25.3.tgz","fileCount":8,"integrity":"sha512-yxhcDdGej1JF8Aqq6KSJKHtDA/Kr83rAljlwYdijmETDqGsrxM9AeMVzTGW7MoPfbt/ZLNFNZsaw89QkSAKYzA==","signatures":[{"sig":"MEYCIQCHEqSXVdjJaqSgkySAT+sWBxhv812fhF8bvXkMFlKDcwIhAOt04kXKhSiKy5B2ZYIVy8l6GpY6wjm1YgA95+/0Xdty","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":719901},"main":"./dist/index.cjs","type":"module","_from":"file:a-company-atelier-schema-0.25.3.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"test":"vitest run","build":"tsup","clean":"rm -rf dist","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ascend42","email":"ascend@a-company.org"},"_resolved":"/private/var/folders/gq/nt2kdpsj39dc8vpq6cfzx8gw0000gn/T/0c63323c33792906ba028c4a4bad4339/a-company-atelier-schema-0.25.3.tgz","_integrity":"sha512-yxhcDdGej1JF8Aqq6KSJKHtDA/Kr83rAljlwYdijmETDqGsrxM9AeMVzTGW7MoPfbt/ZLNFNZsaw89QkSAKYzA==","_npmVersion":"11.7.0","description":"Zod validation schemas with AI-readable error messages","directories":{},"_nodeVersion":"24.12.0","dependencies":{"zod":"^3.24.0","yaml":"^2.7.0","@a-company/atelier-types":"0.25.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.4.0","vitest":"^3.0.0","typescript":"^5.7.0"},"_npmOperationalInternal":{"tmp":"tmp/atelier-schema_0.25.3_1772568031937_0.4139862358966311","host":"s3://npm-registry-packages-npm-production"}},"0.26.0":{"name":"@a-company/atelier-schema","version":"0.26.0","_id":"@a-company/atelier-schema@0.26.0","maintainers":[{"name":"ascend42","email":"ascend@a-company.org"}],"dist":{"shasum":"1b0b2d1351d8e876372de8b6a38bc10bda769bff","tarball":"https://registry.npmjs.org/@a-company/atelier-schema/-/atelier-schema-0.26.0.tgz","fileCount":8,"integrity":"sha512-04Pr8A9zb8BoodkYP9IqW4WW3fJh/XE/G5AZkRj4SnGXtuwrcIGqq5ScufCogAxQWrc5wE9WZgVFP/J1s+wDfw==","signatures":[{"sig":"MEUCIBf5akI1wEIkSIAiwGa1FlNs7QvVwczIGWok5qPF50jMAiEAy5mpN6eQAhE5FIBs3YcRGCaYlf2CE7DV/zn5OaUtmnk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":751003},"main":"./dist/index.cjs","type":"module","_from":"file:a-company-atelier-schema-0.26.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"scripts":{"test":"vitest run","build":"tsup","clean":"rm -rf dist","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ascend42","email":"ascend@a-company.org"},"_resolved":"/private/var/folders/gq/nt2kdpsj39dc8vpq6cfzx8gw0000gn/T/637eafd94fcd076a0f8bf0a6b7920816/a-company-atelier-schema-0.26.0.tgz","_integrity":"sha512-04Pr8A9zb8BoodkYP9IqW4WW3fJh/XE/G5AZkRj4SnGXtuwrcIGqq5ScufCogAxQWrc5wE9WZgVFP/J1s+wDfw==","_npmVersion":"11.7.0","description":"Zod validation schemas with AI-readable error messages","directories":{},"_nodeVersion":"24.12.0","dependencies":{"zod":"^3.24.0","yaml":"^2.7.0","@a-company/atelier-types":"0.26.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.4.0","vitest":"^3.0.0","typescript":"^5.7.0"},"_npmOperationalInternal":{"tmp":"tmp/atelier-schema_0.26.0_1778200992937_0.0920383646564662","host":"s3://npm-registry-packages-npm-production"}},"0.28.0":{"name":"@a-company/atelier-schema","version":"0.28.0","publishConfig":{"access":"public"},"description":"Zod validation schemas with AI-readable error messages","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"}},"dependencies":{"zod":"^3.24.0","yaml":"^2.7.0","@a-company/atelier-types":"0.31.0"},"devDependencies":{"tsup":"^8.4.0","typescript":"^5.7.0","vitest":"^3.0.0"},"scripts":{"build":"tsup","typecheck":"tsc --noEmit","test":"vitest run","clean":"rm -rf dist"},"_id":"@a-company/atelier-schema@0.28.0","_integrity":"sha512-t0WEHrlMD30Yfafh5BoCI0YBnsLra8Q0ptN0nYXR4k0kAn4QYafzxAOBu2sF0zCRaA+eOpjWh9x6m8MDbFM4uQ==","_resolved":"/private/var/folders/gq/nt2kdpsj39dc8vpq6cfzx8gw0000gn/T/a1a781c9ae23eddc5e168ca5d360c394/a-company-atelier-schema-0.28.0.tgz","_from":"file:a-company-atelier-schema-0.28.0.tgz","_nodeVersion":"24.12.0","_npmVersion":"11.7.0","dist":{"integrity":"sha512-t0WEHrlMD30Yfafh5BoCI0YBnsLra8Q0ptN0nYXR4k0kAn4QYafzxAOBu2sF0zCRaA+eOpjWh9x6m8MDbFM4uQ==","shasum":"80dbcb0cb99375effe305c090753c83dcef49da1","tarball":"https://registry.npmjs.org/@a-company/atelier-schema/-/atelier-schema-0.28.0.tgz","fileCount":8,"unpackedSize":834713,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIEhoa00G+2Gd8WGoDYZplji7JzcxPboFGpEAfffyus6NAiEA9OIifNGII5ExHZKhDLm7xx6ta1fEwsAa+loGtTrC6T0="}]},"_npmUser":{"name":"ascend42","email":"ascend@a-company.org"},"directories":{},"maintainers":[{"name":"ascend42","email":"ascend@a-company.org"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/atelier-schema_0.28.0_1779253709310_0.6334987762971596"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-23T23:03:31.997Z","modified":"2026-05-20T05:08:29.613Z","0.25.1":"2026-02-23T23:03:32.212Z","0.25.2":"2026-03-03T17:47:53.409Z","0.25.3":"2026-03-03T20:00:32.122Z","0.26.0":"2026-05-08T00:43:13.109Z","0.28.0":"2026-05-20T05:08:29.474Z"},"description":"Zod validation schemas with AI-readable error messages","maintainers":[{"name":"ascend42","email":"ascend@a-company.org"}],"readme":"---\ntitle: \"@atelier/schema\"\nscope: Zod validation schemas, YAML parse/serialize, AI-readable error formatting\npackages: [\"@atelier/schema\"]\nrelated: [\"docs/format-spec.md\", \"packages/types/README.md\", \"packages/core/README.md\"]\n---\n\n# @atelier/schema\n\nZod validation schemas for the Atelier animation document format. Every type in `@atelier/types` has a corresponding runtime schema here, plus validation functions that return flat, AI-readable errors and YAML parse/serialize utilities.\n\n## Package Info\n\n| Field | Value |\n|-------|-------|\n| Name | `@atelier/schema` |\n| Version | `0.1.0` |\n| Build | tsup (ESM + CJS + DTS) |\n| Source | `packages/schema/src/` |\n| Test | `vitest run` |\n\n### Dependencies\n\n| Package | Version |\n|---------|---------|\n| `@atelier/types` | `workspace:*` |\n| `zod` | `^3.24.0` |\n| `yaml` | `^2.7.0` |\n\n## Architecture\n\n```\n@atelier/types (TypeScript interfaces)\n        |\n        v\n@atelier/schema (Zod runtime schemas)\n   |         |\n   v         v\nvalidate  parse/serialize\n   |         |\n   +----+----+\n        |\n        v\n  ValidationResult<T>\n  { success, data | errors }\n```\n\nAll validation paths converge on a single `ValidationResult<T>` type. Whether you validate a JS object or parse YAML, you get the same result shape with flat `{path, message}` errors.\n\n## Exports\n\n### Zod Schemas\n\nEvery schema mirrors a type from `@atelier/types` one-to-one.\n\n#### Units (`src/units.ts`)\n\n| Schema | Validates |\n|--------|-----------|\n| `PixelSchema` | Any number (pixel value) |\n| `PercentageSchema` | String matching `/^-?\\d+(\\.\\d+)?%$/` (e.g. `\"50%\"`) |\n| `UnitValueSchema` | Union of `PixelSchema \\| PercentageSchema` |\n\n#### Coordinates (`src/coordinates.ts`)\n\n| Schema | Validates |\n|--------|-----------|\n| `FrameSchema` | `{ x: UnitValue, y: UnitValue }` |\n| `BoundsSchema` | `{ width: UnitValue, height: UnitValue }` |\n| `AnchorPointSchema` | `{ x: 0..1, y: 0..1 }` |\n\n#### Color (`src/color.ts`)\n\n| Schema | Validates |\n|--------|-----------|\n| `RGBAColorSchema` | `{ r: 0-255, g: 0-255, b: 0-255, a: 0-1 }` |\n| `HSLAColorSchema` | `{ h: 0-360, s: 0-100, l: 0-100, a: 0-1 }` |\n| `HexColorSchema` | Hex string: `#RGB`, `#RGBA`, `#RRGGBB`, or `#RRGGBBAA` |\n| `ColorSchema` | Union of `RGBA \\| HSLA \\| Hex` |\n\n#### Shape & Fill (`src/shape.ts`)\n\n| Schema | Validates |\n|--------|-----------|\n| `PathPointSchema` | `{ x, y }` with optional `in`/`out` control points |\n| `RectShapeSchema` | `{ type: \"rect\" }` with optional `cornerRadius` (number or 4-tuple) |\n| `EllipseShapeSchema` | `{ type: \"ellipse\" }` |\n| `PathShapeSchema` | `{ type: \"path\", points: [...] }` (min 2 points), optional `closed` |\n| `ShapeSchema` | Discriminated union on `type`: rect, ellipse, path |\n| `GradientStopSchema` | `{ offset: 0-1, color: Color }` |\n| `SolidFillSchema` | `{ type: \"solid\", color: Color }` |\n| `LinearGradientFillSchema` | `{ type: \"linear-gradient\", angle, stops }` (min 2 stops) |\n| `RadialGradientFillSchema` | `{ type: \"radial-gradient\", center, radius, stops }` (min 2 stops) |\n| `FillSchema` | Discriminated union on `type`: solid, linear-gradient, radial-gradient |\n| `StrokeSchema` | `{ color, width }` with optional `dash`, `lineCap`, `lineJoin` |\n| `TextStyleSchema` | `{ fontFamily, fontSize, color }` with optional weight, style, align, etc. |\n\n#### Easing (`src/easing.ts`)\n\n| Schema | Validates |\n|--------|-----------|\n| `LinearEasingSchema` | `{ type: \"linear\" }` |\n| `CubicBezierEasingSchema` | `{ type: \"cubic-bezier\", x1, y1, x2, y2 }` (x1/x2 clamped 0-1) |\n| `SpringEasingSchema` | `{ type: \"spring\" }` with optional mass, stiffness, damping, velocity |\n| `StepEasingSchema` | `{ type: \"step\", steps }` with optional position (start/end) |\n| `EasingPresetSchema` | Enum: `\"ease-in\"`, `\"ease-out\"`, `\"ease-in-out\"` |\n| `EasingSchema` | Union of all easing types + presets |\n\n#### Layer (`src/layer.ts`)\n\n| Schema | Validates |\n|--------|-----------|\n| `ShapeVisualSchema` | `{ type: \"shape\", shape }` with optional fill/stroke |\n| `TextVisualSchema` | `{ type: \"text\", content, style }` |\n| `ImageVisualSchema` | `{ type: \"image\", assetId }` |\n| `GroupVisualSchema` | `{ type: \"group\" }` |\n| `RefVisualSchema` | `{ type: \"ref\", src }` |\n| `VisualSchema` | Discriminated union on `type`: shape, text, image, group, ref |\n| `LayerSchema` | Full layer: `{ id, visual, frame, bounds }` + optional fields |\n\n#### Delta (`src/delta.ts`)\n\n| Schema | Validates |\n|--------|-----------|\n| `AnimatablePropertySchema` | Enum of animatable dot-paths (e.g. `\"opacity\"`, `\"frame.x\"`, `\"scale.y\"`) |\n| `FrameRangeSchema` | `[start, end]` tuple where end >= start (both non-negative integers) |\n| `DeltaSchema` | `{ layer, property, range, from, to }` with optional easing, id, description, tags |\n\n#### State (`src/state.ts`)\n\n| Schema | Validates |\n|--------|-----------|\n| `StateSchema` | `{ duration, deltas: Delta[] }` with optional description, tags |\n\n#### Preset (`src/preset.ts`)\n\n| Schema | Validates |\n|--------|-----------|\n| `PresetDeltaSchema` | `{ property, from, to }` with optional offset and easing |\n| `PresetSchema` | `{ deltas: PresetDelta[] }` (min 1 delta) with optional description, tags |\n\n#### Variable (`src/variable.ts`)\n\n| Schema | Validates |\n|--------|-----------|\n| `VariableTypeSchema` | Enum: `\"string\"`, `\"number\"`, `\"color\"`, `\"asset\"`, `\"boolean\"` |\n| `VariableSchema` | `{ type: VariableType }` with optional default, description |\n\n#### Asset (`src/asset.ts`)\n\n| Schema | Validates |\n|--------|-----------|\n| `AssetTypeSchema` | Enum: `\"image\"`, `\"svg\"`, `\"font\"`, `\"animation\"` |\n| `AssetSchema` | `{ type: AssetType, src }` with optional description |\n\n#### Document (`src/document.ts`)\n\n| Schema | Validates |\n|--------|-----------|\n| `CanvasSchema` | `{ width, height, fps }` (positive integers) with optional background |\n| `AtelierDocumentSchema` | Full document: `{ version, name, canvas, layers, states }` with optional description, tags, variables, assets, presets |\n\n### Validation Functions (`src/validate.ts`)\n\n```typescript\ntype ValidationResult<T> =\n  | { success: true; data: T }\n  | { success: false; errors: ValidationError[] };\n\ninterface ValidationError {\n  path: string;\n  message: string;\n}\n\nfunction validateDocument(input: unknown): ValidationResult<AtelierDocument>;\nfunction validateLayer(input: unknown): ValidationResult<Layer>;\nfunction validateDelta(input: unknown): ValidationResult<Delta>;\n```\n\nAll three functions follow the same pattern: call `safeParse` on the corresponding Zod schema, then flatten any Zod issues into `{path, message}` pairs via an internal `formatErrors()` function. The path is built from `issue.path.join(\".\")`, falling back to `\"(root)\"` when the issue has no path segments.\n\n### YAML Parsing (`src/parse.ts`)\n\n```typescript\nfunction parseAtelier(yamlString: string): ValidationResult<AtelierDocument>;\nfunction serializeAtelier(doc: AtelierDocument): string;\n```\n\n**`parseAtelier`** performs YAML parse followed by `validateDocument()` in one step. If the YAML itself is malformed, it returns a single error with `path: \"(yaml)\"` and the parse error message. Otherwise, it delegates to `validateDocument` and returns whatever schema errors apply.\n\n**`serializeAtelier`** converts a validated `AtelierDocument` to a YAML string using `yaml.stringify` with `indent: 2`.\n\nRoundtrip fidelity is tested: parse, validate, serialize, re-parse produces matching data.\n\n## Usage Examples\n\n### 1. Validating a Document\n\n```typescript\nimport { validateDocument } from \"@atelier/schema\";\n\nconst result = validateDocument({\n  version: \"1.0\",\n  name: \"my-animation\",\n  canvas: { width: 1080, height: 1080, fps: 30 },\n  layers: [],\n  states: {},\n});\n\nif (result.success) {\n  console.log(result.data.name); // \"my-animation\"\n} else {\n  console.error(result.errors);\n}\n```\n\n### 2. Parsing YAML\n\n```typescript\nimport { parseAtelier } from \"@atelier/schema\";\n\nconst yaml = `\nversion: \"1.0\"\nname: fade-in\ncanvas:\n  width: 1920\n  height: 1080\n  fps: 60\nlayers:\n  - id: bg\n    visual:\n      type: shape\n      shape:\n        type: rect\n      fill:\n        type: solid\n        color: \"#000000\"\n    frame: { x: 0, y: 0 }\n    bounds: { width: 1920, height: 1080 }\nstates:\n  idle:\n    duration: 30\n    deltas: []\n`;\n\nconst result = parseAtelier(yaml);\nif (result.success) {\n  console.log(result.data.layers[0].id); // \"bg\"\n}\n```\n\n### 3. Serializing to YAML\n\n```typescript\nimport { validateDocument, serializeAtelier } from \"@atelier/schema\";\n\nconst result = validateDocument({\n  version: \"1.0\",\n  name: \"bounce\",\n  canvas: { width: 1080, height: 1080, fps: 30 },\n  layers: [],\n  states: {},\n});\n\nif (result.success) {\n  const yaml = serializeAtelier(result.data);\n  console.log(yaml);\n  // version: \"1.0\"\n  // name: bounce\n  // canvas:\n  //   width: 1080\n  //   height: 1080\n  //   fps: 30\n  // layers: []\n  // states: {}\n}\n```\n\n### 4. Handling Validation Errors (Flat Format)\n\nErrors are flat `{path, message}` objects -- no nested Zod error trees. This makes them easy to log, display, or feed to an AI model.\n\n```typescript\nimport { validateDocument } from \"@atelier/schema\";\n\nconst result = validateDocument({ name: \"test\" });\n// result:\n// {\n//   success: false,\n//   errors: [\n//     { path: \"version\", message: \"Required\" },\n//     { path: \"canvas\", message: \"Required\" },\n//     { path: \"layers\", message: \"Required\" },\n//     { path: \"states\", message: \"Required\" }\n//   ]\n// }\n\nif (!result.success) {\n  for (const err of result.errors) {\n    console.error(`${err.path}: ${err.message}`);\n  }\n  // version: Required\n  // canvas: Required\n  // layers: Required\n  // states: Required\n}\n```\n\nYAML parse errors follow the same shape:\n\n```typescript\nimport { parseAtelier } from \"@atelier/schema\";\n\nconst result = parseAtelier(\"{{{{ not yaml\");\n// { success: false, errors: [{ path: \"(yaml)\", message: \"YAML parse error: ...\" }] }\n```\n\n### 5. Using Individual Schemas for Partial Validation\n\nYou can import any schema directly for ad-hoc validation of fragments:\n\n```typescript\nimport { LayerSchema, DeltaSchema, ColorSchema } from \"@atelier/schema\";\n\n// Validate a single layer\nconst layerResult = LayerSchema.safeParse({\n  id: \"circle\",\n  visual: { type: \"shape\", shape: { type: \"ellipse\" } },\n  frame: { x: \"50%\", y: \"50%\" },\n  bounds: { width: 200, height: 200 },\n});\n\n// Validate a color value\nconst colorResult = ColorSchema.safeParse(\"#FF5500\");\n\n// Validate a delta\nconst deltaResult = DeltaSchema.safeParse({\n  layer: \"title\",\n  property: \"opacity\",\n  range: [0, 30],\n  from: 0,\n  to: 1,\n  easing: { type: \"spring\", stiffness: 200, damping: 12 },\n});\n```\n\n## Schema Source Map\n\n| File | Schemas |\n|------|---------|\n| `src/units.ts` | PixelSchema, PercentageSchema, UnitValueSchema |\n| `src/coordinates.ts` | FrameSchema, BoundsSchema, AnchorPointSchema |\n| `src/color.ts` | RGBAColorSchema, HSLAColorSchema, HexColorSchema, ColorSchema |\n| `src/shape.ts` | PathPointSchema, RectShapeSchema, EllipseShapeSchema, PathShapeSchema, ShapeSchema, GradientStopSchema, SolidFillSchema, LinearGradientFillSchema, RadialGradientFillSchema, FillSchema, StrokeSchema, TextStyleSchema |\n| `src/easing.ts` | LinearEasingSchema, CubicBezierEasingSchema, SpringEasingSchema, StepEasingSchema, EasingPresetSchema, EasingSchema |\n| `src/layer.ts` | ShapeVisualSchema, TextVisualSchema, ImageVisualSchema, GroupVisualSchema, RefVisualSchema, VisualSchema, LayerSchema |\n| `src/delta.ts` | AnimatablePropertySchema, FrameRangeSchema, DeltaSchema |\n| `src/state.ts` | StateSchema |\n| `src/preset.ts` | PresetDeltaSchema, PresetSchema |\n| `src/variable.ts` | VariableTypeSchema, VariableSchema |\n| `src/asset.ts` | AssetTypeSchema, AssetSchema |\n| `src/document.ts` | CanvasSchema, AtelierDocumentSchema |\n| `src/validate.ts` | validateDocument, validateLayer, validateDelta, ValidationResult, ValidationError |\n| `src/parse.ts` | parseAtelier, serializeAtelier |\n\n## Animatable Properties\n\nThe `AnimatablePropertySchema` defines every dot-path that can appear in a delta's `property` field:\n\n```\nframe.x           frame.y\nbounds.width       bounds.height\nopacity            rotation\nscale.x            scale.y\nanchorPoint.x      anchorPoint.y\nvisual.shape.cornerRadius\nvisual.fill.color  visual.stroke.color  visual.stroke.width\nvisual.style.fontSize  visual.style.color\n```\n\n## Design Decisions\n\n**Flat errors over Zod error trees.** Zod's native `ZodError` produces deeply nested issue objects with arrays of unions. The `formatErrors()` function flattens these into `{path, message}` pairs that are trivial to iterate, log, or pass to an LLM for interpretation.\n\n**Single result type for JSON and YAML.** Both `validateDocument()` and `parseAtelier()` return `ValidationResult<AtelierDocument>`, so consuming code handles one shape regardless of input format.\n\n**Discriminated unions for extensibility.** Shapes, fills, visuals, and easings all use Zod's `discriminatedUnion` on the `type` field, matching the tagged-union pattern in `@atelier/types`. Adding a new visual type means adding one schema variant to the union.\n\n**Schemas mirror types, not the other way around.** `@atelier/types` is the source of truth for the type system. Schemas here validate against those types at runtime but do not generate them. This keeps the types package dependency-free.\n","readmeFilename":"README.md"}