---
created: 2026-02-24T23:33:16.422Z
visibility: public
permanent: true
content_id: 01KJ8ZZX0709H02GT63HSKJ81J
modified: 2026-02-24T23:35:30.866Z
---

# ページスキーマ

ページのデータモデルについて。型定義は `types/page.ts`、RxDB スキーマは `utils/rxdb/pageSchema.ts` にある。

## Page インターフェース

`Page` は frontmatter の情報に加えて、パース済みの本文やアウトリンクを含む完全なページデータ。

主なフィールド:

- `path` — ファイルパス（拡張子なし）
- `slug` — 正規化済みの識別子（→ [information-architecture.md](./information-architecture.md)）
- `content_id` — ULID。リネーム追跡用の不変 ID
- `body` — パース済みの本文 AST（`MDCRoot`、en/ja の多言語対応）
- `source` — Markdown のソースコード（非 admin には非公開）
- `outlinks` — このページから出ている内部リンクの配列
- `redlink` — 赤リンクページかどうか（実体がなく、バックリンクのみで存在するページ）

## Frontmatter

YAML frontmatter のスキーマは Zod で定義している（`FrontmatterSchema`）。Obsidian 互換を意識したフォーマット。

```yaml
content_id: 01HQ3K5P0G3Z1VXYZ2ABC3DEF
title: Hello World
title_locale:
  ja: こんにちは、世界！
created: 2025-08-20
modified: 2025-08-21
visibility: public
permanent: true
layout: grid
sort_by: modified
sort_order: desc
tags:
  - video
  - diary
description: A sample page
thumbnail: https://example.com/image.jpg
thumbnail_list: https://example.com/image-list.jpg
```

主なフィールド:

- `content_id` — ULID。省略時は自動生成
- `title` — 文字列、または `{en, ja}` のオブジェクト
- `created` / `modified` — ISO 8601 の日付または日時
- `visibility` — `'public'` / `'unlisted'` / `'protected'` / `'private'`（→ [information-architecture.md](./information-architecture.md)）
- `thumbnail` — 基本のサムネイル画像
- `thumbnail_list` — 一覧表示用に優先されるサムネイル画像
- `permanent` — 永続化フラグ。`true` なら permanent page、`false` なら note page。特定のタグやレイアウトで自動推定される
- `theme` — CSS 変数の上書き。`color_bg` や `color_note_bg`、`radius_pill` などをページ単位で指定できる
- `layout` — `'page'`（デフォルト）/ `'grid'` / `'table'`
- `tags` — tag link の配列。slug に正規化されて outlinks に変換される
- `aliases` — ページの別名（リダイレクト用）
- `redirect` — 別ページへの転送

例:

```yaml
theme:
  radius_pill: 2rem
```

## Link 型

ページから出ていくリンクの型。

```typescript
interface Link {
  type: 'tag' | 'link'
  slug: string
  hash?: string
}
```

- `tag` — frontmatter の `tags` と、パスの親ディレクトリから暗黙的に生成
- `link` — 本文中のハイパーリンクから生成

## RxDB スキーマ

DB 上ではおおむね `PageDoc = {id: string, updatedAt: number, softDeleted: boolean, content: Page}` の形で保存される。`redlink` は実行時に導出されるフィールドなので DB には含まれない。

- `id` — RxDB の primary key。実体は `content.content_id` から導かれる
- `updatedAt` — 同期と競合解決のための内部タイムスタンプ。`Date.now()` ベースで更新される
- `softDeleted` — 論理削除フラグ。削除時もドキュメント自体は残り、`true` に切り替わる

`content` は `additionalProperties: true` にしている。MDC の AST のような複雑な JSON を RxDB の JSON Schema で完全にモデル化するのは現実的でないので。

主なインデックス: `content.content_id`, `content.slug`, `softDeleted`, `content.created`, `content.modified`, `content.visibility.type`, `updatedAt`

slug の重複チェックは `upsertPage` メソッド内で行っている。同じ `slug` で異なる `content_id` のドキュメントが既にあれば、パスに `_2`, `_3`, … のサフィックスを付けて自動リネームし、toast で通知する。

この重複チェックの対象になるのは `softDeleted: false` のドキュメントだけ。削除済みページの slug は再利用できる。
