@faceless-photolib/schemas
Zod schemas as the single source of truth for faceless-photolib types — branded IDs, the Result/Resource vocabulary, the Layer discriminated union, color spaces, transforms, and masks.
Zod schemas as the single source of truth for faceless-photolib — a headless, color-managed, GPU-accelerated image-editing engine.
Every cross-package type — branded IDs, the Result / Resource vocabulary, the Layer
discriminated union, color-space identifiers, transforms, and masks — is defined once here as a
Zod schema with its inferred TypeScript type. There are no optional or nullable fields; unions are
named and discriminated, so invalid states fail safeParse rather than slipping through.
Install
pnpm add @faceless-photolib/schemasUsage
import { match } from "ts-pattern";
import {
LayerIdSchema,
LayerSchema,
ok,
rejected,
type Result,
resource,
ready,
type Layer,
} from "@faceless-photolib/schemas";
// Branded IDs: a LayerId is not assignable from a bare string.
const id = LayerIdSchema.parse("layer-1");
// Validate untrusted input against the Layer discriminated union.
const parsed = LayerSchema.safeParse(input);
if (!parsed.success) {
// parsed.error.issues describes exactly what was wrong.
}
// Construct canonical results instead of throwing or returning null.
function lockedEdit(layer: Layer): Result<Layer> {
if (layer.locks.composite) return rejected("locked", "composite lock is active");
return ok(layer);
}
// Pattern-match the result; the union forces every variant to be handled.
const message = match(lockedEdit(layer))
.with({ kind: "ok" }, () => "applied")
.with({ kind: "rejected" }, (r) => `rejected: ${r.reason} — ${r.detail}`)
.otherwise(() => "other outcome");
// Resource<T> models async/heavy work with explicit idle/loading/ready/error states.
const renderSchema = resource(LayerSchema);
const state = ready(layer); // { status: "ready", value: layer }API
| Export | Description |
|---|---|
LayerIdSchema, DocumentIdSchema, AssetIdSchema, MaskIdSchema, ContentHashSchema | Branded string-ID schemas (with matching inferred types); structurally incompatible with each other. |
LayerSchema / Layer | Discriminated union (on type) of image, adjustment, lut3d, colorTransform, fill, text, and recursive group layers. |
result(value) / type Result<T> | Result-vocabulary factory and TS union (ok, not-found, invalid-request, validation-error, rejected, conflict, backend-unavailable). |
ok, notFound, invalidRequest, validationError, rejected, conflict, backendUnavailable | Variant constructors for Result<T>. |
issuesFromZod(error) | Maps a ZodError's issues into the canonical Issue[] shape. |
resource(value) / type Resource<T> | Async-lifecycle factory and TS union with idle, loading, ready, error. |
idle, loading, ready, errored | Variant constructors for Resource<T>. |
ColorSpaceSchema, ColorSpaceIdSchema, PrimariesSchema, TransferFnSchema, ChromaticitySchema | Color-management schemas (ACEScg-centric color spaces, primaries, transfer functions). |
BlendModeSchema, BlendSpaceSchema | Blend-mode and blend-space enums. |
TransformSchema, Mat3Schema, DecomposedTransformSchema, ResampleQualitySchema, IDENTITY_MAT3 | Geometry: affine transforms, 3×3 matrices, resample quality. |
MaskSchema / Mask, NO_MASK | Layer mask schema plus the canonical empty-mask constant. |
DocumentSchema, CanvasSizeSchema, DocumentSettingsSchema, CURRENT_SCHEMA_VERSION | Document model and current schema version. |
StoredDocumentSchema, DocV1Schema | On-disk file-format schemas. |
RenderResultSchema, ViewportSchema, SurfaceSchema | Render-output, viewport, and surface schemas. |
Each schema is paired with its z.infer TypeScript type (e.g. LayerSchema / Layer). engine-api
re-exports the Result / Resource vocabulary so leaf packages can construct outcomes without
depending on it.
License
MIT
API reference
87 public exports · 39 documented · generated from source.
conflictfunctionconflict<T = never>(what: string, detail: string): Result<T>erroredfunctionerrored<T = never>(message: string): Resource<T>idlefunctionidle<T = never>(): Resource<T>invalidRequestfunctioninvalidRequest<T = never>(issues: readonly { path: readonly (string | number)[]; message: string; code: string; }[]): Result<T>issuesFromZodfunctionissuesFromZod(error: ZodError<unknown>): readonly { path: readonly (string | number)[]; message: string; code: string; }[]Convert a `ZodError`'s issues into the canonical `Issue[]` shape.
loadingfunctionloading<T = never>(): Resource<T>notFoundfunctionnotFound<T = never>(what: string): Result<T>okfunctionok<T>(value: T): Result<T>readyfunctionready<T>(value: T): Resource<T>rejectedfunctionrejected<T = never>(reason: "not-implemented" | "locked" | "degenerate" | "unsupported" | "out-of-range" | "conflict", detail: string): Result<T>resourcefunctionresource<T extends z.ZodTypeAny>(value: T): ZodDiscriminatedUnion<[ZodObject<{ status: ZodLiteral<"idle">; }, $strip>, ZodObject<{ status: ZodLiteral<"loading">; }, $strip>, ZodObject<...>, ZodObject<...>], "status">`Resource<T>` — the lifecycle of asynchronous or heavy work (a render, an export). Held with `@observable.ref` in UI samples; every async UI action renders all four states distinctly (project.md §4, engine-api spec). Note the controller `idle` *lifecycle* state (interaction-controllers spec) is a different concept from this async `idle` — controllers are synchronous and have no `loading` phase.
resultfunctionresult<T extends z.ZodTypeAny>(value: T): ZodDiscriminatedUnion<[ZodObject<{ kind: ZodLiteral<"ok">; value: T; }, $strip>, ZodObject<{ kind: ZodLiteral<"not-found">; what: ZodString; }, $strip>, ... 4 more ..., ZodObject<...>], "kind">Generic result factory. `result(z.object({...}))` yields a Zod schema for the success payload plus every canonical failure variant; `z.infer` of it is the `Result<T>` TS type. Discriminated on `kind`, so callers `match(result).with({ kind: "ok" }, …).exhaustive()`.
validationErrorfunctionvalidationError<T = never>(fields: readonly { path: readonly (string | number)[]; message: string; code: string; }[]): Result<T>AdjustmentParamstypetype AdjustmentParamsAssetIdtypetype AssetIdBlendModetypetype BlendModeBlendSpacetypetype BlendSpaceCanvasSizetypetype CanvasSizeChromaticitytypetype ChromaticityColorSpacetypetype ColorSpaceColorSpaceIdtypetype ColorSpaceIdContentHashtypetype ContentHashDecomposedTransformtypetype DecomposedTransformDocumenttypetype DocumentDocumentIdtypetype DocumentIdDocumentSettingstypetype DocumentSettingsDocV1typetype DocV1FillContenttypetype FillContentGroupIsolationtypetype GroupIsolationGroupLayertypetype GroupLayerA group layer, expressed explicitly because of the recursive `children` field.
Issuetypetype IssueLayertypetype Layer`Layer` — a discriminated union on `type` with a shared base (document-model spec; D4). `group` is recursive (children are themselves `Layer`s) so groups nest to arbitrary depth, hence the `z.lazy` for the child stack. An unknown `type`, missing discriminant, or mismatched variant-field is unrepresentable: it fails `safeParse`.
LayerIdtypetype LayerIdLayerLockstypetype LayerLocksLayerTypetypetype LayerTypeMasktypetype MaskMaskIdtypetype MaskIdMat3typetype Mat3OutputTransformVersiontypetype OutputTransformVersionPrimariestypetype PrimariesRejectReasontypetype RejectReasonRenderResulttypetype RenderResultResampleQualitytypetype ResampleQualityResourcetypetype Resource`Resource<T>` as a plain TS discriminated union.
Resulttypetype Result`Result<T>` as a plain TS discriminated union, decoupled from any specific payload schema so signatures across the workspace can name `Result<Foo>` uniformly.
StoredDocumenttypetype StoredDocumentSurfacetypetype SurfaceTransferFntypetype TransferFnTransformtypetype TransformViewporttypetype ViewportAdjustmentParamsSchemaconstAdjustmentParamsSchema: ZodObject<{ effect: ZodString; params: ZodRecord<ZodString, ZodUnknown>; }, $strip>Adjustment payloads (interpreted by the `adjustments` package).
AssetIdSchemaconstAssetIdSchema: $ZodBranded<ZodString, "AssetId", "out">BlendModeSchemaconstconst BlendModeSchemaThe complete set of 27 blend modes (blend-compositing spec; D5). Order mirrors the spec's enumeration: 12 separable, then the Photoshop-only modes, then the 4 non-separable HSL modes. A value outside this set fails `safeParse` and returns `invalid-request` — never silently substituted with `normal`. Names are the camelCase identifiers used throughout the engine; the spec's display names map 1:1 (e.g. "Linear Dodge (Add)" → `linearDodge`).
BlendSpaceSchemaconstBlendSpaceSchema: ZodEnum<{ "linear-light": "linear-light"; perceptual: "perceptual"; }>Per-document toggle selecting the space blends are computed in (blend-compositing spec; D5): physically-correct linear-light vs Photoshop-parity perceptual.
CanvasSizeSchemaconstCanvasSizeSchema: ZodObject<{ width: ZodNumber; height: ZodNumber; }, $strip>Pixel dimensions of the document canvas.
ChromaticitySchemaconstChromaticitySchema: ZodObject<{ x: ZodNumber; y: ZodNumber; }, $strip>A CIE xy chromaticity coordinate.
ColorSpaceIdSchemaconstColorSpaceIdSchema: $ZodBranded<ZodString, "ColorSpaceId", "out">A color-space id, branded so an unknown id cannot masquerade as a resolved one.
ColorSpaceSchemaconstconst ColorSpaceSchemaA registered color space. `toReference`/`fromReference` are 3x3 row-major matrices.
ContentHashSchemaconstContentHashSchema: $ZodBranded<ZodString, "ContentHash", "out">A content-addressed asset hash (CAS). Assets are referenced by hash, never inlined.
CURRENT_SCHEMA_VERSIONconstCURRENT_SCHEMA_VERSION: 1The current persisted document schema version literal.
DecomposedTransformSchemaconstconst DecomposedTransformSchemaDecomposed transform components (W3C "unmatrix"). Transforms are *stored* decomposed so interactive handles stay editable and recomposed to a `Mat3` at render (transform-geometry spec; D8). This is a required named field of every `Transform`, never optional.
DocumentIdSchemaconstDocumentIdSchema: $ZodBranded<ZodString, "DocumentId", "out">DocumentSchemaconstconst DocumentSchemaThe in-memory `Document` model. Layers are an ordered stack — index 0 is the bottom-most, the last index is the top-most (document-model spec). Every edit produces a new `Document` value (immer); source pixels are never mutated.
DocumentSettingsSchemaconstconst DocumentSettingsSchemaDocument-level rendering settings (color-management + blend specs). `connectionSpace` is the working/reference space (ACEScg in v1); the blend space toggle and output-transform version are explicit, never inferred.
DocV1Schemaconstconst DocV1SchemaThe persisted file format (file-format spec; D10). A top-level `z.discriminatedUnion` keyed on the `schemaVersion` literal, with one historical schema variant retained for every version ever shipped. A payload lacking `schemaVersion`, or carrying an unrecognized literal, fails `safeParse` → `validation-error`; the engine never guesses a version. v1 is the only shipped version. `.passthrough()` preserves unknown forward-compatible fields so a load → no-op → save round-trip is byte-stable (the engine MUST NOT drop content it did not intentionally change).
FillContentSchemaconstconst FillContentSchemaFill content as a named union — solid, linear gradient, or radial gradient.
GroupIsolationSchemaconstGroupIsolationSchema: ZodEnum<{ isolated: "isolated"; "pass-through": "pass-through"; }>Group isolation mode — an explicit field; the engine never infers it.
IDENTITY_MAT3constIDENTITY_MAT3: [number, number, number, number, number, number, number, number, number]The 3x3 identity, row-major.
IssueSchemaconstconst IssueSchemaOne structured validation finding (mirrors a Zod issue without the union sprawl).
LayerIdSchemaconstLayerIdSchema: $ZodBranded<ZodString, "LayerId", "out">Branded string IDs. Branding makes a `LayerId` structurally incompatible with a `DocumentId` even though both are strings — you cannot pass one where the other is expected (project.md §4: brand IDs).
LayerLocksSchemaconstLayerLocksSchema: ZodObject<{ transparency: ZodBoolean; composite: ZodBoolean; position: ZodBoolean; }, $strip>Layer locks (document-model spec; D4). Each is an explicit boolean — an operation that violates an active lock returns `rejected` naming the lock, never silently applies or silently ignores.
LayerSchemaconstLayerSchema: ZodType<Layer, unknown, $ZodTypeInternals<Layer, unknown>>MaskIdSchemaconstMaskIdSchema: $ZodBranded<ZodString, "MaskId", "out">MaskSchemaconstconst MaskSchemaA layer mask as a discriminated union. The `none` variant means "no mask" — never `null`/`undefined` (document-model spec: "absent mask is a named variant, not null"). v1 supports raster masks; the raster bytes are referenced by content hash through the asset store.
Mat3SchemaconstMat3Schema: ZodTuple<[ZodNumber, ZodNumber, ZodNumber, ZodNumber, ZodNumber, ZodNumber, ZodNumber, ZodNumber, ZodNumber], null>`Mat3` — a 3x3 row-major matrix as a 9-tuple. Affine = last row [0,0,1]; perspective = full homography. Coordinate convention is fixed: top-left origin, Y-down (transform-geometry spec; D8). No backend may flip it.
NO_MASKconstconst NO_MASKThe "no mask" sentinel value, reused everywhere a default mask is needed.
OutputTransformVersionSchemaconstOutputTransformVersionSchema: ZodEnum<{ "aces-1.x": "aces-1.x"; "aces-2.0": "aces-2.0"; }>Selects the output-transform generation; ACES 1.x is the default (color-management spec).
PrimariesSchemaconstconst PrimariesSchemaRGB primaries + white point as CIE xy coordinates.
RejectReasonSchemaconstconst RejectReasonSchemaBusiness-rejection reasons. `not-implemented` is always paired with a beacon.
RenderResultSchemaconstconst RenderResultSchema`RenderResult` — pure pixel data produced by a render (the payload of a `Resource<RenderResult>`). Lives in `schemas` because it carries no effect-IR references, so every consumer (render-graph, backends, engine-api, color-conformance) can name `Resource<RenderResult>` without extra coupling. Pixels are f32 RGBA in the named color space; integer quantization only ever happens at the export boundary, never here (color-management spec).
ResampleQualitySchemaconstResampleQualitySchema: ZodEnum<{ bilinear: "bilinear"; "mipmap-trilinear": "mipmap-trilinear"; bicubic: "bicubic"; lanczos: "lanczos"; }>Resampling quality for a transformed layer (transform-geometry spec; D8).
StoredDocumentSchemaconstconst StoredDocumentSchemaThe versioned union. Add `DocV2Schema`, … here as new versions ship.
SurfaceSchemaconstSurfaceSchema: ZodEnum<{ web: "web"; expo: "expo"; server: "server"; mcp: "mcp"; "node-sdk": "node-sdk"; }>The transports/runtimes the one engine drives identically (project.md §6). `mcp` is a future surface but kept in the union, never dropped.
TransferFnSchemaconstconst TransferFnSchemaTransfer function as a discriminated union. `linear` is the connection-space EOTF; named curves carry their parameters explicitly. Unknown curves are a `not-implemented` rejection at resolve time, never a silent identity.
TransformSchemaconstconst TransformSchemaA transform is a discriminated union on `kind`. Both variants carry the recomposed `matrix` and the canonical `decomposed` components (a named field, not an optional) so storage keeps handles editable while render reads the matrix directly.
ViewportSchemaconstViewportSchema: ZodObject<{ x: ZodNumber; y: ZodNumber; width: ZodNumber; height: ZodNumber; scale: ZodNumber; }, $strip>A rectangular region of the canvas to render (top-left origin, Y-down).
@faceless-photolib/engine-api
The main public entry point for the faceless-photolib engine: open a document, edit its layer stack with named commands, and render/export through a pluggable backend (CPU reference today, GPU later).
@faceless-photolib/diagnostics
Runtime diagnostics for faceless-photolib — warnNotImplemented / warnDegraded / warnUnexpected beacons backed by a dedup registry, used instead of // TODO comments.