@notion-headless-cms/core 0.3.11 → 0.3.13

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.
package/README.md CHANGED
@@ -47,6 +47,18 @@ Node.js 向けプリセット。`{ cache: { document: memoryDocumentCache, image
47
47
  - `ttlMs?`: SWR TTL (ミリ秒)
48
48
  - `renderer?`: カスタム renderer
49
49
 
50
+ ### `BaseContentItem` — 全コンテンツの基本型
51
+
52
+ CLI 生成の `createCMS` ラッパーで返される items は、スキーマで定義されたプロパティに加えて以下の自動フィールドを含む:
53
+
54
+ - `id: string` — Notion ページ ID
55
+ - `slug: string` — コレクション設定の `fields.slug` で指定したプロパティから抽出
56
+ - `title?: string | null` — Notion `title` 型プロパティ(自動検出)
57
+ - `updatedAt: string` — Notion ページの最終編集時刻(ISO-8601、キャッシュ更新判定に使用)
58
+ - `lastEditedTime?: string` — Notion の `page.last_edited_time` と同値。常にセットされるシステムフィールド(`updatedAt` と同じ値)
59
+ - `status?: string | null` — `fields.status` で指定したプロパティ
60
+ - `publishedAt?: string | null` — `fields.publishedAt` で指定したプロパティ
61
+
50
62
  ### `cms.<collection>` の主なメソッド
51
63
  - `getItem(slug)` — 本文込みで単件取得 (SWR)。返り値は `T & { content: { blocks, html(), markdown() } }`
52
64
  - `getList(opts?)` — 公開済み一覧 (本文なし)
@@ -1,2 +1,2 @@
1
- import { i as memoryCache, n as MemoryDocumentOptions, r as MemoryImageOptions, t as MemoryCacheOptions } from "../memory-CA1uTRbr.mjs";
1
+ import { i as memoryCache, n as MemoryDocumentOptions, r as MemoryImageOptions, t as MemoryCacheOptions } from "../memory-CxF7S-iB.mjs";
2
2
  export { MemoryCacheOptions, MemoryDocumentOptions, MemoryImageOptions, memoryCache };
@@ -1 +1 @@
1
- {"version":3,"file":"memory.mjs","names":[],"sources":["../../src/cache/memory.ts"],"sourcesContent":["import type {\n\tBaseContentItem,\n\tCacheAdapter,\n\tCachedItemContent,\n\tCachedItemList,\n\tCachedItemMeta,\n\tDocumentCacheOps,\n\tImageCacheOps,\n\tInvalidateScope,\n\tStorageBinary,\n} from \"../types/index\";\n\nexport interface MemoryDocumentOptions {\n\t/** アイテム保持上限。未指定時は上限なし。超過時は LRU で古いものから削除。 */\n\tmaxItems?: number;\n}\n\nexport interface MemoryImageOptions {\n\t/** エントリ保持上限。未指定時は上限なし。超過時は LRU で古いものから削除。 */\n\tmaxItems?: number;\n\t/** 合計保持サイズ上限(バイト)。未指定時は上限なし。超過時は LRU で古いものから削除。 */\n\tmaxSizeBytes?: number;\n}\n\nexport interface MemoryCacheOptions\n\textends MemoryDocumentOptions,\n\t\tMemoryImageOptions {}\n\n/**\n * Map の挿入順を LRU として扱う軽量実装。\n * `touch` で既存キーを末尾に移動、`enforceLimit` で古いキー(先頭)から削除する。\n */\nfunction touch<K, V>(map: Map<K, V>, key: K): void {\n\tconst v = map.get(key);\n\tif (v === undefined) return;\n\tmap.delete(key);\n\tmap.set(key, v);\n}\n\nconst itemKey = (collection: string, slug: string): string =>\n\t`${collection}:${slug}`;\n\n/** インメモリのドキュメントオペレーション実装。プロセス再起動でクリアされる。 */\nclass MemoryDocumentOps implements DocumentCacheOps {\n\t// biome-ignore lint/suspicious/noExplicitAny: 複数コレクション分の T を一括保持\n\tprivate lists = new Map<string, CachedItemList<any>>();\n\t// biome-ignore lint/suspicious/noExplicitAny: 同上\n\tprivate metas = new Map<string, CachedItemMeta<any>>();\n\tprivate contents = new Map<string, CachedItemContent>();\n\tprivate readonly maxItems: number | undefined;\n\n\tconstructor(options?: MemoryDocumentOptions) {\n\t\tthis.maxItems = options?.maxItems;\n\t}\n\n\tgetList<T extends BaseContentItem>(\n\t\tcollection: string,\n\t): Promise<CachedItemList<T> | null> {\n\t\treturn Promise.resolve(\n\t\t\t(this.lists.get(collection) as CachedItemList<T> | undefined) ?? null,\n\t\t);\n\t}\n\n\tsetList<T extends BaseContentItem>(\n\t\tcollection: string,\n\t\tdata: CachedItemList<T>,\n\t): Promise<void> {\n\t\tthis.lists.set(collection, data);\n\t\treturn Promise.resolve();\n\t}\n\n\tgetMeta<T extends BaseContentItem>(\n\t\tcollection: string,\n\t\tslug: string,\n\t): Promise<CachedItemMeta<T> | null> {\n\t\tconst key = itemKey(collection, slug);\n\t\tconst entry = this.metas.get(key) as CachedItemMeta<T> | undefined;\n\t\tif (entry) touch(this.metas, key);\n\t\treturn Promise.resolve(entry ?? null);\n\t}\n\n\tsetMeta<T extends BaseContentItem>(\n\t\tcollection: string,\n\t\tslug: string,\n\t\tdata: CachedItemMeta<T>,\n\t): Promise<void> {\n\t\tconst key = itemKey(collection, slug);\n\t\tif (this.metas.has(key)) this.metas.delete(key);\n\t\tthis.metas.set(key, data);\n\t\tthis.enforceLimit();\n\t\treturn Promise.resolve();\n\t}\n\n\tgetContent(\n\t\tcollection: string,\n\t\tslug: string,\n\t): Promise<CachedItemContent | null> {\n\t\tconst key = itemKey(collection, slug);\n\t\tconst entry = this.contents.get(key);\n\t\tif (entry) touch(this.contents, key);\n\t\treturn Promise.resolve(entry ?? null);\n\t}\n\n\tsetContent(\n\t\tcollection: string,\n\t\tslug: string,\n\t\tdata: CachedItemContent,\n\t): Promise<void> {\n\t\tconst key = itemKey(collection, slug);\n\t\tif (this.contents.has(key)) this.contents.delete(key);\n\t\tthis.contents.set(key, data);\n\t\tthis.enforceLimit();\n\t\treturn Promise.resolve();\n\t}\n\n\tinvalidate(scope: InvalidateScope): Promise<void> {\n\t\tif (scope === \"all\") {\n\t\t\tthis.lists.clear();\n\t\t\tthis.metas.clear();\n\t\t\tthis.contents.clear();\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\tconst kind = scope.kind ?? \"all\";\n\t\tconst collection = scope.collection;\n\t\tif (\"slug\" in scope) {\n\t\t\tconst key = itemKey(collection, scope.slug);\n\t\t\tif (kind === \"all\" || kind === \"meta\") this.metas.delete(key);\n\t\t\tif (kind === \"all\" || kind === \"content\") this.contents.delete(key);\n\t\t\t// 単一スラッグ無効化ではリストは触らない(リスト全体の整合は別管理)\n\t\t\treturn Promise.resolve();\n\t\t}\n\t\t// コレクション全体\n\t\tif (kind === \"all\" || kind === \"meta\") {\n\t\t\tthis.lists.delete(collection);\n\t\t\tconst prefix = `${collection}:`;\n\t\t\tfor (const key of [...this.metas.keys()]) {\n\t\t\t\tif (key.startsWith(prefix)) this.metas.delete(key);\n\t\t\t}\n\t\t}\n\t\tif (kind === \"all\" || kind === \"content\") {\n\t\t\tconst prefix = `${collection}:`;\n\t\t\tfor (const key of [...this.contents.keys()]) {\n\t\t\t\tif (key.startsWith(prefix)) this.contents.delete(key);\n\t\t\t}\n\t\t}\n\t\treturn Promise.resolve();\n\t}\n\n\tprivate enforceLimit(): void {\n\t\tif (this.maxItems === undefined) return;\n\t\twhile (this.metas.size > this.maxItems) {\n\t\t\tconst firstKey = this.metas.keys().next().value;\n\t\t\tif (firstKey === undefined) break;\n\t\t\tthis.metas.delete(firstKey);\n\t\t}\n\t\twhile (this.contents.size > this.maxItems) {\n\t\t\tconst firstKey = this.contents.keys().next().value;\n\t\t\tif (firstKey === undefined) break;\n\t\t\tthis.contents.delete(firstKey);\n\t\t}\n\t}\n}\n\n/** インメモリの画像オペレーション実装。 */\nclass MemoryImageOps implements ImageCacheOps {\n\tprivate store = new Map<string, StorageBinary>();\n\tprivate totalBytes = 0;\n\tprivate readonly maxItems: number | undefined;\n\tprivate readonly maxSizeBytes: number | undefined;\n\n\tconstructor(options?: MemoryImageOptions) {\n\t\tthis.maxItems = options?.maxItems;\n\t\tthis.maxSizeBytes = options?.maxSizeBytes;\n\t}\n\n\tget(hash: string): Promise<StorageBinary | null> {\n\t\tconst entry = this.store.get(hash);\n\t\tif (entry) touch(this.store, hash);\n\t\treturn Promise.resolve(entry ?? null);\n\t}\n\n\tset(hash: string, data: ArrayBuffer, contentType: string): Promise<void> {\n\t\tconst existing = this.store.get(hash);\n\t\tif (existing) {\n\t\t\tthis.totalBytes -= existing.data.byteLength;\n\t\t\tthis.store.delete(hash);\n\t\t}\n\t\tthis.store.set(hash, { data, contentType });\n\t\tthis.totalBytes += data.byteLength;\n\t\tthis.enforceLimit();\n\t\treturn Promise.resolve();\n\t}\n\n\tprivate enforceLimit(): void {\n\t\twhile (\n\t\t\t(this.maxItems !== undefined && this.store.size > this.maxItems) ||\n\t\t\t(this.maxSizeBytes !== undefined && this.totalBytes > this.maxSizeBytes)\n\t\t) {\n\t\t\tconst firstKey = this.store.keys().next().value;\n\t\t\tif (firstKey === undefined) break;\n\t\t\tconst victim = this.store.get(firstKey);\n\t\t\tif (victim) this.totalBytes -= victim.data.byteLength;\n\t\t\tthis.store.delete(firstKey);\n\t\t}\n\t}\n}\n\n/**\n * インメモリのキャッシュアダプタ。document + image 両方を担当する。\n * プロセス再起動でクリアされるため、ローカル開発・SSG ビルド・テスト用途。\n *\n * @example\n * cache: memoryCache({ ttlMs: 5 * 60_000, maxItems: 1000 })\n */\nexport function memoryCache(options?: MemoryCacheOptions): CacheAdapter {\n\treturn {\n\t\tname: \"memory\",\n\t\thandles: [\"document\", \"image\"] as const,\n\t\tdoc: new MemoryDocumentOps(options),\n\t\timg: new MemoryImageOps(options),\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"memory.mjs","names":[],"sources":["../../src/cache/memory.ts"],"sourcesContent":["import type {\n BaseContentItem,\n CacheAdapter,\n CachedItemContent,\n CachedItemList,\n CachedItemMeta,\n DocumentCacheOps,\n ImageCacheOps,\n InvalidateScope,\n StorageBinary,\n} from \"../types/index\";\n\nexport interface MemoryDocumentOptions {\n /** アイテム保持上限。未指定時は上限なし。超過時は LRU で古いものから削除。 */\n maxItems?: number;\n}\n\nexport interface MemoryImageOptions {\n /** エントリ保持上限。未指定時は上限なし。超過時は LRU で古いものから削除。 */\n maxItems?: number;\n /** 合計保持サイズ上限(バイト)。未指定時は上限なし。超過時は LRU で古いものから削除。 */\n maxSizeBytes?: number;\n}\n\nexport interface MemoryCacheOptions\n extends MemoryDocumentOptions,\n MemoryImageOptions {}\n\n/**\n * Map の挿入順を LRU として扱う軽量実装。\n * `touch` で既存キーを末尾に移動、`enforceLimit` で古いキー(先頭)から削除する。\n */\nfunction touch<K, V>(map: Map<K, V>, key: K): void {\n const v = map.get(key);\n if (v === undefined) return;\n map.delete(key);\n map.set(key, v);\n}\n\nconst itemKey = (collection: string, slug: string): string =>\n `${collection}:${slug}`;\n\n/** インメモリのドキュメントオペレーション実装。プロセス再起動でクリアされる。 */\nclass MemoryDocumentOps implements DocumentCacheOps {\n private lists = new Map<string, CachedItemList<BaseContentItem>>();\n private metas = new Map<string, CachedItemMeta<BaseContentItem>>();\n private contents = new Map<string, CachedItemContent>();\n private readonly maxItems: number | undefined;\n\n constructor(options?: MemoryDocumentOptions) {\n this.maxItems = options?.maxItems;\n }\n\n getList<T extends BaseContentItem>(\n collection: string,\n ): Promise<CachedItemList<T> | null> {\n return Promise.resolve(\n (this.lists.get(collection) as CachedItemList<T> | undefined) ?? null,\n );\n }\n\n setList<T extends BaseContentItem>(\n collection: string,\n data: CachedItemList<T>,\n ): Promise<void> {\n this.lists.set(collection, data);\n return Promise.resolve();\n }\n\n getMeta<T extends BaseContentItem>(\n collection: string,\n slug: string,\n ): Promise<CachedItemMeta<T> | null> {\n const key = itemKey(collection, slug);\n const entry = this.metas.get(key) as CachedItemMeta<T> | undefined;\n if (entry) touch(this.metas, key);\n return Promise.resolve(entry ?? null);\n }\n\n setMeta<T extends BaseContentItem>(\n collection: string,\n slug: string,\n data: CachedItemMeta<T>,\n ): Promise<void> {\n const key = itemKey(collection, slug);\n if (this.metas.has(key)) this.metas.delete(key);\n this.metas.set(key, data);\n this.enforceLimit();\n return Promise.resolve();\n }\n\n getContent(\n collection: string,\n slug: string,\n ): Promise<CachedItemContent | null> {\n const key = itemKey(collection, slug);\n const entry = this.contents.get(key);\n if (entry) touch(this.contents, key);\n return Promise.resolve(entry ?? null);\n }\n\n setContent(\n collection: string,\n slug: string,\n data: CachedItemContent,\n ): Promise<void> {\n const key = itemKey(collection, slug);\n if (this.contents.has(key)) this.contents.delete(key);\n this.contents.set(key, data);\n this.enforceLimit();\n return Promise.resolve();\n }\n\n invalidate(scope: InvalidateScope): Promise<void> {\n if (scope === \"all\") {\n this.lists.clear();\n this.metas.clear();\n this.contents.clear();\n return Promise.resolve();\n }\n const kind = scope.kind ?? \"all\";\n const collection = scope.collection;\n if (\"slug\" in scope) {\n const key = itemKey(collection, scope.slug);\n if (kind === \"all\" || kind === \"meta\") this.metas.delete(key);\n if (kind === \"all\" || kind === \"content\") this.contents.delete(key);\n // 単一スラッグ無効化ではリストは触らない(リスト全体の整合は別管理)\n return Promise.resolve();\n }\n // コレクション全体\n if (kind === \"all\" || kind === \"meta\") {\n this.lists.delete(collection);\n const prefix = `${collection}:`;\n for (const key of [...this.metas.keys()]) {\n if (key.startsWith(prefix)) this.metas.delete(key);\n }\n }\n if (kind === \"all\" || kind === \"content\") {\n const prefix = `${collection}:`;\n for (const key of [...this.contents.keys()]) {\n if (key.startsWith(prefix)) this.contents.delete(key);\n }\n }\n return Promise.resolve();\n }\n\n private enforceLimit(): void {\n if (this.maxItems === undefined) return;\n while (this.metas.size > this.maxItems) {\n const firstKey = this.metas.keys().next().value;\n if (firstKey === undefined) break;\n this.metas.delete(firstKey);\n }\n while (this.contents.size > this.maxItems) {\n const firstKey = this.contents.keys().next().value;\n if (firstKey === undefined) break;\n this.contents.delete(firstKey);\n }\n }\n}\n\n/** インメモリの画像オペレーション実装。 */\nclass MemoryImageOps implements ImageCacheOps {\n private store = new Map<string, StorageBinary>();\n private totalBytes = 0;\n private readonly maxItems: number | undefined;\n private readonly maxSizeBytes: number | undefined;\n\n constructor(options?: MemoryImageOptions) {\n this.maxItems = options?.maxItems;\n this.maxSizeBytes = options?.maxSizeBytes;\n }\n\n get(hash: string): Promise<StorageBinary | null> {\n const entry = this.store.get(hash);\n if (entry) touch(this.store, hash);\n return Promise.resolve(entry ?? null);\n }\n\n set(hash: string, data: ArrayBuffer, contentType: string): Promise<void> {\n const existing = this.store.get(hash);\n if (existing) {\n this.totalBytes -= existing.data.byteLength;\n this.store.delete(hash);\n }\n this.store.set(hash, { data, contentType });\n this.totalBytes += data.byteLength;\n this.enforceLimit();\n return Promise.resolve();\n }\n\n private enforceLimit(): void {\n while (\n (this.maxItems !== undefined && this.store.size > this.maxItems) ||\n (this.maxSizeBytes !== undefined && this.totalBytes > this.maxSizeBytes)\n ) {\n const firstKey = this.store.keys().next().value;\n if (firstKey === undefined) break;\n const victim = this.store.get(firstKey);\n if (victim) this.totalBytes -= victim.data.byteLength;\n this.store.delete(firstKey);\n }\n }\n}\n\n/**\n * インメモリのキャッシュアダプタ。document + image 両方を担当する。\n * プロセス再起動でクリアされるため、ローカル開発・SSG ビルド・テスト用途。\n *\n * @example\n * cache: memoryCache({ ttlMs: 5 * 60_000, maxItems: 1000 })\n */\nexport function memoryCache(options?: MemoryCacheOptions): CacheAdapter {\n return {\n name: \"memory\",\n handles: [\"document\", \"image\"] as const,\n doc: new MemoryDocumentOps(options),\n img: new MemoryImageOps(options),\n };\n}\n"],"mappings":"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"}
@@ -86,18 +86,28 @@ interface ImageRef {
86
86
  * createCMS<Post>({ source: createNotionCollection({ ... }) })
87
87
  */
88
88
  interface BaseContentItem {
89
- /** Notion ページ ID(変更検知に必須)。 */
89
+ /** ページ ID(変更検知に必須)。 */
90
90
  id: string;
91
91
  /** URL キー(必須)。 */
92
92
  slug: string;
93
- /** Notion ページ名(title 型プロパティのテキスト)。 */
93
+ /** ページ名(title 型プロパティのテキスト)。 */
94
94
  title?: string | null;
95
- /** 最終更新タイムスタンプ(変更検知に必須)。 */
96
- updatedAt: string;
97
- /** コンテンツのステータス。ステータスのない DB では省略可能。Notion の select 型は null を返す場合がある。 */
95
+ /** ページの最終編集日時(Notion の last_edited_time、変更検知に必須)。 */
96
+ lastEditedTime: string;
97
+ /** コンテンツのステータス。ステータスのない DB では省略可能。 */
98
98
  status?: string | null;
99
- /** 公開日時。日付プロパティのない DB では省略可能。Notion の date 型は null を返す場合がある。 */
99
+ /** 公開日時。日付プロパティのない DB では省略可能。 */
100
100
  publishedAt?: string | null;
101
+ /** ページ作成日時(ISO8601)。 */
102
+ createdAt?: string;
103
+ /** Notion の archived フラグ (手動アーカイブ)。list() から自動的に除外される。 */
104
+ isArchived?: boolean;
105
+ /** Notion の in_trash フラグ (ゴミ箱)。list() から自動的に除外される。 */
106
+ isInTrash?: boolean;
107
+ /** カバー画像の URL。cover が設定されていない場合は null。 */
108
+ coverImageUrl?: string | null;
109
+ /** 絵文字アイコン。icon が絵文字でない場合や未設定の場合は null。 */
110
+ iconEmoji?: string | null;
101
111
  }
102
112
  /**
103
113
  * メタデータのみの軽量キャッシュエントリ。
@@ -143,4 +153,4 @@ interface CMSSchemaProperties {
143
153
  }
144
154
  //#endregion
145
155
  export { CachedItemMeta as a, ContentResult as c, CachedItemList as i, ImageRef as l, CMSSchemaProperties as n, StorageBinary as o, CachedItemContent as r, ContentBlock as s, BaseContentItem as t, InlineNode as u };
146
- //# sourceMappingURL=content-Bffid8da.d.mts.map
156
+ //# sourceMappingURL=content-D7PfY-bV.d.mts.map
@@ -0,0 +1,66 @@
1
+ //#region src/errors.d.ts
2
+ /**
3
+ * ライブラリ組み込みの CMS エラーコード。
4
+ *
5
+ * | コード | 発生条件 |
6
+ * |---|---|
7
+ * | `core/config_invalid` | 設定不備(token 未設定など) |
8
+ * | `core/schema_invalid` | schema/mapping の型不整合 |
9
+ * | `core/notion_orm_missing` | `@notion-headless-cms/notion-orm` の動的ロード失敗 |
10
+ * | `core/sort_unsupported_type` | ソートキーの値型が string / number でない |
11
+ * | `webhook/signature_invalid` | Webhook 署名検証失敗 |
12
+ * | `webhook/payload_invalid` | Webhook ペイロード形式不正 |
13
+ * | `webhook/unknown_collection` | Webhook の対象コレクションが未知 |
14
+ * | `webhook/not_implemented` | DataSource が parseWebhook を実装していない |
15
+ * | `source/fetch_items_failed` | `DataSource.list()` 失敗 |
16
+ * | `source/fetch_item_failed` | `DataSource.findByProp()` 失敗 |
17
+ * | `source/load_markdown_failed` | `DataSource.loadMarkdown()` 失敗 |
18
+ * | `source/load_blocks_failed` | `DataSource.loadBlocks()` 失敗 |
19
+ * | `cache/io_failed` | document / image キャッシュの I/O 失敗 |
20
+ * | `cache/image_fetch_failed` | Notion 画像の HTTP 取得失敗 |
21
+ * | `cache/image_invalid_content_type` | 画像レスポンスの Content-Type が不正 |
22
+ * | `renderer/failed` | Markdown → HTML 変換失敗 |
23
+ * | `swr/item_check_failed` | SWR バックグラウンドのアイテム差分チェック失敗 |
24
+ * | `swr/list_check_failed` | SWR バックグラウンドのリスト差分チェック失敗 |
25
+ * | `swr/content_rebuild_failed` | SWR バックグラウンドの本文再生成失敗 |
26
+ * | `cli/config_invalid` | `nhc.config.ts` の内容不整合 |
27
+ * | `cli/config_load_failed` | 設定ファイルの読み込み / 評価失敗 |
28
+ * | `cli/schema_invalid` | CLI が受け取ったスキーマ / マッピング不整合 |
29
+ * | `cli/generate_failed` | `nhc generate` の処理失敗 |
30
+ * | `cli/init_failed` | `nhc init` の処理失敗 |
31
+ * | `cli/notion_api_failed` | CLI が Notion API を呼び出す際の失敗 |
32
+ * | `cli/env_file_not_found` | `--env-file` で指定したファイルが存在しない |
33
+ *
34
+ * サードパーティアダプタが独自コードを追加したい場合は `CMSErrorCode` を参照。
35
+ */
36
+ type BuiltInCMSErrorCode = "core/config_invalid" | "core/schema_invalid" | "core/notion_orm_missing" | "core/sort_unsupported_type" | "webhook/signature_invalid" | "webhook/payload_invalid" | "webhook/unknown_collection" | "webhook/not_implemented" | "source/fetch_items_failed" | "source/fetch_item_failed" | "source/load_markdown_failed" | "source/load_blocks_failed" | "cache/io_failed" | "cache/image_fetch_failed" | "cache/image_invalid_content_type" | "renderer/failed" | "swr/item_check_failed" | "swr/list_check_failed" | "swr/content_rebuild_failed" | "cli/config_invalid" | "cli/config_load_failed" | "cli/schema_invalid" | "cli/generate_failed" | "cli/init_failed" | "cli/notion_api_failed" | "cli/env_file_not_found";
37
+ /**
38
+ * CMS エラーコード。
39
+ * `BuiltInCMSErrorCode` のリテラル補完を維持しつつ、
40
+ * サードパーティアダプタが独自コードを定義できるよう `string & {}` で拡張可能にする。
41
+ */
42
+ type CMSErrorCode = BuiltInCMSErrorCode | (string & {});
43
+ interface CMSErrorContext {
44
+ operation: string;
45
+ slug?: string;
46
+ dataSourceId?: string;
47
+ pageId?: string;
48
+ [key: string]: string | number | boolean | null | undefined;
49
+ }
50
+ declare class CMSError extends Error {
51
+ readonly code: CMSErrorCode;
52
+ readonly cause?: unknown;
53
+ readonly context: CMSErrorContext;
54
+ constructor(params: {
55
+ code: CMSErrorCode;
56
+ message: string;
57
+ cause?: unknown;
58
+ context: CMSErrorContext;
59
+ });
60
+ }
61
+ declare function isCMSError(error: unknown): error is CMSError;
62
+ /** エラーコードが特定の名前空間に属するかを判定する(例: "source/")。 */
63
+ declare function isCMSErrorInNamespace(error: unknown, namespace: string): error is CMSError;
64
+ //#endregion
65
+ export { isCMSError as a, CMSErrorContext as i, CMSError as n, isCMSErrorInNamespace as o, CMSErrorCode as r, BuiltInCMSErrorCode as t };
66
+ //# sourceMappingURL=errors-B5l-3w_F.d.mts.map
package/dist/errors.d.mts CHANGED
@@ -1,56 +1,2 @@
1
- //#region src/errors.d.ts
2
- /**
3
- * ライブラリ組み込みの CMS エラーコード。
4
- *
5
- * | コード | 発生条件 |
6
- * |---|---|
7
- * | `core/config_invalid` | 設定不備(token 未設定など) |
8
- * | `core/schema_invalid` | schema/mapping の型不整合 |
9
- * | `core/notion_orm_missing` | `@notion-headless-cms/notion-orm` の動的ロード失敗 |
10
- * | `source/fetch_items_failed` | `DataSource.list()` 失敗 |
11
- * | `source/fetch_item_failed` | `DataSource.findByProp()` 失敗 |
12
- * | `source/load_markdown_failed` | `DataSource.loadMarkdown()` 失敗 |
13
- * | `cache/io_failed` | document / image キャッシュの I/O 失敗 |
14
- * | `cache/image_fetch_failed` | Notion 画像の HTTP 取得失敗 |
15
- * | `renderer/failed` | Markdown → HTML 変換失敗 |
16
- * | `cli/config_invalid` | `nhc.config.ts` の内容不整合 |
17
- * | `cli/config_load_failed` | 設定ファイルの読み込み / 評価失敗 |
18
- * | `cli/schema_invalid` | CLI が受け取ったスキーマ / マッピング不整合 |
19
- * | `cli/generate_failed` | `nhc generate` の処理失敗 |
20
- * | `cli/init_failed` | `nhc init` の処理失敗 |
21
- * | `cli/notion_api_failed` | CLI が Notion API を呼び出す際の失敗 |
22
- * | `cli/env_file_not_found` | `--env-file` で指定したファイルが存在しない |
23
- *
24
- * サードパーティアダプタが独自コードを追加したい場合は `CMSErrorCode` を参照。
25
- */
26
- type BuiltInCMSErrorCode = "core/config_invalid" | "core/schema_invalid" | "core/notion_orm_missing" | "source/fetch_items_failed" | "source/fetch_item_failed" | "source/load_markdown_failed" | "cache/io_failed" | "cache/image_fetch_failed" | "renderer/failed" | "cli/config_invalid" | "cli/config_load_failed" | "cli/schema_invalid" | "cli/generate_failed" | "cli/init_failed" | "cli/notion_api_failed" | "cli/env_file_not_found";
27
- /**
28
- * CMS エラーコード。
29
- * `BuiltInCMSErrorCode` のリテラル補完を維持しつつ、
30
- * サードパーティアダプタが独自コードを定義できるよう `string & {}` で拡張可能にする。
31
- */
32
- type CMSErrorCode = BuiltInCMSErrorCode | (string & {});
33
- interface CMSErrorContext {
34
- operation: string;
35
- slug?: string;
36
- dataSourceId?: string;
37
- pageId?: string;
38
- [key: string]: string | number | boolean | null | undefined;
39
- }
40
- declare class CMSError extends Error {
41
- readonly code: CMSErrorCode;
42
- readonly cause?: unknown;
43
- readonly context: CMSErrorContext;
44
- constructor(params: {
45
- code: CMSErrorCode;
46
- message: string;
47
- cause?: unknown;
48
- context: CMSErrorContext;
49
- });
50
- }
51
- declare function isCMSError(error: unknown): error is CMSError;
52
- /** エラーコードが特定の名前空間に属するかを判定する(例: "source/")。 */
53
- declare function isCMSErrorInNamespace(error: unknown, namespace: string): error is CMSError;
54
- //#endregion
55
- export { BuiltInCMSErrorCode, CMSError, CMSErrorCode, CMSErrorContext, isCMSError, isCMSErrorInNamespace };
56
- //# sourceMappingURL=errors.d.mts.map
1
+ import { a as isCMSError, i as CMSErrorContext, n as CMSError, o as isCMSErrorInNamespace, r as CMSErrorCode, t as BuiltInCMSErrorCode } from "./errors-B5l-3w_F.mjs";
2
+ export { BuiltInCMSErrorCode, CMSError, CMSErrorCode, CMSErrorContext, isCMSError, isCMSErrorInNamespace };
@@ -1 +1 @@
1
- {"version":3,"file":"errors.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * ライブラリ組み込みの CMS エラーコード。\n *\n * | コード | 発生条件 |\n * |---|---|\n * | `core/config_invalid` | 設定不備(token 未設定など) |\n * | `core/schema_invalid` | schema/mapping の型不整合 |\n * | `core/notion_orm_missing` | `@notion-headless-cms/notion-orm` の動的ロード失敗 |\n * | `source/fetch_items_failed` | `DataSource.list()` 失敗 |\n * | `source/fetch_item_failed` | `DataSource.findByProp()` 失敗 |\n * | `source/load_markdown_failed` | `DataSource.loadMarkdown()` 失敗 |\n * | `cache/io_failed` | document / image キャッシュの I/O 失敗 |\n * | `cache/image_fetch_failed` | Notion 画像の HTTP 取得失敗 |\n * | `renderer/failed` | Markdown → HTML 変換失敗 |\n * | `cli/config_invalid` | `nhc.config.ts` の内容不整合 |\n * | `cli/config_load_failed` | 設定ファイルの読み込み / 評価失敗 |\n * | `cli/schema_invalid` | CLI が受け取ったスキーマ / マッピング不整合 |\n * | `cli/generate_failed` | `nhc generate` の処理失敗 |\n * | `cli/init_failed` | `nhc init` の処理失敗 |\n * | `cli/notion_api_failed` | CLI が Notion API を呼び出す際の失敗 |\n * | `cli/env_file_not_found` | `--env-file` で指定したファイルが存在しない |\n *\n * サードパーティアダプタが独自コードを追加したい場合は `CMSErrorCode` を参照。\n */\nexport type BuiltInCMSErrorCode =\n\t| \"core/config_invalid\"\n\t| \"core/schema_invalid\"\n\t| \"core/notion_orm_missing\"\n\t| \"source/fetch_items_failed\"\n\t| \"source/fetch_item_failed\"\n\t| \"source/load_markdown_failed\"\n\t| \"cache/io_failed\"\n\t| \"cache/image_fetch_failed\"\n\t| \"renderer/failed\"\n\t| \"cli/config_invalid\"\n\t| \"cli/config_load_failed\"\n\t| \"cli/schema_invalid\"\n\t| \"cli/generate_failed\"\n\t| \"cli/init_failed\"\n\t| \"cli/notion_api_failed\"\n\t| \"cli/env_file_not_found\";\n\n/**\n * CMS エラーコード。\n * `BuiltInCMSErrorCode` のリテラル補完を維持しつつ、\n * サードパーティアダプタが独自コードを定義できるよう `string & {}` で拡張可能にする。\n */\nexport type CMSErrorCode = BuiltInCMSErrorCode | (string & {});\n\nexport interface CMSErrorContext {\n\toperation: string;\n\tslug?: string;\n\tdataSourceId?: string;\n\tpageId?: string;\n\t[key: string]: string | number | boolean | null | undefined;\n}\n\nexport class CMSError extends Error {\n\treadonly code: CMSErrorCode;\n\toverride readonly cause?: unknown;\n\treadonly context: CMSErrorContext;\n\n\tconstructor(params: {\n\t\tcode: CMSErrorCode;\n\t\tmessage: string;\n\t\tcause?: unknown;\n\t\tcontext: CMSErrorContext;\n\t}) {\n\t\tsuper(params.message, { cause: params.cause });\n\t\tthis.name = \"CMSError\";\n\t\tthis.code = params.code;\n\t\tthis.cause = params.cause;\n\t\tthis.context = params.context;\n\t}\n}\n\nexport function isCMSError(error: unknown): error is CMSError {\n\treturn error instanceof CMSError;\n}\n\n/** エラーコードが特定の名前空間に属するかを判定する(例: \"source/\")。 */\nexport function isCMSErrorInNamespace(\n\terror: unknown,\n\tnamespace: string,\n): error is CMSError {\n\treturn isCMSError(error) && error.code.startsWith(namespace);\n}\n"],"mappings":";AAyDA,IAAa,WAAb,cAA8B,MAAM;CACnC;CACA;CACA;CAEA,YAAY,QAKT;AACF,QAAM,OAAO,SAAS,EAAE,OAAO,OAAO,OAAO,CAAC;AAC9C,OAAK,OAAO;AACZ,OAAK,OAAO,OAAO;AACnB,OAAK,QAAQ,OAAO;AACpB,OAAK,UAAU,OAAO;;;AAIxB,SAAgB,WAAW,OAAmC;AAC7D,QAAO,iBAAiB;;;AAIzB,SAAgB,sBACf,OACA,WACoB;AACpB,QAAO,WAAW,MAAM,IAAI,MAAM,KAAK,WAAW,UAAU"}
1
+ {"version":3,"file":"errors.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * ライブラリ組み込みの CMS エラーコード。\n *\n * | コード | 発生条件 |\n * |---|---|\n * | `core/config_invalid` | 設定不備(token 未設定など) |\n * | `core/schema_invalid` | schema/mapping の型不整合 |\n * | `core/notion_orm_missing` | `@notion-headless-cms/notion-orm` の動的ロード失敗 |\n * | `core/sort_unsupported_type` | ソートキーの値型が string / number でない |\n * | `webhook/signature_invalid` | Webhook 署名検証失敗 |\n * | `webhook/payload_invalid` | Webhook ペイロード形式不正 |\n * | `webhook/unknown_collection` | Webhook の対象コレクションが未知 |\n * | `webhook/not_implemented` | DataSource が parseWebhook を実装していない |\n * | `source/fetch_items_failed` | `DataSource.list()` 失敗 |\n * | `source/fetch_item_failed` | `DataSource.findByProp()` 失敗 |\n * | `source/load_markdown_failed` | `DataSource.loadMarkdown()` 失敗 |\n * | `source/load_blocks_failed` | `DataSource.loadBlocks()` 失敗 |\n * | `cache/io_failed` | document / image キャッシュの I/O 失敗 |\n * | `cache/image_fetch_failed` | Notion 画像の HTTP 取得失敗 |\n * | `cache/image_invalid_content_type` | 画像レスポンスの Content-Type が不正 |\n * | `renderer/failed` | Markdown → HTML 変換失敗 |\n * | `swr/item_check_failed` | SWR バックグラウンドのアイテム差分チェック失敗 |\n * | `swr/list_check_failed` | SWR バックグラウンドのリスト差分チェック失敗 |\n * | `swr/content_rebuild_failed` | SWR バックグラウンドの本文再生成失敗 |\n * | `cli/config_invalid` | `nhc.config.ts` の内容不整合 |\n * | `cli/config_load_failed` | 設定ファイルの読み込み / 評価失敗 |\n * | `cli/schema_invalid` | CLI が受け取ったスキーマ / マッピング不整合 |\n * | `cli/generate_failed` | `nhc generate` の処理失敗 |\n * | `cli/init_failed` | `nhc init` の処理失敗 |\n * | `cli/notion_api_failed` | CLI が Notion API を呼び出す際の失敗 |\n * | `cli/env_file_not_found` | `--env-file` で指定したファイルが存在しない |\n *\n * サードパーティアダプタが独自コードを追加したい場合は `CMSErrorCode` を参照。\n */\nexport type BuiltInCMSErrorCode =\n | \"core/config_invalid\"\n | \"core/schema_invalid\"\n | \"core/notion_orm_missing\"\n | \"core/sort_unsupported_type\"\n | \"webhook/signature_invalid\"\n | \"webhook/payload_invalid\"\n | \"webhook/unknown_collection\"\n | \"webhook/not_implemented\"\n | \"source/fetch_items_failed\"\n | \"source/fetch_item_failed\"\n | \"source/load_markdown_failed\"\n | \"source/load_blocks_failed\"\n | \"cache/io_failed\"\n | \"cache/image_fetch_failed\"\n | \"cache/image_invalid_content_type\"\n | \"renderer/failed\"\n | \"swr/item_check_failed\"\n | \"swr/list_check_failed\"\n | \"swr/content_rebuild_failed\"\n | \"cli/config_invalid\"\n | \"cli/config_load_failed\"\n | \"cli/schema_invalid\"\n | \"cli/generate_failed\"\n | \"cli/init_failed\"\n | \"cli/notion_api_failed\"\n | \"cli/env_file_not_found\";\n\n/**\n * CMS エラーコード。\n * `BuiltInCMSErrorCode` のリテラル補完を維持しつつ、\n * サードパーティアダプタが独自コードを定義できるよう `string & {}` で拡張可能にする。\n */\nexport type CMSErrorCode = BuiltInCMSErrorCode | (string & {});\n\nexport interface CMSErrorContext {\n operation: string;\n slug?: string;\n dataSourceId?: string;\n pageId?: string;\n [key: string]: string | number | boolean | null | undefined;\n}\n\nexport class CMSError extends Error {\n readonly code: CMSErrorCode;\n override readonly cause?: unknown;\n readonly context: CMSErrorContext;\n\n constructor(params: {\n code: CMSErrorCode;\n message: string;\n cause?: unknown;\n context: CMSErrorContext;\n }) {\n super(params.message, { cause: params.cause });\n this.name = \"CMSError\";\n this.code = params.code;\n this.cause = params.cause;\n this.context = params.context;\n }\n}\n\nexport function isCMSError(error: unknown): error is CMSError {\n return error instanceof CMSError;\n}\n\n/** エラーコードが特定の名前空間に属するかを判定する(例: \"source/\")。 */\nexport function isCMSErrorInNamespace(\n error: unknown,\n namespace: string,\n): error is CMSError {\n return isCMSError(error) && error.code.startsWith(namespace);\n}\n"],"mappings":";AA6EA,IAAa,WAAb,cAA8B,MAAM;CAClC;CACA;CACA;CAEA,YAAY,QAKT;AACD,QAAM,OAAO,SAAS,EAAE,OAAO,OAAO,OAAO,CAAC;AAC9C,OAAK,OAAO;AACZ,OAAK,OAAO,OAAO;AACnB,OAAK,QAAQ,OAAO;AACpB,OAAK,UAAU,OAAO;;;AAI1B,SAAgB,WAAW,OAAmC;AAC5D,QAAO,iBAAiB;;;AAI1B,SAAgB,sBACd,OACA,WACmB;AACnB,QAAO,WAAW,MAAM,IAAI,MAAM,KAAK,WAAW,UAAU"}
@@ -1,4 +1,5 @@
1
- import { a as CachedItemMeta, i as CachedItemList, r as CachedItemContent, t as BaseContentItem } from "./content-Bffid8da.mjs";
1
+ import { a as CachedItemMeta, i as CachedItemList, r as CachedItemContent, t as BaseContentItem } from "./content-D7PfY-bV.mjs";
2
+ import { n as CMSError } from "./errors-B5l-3w_F.mjs";
2
3
 
3
4
  //#region src/types/hooks.d.ts
4
5
  type MaybePromise<T> = T | Promise<T>;
@@ -7,23 +8,32 @@ interface CMSHooks<T extends BaseContentItem = BaseContentItem> {
7
8
  * 本文キャッシュ書き込み直前に呼ばれる。html を加工したい場合などに使う。
8
9
  * 戻り値が新しい `CachedItemContent` として保存される。
9
10
  */
10
- beforeCacheContent?: (content: CachedItemContent, item: T) => MaybePromise<CachedItemContent>;
11
+ beforeCacheContent?(content: CachedItemContent, item: T): MaybePromise<CachedItemContent>;
11
12
  /** メタデータキャッシュ書き込み直前に呼ばれる。 */
12
- beforeCacheMeta?: (meta: CachedItemMeta<T>) => MaybePromise<CachedItemMeta<T>>;
13
- afterRender?: (html: string, item: T) => MaybePromise<string>;
14
- onCacheHit?: (slug: string, meta: CachedItemMeta<T>) => void;
15
- onCacheMiss?: (slug: string) => void;
13
+ beforeCacheMeta?(meta: CachedItemMeta<T>): MaybePromise<CachedItemMeta<T>>;
14
+ afterRender?(html: string, item: T): MaybePromise<string>;
15
+ onCacheHit?(slug: string, meta: CachedItemMeta<T>): void;
16
+ onCacheMiss?(slug: string): void;
16
17
  /** SWR バックグラウンド差分チェックで更新を検出し、メタを差し替えたときに呼ばれる。 */
17
- onCacheRevalidated?: (slug: string, meta: CachedItemMeta<T>) => void;
18
+ onCacheRevalidated?(slug: string, meta: CachedItemMeta<T>): void;
18
19
  /** 本文キャッシュが(lazy ロード or バックグラウンド再生成で)更新されたときに呼ばれる。 */
19
- onContentRevalidated?: (slug: string, content: CachedItemContent) => void;
20
- onListCacheHit?: (list: CachedItemList<T>) => void;
21
- onListCacheMiss?: () => void;
20
+ onContentRevalidated?(slug: string, content: CachedItemContent): void;
21
+ onListCacheHit?(list: CachedItemList<T>): void;
22
+ onListCacheMiss?(): void;
22
23
  /** SWR バックグラウンド差分チェックでリスト更新を検出し、キャッシュを差し替えたときに呼ばれる。 */
23
- onListCacheRevalidated?: (list: CachedItemList<T>) => void;
24
- onError?: (error: Error) => void;
25
- onRenderStart?: (slug: string) => void;
26
- onRenderEnd?: (slug: string, durationMs: number) => void;
24
+ onListCacheRevalidated?(list: CachedItemList<T>): void;
25
+ onError?(error: Error): void;
26
+ onRenderStart?(slug: string): void;
27
+ onRenderEnd?(slug: string, durationMs: number): void;
28
+ /**
29
+ * SWR バックグラウンド処理(差分チェック / 本文再生成)の失敗ハンドラ。
30
+ * これらは UI に伝搬させずに握り続ける必要があるため、エラーを補足するには
31
+ * この hook を実装する。実装しない場合、エラーは logger.warn のみで消える。
32
+ */
33
+ onSwrError?(error: CMSError, ctx: {
34
+ phase: "item-meta" | "item-content" | "list";
35
+ slug?: string;
36
+ }): void;
27
37
  }
28
38
  //#endregion
29
39
  //#region src/types/logger.d.ts
@@ -75,4 +85,4 @@ declare function mergeLoggers(plugins: Array<{
75
85
  }>, directLogger?: Logger): Logger | undefined;
76
86
  //#endregion
77
87
  export { Logger as a, definePlugin as i, mergeLoggers as n, CMSHooks as o, CMSPlugin as r, MaybePromise as s, mergeHooks as t };
78
- //# sourceMappingURL=hooks-CqqVxrYg.d.mts.map
88
+ //# sourceMappingURL=hooks-CGOl6uP5.d.mts.map
package/dist/hooks.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { n as mergeLoggers, t as mergeHooks } from "./hooks-CqqVxrYg.mjs";
1
+ import { n as mergeLoggers, t as mergeHooks } from "./hooks-CGOl6uP5.mjs";
2
2
  export { mergeHooks, mergeLoggers };
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.mjs","names":[],"sources":["../src/hooks.ts"],"sourcesContent":["import type { BaseContentItem } from \"./types/content\";\nimport type { CMSHooks, MaybePromise } from \"./types/hooks\";\nimport type { Logger } from \"./types/logger\";\nimport type { CMSPlugin } from \"./types/plugin\";\n\n/**\n * プラグイン配列とダイレクトフックを合成して単一の CMSHooks を返す。\n * beforeCacheMeta / beforeCacheContent / afterRender はパイプライン(前の出力が次の入力)。\n * オブザーバー系は全員に同じ値を渡し、例外は logger に流して握りつぶす。\n */\nexport function mergeHooks<T extends BaseContentItem>(\n\tplugins: CMSPlugin<T>[],\n\tdirectHooks?: CMSHooks<T>,\n\tlogger?: Logger,\n): CMSHooks<T> {\n\tconst allHooks: CMSHooks<T>[] = [\n\t\t...plugins.map((p) => p.hooks ?? {}),\n\t\t...(directHooks ? [directHooks] : []),\n\t];\n\n\tif (allHooks.length === 0) return {};\n\n\treturn {\n\t\tbeforeCacheMeta: buildMetaPipeline(allHooks),\n\t\tbeforeCacheContent: buildContentPipeline(allHooks),\n\t\tafterRender: buildRenderPipeline(allHooks),\n\t\tonCacheHit: buildObserver(allHooks, \"onCacheHit\", logger),\n\t\tonCacheMiss: buildObserver(allHooks, \"onCacheMiss\", logger),\n\t\tonCacheRevalidated: buildObserver(allHooks, \"onCacheRevalidated\", logger),\n\t\tonContentRevalidated: buildObserver(\n\t\t\tallHooks,\n\t\t\t\"onContentRevalidated\",\n\t\t\tlogger,\n\t\t),\n\t\tonListCacheHit: buildObserver(allHooks, \"onListCacheHit\", logger),\n\t\tonListCacheMiss: buildObserver(allHooks, \"onListCacheMiss\", logger),\n\t\tonListCacheRevalidated: buildObserver(\n\t\t\tallHooks,\n\t\t\t\"onListCacheRevalidated\",\n\t\t\tlogger,\n\t\t),\n\t\tonError: buildObserver(allHooks, \"onError\", logger),\n\t\tonRenderStart: buildObserver(allHooks, \"onRenderStart\", logger),\n\t\tonRenderEnd: buildObserver(allHooks, \"onRenderEnd\", logger),\n\t};\n}\n\nfunction buildMetaPipeline<T extends BaseContentItem>(\n\thooks: CMSHooks<T>[],\n): CMSHooks<T>[\"beforeCacheMeta\"] {\n\tconst fns = hooks\n\t\t.map((h) => h.beforeCacheMeta)\n\t\t.filter(Boolean) as NonNullable<CMSHooks<T>[\"beforeCacheMeta\"]>[];\n\tif (fns.length === 0) return undefined;\n\treturn async (meta) => {\n\t\tlet current = meta;\n\t\tfor (const fn of fns) {\n\t\t\tcurrent = await (fn(current) as MaybePromise<typeof meta>);\n\t\t}\n\t\treturn current;\n\t};\n}\n\nfunction buildContentPipeline<T extends BaseContentItem>(\n\thooks: CMSHooks<T>[],\n): CMSHooks<T>[\"beforeCacheContent\"] {\n\tconst fns = hooks\n\t\t.map((h) => h.beforeCacheContent)\n\t\t.filter(Boolean) as NonNullable<CMSHooks<T>[\"beforeCacheContent\"]>[];\n\tif (fns.length === 0) return undefined;\n\treturn async (content, item) => {\n\t\tlet current = content;\n\t\tfor (const fn of fns) {\n\t\t\tcurrent = await (fn(current, item) as MaybePromise<typeof content>);\n\t\t}\n\t\treturn current;\n\t};\n}\n\nfunction buildRenderPipeline<T extends BaseContentItem>(\n\thooks: CMSHooks<T>[],\n): CMSHooks<T>[\"afterRender\"] {\n\tconst fns = hooks.map((h) => h.afterRender).filter(Boolean) as NonNullable<\n\t\tCMSHooks<T>[\"afterRender\"]\n\t>[];\n\tif (fns.length === 0) return undefined;\n\treturn async (html, item) => {\n\t\tlet current = html;\n\t\tfor (const fn of fns) {\n\t\t\tcurrent = await (fn(current, item) as MaybePromise<string>);\n\t\t}\n\t\treturn current;\n\t};\n}\n\nfunction buildObserver<T extends BaseContentItem, K extends keyof CMSHooks<T>>(\n\thooks: CMSHooks<T>[],\n\tkey: K,\n\tlogger?: Logger,\n): CMSHooks<T>[K] {\n\tconst fns = hooks.map((h) => h[key]).filter(Boolean);\n\tif (fns.length === 0) return undefined;\n\treturn ((...args: unknown[]) => {\n\t\tfor (const fn of fns) {\n\t\t\ttry {\n\t\t\t\t(fn as (...a: unknown[]) => void)(...args);\n\t\t\t} catch (err) {\n\t\t\t\tlogger?.error?.(\"観測フックで例外が発生\", {\n\t\t\t\t\thook: String(key),\n\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}) as CMSHooks<T>[K];\n}\n\n/** プラグイン配列とダイレクトロガーを合成して単一の Logger を返す。 */\nexport function mergeLoggers(\n\tplugins: Array<{ logger?: Partial<Logger> }>,\n\tdirectLogger?: Logger,\n): Logger | undefined {\n\tconst loggers: Partial<Logger>[] = [\n\t\t...plugins.map((p) => p.logger ?? {}),\n\t\t...(directLogger ? [directLogger] : []),\n\t];\n\tif (loggers.length === 0) return undefined;\n\n\tconst merged: Logger = {};\n\tfor (const level of [\"debug\", \"info\", \"warn\", \"error\"] as const) {\n\t\tconst fns = loggers.map((l) => l[level]).filter(Boolean) as NonNullable<\n\t\t\tLogger[typeof level]\n\t\t>[];\n\t\tif (fns.length > 0) {\n\t\t\tmerged[level] = (message, context) => {\n\t\t\t\tfor (const fn of fns) fn(message, context);\n\t\t\t};\n\t\t}\n\t}\n\treturn merged;\n}\n"],"mappings":";;;;;;AAUA,SAAgB,WACf,SACA,aACA,QACc;CACd,MAAM,WAA0B,CAC/B,GAAG,QAAQ,KAAK,MAAM,EAAE,SAAS,EAAE,CAAC,EACpC,GAAI,cAAc,CAAC,YAAY,GAAG,EAAE,CACpC;AAED,KAAI,SAAS,WAAW,EAAG,QAAO,EAAE;AAEpC,QAAO;EACN,iBAAiB,kBAAkB,SAAS;EAC5C,oBAAoB,qBAAqB,SAAS;EAClD,aAAa,oBAAoB,SAAS;EAC1C,YAAY,cAAc,UAAU,cAAc,OAAO;EACzD,aAAa,cAAc,UAAU,eAAe,OAAO;EAC3D,oBAAoB,cAAc,UAAU,sBAAsB,OAAO;EACzE,sBAAsB,cACrB,UACA,wBACA,OACA;EACD,gBAAgB,cAAc,UAAU,kBAAkB,OAAO;EACjE,iBAAiB,cAAc,UAAU,mBAAmB,OAAO;EACnE,wBAAwB,cACvB,UACA,0BACA,OACA;EACD,SAAS,cAAc,UAAU,WAAW,OAAO;EACnD,eAAe,cAAc,UAAU,iBAAiB,OAAO;EAC/D,aAAa,cAAc,UAAU,eAAe,OAAO;EAC3D;;AAGF,SAAS,kBACR,OACiC;CACjC,MAAM,MAAM,MACV,KAAK,MAAM,EAAE,gBAAgB,CAC7B,OAAO,QAAQ;AACjB,KAAI,IAAI,WAAW,EAAG,QAAO,KAAA;AAC7B,QAAO,OAAO,SAAS;EACtB,IAAI,UAAU;AACd,OAAK,MAAM,MAAM,IAChB,WAAU,MAAO,GAAG,QAAQ;AAE7B,SAAO;;;AAIT,SAAS,qBACR,OACoC;CACpC,MAAM,MAAM,MACV,KAAK,MAAM,EAAE,mBAAmB,CAChC,OAAO,QAAQ;AACjB,KAAI,IAAI,WAAW,EAAG,QAAO,KAAA;AAC7B,QAAO,OAAO,SAAS,SAAS;EAC/B,IAAI,UAAU;AACd,OAAK,MAAM,MAAM,IAChB,WAAU,MAAO,GAAG,SAAS,KAAK;AAEnC,SAAO;;;AAIT,SAAS,oBACR,OAC6B;CAC7B,MAAM,MAAM,MAAM,KAAK,MAAM,EAAE,YAAY,CAAC,OAAO,QAAQ;AAG3D,KAAI,IAAI,WAAW,EAAG,QAAO,KAAA;AAC7B,QAAO,OAAO,MAAM,SAAS;EAC5B,IAAI,UAAU;AACd,OAAK,MAAM,MAAM,IAChB,WAAU,MAAO,GAAG,SAAS,KAAK;AAEnC,SAAO;;;AAIT,SAAS,cACR,OACA,KACA,QACiB;CACjB,MAAM,MAAM,MAAM,KAAK,MAAM,EAAE,KAAK,CAAC,OAAO,QAAQ;AACpD,KAAI,IAAI,WAAW,EAAG,QAAO,KAAA;AAC7B,UAAS,GAAG,SAAoB;AAC/B,OAAK,MAAM,MAAM,IAChB,KAAI;AACF,MAAiC,GAAG,KAAK;WAClC,KAAK;AACb,WAAQ,QAAQ,eAAe;IAC9B,MAAM,OAAO,IAAI;IACjB,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;IACvD,CAAC;;;;;AAON,SAAgB,aACf,SACA,cACqB;CACrB,MAAM,UAA6B,CAClC,GAAG,QAAQ,KAAK,MAAM,EAAE,UAAU,EAAE,CAAC,EACrC,GAAI,eAAe,CAAC,aAAa,GAAG,EAAE,CACtC;AACD,KAAI,QAAQ,WAAW,EAAG,QAAO,KAAA;CAEjC,MAAM,SAAiB,EAAE;AACzB,MAAK,MAAM,SAAS;EAAC;EAAS;EAAQ;EAAQ;EAAQ,EAAW;EAChE,MAAM,MAAM,QAAQ,KAAK,MAAM,EAAE,OAAO,CAAC,OAAO,QAAQ;AAGxD,MAAI,IAAI,SAAS,EAChB,QAAO,UAAU,SAAS,YAAY;AACrC,QAAK,MAAM,MAAM,IAAK,IAAG,SAAS,QAAQ;;;AAI7C,QAAO"}
1
+ {"version":3,"file":"hooks.mjs","names":[],"sources":["../src/hooks.ts"],"sourcesContent":["import type { BaseContentItem } from \"./types/content\";\nimport type { CMSHooks, MaybePromise } from \"./types/hooks\";\nimport type { Logger } from \"./types/logger\";\nimport type { CMSPlugin } from \"./types/plugin\";\n\n/**\n * プラグイン配列とダイレクトフックを合成して単一の CMSHooks を返す。\n * beforeCacheMeta / beforeCacheContent / afterRender はパイプライン(前の出力が次の入力)。\n * オブザーバー系は全員に同じ値を渡し、例外は logger に流して握りつぶす。\n */\nexport function mergeHooks<T extends BaseContentItem>(\n plugins: CMSPlugin<T>[],\n directHooks?: CMSHooks<T>,\n logger?: Logger,\n): CMSHooks<T> {\n const allHooks: CMSHooks<T>[] = [\n ...plugins.map((p) => p.hooks ?? {}),\n ...(directHooks ? [directHooks] : []),\n ];\n\n if (allHooks.length === 0) return {};\n\n return {\n beforeCacheMeta: buildMetaPipeline(allHooks),\n beforeCacheContent: buildContentPipeline(allHooks),\n afterRender: buildRenderPipeline(allHooks),\n onCacheHit: buildObserver(allHooks, \"onCacheHit\", logger),\n onCacheMiss: buildObserver(allHooks, \"onCacheMiss\", logger),\n onCacheRevalidated: buildObserver(allHooks, \"onCacheRevalidated\", logger),\n onContentRevalidated: buildObserver(\n allHooks,\n \"onContentRevalidated\",\n logger,\n ),\n onListCacheHit: buildObserver(allHooks, \"onListCacheHit\", logger),\n onListCacheMiss: buildObserver(allHooks, \"onListCacheMiss\", logger),\n onListCacheRevalidated: buildObserver(\n allHooks,\n \"onListCacheRevalidated\",\n logger,\n ),\n onError: buildObserver(allHooks, \"onError\", logger),\n onRenderStart: buildObserver(allHooks, \"onRenderStart\", logger),\n onRenderEnd: buildObserver(allHooks, \"onRenderEnd\", logger),\n };\n}\n\nfunction buildMetaPipeline<T extends BaseContentItem>(\n hooks: CMSHooks<T>[],\n): CMSHooks<T>[\"beforeCacheMeta\"] {\n const fns = hooks\n .map((h) => h.beforeCacheMeta)\n .filter(Boolean) as NonNullable<CMSHooks<T>[\"beforeCacheMeta\"]>[];\n if (fns.length === 0) return undefined;\n return async (meta) => {\n let current = meta;\n for (const fn of fns) {\n current = await (fn(current) as MaybePromise<typeof meta>);\n }\n return current;\n };\n}\n\nfunction buildContentPipeline<T extends BaseContentItem>(\n hooks: CMSHooks<T>[],\n): CMSHooks<T>[\"beforeCacheContent\"] {\n const fns = hooks\n .map((h) => h.beforeCacheContent)\n .filter(Boolean) as NonNullable<CMSHooks<T>[\"beforeCacheContent\"]>[];\n if (fns.length === 0) return undefined;\n return async (content, item) => {\n let current = content;\n for (const fn of fns) {\n current = await (fn(current, item) as MaybePromise<typeof content>);\n }\n return current;\n };\n}\n\nfunction buildRenderPipeline<T extends BaseContentItem>(\n hooks: CMSHooks<T>[],\n): CMSHooks<T>[\"afterRender\"] {\n const fns = hooks.map((h) => h.afterRender).filter(Boolean) as NonNullable<\n CMSHooks<T>[\"afterRender\"]\n >[];\n if (fns.length === 0) return undefined;\n return async (html, item) => {\n let current = html;\n for (const fn of fns) {\n current = await (fn(current, item) as MaybePromise<string>);\n }\n return current;\n };\n}\n\nfunction buildObserver<T extends BaseContentItem, K extends keyof CMSHooks<T>>(\n hooks: CMSHooks<T>[],\n key: K,\n logger?: Logger,\n): CMSHooks<T>[K] {\n const fns = hooks.map((h) => h[key]).filter(Boolean);\n if (fns.length === 0) return undefined;\n return ((...args: unknown[]) => {\n for (const fn of fns) {\n try {\n (fn as (...a: unknown[]) => void)(...args);\n } catch (err) {\n logger?.error?.(\"観測フックで例外が発生\", {\n hook: String(key),\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n }) as CMSHooks<T>[K];\n}\n\n/** プラグイン配列とダイレクトロガーを合成して単一の Logger を返す。 */\nexport function mergeLoggers(\n plugins: Array<{ logger?: Partial<Logger> }>,\n directLogger?: Logger,\n): Logger | undefined {\n const loggers: Partial<Logger>[] = [\n ...plugins.map((p) => p.logger ?? {}),\n ...(directLogger ? [directLogger] : []),\n ];\n if (loggers.length === 0) return undefined;\n\n const merged: Logger = {};\n for (const level of [\"debug\", \"info\", \"warn\", \"error\"] as const) {\n const fns = loggers.map((l) => l[level]).filter(Boolean) as NonNullable<\n Logger[typeof level]\n >[];\n if (fns.length > 0) {\n merged[level] = (message, context) => {\n for (const fn of fns) fn(message, context);\n };\n }\n }\n return merged;\n}\n"],"mappings":";;;;;;AAUA,SAAgB,WACd,SACA,aACA,QACa;CACb,MAAM,WAA0B,CAC9B,GAAG,QAAQ,KAAK,MAAM,EAAE,SAAS,EAAE,CAAC,EACpC,GAAI,cAAc,CAAC,YAAY,GAAG,EAAE,CACrC;AAED,KAAI,SAAS,WAAW,EAAG,QAAO,EAAE;AAEpC,QAAO;EACL,iBAAiB,kBAAkB,SAAS;EAC5C,oBAAoB,qBAAqB,SAAS;EAClD,aAAa,oBAAoB,SAAS;EAC1C,YAAY,cAAc,UAAU,cAAc,OAAO;EACzD,aAAa,cAAc,UAAU,eAAe,OAAO;EAC3D,oBAAoB,cAAc,UAAU,sBAAsB,OAAO;EACzE,sBAAsB,cACpB,UACA,wBACA,OACD;EACD,gBAAgB,cAAc,UAAU,kBAAkB,OAAO;EACjE,iBAAiB,cAAc,UAAU,mBAAmB,OAAO;EACnE,wBAAwB,cACtB,UACA,0BACA,OACD;EACD,SAAS,cAAc,UAAU,WAAW,OAAO;EACnD,eAAe,cAAc,UAAU,iBAAiB,OAAO;EAC/D,aAAa,cAAc,UAAU,eAAe,OAAO;EAC5D;;AAGH,SAAS,kBACP,OACgC;CAChC,MAAM,MAAM,MACT,KAAK,MAAM,EAAE,gBAAgB,CAC7B,OAAO,QAAQ;AAClB,KAAI,IAAI,WAAW,EAAG,QAAO,KAAA;AAC7B,QAAO,OAAO,SAAS;EACrB,IAAI,UAAU;AACd,OAAK,MAAM,MAAM,IACf,WAAU,MAAO,GAAG,QAAQ;AAE9B,SAAO;;;AAIX,SAAS,qBACP,OACmC;CACnC,MAAM,MAAM,MACT,KAAK,MAAM,EAAE,mBAAmB,CAChC,OAAO,QAAQ;AAClB,KAAI,IAAI,WAAW,EAAG,QAAO,KAAA;AAC7B,QAAO,OAAO,SAAS,SAAS;EAC9B,IAAI,UAAU;AACd,OAAK,MAAM,MAAM,IACf,WAAU,MAAO,GAAG,SAAS,KAAK;AAEpC,SAAO;;;AAIX,SAAS,oBACP,OAC4B;CAC5B,MAAM,MAAM,MAAM,KAAK,MAAM,EAAE,YAAY,CAAC,OAAO,QAAQ;AAG3D,KAAI,IAAI,WAAW,EAAG,QAAO,KAAA;AAC7B,QAAO,OAAO,MAAM,SAAS;EAC3B,IAAI,UAAU;AACd,OAAK,MAAM,MAAM,IACf,WAAU,MAAO,GAAG,SAAS,KAAK;AAEpC,SAAO;;;AAIX,SAAS,cACP,OACA,KACA,QACgB;CAChB,MAAM,MAAM,MAAM,KAAK,MAAM,EAAE,KAAK,CAAC,OAAO,QAAQ;AACpD,KAAI,IAAI,WAAW,EAAG,QAAO,KAAA;AAC7B,UAAS,GAAG,SAAoB;AAC9B,OAAK,MAAM,MAAM,IACf,KAAI;AACD,MAAiC,GAAG,KAAK;WACnC,KAAK;AACZ,WAAQ,QAAQ,eAAe;IAC7B,MAAM,OAAO,IAAI;IACjB,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;IACxD,CAAC;;;;;AAOV,SAAgB,aACd,SACA,cACoB;CACpB,MAAM,UAA6B,CACjC,GAAG,QAAQ,KAAK,MAAM,EAAE,UAAU,EAAE,CAAC,EACrC,GAAI,eAAe,CAAC,aAAa,GAAG,EAAE,CACvC;AACD,KAAI,QAAQ,WAAW,EAAG,QAAO,KAAA;CAEjC,MAAM,SAAiB,EAAE;AACzB,MAAK,MAAM,SAAS;EAAC;EAAS;EAAQ;EAAQ;EAAQ,EAAW;EAC/D,MAAM,MAAM,QAAQ,KAAK,MAAM,EAAE,OAAO,CAAC,OAAO,QAAQ;AAGxD,MAAI,IAAI,SAAS,EACf,QAAO,UAAU,SAAS,YAAY;AACpC,QAAK,MAAM,MAAM,IAAK,IAAG,SAAS,QAAQ;;;AAIhD,QAAO"}
package/dist/index.d.mts CHANGED
@@ -1,22 +1,31 @@
1
- import { a as CachedItemMeta, c as ContentResult, i as CachedItemList, l as ImageRef, n as CMSSchemaProperties, o as StorageBinary, r as CachedItemContent, s as ContentBlock, t as BaseContentItem, u as InlineNode } from "./content-Bffid8da.mjs";
2
- import { a as CacheAdapter, c as DataSource, d as PropertyDef, f as PropertyMap, i as memoryCache, l as InvalidateKind, o as DocumentCacheOps, p as WebhookConfig, s as ImageCacheOps, t as MemoryCacheOptions, u as InvalidateScope } from "./memory-CA1uTRbr.mjs";
3
- import { a as Logger, i as definePlugin, n as mergeLoggers, o as CMSHooks, r as CMSPlugin, s as MaybePromise, t as mergeHooks } from "./hooks-CqqVxrYg.mjs";
4
- import { BuiltInCMSErrorCode, CMSError, CMSErrorCode, CMSErrorContext, isCMSError, isCMSErrorInNamespace } from "./errors.mjs";
1
+ import { a as CachedItemMeta, c as ContentResult, i as CachedItemList, l as ImageRef, n as CMSSchemaProperties, o as StorageBinary, r as CachedItemContent, s as ContentBlock, t as BaseContentItem, u as InlineNode } from "./content-D7PfY-bV.mjs";
2
+ import { a as CacheAdapter, c as DataSource, d as PropertyDef, f as PropertyMap, i as memoryCache, l as InvalidateKind, o as DocumentCacheOps, p as WebhookConfig, s as ImageCacheOps, t as MemoryCacheOptions, u as InvalidateScope } from "./memory-CxF7S-iB.mjs";
3
+ import { a as isCMSError, i as CMSErrorContext, n as CMSError, o as isCMSErrorInNamespace, r as CMSErrorCode, t as BuiltInCMSErrorCode } from "./errors-B5l-3w_F.mjs";
4
+ import { a as Logger, i as definePlugin, n as mergeLoggers, o as CMSHooks, r as CMSPlugin, s as MaybePromise, t as mergeHooks } from "./hooks-CGOl6uP5.mjs";
5
5
 
6
6
  //#region src/types/collection.d.ts
7
+ /**
8
+ * `where` フィルタの型。各フィールドに単一値、または OR 候補の配列を指定できる。
9
+ * 配列を渡すと「いずれかに一致」(in-match) になる。
10
+ */
11
+ type WhereClause<T extends BaseContentItem> = { [K in keyof T]?: T[K] | readonly T[K][] };
7
12
  /** 並び順指定。 */
8
13
  interface SortOption<T extends BaseContentItem = BaseContentItem> {
9
14
  /** ソートするプロパティ名。 */
10
15
  by: keyof T & string;
11
16
  /** 昇順 / 降順。デフォルト "desc"。 */
12
17
  dir?: "asc" | "desc";
18
+ /** カスタム comparator。指定した場合 `by` / `dir` より優先される。 */
19
+ compare?: (a: T, b: T) => number;
13
20
  }
14
21
  /** `list()` のオプション。ページ取得に必要な絞り込み・ソート・ページングを表現する。 */
15
22
  interface ListOptions<T extends BaseContentItem = BaseContentItem> {
16
23
  /** ステータス絞り込み (`publishedStatuses` を上書き)。 */
17
24
  status?: string | readonly string[];
18
- /** プロパティ一致フィルタ (in-memory フィルタ) */
19
- where?: Partial<Record<keyof T, unknown>>;
25
+ /** プロパティ一致フィルタ (in-memory フィルタ)。配列は OR 一致。 */
26
+ where?: WhereClause<T>;
27
+ /** 任意ロジックのフィルタ関数。`where` より後に適用される。 */
28
+ filter?: (item: T) => boolean;
20
29
  /** タグ絞り込み (schema に tags: string[] フィールドがある場合)。 */
21
30
  tag?: string;
22
31
  /** ソート。デフォルトは publishedAt の降順。 */
@@ -104,7 +113,7 @@ interface CollectionClient<T extends BaseContentItem = BaseContentItem> {
104
113
  slug: string;
105
114
  }[]>;
106
115
  /**
107
- * Notion から最新版を取得し、`currentVersion`(`item.updatedAt`)と比較する。
116
+ * Notion から最新版を取得し、`currentVersion`(`item.lastEditedTime`)と比較する。
108
117
  * 差分があればキャッシュを更新してアイテムを返す。
109
118
  * ページ表示後の1回限りのクライアント再検証エンドポイント用。
110
119
  *
@@ -123,7 +132,7 @@ type LogLevel = "debug" | "info" | "warn" | "error";
123
132
  * renderer プラグインの不透明型。
124
133
  * core は unified / remark / rehype に依存せず、このリストをそのまま renderer に渡すだけ。
125
134
  */
126
- type RendererPluginList = any[];
135
+ type RendererPluginList = unknown[];
127
136
  /**
128
137
  * render() オプション。core は renderer の実装を知らず、この型だけを扱う。
129
138
  * @notion-headless-cms/renderer の renderMarkdown() はこのシグネチャと構造的に互換。
@@ -180,7 +189,7 @@ interface CollectionDef<T extends BaseContentItem = BaseContentItem> {
180
189
  * `createCMS({ collections })` の map 型。
181
190
  * キーがコレクション名、値が `CollectionDef<T>`。
182
191
  */
183
- type CollectionsConfig = Record<string, CollectionDef<any>>;
192
+ type CollectionsConfig = Record<string, CollectionDef<BaseContentItem>>;
184
193
  /** `CollectionsConfig` から各 T を抽出するユーティリティ型。 */
185
194
  type InferCollectionItem<C> = C extends CollectionDef<infer T> ? T : BaseContentItem;
186
195
  /**
@@ -212,16 +221,20 @@ interface CreateCMSOptions<C extends CollectionsConfig = CollectionsConfig> {
212
221
  cache?: CacheAdapter | readonly CacheAdapter[];
213
222
  /** SWR の有効期間 (ミリ秒)。未設定時は TTL なし (失効まで stale を返す)。 */
214
223
  ttlMs?: number;
215
- /** カスタムレンダラー。未指定時は `@notion-headless-cms/renderer` の `renderMarkdown` を動的 import。 */
216
- renderer?: RendererFn;
224
+ /**
225
+ * Markdown→HTML レンダラー(必須)。
226
+ * renderer パッケージの `renderMarkdown` を渡すのが標準。
227
+ * カスタム実装も `RendererFn` 型を満たせば使用可能。
228
+ */
229
+ renderer: RendererFn;
217
230
  /** 画像プロキシのベース URL。デフォルト `/api/images`。 */
218
231
  imageProxyBase?: string;
219
232
  /** Cloudflare Workers の `waitUntil` に相当する非同期処理の登録関数。 */
220
233
  waitUntil?: (p: Promise<unknown>) => void;
221
234
  /** ライフサイクルフック (全コレクション共通)。 */
222
- hooks?: CMSHooks<any>;
235
+ hooks?: CMSHooks<BaseContentItem>;
223
236
  /** プラグイン配列。 */
224
- plugins?: CMSPlugin<any>[];
237
+ plugins?: CMSPlugin<BaseContentItem>[];
225
238
  /** ロガー。 */
226
239
  logger?: Logger;
227
240
  /** ログレベルの下限。指定したレベル未満のログを内部で抑制する。 */
@@ -266,8 +279,12 @@ interface HandlerOptions {
266
279
  /** `$handler()` が内部で依存する CMS 機能の最小セット。 */
267
280
  interface HandlerAdapter {
268
281
  imageCache: ImageCacheOps;
269
- /** コレクション名で DataSource を取り出し parseWebhook にフォワードする。 */
270
- parseWebhook(req: Request, webhookSecret: string | undefined): Promise<InvalidateScope | null>;
282
+ /**
283
+ * 指定コレクションの DataSource.parseWebhook を呼ぶ。
284
+ * 未知コレクション → `webhook/unknown_collection` CMSError
285
+ * parseWebhook 未実装 → `webhook/not_implemented` CMSError
286
+ */
287
+ parseWebhookFor(collection: string, req: Request, webhookSecret: string | undefined): Promise<InvalidateScope>;
271
288
  revalidate(scope: InvalidateScope): Promise<void>;
272
289
  }
273
290
  /**
@@ -275,8 +292,8 @@ interface HandlerAdapter {
275
292
  * Next.js / React Router / Hono / Cloudflare Workers いずれでも使える。
276
293
  *
277
294
  * ルート:
278
- * - GET `{basePath}/images/:hash` — 画像プロキシ
279
- * - POST `{basePath}/revalidate` — Webhook 受信 + $revalidate()
295
+ * - GET `{basePath}/images/:hash` — 画像プロキシ
296
+ * - POST `{basePath}/revalidate/:collection` — Webhook 受信 + $revalidate()
280
297
  */
281
298
  declare function createHandler(adapter: HandlerAdapter, opts?: HandlerOptions): (req: Request) => Promise<Response>;
282
299
  //#endregion
@@ -319,7 +336,7 @@ declare function createCMS<C extends CollectionsConfig>(opts: CreateCMSOptions<C
319
336
  /** 本文レンダリングに必要な依存を束ねたコンテキスト。 */
320
337
  interface RenderContext<T extends BaseContentItem> {
321
338
  source: DataSource<T>;
322
- rendererFn: RendererFn | undefined;
339
+ rendererFn: RendererFn;
323
340
  imgCache: ImageCacheOps;
324
341
  imgCacheName: string;
325
342
  hasImageCache: boolean;
@@ -401,7 +418,7 @@ declare class CollectionClientImpl<T extends BaseContentItem> implements Collect
401
418
  * 再生成して書き戻す。
402
419
  */
403
420
  private loadOrBuildContent;
404
- /** メタ既知の状態で本文だけバックグラウンド再生成する。エラーは握りつぶす。 */
421
+ /** メタ既知の状態で本文だけバックグラウンド再生成する。エラーは onSwrError フックに通知する。 */
405
422
  private rebuildContentBg;
406
423
  private attachLazyContent;
407
424
  private fetchList;