OutputData: формат сохранённого JSON
Структура данных, возвращаемая методом save(). Во входных позициях — опция конфигурации `data`, `render()`, `blocks.render()` и `blocks.insertMany()` — также принимаются нестрогие варианты `LooseOutputData` / `LooseOutputBlockData`, где `data`, `id` и `time` блока могут быть `null`: `null` в `data` становится `{}`, `null`/пустой `id` заменяется сгенерированным. Сохранённый вывод всегда имеет строгую форму.
Как получить экземпляр редактора
Методы ниже вызываются на редакторе, созданном через new Blok(). Они доступны после того, как разрешится editor.isReady.
// You already hold the instance returned by the constructor.
const editor = new Blok({ holder: 'editor' });
await editor.isReady;
// Call any API method on it.
editor.caret.setToLastBlock('end');Методы
equalsOutputData(a, b)
booleanСтруктурное равенство сохранённых документов, экспортируется из главной точки входа. Глубоко сравнивает массивы `blocks`; изменчивые поля-конверты `time` и `version` игнорируются, поэтому документ, прошедший через save(), равен своему эху. Принимаются nullish-документы и нестрогие форматы — `null`/`undefined` равны `{ blocks: [] }`.
Когда использовать
Используйте, чтобы сохранять или обновлять состояние только при реальных изменениях — time и version различаются при каждом save(), поэтому наивное глубокое сравнение всегда сообщает об изменении.
import { equalsOutputData } from '@bloklabs/core';
const saved = await editor.save();
if (!equalsOutputData(saved, previousData)) {
await persist(saved); // only hit the backend on real changes
}isEmptyOutputData(data)
booleanTrue, когда документ не содержит пользовательского контента, экспортируется из главной точки входа: он nullish, не имеет блоков, либо данные каждого блока содержат только пустые значения (пустые строки и строки из пробелов, пустые массивы/объекты). Числа и булевы значения (`level`, `checked`, стили) — презентационные метаданные и сами по себе контентом не считаются.
Когда использовать
Визуальные блоки без контента (например, разделитель с data: {}) считаются пустыми — проверяйте blocks.length, когда важно само наличие блоков.
import { isEmptyOutputData } from '@bloklabs/core';
const data = await editor.save();
submitButton.disabled = isEmptyOutputData(data);
// → true for a fresh editor holding one blank paragraphnormalizeOutputData(data)
OutputDataNormalizes a whole loose backend DTO into the strict saved OutputData shape, exported from the main entry. A nullish document becomes `{ blocks: [] }`; `null` envelope fields (`time`/`version`) are dropped; each block is normalized so `null`/missing `data` becomes `{}` and `null`/empty ids are dropped for regeneration. Unlike a hand-written `blocks.map(...)` mapper it preserves every passthrough field — `tunes`, `parent`, `content`, `indent`, edit metadata — so hierarchy and tunes are never silently lost. Idempotent: a strict document passes through unchanged.
import { normalizeOutputData } from '@bloklabs/core';
// A loose Editor.js-era DTO (data: null, id: null) from your backend
const strict = normalizeOutputData(dtoFromApi);
// → strict OutputData, safe to persist or diff — no blind `as OutputData` castnormalizeOutputBlocks(blocks)
OutputBlockData[]Block-level counterpart of normalizeOutputData, exported from the main entry: normalizes an array of loose wire blocks into the strict saved shape (`null`/missing `data` → `{}`, `null`/empty `id` dropped for regeneration) while passing every other field through untouched. Use normalizeOutputData when you hold the whole document envelope.
import { normalizeOutputBlocks } from '@bloklabs/core';
const blocks = normalizeOutputBlocks(looseBlocksFromApi);
// → OutputBlockData[] with tunes/parent/content/indent intactEMPTY_OUTPUT_DATA
OutputDataA shared, deeply frozen empty document (`{ blocks: [] }`), exported from the main entry. Use it in place of a hand-written `{ blocks: [] }` literal for cleared/pristine baselines. Frozen (blocks array included) so a shared reference can never be mutated into a stale non-empty baseline.
import { EMPTY_OUTPUT_DATA, equalsOutputData } from '@bloklabs/core';
const saved = await editor.save();
const isPristine = equalsOutputData(saved, EMPTY_OUTPUT_DATA, {
ignoreEmptyDefaultBlocks: true,
});toRenderableData(data)
OutputData | LooseOutputDataMaps a controlled `data` value to something render()/blocks.render() accepts, exported from the main entry: a whole-document `null` (a controlled "clear to empty") becomes `{ blocks: [] }`; any real document passes through untouched. render()'s strict guard reads `data.blocks` and would throw on `null`, so route a nullable controlled value through this first.
import { toRenderableData } from '@bloklabs/core';
// `draft` may be null when the host clears the document
await editor.blocks.render(toRenderableData(draft));createEmittedEchoWindow(capacity?)
{ record; matches; clear }Creates a bounded window of recently emitted onSave payloads for recognizing controlled-`data` echoes, exported from the main entry. Deduping against only the LAST emitted payload is not enough: a host that persists on save and refetches can hand back a STALE echo (an earlier save arriving after a newer one already replaced the baseline), and re-rendering it would clobber the caret and any content typed since. Matching is structural (equalsOutputData), so envelopes reshaped in transit (fresh `time`, stripped ids) still count as echoes.
import { createEmittedEchoWindow } from '@bloklabs/core';
const echoes = createEmittedEchoWindow();
// in onSave: echoes.record(data)
// before re-rendering incoming props: if (echoes.matches(next)) return;migrateLegacyBlocks(blocks)
OutputBlockData[]Migrate legacy / Editor.js-style blocks into Blok's hierarchical flat-with-references format, exported from the `@bloklabs/core/migrate` subpath — the same transform the renderer runs automatically at load. Legacy nested shapes (list items, toggle/callout children) explode into separate blocks linked by `parentId`/`content`, and id-less blocks are stamped with an id. Already-hierarchical blocks pass through unchanged, so it is safe to run on current data and idempotent across repeated runs. Use it to migrate a stored document explicitly — a one-off batch upgrade of persisted records, or a pre-load normalization step — instead of hand-rolling per-tool shape conversion. `migrateLegacyOutputData(data)` is the envelope-preserving variant, and `needsLegacyMigration(blocks)` reports whether a migration would change anything (skip the id-minting pass when the document is already current).
Когда использовать
For a data shape only a specific tool understands (a columns layout, a custom media envelope) that core's built-in migration can't read, give that tool a static upgradeData(data) — a pure function returning the tool's current data shape. Blok runs it at load, while composing each stored block, before the tool is constructed; a hook that throws is caught and the block loads with its stored data.
import {
migrateLegacyBlocks,
migrateLegacyOutputData,
needsLegacyMigration,
} from '@bloklabs/core/migrate';
// Batch-upgrade persisted Editor.js documents
const upgraded = migrateLegacyOutputData(storedDocument);
// Or migrate just the blocks, skipping the pass when already current
const blocks = needsLegacyMigration(stored.blocks)
? migrateLegacyBlocks(stored.blocks)
: stored.blocks;// Save editor content
const data = await editor.save();
// Result structure:
interface OutputData {
version?: string; // Editor version
time?: number; // Save timestamp
blocks: OutputBlockData[]; // Array of block data
}
// Example output:
{
"version": "1.4.2",
"time": 1704067200000,
"blocks": [
{
"id": "p6QK0Xz1Ab",
"type": "paragraph",
"data": { "text": "Hello, world!" }
},
{
"id": "hM3lTn9RdC",
"type": "header",
"data": { "text": "Title", "level": 2 }
}
]
}OutputData
| Свойство | Описание | |
|---|---|---|
version | string (optional) | Структура данных, возвращаемая методом save(). Во входных позициях — опция конфигурации `data`, `render()`, `blocks.render()` и `blocks.insertMany()` — также принимаются нестрогие варианты `LooseOutputData` / `LooseOutputBlockData`, где `data`, `id` и `time` блока могут быть `null`: `null` в `data` становится `{}`, `null`/пустой `id` заменяется сгенерированным. Сохранённый вывод всегда имеет строгую форму. |
time | number (optional) | Структура данных, возвращаемая методом save(). Во входных позициях — опция конфигурации `data`, `render()`, `blocks.render()` и `blocks.insertMany()` — также принимаются нестрогие варианты `LooseOutputData` / `LooseOutputBlockData`, где `data`, `id` и `time` блока могут быть `null`: `null` в `data` становится `{}`, `null`/пустой `id` заменяется сгенерированным. Сохранённый вывод всегда имеет строгую форму. |
blocks | OutputBlockData[] | Структура данных, возвращаемая методом save(). Во входных позициях — опция конфигурации `data`, `render()`, `blocks.render()` и `blocks.insertMany()` — также принимаются нестрогие варианты `LooseOutputData` / `LooseOutputBlockData`, где `data`, `id` и `time` блока могут быть `null`: `null` в `data` становится `{}`, `null`/пустой `id` заменяется сгенерированным. Сохранённый вывод всегда имеет строгую форму. |