cmx-sdk 0.2.7 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/add-studio-YUDYE2OH.js +0 -0
  2. package/dist/chunk-7TDMLYBI.js +0 -0
  3. package/dist/chunk-EDXXR5BE.js +0 -0
  4. package/dist/{chunk-S3TFTN6H.js → chunk-EZMBZWH7.js} +57 -0
  5. package/dist/chunk-FPQYL5GE.js +0 -0
  6. package/dist/chunk-IIQLQIDP.js +0 -0
  7. package/dist/chunk-NZQ6SBFS.js +0 -0
  8. package/dist/cli.js +567 -121
  9. package/dist/index.d.ts +343 -2
  10. package/dist/index.js +79 -31
  11. package/dist/index.js.map +1 -1
  12. package/dist/init-FLRQXJX4.js +0 -0
  13. package/dist/{interactive-menu-QKE6FMPN.js → interactive-menu-FYVOQSTL.js} +1 -1
  14. package/dist/studio-HAS6DYLO.js +0 -0
  15. package/dist/{update-sdk-ZDFOSMN4.js → update-sdk-KJZ6VB4M.js} +1 -1
  16. package/dist/update-studio-TWCYSYIS.js +0 -0
  17. package/package.json +16 -14
  18. package/templates/AGENTS.md +173 -0
  19. package/templates/CLAUDE.md +28 -0
  20. package/templates/claude/commands/check.md +64 -0
  21. package/templates/claude/commands/next-action.md +66 -0
  22. package/templates/claude/skills/cmx-cache/SKILL.md +50 -0
  23. package/templates/claude/skills/cmx-cache/references/cache-patterns.md +153 -0
  24. package/templates/claude/skills/cmx-component/SKILL.md +108 -0
  25. package/templates/claude/skills/cmx-component/references/component-schema.md +123 -0
  26. package/templates/claude/skills/cmx-content/SKILL.md +158 -0
  27. package/templates/claude/skills/cmx-content/references/migration-patterns.md +120 -0
  28. package/templates/claude/skills/cmx-content/references/seed-patterns.md +146 -0
  29. package/templates/claude/skills/cmx-dev/SKILL.md +266 -0
  30. package/templates/claude/skills/cmx-dev/references/api-patterns.md +220 -0
  31. package/templates/claude/skills/cmx-dev/references/cli-reference.md +54 -0
  32. package/templates/claude/skills/cmx-form/SKILL.md +103 -0
  33. package/templates/claude/skills/cmx-form/references/form-template.md +152 -0
  34. package/templates/claude/skills/cmx-migrate/SKILL.md +501 -0
  35. package/templates/claude/skills/cmx-migrate/references/analysis-guide.md +127 -0
  36. package/templates/claude/skills/cmx-migrate/references/html-to-mdx.md +99 -0
  37. package/templates/claude/skills/cmx-migrate/references/intermediate-format.md +196 -0
  38. package/templates/claude/skills/cmx-migrate/references/tool-setup.md +150 -0
  39. package/templates/claude/skills/cmx-schema/SKILL.md +159 -0
  40. package/templates/claude/skills/cmx-schema/references/field-types.md +164 -0
  41. package/templates/claude/skills/cmx-schema/references/migration-scenarios.md +44 -0
  42. package/templates/claude/skills/cmx-seo/SKILL.md +54 -0
  43. package/templates/claude/skills/cmx-seo/references/seo-patterns.md +216 -0
  44. package/templates/claude/skills/cmx-style/SKILL.md +48 -0
  45. package/templates/claude/skills/cmx-style/references/style-patterns.md +114 -0
File without changes
@@ -31,7 +31,7 @@ async function interactiveMenu() {
31
31
  return updateStudio({});
32
32
  }
33
33
  case "update-sdk": {
34
- const { updateSdk } = await import("./update-sdk-ZDFOSMN4.js");
34
+ const { updateSdk } = await import("./update-sdk-KJZ6VB4M.js");
35
35
  return updateSdk({});
36
36
  }
37
37
  case "studio": {
File without changes
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  resolveRequestedVersion,
4
4
  updateSdk
5
- } from "./chunk-S3TFTN6H.js";
5
+ } from "./chunk-EZMBZWH7.js";
6
6
  import "./chunk-EDXXR5BE.js";
7
7
  export {
8
8
  resolveRequestedVersion,
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cmx-sdk",
3
- "version": "0.2.7",
3
+ "version": "0.2.9",
4
4
  "description": "CMX SDK - Official SDK for building content-driven websites with CMX",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -26,6 +26,7 @@
26
26
  ],
27
27
  "files": [
28
28
  "dist",
29
+ "templates",
29
30
  "README.md"
30
31
  ],
31
32
  "exports": {
@@ -34,6 +35,19 @@
34
35
  "import": "./dist/index.js"
35
36
  }
36
37
  },
38
+ "scripts": {
39
+ "prebuild": "node scripts/copy-api-client.js",
40
+ "sync:starter-kit-docs": "pnpm --filter @cmx/api-server generate:openapi && node scripts/sync-starter-kit-docs.mjs --write",
41
+ "sync:starter-kit-docs:check": "pnpm --filter @cmx/api-server generate:openapi && node scripts/sync-starter-kit-docs.mjs --check",
42
+ "check:sdk-command-api-usage": "node scripts/check-sdk-command-api-usage.mjs",
43
+ "check:generated-sync": "node scripts/check-generated-sync.mjs",
44
+ "test:contracts": "tsx scripts/check-cli-contracts.ts",
45
+ "verify:release": "pnpm run check:sdk-command-api-usage && pnpm run check:generated-sync && pnpm run test:contracts",
46
+ "build": "tsup",
47
+ "dev": "tsup --watch",
48
+ "typecheck": "tsc --noEmit",
49
+ "prepublishOnly": "pnpm run verify:release && pnpm run sync:starter-kit-docs:check && pnpm run build"
50
+ },
37
51
  "dependencies": {
38
52
  "@inquirer/prompts": "^8.2.1",
39
53
  "@mdx-js/mdx": "^3.1.1",
@@ -56,17 +70,5 @@
56
70
  "tsup": "^8.5.1",
57
71
  "tsx": "^4.21.0",
58
72
  "typescript": "^5.9.3"
59
- },
60
- "scripts": {
61
- "prebuild": "node scripts/copy-api-client.js",
62
- "sync:starter-kit-docs": "pnpm --filter @cmx/api-server generate:openapi && node scripts/sync-starter-kit-docs.mjs --write",
63
- "sync:starter-kit-docs:check": "pnpm --filter @cmx/api-server generate:openapi && node scripts/sync-starter-kit-docs.mjs --check",
64
- "check:sdk-command-api-usage": "node scripts/check-sdk-command-api-usage.mjs",
65
- "check:generated-sync": "node scripts/check-generated-sync.mjs",
66
- "test:contracts": "tsx scripts/check-cli-contracts.ts",
67
- "verify:release": "pnpm run check:sdk-command-api-usage && pnpm run check:generated-sync && pnpm run test:contracts",
68
- "build": "tsup",
69
- "dev": "tsup --watch",
70
- "typecheck": "tsc --noEmit"
71
73
  }
72
- }
74
+ }
@@ -0,0 +1,173 @@
1
+ # CMX Starter Kit — AI Agent Instructions
2
+
3
+ このファイルはAIコーディングエージェント(Claude Code, Cursor, GitHub Copilot, Google Jules 等)向けの共通指示書です。
4
+
5
+ > **注意:** `<!-- cmx-sdk:start -->` ~ `<!-- cmx-sdk:end -->` の範囲は `npx cmx-sdk update-sdk` で自動更新されます。
6
+ > その範囲外はカスタマイズ可能です。
7
+
8
+ ## サイト設定
9
+
10
+ サイト固有の設定は以下を参照:
11
+
12
+ → cmx/site-config.md — 開発方針(種別・トーン・デザイン方針・禁止事項)
13
+ → cmx-workflow/style-guide.md — 執筆ルール(文体・用語・記事構成、別配布)
14
+
15
+ すべてのコード変更・提案は site-config.md の方針と矛盾しないことを確認すること。
16
+ コンテンツ生成時は style-guide.md のトーン・フォーマットに従うこと。
17
+
18
+ ---
19
+
20
+ <!-- cmx-sdk:start id="architecture" -->
21
+ ## アーキテクチャ
22
+
23
+ CMX Starter Kit はヘッドレスCMSのフロントエンドテンプレートです。
24
+
25
+ ```
26
+ CMX Admin(サービス側) Starter Kit(このリポジトリ)
27
+ ┌─────────────────────┐ ┌──────────────────────────┐
28
+ │ コレクション / データタイプ │ Public │ Next.js 16 (App Router) │
29
+ │ 投稿 / アセット / フォーム │ API │ cmx-sdk でデータ取得 │
30
+ │ │◄─────────│ Cloudflare Workers デプロイ │
31
+ │ APIキー ─────────────────────────│ .env の CMX_API_KEY で認証 │
32
+ └─────────────────────┘ └──────────────────────────┘
33
+ ```
34
+
35
+ ### 厳守ルール
36
+
37
+ - **すべてのデータ取得は `cmx-sdk` 経由。** DB直接アクセスは存在しない
38
+ - **サーバーコンポーネントがデフォルト。** クライアントコンポーネントは必要最小限に
39
+ - **CMXデータを取得するページには `export const dynamic = "force-dynamic"` を必ず付ける**
40
+ - **MDXレンダリングは `src/lib/mdx/render.tsx` の `renderMdx` を使う。** 独自のMDX処理を書かない
41
+ - **カスタムコンポーネントは `src/components/custom/index.ts` から export する。** export すれば MDX 内で自動的に使用可能になる
42
+ - **`cmx/generated/` 配下のファイルは手動編集禁止。** `npx cmx-sdk codegen types` で再生成する
43
+
44
+ ### 独自実装の禁止
45
+
46
+ - **独自の API クライアントや fetch ラッパーを作成しない。** `cmx-sdk` と `@/lib/` 配下の既存関数のみ使用する
47
+ - **`cmx-sdk` にない機能が必要な場合、独自実装せずユーザーに相談する**
48
+
49
+ ### エラー時の行動規範
50
+
51
+ 1. **1回リトライ**する
52
+ 2. リトライでも解決しない場合、**代替手段を勝手に実装せず、ユーザーに報告して確認を取る**
53
+ <!-- cmx-sdk:end -->
54
+
55
+ ---
56
+
57
+ <!-- cmx-sdk:start id="env-vars" -->
58
+ ## 環境変数
59
+
60
+ | 変数名 | 必須 | 説明 |
61
+ |--------|------|------|
62
+ | `CMX_API_KEY` | 必須 | CMX Admin で発行した API キー |
63
+ | `CMX_API_URL` | 必須 | CMX Admin インスタンスの URL |
64
+ | `CMX_WORKSPACE_ID` | 任意 | ワークスペースID(APIキーから自動判定) |
65
+ | `NEXT_PUBLIC_SITE_URL` | 必須 | 公開サイトの URL |
66
+ | `REVALIDATE_API_KEY` | 任意 | リバリデーション用の秘密鍵 |
67
+ <!-- cmx-sdk:end -->
68
+
69
+ ---
70
+
71
+ <!-- cmx-sdk:start id="commands" -->
72
+ ## コマンド
73
+
74
+ ```bash
75
+ pnpm dev # 開発サーバー起動(Turbopack, port 4000)
76
+ pnpm build # Next.js ビルド
77
+ pnpm build:cf # Cloudflare Workers 向けビルド
78
+ pnpm deploy # Cloudflare Workers デプロイ
79
+ pnpm typecheck # TypeScript 型チェック
80
+ pnpm lint # ESLint
81
+ npx cmx-sdk sync-components # カスタムコンポーネントを Admin に同期
82
+ npx cmx-sdk components scaffold --name FeatureCard # コンポーネント雛形を生成
83
+ npx cmx-sdk codegen types # スキーマから型付きコードを自動生成
84
+ npx cmx-sdk codegen pages --template layered # ページ雛形(resolver/meta/view分離)を生成
85
+ npx cmx-sdk codegen check # 生成コードと import/tsconfig の整合を検証
86
+ npx cmx-sdk codegen all # types → pages → check を一括実行
87
+ npx cmx-sdk mdx validate # MDX本文の構文/禁止構文/props を検証
88
+ npx cmx-sdk mdx doctor # MDX定義JSONと実装/exportの整合を検証
89
+ npx cmx-sdk create-collection --json '...' # コレクションを API 経由で作成
90
+ npx cmx-sdk create-data-type --json '...' # データタイプを API 経由で作成
91
+ npx cmx-sdk create-data-entry --type-slug {slug} --json '...' # データエントリを作成
92
+ npx cmx-sdk list-collection-data-types --collection {slug} # コレクションの付属データタイプ一覧
93
+ npx cmx-sdk list-collection-presets --type {type} # プリセット一覧(おすすめ/その他)
94
+ npx cmx-sdk update-sdk # SDK・コマンド・スキルを最新版に更新
95
+ ```
96
+ <!-- cmx-sdk:end -->
97
+
98
+ ---
99
+
100
+ <!-- cmx-sdk:start id="setup-flow" -->
101
+ ## 開発フロー
102
+
103
+ ### 初期構築
104
+
105
+ ```
106
+ 1. サイトコンフィグ作成 cmx/site-config.md を作成
107
+ 2. 環境セットアップ .env.local 設定 → pnpm install → pnpm dev
108
+ 3. スキーマ設計 コレクション・データタイプの JSON 定義を作成
109
+ → create-collection / create-data-type コマンドで登録
110
+ 4. テストデータ投入 Admin 側でコンテンツを作成し「公開」にする
111
+ 5. コード生成 npx cmx-sdk codegen types で型付き関数を生成
112
+ 6. 雛形生成(任意) npx cmx-sdk codegen pages --template layered
113
+ 7. ページ実装 一覧ページ・詳細ページ・静的ページを作成
114
+ 8. コンポーネント作成 MDX 用カスタムコンポーネントを定義・実装・同期
115
+ 9. デプロイ pnpm build:cf && pnpm deploy
116
+ ```
117
+ <!-- cmx-sdk:end -->
118
+
119
+ ---
120
+
121
+ <!-- cmx-sdk:start id="api-patterns" -->
122
+ ## API パターン
123
+
124
+ ### データ取得
125
+
126
+ ```tsx
127
+ // cmx-sdk 直接(汎用)
128
+ import { getCollectionContents, getCollectionContentDetail, getDataEntries, getDataEntry } from "cmx-sdk"
129
+
130
+ // re-export 経由(推奨)
131
+ import { getCollectionContents, getCollectionContentDetail, getDataEntries } from "@/lib/api/admin-client"
132
+
133
+ // エラー時 throw するラッパー(ページで使用)
134
+ import { requireFetchContents, requireFetchContent, requireDataEntries } from "@/lib/utils/data-fetching"
135
+
136
+ // 型付き自動生成関数(npx cmx-sdk codegen types 後)
137
+ import { getBlogContents, getBlogContentDetail } from "@cmx/generated"
138
+ import { getStaff, getStaffById } from "@cmx/generated"
139
+ ```
140
+
141
+ ### ページテンプレート: コレクション一覧
142
+
143
+ ```tsx
144
+ // src/app/{collection}/page.tsx
145
+ import { COLLECTION_SLUGS } from "@/lib/constants/collections"
146
+ import { requireFetchContents } from "@/lib/utils/data-fetching"
147
+
148
+ export const dynamic = "force-dynamic"
149
+
150
+ export default async function ListPage() {
151
+ const { collection, contents } = await requireFetchContents(COLLECTION_SLUGS.xxx)
152
+ return (/* 一覧 UI */)
153
+ }
154
+ ```
155
+
156
+ ### ページテンプレート: コレクション詳細
157
+
158
+ ```tsx
159
+ // src/app/{collection}/[slug]/page.tsx
160
+ import { COLLECTION_SLUGS } from "@/lib/constants/collections"
161
+ import { requireFetchContent } from "@/lib/utils/data-fetching"
162
+ import { renderMdx } from "@/lib/mdx/render"
163
+
164
+ export const dynamic = "force-dynamic"
165
+
166
+ export default async function DetailPage({ params }: { params: Promise<{ slug: string }> }) {
167
+ const { slug } = await params
168
+ const { content, references } = await requireFetchContent(COLLECTION_SLUGS.xxx, slug)
169
+ const { content: rendered } = await renderMdx(content.mdx, references)
170
+ return <article className="prose prose-lg max-w-none">{rendered}</article>
171
+ }
172
+ ```
173
+ <!-- cmx-sdk:end -->
@@ -0,0 +1,28 @@
1
+ # Claude Code 設定
2
+
3
+ > **注意:** `<!-- cmx-sdk:start -->` ~ `<!-- cmx-sdk:end -->` の範囲は `npx cmx-sdk update-sdk` で自動更新されます。
4
+ > その範囲外はカスタマイズ可能です。
5
+
6
+ <!-- cmx-sdk:start id="project-rules" -->
7
+ ## プロジェクトルール
8
+
9
+ CMX Starter Kit のルール・アーキテクチャ・API パターンは `AGENTS.md` を参照。
10
+
11
+ ### コミット前
12
+
13
+ 変更を加えたらコミット前に必ずチェックを実行:
14
+
15
+ ```bash
16
+ pnpm typecheck # TypeScript 型チェック
17
+ pnpm build # ビルド確認
18
+ npx cmx-sdk codegen check # 生成コード整合チェック
19
+ ```
20
+
21
+ または Studio の「チェック」ボタンを使用。
22
+ <!-- cmx-sdk:end -->
23
+
24
+ ---
25
+
26
+ ## プロジェクト固有の設定
27
+
28
+ <!-- ここにプロジェクト固有のルールやメモを追記してください -->
@@ -0,0 +1,64 @@
1
+ ---
2
+ name: Check
3
+ description: ビルド・型・整合性を一括チェック
4
+ disable-model-invocation: true
5
+ allowed-tools: Bash
6
+ continuation-mode: auto
7
+ ---
8
+
9
+ # Check(ビルド・整合性チェック)
10
+
11
+ ## 実行内容
12
+
13
+ 以下をこの順番で実行する。エラーがあれば即座に報告して止まる。
14
+
15
+ ### 1. TypeScript 型チェック
16
+
17
+ ```bash
18
+ pnpm typecheck
19
+ ```
20
+
21
+ ### 2. ビルド確認
22
+
23
+ ```bash
24
+ pnpm build
25
+ ```
26
+
27
+ ### 3. 生成コードと import/tsconfig の整合チェック
28
+
29
+ ```bash
30
+ npx cmx-sdk codegen check
31
+ ```
32
+
33
+ ## 出力フォーマット
34
+
35
+ 成功時:
36
+
37
+ ```
38
+ ## チェック結果
39
+
40
+ - [x] TypeScript: 型エラーなし
41
+ - [x] ビルド: 成功
42
+ - [x] 整合性: 問題なし
43
+
44
+ ✅ すべてのチェックが通りました。
45
+ ```
46
+
47
+ エラー時:
48
+
49
+ ```
50
+ ## チェック結果
51
+
52
+ - [x] TypeScript: 型エラーなし
53
+ - [ ] ビルド: ❌ エラー {n} 件
54
+ - [ ] 整合性: スキップ(ビルドエラーのため)
55
+
56
+ 修正すべき箇所:
57
+ 1. {ファイル名}:{行番号} - {エラー内容}
58
+ ```
59
+
60
+ ## ルール
61
+
62
+ - エラーが出たステップで止め、以降のステップはスキップする
63
+ - エラー内容は要約せず、そのまま出力する
64
+ - 修正案がある場合は箇条書きで提示する
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: Next Action
3
+ description: 迷ったときに現在地を確認し、次にやるべきことを案内
4
+ disable-model-invocation: true
5
+ allowed-tools: Bash, Read, Glob, Grep
6
+ continuation-mode: auto
7
+ ---
8
+
9
+ # Next Action
10
+
11
+ ## チェック項目
12
+
13
+ ### 環境
14
+
15
+ - `.env.local` が存在するか
16
+ - `CMX_API_KEY` が設定されているか
17
+
18
+ ### サイトコンフィグ
19
+
20
+ - `cmx/site-config.md` が記入済みか(「未設定」が残っていないか)
21
+
22
+ ### スキーマ
23
+
24
+ - `src/lib/constants/collections.ts` の登録スラッグ一覧
25
+ - `cmx/generated/` の生成済みファイル一覧
26
+ - 両者の不一致がないか
27
+
28
+ ### ページ
29
+
30
+ - `src/app/` 配下のページ一覧
31
+ - 登録済みコレクションに対応するページが全て存在するか
32
+ - データタイプに対応するページが存在するか
33
+
34
+ ### コンポーネント
35
+
36
+ - `cmx/components/` の JSON 定義一覧
37
+ - `src/components/custom/index.ts` のエクスポート一覧
38
+ - 定義と実装の不一致がないか
39
+
40
+ ## 判定ルール
41
+
42
+ - 未完了(✗ または △)の項目が1つでもある場合は、未完了優先で次アクションを案内する
43
+ - すべて完了(✓)の場合は、保守・改善フェーズ向けの追加提案を出す
44
+
45
+ ## 出力フォーマット
46
+
47
+ ```
48
+ ## Next Action(現在地)
49
+
50
+ - [x] 環境: 設定済み
51
+ - [ ] サイトコンフィグ: 一部未設定(未設定: {項目名...})
52
+ - [x] スキーマ: 整合済み(コレクション {n} / データタイプ {n})
53
+ - [ ] ページ: 未実装 {n} 件({slug...})
54
+ - [x] コンポーネント: 整合済み(定義 {n} / 実装 {n})
55
+
56
+ ### Next Action(最優先)
57
+ - {1件だけ。具体的なコマンド付きで提示}
58
+ 例: `/setup/05_pages` で未実装ページを作成する
59
+
60
+ ### 追加提案
61
+ - 未完了がある場合:
62
+ - 未完了項目に直結する提案を最大2件
63
+ - すべて完了している場合:
64
+ - 保守・改善に進む提案を最大2件
65
+ - 例: `/maintain/component`, `/maintain/style`, `/deploy`
66
+ ```
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: cmx-cache
3
+ description: |
4
+ CMX Starter Kit のキャッシュ戦略スキル。force-dynamic → ISR 切り替え、sdkFetchWithTags、CACHE_TAGS、リバリデーション設定、Cloudflare R2 キャッシュ。
5
+ トリガー: 「キャッシュを設定」「ISRに切り替え」「リバリデーションを設定」
6
+ 「force-dynamicを外す」「キャッシュタグを設定」「パフォーマンス改善」
7
+ 「ページの表示速度を上げたい」「キャッシュ戦略」「R2キャッシュ」
8
+ 「revalidateを設定」「オンデマンド再検証」など。
9
+ ---
10
+
11
+ # CMX キャッシュ戦略
12
+
13
+ ## 現状の構成
14
+
15
+ スターターキットの初期状態は全ページ `export const dynamic = "force-dynamic"`(毎リクエスト SSR)。ISR 基盤(R2 バケット、リバリデーション API)は構築済み。
16
+
17
+ ## ISR 移行の判断基準
18
+
19
+ | 条件 | 推奨 |
20
+ |------|------|
21
+ | コンテンツ更新頻度が低い | ISR(revalidate: 3600 等) |
22
+ | リアルタイム性が必要 | force-dynamic のまま |
23
+ | プレビューページ | 常に force-dynamic |
24
+ | 静的ページ(about 等) | ISR(revalidate: 86400)または静的生成 |
25
+
26
+ ## 移行手順
27
+
28
+ 詳細は [references/cache-patterns.md](references/cache-patterns.md) を参照。
29
+
30
+ ### 1. ページの revalidate 設定
31
+
32
+ `export const dynamic = "force-dynamic"` を削除し `export const revalidate = 秒数` に変更。
33
+
34
+ ### 2. タグ付き fetch に切り替え
35
+
36
+ `sdkFetchWithTags` を使い、CACHE_TAGS でタグを付与。
37
+
38
+ ### 3. リバリデーション API の確認
39
+
40
+ `/api/revalidate` エンドポイントが動作することを確認。`REVALIDATE_API_KEY` 環境変数が必要。
41
+
42
+ ### 4. Cloudflare R2 の確認
43
+
44
+ `wrangler.jsonc` で R2 バケットバインディング `NEXT_INC_CACHE_R2_BUCKET` が設定済みか確認。
45
+
46
+ ## 変更後
47
+
48
+ 1. デプロイ後にページの `Cache-Control` ヘッダーを確認
49
+ 2. コンテンツ更新後にリバリデーションが動作することを確認
50
+ 3. プレビューページが常に最新データを返すことを確認
@@ -0,0 +1,153 @@
1
+ # キャッシュ実装パターン
2
+
3
+ ## CACHE_TAGS
4
+
5
+ `@cmx/api-client/core` または `cmx-sdk` からインポート:
6
+
7
+ ```tsx
8
+ import { CACHE_TAGS } from "@/lib/api/admin-client"
9
+
10
+ CACHE_TAGS.collections // "collections" — 全コレクション
11
+ CACHE_TAGS.collection("blog") // "collection:blog" — 特定コレクション
12
+ CACHE_TAGS.content("blog", "hello") // "content:blog:hello" — 特定記事
13
+ CACHE_TAGS.data // "data" — 全データタイプ
14
+ CACHE_TAGS.dataType("faq") // "data:faq" — 特定データタイプ
15
+ ```
16
+
17
+ ## force-dynamic → ISR 移行
18
+
19
+ ### Before(毎リクエスト SSR)
20
+
21
+ ```tsx
22
+ // src/app/blog/page.tsx
23
+ export const dynamic = "force-dynamic"
24
+
25
+ export default async function BlogPage() {
26
+ const data = await getCollectionContents("blog")
27
+ // ...
28
+ }
29
+ ```
30
+
31
+ ### After(ISR + キャッシュタグ)
32
+
33
+ ```tsx
34
+ // src/app/blog/page.tsx
35
+ import { CACHE_TAGS, sdkFetchWithTags } from "@/lib/api/admin-client"
36
+
37
+ export const revalidate = 3600 // 1時間
38
+
39
+ export default async function BlogPage() {
40
+ const data = await sdkFetchWithTags<CollectionContentsResponse>(
41
+ `/sdk/collections/blog/contents`,
42
+ [CACHE_TAGS.collections, CACHE_TAGS.collection("blog")],
43
+ 3600
44
+ )
45
+ // ...
46
+ }
47
+ ```
48
+
49
+ ### 記事詳細ページ
50
+
51
+ ```tsx
52
+ // src/app/blog/[slug]/page.tsx
53
+ import { CACHE_TAGS, sdkFetchWithTags } from "@/lib/api/admin-client"
54
+
55
+ export const revalidate = 3600
56
+
57
+ export default async function BlogPostPage({ params }: PageProps) {
58
+ const { slug } = await params
59
+ const data = await sdkFetchWithTags<CollectionContentDetailResponse>(
60
+ `/sdk/collections/blog/contents/${slug}`,
61
+ [CACHE_TAGS.collection("blog"), CACHE_TAGS.content("blog", slug)],
62
+ 3600
63
+ )
64
+ // ...
65
+ }
66
+ ```
67
+
68
+ ## 推奨 revalidate 値
69
+
70
+ | ページ種類 | revalidate | 理由 |
71
+ |-----------|-----------|------|
72
+ | トップページ | 600(10分) | 新着表示の鮮度 |
73
+ | コレクション一覧 | 3600(1時間) | 記事追加は頻繁ではない |
74
+ | 記事詳細 | 3600(1時間) | 公開後の修正は稀 |
75
+ | データタイプ表示 | 3600(1時間) | 構造データの更新頻度 |
76
+ | 静的ページ(about等) | 86400(1日) | ほぼ変わらない |
77
+ | プレビュー | force-dynamic | 常に最新が必須 |
78
+
79
+ ## リバリデーション API
80
+
81
+ ### エンドポイント
82
+
83
+ `POST /api/revalidate`
84
+
85
+ ### 認証
86
+
87
+ ```
88
+ X-API-Key: {REVALIDATE_API_KEY}
89
+ ```
90
+
91
+ ### リクエスト例
92
+
93
+ ```bash
94
+ # 特定コレクションのキャッシュを無効化
95
+ curl -X POST https://yoursite.com/api/revalidate \
96
+ -H "Content-Type: application/json" \
97
+ -H "X-API-Key: your_secret_key" \
98
+ -d '{"tags": ["collection:blog"]}'
99
+
100
+ # 特定記事のキャッシュを無効化
101
+ curl -X POST https://yoursite.com/api/revalidate \
102
+ -H "Content-Type: application/json" \
103
+ -H "X-API-Key: your_secret_key" \
104
+ -d '{"tag": "content:blog:hello-world"}'
105
+ ```
106
+
107
+ ### CMX Admin からの連携
108
+
109
+ CMX Admin の Webhook 設定でリバリデーション API を呼び出す:
110
+
111
+ 1. Admin の Webhook 設定画面で URL に `https://yoursite.com/api/revalidate` を登録
112
+ 2. ヘッダーに `X-API-Key` を設定
113
+ 3. イベント(記事公開・更新・削除)ごとに対応するタグを送信
114
+
115
+ ## Cloudflare R2 キャッシュ構成
116
+
117
+ ### 設定ファイル
118
+
119
+ `open-next.config.ts`:
120
+
121
+ ```tsx
122
+ import { defineCloudflareConfig } from "@opennextjs/cloudflare"
123
+ import r2IncrementalCache from "@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache"
124
+
125
+ export default defineCloudflareConfig({
126
+ incrementalCache: r2IncrementalCache,
127
+ })
128
+ ```
129
+
130
+ ### wrangler.jsonc
131
+
132
+ ```jsonc
133
+ {
134
+ "r2_buckets": [
135
+ {
136
+ "binding": "NEXT_INC_CACHE_R2_BUCKET",
137
+ "bucket_name": "my-website-cache"
138
+ }
139
+ ]
140
+ }
141
+ ```
142
+
143
+ ### 環境ごとのバケット
144
+
145
+ - 本番: `my-website-cache`
146
+ - ステージング: `my-website-cache-stg`(`wrangler.jsonc` の `env.stg` で設定)
147
+
148
+ ## 環境変数チェックリスト
149
+
150
+ | 変数 | 用途 | 必須タイミング |
151
+ |------|------|--------------|
152
+ | `REVALIDATE_API_KEY` | リバリデーション API の認証キー | ISR 使用時 |
153
+ | `NEXT_INC_CACHE_R2_BUCKET`(バインディング) | R2 キャッシュバケット | Cloudflare デプロイ時 |