npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ryhrm-gz/xincodo-lib

v0.1.0

Published

Utilities for working with Xincodo body documents.

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): bodySchemaunknown を検証し、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 install

Run format, lint, type check, and tests:

vp check
vp test

Build the library:

vp pack

Release

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 release

To choose the next version explicitly, pass a bumpp version argument:

vp run release -- patch
vp run release -- minor
vp run release -- major

The 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.