@ryhrm-gz/xincodo-lib
v0.1.0
Published
Utilities for working with Xincodo body documents.
Maintainers
Readme
xincodo-lib
Xincodo body documents を TypeScript で構築、検証、移行、走査、編集、テキスト抽出するためのユーティリティです。
Body は { version: 1, content: BodyBlock[] } というシンプルな構造で、本文ブロック、リッチテキスト、画像、テーブル、リンクなどを型付きで扱えます。
Installation
pnpm add @ryhrm-gz/xincodo-libこのパッケージは ESM として公開され、型定義は同梱されています。
If you use an AI agent, run npx @tanstack/intent@latest install.
Quick Start
import {
bulletedList,
callout,
createBody,
estimateReadingTime,
heading,
listItem,
paragraph,
text,
toPlainText,
} from "@ryhrm-gz/xincodo-lib";
const body = createBody([
heading(1, "Getting started", { id: "getting-started" }),
paragraph(
"Create structured content with ",
text("typed helpers", { annotations: { bold: true } }),
".",
),
bulletedList([listItem("Build documents"), listItem("Validate before saving")]),
callout("Remember to add image alt text.", {
icon: { type: "emoji", emoji: "!" },
color: "yellow",
}),
]);
const plainText = toPlainText(body);
const readingTime = estimateReadingTime(body);
console.log(plainText);
console.log(readingTime.minutes);Document Model
Body は現在 version: 1 のドキュメントです。本文は次の BodyBlock で表現します。
text: 段落。childrenに子ブロックを持てます。heading_1からheading_4: 見出し。bulleted_list,numbered_list,toggle_list: リスト。各 item はリッチテキストと子ブロックを持てます。callout,quote: 補足や引用。childrenに子ブロックを持てます。table: 行とセルを持つテーブル。divider: 区切り線。page_link: Xincodo 内ページ ID または URL への参照。image,gallery: 単一画像または画像ギャラリー。
リッチテキストは text() と lineBreak() で作成できます。text() には bold, italic, underline, strikethrough, color, backgroundColor, link などの注釈を付けられます。
import { lineBreak, paragraph, text } from "@ryhrm-gz/xincodo-lib";
const block = paragraph(
text("Docs", {
annotations: { underline: true },
link: { href: "https://example.com/docs", title: "Documentation" },
}),
lineBreak(),
"Read more",
);Usage Examples
Validate Unknown Input
外部入力や保存済み JSON は parseBody() で安全に検証できます。
import { parseBody } from "@ryhrm-gz/xincodo-lib";
const result = parseBody(JSON.parse(serializedBody));
if (!result.success) {
console.error(result.issues);
throw new Error("Invalid Xincodo body");
}
const body = result.output;将来のバージョン移行を見越した読み込みには safeMigrateBody() または migrateBody() を使います。
import { safeMigrateBody } from "@ryhrm-gz/xincodo-lib";
const result = safeMigrateBody(rawBody);
if (result.success) {
console.log(result.output);
} else {
console.error(result.issue.message);
}Lint Before Saving
lintBody() は重複 ID、不正 URL、空のテーブル、見出しレベルのジャンプ、画像 alt 欠落などを検出します。
import { lintBody } from "@ryhrm-gz/xincodo-lib";
const report = lintBody(body, {
maxDepth: 3,
requireImageAlt: true,
validateUrls: true,
});
if (!report.valid) {
console.log(report.errors);
}Extract Render Metadata
目次、抜粋、読了時間、画像、リンクなど、レンダリング前に必要になりやすい情報を抽出できます。
import {
collectImageSources,
collectLinks,
createExcerpt,
estimateReadingTime,
extractHeadingAnchors,
} from "@ryhrm-gz/xincodo-lib";
const headings = extractHeadingAnchors(body);
const excerpt = createExcerpt(body, { maxLength: 120, preserveWords: true });
const readingTime = estimateReadingTime(body);
const images = collectImageSources(body);
const links = collectLinks(body);Traverse And Edit Immutably
走査や編集ヘルパーは元の Body を変更せず、新しい Body を返します。
import {
findBlockPathById,
insertBlockAtPath,
paragraph,
richText,
updateRichTextAtPath,
} from "@ryhrm-gz/xincodo-lib";
const introPath = findBlockPathById(body, "intro");
const withNote = introPath
? insertBlockAtPath(body, introPath, paragraph("Inserted after intro"), {
position: "after",
})
: body;
const updatedIntro = introPath
? updateRichTextAtPath(body, [...introPath, { key: "richText" }], () => richText("Updated intro"))
: body;Public API
すべての API はパッケージルートから import できます。
import { createBody, parseBody, type Body } from "@ryhrm-gz/xincodo-lib";Builders
createBody(content):Bodyを作成します。text(value, options),lineBreak(),richText(...content): リッチテキストを作成します。paragraph(),heading(),callout(),quote(): テキスト系ブロックを作成します。listItem(),toggleListItem(),bulletedList(),numberedList(),toggleList(): リストを作成します。tableCell(),tableRow(),table(): テーブルを作成します。divider(),pageLink(),image(),galleryImage(),gallery(): その他のブロックを作成します。
Validation And Migration
parseBody(input):bodySchemaでunknownを検証し、Valibot の safe parse result を返します。bodySchema,bodyBlockSchema,bodyRichTextSchemaなど: Valibot schema を直接利用できます。safeMigrateBody(input): 移行結果を success/failure の union として返します。migrateBody(input): 移行できない場合はBodyMigrationErrorを投げます。
Lint
lintBody(input, options): 構造や編集上の問題をBodyLintReportとして返します。BodyLintIssue,BodyLintCode,BodyLintSeverity: lint 結果の型です。
Traversal And Editing
walkBody(input, visitor): block, richText, inline, listItem, tableCell, galleryImage を訪問します。findBlock(input, predicate),filterBlocks(input, predicate): 条件に合うブロックを検索します。mapBody(input, mapper): ブロックを再帰的に変換します。findBlockPathById(input, id),getBlockAtPath(input, path): パスでブロックを参照します。insertBlockAtRoot(),insertBlockAtPath(),updateBlockAtPath(),replaceBlockAtPath(),removeBlockAtPath(),moveBlock(): ブロックを immutable に編集します。updateRichTextAtPath(): 指定パスのリッチテキストを immutable に編集します。- 編集に失敗した場合は
BodyEditErrorを投げます。
Text And Rendering Helpers
toPlainText(input):Body, block 配列, block, rich text をプレーンテキストに変換します。richTextToPlainText(richText): リッチテキストだけを文字列化します。extractHeadings(input): 見出しの level, text, block, path を抽出します。extractHeadingAnchors(input, options): 目次やアンカー用の一意な ID と href を作成します。slugifyHeading(value): 見出し文字列を slug に変換します。createExcerpt(input, options): 抜粋を作成します。estimateReadingTime(input, options): 読了時間、単語数、文字数を推定します。collectImageSources(input): image, gallery image, callout icon の画像参照を集めます。collectLinks(input): text link と page link を集めます。
Constants And Types
BODY_VERSION: 現在の body version です。BODY_BLOCK_TYPES,TEXT_COLORS,HEADING_LEVELS: サポートされる値の一覧です。Body,BodyBlock,BodyRichText,BodyBlockType,TextAnnotations,TextLink,ImageSource,PageReferenceなどの公開型を利用できます。
Development
Install dependencies:
vp installRun format, lint, type check, and tests:
vp check
vp testBuild the library:
vp packRelease
Releases are published from a clean local main branch.
Prerequisites:
- Authenticate the GitHub CLI with
gh auth login. - Authenticate npm with
npm login.
Run a release:
vp run releaseTo choose the next version explicitly, pass a bumpp version argument:
vp run release -- patch
vp run release -- minor
vp run release -- majorThe release script checks that local main matches origin/main, runs checks and tests, bumps package.json, creates and pushes the release commit and vX.Y.Z tag, publishes GitHub release notes, then publishes the package to npm.
