---
title: "Edit Operations"
description: "The complete EditOp catalog for dispatch(), can(), and batch()."
---
Every mutation in the SDK is expressed as an `EditOp` — a plain data object with a discriminated `type` field. You can submit ops individually through `dispatch()`, validate them with `can()`, or group them into a single undo/persist step with `batch()`.
Every element op requires an explicit `target` (an `HfId` string or `HfId[]` array). There is no selection-implicit mutation — the SDK never reads the current selection to decide what to edit. The typed methods on `Composition` (such as `comp.setText()` and `comp.setStyle()`) are convenience sugar that construct and dispatch these same ops.
## dispatch, batch, and can
### dispatch
```typescript
comp.dispatch(op: EditOp, opts?: { origin?: unknown }): void
```
Applies `op` immediately. Emits a `patch` event, persists if an adapter is attached, and records a history entry. The optional `origin` is forwarded verbatim in the resulting `PatchEvent`; use it to label the source of the change (e.g. `"user"`, `"agent"`, or your own string constant).
```typescript
comp.dispatch(
{ type: "setStyle", target: "hf-title", styles: { color: "#FFD60A" } },
{ origin: "agent" },
);
```
### batch
```typescript
comp.batch(fn: () => void, opts?: { origin?: unknown }): void
```
Groups all `dispatch()` calls made inside `fn` into a single undo step, a single persist write, and a single `patch` event. Use `batch()` when several mutations belong together logically.
```typescript
comp.batch(() => {
comp.dispatch({ type: "setText", target: "hf-title", value: "Launch Day" });
comp.dispatch({ type: "setStyle", target: "hf-title", styles: { fontSize: "96px" } });
comp.dispatch({ type: "setTiming", target: "hf-title", start: 0.5, duration: 3 });
});
```
### can
```typescript
comp.can(op: EditOp): CanResult
```
Dry-runs `op` without mutating the document. Returns `{ ok: true }` when `dispatch(op)` would succeed, or `{ ok: false; code: string; message: string; hint?: string }` when it would be a no-op or error.
Use `can()` as a feature-detection gate before rendering controls or applying optional operations:
```typescript
const result = comp.can({
type: "setGsapTween",
animationId: "anim-1",
properties: { ease: "power3.out" },
});
if (result.ok) {
comp.setGsapTween("anim-1", { ease: "power3.out" });
} else {
console.warn(result.code, result.message);
}
```
Stable `code` values for `ok: false`:
| Code | Meaning |
|------|---------|
| `E_TARGET_NOT_FOUND` | The `target` hf-id does not exist in the document. |
| `E_NO_ROOT` | The document has no root element (empty HTML). |
| `E_NO_GSAP_TIMELINE` | Op requires a parsed GSAP timeline; parser engine not yet active. |
| `E_NO_GSAP_SCRIPT` | Op requires a GSAP `