Deutsch
Operationen
Eine Operation ist eine Änderung an einem Template: einen Block hinzufügen, verschieben, löschen oder ändern, die Einstellungen ändern oder den ganzen Inhalt ersetzen. Dieselben sechs Operationen sind überall unterwegs, wo sich ein Template außerhalb eines einzelnen Editors ändert:
- Zusammenarbeit — ein Editor mit
init({ collaboration })schickt jede Änderung als Batch von Operationen an Ihr Backend und wendet die Batches anderer an. - Die CLI —
template-tools editwendet sie auf eine Template-Datei an. - Templatical Cloud — speichert jeden Batch mit seiner Revision, und mit seinem MCP-Server verbundene KI-Agenten bearbeiten mit demselben Vokabular über
edit_template.
applyOperations aus @templatical/types (MIT) wendet sie an. Der Server von Templatical Cloud verwendet eine Engine mit denselben Regeln, und gemeinsame Testfälle halten beide identisch.
Die sechs Operationen
json
{"op": "addBlock", "block": {"id": "b2", "type": "paragraph", "content": "<p>Hi</p>"}, "parent": null, "after": "b1"}
{"op": "moveBlock", "blockId": "b2", "parent": {"sectionId": "s1", "column": 0}, "after": null}
{"op": "deleteBlock", "blockId": "b2"}
{"op": "updateBlock", "blockId": "b1", "set": {"content": "<p>Hello</p>"}, "unset": ["visibility"]}
{"op": "updateSettings", "set": {"width": 640}, "unset": ["preheader"]}
{"op": "setContent", "content": {"blocks": [], "settings": {"width": 600}}}Ein Batch ist eine Liste von Operationen. Er wird der Reihe nach angewendet, jede Operation sieht die vorherigen, und alles oder nichts: Wird eine abgelehnt, gilt keine Operation des Batches.
Positionen
Positionen nennen einen Nachbarblock, nie einen Index, sodass eine Operation dasselbe bedeutet, auch wenn vorher die Änderung eines anderen ankommt.
parentistnullfür die oberste Ebene oder{"sectionId": "…", "column": 0}für eine Spalte einer Sektion.columnist ein 0-basierter Index in diechildrender Sektion.afterist die ID des Blocks, hinter dem der Block landet, odernull, damit er als erster landet. BeimoveBlockzählen die Nachbarn im Zielcontainer ohne den verschobenen Block.parentundaftersind beiaddBlockundmoveBlockPflicht, auch wenn sienullsind.
Sektionen und Spalten
- Eine Sektion steht nur auf der obersten Ebene. Eine Sektion in eine Spalte hinzuzufügen oder zu verschieben wird abgelehnt, ebenso eine neue Sektion mit einer Sektion unter ihren Kindern.
deleteBlockauf einer Sektion löscht ihre Blöcke mit.updateBlockersetzt jeden Schlüssel insetvollständig, einstyles-Objekt eingeschlossen, und entfernt die Schlüssel inunset.id,typeundchildrenlassen sich weder setzen noch entfernen, undcolumnseiner Sektion lässt sich nicht entfernen.set: {"columns": …}auf einer Sektion passt ihrechildrenan die Spaltenzahl des Layouts an:"1"hat eine Spalte,"3"hat drei, jedes andere Layout ("2","2-1","1-2") hat zwei. Beim Vergrößern kommen leere Spalten hinzu. Beim Verkleinern fallen hintere Spalten nur weg, wenn sie leer sind; verschieben Sie ihre Blöcke also zuerst.- Ein neuer Block braucht eine nicht leere String-
idund einen String-type, und jede ID darin, die Kinder einer neuen Sektion eingeschlossen, muss im Template neu sein.
Vorbedingungen
Jede Operation außer addBlock und setContent kann expect mitführen, das gelten muss, bevor sie angewendet wird. Bei einer Abweichung wird die Operation mit precondition_failed abgelehnt. Rückgängig nutzt das: Ein Rückgängig-Schritt, den die Änderung eines anderen überholt hat, wird abgelehnt, statt sie zu überschreiben.
updateBlockundupdateSettings:expectordnet Schlüsseln ihre erwarteten aktuellen Werte zu.nullpasst zu einemnull-Wert oder einem fehlenden Schlüssel.moveBlock:expect.parentundexpect.aftersind die aktuelle Position des Blocks. Ein fehlender Schlüssel zählt alsnull.deleteBlock:expect.blockist der ganze aktuelle Block, Kinder eingeschlossen, exakt verglichen.- Werte werden wie JSON verglichen: Die Reihenfolge der Objektschlüssel spielt keine Rolle, Arrays werden der Reihe nach verglichen und Zahlen nach Wert, sodass
1gleich1.0ist.set,unsetundexpectmit dem Wertnullzählen als nicht angegeben.
Sperren
Ein Backend übergibt die Sperren, die es hält, und die handelnde Sitzung: applyOperations(content, ops, { locks, session }), mit locks als [{ blockId, sessionId }].
updateBlock,moveBlockunddeleteBlockauf einem Block, den eine andere Sitzung hält, werden mitlock_heldabgelehnt. Beim Verschieben oder Löschen einer Sektion zählt auch jeder Block darin.setContentwird mitlocked_by_othersabgelehnt, solange eine andere Sitzung eine Sperre hält.- Mit
sessiongleichnull(eine serverseitige Änderung) gehört jede Sperre jemand anderem.
Fehler
Ein abgelehnter Batch wirft einen OperationError mit code und opIndex, dem 0-basierten Index der abgelehnten Operation.
| Code | Wann | Templatical Cloud antwortet |
|---|---|---|
block_exists | Die ID eines neuen Blocks oder eine ID darin gibt es im Template schon | 409 |
block_missing | Der Block oder die Eltern-Sektion fehlt | 409 |
anchor_missing | Der after-Block ist nicht im Zielcontainer | 409 |
precondition_failed | expect gilt nicht | 409 |
lock_held | Eine andere Sitzung hält den Block | 409 |
locked_by_others | Eine andere Sitzung hält eine Sperre, bei setContent | 409 |
invalid_operation | Die Operation ist fehlerhaft oder kann nie gelten | 422 |
Welcher Fehler gewinnt
Verletzt eine Operation mehrere Regeln, entscheidet die erste fehlschlagende Prüfung:
- Ihre Form (
invalid_operation): eine fehlerhafte Operation, ein fehlender Pflichtschlüssel, eine neue Sektion mit Eltern,id/type/childreninsetoderunset,columnseiner Sektion inunset. - Die Blöcke, die sie nennt (
block_missing,block_exists). - Sperren (
lock_held,locked_by_others). - Vorbedingungen (
precondition_failed). - Platzierung (
invalid_operation): eine Sektion in eine Spalte, ein Block hinter sich selbst. - Der Zielcontainer:
block_missingfür eine fehlende Eltern-Sektion;invalid_operationfür Eltern, die keine Sektion sind, oder eine Spalte außerhalb des Bereichs. - Der Anker (
anchor_missing). - Die Änderung selbst (
invalid_operation): ein Layout, das Spalten mit Blöcken wegfallen ließe.
Operationen anwenden
ts
import {
applyOperations,
OperationError,
type Operation,
} from '@templatical/types';
const ops: Operation[] = [
{ op: 'updateBlock', blockId: 'b1', set: { content: '<p>Hello</p>' } },
];
try {
const next = applyOperations(template.content, ops, {
locks: [{ blockId: 'b4', sessionId: 'f7c1…' }],
session: requestSessionId,
});
// `next` speichern
} catch (error) {
if (error instanceof OperationError) {
// error.code, error.opIndex
}
throw error;
}applyOperations liefert ein neues Dokument und verändert das übergebene nie. applyOperationsInPlace ändert es direkt, für eine Kopie, die Sie verwerfen können. getColumnCount(layout) ist die Layout-Regel von oben.
In der CLI
bash
npx -y @templatical/template-tools edit emails/welcome.json \
--op '{"op":"updateBlock","blockId":"button_1","set":{"backgroundColor":"#1d4ed8"}}'--ops <file> nimmt ein JSON-Array für einen Batch. Siehe Template Tools.