@ryhrm-gz/xincodo-lib
Version:
Utilities for working with Xincodo body documents.
226 lines (165 loc) • 5.6 kB
Markdown
---
name: building-and-parsing-body
description: >
Load when creating Xincodo Body data with createBody, paragraph, heading,
text, richText, list, table, pageLink, image, or gallery builders; when
handling Body block shapes; or when accepting persisted JSON with
safeMigrateBody instead of defaulting to parseBody.
type: core
library: "@ryhrm-gz/xincodo-lib"
library_version: "0.1.0"
sources:
- "ryhrm-gz/xincodo-lib:README.md"
- "ryhrm-gz/xincodo-lib:src/types.ts"
- "ryhrm-gz/xincodo-lib:src/builders.ts"
- "ryhrm-gz/xincodo-lib:src/schemas.ts"
- "ryhrm-gz/xincodo-lib:src/migration.ts"
- "ryhrm-gz/xincodo-lib:tests/builders.test.ts"
- "ryhrm-gz/xincodo-lib:tests/migration.test.ts"
---
# Building and Parsing Body
Use typed builders to create `Body` data. Treat saved or external JSON as
unknown input and pass it through `safeMigrateBody` before using it.
## Setup
```ts
import { createBody, heading, paragraph, safeMigrateBody, text } from "@ryhrm-gz/xincodo-lib";
const body = createBody([
heading(1, "Getting started", { id: "getting-started" }),
paragraph("Create content with ", text("typed helpers", { annotations: { bold: true } })),
]);
const migrated = safeMigrateBody(JSON.parse(JSON.stringify(body)));
if (!migrated.success) {
throw new Error(migrated.issue.message);
}
const currentBody = migrated.output;
```
## Core Patterns
### Build rich text with helper inputs
```ts
import { lineBreak, paragraph, text } from "@ryhrm-gz/xincodo-lib";
const intro = paragraph(
"Read ",
text("the docs", {
annotations: { underline: true },
link: { href: "https://example.com/docs", title: "Documentation" },
}),
lineBreak(),
"before publishing.",
);
```
`paragraph`, `heading`, `callout`, `quote`, `listItem`, and similar helpers
normalize strings into `{ type: "text" }` inline values.
### Build structured blocks through builders
```ts
import {
bulletedList,
callout,
createBody,
image,
listItem,
table,
tableCell,
tableRow,
} from "@ryhrm-gz/xincodo-lib";
const body = createBody([
bulletedList([listItem("Draft"), listItem("Review"), listItem("Publish")]),
callout("Remember image alt text.", {
icon: { type: "emoji", emoji: "!" },
color: "yellow",
}),
table([tableRow([tableCell("Status"), tableCell("Ready")])]),
image({ type: "file", url: "/uploads/cover.png" }, { alt: "Article cover" }),
]);
```
Prefer builders over handwritten JSON. Builders keep the serialized block
type names and nested shapes aligned with the library types.
### Accept persisted JSON with safeMigrateBody
```ts
import { safeMigrateBody } from "@ryhrm-gz/xincodo-lib";
export function readPersistedBody(value: unknown) {
const result = safeMigrateBody(value);
if (!result.success) {
return { ok: false as const, issue: result.issue };
}
return { ok: true as const, body: result.output, migrated: result.migrated };
}
```
`parseBody` is a lower-level schema API. Use `safeMigrateBody` at saved-data
or external-input boundaries so version handling is included.
### Use parseBody only for lower-level schema checks
```ts
import { parseBody } from "@ryhrm-gz/xincodo-lib";
const result = parseBody({
version: 1,
content: [],
});
if (!result.success) {
console.error(result.issues);
}
```
Use this when the calling code deliberately wants Valibot safe-parse issues
instead of migration-aware handling.
## Common Mistakes
### HIGH Defaulting to parseBody for saved JSON
Wrong:
```ts
import { parseBody } from "@ryhrm-gz/xincodo-lib";
const result = parseBody(JSON.parse(serializedBody));
if (!result.success) {
throw new Error("Invalid Xincodo body");
}
renderPreview(result.output);
```
Correct:
```ts
import { safeMigrateBody } from "@ryhrm-gz/xincodo-lib";
const result = safeMigrateBody(JSON.parse(serializedBody));
if (!result.success) {
throw new Error(result.issue.message);
}
renderPreview(result.output);
```
`parseBody` checks only the current schema; `safeMigrateBody` is the default
boundary for persisted or external Body JSON.
Source: maintainer interview; `src/schemas.ts`; `src/migration.ts`;
`tests/migration.test.ts`
### HIGH Handwriting display block names
Wrong:
```ts
import { safeMigrateBody } from "@ryhrm-gz/xincodo-lib";
const result = safeMigrateBody({
version: 1,
content: [{ type: "paragraph", richText: [{ type: "text", text: "Intro" }] }],
});
```
Correct:
```ts
import { createBody, paragraph } from "@ryhrm-gz/xincodo-lib";
const body = createBody([paragraph("Intro")]);
```
Serialized block type values are fixed snake_case literals such as `"text"`
and `"heading_1"`; builders avoid mismatching UI labels with stored shapes.
Source: maintainer interview; `src/types.ts`; `src/builders.ts`;
`tests/builders.test.ts`
### HIGH Omitting the Body version
Wrong:
```ts
import { paragraph, safeMigrateBody } from "@ryhrm-gz/xincodo-lib";
const result = safeMigrateBody({
content: [paragraph("Intro")],
});
```
Correct:
```ts
import { createBody, paragraph, safeMigrateBody } from "@ryhrm-gz/xincodo-lib";
const body = createBody([paragraph("Intro")]);
const result = safeMigrateBody(body);
```
`Body` values must include `version: 1`; missing or non-integer versions are
reported as invalid migration input.
Source: `src/types.ts`; `src/migration.ts`; `tests/migration.test.ts`
## References
- [Body block shapes](references/body-block-shapes.md)
- [Builder signatures](references/builder-signatures.md)
See also: `validating-body/SKILL.md` — migrated or built Body values often
need lint checks before save or publish.