faceless-photolib

@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/schemas

Usage

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

ExportDescription
LayerIdSchema, DocumentIdSchema, AssetIdSchema, MaskIdSchema, ContentHashSchemaBranded string-ID schemas (with matching inferred types); structurally incompatible with each other.
LayerSchema / LayerDiscriminated 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, backendUnavailableVariant 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, erroredVariant constructors for Resource<T>.
ColorSpaceSchema, ColorSpaceIdSchema, PrimariesSchema, TransferFnSchema, ChromaticitySchemaColor-management schemas (ACEScg-centric color spaces, primaries, transfer functions).
BlendModeSchema, BlendSpaceSchemaBlend-mode and blend-space enums.
TransformSchema, Mat3Schema, DecomposedTransformSchema, ResampleQualitySchema, IDENTITY_MAT3Geometry: affine transforms, 3×3 matrices, resample quality.
MaskSchema / Mask, NO_MASKLayer mask schema plus the canonical empty-mask constant.
DocumentSchema, CanvasSizeSchema, DocumentSettingsSchema, CURRENT_SCHEMA_VERSIONDocument model and current schema version.
StoredDocumentSchema, DocV1SchemaOn-disk file-format schemas.
RenderResultSchema, ViewportSchema, SurfaceSchemaRender-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.

backendUnavailablefunction
backendUnavailable<T = never>(detail: string): Result<T>
conflictfunction
conflict<T = never>(what: string, detail: string): Result<T>
erroredfunction
errored<T = never>(message: string): Resource<T>
idlefunction
idle<T = never>(): Resource<T>
invalidRequestfunction
invalidRequest<T = never>(issues: readonly { path: readonly (string | number)[]; message: string; code: string; }[]): Result<T>
issuesFromZodfunction
issuesFromZod(error: ZodError<unknown>): readonly { path: readonly (string | number)[]; message: string; code: string; }[]

Convert a `ZodError`'s issues into the canonical `Issue[]` shape.

loadingfunction
loading<T = never>(): Resource<T>
notFoundfunction
notFound<T = never>(what: string): Result<T>
okfunction
ok<T>(value: T): Result<T>
readyfunction
ready<T>(value: T): Resource<T>
rejectedfunction
rejected<T = never>(reason: "not-implemented" | "locked" | "degenerate" | "unsupported" | "out-of-range" | "conflict", detail: string): Result<T>
resourcefunction
resource<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.

resultfunction
result<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()`.

validationErrorfunction
validationError<T = never>(fields: readonly { path: readonly (string | number)[]; message: string; code: string; }[]): Result<T>
AdjustmentParamstype
type AdjustmentParams
AssetIdtype
type AssetId
BlendModetype
type BlendMode
BlendSpacetype
type BlendSpace
CanvasSizetype
type CanvasSize
Chromaticitytype
type Chromaticity
ColorSpacetype
type ColorSpace
ColorSpaceIdtype
type ColorSpaceId
ContentHashtype
type ContentHash
DecomposedTransformtype
type DecomposedTransform
Documenttype
type Document
DocumentIdtype
type DocumentId
DocumentSettingstype
type DocumentSettings
DocV1type
type DocV1
FillContenttype
type FillContent
GroupIsolationtype
type GroupIsolation
GroupLayertype
type GroupLayer

A group layer, expressed explicitly because of the recursive `children` field.

Issuetype
type Issue
Layertype
type 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`.

LayerIdtype
type LayerId
LayerLockstype
type LayerLocks
LayerTypetype
type LayerType
Masktype
type Mask
MaskIdtype
type MaskId
Mat3type
type Mat3
OutputTransformVersiontype
type OutputTransformVersion
Primariestype
type Primaries
RejectReasontype
type RejectReason
RenderResulttype
type RenderResult
ResampleQualitytype
type ResampleQuality
Resourcetype
type Resource

`Resource<T>` as a plain TS discriminated union.

Resulttype
type 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.

StoredDocumenttype
type StoredDocument
Surfacetype
type Surface
TransferFntype
type TransferFn
Transformtype
type Transform
Viewporttype
type Viewport
AdjustmentParamsSchemaconst
AdjustmentParamsSchema: ZodObject<{ effect: ZodString; params: ZodRecord<ZodString, ZodUnknown>; }, $strip>

Adjustment payloads (interpreted by the `adjustments` package).

AssetIdSchemaconst
AssetIdSchema: $ZodBranded<ZodString, "AssetId", "out">
BlendModeSchemaconst
const BlendModeSchema

The 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`).

BlendSpaceSchemaconst
BlendSpaceSchema: 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.

CanvasSizeSchemaconst
CanvasSizeSchema: ZodObject<{ width: ZodNumber; height: ZodNumber; }, $strip>

Pixel dimensions of the document canvas.

ChromaticitySchemaconst
ChromaticitySchema: ZodObject<{ x: ZodNumber; y: ZodNumber; }, $strip>

A CIE xy chromaticity coordinate.

ColorSpaceIdSchemaconst
ColorSpaceIdSchema: $ZodBranded<ZodString, "ColorSpaceId", "out">

A color-space id, branded so an unknown id cannot masquerade as a resolved one.

ColorSpaceSchemaconst
const ColorSpaceSchema

A registered color space. `toReference`/`fromReference` are 3x3 row-major matrices.

ContentHashSchemaconst
ContentHashSchema: $ZodBranded<ZodString, "ContentHash", "out">

A content-addressed asset hash (CAS). Assets are referenced by hash, never inlined.

CURRENT_SCHEMA_VERSIONconst
CURRENT_SCHEMA_VERSION: 1

The current persisted document schema version literal.

DecomposedTransformSchemaconst
const DecomposedTransformSchema

Decomposed 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.

DocumentIdSchemaconst
DocumentIdSchema: $ZodBranded<ZodString, "DocumentId", "out">
DocumentSchemaconst
const DocumentSchema

The 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.

DocumentSettingsSchemaconst
const DocumentSettingsSchema

Document-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.

DocV1Schemaconst
const DocV1Schema

The 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).

FillContentSchemaconst
const FillContentSchema

Fill content as a named union — solid, linear gradient, or radial gradient.

GroupIsolationSchemaconst
GroupIsolationSchema: ZodEnum<{ isolated: "isolated"; "pass-through": "pass-through"; }>

Group isolation mode — an explicit field; the engine never infers it.

IDENTITY_MAT3const
IDENTITY_MAT3: [number, number, number, number, number, number, number, number, number]

The 3x3 identity, row-major.

IssueSchemaconst
const IssueSchema

One structured validation finding (mirrors a Zod issue without the union sprawl).

LayerIdSchemaconst
LayerIdSchema: $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).

LayerLocksSchemaconst
LayerLocksSchema: 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.

LayerSchemaconst
LayerSchema: ZodType<Layer, unknown, $ZodTypeInternals<Layer, unknown>>
MaskIdSchemaconst
MaskIdSchema: $ZodBranded<ZodString, "MaskId", "out">
MaskSchemaconst
const MaskSchema

A 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.

Mat3Schemaconst
Mat3Schema: 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_MASKconst
const NO_MASK

The "no mask" sentinel value, reused everywhere a default mask is needed.

OutputTransformVersionSchemaconst
OutputTransformVersionSchema: 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).

PrimariesSchemaconst
const PrimariesSchema

RGB primaries + white point as CIE xy coordinates.

RejectReasonSchemaconst
const RejectReasonSchema

Business-rejection reasons. `not-implemented` is always paired with a beacon.

RenderResultSchemaconst
const 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).

ResampleQualitySchemaconst
ResampleQualitySchema: ZodEnum<{ bilinear: "bilinear"; "mipmap-trilinear": "mipmap-trilinear"; bicubic: "bicubic"; lanczos: "lanczos"; }>

Resampling quality for a transformed layer (transform-geometry spec; D8).

StoredDocumentSchemaconst
const StoredDocumentSchema

The versioned union. Add `DocV2Schema`, … here as new versions ship.

SurfaceSchemaconst
SurfaceSchema: 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.

TransferFnSchemaconst
const TransferFnSchema

Transfer 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.

TransformSchemaconst
const TransformSchema

A 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.

ViewportSchemaconst
ViewportSchema: 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).

On this page