faceless-photolib

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

The main public entry point for faceless-photolib — a headless, color-managed, GPU-accelerated image-editing engine.

createEngine() gives you the engine capability surface: open a stored document, apply named commands to its layer stack, render a viewport region on a backend, export the document to an encoded image, subscribe to committed changes, and run headless crop/transform interactions. Every operation returns a canonical Result/Resource and never throws across the boundary — each request is safeParsed before any side effect. The same surface is also exposed over a JSON-RPC wire via createWireTransport(). In this layer only the CPU reference backend is wired in-process; requesting "webgpu" or "webgl2" returns backend-unavailable (never blank or substituted pixels).

Install

pnpm add @faceless-photolib/engine-api

Usage

import { createEngine, type Command } from "@faceless-photolib/engine-api";

const engine = createEngine();

// Validate + load a stored document payload (versioned, migrated, asset-checked).
const opened = await engine.openDocument(storedDocumentJson);
if (opened.kind !== "ok") throw new Error(`open failed: ${opened.kind}`);
const doc = opened.value;

// Edit the layer stack with a named command (immer produces a fresh document value).
const setOpacity: Command = { op: "setOpacity", layerId, opacity: 0.5 };
const edited = await engine.applyCommand(doc.id, setOpacity);

// Render a viewport region on the CPU reference backend; observe via a Resource.
const render = engine.renderRegion(doc.id, { x: 0, y: 0, width: 512, height: 512, scale: 1 }, "cpu");
if (render.status === "ready") {
  // render.value is the RenderResult
}

// Export the full canvas to an encoded image.
const exported = await engine.exportImage(doc.id, { format: "png" });
if (exported.kind === "ok") {
  // exported.value is the encoded Uint8Array
}

API

ExportDescription
createEngine(deps?)Constructs the in-process engine. Optional EngineDeps inject a resolveSource, codec, assetStore, textRasterizer, or surface.
EngineThe capability surface: openDocument, applyCommand, renderRegion, exportImage, subscribe, beginInteraction.
EngineDepsOptional dependencies wired into the engine (source resolver, codec, asset store, text rasterizer, surface).
ChangeEvent / SubscriptionEvents on the subscribe stream (documentChanged / renderProgress) and the unsubscribe handle.
InteractionSessionA headless crop/transform/straighten/resize session from beginInteraction (state / send / commit / cancel).
Command / CommandSchemaThe named document op-set (addLayer, removeLayer, reorder, setBlendMode, setOpacity, setFill, transform, addAdjustment, group, clip, setMask) plus its Zod schema.
createWireTransport(engine, opts?)Wraps an Engine as a JSON-RPC wire transport (WireMessage in, WireResponse out).
WIRE_VERSION / WireMessage / TransportRequest (+ schemas)The wire protocol version and message/request envelopes.
ok / notFound / invalidRequest / validationError / rejected / conflict / backendUnavailableCanonical result constructors, re-exported from the authority.
Result / Resource / Issue / RejectReasonThe canonical result vocabulary types.

engine-api is the semantic authority for the canonical result vocabulary; the Zod shapes live in @faceless-photolib/schemas and are re-exported here so callers import them from one place.

License

MIT

API reference

19 public exports · 15 documented · generated from source.

createEnginefunction
createEngine(deps?: EngineDeps): Engine

Construct the in-process engine. Optional {@link EngineDeps} inject the source resolver / codec / surface; the zero-arg `createEngine()` call stays valid (an image render then fails loud for want of resolvable pixels, never faked).

createWireTransportfunction
createWireTransport(engine: Engine, options?: WireTransportOptions): WireTransport

Create the wire transport over an existing in-process engine. If no `onNotify` is supplied, change events for wire `subscribe`-d documents have no channel to be pushed through — so the default forwarder fires a `warnNotImplemented` beacon naming the missing server→client notification message (the frozen `WireMessage` schema defines none) rather than dropping the event silently. Listed in notImplementedRemaining as the wire-notification channel.

Engineinterface
interface Engine

The engine capability surface. Identical in-process and over JSON-RPC. Every operation returns a canonical `Result`/`Resource`, never throws across the boundary, and validates its request with `safeParse` before any side effect.

EngineDepsinterface
interface EngineDeps

Dependencies the in-process engine wires together. All are optional so the frozen zero-arg `createEngine()` call sites stay source-compatible (mirroring `backend-cpu`'s `createBackend(resolveSource?)` — the sanctioned injection pattern for inputs the frozen signatures cannot carry). The frozen `GpuBackend` port and the `Engine` surface carry no asset-bytes ingestion point (CAS references assets by hash), and `codec.decode` is async while the CPU `SourceResolver` is synchronous — so source pixels must be injected up front. With no resolver, an image-layer render fails loud (`error`) exactly like `unresolvedSource()`, never fabricated pixels.

InteractionSessioninterface
interface InteractionSession

An in-progress interaction session opened by `beginInteraction`.

Subscriptioninterface
interface Subscription

Unsubscribe handle returned by `subscribe`.

WireTransportinterface
interface WireTransport

The wire transport: dispatch an untrusted message against the wrapped engine.

WireTransportOptionsinterface
interface WireTransportOptions

Options for {@link createWireTransport}.

ChangeEventtype
type ChangeEvent

A change event emitted on the `subscribe` stream.

Commandtype
type Command
InsertPositiontype
type InsertPosition
TransportRequesttype
type TransportRequest
WireMessagetype
type WireMessage
WireResponsetype
type WireResponse

A wire-dispatch outcome. Synchronous operations (`renderRegion`, `subscribe`, `beginInteraction`) and async ones (`openDocument`, `applyCommand`, `exportImage`) all flow through a single `Promise`-returning dispatch, so the transport surface is uniform. The payload is the canonical `Result`/`Resource` the matching `Engine` method returns; a session/ subscription handle is non-serializable, so the wire transport reports the establishment outcome as a canonical `Result` carrying the live handle for the in-process embedder (a network transport would substitute a token).

CommandSchemaconst
const CommandSchema

The named command op-set as a discriminated union keyed on `op`, mirroring document-model's `DocumentOp` 1:1 (engine-api spec: "ApplyCommand Exposes the Named Document Op-Set" lists all 11 ops). The original frozen stub carried only the seven scalar ops whose payloads need no full `Layer`/`InsertPosition` shape; the four structural ops (`addLayer`, `reorder`, `addAdjustment`, `group`) are added here so the typed `Command` covers the whole spec op-set and the acceptance test's `applyCommand(addLayer)` is expressible. The additive members leave every prior constructor unchanged.

InsertPositionSchemaconst
const InsertPositionSchema

Where to insert a layer relative to the stack/group (index 0 = bottom). The Zod mirror of document-model's `InsertPosition` so the structural-add ops (`addLayer`, `addAdjustment`) and `reorder` carry a `safeParse`-able target.

TransportRequestSchemaconst
const TransportRequestSchema

The full request union accepted at a transport boundary.

WIRE_VERSIONconst
WIRE_VERSION: 1

The wire protocol version this build advertises.

WireMessageSchemaconst
const WireMessageSchema

An envelope wrapping a request with the wire version for negotiation.

On this page