@notion-headless-cms/core 0.3.13 → 0.3.14
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/dist/cache/memory.d.mts +1 -1
- package/dist/cache/memory.mjs +1 -1
- package/dist/cache/memory.mjs.map +1 -1
- package/dist/{errors-B5l-3w_F.d.mts → errors-CC_x98vG.d.mts} +20 -2
- package/dist/errors.d.mts +2 -2
- package/dist/errors.mjs +23 -1
- package/dist/errors.mjs.map +1 -1
- package/dist/{hooks-CGOl6uP5.d.mts → hooks-C4HRqHLo.d.mts} +2 -2
- package/dist/hooks.d.mts +1 -1
- package/dist/index.d.mts +75 -62
- package/dist/index.mjs +90 -72
- package/dist/index.mjs.map +1 -1
- package/dist/{memory-CxF7S-iB.d.mts → memory-BKDsuGVN.d.mts} +2 -2
- package/package.json +1 -1
package/dist/cache/memory.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as memoryCache, n as MemoryDocumentOptions, r as MemoryImageOptions, t as MemoryCacheOptions } from "../memory-
|
|
1
|
+
import { i as memoryCache, n as MemoryDocumentOptions, r as MemoryImageOptions, t as MemoryCacheOptions } from "../memory-BKDsuGVN.mjs";
|
|
2
2
|
export { MemoryCacheOptions, MemoryDocumentOptions, MemoryImageOptions, memoryCache };
|
package/dist/cache/memory.mjs
CHANGED
|
@@ -136,7 +136,7 @@ var MemoryImageOps = class {
|
|
|
136
136
|
* プロセス再起動でクリアされるため、ローカル開発・SSG ビルド・テスト用途。
|
|
137
137
|
*
|
|
138
138
|
* @example
|
|
139
|
-
* cache: memoryCache({
|
|
139
|
+
* cache: [memoryCache({ maxItems: 1000 })]
|
|
140
140
|
*/
|
|
141
141
|
function memoryCache(options) {
|
|
142
142
|
return {
|
|
@@ -1 +1 @@
|
|
|
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({
|
|
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({ 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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"}
|
|
@@ -57,10 +57,28 @@ declare class CMSError extends Error {
|
|
|
57
57
|
cause?: unknown;
|
|
58
58
|
context: CMSErrorContext;
|
|
59
59
|
});
|
|
60
|
+
/** エラーコードが指定した値と一致するか判定する。 */
|
|
61
|
+
is(code: CMSErrorCode): boolean;
|
|
62
|
+
/** エラーコードが指定した名前空間に属するか判定する(例: `"source/"`)。 */
|
|
63
|
+
inNamespace(namespace: string): boolean;
|
|
60
64
|
}
|
|
61
65
|
declare function isCMSError(error: unknown): error is CMSError;
|
|
62
66
|
/** エラーコードが特定の名前空間に属するかを判定する(例: "source/")。 */
|
|
63
67
|
declare function isCMSErrorInNamespace(error: unknown, namespace: string): error is CMSError;
|
|
68
|
+
type CMSErrorHandler<R> = (err: CMSError) => R;
|
|
69
|
+
/**
|
|
70
|
+
* `CMSError` を switch 式のように分岐して処理するユーティリティ。
|
|
71
|
+
* `_` キーはフォールバック(CMSError 以外 or 未マッチ時)に使われる。
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* matchCMSError(err, {
|
|
75
|
+
* "source/fetch_items_failed": (e) => handleFetchError(e),
|
|
76
|
+
* _: (e) => { throw e; },
|
|
77
|
+
* });
|
|
78
|
+
*/
|
|
79
|
+
declare function matchCMSError<R>(error: unknown, handlers: Partial<Record<CMSErrorCode, CMSErrorHandler<R>>> & {
|
|
80
|
+
_?: (err: unknown) => R;
|
|
81
|
+
}): R | undefined;
|
|
64
82
|
//#endregion
|
|
65
|
-
export { isCMSError as a, CMSErrorContext as i, CMSError as n, isCMSErrorInNamespace as o, CMSErrorCode as r, BuiltInCMSErrorCode as t };
|
|
66
|
-
//# sourceMappingURL=errors-
|
|
83
|
+
export { isCMSError as a, CMSErrorContext as i, CMSError as n, isCMSErrorInNamespace as o, CMSErrorCode as r, matchCMSError as s, BuiltInCMSErrorCode as t };
|
|
84
|
+
//# sourceMappingURL=errors-CC_x98vG.d.mts.map
|
package/dist/errors.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as isCMSError, i as CMSErrorContext, n as CMSError, o as isCMSErrorInNamespace, r as CMSErrorCode, t as BuiltInCMSErrorCode } from "./errors-
|
|
2
|
-
export { BuiltInCMSErrorCode, CMSError, CMSErrorCode, CMSErrorContext, isCMSError, isCMSErrorInNamespace };
|
|
1
|
+
import { a as isCMSError, i as CMSErrorContext, n as CMSError, o as isCMSErrorInNamespace, r as CMSErrorCode, s as matchCMSError, t as BuiltInCMSErrorCode } from "./errors-CC_x98vG.mjs";
|
|
2
|
+
export { BuiltInCMSErrorCode, CMSError, CMSErrorCode, CMSErrorContext, isCMSError, isCMSErrorInNamespace, matchCMSError };
|
package/dist/errors.mjs
CHANGED
|
@@ -10,6 +10,14 @@ var CMSError = class extends Error {
|
|
|
10
10
|
this.cause = params.cause;
|
|
11
11
|
this.context = params.context;
|
|
12
12
|
}
|
|
13
|
+
/** エラーコードが指定した値と一致するか判定する。 */
|
|
14
|
+
is(code) {
|
|
15
|
+
return this.code === code;
|
|
16
|
+
}
|
|
17
|
+
/** エラーコードが指定した名前空間に属するか判定する(例: `"source/"`)。 */
|
|
18
|
+
inNamespace(namespace) {
|
|
19
|
+
return this.code.startsWith(namespace);
|
|
20
|
+
}
|
|
13
21
|
};
|
|
14
22
|
function isCMSError(error) {
|
|
15
23
|
return error instanceof CMSError;
|
|
@@ -18,7 +26,21 @@ function isCMSError(error) {
|
|
|
18
26
|
function isCMSErrorInNamespace(error, namespace) {
|
|
19
27
|
return isCMSError(error) && error.code.startsWith(namespace);
|
|
20
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* `CMSError` を switch 式のように分岐して処理するユーティリティ。
|
|
31
|
+
* `_` キーはフォールバック(CMSError 以外 or 未マッチ時)に使われる。
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* matchCMSError(err, {
|
|
35
|
+
* "source/fetch_items_failed": (e) => handleFetchError(e),
|
|
36
|
+
* _: (e) => { throw e; },
|
|
37
|
+
* });
|
|
38
|
+
*/
|
|
39
|
+
function matchCMSError(error, handlers) {
|
|
40
|
+
if (!isCMSError(error)) return handlers._?.(error);
|
|
41
|
+
return (handlers[error.code] ?? handlers._)?.(error);
|
|
42
|
+
}
|
|
21
43
|
//#endregion
|
|
22
|
-
export { CMSError, isCMSError, isCMSErrorInNamespace };
|
|
44
|
+
export { CMSError, isCMSError, isCMSErrorInNamespace, matchCMSError };
|
|
23
45
|
|
|
24
46
|
//# sourceMappingURL=errors.mjs.map
|
package/dist/errors.mjs.map
CHANGED
|
@@ -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 * | `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;;;
|
|
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 /** エラーコードが指定した値と一致するか判定する。 */\n is(code: CMSErrorCode): boolean {\n return this.code === code;\n }\n\n /** エラーコードが指定した名前空間に属するか判定する(例: `\"source/\"`)。 */\n inNamespace(namespace: string): boolean {\n return this.code.startsWith(namespace);\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\ntype CMSErrorHandler<R> = (err: CMSError) => R;\n\n/**\n * `CMSError` を switch 式のように分岐して処理するユーティリティ。\n * `_` キーはフォールバック(CMSError 以外 or 未マッチ時)に使われる。\n *\n * @example\n * matchCMSError(err, {\n * \"source/fetch_items_failed\": (e) => handleFetchError(e),\n * _: (e) => { throw e; },\n * });\n */\nexport function matchCMSError<R>(\n error: unknown,\n handlers: Partial<Record<CMSErrorCode, CMSErrorHandler<R>>> & {\n _?: (err: unknown) => R;\n },\n): R | undefined {\n if (!isCMSError(error)) {\n return handlers._?.(error);\n }\n const handler =\n handlers[error.code as CMSErrorCode] ??\n (handlers._ as CMSErrorHandler<R> | undefined);\n return handler?.(error);\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;;;CAIxB,GAAG,MAA6B;AAC9B,SAAO,KAAK,SAAS;;;CAIvB,YAAY,WAA4B;AACtC,SAAO,KAAK,KAAK,WAAW,UAAU;;;AAI1C,SAAgB,WAAW,OAAmC;AAC5D,QAAO,iBAAiB;;;AAI1B,SAAgB,sBACd,OACA,WACmB;AACnB,QAAO,WAAW,MAAM,IAAI,MAAM,KAAK,WAAW,UAAU;;;;;;;;;;;;AAe9D,SAAgB,cACd,OACA,UAGe;AACf,KAAI,CAAC,WAAW,MAAM,CACpB,QAAO,SAAS,IAAI,MAAM;AAK5B,SAFE,SAAS,MAAM,SACd,SAAS,KACK,MAAM"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
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-
|
|
2
|
+
import { n as CMSError } from "./errors-CC_x98vG.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/types/hooks.d.ts
|
|
5
5
|
type MaybePromise<T> = T | Promise<T>;
|
|
@@ -85,4 +85,4 @@ declare function mergeLoggers(plugins: Array<{
|
|
|
85
85
|
}>, directLogger?: Logger): Logger | undefined;
|
|
86
86
|
//#endregion
|
|
87
87
|
export { Logger as a, definePlugin as i, mergeLoggers as n, CMSHooks as o, CMSPlugin as r, MaybePromise as s, mergeHooks as t };
|
|
88
|
-
//# sourceMappingURL=hooks-
|
|
88
|
+
//# sourceMappingURL=hooks-C4HRqHLo.d.mts.map
|
package/dist/hooks.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as mergeLoggers, t as mergeHooks } from "./hooks-
|
|
1
|
+
import { n as mergeLoggers, t as mergeHooks } from "./hooks-C4HRqHLo.mjs";
|
|
2
2
|
export { mergeHooks, mergeLoggers };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
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-
|
|
3
|
-
import { a as isCMSError, i as CMSErrorContext, n as CMSError, o as isCMSErrorInNamespace, r as CMSErrorCode, t as BuiltInCMSErrorCode } from "./errors-
|
|
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-
|
|
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-BKDsuGVN.mjs";
|
|
3
|
+
import { a as isCMSError, i as CMSErrorContext, n as CMSError, o as isCMSErrorInNamespace, r as CMSErrorCode, s as matchCMSError, t as BuiltInCMSErrorCode } from "./errors-CC_x98vG.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-C4HRqHLo.mjs";
|
|
5
5
|
|
|
6
6
|
//#region src/types/collection.d.ts
|
|
7
7
|
/**
|
|
@@ -20,8 +20,8 @@ interface SortOption<T extends BaseContentItem = BaseContentItem> {
|
|
|
20
20
|
}
|
|
21
21
|
/** `list()` のオプション。ページ取得に必要な絞り込み・ソート・ページングを表現する。 */
|
|
22
22
|
interface ListOptions<T extends BaseContentItem = BaseContentItem> {
|
|
23
|
-
/** ステータス絞り込み (`publishedStatuses` を上書き)
|
|
24
|
-
|
|
23
|
+
/** ステータス絞り込み (`publishedStatuses` を上書き)。単一値または配列で指定。 */
|
|
24
|
+
statuses?: string | readonly string[];
|
|
25
25
|
/** プロパティ一致フィルタ (in-memory フィルタ)。配列は OR 一致。 */
|
|
26
26
|
where?: WhereClause<T>;
|
|
27
27
|
/** 任意ロジックのフィルタ関数。`where` より後に適用される。 */
|
|
@@ -39,23 +39,19 @@ interface ListOptions<T extends BaseContentItem = BaseContentItem> {
|
|
|
39
39
|
interface AdjacencyOptions<T extends BaseContentItem = BaseContentItem> {
|
|
40
40
|
sort?: SortOption<T>;
|
|
41
41
|
}
|
|
42
|
-
/** `
|
|
43
|
-
interface
|
|
42
|
+
/** `find()` のオプション。 */
|
|
43
|
+
interface FindOptions {
|
|
44
44
|
/** true なら TTL に関わらずブロッキングで再取得し、本文 cache を破棄する。 */
|
|
45
|
-
|
|
45
|
+
bypassCache?: boolean;
|
|
46
46
|
}
|
|
47
47
|
/**
|
|
48
|
-
* `
|
|
49
|
-
*
|
|
48
|
+
* `find()` の戻り値。アイテム本体に `html()` / `markdown()` / `blocks()` が生える。
|
|
49
|
+
* これらを呼んだ時点で初めて本文をロードする lazy 設計。
|
|
50
50
|
*/
|
|
51
|
-
type
|
|
52
|
-
/**
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
*/
|
|
56
|
-
render(opts?: {
|
|
57
|
-
format?: "html" | "markdown";
|
|
58
|
-
}): Promise<string>;
|
|
51
|
+
type ItemWithContent<T extends BaseContentItem> = T & {
|
|
52
|
+
/** HTML 文字列を返す。 */html(): Promise<string>; /** Markdown 文字列を返す。 */
|
|
53
|
+
markdown(): Promise<string>; /** コンテンツ AST(ContentBlock 配列)を返す。 */
|
|
54
|
+
blocks(): Promise<ContentBlock[]>;
|
|
59
55
|
};
|
|
60
56
|
/** `cache.warm()` のオプション。 */
|
|
61
57
|
interface WarmOptions {
|
|
@@ -64,54 +60,57 @@ interface WarmOptions {
|
|
|
64
60
|
/** 進捗コールバック。 */
|
|
65
61
|
onProgress?: (done: number, total: number) => void;
|
|
66
62
|
}
|
|
63
|
+
/** `cache.warm()` の戻り値。 */
|
|
64
|
+
interface WarmResult {
|
|
65
|
+
ok: number;
|
|
66
|
+
failed: Array<{
|
|
67
|
+
slug: string;
|
|
68
|
+
error: unknown;
|
|
69
|
+
}>;
|
|
70
|
+
}
|
|
67
71
|
/** コレクションごとのキャッシュ操作 namespace。 */
|
|
68
72
|
interface CollectionCacheOps<T extends BaseContentItem> {
|
|
69
73
|
/**
|
|
70
|
-
*
|
|
74
|
+
* コレクション全体のキャッシュを失効させる。
|
|
71
75
|
* 次回 `get` / `list` で source から再取得される。
|
|
72
76
|
*/
|
|
73
|
-
invalidate(
|
|
77
|
+
invalidate(): Promise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* 指定 slug のアイテムキャッシュを失効させる。
|
|
80
|
+
* 次回 `get` で source から再取得される。
|
|
81
|
+
*/
|
|
82
|
+
invalidateItem(slug: string): Promise<void>;
|
|
74
83
|
/**
|
|
75
84
|
* 全アイテムを並列に事前取得・レンダリングしてキャッシュに格納する。
|
|
76
85
|
* SSG ビルド前のウォームアップに利用する。
|
|
77
86
|
*/
|
|
78
|
-
warm(opts?: WarmOptions): Promise<
|
|
79
|
-
ok: number;
|
|
80
|
-
failed: number;
|
|
81
|
-
}>;
|
|
82
|
-
/** 前後アイテムのナビゲーション (リスト順序ベース)。 */
|
|
83
|
-
adjacent(slug: string, opts?: AdjacencyOptions<T>): Promise<{
|
|
84
|
-
prev: T | null;
|
|
85
|
-
next: T | null;
|
|
86
|
-
}>;
|
|
87
|
+
warm(opts?: WarmOptions): Promise<WarmResult>;
|
|
87
88
|
}
|
|
88
89
|
/** `check()` の戻り値。差分なしか、差分ありの場合はアイテムを含む。 */
|
|
89
90
|
type CheckResult<T extends BaseContentItem> = {
|
|
90
91
|
stale: false;
|
|
91
92
|
} | {
|
|
92
93
|
stale: true;
|
|
93
|
-
item:
|
|
94
|
+
item: ItemWithContent<T>;
|
|
94
95
|
};
|
|
95
96
|
/**
|
|
96
97
|
* コレクション別の CMS クライアント。
|
|
97
|
-
* `cms.posts.
|
|
98
|
+
* `cms.posts.find(slug)` / `cms.posts.list()` のようにアクセスする。
|
|
98
99
|
*/
|
|
99
100
|
interface CollectionClient<T extends BaseContentItem = BaseContentItem> {
|
|
100
101
|
/**
|
|
101
|
-
* スラッグで単件取得。アイテム本体に `
|
|
102
|
+
* スラッグで単件取得。アイテム本体に `html()` / `markdown()` / `blocks()` が生える。
|
|
102
103
|
*
|
|
103
104
|
* SWR: TTL 未設定 or 期限内ならキャッシュ即時返却 + バックグラウンド差分チェック。
|
|
104
|
-
* TTL 期限切れ、または `opts.
|
|
105
|
+
* TTL 期限切れ、または `opts.bypassCache === true` でブロッキング取得。
|
|
105
106
|
*
|
|
106
107
|
* @returns キャッシュまたは source から取得したアイテム。存在しない場合は null。
|
|
107
108
|
*/
|
|
108
|
-
|
|
109
|
+
find(slug: string, opts?: FindOptions): Promise<ItemWithContent<T> | null>;
|
|
109
110
|
/** 公開済みアイテム一覧を取得する。 */
|
|
110
111
|
list(opts?: ListOptions<T>): Promise<T[]>;
|
|
111
|
-
/**
|
|
112
|
-
params(): Promise<
|
|
113
|
-
slug: string;
|
|
114
|
-
}[]>;
|
|
112
|
+
/** コレクション内の全スラッグを返す。`generateStaticParams` 等の SSG パラメータ生成に利用する。 */
|
|
113
|
+
params(): Promise<string[]>;
|
|
115
114
|
/**
|
|
116
115
|
* Notion から最新版を取得し、`currentVersion`(`item.lastEditedTime`)と比較する。
|
|
117
116
|
* 差分があればキャッシュを更新してアイテムを返す。
|
|
@@ -121,6 +120,11 @@ interface CollectionClient<T extends BaseContentItem = BaseContentItem> {
|
|
|
121
120
|
* アイテムが存在しない: `null`
|
|
122
121
|
*/
|
|
123
122
|
check(slug: string, currentVersion: string): Promise<CheckResult<T> | null>;
|
|
123
|
+
/** 前後アイテムのナビゲーション (リスト順序ベース)。 */
|
|
124
|
+
adjacent(slug: string, opts?: AdjacencyOptions<T>): Promise<{
|
|
125
|
+
prev: T | null;
|
|
126
|
+
next: T | null;
|
|
127
|
+
}>;
|
|
124
128
|
/** キャッシュ操作 namespace。 */
|
|
125
129
|
cache: CollectionCacheOps<T>;
|
|
126
130
|
}
|
|
@@ -154,6 +158,11 @@ interface ContentConfig {
|
|
|
154
158
|
/** 追加する rehype プラグイン。 */
|
|
155
159
|
rehypePlugins?: RendererPluginList;
|
|
156
160
|
}
|
|
161
|
+
/** SWR(Stale-While-Revalidate)設定。 */
|
|
162
|
+
interface SWRConfig {
|
|
163
|
+
/** SWR の有効期間 (ミリ秒)。未設定時は TTL なし(失効まで stale を返す)。 */
|
|
164
|
+
ttlMs?: number;
|
|
165
|
+
}
|
|
157
166
|
/** レートリミット・リトライ設定。 */
|
|
158
167
|
interface RateLimiterConfig {
|
|
159
168
|
/** 同時実行数の上限。デフォルト: 3 */
|
|
@@ -206,27 +215,28 @@ type InferCollectionItem<C> = C extends CollectionDef<infer T> ? T : BaseContent
|
|
|
206
215
|
* publishedStatuses: ["公開済み"],
|
|
207
216
|
* }
|
|
208
217
|
* },
|
|
209
|
-
* cache: memoryCache(
|
|
218
|
+
* cache: [memoryCache()],
|
|
219
|
+
* swr: { ttlMs: 5 * 60_000 },
|
|
210
220
|
* });
|
|
211
221
|
*/
|
|
212
222
|
interface CreateCMSOptions<C extends CollectionsConfig = CollectionsConfig> {
|
|
213
223
|
/** コレクション定義のマップ。 */
|
|
214
224
|
collections: C;
|
|
215
225
|
/**
|
|
216
|
-
* キャッシュアダプタ (
|
|
226
|
+
* キャッシュアダプタ (配列)。未指定時はキャッシュなし。
|
|
217
227
|
* - `memoryCache()` のように doc + image 両方を担当するもの
|
|
218
228
|
* - `r2Cache()` (image のみ)、`kvCache()` (doc のみ) のように片側のみ担当するもの
|
|
219
|
-
* -
|
|
229
|
+
* - 複数 adapter を配列で組み合わせると、各 adapter の `handles` で振り分けられる
|
|
220
230
|
*/
|
|
221
|
-
cache?:
|
|
222
|
-
/** SWR
|
|
223
|
-
|
|
231
|
+
cache?: readonly CacheAdapter[];
|
|
232
|
+
/** SWR(Stale-While-Revalidate)設定。 */
|
|
233
|
+
swr?: SWRConfig;
|
|
224
234
|
/**
|
|
225
|
-
* Markdown→HTML
|
|
226
|
-
* renderer
|
|
235
|
+
* Markdown→HTML レンダラー。
|
|
236
|
+
* 省略時は `@notion-headless-cms/renderer` の `renderMarkdown` を動的 import で使用する。
|
|
227
237
|
* カスタム実装も `RendererFn` 型を満たせば使用可能。
|
|
228
238
|
*/
|
|
229
|
-
renderer
|
|
239
|
+
renderer?: RendererFn;
|
|
230
240
|
/** 画像プロキシのベース URL。デフォルト `/api/images`。 */
|
|
231
241
|
imageProxyBase?: string;
|
|
232
242
|
/** Cloudflare Workers の `waitUntil` に相当する非同期処理の登録関数。 */
|
|
@@ -300,16 +310,16 @@ declare function createHandler(adapter: HandlerAdapter, opts?: HandlerOptions):
|
|
|
300
310
|
//#region src/cms.d.ts
|
|
301
311
|
/** `CMSClient<C>` — コレクション別アクセス + グローバル操作の合成型。 */
|
|
302
312
|
type CMSClient<C extends CollectionsConfig> = { [K in keyof C]: CollectionClient<InferCollectionItem<C[K]>> } & CMSGlobalOps;
|
|
303
|
-
/** `CMSClient`
|
|
313
|
+
/** `CMSClient` のグローバル名前空間。 */
|
|
304
314
|
interface CMSGlobalOps {
|
|
305
315
|
/** 登録されているコレクション名の一覧。 */
|
|
306
|
-
readonly
|
|
316
|
+
readonly collections: readonly string[];
|
|
307
317
|
/** 全コレクションまたは特定スコープのキャッシュを無効化する。 */
|
|
308
|
-
|
|
318
|
+
invalidate(scope?: InvalidateScope): Promise<void>;
|
|
309
319
|
/** Web Standard なルーティングハンドラ (画像プロキシ / webhook) を生成する。 */
|
|
310
|
-
|
|
320
|
+
handler(opts?: HandlerOptions): (req: Request) => Promise<Response>;
|
|
311
321
|
/** ハッシュキーでキャッシュ画像を取得する。 */
|
|
312
|
-
|
|
322
|
+
getCachedImage(hash: string): Promise<StorageBinary | null>;
|
|
313
323
|
}
|
|
314
324
|
/**
|
|
315
325
|
* 複数の `CollectionDef` を束ねた CMS クライアントを生成する。
|
|
@@ -327,7 +337,8 @@ interface CMSGlobalOps {
|
|
|
327
337
|
* publishedStatuses: ["公開済み"],
|
|
328
338
|
* }
|
|
329
339
|
* },
|
|
330
|
-
* cache: memoryCache(
|
|
340
|
+
* cache: [memoryCache()],
|
|
341
|
+
* swr: { ttlMs: 5 * 60_000 },
|
|
331
342
|
* });
|
|
332
343
|
*/
|
|
333
344
|
declare function createCMS<C extends CollectionsConfig>(opts: CreateCMSOptions<C>): CMSClient<C>;
|
|
@@ -336,7 +347,7 @@ declare function createCMS<C extends CollectionsConfig>(opts: CreateCMSOptions<C
|
|
|
336
347
|
/** 本文レンダリングに必要な依存を束ねたコンテキスト。 */
|
|
337
348
|
interface RenderContext<T extends BaseContentItem> {
|
|
338
349
|
source: DataSource<T>;
|
|
339
|
-
rendererFn: RendererFn;
|
|
350
|
+
rendererFn: RendererFn | undefined;
|
|
340
351
|
imgCache: ImageCacheOps;
|
|
341
352
|
imgCacheName: string;
|
|
342
353
|
hasImageCache: boolean;
|
|
@@ -402,17 +413,19 @@ declare class CollectionClientImpl<T extends BaseContentItem> implements Collect
|
|
|
402
413
|
private readonly ctx;
|
|
403
414
|
readonly cache: CollectionCacheOps<T>;
|
|
404
415
|
constructor(ctx: CollectionContext<T>);
|
|
405
|
-
|
|
416
|
+
find(slug: string, opts?: FindOptions): Promise<ItemWithContent<T> | null>;
|
|
406
417
|
list(opts?: ListOptions<T>): Promise<T[]>;
|
|
407
|
-
params(): Promise<
|
|
408
|
-
slug: string;
|
|
409
|
-
}[]>;
|
|
418
|
+
params(): Promise<string[]>;
|
|
410
419
|
check(slug: string, currentVersion: string): Promise<CheckResult<T> | null>;
|
|
420
|
+
adjacent(slug: string, opts?: AdjacencyOptions<T>): Promise<{
|
|
421
|
+
prev: T | null;
|
|
422
|
+
next: T | null;
|
|
423
|
+
}>;
|
|
411
424
|
private invalidateImpl;
|
|
425
|
+
private invalidateItemImpl;
|
|
412
426
|
private warmImpl;
|
|
413
|
-
private adjacentImpl;
|
|
414
427
|
private persistMeta;
|
|
415
|
-
private
|
|
428
|
+
private invalidateContentEntry;
|
|
416
429
|
/**
|
|
417
430
|
* 本文キャッシュをロードする。キャッシュが無いか、メタとの整合性が取れない場合は
|
|
418
431
|
* 再生成して書き戻す。
|
|
@@ -425,8 +438,8 @@ declare class CollectionClientImpl<T extends BaseContentItem> implements Collect
|
|
|
425
438
|
private checkAndUpdateItemBg;
|
|
426
439
|
private checkAndUpdateListBg;
|
|
427
440
|
private fetchListRaw;
|
|
428
|
-
private
|
|
441
|
+
private fetchRaw;
|
|
429
442
|
}
|
|
430
443
|
//#endregion
|
|
431
|
-
export { type AdjacencyOptions, type BaseContentItem, type BuiltInCMSErrorCode, type CMSClient, CMSError, type CMSErrorCode, type CMSErrorContext, type CMSGlobalOps, type CMSHooks, type CMSPlugin, type CMSSchemaProperties, type CacheAdapter, type CachedItemContent, type CachedItemList, type CachedItemMeta, type CheckResult, type CollectionCacheOps, type CollectionClient, CollectionClientImpl, type CollectionContext, type CollectionDef, type CollectionsConfig, type ContentBlock, type ContentConfig, type ContentResult, type CreateCMSOptions, DEFAULT_RETRY_CONFIG, type DataSource, type DocumentCacheOps, type
|
|
444
|
+
export { type AdjacencyOptions, type BaseContentItem, type BuiltInCMSErrorCode, type CMSClient, CMSError, type CMSErrorCode, type CMSErrorContext, type CMSGlobalOps, type CMSHooks, type CMSPlugin, type CMSSchemaProperties, type CacheAdapter, type CachedItemContent, type CachedItemList, type CachedItemMeta, type CheckResult, type CollectionCacheOps, type CollectionClient, CollectionClientImpl, type CollectionContext, type CollectionDef, type CollectionsConfig, type ContentBlock, type ContentConfig, type ContentResult, type CreateCMSOptions, DEFAULT_RETRY_CONFIG, type DataSource, type DocumentCacheOps, type FindOptions, type HandlerAdapter, type HandlerOptions, type ImageCacheOps, type ImageRef, type InferCollectionItem, type InlineNode, type InvalidateKind, type InvalidateScope, type ItemWithContent, type ListOptions, type LogLevel, type Logger, type MaybePromise, type MemoryCacheOptions, type PropertyDef, type PropertyMap, type RateLimiterConfig, type RenderOptions, type RendererFn, type RendererPluginList, type RetryConfig, type SWRConfig, type SortOption, type StorageBinary, type WarmOptions, type WarmResult, type WebhookConfig, collectionKey, createCMS, createHandler, definePlugin, isCMSError, isCMSErrorInNamespace, isStale, matchCMSError, memoryCache, mergeHooks, mergeLoggers, noopDocOps, noopImgOps, sha256Hex, withRetry };
|
|
432
445
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { memoryCache } from "./cache/memory.mjs";
|
|
2
|
-
import { CMSError, isCMSError, isCMSErrorInNamespace } from "./errors.mjs";
|
|
2
|
+
import { CMSError, isCMSError, isCMSErrorInNamespace, matchCMSError } from "./errors.mjs";
|
|
3
3
|
import { mergeHooks, mergeLoggers } from "./hooks.mjs";
|
|
4
4
|
//#region src/cache.ts
|
|
5
5
|
/** 文字列をSHA-256でハッシュ化し、16進数文字列として返す。画像キーの生成に使用。 */
|
|
@@ -159,6 +159,21 @@ function buildCacheImageFn(cache, cacheName, imageProxyBase, logger) {
|
|
|
159
159
|
//#endregion
|
|
160
160
|
//#region src/rendering.ts
|
|
161
161
|
/**
|
|
162
|
+
* `@notion-headless-cms/renderer` を動的 import してデフォルトレンダラーを返す。
|
|
163
|
+
* core のゼロ依存ルールを守るため静的 import は禁止。
|
|
164
|
+
*/
|
|
165
|
+
async function loadDefaultRenderer() {
|
|
166
|
+
try {
|
|
167
|
+
return (await import("@notion-headless-cms/renderer")).renderMarkdown;
|
|
168
|
+
} catch {
|
|
169
|
+
throw new CMSError({
|
|
170
|
+
code: "core/config_invalid",
|
|
171
|
+
message: "renderer が未指定で、@notion-headless-cms/renderer のロードにも失敗しました。 createCMS の renderer オプションを指定するか、@notion-headless-cms/renderer をインストールしてください。",
|
|
172
|
+
context: { operation: "loadDefaultRenderer" }
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
162
177
|
* メタデータキャッシュエントリを生成する。Notion API も renderer も呼ばない軽量関数。
|
|
163
178
|
*/
|
|
164
179
|
function buildCachedItemMeta(item, source) {
|
|
@@ -212,9 +227,10 @@ async function buildCachedItemContent(item, ctx) {
|
|
|
212
227
|
});
|
|
213
228
|
}
|
|
214
229
|
const cacheImage = ctx.hasImageCache ? buildCacheImageFn(ctx.imgCache, ctx.imgCacheName, ctx.imageProxyBase, ctx.logger) : void 0;
|
|
230
|
+
const rendererFn = ctx.rendererFn ?? await loadDefaultRenderer();
|
|
215
231
|
let html;
|
|
216
232
|
try {
|
|
217
|
-
html = await
|
|
233
|
+
html = await rendererFn(markdown, {
|
|
218
234
|
imageProxyBase: ctx.imageProxyBase,
|
|
219
235
|
cacheImage,
|
|
220
236
|
remarkPlugins: ctx.contentConfig?.remarkPlugins,
|
|
@@ -305,40 +321,40 @@ var CollectionClientImpl = class {
|
|
|
305
321
|
constructor(ctx) {
|
|
306
322
|
this.ctx = ctx;
|
|
307
323
|
this.cache = {
|
|
308
|
-
invalidate: (
|
|
309
|
-
|
|
310
|
-
|
|
324
|
+
invalidate: () => this.invalidateImpl(),
|
|
325
|
+
invalidateItem: (slug) => this.invalidateItemImpl(slug),
|
|
326
|
+
warm: (opts) => this.warmImpl(opts)
|
|
311
327
|
};
|
|
312
328
|
}
|
|
313
|
-
async
|
|
314
|
-
if (opts.
|
|
329
|
+
async find(slug, opts = {}) {
|
|
330
|
+
if (opts.bypassCache) {
|
|
315
331
|
this.ctx.hooks.onCacheMiss?.(slug);
|
|
316
|
-
const item = await this.
|
|
332
|
+
const item = await this.fetchRaw(slug);
|
|
317
333
|
if (!item) return null;
|
|
318
334
|
const meta = await this.persistMeta(slug, item);
|
|
319
|
-
await this.
|
|
335
|
+
await this.invalidateContentEntry(slug);
|
|
320
336
|
return this.attachLazyContent(meta);
|
|
321
337
|
}
|
|
322
338
|
const cachedMeta = await this.ctx.docCache.getMeta(this.ctx.collection, slug);
|
|
323
339
|
if (cachedMeta) {
|
|
324
340
|
if (this.ctx.ttlMs !== void 0 && isStale(cachedMeta.cachedAt, this.ctx.ttlMs)) {
|
|
325
341
|
this.ctx.logger?.debug?.("キャッシュ期限切れ(TTL)、フェッチ", {
|
|
326
|
-
operation: "
|
|
342
|
+
operation: "find",
|
|
327
343
|
slug,
|
|
328
344
|
collection: this.ctx.collection,
|
|
329
345
|
cacheAdapter: this.ctx.docCacheName
|
|
330
346
|
});
|
|
331
347
|
this.ctx.hooks.onCacheMiss?.(slug);
|
|
332
|
-
const item = await this.
|
|
348
|
+
const item = await this.fetchRaw(slug);
|
|
333
349
|
if (!item) return null;
|
|
334
350
|
const meta = await this.persistMeta(slug, item);
|
|
335
|
-
await this.
|
|
351
|
+
await this.invalidateContentEntry(slug);
|
|
336
352
|
return this.attachLazyContent(meta);
|
|
337
353
|
}
|
|
338
354
|
const bg = this.checkAndUpdateItemBg(slug, cachedMeta);
|
|
339
355
|
if (this.ctx.waitUntil) this.ctx.waitUntil(bg);
|
|
340
356
|
this.ctx.logger?.debug?.("キャッシュヒット", {
|
|
341
|
-
operation: "
|
|
357
|
+
operation: "find",
|
|
342
358
|
slug,
|
|
343
359
|
collection: this.ctx.collection,
|
|
344
360
|
cacheAdapter: this.ctx.docCacheName,
|
|
@@ -348,13 +364,13 @@ var CollectionClientImpl = class {
|
|
|
348
364
|
return this.attachLazyContent(cachedMeta);
|
|
349
365
|
}
|
|
350
366
|
this.ctx.logger?.debug?.("キャッシュミス、フェッチ", {
|
|
351
|
-
operation: "
|
|
367
|
+
operation: "find",
|
|
352
368
|
slug,
|
|
353
369
|
collection: this.ctx.collection,
|
|
354
370
|
cacheAdapter: this.ctx.docCacheName
|
|
355
371
|
});
|
|
356
372
|
this.ctx.hooks.onCacheMiss?.(slug);
|
|
357
|
-
const item = await this.
|
|
373
|
+
const item = await this.fetchRaw(slug);
|
|
358
374
|
if (!item) return null;
|
|
359
375
|
const meta = await this.persistMeta(slug, item, { background: true });
|
|
360
376
|
return this.attachLazyContent(meta);
|
|
@@ -363,33 +379,32 @@ var CollectionClientImpl = class {
|
|
|
363
379
|
return applyListOptions(await this.fetchList(), opts);
|
|
364
380
|
}
|
|
365
381
|
async params() {
|
|
366
|
-
return (await this.fetchList()).map((item) =>
|
|
382
|
+
return (await this.fetchList()).map((item) => item.slug);
|
|
367
383
|
}
|
|
368
384
|
async check(slug, currentVersion) {
|
|
369
|
-
const raw = await this.
|
|
385
|
+
const raw = await this.fetchRaw(slug);
|
|
370
386
|
if (!raw) return null;
|
|
371
387
|
if (raw.lastEditedTime === currentVersion) return { stale: false };
|
|
372
388
|
const meta = await this.persistMeta(slug, raw);
|
|
373
|
-
await this.
|
|
389
|
+
await this.invalidateContentEntry(slug);
|
|
374
390
|
return {
|
|
375
391
|
stale: true,
|
|
376
392
|
item: this.attachLazyContent(meta)
|
|
377
393
|
};
|
|
378
394
|
}
|
|
379
|
-
async
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
}
|
|
395
|
+
async adjacent(slug, opts) {
|
|
396
|
+
const items = applyListOptions(await this.fetchList(), { sort: opts?.sort });
|
|
397
|
+
const index = items.findIndex((it) => it.slug === slug);
|
|
398
|
+
if (index === -1) return {
|
|
399
|
+
prev: null,
|
|
400
|
+
next: null
|
|
401
|
+
};
|
|
402
|
+
return {
|
|
403
|
+
prev: index > 0 ? items[index - 1] ?? null : null,
|
|
404
|
+
next: index < items.length - 1 ? items[index + 1] ?? null : null
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
async invalidateImpl() {
|
|
393
408
|
this.ctx.logger?.debug?.("コレクション全体のキャッシュを無効化", {
|
|
394
409
|
operation: "cache.invalidate",
|
|
395
410
|
collection: this.ctx.collection,
|
|
@@ -397,11 +412,23 @@ var CollectionClientImpl = class {
|
|
|
397
412
|
});
|
|
398
413
|
await this.ctx.docCache.invalidate({ collection: this.ctx.collection });
|
|
399
414
|
}
|
|
415
|
+
async invalidateItemImpl(slug) {
|
|
416
|
+
this.ctx.logger?.debug?.("アイテムキャッシュを無効化", {
|
|
417
|
+
operation: "cache.invalidateItem",
|
|
418
|
+
collection: this.ctx.collection,
|
|
419
|
+
cacheAdapter: this.ctx.docCacheName,
|
|
420
|
+
slug
|
|
421
|
+
});
|
|
422
|
+
await this.ctx.docCache.invalidate({
|
|
423
|
+
collection: this.ctx.collection,
|
|
424
|
+
slug
|
|
425
|
+
});
|
|
426
|
+
}
|
|
400
427
|
async warmImpl(opts) {
|
|
401
428
|
const items = await this.fetchListRaw();
|
|
402
429
|
const concurrency = opts?.concurrency ?? this.ctx.maxConcurrent;
|
|
403
430
|
let ok = 0;
|
|
404
|
-
|
|
431
|
+
const failed = [];
|
|
405
432
|
for (let i = 0; i < items.length; i += concurrency) {
|
|
406
433
|
const chunk = items.slice(i, i + concurrency);
|
|
407
434
|
await Promise.all(chunk.map(async (item) => {
|
|
@@ -411,7 +438,10 @@ var CollectionClientImpl = class {
|
|
|
411
438
|
await this.ctx.docCache.setContent(this.ctx.collection, item.slug, content);
|
|
412
439
|
ok++;
|
|
413
440
|
} catch (err) {
|
|
414
|
-
failed
|
|
441
|
+
failed.push({
|
|
442
|
+
slug: item.slug,
|
|
443
|
+
error: err
|
|
444
|
+
});
|
|
415
445
|
this.ctx.logger?.warn?.("warm: アイテムの事前レンダリングに失敗", {
|
|
416
446
|
slug: item.slug,
|
|
417
447
|
pageId: item.id,
|
|
@@ -430,18 +460,6 @@ var CollectionClientImpl = class {
|
|
|
430
460
|
failed
|
|
431
461
|
};
|
|
432
462
|
}
|
|
433
|
-
async adjacentImpl(slug, opts) {
|
|
434
|
-
const items = applyListOptions(await this.fetchList(), { sort: opts?.sort });
|
|
435
|
-
const index = items.findIndex((it) => it.slug === slug);
|
|
436
|
-
if (index === -1) return {
|
|
437
|
-
prev: null,
|
|
438
|
-
next: null
|
|
439
|
-
};
|
|
440
|
-
return {
|
|
441
|
-
prev: index > 0 ? items[index - 1] ?? null : null,
|
|
442
|
-
next: index < items.length - 1 ? items[index + 1] ?? null : null
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
463
|
async persistMeta(slug, item, opts = {}) {
|
|
446
464
|
let meta = buildCachedItemMeta(item, this.ctx.source);
|
|
447
465
|
if (this.ctx.hooks.beforeCacheMeta) meta = await this.ctx.hooks.beforeCacheMeta(meta);
|
|
@@ -450,7 +468,7 @@ var CollectionClientImpl = class {
|
|
|
450
468
|
else await save;
|
|
451
469
|
return meta;
|
|
452
470
|
}
|
|
453
|
-
async
|
|
471
|
+
async invalidateContentEntry(slug) {
|
|
454
472
|
await this.ctx.docCache.invalidate({
|
|
455
473
|
collection: this.ctx.collection,
|
|
456
474
|
slug,
|
|
@@ -507,11 +525,11 @@ var CollectionClientImpl = class {
|
|
|
507
525
|
if (!payloadPromise) payloadPromise = this.loadOrBuildContent(slug, item);
|
|
508
526
|
return payloadPromise;
|
|
509
527
|
};
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
528
|
+
return Object.assign(Object.create(null), item, {
|
|
529
|
+
html: async () => (await loadPayload()).html,
|
|
530
|
+
markdown: async () => (await loadPayload()).markdown,
|
|
531
|
+
blocks: async () => (await loadPayload()).blocks
|
|
532
|
+
});
|
|
515
533
|
}
|
|
516
534
|
async fetchList() {
|
|
517
535
|
const cached = await this.ctx.docCache.getList(this.ctx.collection);
|
|
@@ -558,13 +576,13 @@ var CollectionClientImpl = class {
|
|
|
558
576
|
}
|
|
559
577
|
async checkAndUpdateItemBg(slug, cached) {
|
|
560
578
|
try {
|
|
561
|
-
const item = await this.
|
|
579
|
+
const item = await this.fetchRaw(slug);
|
|
562
580
|
if (!item) return;
|
|
563
581
|
if (this.ctx.source.getLastModified(item) !== cached.notionUpdatedAt) {
|
|
564
582
|
const meta = await this.persistMeta(slug, item);
|
|
565
|
-
await this.
|
|
583
|
+
await this.invalidateContentEntry(slug);
|
|
566
584
|
this.ctx.logger?.debug?.("SWR: 差分を検出、メタを差し替え", {
|
|
567
|
-
operation: "
|
|
585
|
+
operation: "find:bg",
|
|
568
586
|
slug,
|
|
569
587
|
collection: this.ctx.collection,
|
|
570
588
|
notionUpdatedAt: cached.notionUpdatedAt
|
|
@@ -577,7 +595,7 @@ var CollectionClientImpl = class {
|
|
|
577
595
|
cachedAt: Date.now()
|
|
578
596
|
});
|
|
579
597
|
this.ctx.logger?.debug?.("SWR: 差分なし、TTL をリセット", {
|
|
580
|
-
operation: "
|
|
598
|
+
operation: "find:bg",
|
|
581
599
|
slug,
|
|
582
600
|
collection: this.ctx.collection
|
|
583
601
|
});
|
|
@@ -662,11 +680,11 @@ var CollectionClientImpl = class {
|
|
|
662
680
|
return true;
|
|
663
681
|
});
|
|
664
682
|
}
|
|
665
|
-
async
|
|
683
|
+
async fetchRaw(slug) {
|
|
666
684
|
const retryOpts = {
|
|
667
685
|
...this.ctx.retryConfig,
|
|
668
686
|
onRetry: (attempt, status) => {
|
|
669
|
-
this.ctx.logger?.warn?.("
|
|
687
|
+
this.ctx.logger?.warn?.("find() リトライ中", {
|
|
670
688
|
attempt,
|
|
671
689
|
status,
|
|
672
690
|
slug
|
|
@@ -697,8 +715,8 @@ function matchesWhere(item, where) {
|
|
|
697
715
|
function applyListOptions(items, opts) {
|
|
698
716
|
if (!opts) return sortByPublishedAtDesc(items);
|
|
699
717
|
let result = items;
|
|
700
|
-
if (opts.
|
|
701
|
-
const allow = new Set(Array.isArray(opts.
|
|
718
|
+
if (opts.statuses) {
|
|
719
|
+
const allow = new Set(Array.isArray(opts.statuses) ? opts.statuses : [opts.statuses]);
|
|
702
720
|
result = result.filter((it) => it.status != null && allow.has(it.status));
|
|
703
721
|
}
|
|
704
722
|
if (opts.tag) {
|
|
@@ -841,12 +859,11 @@ const DEFAULT_IMAGE_PROXY_BASE = "/api/images";
|
|
|
841
859
|
/**
|
|
842
860
|
* `cache` オプションから document / image オペレーションを解決する。
|
|
843
861
|
*
|
|
844
|
-
* -
|
|
845
|
-
* - 単体で渡された場合は `handles` の領域だけ反映、片側は noop
|
|
862
|
+
* - 各 adapter の `handles` を見て先勝ち (最初に見つかったもの) で振り分ける
|
|
846
863
|
* - 未指定なら両方 noop
|
|
847
864
|
*/
|
|
848
865
|
function resolveCache(cache) {
|
|
849
|
-
const adapters = cache
|
|
866
|
+
const adapters = cache ?? [];
|
|
850
867
|
let doc = noopDocOps;
|
|
851
868
|
let docName = "noop-document";
|
|
852
869
|
let img = noopImgOps;
|
|
@@ -908,7 +925,8 @@ function applyLogLevel(logger, minLevel) {
|
|
|
908
925
|
* publishedStatuses: ["公開済み"],
|
|
909
926
|
* }
|
|
910
927
|
* },
|
|
911
|
-
* cache: memoryCache(
|
|
928
|
+
* cache: [memoryCache()],
|
|
929
|
+
* swr: { ttlMs: 5 * 60_000 },
|
|
912
930
|
* });
|
|
913
931
|
*/
|
|
914
932
|
function createCMS(opts) {
|
|
@@ -936,7 +954,7 @@ function createCMS(opts) {
|
|
|
936
954
|
});
|
|
937
955
|
}
|
|
938
956
|
const cacheRes = resolveCache(opts.cache);
|
|
939
|
-
const ttlMs = opts.ttlMs;
|
|
957
|
+
const ttlMs = opts.swr?.ttlMs;
|
|
940
958
|
const imageProxyBase = opts.imageProxyBase ?? DEFAULT_IMAGE_PROXY_BASE;
|
|
941
959
|
const contentConfig = opts.content;
|
|
942
960
|
const rendererFn = opts.renderer;
|
|
@@ -988,15 +1006,15 @@ function createCMS(opts) {
|
|
|
988
1006
|
});
|
|
989
1007
|
}
|
|
990
1008
|
const globalOps = {
|
|
991
|
-
|
|
992
|
-
async
|
|
1009
|
+
collections: collectionNames,
|
|
1010
|
+
async invalidate(scope) {
|
|
993
1011
|
logger?.debug?.("グローバルキャッシュを無効化", {
|
|
994
|
-
operation: "
|
|
1012
|
+
operation: "invalidate",
|
|
995
1013
|
cacheAdapter: cacheRes.docName
|
|
996
1014
|
});
|
|
997
1015
|
await cacheRes.doc.invalidate(scope ?? "all");
|
|
998
1016
|
},
|
|
999
|
-
|
|
1017
|
+
handler(handlerOpts) {
|
|
1000
1018
|
return createHandler({
|
|
1001
1019
|
imageCache: cacheRes.img,
|
|
1002
1020
|
async parseWebhookFor(collection, req, webhookSecret) {
|
|
@@ -1020,10 +1038,10 @@ function createCMS(opts) {
|
|
|
1020
1038
|
});
|
|
1021
1039
|
return ds.parseWebhook(req, { secret: webhookSecret });
|
|
1022
1040
|
},
|
|
1023
|
-
revalidate: (scope) => globalOps
|
|
1041
|
+
revalidate: (scope) => globalOps.invalidate(scope)
|
|
1024
1042
|
}, handlerOpts);
|
|
1025
1043
|
},
|
|
1026
|
-
|
|
1044
|
+
getCachedImage(hash) {
|
|
1027
1045
|
return cacheRes.img.get(hash);
|
|
1028
1046
|
}
|
|
1029
1047
|
};
|
|
@@ -1035,6 +1053,6 @@ function definePlugin(plugin) {
|
|
|
1035
1053
|
return plugin;
|
|
1036
1054
|
}
|
|
1037
1055
|
//#endregion
|
|
1038
|
-
export { CMSError, CollectionClientImpl, DEFAULT_RETRY_CONFIG, collectionKey, createCMS, createHandler, definePlugin, isCMSError, isCMSErrorInNamespace, isStale, memoryCache, mergeHooks, mergeLoggers, noopDocOps, noopImgOps, sha256Hex, withRetry };
|
|
1056
|
+
export { CMSError, CollectionClientImpl, DEFAULT_RETRY_CONFIG, collectionKey, createCMS, createHandler, definePlugin, isCMSError, isCMSErrorInNamespace, isStale, matchCMSError, memoryCache, mergeHooks, mergeLoggers, noopDocOps, noopImgOps, sha256Hex, withRetry };
|
|
1039
1057
|
|
|
1040
1058
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/cache.ts","../src/cache/noop.ts","../src/image.ts","../src/rendering.ts","../src/retry.ts","../src/collection.ts","../src/handler.ts","../src/cms.ts","../src/types/plugin.ts"],"sourcesContent":["/** 文字列をSHA-256でハッシュ化し、16進数文字列として返す。画像キーの生成に使用。 */\nexport async function sha256Hex(input: string): Promise<string> {\n const data = new TextEncoder().encode(input);\n const hash = await crypto.subtle.digest(\"SHA-256\", data);\n return Array.from(new Uint8Array(hash))\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\n/**\n * キャッシュが有効期限切れかどうかを判定する。\n * ttlMs が未指定の場合は常に false(無期限有効)を返す。\n */\nexport function isStale(cachedAt: number, ttlMs?: number): boolean {\n if (ttlMs === undefined) return false;\n return Date.now() - cachedAt > ttlMs;\n}\n","import type {\n BaseContentItem,\n CachedItemContent,\n CachedItemList,\n CachedItemMeta,\n DocumentCacheOps,\n ImageCacheOps,\n StorageBinary,\n} from \"../types/index\";\n\n/** 何もキャッシュしないドキュメントオペレーション。常に null を返す。 */\nconst noopDoc: DocumentCacheOps = {\n getList<T extends BaseContentItem>(\n _collection: string,\n ): Promise<CachedItemList<T> | null> {\n return Promise.resolve(null);\n },\n setList<T extends BaseContentItem>(\n _collection: string,\n _data: CachedItemList<T>,\n ): Promise<void> {\n return Promise.resolve();\n },\n getMeta<T extends BaseContentItem>(\n _collection: string,\n _slug: string,\n ): Promise<CachedItemMeta<T> | null> {\n return Promise.resolve(null);\n },\n setMeta<T extends BaseContentItem>(\n _collection: string,\n _slug: string,\n _data: CachedItemMeta<T>,\n ): Promise<void> {\n return Promise.resolve();\n },\n getContent(\n _collection: string,\n _slug: string,\n ): Promise<CachedItemContent | null> {\n return Promise.resolve(null);\n },\n setContent(\n _collection: string,\n _slug: string,\n _data: CachedItemContent,\n ): Promise<void> {\n return Promise.resolve();\n },\n invalidate(): Promise<void> {\n return Promise.resolve();\n },\n};\n\n/** 何もキャッシュしない画像オペレーション。 */\nconst noopImg: ImageCacheOps = {\n get(_hash: string): Promise<StorageBinary | null> {\n return Promise.resolve(null);\n },\n set(): Promise<void> {\n return Promise.resolve();\n },\n};\n\n/**\n * 何もキャッシュしないアダプタ。`createCMS({ cache })` 未指定時の内部デフォルト。\n * テストでも使える。\n */\nexport const noopDocOps: DocumentCacheOps = noopDoc;\nexport const noopImgOps: ImageCacheOps = noopImg;\n","import { sha256Hex } from \"./cache\";\nimport { CMSError, isCMSError } from \"./errors\";\nimport type { ImageCacheOps, Logger, StorageBinary } from \"./types/index\";\n\n/**\n * レスポンスの Content-Type ヘッダから画像の MIME タイプを取り出す。\n * ヘッダがない、または image/* でない場合は CMSError を投げる。\n * URL 拡張子からの推測や jpeg デフォルトは行わない。\n */\nfunction pickImageContentType(\n headerValue: string | null,\n notionUrl: string,\n): string {\n if (!headerValue) {\n throw new CMSError({\n code: \"cache/image_invalid_content_type\",\n message: \"Image response missing Content-Type header.\",\n context: { operation: \"fetchAndCacheImage:contentType\", notionUrl },\n });\n }\n const value = (headerValue.split(\";\")[0] ?? headerValue).trim().toLowerCase();\n if (!value.startsWith(\"image/\")) {\n throw new CMSError({\n code: \"cache/image_invalid_content_type\",\n message: `Image response has non-image Content-Type: ${value}`,\n context: {\n operation: \"fetchAndCacheImage:contentType\",\n notionUrl,\n contentType: value,\n },\n });\n }\n return value;\n}\n\n/**\n * Notion画像URLをfetchして ImageCacheOps にキャッシュし、プロキシURL を返す。\n * 既存キャッシュがあれば再fetchしない。\n */\nasync function fetchAndCacheImage(\n cache: ImageCacheOps,\n cacheName: string,\n notionUrl: string,\n hash: string,\n imageProxyBase: string,\n logger?: Logger,\n): Promise<string> {\n const proxyUrl = `${imageProxyBase}/${hash}`;\n\n const existing = await cache.get(hash);\n if (existing) {\n logger?.debug?.(\"画像キャッシュヒット\", {\n operation: \"fetchAndCacheImage\",\n cacheAdapter: cacheName,\n imageHash: hash,\n });\n return proxyUrl;\n }\n\n logger?.debug?.(\"画像キャッシュミス、Notion からフェッチ\", {\n operation: \"fetchAndCacheImage\",\n cacheAdapter: cacheName,\n imageHash: hash,\n });\n\n try {\n const response = await fetch(notionUrl, {\n signal: AbortSignal.timeout(10_000),\n });\n if (!response.ok) {\n throw new CMSError({\n code: \"cache/image_fetch_failed\",\n message: `Failed to fetch Notion image: HTTP ${response.status}`,\n context: {\n operation: \"fetchAndCacheImage\",\n notionUrl,\n httpStatus: response.status,\n },\n });\n }\n\n const data = await response.arrayBuffer();\n const contentType = pickImageContentType(\n response.headers.get(\"content-type\"),\n notionUrl,\n );\n await cache.set(hash, data, contentType);\n logger?.debug?.(\"画像をキャッシュに保存\", {\n operation: \"fetchAndCacheImage\",\n cacheAdapter: cacheName,\n imageHash: hash,\n });\n } catch (err) {\n if (isCMSError(err)) throw err;\n throw new CMSError({\n code: \"cache/io_failed\",\n message: \"Failed to fetch or cache Notion image.\",\n cause: err,\n context: { operation: \"fetchAndCacheImage\", notionUrl },\n });\n }\n\n return proxyUrl;\n}\n\n/**\n * `ImageCacheOps` と `imageProxyBase` から `cacheImage` 関数を構築する。\n * 返り値は Notion 画像 URL を受け取り、SHA-256 ハッシュをキャッシュキーとして\n * {@link ImageCacheOps} に保存後、プロキシ URL を返す。\n *\n * ハッシュのメモ化はファクトリ呼び出し単位でスコープ化されており、\n * インスタンス間でキャッシュを共有しない。\n */\nexport function buildCacheImageFn(\n cache: ImageCacheOps,\n cacheName: string,\n imageProxyBase: string,\n logger?: Logger,\n): (notionUrl: string) => Promise<string> {\n const hashMemo = new Map<string, string>();\n return async (notionUrl) => {\n let hash = hashMemo.get(notionUrl);\n if (hash === undefined) {\n hash = await sha256Hex(notionUrl);\n hashMemo.set(notionUrl, hash);\n }\n return fetchAndCacheImage(\n cache,\n cacheName,\n notionUrl,\n hash,\n imageProxyBase,\n logger,\n );\n };\n}\n\nexport type { StorageBinary };\n","import type { ContentBlock } from \"./content/blocks\";\nimport { CMSError, isCMSError } from \"./errors\";\nimport { buildCacheImageFn } from \"./image\";\nimport type {\n BaseContentItem,\n CachedItemContent,\n CachedItemMeta,\n CMSHooks,\n ContentConfig,\n DataSource,\n ImageCacheOps,\n Logger,\n RendererFn,\n} from \"./types/index\";\n\n/** 本文レンダリングに必要な依存を束ねたコンテキスト。 */\nexport interface RenderContext<T extends BaseContentItem> {\n source: DataSource<T>;\n rendererFn: RendererFn;\n imgCache: ImageCacheOps;\n imgCacheName: string;\n hasImageCache: boolean;\n imageProxyBase: string;\n contentConfig: ContentConfig | undefined;\n hooks: CMSHooks<T>;\n logger: Logger | undefined;\n}\n\n/**\n * メタデータキャッシュエントリを生成する。Notion API も renderer も呼ばない軽量関数。\n */\nexport function buildCachedItemMeta<T extends BaseContentItem>(\n item: T,\n source: DataSource<T>,\n): CachedItemMeta<T> {\n return {\n item,\n notionUpdatedAt: source.getLastModified(item),\n cachedAt: Date.now(),\n };\n}\n\n/**\n * アイテム本文を Markdown ロード → blocks 生成 → HTML レンダリング → フック適用まで\n * 実行し、本文キャッシュ用の `CachedItemContent` を返す。\n */\nexport async function buildCachedItemContent<T extends BaseContentItem>(\n item: T,\n ctx: RenderContext<T>,\n): Promise<CachedItemContent> {\n const start = Date.now();\n ctx.logger?.info?.(\"コンテンツのレンダリング開始\", {\n slug: item.slug,\n pageId: item.id,\n });\n ctx.hooks.onRenderStart?.(item.slug);\n\n let markdown: string;\n try {\n markdown = await ctx.source.loadMarkdown(item);\n } catch (err) {\n if (isCMSError(err)) throw err;\n throw new CMSError({\n code: \"source/load_markdown_failed\",\n message: \"Failed to load markdown from source.\",\n cause: err,\n context: {\n operation: \"buildCachedItemContent:loadMarkdown\",\n pageId: item.id,\n slug: item.slug,\n },\n });\n }\n\n let blocks: ContentBlock[];\n try {\n blocks = await ctx.source.loadBlocks(item);\n } catch (err) {\n if (isCMSError(err)) throw err;\n throw new CMSError({\n code: \"source/load_blocks_failed\",\n message: \"Failed to load blocks from source.\",\n cause: err,\n context: {\n operation: \"buildCachedItemContent:loadBlocks\",\n pageId: item.id,\n slug: item.slug,\n },\n });\n }\n\n const cacheImage = ctx.hasImageCache\n ? buildCacheImageFn(\n ctx.imgCache,\n ctx.imgCacheName,\n ctx.imageProxyBase,\n ctx.logger,\n )\n : undefined;\n\n let html: string;\n try {\n html = await ctx.rendererFn(markdown, {\n imageProxyBase: ctx.imageProxyBase,\n cacheImage,\n remarkPlugins: ctx.contentConfig?.remarkPlugins,\n rehypePlugins: ctx.contentConfig?.rehypePlugins,\n });\n } catch (err) {\n if (isCMSError(err)) throw err;\n throw new CMSError({\n code: \"renderer/failed\",\n message: \"Failed to render markdown.\",\n cause: err,\n context: {\n operation: \"buildCachedItemContent:renderMarkdown\",\n pageId: item.id,\n slug: item.slug,\n },\n });\n }\n\n if (ctx.hooks.afterRender) {\n html = await ctx.hooks.afterRender(html, item);\n }\n\n let result: CachedItemContent = {\n html,\n blocks,\n markdown,\n notionUpdatedAt: ctx.source.getLastModified(item),\n cachedAt: Date.now(),\n };\n\n if (ctx.hooks.beforeCacheContent) {\n result = await ctx.hooks.beforeCacheContent(result, item);\n }\n\n const durationMs = Date.now() - start;\n ctx.logger?.info?.(\"コンテンツのレンダリング完了\", {\n slug: item.slug,\n durationMs,\n });\n ctx.hooks.onRenderEnd?.(item.slug, durationMs);\n\n return result;\n}\n","export interface RetryConfig {\n retryOn: number[];\n maxRetries: number;\n baseDelayMs: number;\n /** true のとき指数バックオフにランダムジッターを加える(Thundering Herd 対策)。デフォルト: true */\n jitter?: boolean;\n onRetry?: (attempt: number, status: number) => void;\n}\n\nexport const DEFAULT_RETRY_CONFIG: RetryConfig = {\n retryOn: [429, 502, 503],\n maxRetries: 4,\n baseDelayMs: 1000,\n jitter: true,\n};\n\n/**\n * 指数バックオフ(オプションでジッター付き)でリトライする。\n *\n * `config.retryOn` に含まれるステータスコードを持つエラーのみリトライ対象。\n * 遅延は `baseDelayMs * 2^attempt` の指数バックオフ。\n * `jitter` が `true`(デフォルト)の場合、0.5〜1.0 の乱数係数を乗算して\n * Thundering Herd を防ぐ。`false` にすると確定的な遅延になる。\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n config: RetryConfig,\n): Promise<T> {\n let lastError: unknown;\n for (let attempt = 0; attempt <= config.maxRetries; attempt++) {\n try {\n return await fn();\n } catch (err) {\n const status = (err as { status?: number }).status;\n if (status === undefined || !config.retryOn.includes(status)) {\n throw err;\n }\n lastError = err;\n if (attempt < config.maxRetries) {\n config.onRetry?.(attempt + 1, status);\n const jitterFactor =\n config.jitter !== false ? 0.5 + Math.random() * 0.5 : 1;\n const delay = config.baseDelayMs * 2 ** attempt * jitterFactor;\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n }\n throw lastError;\n}\n","import { isStale } from \"./cache\";\nimport { CMSError, isCMSError } from \"./errors\";\nimport type { RenderContext } from \"./rendering\";\nimport { buildCachedItemContent, buildCachedItemMeta } from \"./rendering\";\nimport type { RetryConfig } from \"./retry\";\nimport { withRetry } from \"./retry\";\nimport type {\n AdjacencyOptions,\n BaseContentItem,\n CachedItemContent,\n CachedItemList,\n CachedItemMeta,\n CheckResult,\n CMSHooks,\n CollectionCacheOps,\n CollectionClient,\n DataSource,\n DocumentCacheOps,\n GetOptions,\n ItemWithRender,\n ListOptions,\n Logger,\n SortOption,\n WarmOptions,\n WhereClause,\n} from \"./types/index\";\n\n/**\n * コレクション別キャッシュキーを生成する。\n * item: `{collection}:{slug}` / list: `{collection}`\n *\n * (Cache adapter 内部のキー戦略はアダプタごとに異なるが、\n * 表示や再計算用に core 側でも公開ヘルパーを提供する)\n */\nexport function collectionKey(collection: string, slug?: string): string {\n return slug ? `${collection}:${slug}` : collection;\n}\n\n/** 単一コレクションの DataSource + SWR キャッシュ依存を束ねたコンテキスト。 */\nexport interface CollectionContext<T extends BaseContentItem> {\n collection: string;\n source: DataSource<T>;\n docCache: DocumentCacheOps;\n docCacheName: string;\n render: RenderContext<T>;\n hooks: CMSHooks<T>;\n logger: Logger | undefined;\n ttlMs: number | undefined;\n publishedStatuses: string[];\n accessibleStatuses: string[];\n retryConfig: RetryConfig;\n maxConcurrent: number;\n waitUntil: ((p: Promise<unknown>) => void) | undefined;\n /**\n * slug として使うフィールド名 (CLI 生成の `CollectionDef.slugField`)。\n * `source.properties[slugField].notion` を Notion プロパティ名として\n * `findByProp` を呼び出す。\n */\n slugField: string;\n}\n\n/** CollectionClient の実装。ユーザーは `createCMS` 経由でインスタンスを受け取る。 */\nexport class CollectionClientImpl<T extends BaseContentItem>\n implements CollectionClient<T>\n{\n readonly cache: CollectionCacheOps<T>;\n\n constructor(private readonly ctx: CollectionContext<T>) {\n this.cache = {\n invalidate: (slug?: string) => this.invalidateImpl(slug),\n warm: (opts?: WarmOptions) => this.warmImpl(opts),\n adjacent: (slug, opts) => this.adjacentImpl(slug, opts),\n };\n }\n\n // ── 基本取得 ──────────────────────────────────────────────────────────\n\n async get(\n slug: string,\n opts: GetOptions = {},\n ): Promise<ItemWithRender<T> | null> {\n // fresh: 強制ブロッキング取得\n if (opts.fresh) {\n this.ctx.hooks.onCacheMiss?.(slug);\n const item = await this.findRaw(slug);\n if (!item) return null;\n const meta = await this.persistMeta(slug, item);\n await this.invalidateContent(slug);\n return this.attachLazyContent(meta);\n }\n\n const cachedMeta = await this.ctx.docCache.getMeta<T>(\n this.ctx.collection,\n slug,\n );\n if (cachedMeta) {\n if (\n this.ctx.ttlMs !== undefined &&\n isStale(cachedMeta.cachedAt, this.ctx.ttlMs)\n ) {\n // TTL 切れ: ブロッキング再取得\n this.ctx.logger?.debug?.(\"キャッシュ期限切れ(TTL)、フェッチ\", {\n operation: \"get\",\n slug,\n collection: this.ctx.collection,\n cacheAdapter: this.ctx.docCacheName,\n });\n this.ctx.hooks.onCacheMiss?.(slug);\n const item = await this.findRaw(slug);\n if (!item) return null;\n const meta = await this.persistMeta(slug, item);\n await this.invalidateContent(slug);\n return this.attachLazyContent(meta);\n }\n // SWR: キャッシュ即時返却 + バックグラウンド差分チェック\n const bg = this.checkAndUpdateItemBg(slug, cachedMeta);\n if (this.ctx.waitUntil) this.ctx.waitUntil(bg);\n this.ctx.logger?.debug?.(\"キャッシュヒット\", {\n operation: \"get\",\n slug,\n collection: this.ctx.collection,\n cacheAdapter: this.ctx.docCacheName,\n cachedAt: cachedMeta.cachedAt,\n });\n this.ctx.hooks.onCacheHit?.(slug, cachedMeta);\n return this.attachLazyContent(cachedMeta);\n }\n\n // メタ未キャッシュ: 同期フェッチ (保存はバックグラウンド可)\n this.ctx.logger?.debug?.(\"キャッシュミス、フェッチ\", {\n operation: \"get\",\n slug,\n collection: this.ctx.collection,\n cacheAdapter: this.ctx.docCacheName,\n });\n this.ctx.hooks.onCacheMiss?.(slug);\n const item = await this.findRaw(slug);\n if (!item) return null;\n const meta = await this.persistMeta(slug, item, { background: true });\n return this.attachLazyContent(meta);\n }\n\n async list(opts?: ListOptions<T>): Promise<T[]> {\n const allItems = await this.fetchList();\n return applyListOptions(allItems, opts);\n }\n\n async params(): Promise<{ slug: string }[]> {\n const items = await this.fetchList();\n return items.map((item) => ({ slug: item.slug }));\n }\n\n async check(\n slug: string,\n currentVersion: string,\n ): Promise<CheckResult<T> | null> {\n const raw = await this.findRaw(slug);\n if (!raw) return null;\n if (raw.lastEditedTime === currentVersion) return { stale: false };\n const meta = await this.persistMeta(slug, raw);\n await this.invalidateContent(slug);\n return { stale: true, item: this.attachLazyContent(meta) };\n }\n\n // ── キャッシュ操作 ────────────────────────────────────────────────────\n\n private async invalidateImpl(slug?: string): Promise<void> {\n if (slug !== undefined) {\n this.ctx.logger?.debug?.(\"アイテムキャッシュを無効化\", {\n operation: \"cache.invalidate\",\n collection: this.ctx.collection,\n cacheAdapter: this.ctx.docCacheName,\n slug,\n });\n await this.ctx.docCache.invalidate({\n collection: this.ctx.collection,\n slug,\n });\n return;\n }\n this.ctx.logger?.debug?.(\"コレクション全体のキャッシュを無効化\", {\n operation: \"cache.invalidate\",\n collection: this.ctx.collection,\n cacheAdapter: this.ctx.docCacheName,\n });\n await this.ctx.docCache.invalidate({ collection: this.ctx.collection });\n }\n\n private async warmImpl(\n opts?: WarmOptions,\n ): Promise<{ ok: number; failed: number }> {\n const items = await this.fetchListRaw();\n const concurrency = opts?.concurrency ?? this.ctx.maxConcurrent;\n let ok = 0;\n let failed = 0;\n\n for (let i = 0; i < items.length; i += concurrency) {\n const chunk = items.slice(i, i + concurrency);\n await Promise.all(\n chunk.map(async (item) => {\n try {\n await this.persistMeta(item.slug, item);\n const content = await buildCachedItemContent(item, this.ctx.render);\n await this.ctx.docCache.setContent(\n this.ctx.collection,\n item.slug,\n content,\n );\n ok++;\n } catch (err) {\n failed++;\n this.ctx.logger?.warn?.(\"warm: アイテムの事前レンダリングに失敗\", {\n slug: item.slug,\n pageId: item.id,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }),\n );\n opts?.onProgress?.(Math.min(i + concurrency, items.length), items.length);\n }\n\n await this.ctx.docCache.setList(this.ctx.collection, {\n items,\n cachedAt: Date.now(),\n });\n return { ok, failed };\n }\n\n private async adjacentImpl(\n slug: string,\n opts?: AdjacencyOptions<T>,\n ): Promise<{ prev: T | null; next: T | null }> {\n const items = applyListOptions(await this.fetchList(), {\n sort: opts?.sort,\n });\n const index = items.findIndex((it) => it.slug === slug);\n if (index === -1) return { prev: null, next: null };\n return {\n prev: index > 0 ? (items[index - 1] ?? null) : null,\n next: index < items.length - 1 ? (items[index + 1] ?? null) : null,\n };\n }\n\n // ── 内部 ──────────────────────────────────────────────────────────────\n\n private async persistMeta(\n slug: string,\n item: T,\n opts: { background?: boolean } = {},\n ): Promise<CachedItemMeta<T>> {\n let meta = buildCachedItemMeta(item, this.ctx.source);\n if (this.ctx.hooks.beforeCacheMeta) {\n meta = await this.ctx.hooks.beforeCacheMeta(meta);\n }\n const save = this.ctx.docCache.setMeta(this.ctx.collection, slug, meta);\n if (opts.background && this.ctx.waitUntil) {\n this.ctx.waitUntil(save);\n } else {\n await save;\n }\n return meta;\n }\n\n private async invalidateContent(slug: string): Promise<void> {\n await this.ctx.docCache.invalidate({\n collection: this.ctx.collection,\n slug,\n kind: \"content\",\n });\n }\n\n /**\n * 本文キャッシュをロードする。キャッシュが無いか、メタとの整合性が取れない場合は\n * 再生成して書き戻す。\n */\n private async loadOrBuildContent(\n slug: string,\n item: T,\n ): Promise<CachedItemContent> {\n const expected = this.ctx.source.getLastModified(item);\n const cached = await this.ctx.docCache.getContent(\n this.ctx.collection,\n slug,\n );\n if (cached && cached.notionUpdatedAt === expected) return cached;\n\n const fresh = await buildCachedItemContent(item, this.ctx.render);\n await this.ctx.docCache.setContent(this.ctx.collection, slug, fresh);\n this.ctx.hooks.onContentRevalidated?.(slug, fresh);\n return fresh;\n }\n\n /** メタ既知の状態で本文だけバックグラウンド再生成する。エラーは onSwrError フックに通知する。 */\n private async rebuildContentBg(slug: string, item: T): Promise<void> {\n try {\n const fresh = await buildCachedItemContent(item, this.ctx.render);\n await this.ctx.docCache.setContent(this.ctx.collection, slug, fresh);\n this.ctx.hooks.onContentRevalidated?.(slug, fresh);\n } catch (err) {\n const cmsErr = isCMSError(err)\n ? err\n : new CMSError({\n code: \"swr/content_rebuild_failed\",\n message: \"SWR background content rebuild failed.\",\n cause: err,\n context: {\n operation: \"swr.rebuildContentBg\",\n collection: this.ctx.collection,\n slug,\n },\n });\n this.ctx.hooks.onSwrError?.(cmsErr, { phase: \"item-content\", slug });\n this.ctx.logger?.warn?.(\"本文のバックグラウンド再生成に失敗\", {\n slug,\n collection: this.ctx.collection,\n code: cmsErr.code,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n private attachLazyContent(meta: CachedItemMeta<T>): ItemWithRender<T> {\n const slug = meta.item.slug;\n const item = meta.item;\n // 同一インスタンス内で本文ロードを集約する (複数 render() でも 1 回の I/O)\n let payloadPromise: Promise<CachedItemContent> | undefined;\n const loadPayload = (): Promise<CachedItemContent> => {\n if (!payloadPromise) {\n payloadPromise = this.loadOrBuildContent(slug, item);\n }\n return payloadPromise;\n };\n\n const render = async (opts?: {\n format?: \"html\" | \"markdown\";\n }): Promise<string> => {\n const payload = await loadPayload();\n return opts?.format === \"markdown\" ? payload.markdown : payload.html;\n };\n\n return Object.assign(Object.create(null) as object, item, {\n render,\n }) as ItemWithRender<T>;\n }\n\n private async fetchList(): Promise<T[]> {\n const cached = await this.ctx.docCache.getList<T>(this.ctx.collection);\n if (cached) {\n if (\n this.ctx.ttlMs !== undefined &&\n isStale(cached.cachedAt, this.ctx.ttlMs)\n ) {\n // TTL 切れ: ブロッキング再取得\n this.ctx.logger?.debug?.(\"リストキャッシュ期限切れ(TTL)、フェッチ\", {\n operation: \"list\",\n collection: this.ctx.collection,\n cacheAdapter: this.ctx.docCacheName,\n });\n this.ctx.hooks.onListCacheMiss?.();\n const items = await this.fetchListRaw();\n await this.ctx.docCache.setList(this.ctx.collection, {\n items,\n cachedAt: Date.now(),\n });\n return items;\n }\n // SWR: 即時返却 + バックグラウンド差分チェック\n const bg = this.checkAndUpdateListBg(cached);\n if (this.ctx.waitUntil) this.ctx.waitUntil(bg);\n this.ctx.logger?.debug?.(\"リストキャッシュヒット\", {\n operation: \"list\",\n collection: this.ctx.collection,\n cacheAdapter: this.ctx.docCacheName,\n });\n this.ctx.hooks.onListCacheHit?.(cached);\n return cached.items;\n }\n\n // 未キャッシュ: 同期フェッチ\n this.ctx.logger?.debug?.(\"リストキャッシュミス、フェッチ\", {\n operation: \"list\",\n collection: this.ctx.collection,\n cacheAdapter: this.ctx.docCacheName,\n });\n this.ctx.hooks.onListCacheMiss?.();\n const items = await this.fetchListRaw();\n const cachedAt = Date.now();\n const save = this.ctx.docCache.setList(this.ctx.collection, {\n items,\n cachedAt,\n });\n if (this.ctx.waitUntil) {\n this.ctx.waitUntil(save);\n } else {\n await save;\n }\n return items;\n }\n\n private async checkAndUpdateItemBg(\n slug: string,\n cached: CachedItemMeta<T>,\n ): Promise<void> {\n try {\n const item = await this.findRaw(slug);\n if (!item) return;\n const lm = this.ctx.source.getLastModified(item);\n if (lm !== cached.notionUpdatedAt) {\n const meta = await this.persistMeta(slug, item);\n await this.invalidateContent(slug);\n this.ctx.logger?.debug?.(\"SWR: 差分を検出、メタを差し替え\", {\n operation: \"get:bg\",\n slug,\n collection: this.ctx.collection,\n notionUpdatedAt: cached.notionUpdatedAt,\n });\n this.ctx.hooks.onCacheRevalidated?.(slug, meta);\n await this.rebuildContentBg(slug, item);\n } else if (this.ctx.ttlMs !== undefined) {\n // 変更なし + TTL あり: cachedAt をリセットして次回の期限切れを先送りする\n await this.ctx.docCache.setMeta(this.ctx.collection, slug, {\n ...cached,\n cachedAt: Date.now(),\n });\n this.ctx.logger?.debug?.(\"SWR: 差分なし、TTL をリセット\", {\n operation: \"get:bg\",\n slug,\n collection: this.ctx.collection,\n });\n }\n } catch (err) {\n const cmsErr = isCMSError(err)\n ? err\n : new CMSError({\n code: \"swr/item_check_failed\",\n message: \"SWR background item check failed.\",\n cause: err,\n context: {\n operation: \"swr.checkAndUpdateItemBg\",\n collection: this.ctx.collection,\n slug,\n },\n });\n this.ctx.hooks.onSwrError?.(cmsErr, { phase: \"item-meta\", slug });\n this.ctx.logger?.warn?.(\n \"SWR: アイテムのバックグラウンド差分チェックに失敗\",\n {\n slug,\n collection: this.ctx.collection,\n code: cmsErr.code,\n error: err instanceof Error ? err.message : String(err),\n },\n );\n }\n }\n\n private async checkAndUpdateListBg(cached: CachedItemList<T>): Promise<void> {\n try {\n const items = await this.fetchListRaw();\n if (\n this.ctx.source.getListVersion(items) !==\n this.ctx.source.getListVersion(cached.items)\n ) {\n const listEntry = { items, cachedAt: Date.now() };\n await this.ctx.docCache.setList(this.ctx.collection, listEntry);\n this.ctx.logger?.debug?.(\n \"SWR: リスト差分を検出、キャッシュを差し替え\",\n {\n operation: \"list:bg\",\n collection: this.ctx.collection,\n },\n );\n this.ctx.hooks.onListCacheRevalidated?.(listEntry);\n } else if (this.ctx.ttlMs !== undefined) {\n await this.ctx.docCache.setList(this.ctx.collection, {\n ...cached,\n cachedAt: Date.now(),\n });\n this.ctx.logger?.debug?.(\"SWR: リスト差分なし、TTL をリセット\", {\n operation: \"list:bg\",\n collection: this.ctx.collection,\n });\n }\n } catch (err) {\n const cmsErr = isCMSError(err)\n ? err\n : new CMSError({\n code: \"swr/list_check_failed\",\n message: \"SWR background list check failed.\",\n cause: err,\n context: {\n operation: \"swr.checkAndUpdateListBg\",\n collection: this.ctx.collection,\n },\n });\n this.ctx.hooks.onSwrError?.(cmsErr, { phase: \"list\" });\n this.ctx.logger?.warn?.(\n \"SWR: リストのバックグラウンド差分チェックに失敗\",\n {\n collection: this.ctx.collection,\n code: cmsErr.code,\n error: err instanceof Error ? err.message : String(err),\n },\n );\n }\n }\n\n private async fetchListRaw(): Promise<T[]> {\n const items = await withRetry(\n () =>\n this.ctx.source.list({\n publishedStatuses:\n this.ctx.publishedStatuses.length > 0\n ? this.ctx.publishedStatuses\n : undefined,\n }),\n {\n ...this.ctx.retryConfig,\n onRetry: (attempt, status) => {\n this.ctx.logger?.warn?.(\"list() リトライ中\", { attempt, status });\n },\n },\n );\n return items.filter((item) => {\n if (item.isArchived || item.isInTrash) return false;\n if (\n this.ctx.accessibleStatuses.length > 0 &&\n (!item.status || !this.ctx.accessibleStatuses.includes(item.status))\n )\n return false;\n return true;\n });\n }\n\n private async findRaw(slug: string): Promise<T | null> {\n const retryOpts = {\n ...this.ctx.retryConfig,\n onRetry: (attempt: number, status: number) => {\n this.ctx.logger?.warn?.(\"get() リトライ中\", {\n attempt,\n status,\n slug,\n });\n },\n };\n\n // slugField から Notion プロパティ名を解決して効率的なフィルタクエリを実行する。\n const notionPropName =\n this.ctx.source.properties?.[this.ctx.slugField]?.notion;\n\n let item: T | null;\n const findByProp = this.ctx.source.findByProp?.bind(this.ctx.source);\n if (notionPropName && findByProp) {\n item = await withRetry(() => findByProp(notionPropName, slug), retryOpts);\n } else {\n // フォールバック: list して線形探索\n const all = await withRetry(() => this.ctx.source.list(), retryOpts);\n item = all.find((i) => i.slug === slug) ?? null;\n }\n\n if (!item) return null;\n if (item.isArchived || item.isInTrash) return null;\n if (\n this.ctx.accessibleStatuses.length > 0 &&\n (!item.status || !this.ctx.accessibleStatuses.includes(item.status))\n ) {\n return null;\n }\n return item;\n }\n}\n\nfunction matchesWhere<T extends BaseContentItem>(\n item: T,\n where: WhereClause<T>,\n): boolean {\n for (const key of Object.keys(where) as (keyof T & string)[]) {\n const expected = where[key];\n const actual = item[key];\n if (Array.isArray(expected)) {\n if (!(expected as readonly unknown[]).includes(actual)) return false;\n } else {\n if (actual !== expected) return false;\n }\n }\n return true;\n}\n\nfunction applyListOptions<T extends BaseContentItem>(\n items: T[],\n opts?: ListOptions<T>,\n): T[] {\n if (!opts) return sortByPublishedAtDesc(items);\n let result = items;\n\n if (opts.status) {\n const allow = new Set(\n Array.isArray(opts.status) ? opts.status : [opts.status],\n );\n result = result.filter((it) => it.status != null && allow.has(it.status));\n }\n\n if (opts.tag) {\n const tag = opts.tag;\n result = result.filter((it) => {\n const tags = (it as { tags?: string[] }).tags;\n return Array.isArray(tags) && tags.includes(tag);\n });\n }\n\n if (opts.where) {\n const where = opts.where;\n result = result.filter((it) => matchesWhere(it, where));\n }\n\n if (opts.filter) {\n result = result.filter(opts.filter);\n }\n\n if (opts.sort) {\n result = [...result].sort(makeComparator(opts.sort));\n } else {\n result = sortByPublishedAtDesc(result);\n }\n\n const skip = opts.skip ?? 0;\n const limit = opts.limit;\n if (skip > 0 || limit !== undefined) {\n result = result.slice(skip, limit !== undefined ? skip + limit : undefined);\n }\n\n return result;\n}\n\n/** publishedAt 降順、未設定の場合は lastEditedTime 降順でソートする。 */\nfunction sortByPublishedAtDesc<T extends BaseContentItem>(items: T[]): T[] {\n return [...items].sort((a, b) => {\n // lastEditedTime は必須なので av/bv は常に truthy\n const av = a.publishedAt ?? a.lastEditedTime;\n const bv = b.publishedAt ?? b.lastEditedTime;\n if (av === bv) return 0;\n return av > bv ? -1 : 1;\n });\n}\n\nfunction makeComparator<T extends BaseContentItem>(\n sort: SortOption<T>,\n): (a: T, b: T) => number {\n if (sort.compare) return sort.compare;\n const by = sort.by as keyof T;\n const dir = sort.dir === \"asc\" ? 1 : -1;\n return (a, b) => {\n const av = a[by];\n const bv = b[by];\n if (av === bv) return 0;\n if (av === undefined || av === null) return 1;\n if (bv === undefined || bv === null) return -1;\n if (typeof av === \"string\" && typeof bv === \"string\") {\n return av > bv ? dir : -dir;\n }\n if (typeof av === \"number\" && typeof bv === \"number\") {\n return av > bv ? dir : -dir;\n }\n throw new CMSError({\n code: \"core/sort_unsupported_type\",\n message: `\"${String(by)}\" フィールドの型 \"${typeof av}\" はソート非対応です。compare 関数を指定してください。`,\n context: {\n operation: \"makeComparator\",\n field: String(by),\n type: typeof av,\n },\n });\n };\n}\n","import { isCMSError } from \"./errors\";\nimport type { ImageCacheOps, InvalidateScope } from \"./types/index\";\n\n/** `$handler()` の挙動設定。 */\nexport interface HandlerOptions {\n /** マウントするベースパス。デフォルト `/api/cms`。 */\n basePath?: string;\n /** 画像プロキシのパス (basePath 相対)。デフォルト `/images/:hash`。 */\n imagesPath?: string;\n /** revalidate webhook のパス (basePath 相対)。デフォルト `/revalidate`。 */\n revalidatePath?: string;\n /** Webhook 署名検証用シークレット (未指定なら検証スキップ)。 */\n webhookSecret?: string;\n /** デフォルト実装を無効化する場合 true。 */\n disabled?: boolean;\n}\n\n/** `$handler()` が内部で依存する CMS 機能の最小セット。 */\nexport interface HandlerAdapter {\n imageCache: ImageCacheOps;\n /**\n * 指定コレクションの DataSource.parseWebhook を呼ぶ。\n * 未知コレクション → `webhook/unknown_collection` CMSError\n * parseWebhook 未実装 → `webhook/not_implemented` CMSError\n */\n parseWebhookFor(\n collection: string,\n req: Request,\n webhookSecret: string | undefined,\n ): Promise<InvalidateScope>;\n revalidate(scope: InvalidateScope): Promise<void>;\n}\n\nconst DEFAULT_OPTS = {\n basePath: \"/api/cms\",\n imagesPath: \"/images\",\n revalidatePath: \"/revalidate\",\n} as const;\n\n/**\n * CMSError のコードから HTTP ステータスコードを返す。\n * 既知の webhook エラーコードのみ対応し、それ以外は null を返す。\n */\nfunction webhookErrorStatus(code: string): number | null {\n if (code === \"webhook/signature_invalid\") return 401;\n if (code === \"webhook/not_implemented\") return 501;\n if (code === \"webhook/unknown_collection\") return 404;\n if (code === \"webhook/payload_invalid\") return 400;\n return null;\n}\n\n/**\n * Web Standard な Request → Response ルーター。\n * Next.js / React Router / Hono / Cloudflare Workers いずれでも使える。\n *\n * ルート:\n * - GET `{basePath}/images/:hash` — 画像プロキシ\n * - POST `{basePath}/revalidate/:collection` — Webhook 受信 + $revalidate()\n */\nexport function createHandler(\n adapter: HandlerAdapter,\n opts: HandlerOptions = {},\n): (req: Request) => Promise<Response> {\n const basePath = trimTrailingSlash(opts.basePath ?? DEFAULT_OPTS.basePath);\n const imagesPath = opts.imagesPath ?? DEFAULT_OPTS.imagesPath;\n const revalidatePath = opts.revalidatePath ?? DEFAULT_OPTS.revalidatePath;\n\n return async (req: Request): Promise<Response> => {\n const url = new URL(req.url);\n const path = url.pathname;\n\n if (!path.startsWith(basePath)) {\n return new Response(\"Not Found\", { status: 404 });\n }\n const rel = path.slice(basePath.length) || \"/\";\n\n // 画像: GET {basePath}/images/:hash\n if (req.method === \"GET\" && rel.startsWith(`${imagesPath}/`)) {\n const hash = rel.slice(imagesPath.length + 1);\n if (!hash) return new Response(\"Bad Request\", { status: 400 });\n const object = await adapter.imageCache.get(hash);\n if (!object) return new Response(\"Not Found\", { status: 404 });\n const headers = new Headers();\n if (object.contentType) headers.set(\"content-type\", object.contentType);\n headers.set(\"cache-control\", \"public, max-age=31536000, immutable\");\n return new Response(object.data, { headers });\n }\n\n // Revalidate: POST {basePath}/revalidate/:collection\n if (req.method === \"POST\" && rel.startsWith(`${revalidatePath}/`)) {\n const collection = rel.slice(revalidatePath.length + 1);\n if (!collection || collection.includes(\"/\")) {\n return new Response(\n JSON.stringify({ ok: false, reason: \"collection required\" }),\n { status: 400, headers: { \"content-type\": \"application/json\" } },\n );\n }\n try {\n const scope = await adapter.parseWebhookFor(\n collection,\n req,\n opts.webhookSecret,\n );\n await adapter.revalidate(scope);\n return new Response(JSON.stringify({ ok: true, scope }), {\n status: 200,\n headers: { \"content-type\": \"application/json\" },\n });\n } catch (err) {\n if (isCMSError(err)) {\n const status = webhookErrorStatus(err.code);\n if (status !== null) {\n return new Response(JSON.stringify({ ok: false, code: err.code }), {\n status,\n headers: { \"content-type\": \"application/json\" },\n });\n }\n }\n throw err;\n }\n }\n\n return new Response(\"Not Found\", { status: 404 });\n };\n}\n\nfunction trimTrailingSlash(s: string): string {\n return s.endsWith(\"/\") ? s.slice(0, -1) : s;\n}\n","import { noopDocOps, noopImgOps } from \"./cache/noop\";\nimport { CollectionClientImpl, type CollectionContext } from \"./collection\";\nimport { CMSError } from \"./errors\";\nimport { createHandler, type HandlerOptions } from \"./handler\";\nimport { mergeHooks, mergeLoggers } from \"./hooks\";\nimport type { RenderContext } from \"./rendering\";\nimport type { RetryConfig } from \"./retry\";\nimport { DEFAULT_RETRY_CONFIG } from \"./retry\";\nimport type {\n BaseContentItem,\n CacheAdapter,\n CMSHooks,\n CollectionClient,\n CollectionsConfig,\n CreateCMSOptions,\n DataSource,\n DocumentCacheOps,\n ImageCacheOps,\n InferCollectionItem,\n InvalidateScope,\n Logger,\n LogLevel,\n RendererFn,\n StorageBinary,\n} from \"./types/index\";\n\nconst DEFAULT_IMAGE_PROXY_BASE = \"/api/images\";\n\n/** `CMSClient<C>` — コレクション別アクセス + グローバル操作の合成型。 */\nexport type CMSClient<C extends CollectionsConfig> = {\n [K in keyof C]: CollectionClient<InferCollectionItem<C[K]>>;\n} & CMSGlobalOps;\n\n/** `CMSClient` のグローバル名前空間。`$` プレフィックス。 */\nexport interface CMSGlobalOps {\n /** 登録されているコレクション名の一覧。 */\n readonly $collections: readonly string[];\n /** 全コレクションまたは特定スコープのキャッシュを無効化する。 */\n $invalidate(scope?: InvalidateScope): Promise<void>;\n /** Web Standard なルーティングハンドラ (画像プロキシ / webhook) を生成する。 */\n $handler(opts?: HandlerOptions): (req: Request) => Promise<Response>;\n /** ハッシュキーでキャッシュ画像を取得する。 */\n $getCachedImage(hash: string): Promise<StorageBinary | null>;\n}\n\ninterface ResolvedCache {\n doc: DocumentCacheOps;\n docName: string;\n img: ImageCacheOps;\n imgName: string;\n hasImg: boolean;\n}\n\n/**\n * `cache` オプションから document / image オペレーションを解決する。\n *\n * - 配列で渡された場合は各 adapter の `handles` を見て先勝ち (最初に見つかったもの) で振り分ける\n * - 単体で渡された場合は `handles` の領域だけ反映、片側は noop\n * - 未指定なら両方 noop\n */\nfunction resolveCache(\n cache: CacheAdapter | readonly CacheAdapter[] | undefined,\n): ResolvedCache {\n const adapters =\n cache === undefined ? [] : Array.isArray(cache) ? cache : [cache];\n\n let doc: DocumentCacheOps = noopDocOps;\n let docName = \"noop-document\";\n let img: ImageCacheOps = noopImgOps;\n let imgName = \"noop-image\";\n let docFound = false;\n let imgFound = false;\n\n for (const adapter of adapters) {\n if (!docFound && adapter.handles.includes(\"document\") && adapter.doc) {\n doc = adapter.doc;\n docName = adapter.name;\n docFound = true;\n }\n if (!imgFound && adapter.handles.includes(\"image\") && adapter.img) {\n img = adapter.img;\n imgName = adapter.name;\n imgFound = true;\n }\n }\n\n return { doc, docName, img, imgName, hasImg: imgFound };\n}\n\nconst LOG_LEVEL_ORDER: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\n/** `logger` から `minLevel` 未満のレベルを除いた新しい Logger を返す。 */\nfunction applyLogLevel(\n logger: Logger | undefined,\n minLevel: LogLevel,\n): Logger | undefined {\n if (!logger) return undefined;\n const minOrder = LOG_LEVEL_ORDER[minLevel];\n const filtered: Logger = {};\n for (const level of [\"debug\", \"info\", \"warn\", \"error\"] as const) {\n if (LOG_LEVEL_ORDER[level] >= minOrder) {\n filtered[level] = logger[level];\n }\n }\n return filtered;\n}\n\n/**\n * 複数の `CollectionDef` を束ねた CMS クライアントを生成する。\n *\n * 通常はユーザーが直接呼ぶことはなく、CLI 生成の `nhc.ts` の `createCMS`\n * (低レベルのこの関数をラップしたもの) を経由する。\n *\n * @example\n * createCMS({\n * collections: {\n * posts: {\n * source: createNotionCollection({ token, dataSourceId, properties }),\n * slugField: \"slug\",\n * statusField: \"status\",\n * publishedStatuses: [\"公開済み\"],\n * }\n * },\n * cache: memoryCache({ ttlMs: 5 * 60_000 }),\n * });\n */\nexport function createCMS<C extends CollectionsConfig>(\n opts: CreateCMSOptions<C>,\n): CMSClient<C> {\n if (!opts.collections || Object.keys(opts.collections).length === 0) {\n throw new CMSError({\n code: \"core/config_invalid\",\n message:\n \"createCMS: collections に少なくとも 1 つのコレクションを指定してください。\",\n context: { operation: \"createCMS\" },\n });\n }\n\n for (const [name, def] of Object.entries(opts.collections)) {\n if (!def.source) {\n throw new CMSError({\n code: \"core/config_invalid\",\n message: `createCMS: コレクション \"${name}\" の source は必須です。`,\n context: { operation: \"createCMS\", collection: name },\n });\n }\n if (!def.slugField) {\n throw new CMSError({\n code: \"core/config_invalid\",\n message: `createCMS: コレクション \"${name}\" の slugField は必須です。`,\n context: { operation: \"createCMS\", collection: name },\n });\n }\n }\n\n const cacheRes = resolveCache(opts.cache);\n const ttlMs = opts.ttlMs;\n const imageProxyBase = opts.imageProxyBase ?? DEFAULT_IMAGE_PROXY_BASE;\n const contentConfig = opts.content;\n const rendererFn: RendererFn = opts.renderer;\n const waitUntil = opts.waitUntil;\n const baseLogger: Logger | undefined = mergeLoggers(\n opts.plugins ?? [],\n opts.logger,\n );\n const logger = opts.logLevel\n ? applyLogLevel(baseLogger, opts.logLevel)\n : baseLogger;\n const hooks: CMSHooks<BaseContentItem> = mergeHooks(\n opts.plugins ?? [],\n opts.hooks,\n logger,\n );\n const maxConcurrent = opts.rateLimiter?.maxConcurrent ?? 3;\n const retryConfig: RetryConfig = {\n ...DEFAULT_RETRY_CONFIG,\n ...(opts.rateLimiter ?? {}),\n };\n\n const collectionNames: (keyof C & string)[] = [];\n const collections: Record<string, CollectionClient<BaseContentItem>> = {};\n for (const [name, def] of Object.entries(opts.collections)) {\n collectionNames.push(name as keyof C & string);\n const source = def.source as DataSource<BaseContentItem>;\n const colHooks = def.hooks as CMSHooks<BaseContentItem> | undefined;\n const collectionHooks: CMSHooks<BaseContentItem> = colHooks\n ? mergeHooks([{ name: `${name}:global`, hooks }], colHooks, logger)\n : hooks;\n const renderCtx: RenderContext<BaseContentItem> = {\n source,\n rendererFn,\n imgCache: cacheRes.img,\n imgCacheName: cacheRes.imgName,\n hasImageCache: cacheRes.hasImg,\n imageProxyBase,\n contentConfig,\n hooks: collectionHooks,\n logger,\n };\n const ctx: CollectionContext<BaseContentItem> = {\n collection: name,\n source,\n docCache: cacheRes.doc,\n docCacheName: cacheRes.docName,\n render: renderCtx,\n hooks: collectionHooks,\n logger,\n ttlMs,\n publishedStatuses: def.publishedStatuses\n ? [...def.publishedStatuses]\n : [],\n accessibleStatuses: def.accessibleStatuses\n ? [...def.accessibleStatuses]\n : [],\n retryConfig,\n maxConcurrent,\n waitUntil,\n slugField: def.slugField,\n };\n collections[name] = new CollectionClientImpl(ctx);\n }\n\n const globalOps: CMSGlobalOps = {\n $collections: collectionNames,\n async $invalidate(scope?: InvalidateScope): Promise<void> {\n logger?.debug?.(\"グローバルキャッシュを無効化\", {\n operation: \"$invalidate\",\n cacheAdapter: cacheRes.docName,\n });\n await cacheRes.doc.invalidate(scope ?? \"all\");\n },\n $handler(handlerOpts?: HandlerOptions) {\n return createHandler(\n {\n imageCache: cacheRes.img,\n async parseWebhookFor(collection, req, webhookSecret) {\n const def = opts.collections[collection];\n if (!def) {\n throw new CMSError({\n code: \"webhook/unknown_collection\",\n message: `Unknown collection: ${collection}`,\n context: { operation: \"parseWebhookFor\", collection },\n });\n }\n const ds = def.source as DataSource<BaseContentItem>;\n if (!ds.parseWebhook) {\n throw new CMSError({\n code: \"webhook/not_implemented\",\n message: `Collection \"${collection}\" does not support webhooks.`,\n context: { operation: \"parseWebhookFor\", collection },\n });\n }\n return ds.parseWebhook(req, { secret: webhookSecret });\n },\n revalidate: (scope) => globalOps.$invalidate(scope),\n },\n handlerOpts,\n );\n },\n $getCachedImage(hash) {\n return cacheRes.img.get(hash);\n },\n };\n\n return Object.assign(\n Object.create(null) as object,\n collections,\n globalOps,\n ) as CMSClient<C>;\n}\n","import type { BaseContentItem } from \"./content\";\nimport type { CMSHooks } from \"./hooks\";\nimport type { Logger } from \"./logger\";\n\nexport interface CMSPlugin<T extends BaseContentItem = BaseContentItem> {\n name: string;\n hooks?: CMSHooks<T>;\n logger?: Partial<Logger>;\n}\n\nexport function definePlugin<T extends BaseContentItem>(\n plugin: CMSPlugin<T>,\n): CMSPlugin<T> {\n return plugin;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/cache.ts","../src/cache/noop.ts","../src/image.ts","../src/rendering.ts","../src/retry.ts","../src/collection.ts","../src/handler.ts","../src/cms.ts","../src/types/plugin.ts"],"sourcesContent":["/** 文字列をSHA-256でハッシュ化し、16進数文字列として返す。画像キーの生成に使用。 */\nexport async function sha256Hex(input: string): Promise<string> {\n const data = new TextEncoder().encode(input);\n const hash = await crypto.subtle.digest(\"SHA-256\", data);\n return Array.from(new Uint8Array(hash))\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\n/**\n * キャッシュが有効期限切れかどうかを判定する。\n * ttlMs が未指定の場合は常に false(無期限有効)を返す。\n */\nexport function isStale(cachedAt: number, ttlMs?: number): boolean {\n if (ttlMs === undefined) return false;\n return Date.now() - cachedAt > ttlMs;\n}\n","import type {\n BaseContentItem,\n CachedItemContent,\n CachedItemList,\n CachedItemMeta,\n DocumentCacheOps,\n ImageCacheOps,\n StorageBinary,\n} from \"../types/index\";\n\n/** 何もキャッシュしないドキュメントオペレーション。常に null を返す。 */\nconst noopDoc: DocumentCacheOps = {\n getList<T extends BaseContentItem>(\n _collection: string,\n ): Promise<CachedItemList<T> | null> {\n return Promise.resolve(null);\n },\n setList<T extends BaseContentItem>(\n _collection: string,\n _data: CachedItemList<T>,\n ): Promise<void> {\n return Promise.resolve();\n },\n getMeta<T extends BaseContentItem>(\n _collection: string,\n _slug: string,\n ): Promise<CachedItemMeta<T> | null> {\n return Promise.resolve(null);\n },\n setMeta<T extends BaseContentItem>(\n _collection: string,\n _slug: string,\n _data: CachedItemMeta<T>,\n ): Promise<void> {\n return Promise.resolve();\n },\n getContent(\n _collection: string,\n _slug: string,\n ): Promise<CachedItemContent | null> {\n return Promise.resolve(null);\n },\n setContent(\n _collection: string,\n _slug: string,\n _data: CachedItemContent,\n ): Promise<void> {\n return Promise.resolve();\n },\n invalidate(): Promise<void> {\n return Promise.resolve();\n },\n};\n\n/** 何もキャッシュしない画像オペレーション。 */\nconst noopImg: ImageCacheOps = {\n get(_hash: string): Promise<StorageBinary | null> {\n return Promise.resolve(null);\n },\n set(): Promise<void> {\n return Promise.resolve();\n },\n};\n\n/**\n * 何もキャッシュしないアダプタ。`createCMS({ cache })` 未指定時の内部デフォルト。\n * テストでも使える。\n */\nexport const noopDocOps: DocumentCacheOps = noopDoc;\nexport const noopImgOps: ImageCacheOps = noopImg;\n","import { sha256Hex } from \"./cache\";\nimport { CMSError, isCMSError } from \"./errors\";\nimport type { ImageCacheOps, Logger, StorageBinary } from \"./types/index\";\n\n/**\n * レスポンスの Content-Type ヘッダから画像の MIME タイプを取り出す。\n * ヘッダがない、または image/* でない場合は CMSError を投げる。\n * URL 拡張子からの推測や jpeg デフォルトは行わない。\n */\nfunction pickImageContentType(\n headerValue: string | null,\n notionUrl: string,\n): string {\n if (!headerValue) {\n throw new CMSError({\n code: \"cache/image_invalid_content_type\",\n message: \"Image response missing Content-Type header.\",\n context: { operation: \"fetchAndCacheImage:contentType\", notionUrl },\n });\n }\n const value = (headerValue.split(\";\")[0] ?? headerValue).trim().toLowerCase();\n if (!value.startsWith(\"image/\")) {\n throw new CMSError({\n code: \"cache/image_invalid_content_type\",\n message: `Image response has non-image Content-Type: ${value}`,\n context: {\n operation: \"fetchAndCacheImage:contentType\",\n notionUrl,\n contentType: value,\n },\n });\n }\n return value;\n}\n\n/**\n * Notion画像URLをfetchして ImageCacheOps にキャッシュし、プロキシURL を返す。\n * 既存キャッシュがあれば再fetchしない。\n */\nasync function fetchAndCacheImage(\n cache: ImageCacheOps,\n cacheName: string,\n notionUrl: string,\n hash: string,\n imageProxyBase: string,\n logger?: Logger,\n): Promise<string> {\n const proxyUrl = `${imageProxyBase}/${hash}`;\n\n const existing = await cache.get(hash);\n if (existing) {\n logger?.debug?.(\"画像キャッシュヒット\", {\n operation: \"fetchAndCacheImage\",\n cacheAdapter: cacheName,\n imageHash: hash,\n });\n return proxyUrl;\n }\n\n logger?.debug?.(\"画像キャッシュミス、Notion からフェッチ\", {\n operation: \"fetchAndCacheImage\",\n cacheAdapter: cacheName,\n imageHash: hash,\n });\n\n try {\n const response = await fetch(notionUrl, {\n signal: AbortSignal.timeout(10_000),\n });\n if (!response.ok) {\n throw new CMSError({\n code: \"cache/image_fetch_failed\",\n message: `Failed to fetch Notion image: HTTP ${response.status}`,\n context: {\n operation: \"fetchAndCacheImage\",\n notionUrl,\n httpStatus: response.status,\n },\n });\n }\n\n const data = await response.arrayBuffer();\n const contentType = pickImageContentType(\n response.headers.get(\"content-type\"),\n notionUrl,\n );\n await cache.set(hash, data, contentType);\n logger?.debug?.(\"画像をキャッシュに保存\", {\n operation: \"fetchAndCacheImage\",\n cacheAdapter: cacheName,\n imageHash: hash,\n });\n } catch (err) {\n if (isCMSError(err)) throw err;\n throw new CMSError({\n code: \"cache/io_failed\",\n message: \"Failed to fetch or cache Notion image.\",\n cause: err,\n context: { operation: \"fetchAndCacheImage\", notionUrl },\n });\n }\n\n return proxyUrl;\n}\n\n/**\n * `ImageCacheOps` と `imageProxyBase` から `cacheImage` 関数を構築する。\n * 返り値は Notion 画像 URL を受け取り、SHA-256 ハッシュをキャッシュキーとして\n * {@link ImageCacheOps} に保存後、プロキシ URL を返す。\n *\n * ハッシュのメモ化はファクトリ呼び出し単位でスコープ化されており、\n * インスタンス間でキャッシュを共有しない。\n */\nexport function buildCacheImageFn(\n cache: ImageCacheOps,\n cacheName: string,\n imageProxyBase: string,\n logger?: Logger,\n): (notionUrl: string) => Promise<string> {\n const hashMemo = new Map<string, string>();\n return async (notionUrl) => {\n let hash = hashMemo.get(notionUrl);\n if (hash === undefined) {\n hash = await sha256Hex(notionUrl);\n hashMemo.set(notionUrl, hash);\n }\n return fetchAndCacheImage(\n cache,\n cacheName,\n notionUrl,\n hash,\n imageProxyBase,\n logger,\n );\n };\n}\n\nexport type { StorageBinary };\n","import type { ContentBlock } from \"./content/blocks\";\nimport { CMSError, isCMSError } from \"./errors\";\nimport { buildCacheImageFn } from \"./image\";\nimport type {\n BaseContentItem,\n CachedItemContent,\n CachedItemMeta,\n CMSHooks,\n ContentConfig,\n DataSource,\n ImageCacheOps,\n Logger,\n RendererFn,\n} from \"./types/index\";\n\n/**\n * `@notion-headless-cms/renderer` を動的 import してデフォルトレンダラーを返す。\n * core のゼロ依存ルールを守るため静的 import は禁止。\n */\nasync function loadDefaultRenderer(): Promise<RendererFn> {\n try {\n const mod = await import(\"@notion-headless-cms/renderer\");\n return (mod as { renderMarkdown: RendererFn }).renderMarkdown;\n } catch {\n throw new CMSError({\n code: \"core/config_invalid\",\n message:\n \"renderer が未指定で、@notion-headless-cms/renderer のロードにも失敗しました。\" +\n \" createCMS の renderer オプションを指定するか、@notion-headless-cms/renderer をインストールしてください。\",\n context: { operation: \"loadDefaultRenderer\" },\n });\n }\n}\n\n/** 本文レンダリングに必要な依存を束ねたコンテキスト。 */\nexport interface RenderContext<T extends BaseContentItem> {\n source: DataSource<T>;\n rendererFn: RendererFn | undefined;\n imgCache: ImageCacheOps;\n imgCacheName: string;\n hasImageCache: boolean;\n imageProxyBase: string;\n contentConfig: ContentConfig | undefined;\n hooks: CMSHooks<T>;\n logger: Logger | undefined;\n}\n\n/**\n * メタデータキャッシュエントリを生成する。Notion API も renderer も呼ばない軽量関数。\n */\nexport function buildCachedItemMeta<T extends BaseContentItem>(\n item: T,\n source: DataSource<T>,\n): CachedItemMeta<T> {\n return {\n item,\n notionUpdatedAt: source.getLastModified(item),\n cachedAt: Date.now(),\n };\n}\n\n/**\n * アイテム本文を Markdown ロード → blocks 生成 → HTML レンダリング → フック適用まで\n * 実行し、本文キャッシュ用の `CachedItemContent` を返す。\n */\nexport async function buildCachedItemContent<T extends BaseContentItem>(\n item: T,\n ctx: RenderContext<T>,\n): Promise<CachedItemContent> {\n const start = Date.now();\n ctx.logger?.info?.(\"コンテンツのレンダリング開始\", {\n slug: item.slug,\n pageId: item.id,\n });\n ctx.hooks.onRenderStart?.(item.slug);\n\n let markdown: string;\n try {\n markdown = await ctx.source.loadMarkdown(item);\n } catch (err) {\n if (isCMSError(err)) throw err;\n throw new CMSError({\n code: \"source/load_markdown_failed\",\n message: \"Failed to load markdown from source.\",\n cause: err,\n context: {\n operation: \"buildCachedItemContent:loadMarkdown\",\n pageId: item.id,\n slug: item.slug,\n },\n });\n }\n\n let blocks: ContentBlock[];\n try {\n blocks = await ctx.source.loadBlocks(item);\n } catch (err) {\n if (isCMSError(err)) throw err;\n throw new CMSError({\n code: \"source/load_blocks_failed\",\n message: \"Failed to load blocks from source.\",\n cause: err,\n context: {\n operation: \"buildCachedItemContent:loadBlocks\",\n pageId: item.id,\n slug: item.slug,\n },\n });\n }\n\n const cacheImage = ctx.hasImageCache\n ? buildCacheImageFn(\n ctx.imgCache,\n ctx.imgCacheName,\n ctx.imageProxyBase,\n ctx.logger,\n )\n : undefined;\n\n const rendererFn = ctx.rendererFn ?? (await loadDefaultRenderer());\n\n let html: string;\n try {\n html = await rendererFn(markdown, {\n imageProxyBase: ctx.imageProxyBase,\n cacheImage,\n remarkPlugins: ctx.contentConfig?.remarkPlugins,\n rehypePlugins: ctx.contentConfig?.rehypePlugins,\n });\n } catch (err) {\n if (isCMSError(err)) throw err;\n throw new CMSError({\n code: \"renderer/failed\",\n message: \"Failed to render markdown.\",\n cause: err,\n context: {\n operation: \"buildCachedItemContent:renderMarkdown\",\n pageId: item.id,\n slug: item.slug,\n },\n });\n }\n\n if (ctx.hooks.afterRender) {\n html = await ctx.hooks.afterRender(html, item);\n }\n\n let result: CachedItemContent = {\n html,\n blocks,\n markdown,\n notionUpdatedAt: ctx.source.getLastModified(item),\n cachedAt: Date.now(),\n };\n\n if (ctx.hooks.beforeCacheContent) {\n result = await ctx.hooks.beforeCacheContent(result, item);\n }\n\n const durationMs = Date.now() - start;\n ctx.logger?.info?.(\"コンテンツのレンダリング完了\", {\n slug: item.slug,\n durationMs,\n });\n ctx.hooks.onRenderEnd?.(item.slug, durationMs);\n\n return result;\n}\n","export interface RetryConfig {\n retryOn: number[];\n maxRetries: number;\n baseDelayMs: number;\n /** true のとき指数バックオフにランダムジッターを加える(Thundering Herd 対策)。デフォルト: true */\n jitter?: boolean;\n onRetry?: (attempt: number, status: number) => void;\n}\n\nexport const DEFAULT_RETRY_CONFIG: RetryConfig = {\n retryOn: [429, 502, 503],\n maxRetries: 4,\n baseDelayMs: 1000,\n jitter: true,\n};\n\n/**\n * 指数バックオフ(オプションでジッター付き)でリトライする。\n *\n * `config.retryOn` に含まれるステータスコードを持つエラーのみリトライ対象。\n * 遅延は `baseDelayMs * 2^attempt` の指数バックオフ。\n * `jitter` が `true`(デフォルト)の場合、0.5〜1.0 の乱数係数を乗算して\n * Thundering Herd を防ぐ。`false` にすると確定的な遅延になる。\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n config: RetryConfig,\n): Promise<T> {\n let lastError: unknown;\n for (let attempt = 0; attempt <= config.maxRetries; attempt++) {\n try {\n return await fn();\n } catch (err) {\n const status = (err as { status?: number }).status;\n if (status === undefined || !config.retryOn.includes(status)) {\n throw err;\n }\n lastError = err;\n if (attempt < config.maxRetries) {\n config.onRetry?.(attempt + 1, status);\n const jitterFactor =\n config.jitter !== false ? 0.5 + Math.random() * 0.5 : 1;\n const delay = config.baseDelayMs * 2 ** attempt * jitterFactor;\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n }\n throw lastError;\n}\n","import { isStale } from \"./cache\";\nimport { CMSError, isCMSError } from \"./errors\";\nimport type { RenderContext } from \"./rendering\";\nimport { buildCachedItemContent, buildCachedItemMeta } from \"./rendering\";\nimport type { RetryConfig } from \"./retry\";\nimport { withRetry } from \"./retry\";\nimport type {\n AdjacencyOptions,\n BaseContentItem,\n CachedItemContent,\n CachedItemList,\n CachedItemMeta,\n CheckResult,\n CMSHooks,\n CollectionCacheOps,\n CollectionClient,\n DataSource,\n DocumentCacheOps,\n FindOptions,\n ItemWithContent,\n ListOptions,\n Logger,\n SortOption,\n WarmOptions,\n WarmResult,\n WhereClause,\n} from \"./types/index\";\n\n/**\n * コレクション別キャッシュキーを生成する。\n * item: `{collection}:{slug}` / list: `{collection}`\n *\n * (Cache adapter 内部のキー戦略はアダプタごとに異なるが、\n * 表示や再計算用に core 側でも公開ヘルパーを提供する)\n */\nexport function collectionKey(collection: string, slug?: string): string {\n return slug ? `${collection}:${slug}` : collection;\n}\n\n/** 単一コレクションの DataSource + SWR キャッシュ依存を束ねたコンテキスト。 */\nexport interface CollectionContext<T extends BaseContentItem> {\n collection: string;\n source: DataSource<T>;\n docCache: DocumentCacheOps;\n docCacheName: string;\n render: RenderContext<T>;\n hooks: CMSHooks<T>;\n logger: Logger | undefined;\n ttlMs: number | undefined;\n publishedStatuses: string[];\n accessibleStatuses: string[];\n retryConfig: RetryConfig;\n maxConcurrent: number;\n waitUntil: ((p: Promise<unknown>) => void) | undefined;\n /**\n * slug として使うフィールド名 (CLI 生成の `CollectionDef.slugField`)。\n * `source.properties[slugField].notion` を Notion プロパティ名として\n * `findByProp` を呼び出す。\n */\n slugField: string;\n}\n\n/** CollectionClient の実装。ユーザーは `createCMS` 経由でインスタンスを受け取る。 */\nexport class CollectionClientImpl<T extends BaseContentItem>\n implements CollectionClient<T>\n{\n readonly cache: CollectionCacheOps<T>;\n\n constructor(private readonly ctx: CollectionContext<T>) {\n this.cache = {\n invalidate: () => this.invalidateImpl(),\n invalidateItem: (slug: string) => this.invalidateItemImpl(slug),\n warm: (opts?: WarmOptions) => this.warmImpl(opts),\n };\n }\n\n // ── 基本取得 ──────────────────────────────────────────────────────────\n\n async find(\n slug: string,\n opts: FindOptions = {},\n ): Promise<ItemWithContent<T> | null> {\n // bypassCache: 強制ブロッキング取得\n if (opts.bypassCache) {\n this.ctx.hooks.onCacheMiss?.(slug);\n const item = await this.fetchRaw(slug);\n if (!item) return null;\n const meta = await this.persistMeta(slug, item);\n await this.invalidateContentEntry(slug);\n return this.attachLazyContent(meta);\n }\n\n const cachedMeta = await this.ctx.docCache.getMeta<T>(\n this.ctx.collection,\n slug,\n );\n if (cachedMeta) {\n if (\n this.ctx.ttlMs !== undefined &&\n isStale(cachedMeta.cachedAt, this.ctx.ttlMs)\n ) {\n // TTL 切れ: ブロッキング再取得\n this.ctx.logger?.debug?.(\"キャッシュ期限切れ(TTL)、フェッチ\", {\n operation: \"find\",\n slug,\n collection: this.ctx.collection,\n cacheAdapter: this.ctx.docCacheName,\n });\n this.ctx.hooks.onCacheMiss?.(slug);\n const item = await this.fetchRaw(slug);\n if (!item) return null;\n const meta = await this.persistMeta(slug, item);\n await this.invalidateContentEntry(slug);\n return this.attachLazyContent(meta);\n }\n // SWR: キャッシュ即時返却 + バックグラウンド差分チェック\n const bg = this.checkAndUpdateItemBg(slug, cachedMeta);\n if (this.ctx.waitUntil) this.ctx.waitUntil(bg);\n this.ctx.logger?.debug?.(\"キャッシュヒット\", {\n operation: \"find\",\n slug,\n collection: this.ctx.collection,\n cacheAdapter: this.ctx.docCacheName,\n cachedAt: cachedMeta.cachedAt,\n });\n this.ctx.hooks.onCacheHit?.(slug, cachedMeta);\n return this.attachLazyContent(cachedMeta);\n }\n\n // メタ未キャッシュ: 同期フェッチ (保存はバックグラウンド可)\n this.ctx.logger?.debug?.(\"キャッシュミス、フェッチ\", {\n operation: \"find\",\n slug,\n collection: this.ctx.collection,\n cacheAdapter: this.ctx.docCacheName,\n });\n this.ctx.hooks.onCacheMiss?.(slug);\n const item = await this.fetchRaw(slug);\n if (!item) return null;\n const meta = await this.persistMeta(slug, item, { background: true });\n return this.attachLazyContent(meta);\n }\n\n async list(opts?: ListOptions<T>): Promise<T[]> {\n const allItems = await this.fetchList();\n return applyListOptions(allItems, opts);\n }\n\n async params(): Promise<string[]> {\n const items = await this.fetchList();\n return items.map((item) => item.slug);\n }\n\n async check(\n slug: string,\n currentVersion: string,\n ): Promise<CheckResult<T> | null> {\n const raw = await this.fetchRaw(slug);\n if (!raw) return null;\n if (raw.lastEditedTime === currentVersion) return { stale: false };\n const meta = await this.persistMeta(slug, raw);\n await this.invalidateContentEntry(slug);\n return { stale: true, item: this.attachLazyContent(meta) };\n }\n\n async adjacent(\n slug: string,\n opts?: AdjacencyOptions<T>,\n ): Promise<{ prev: T | null; next: T | null }> {\n const items = applyListOptions(await this.fetchList(), {\n sort: opts?.sort,\n });\n const index = items.findIndex((it) => it.slug === slug);\n if (index === -1) return { prev: null, next: null };\n return {\n prev: index > 0 ? (items[index - 1] ?? null) : null,\n next: index < items.length - 1 ? (items[index + 1] ?? null) : null,\n };\n }\n\n // ── キャッシュ操作 ────────────────────────────────────────────────────\n\n private async invalidateImpl(): Promise<void> {\n this.ctx.logger?.debug?.(\"コレクション全体のキャッシュを無効化\", {\n operation: \"cache.invalidate\",\n collection: this.ctx.collection,\n cacheAdapter: this.ctx.docCacheName,\n });\n await this.ctx.docCache.invalidate({ collection: this.ctx.collection });\n }\n\n private async invalidateItemImpl(slug: string): Promise<void> {\n this.ctx.logger?.debug?.(\"アイテムキャッシュを無効化\", {\n operation: \"cache.invalidateItem\",\n collection: this.ctx.collection,\n cacheAdapter: this.ctx.docCacheName,\n slug,\n });\n await this.ctx.docCache.invalidate({\n collection: this.ctx.collection,\n slug,\n });\n }\n\n private async warmImpl(opts?: WarmOptions): Promise<WarmResult> {\n const items = await this.fetchListRaw();\n const concurrency = opts?.concurrency ?? this.ctx.maxConcurrent;\n let ok = 0;\n const failed: Array<{ slug: string; error: unknown }> = [];\n\n for (let i = 0; i < items.length; i += concurrency) {\n const chunk = items.slice(i, i + concurrency);\n await Promise.all(\n chunk.map(async (item) => {\n try {\n await this.persistMeta(item.slug, item);\n const content = await buildCachedItemContent(item, this.ctx.render);\n await this.ctx.docCache.setContent(\n this.ctx.collection,\n item.slug,\n content,\n );\n ok++;\n } catch (err) {\n failed.push({ slug: item.slug, error: err });\n this.ctx.logger?.warn?.(\"warm: アイテムの事前レンダリングに失敗\", {\n slug: item.slug,\n pageId: item.id,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }),\n );\n opts?.onProgress?.(Math.min(i + concurrency, items.length), items.length);\n }\n\n await this.ctx.docCache.setList(this.ctx.collection, {\n items,\n cachedAt: Date.now(),\n });\n return { ok, failed };\n }\n\n // ── 内部 ──────────────────────────────────────────────────────────────\n\n private async persistMeta(\n slug: string,\n item: T,\n opts: { background?: boolean } = {},\n ): Promise<CachedItemMeta<T>> {\n let meta = buildCachedItemMeta(item, this.ctx.source);\n if (this.ctx.hooks.beforeCacheMeta) {\n meta = await this.ctx.hooks.beforeCacheMeta(meta);\n }\n const save = this.ctx.docCache.setMeta(this.ctx.collection, slug, meta);\n if (opts.background && this.ctx.waitUntil) {\n this.ctx.waitUntil(save);\n } else {\n await save;\n }\n return meta;\n }\n\n private async invalidateContentEntry(slug: string): Promise<void> {\n await this.ctx.docCache.invalidate({\n collection: this.ctx.collection,\n slug,\n kind: \"content\",\n });\n }\n\n /**\n * 本文キャッシュをロードする。キャッシュが無いか、メタとの整合性が取れない場合は\n * 再生成して書き戻す。\n */\n private async loadOrBuildContent(\n slug: string,\n item: T,\n ): Promise<CachedItemContent> {\n const expected = this.ctx.source.getLastModified(item);\n const cached = await this.ctx.docCache.getContent(\n this.ctx.collection,\n slug,\n );\n if (cached && cached.notionUpdatedAt === expected) return cached;\n\n const fresh = await buildCachedItemContent(item, this.ctx.render);\n await this.ctx.docCache.setContent(this.ctx.collection, slug, fresh);\n this.ctx.hooks.onContentRevalidated?.(slug, fresh);\n return fresh;\n }\n\n /** メタ既知の状態で本文だけバックグラウンド再生成する。エラーは onSwrError フックに通知する。 */\n private async rebuildContentBg(slug: string, item: T): Promise<void> {\n try {\n const fresh = await buildCachedItemContent(item, this.ctx.render);\n await this.ctx.docCache.setContent(this.ctx.collection, slug, fresh);\n this.ctx.hooks.onContentRevalidated?.(slug, fresh);\n } catch (err) {\n const cmsErr = isCMSError(err)\n ? err\n : new CMSError({\n code: \"swr/content_rebuild_failed\",\n message: \"SWR background content rebuild failed.\",\n cause: err,\n context: {\n operation: \"swr.rebuildContentBg\",\n collection: this.ctx.collection,\n slug,\n },\n });\n this.ctx.hooks.onSwrError?.(cmsErr, { phase: \"item-content\", slug });\n this.ctx.logger?.warn?.(\"本文のバックグラウンド再生成に失敗\", {\n slug,\n collection: this.ctx.collection,\n code: cmsErr.code,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n\n private attachLazyContent(meta: CachedItemMeta<T>): ItemWithContent<T> {\n const slug = meta.item.slug;\n const item = meta.item;\n // 同一インスタンス内で本文ロードを集約する (複数呼び出しでも 1 回の I/O)\n let payloadPromise: Promise<CachedItemContent> | undefined;\n const loadPayload = (): Promise<CachedItemContent> => {\n if (!payloadPromise) {\n payloadPromise = this.loadOrBuildContent(slug, item);\n }\n return payloadPromise;\n };\n\n return Object.assign(Object.create(null) as object, item, {\n html: async () => (await loadPayload()).html,\n markdown: async () => (await loadPayload()).markdown,\n blocks: async () => (await loadPayload()).blocks,\n }) as ItemWithContent<T>;\n }\n\n private async fetchList(): Promise<T[]> {\n const cached = await this.ctx.docCache.getList<T>(this.ctx.collection);\n if (cached) {\n if (\n this.ctx.ttlMs !== undefined &&\n isStale(cached.cachedAt, this.ctx.ttlMs)\n ) {\n // TTL 切れ: ブロッキング再取得\n this.ctx.logger?.debug?.(\"リストキャッシュ期限切れ(TTL)、フェッチ\", {\n operation: \"list\",\n collection: this.ctx.collection,\n cacheAdapter: this.ctx.docCacheName,\n });\n this.ctx.hooks.onListCacheMiss?.();\n const items = await this.fetchListRaw();\n await this.ctx.docCache.setList(this.ctx.collection, {\n items,\n cachedAt: Date.now(),\n });\n return items;\n }\n // SWR: 即時返却 + バックグラウンド差分チェック\n const bg = this.checkAndUpdateListBg(cached);\n if (this.ctx.waitUntil) this.ctx.waitUntil(bg);\n this.ctx.logger?.debug?.(\"リストキャッシュヒット\", {\n operation: \"list\",\n collection: this.ctx.collection,\n cacheAdapter: this.ctx.docCacheName,\n });\n this.ctx.hooks.onListCacheHit?.(cached);\n return cached.items;\n }\n\n // 未キャッシュ: 同期フェッチ\n this.ctx.logger?.debug?.(\"リストキャッシュミス、フェッチ\", {\n operation: \"list\",\n collection: this.ctx.collection,\n cacheAdapter: this.ctx.docCacheName,\n });\n this.ctx.hooks.onListCacheMiss?.();\n const items = await this.fetchListRaw();\n const cachedAt = Date.now();\n const save = this.ctx.docCache.setList(this.ctx.collection, {\n items,\n cachedAt,\n });\n if (this.ctx.waitUntil) {\n this.ctx.waitUntil(save);\n } else {\n await save;\n }\n return items;\n }\n\n private async checkAndUpdateItemBg(\n slug: string,\n cached: CachedItemMeta<T>,\n ): Promise<void> {\n try {\n const item = await this.fetchRaw(slug);\n if (!item) return;\n const lm = this.ctx.source.getLastModified(item);\n if (lm !== cached.notionUpdatedAt) {\n const meta = await this.persistMeta(slug, item);\n await this.invalidateContentEntry(slug);\n this.ctx.logger?.debug?.(\"SWR: 差分を検出、メタを差し替え\", {\n operation: \"find:bg\",\n slug,\n collection: this.ctx.collection,\n notionUpdatedAt: cached.notionUpdatedAt,\n });\n this.ctx.hooks.onCacheRevalidated?.(slug, meta);\n await this.rebuildContentBg(slug, item);\n } else if (this.ctx.ttlMs !== undefined) {\n // 変更なし + TTL あり: cachedAt をリセットして次回の期限切れを先送りする\n await this.ctx.docCache.setMeta(this.ctx.collection, slug, {\n ...cached,\n cachedAt: Date.now(),\n });\n this.ctx.logger?.debug?.(\"SWR: 差分なし、TTL をリセット\", {\n operation: \"find:bg\",\n slug,\n collection: this.ctx.collection,\n });\n }\n } catch (err) {\n const cmsErr = isCMSError(err)\n ? err\n : new CMSError({\n code: \"swr/item_check_failed\",\n message: \"SWR background item check failed.\",\n cause: err,\n context: {\n operation: \"swr.checkAndUpdateItemBg\",\n collection: this.ctx.collection,\n slug,\n },\n });\n this.ctx.hooks.onSwrError?.(cmsErr, { phase: \"item-meta\", slug });\n this.ctx.logger?.warn?.(\n \"SWR: アイテムのバックグラウンド差分チェックに失敗\",\n {\n slug,\n collection: this.ctx.collection,\n code: cmsErr.code,\n error: err instanceof Error ? err.message : String(err),\n },\n );\n }\n }\n\n private async checkAndUpdateListBg(cached: CachedItemList<T>): Promise<void> {\n try {\n const items = await this.fetchListRaw();\n if (\n this.ctx.source.getListVersion(items) !==\n this.ctx.source.getListVersion(cached.items)\n ) {\n const listEntry = { items, cachedAt: Date.now() };\n await this.ctx.docCache.setList(this.ctx.collection, listEntry);\n this.ctx.logger?.debug?.(\n \"SWR: リスト差分を検出、キャッシュを差し替え\",\n {\n operation: \"list:bg\",\n collection: this.ctx.collection,\n },\n );\n this.ctx.hooks.onListCacheRevalidated?.(listEntry);\n } else if (this.ctx.ttlMs !== undefined) {\n await this.ctx.docCache.setList(this.ctx.collection, {\n ...cached,\n cachedAt: Date.now(),\n });\n this.ctx.logger?.debug?.(\"SWR: リスト差分なし、TTL をリセット\", {\n operation: \"list:bg\",\n collection: this.ctx.collection,\n });\n }\n } catch (err) {\n const cmsErr = isCMSError(err)\n ? err\n : new CMSError({\n code: \"swr/list_check_failed\",\n message: \"SWR background list check failed.\",\n cause: err,\n context: {\n operation: \"swr.checkAndUpdateListBg\",\n collection: this.ctx.collection,\n },\n });\n this.ctx.hooks.onSwrError?.(cmsErr, { phase: \"list\" });\n this.ctx.logger?.warn?.(\n \"SWR: リストのバックグラウンド差分チェックに失敗\",\n {\n collection: this.ctx.collection,\n code: cmsErr.code,\n error: err instanceof Error ? err.message : String(err),\n },\n );\n }\n }\n\n private async fetchListRaw(): Promise<T[]> {\n const items = await withRetry(\n () =>\n this.ctx.source.list({\n publishedStatuses:\n this.ctx.publishedStatuses.length > 0\n ? this.ctx.publishedStatuses\n : undefined,\n }),\n {\n ...this.ctx.retryConfig,\n onRetry: (attempt, status) => {\n this.ctx.logger?.warn?.(\"list() リトライ中\", { attempt, status });\n },\n },\n );\n return items.filter((item) => {\n if (item.isArchived || item.isInTrash) return false;\n if (\n this.ctx.accessibleStatuses.length > 0 &&\n (!item.status || !this.ctx.accessibleStatuses.includes(item.status))\n )\n return false;\n return true;\n });\n }\n\n private async fetchRaw(slug: string): Promise<T | null> {\n const retryOpts = {\n ...this.ctx.retryConfig,\n onRetry: (attempt: number, status: number) => {\n this.ctx.logger?.warn?.(\"find() リトライ中\", {\n attempt,\n status,\n slug,\n });\n },\n };\n\n // slugField から Notion プロパティ名を解決して効率的なフィルタクエリを実行する。\n const notionPropName =\n this.ctx.source.properties?.[this.ctx.slugField]?.notion;\n\n let item: T | null;\n const findByProp = this.ctx.source.findByProp?.bind(this.ctx.source);\n if (notionPropName && findByProp) {\n item = await withRetry(() => findByProp(notionPropName, slug), retryOpts);\n } else {\n // フォールバック: list して線形探索\n const all = await withRetry(() => this.ctx.source.list(), retryOpts);\n item = all.find((i) => i.slug === slug) ?? null;\n }\n\n if (!item) return null;\n if (item.isArchived || item.isInTrash) return null;\n if (\n this.ctx.accessibleStatuses.length > 0 &&\n (!item.status || !this.ctx.accessibleStatuses.includes(item.status))\n ) {\n return null;\n }\n return item;\n }\n}\n\nfunction matchesWhere<T extends BaseContentItem>(\n item: T,\n where: WhereClause<T>,\n): boolean {\n for (const key of Object.keys(where) as (keyof T & string)[]) {\n const expected = where[key];\n const actual = item[key];\n if (Array.isArray(expected)) {\n if (!(expected as readonly unknown[]).includes(actual)) return false;\n } else {\n if (actual !== expected) return false;\n }\n }\n return true;\n}\n\nfunction applyListOptions<T extends BaseContentItem>(\n items: T[],\n opts?: ListOptions<T>,\n): T[] {\n if (!opts) return sortByPublishedAtDesc(items);\n let result = items;\n\n if (opts.statuses) {\n const allow = new Set(\n Array.isArray(opts.statuses) ? opts.statuses : [opts.statuses],\n );\n result = result.filter((it) => it.status != null && allow.has(it.status));\n }\n\n if (opts.tag) {\n const tag = opts.tag;\n result = result.filter((it) => {\n const tags = (it as { tags?: string[] }).tags;\n return Array.isArray(tags) && tags.includes(tag);\n });\n }\n\n if (opts.where) {\n const where = opts.where;\n result = result.filter((it) => matchesWhere(it, where));\n }\n\n if (opts.filter) {\n result = result.filter(opts.filter);\n }\n\n if (opts.sort) {\n result = [...result].sort(makeComparator(opts.sort));\n } else {\n result = sortByPublishedAtDesc(result);\n }\n\n const skip = opts.skip ?? 0;\n const limit = opts.limit;\n if (skip > 0 || limit !== undefined) {\n result = result.slice(skip, limit !== undefined ? skip + limit : undefined);\n }\n\n return result;\n}\n\n/** publishedAt 降順、未設定の場合は lastEditedTime 降順でソートする。 */\nfunction sortByPublishedAtDesc<T extends BaseContentItem>(items: T[]): T[] {\n return [...items].sort((a, b) => {\n // lastEditedTime は必須なので av/bv は常に truthy\n const av = a.publishedAt ?? a.lastEditedTime;\n const bv = b.publishedAt ?? b.lastEditedTime;\n if (av === bv) return 0;\n return av > bv ? -1 : 1;\n });\n}\n\nfunction makeComparator<T extends BaseContentItem>(\n sort: SortOption<T>,\n): (a: T, b: T) => number {\n if (sort.compare) return sort.compare;\n const by = sort.by as keyof T;\n const dir = sort.dir === \"asc\" ? 1 : -1;\n return (a, b) => {\n const av = a[by];\n const bv = b[by];\n if (av === bv) return 0;\n if (av === undefined || av === null) return 1;\n if (bv === undefined || bv === null) return -1;\n if (typeof av === \"string\" && typeof bv === \"string\") {\n return av > bv ? dir : -dir;\n }\n if (typeof av === \"number\" && typeof bv === \"number\") {\n return av > bv ? dir : -dir;\n }\n throw new CMSError({\n code: \"core/sort_unsupported_type\",\n message: `\"${String(by)}\" フィールドの型 \"${typeof av}\" はソート非対応です。compare 関数を指定してください。`,\n context: {\n operation: \"makeComparator\",\n field: String(by),\n type: typeof av,\n },\n });\n };\n}\n","import { isCMSError } from \"./errors\";\nimport type { ImageCacheOps, InvalidateScope } from \"./types/index\";\n\n/** `$handler()` の挙動設定。 */\nexport interface HandlerOptions {\n /** マウントするベースパス。デフォルト `/api/cms`。 */\n basePath?: string;\n /** 画像プロキシのパス (basePath 相対)。デフォルト `/images/:hash`。 */\n imagesPath?: string;\n /** revalidate webhook のパス (basePath 相対)。デフォルト `/revalidate`。 */\n revalidatePath?: string;\n /** Webhook 署名検証用シークレット (未指定なら検証スキップ)。 */\n webhookSecret?: string;\n /** デフォルト実装を無効化する場合 true。 */\n disabled?: boolean;\n}\n\n/** `$handler()` が内部で依存する CMS 機能の最小セット。 */\nexport interface HandlerAdapter {\n imageCache: ImageCacheOps;\n /**\n * 指定コレクションの DataSource.parseWebhook を呼ぶ。\n * 未知コレクション → `webhook/unknown_collection` CMSError\n * parseWebhook 未実装 → `webhook/not_implemented` CMSError\n */\n parseWebhookFor(\n collection: string,\n req: Request,\n webhookSecret: string | undefined,\n ): Promise<InvalidateScope>;\n revalidate(scope: InvalidateScope): Promise<void>;\n}\n\nconst DEFAULT_OPTS = {\n basePath: \"/api/cms\",\n imagesPath: \"/images\",\n revalidatePath: \"/revalidate\",\n} as const;\n\n/**\n * CMSError のコードから HTTP ステータスコードを返す。\n * 既知の webhook エラーコードのみ対応し、それ以外は null を返す。\n */\nfunction webhookErrorStatus(code: string): number | null {\n if (code === \"webhook/signature_invalid\") return 401;\n if (code === \"webhook/not_implemented\") return 501;\n if (code === \"webhook/unknown_collection\") return 404;\n if (code === \"webhook/payload_invalid\") return 400;\n return null;\n}\n\n/**\n * Web Standard な Request → Response ルーター。\n * Next.js / React Router / Hono / Cloudflare Workers いずれでも使える。\n *\n * ルート:\n * - GET `{basePath}/images/:hash` — 画像プロキシ\n * - POST `{basePath}/revalidate/:collection` — Webhook 受信 + $revalidate()\n */\nexport function createHandler(\n adapter: HandlerAdapter,\n opts: HandlerOptions = {},\n): (req: Request) => Promise<Response> {\n const basePath = trimTrailingSlash(opts.basePath ?? DEFAULT_OPTS.basePath);\n const imagesPath = opts.imagesPath ?? DEFAULT_OPTS.imagesPath;\n const revalidatePath = opts.revalidatePath ?? DEFAULT_OPTS.revalidatePath;\n\n return async (req: Request): Promise<Response> => {\n const url = new URL(req.url);\n const path = url.pathname;\n\n if (!path.startsWith(basePath)) {\n return new Response(\"Not Found\", { status: 404 });\n }\n const rel = path.slice(basePath.length) || \"/\";\n\n // 画像: GET {basePath}/images/:hash\n if (req.method === \"GET\" && rel.startsWith(`${imagesPath}/`)) {\n const hash = rel.slice(imagesPath.length + 1);\n if (!hash) return new Response(\"Bad Request\", { status: 400 });\n const object = await adapter.imageCache.get(hash);\n if (!object) return new Response(\"Not Found\", { status: 404 });\n const headers = new Headers();\n if (object.contentType) headers.set(\"content-type\", object.contentType);\n headers.set(\"cache-control\", \"public, max-age=31536000, immutable\");\n return new Response(object.data, { headers });\n }\n\n // Revalidate: POST {basePath}/revalidate/:collection\n if (req.method === \"POST\" && rel.startsWith(`${revalidatePath}/`)) {\n const collection = rel.slice(revalidatePath.length + 1);\n if (!collection || collection.includes(\"/\")) {\n return new Response(\n JSON.stringify({ ok: false, reason: \"collection required\" }),\n { status: 400, headers: { \"content-type\": \"application/json\" } },\n );\n }\n try {\n const scope = await adapter.parseWebhookFor(\n collection,\n req,\n opts.webhookSecret,\n );\n await adapter.revalidate(scope);\n return new Response(JSON.stringify({ ok: true, scope }), {\n status: 200,\n headers: { \"content-type\": \"application/json\" },\n });\n } catch (err) {\n if (isCMSError(err)) {\n const status = webhookErrorStatus(err.code);\n if (status !== null) {\n return new Response(JSON.stringify({ ok: false, code: err.code }), {\n status,\n headers: { \"content-type\": \"application/json\" },\n });\n }\n }\n throw err;\n }\n }\n\n return new Response(\"Not Found\", { status: 404 });\n };\n}\n\nfunction trimTrailingSlash(s: string): string {\n return s.endsWith(\"/\") ? s.slice(0, -1) : s;\n}\n","import { noopDocOps, noopImgOps } from \"./cache/noop\";\nimport { CollectionClientImpl, type CollectionContext } from \"./collection\";\nimport { CMSError } from \"./errors\";\nimport { createHandler, type HandlerOptions } from \"./handler\";\nimport { mergeHooks, mergeLoggers } from \"./hooks\";\nimport type { RenderContext } from \"./rendering\";\nimport type { RetryConfig } from \"./retry\";\nimport { DEFAULT_RETRY_CONFIG } from \"./retry\";\nimport type {\n BaseContentItem,\n CacheAdapter,\n CMSHooks,\n CollectionClient,\n CollectionsConfig,\n CreateCMSOptions,\n DataSource,\n DocumentCacheOps,\n ImageCacheOps,\n InferCollectionItem,\n InvalidateScope,\n Logger,\n LogLevel,\n RendererFn,\n StorageBinary,\n} from \"./types/index\";\n\nconst DEFAULT_IMAGE_PROXY_BASE = \"/api/images\";\n\n/** `CMSClient<C>` — コレクション別アクセス + グローバル操作の合成型。 */\nexport type CMSClient<C extends CollectionsConfig> = {\n [K in keyof C]: CollectionClient<InferCollectionItem<C[K]>>;\n} & CMSGlobalOps;\n\n/** `CMSClient` のグローバル名前空間。 */\nexport interface CMSGlobalOps {\n /** 登録されているコレクション名の一覧。 */\n readonly collections: readonly string[];\n /** 全コレクションまたは特定スコープのキャッシュを無効化する。 */\n invalidate(scope?: InvalidateScope): Promise<void>;\n /** Web Standard なルーティングハンドラ (画像プロキシ / webhook) を生成する。 */\n handler(opts?: HandlerOptions): (req: Request) => Promise<Response>;\n /** ハッシュキーでキャッシュ画像を取得する。 */\n getCachedImage(hash: string): Promise<StorageBinary | null>;\n}\n\ninterface ResolvedCache {\n doc: DocumentCacheOps;\n docName: string;\n img: ImageCacheOps;\n imgName: string;\n hasImg: boolean;\n}\n\n/**\n * `cache` オプションから document / image オペレーションを解決する。\n *\n * - 各 adapter の `handles` を見て先勝ち (最初に見つかったもの) で振り分ける\n * - 未指定なら両方 noop\n */\nfunction resolveCache(\n cache: readonly CacheAdapter[] | undefined,\n): ResolvedCache {\n const adapters = cache ?? [];\n\n let doc: DocumentCacheOps = noopDocOps;\n let docName = \"noop-document\";\n let img: ImageCacheOps = noopImgOps;\n let imgName = \"noop-image\";\n let docFound = false;\n let imgFound = false;\n\n for (const adapter of adapters) {\n if (!docFound && adapter.handles.includes(\"document\") && adapter.doc) {\n doc = adapter.doc;\n docName = adapter.name;\n docFound = true;\n }\n if (!imgFound && adapter.handles.includes(\"image\") && adapter.img) {\n img = adapter.img;\n imgName = adapter.name;\n imgFound = true;\n }\n }\n\n return { doc, docName, img, imgName, hasImg: imgFound };\n}\n\nconst LOG_LEVEL_ORDER: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\n/** `logger` から `minLevel` 未満のレベルを除いた新しい Logger を返す。 */\nfunction applyLogLevel(\n logger: Logger | undefined,\n minLevel: LogLevel,\n): Logger | undefined {\n if (!logger) return undefined;\n const minOrder = LOG_LEVEL_ORDER[minLevel];\n const filtered: Logger = {};\n for (const level of [\"debug\", \"info\", \"warn\", \"error\"] as const) {\n if (LOG_LEVEL_ORDER[level] >= minOrder) {\n filtered[level] = logger[level];\n }\n }\n return filtered;\n}\n\n/**\n * 複数の `CollectionDef` を束ねた CMS クライアントを生成する。\n *\n * 通常はユーザーが直接呼ぶことはなく、CLI 生成の `nhc.ts` の `createCMS`\n * (低レベルのこの関数をラップしたもの) を経由する。\n *\n * @example\n * createCMS({\n * collections: {\n * posts: {\n * source: createNotionCollection({ token, dataSourceId, properties }),\n * slugField: \"slug\",\n * statusField: \"status\",\n * publishedStatuses: [\"公開済み\"],\n * }\n * },\n * cache: [memoryCache()],\n * swr: { ttlMs: 5 * 60_000 },\n * });\n */\nexport function createCMS<C extends CollectionsConfig>(\n opts: CreateCMSOptions<C>,\n): CMSClient<C> {\n if (!opts.collections || Object.keys(opts.collections).length === 0) {\n throw new CMSError({\n code: \"core/config_invalid\",\n message:\n \"createCMS: collections に少なくとも 1 つのコレクションを指定してください。\",\n context: { operation: \"createCMS\" },\n });\n }\n\n for (const [name, def] of Object.entries(opts.collections)) {\n if (!def.source) {\n throw new CMSError({\n code: \"core/config_invalid\",\n message: `createCMS: コレクション \"${name}\" の source は必須です。`,\n context: { operation: \"createCMS\", collection: name },\n });\n }\n if (!def.slugField) {\n throw new CMSError({\n code: \"core/config_invalid\",\n message: `createCMS: コレクション \"${name}\" の slugField は必須です。`,\n context: { operation: \"createCMS\", collection: name },\n });\n }\n }\n\n const cacheRes = resolveCache(opts.cache);\n const ttlMs = opts.swr?.ttlMs;\n const imageProxyBase = opts.imageProxyBase ?? DEFAULT_IMAGE_PROXY_BASE;\n const contentConfig = opts.content;\n const rendererFn: RendererFn | undefined = opts.renderer;\n const waitUntil = opts.waitUntil;\n const baseLogger: Logger | undefined = mergeLoggers(\n opts.plugins ?? [],\n opts.logger,\n );\n const logger = opts.logLevel\n ? applyLogLevel(baseLogger, opts.logLevel)\n : baseLogger;\n const hooks: CMSHooks<BaseContentItem> = mergeHooks(\n opts.plugins ?? [],\n opts.hooks,\n logger,\n );\n const maxConcurrent = opts.rateLimiter?.maxConcurrent ?? 3;\n const retryConfig: RetryConfig = {\n ...DEFAULT_RETRY_CONFIG,\n ...(opts.rateLimiter ?? {}),\n };\n\n const collectionNames: (keyof C & string)[] = [];\n const collections: Record<string, CollectionClient<BaseContentItem>> = {};\n for (const [name, def] of Object.entries(opts.collections)) {\n collectionNames.push(name as keyof C & string);\n const source = def.source as DataSource<BaseContentItem>;\n const colHooks = def.hooks as CMSHooks<BaseContentItem> | undefined;\n const collectionHooks: CMSHooks<BaseContentItem> = colHooks\n ? mergeHooks([{ name: `${name}:global`, hooks }], colHooks, logger)\n : hooks;\n const renderCtx: RenderContext<BaseContentItem> = {\n source,\n rendererFn,\n imgCache: cacheRes.img,\n imgCacheName: cacheRes.imgName,\n hasImageCache: cacheRes.hasImg,\n imageProxyBase,\n contentConfig,\n hooks: collectionHooks,\n logger,\n };\n const ctx: CollectionContext<BaseContentItem> = {\n collection: name,\n source,\n docCache: cacheRes.doc,\n docCacheName: cacheRes.docName,\n render: renderCtx,\n hooks: collectionHooks,\n logger,\n ttlMs,\n publishedStatuses: def.publishedStatuses\n ? [...def.publishedStatuses]\n : [],\n accessibleStatuses: def.accessibleStatuses\n ? [...def.accessibleStatuses]\n : [],\n retryConfig,\n maxConcurrent,\n waitUntil,\n slugField: def.slugField,\n };\n collections[name] = new CollectionClientImpl(ctx);\n }\n\n const globalOps: CMSGlobalOps = {\n collections: collectionNames,\n async invalidate(scope?: InvalidateScope): Promise<void> {\n logger?.debug?.(\"グローバルキャッシュを無効化\", {\n operation: \"invalidate\",\n cacheAdapter: cacheRes.docName,\n });\n await cacheRes.doc.invalidate(scope ?? \"all\");\n },\n handler(handlerOpts?: HandlerOptions) {\n return createHandler(\n {\n imageCache: cacheRes.img,\n async parseWebhookFor(collection, req, webhookSecret) {\n const def = opts.collections[collection];\n if (!def) {\n throw new CMSError({\n code: \"webhook/unknown_collection\",\n message: `Unknown collection: ${collection}`,\n context: { operation: \"parseWebhookFor\", collection },\n });\n }\n const ds = def.source as DataSource<BaseContentItem>;\n if (!ds.parseWebhook) {\n throw new CMSError({\n code: \"webhook/not_implemented\",\n message: `Collection \"${collection}\" does not support webhooks.`,\n context: { operation: \"parseWebhookFor\", collection },\n });\n }\n return ds.parseWebhook(req, { secret: webhookSecret });\n },\n revalidate: (scope) => globalOps.invalidate(scope),\n },\n handlerOpts,\n );\n },\n getCachedImage(hash) {\n return cacheRes.img.get(hash);\n },\n };\n\n return Object.assign(\n Object.create(null) as object,\n collections,\n globalOps,\n ) as CMSClient<C>;\n}\n","import type { BaseContentItem } from \"./content\";\nimport type { CMSHooks } from \"./hooks\";\nimport type { Logger } from \"./logger\";\n\nexport interface CMSPlugin<T extends BaseContentItem = BaseContentItem> {\n name: string;\n hooks?: CMSHooks<T>;\n logger?: Partial<Logger>;\n}\n\nexport function definePlugin<T extends BaseContentItem>(\n plugin: CMSPlugin<T>,\n): CMSPlugin<T> {\n return plugin;\n}\n"],"mappings":";;;;;AACA,eAAsB,UAAU,OAAgC;CAC9D,MAAM,OAAO,IAAI,aAAa,CAAC,OAAO,MAAM;CAC5C,MAAM,OAAO,MAAM,OAAO,OAAO,OAAO,WAAW,KAAK;AACxD,QAAO,MAAM,KAAK,IAAI,WAAW,KAAK,CAAC,CACpC,KAAK,MAAM,EAAE,SAAS,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC,CAC3C,KAAK,GAAG;;;;;;AAOb,SAAgB,QAAQ,UAAkB,OAAyB;AACjE,KAAI,UAAU,KAAA,EAAW,QAAO;AAChC,QAAO,KAAK,KAAK,GAAG,WAAW;;;;;ACJjC,MAAM,UAA4B;CAChC,QACE,aACmC;AACnC,SAAO,QAAQ,QAAQ,KAAK;;CAE9B,QACE,aACA,OACe;AACf,SAAO,QAAQ,SAAS;;CAE1B,QACE,aACA,OACmC;AACnC,SAAO,QAAQ,QAAQ,KAAK;;CAE9B,QACE,aACA,OACA,OACe;AACf,SAAO,QAAQ,SAAS;;CAE1B,WACE,aACA,OACmC;AACnC,SAAO,QAAQ,QAAQ,KAAK;;CAE9B,WACE,aACA,OACA,OACe;AACf,SAAO,QAAQ,SAAS;;CAE1B,aAA4B;AAC1B,SAAO,QAAQ,SAAS;;CAE3B;;AAGD,MAAM,UAAyB;CAC7B,IAAI,OAA8C;AAChD,SAAO,QAAQ,QAAQ,KAAK;;CAE9B,MAAqB;AACnB,SAAO,QAAQ,SAAS;;CAE3B;;;;;AAMD,MAAa,aAA+B;AAC5C,MAAa,aAA4B;;;;;;;;AC5DzC,SAAS,qBACP,aACA,WACQ;AACR,KAAI,CAAC,YACH,OAAM,IAAI,SAAS;EACjB,MAAM;EACN,SAAS;EACT,SAAS;GAAE,WAAW;GAAkC;GAAW;EACpE,CAAC;CAEJ,MAAM,SAAS,YAAY,MAAM,IAAI,CAAC,MAAM,aAAa,MAAM,CAAC,aAAa;AAC7E,KAAI,CAAC,MAAM,WAAW,SAAS,CAC7B,OAAM,IAAI,SAAS;EACjB,MAAM;EACN,SAAS,8CAA8C;EACvD,SAAS;GACP,WAAW;GACX;GACA,aAAa;GACd;EACF,CAAC;AAEJ,QAAO;;;;;;AAOT,eAAe,mBACb,OACA,WACA,WACA,MACA,gBACA,QACiB;CACjB,MAAM,WAAW,GAAG,eAAe,GAAG;AAGtC,KAAI,MADmB,MAAM,IAAI,KAAK,EACxB;AACZ,UAAQ,QAAQ,cAAc;GAC5B,WAAW;GACX,cAAc;GACd,WAAW;GACZ,CAAC;AACF,SAAO;;AAGT,SAAQ,QAAQ,2BAA2B;EACzC,WAAW;EACX,cAAc;EACd,WAAW;EACZ,CAAC;AAEF,KAAI;EACF,MAAM,WAAW,MAAM,MAAM,WAAW,EACtC,QAAQ,YAAY,QAAQ,IAAO,EACpC,CAAC;AACF,MAAI,CAAC,SAAS,GACZ,OAAM,IAAI,SAAS;GACjB,MAAM;GACN,SAAS,sCAAsC,SAAS;GACxD,SAAS;IACP,WAAW;IACX;IACA,YAAY,SAAS;IACtB;GACF,CAAC;EAGJ,MAAM,OAAO,MAAM,SAAS,aAAa;EACzC,MAAM,cAAc,qBAClB,SAAS,QAAQ,IAAI,eAAe,EACpC,UACD;AACD,QAAM,MAAM,IAAI,MAAM,MAAM,YAAY;AACxC,UAAQ,QAAQ,eAAe;GAC7B,WAAW;GACX,cAAc;GACd,WAAW;GACZ,CAAC;UACK,KAAK;AACZ,MAAI,WAAW,IAAI,CAAE,OAAM;AAC3B,QAAM,IAAI,SAAS;GACjB,MAAM;GACN,SAAS;GACT,OAAO;GACP,SAAS;IAAE,WAAW;IAAsB;IAAW;GACxD,CAAC;;AAGJ,QAAO;;;;;;;;;;AAWT,SAAgB,kBACd,OACA,WACA,gBACA,QACwC;CACxC,MAAM,2BAAW,IAAI,KAAqB;AAC1C,QAAO,OAAO,cAAc;EAC1B,IAAI,OAAO,SAAS,IAAI,UAAU;AAClC,MAAI,SAAS,KAAA,GAAW;AACtB,UAAO,MAAM,UAAU,UAAU;AACjC,YAAS,IAAI,WAAW,KAAK;;AAE/B,SAAO,mBACL,OACA,WACA,WACA,MACA,gBACA,OACD;;;;;;;;;AClHL,eAAe,sBAA2C;AACxD,KAAI;AAEF,UAAQ,MADU,OAAO,kCACsB;SACzC;AACN,QAAM,IAAI,SAAS;GACjB,MAAM;GACN,SACE;GAEF,SAAS,EAAE,WAAW,uBAAuB;GAC9C,CAAC;;;;;;AAoBN,SAAgB,oBACd,MACA,QACmB;AACnB,QAAO;EACL;EACA,iBAAiB,OAAO,gBAAgB,KAAK;EAC7C,UAAU,KAAK,KAAK;EACrB;;;;;;AAOH,eAAsB,uBACpB,MACA,KAC4B;CAC5B,MAAM,QAAQ,KAAK,KAAK;AACxB,KAAI,QAAQ,OAAO,kBAAkB;EACnC,MAAM,KAAK;EACX,QAAQ,KAAK;EACd,CAAC;AACF,KAAI,MAAM,gBAAgB,KAAK,KAAK;CAEpC,IAAI;AACJ,KAAI;AACF,aAAW,MAAM,IAAI,OAAO,aAAa,KAAK;UACvC,KAAK;AACZ,MAAI,WAAW,IAAI,CAAE,OAAM;AAC3B,QAAM,IAAI,SAAS;GACjB,MAAM;GACN,SAAS;GACT,OAAO;GACP,SAAS;IACP,WAAW;IACX,QAAQ,KAAK;IACb,MAAM,KAAK;IACZ;GACF,CAAC;;CAGJ,IAAI;AACJ,KAAI;AACF,WAAS,MAAM,IAAI,OAAO,WAAW,KAAK;UACnC,KAAK;AACZ,MAAI,WAAW,IAAI,CAAE,OAAM;AAC3B,QAAM,IAAI,SAAS;GACjB,MAAM;GACN,SAAS;GACT,OAAO;GACP,SAAS;IACP,WAAW;IACX,QAAQ,KAAK;IACb,MAAM,KAAK;IACZ;GACF,CAAC;;CAGJ,MAAM,aAAa,IAAI,gBACnB,kBACE,IAAI,UACJ,IAAI,cACJ,IAAI,gBACJ,IAAI,OACL,GACD,KAAA;CAEJ,MAAM,aAAa,IAAI,cAAe,MAAM,qBAAqB;CAEjE,IAAI;AACJ,KAAI;AACF,SAAO,MAAM,WAAW,UAAU;GAChC,gBAAgB,IAAI;GACpB;GACA,eAAe,IAAI,eAAe;GAClC,eAAe,IAAI,eAAe;GACnC,CAAC;UACK,KAAK;AACZ,MAAI,WAAW,IAAI,CAAE,OAAM;AAC3B,QAAM,IAAI,SAAS;GACjB,MAAM;GACN,SAAS;GACT,OAAO;GACP,SAAS;IACP,WAAW;IACX,QAAQ,KAAK;IACb,MAAM,KAAK;IACZ;GACF,CAAC;;AAGJ,KAAI,IAAI,MAAM,YACZ,QAAO,MAAM,IAAI,MAAM,YAAY,MAAM,KAAK;CAGhD,IAAI,SAA4B;EAC9B;EACA;EACA;EACA,iBAAiB,IAAI,OAAO,gBAAgB,KAAK;EACjD,UAAU,KAAK,KAAK;EACrB;AAED,KAAI,IAAI,MAAM,mBACZ,UAAS,MAAM,IAAI,MAAM,mBAAmB,QAAQ,KAAK;CAG3D,MAAM,aAAa,KAAK,KAAK,GAAG;AAChC,KAAI,QAAQ,OAAO,kBAAkB;EACnC,MAAM,KAAK;EACX;EACD,CAAC;AACF,KAAI,MAAM,cAAc,KAAK,MAAM,WAAW;AAE9C,QAAO;;;;AC7JT,MAAa,uBAAoC;CAC/C,SAAS;EAAC;EAAK;EAAK;EAAI;CACxB,YAAY;CACZ,aAAa;CACb,QAAQ;CACT;;;;;;;;;AAUD,eAAsB,UACpB,IACA,QACY;CACZ,IAAI;AACJ,MAAK,IAAI,UAAU,GAAG,WAAW,OAAO,YAAY,UAClD,KAAI;AACF,SAAO,MAAM,IAAI;UACV,KAAK;EACZ,MAAM,SAAU,IAA4B;AAC5C,MAAI,WAAW,KAAA,KAAa,CAAC,OAAO,QAAQ,SAAS,OAAO,CAC1D,OAAM;AAER,cAAY;AACZ,MAAI,UAAU,OAAO,YAAY;AAC/B,UAAO,UAAU,UAAU,GAAG,OAAO;GACrC,MAAM,eACJ,OAAO,WAAW,QAAQ,KAAM,KAAK,QAAQ,GAAG,KAAM;GACxD,MAAM,QAAQ,OAAO,cAAc,KAAK,UAAU;AAClD,SAAM,IAAI,SAAS,YAAY,WAAW,SAAS,MAAM,CAAC;;;AAIhE,OAAM;;;;;;;;;;;ACZR,SAAgB,cAAc,YAAoB,MAAuB;AACvE,QAAO,OAAO,GAAG,WAAW,GAAG,SAAS;;;AA2B1C,IAAa,uBAAb,MAEA;CACE;CAEA,YAAY,KAA4C;AAA3B,OAAA,MAAA;AAC3B,OAAK,QAAQ;GACX,kBAAkB,KAAK,gBAAgB;GACvC,iBAAiB,SAAiB,KAAK,mBAAmB,KAAK;GAC/D,OAAO,SAAuB,KAAK,SAAS,KAAK;GAClD;;CAKH,MAAM,KACJ,MACA,OAAoB,EAAE,EACc;AAEpC,MAAI,KAAK,aAAa;AACpB,QAAK,IAAI,MAAM,cAAc,KAAK;GAClC,MAAM,OAAO,MAAM,KAAK,SAAS,KAAK;AACtC,OAAI,CAAC,KAAM,QAAO;GAClB,MAAM,OAAO,MAAM,KAAK,YAAY,MAAM,KAAK;AAC/C,SAAM,KAAK,uBAAuB,KAAK;AACvC,UAAO,KAAK,kBAAkB,KAAK;;EAGrC,MAAM,aAAa,MAAM,KAAK,IAAI,SAAS,QACzC,KAAK,IAAI,YACT,KACD;AACD,MAAI,YAAY;AACd,OACE,KAAK,IAAI,UAAU,KAAA,KACnB,QAAQ,WAAW,UAAU,KAAK,IAAI,MAAM,EAC5C;AAEA,SAAK,IAAI,QAAQ,QAAQ,uBAAuB;KAC9C,WAAW;KACX;KACA,YAAY,KAAK,IAAI;KACrB,cAAc,KAAK,IAAI;KACxB,CAAC;AACF,SAAK,IAAI,MAAM,cAAc,KAAK;IAClC,MAAM,OAAO,MAAM,KAAK,SAAS,KAAK;AACtC,QAAI,CAAC,KAAM,QAAO;IAClB,MAAM,OAAO,MAAM,KAAK,YAAY,MAAM,KAAK;AAC/C,UAAM,KAAK,uBAAuB,KAAK;AACvC,WAAO,KAAK,kBAAkB,KAAK;;GAGrC,MAAM,KAAK,KAAK,qBAAqB,MAAM,WAAW;AACtD,OAAI,KAAK,IAAI,UAAW,MAAK,IAAI,UAAU,GAAG;AAC9C,QAAK,IAAI,QAAQ,QAAQ,YAAY;IACnC,WAAW;IACX;IACA,YAAY,KAAK,IAAI;IACrB,cAAc,KAAK,IAAI;IACvB,UAAU,WAAW;IACtB,CAAC;AACF,QAAK,IAAI,MAAM,aAAa,MAAM,WAAW;AAC7C,UAAO,KAAK,kBAAkB,WAAW;;AAI3C,OAAK,IAAI,QAAQ,QAAQ,gBAAgB;GACvC,WAAW;GACX;GACA,YAAY,KAAK,IAAI;GACrB,cAAc,KAAK,IAAI;GACxB,CAAC;AACF,OAAK,IAAI,MAAM,cAAc,KAAK;EAClC,MAAM,OAAO,MAAM,KAAK,SAAS,KAAK;AACtC,MAAI,CAAC,KAAM,QAAO;EAClB,MAAM,OAAO,MAAM,KAAK,YAAY,MAAM,MAAM,EAAE,YAAY,MAAM,CAAC;AACrE,SAAO,KAAK,kBAAkB,KAAK;;CAGrC,MAAM,KAAK,MAAqC;AAE9C,SAAO,iBAAiB,MADD,KAAK,WAAW,EACL,KAAK;;CAGzC,MAAM,SAA4B;AAEhC,UAAO,MADa,KAAK,WAAW,EACvB,KAAK,SAAS,KAAK,KAAK;;CAGvC,MAAM,MACJ,MACA,gBACgC;EAChC,MAAM,MAAM,MAAM,KAAK,SAAS,KAAK;AACrC,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI,IAAI,mBAAmB,eAAgB,QAAO,EAAE,OAAO,OAAO;EAClE,MAAM,OAAO,MAAM,KAAK,YAAY,MAAM,IAAI;AAC9C,QAAM,KAAK,uBAAuB,KAAK;AACvC,SAAO;GAAE,OAAO;GAAM,MAAM,KAAK,kBAAkB,KAAK;GAAE;;CAG5D,MAAM,SACJ,MACA,MAC6C;EAC7C,MAAM,QAAQ,iBAAiB,MAAM,KAAK,WAAW,EAAE,EACrD,MAAM,MAAM,MACb,CAAC;EACF,MAAM,QAAQ,MAAM,WAAW,OAAO,GAAG,SAAS,KAAK;AACvD,MAAI,UAAU,GAAI,QAAO;GAAE,MAAM;GAAM,MAAM;GAAM;AACnD,SAAO;GACL,MAAM,QAAQ,IAAK,MAAM,QAAQ,MAAM,OAAQ;GAC/C,MAAM,QAAQ,MAAM,SAAS,IAAK,MAAM,QAAQ,MAAM,OAAQ;GAC/D;;CAKH,MAAc,iBAAgC;AAC5C,OAAK,IAAI,QAAQ,QAAQ,sBAAsB;GAC7C,WAAW;GACX,YAAY,KAAK,IAAI;GACrB,cAAc,KAAK,IAAI;GACxB,CAAC;AACF,QAAM,KAAK,IAAI,SAAS,WAAW,EAAE,YAAY,KAAK,IAAI,YAAY,CAAC;;CAGzE,MAAc,mBAAmB,MAA6B;AAC5D,OAAK,IAAI,QAAQ,QAAQ,iBAAiB;GACxC,WAAW;GACX,YAAY,KAAK,IAAI;GACrB,cAAc,KAAK,IAAI;GACvB;GACD,CAAC;AACF,QAAM,KAAK,IAAI,SAAS,WAAW;GACjC,YAAY,KAAK,IAAI;GACrB;GACD,CAAC;;CAGJ,MAAc,SAAS,MAAyC;EAC9D,MAAM,QAAQ,MAAM,KAAK,cAAc;EACvC,MAAM,cAAc,MAAM,eAAe,KAAK,IAAI;EAClD,IAAI,KAAK;EACT,MAAM,SAAkD,EAAE;AAE1D,OAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,aAAa;GAClD,MAAM,QAAQ,MAAM,MAAM,GAAG,IAAI,YAAY;AAC7C,SAAM,QAAQ,IACZ,MAAM,IAAI,OAAO,SAAS;AACxB,QAAI;AACF,WAAM,KAAK,YAAY,KAAK,MAAM,KAAK;KACvC,MAAM,UAAU,MAAM,uBAAuB,MAAM,KAAK,IAAI,OAAO;AACnE,WAAM,KAAK,IAAI,SAAS,WACtB,KAAK,IAAI,YACT,KAAK,MACL,QACD;AACD;aACO,KAAK;AACZ,YAAO,KAAK;MAAE,MAAM,KAAK;MAAM,OAAO;MAAK,CAAC;AAC5C,UAAK,IAAI,QAAQ,OAAO,0BAA0B;MAChD,MAAM,KAAK;MACX,QAAQ,KAAK;MACb,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;MACxD,CAAC;;KAEJ,CACH;AACD,SAAM,aAAa,KAAK,IAAI,IAAI,aAAa,MAAM,OAAO,EAAE,MAAM,OAAO;;AAG3E,QAAM,KAAK,IAAI,SAAS,QAAQ,KAAK,IAAI,YAAY;GACnD;GACA,UAAU,KAAK,KAAK;GACrB,CAAC;AACF,SAAO;GAAE;GAAI;GAAQ;;CAKvB,MAAc,YACZ,MACA,MACA,OAAiC,EAAE,EACP;EAC5B,IAAI,OAAO,oBAAoB,MAAM,KAAK,IAAI,OAAO;AACrD,MAAI,KAAK,IAAI,MAAM,gBACjB,QAAO,MAAM,KAAK,IAAI,MAAM,gBAAgB,KAAK;EAEnD,MAAM,OAAO,KAAK,IAAI,SAAS,QAAQ,KAAK,IAAI,YAAY,MAAM,KAAK;AACvE,MAAI,KAAK,cAAc,KAAK,IAAI,UAC9B,MAAK,IAAI,UAAU,KAAK;MAExB,OAAM;AAER,SAAO;;CAGT,MAAc,uBAAuB,MAA6B;AAChE,QAAM,KAAK,IAAI,SAAS,WAAW;GACjC,YAAY,KAAK,IAAI;GACrB;GACA,MAAM;GACP,CAAC;;;;;;CAOJ,MAAc,mBACZ,MACA,MAC4B;EAC5B,MAAM,WAAW,KAAK,IAAI,OAAO,gBAAgB,KAAK;EACtD,MAAM,SAAS,MAAM,KAAK,IAAI,SAAS,WACrC,KAAK,IAAI,YACT,KACD;AACD,MAAI,UAAU,OAAO,oBAAoB,SAAU,QAAO;EAE1D,MAAM,QAAQ,MAAM,uBAAuB,MAAM,KAAK,IAAI,OAAO;AACjE,QAAM,KAAK,IAAI,SAAS,WAAW,KAAK,IAAI,YAAY,MAAM,MAAM;AACpE,OAAK,IAAI,MAAM,uBAAuB,MAAM,MAAM;AAClD,SAAO;;;CAIT,MAAc,iBAAiB,MAAc,MAAwB;AACnE,MAAI;GACF,MAAM,QAAQ,MAAM,uBAAuB,MAAM,KAAK,IAAI,OAAO;AACjE,SAAM,KAAK,IAAI,SAAS,WAAW,KAAK,IAAI,YAAY,MAAM,MAAM;AACpE,QAAK,IAAI,MAAM,uBAAuB,MAAM,MAAM;WAC3C,KAAK;GACZ,MAAM,SAAS,WAAW,IAAI,GAC1B,MACA,IAAI,SAAS;IACX,MAAM;IACN,SAAS;IACT,OAAO;IACP,SAAS;KACP,WAAW;KACX,YAAY,KAAK,IAAI;KACrB;KACD;IACF,CAAC;AACN,QAAK,IAAI,MAAM,aAAa,QAAQ;IAAE,OAAO;IAAgB;IAAM,CAAC;AACpE,QAAK,IAAI,QAAQ,OAAO,qBAAqB;IAC3C;IACA,YAAY,KAAK,IAAI;IACrB,MAAM,OAAO;IACb,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;IACxD,CAAC;;;CAIN,kBAA0B,MAA6C;EACrE,MAAM,OAAO,KAAK,KAAK;EACvB,MAAM,OAAO,KAAK;EAElB,IAAI;EACJ,MAAM,oBAAgD;AACpD,OAAI,CAAC,eACH,kBAAiB,KAAK,mBAAmB,MAAM,KAAK;AAEtD,UAAO;;AAGT,SAAO,OAAO,OAAO,OAAO,OAAO,KAAK,EAAY,MAAM;GACxD,MAAM,aAAa,MAAM,aAAa,EAAE;GACxC,UAAU,aAAa,MAAM,aAAa,EAAE;GAC5C,QAAQ,aAAa,MAAM,aAAa,EAAE;GAC3C,CAAC;;CAGJ,MAAc,YAA0B;EACtC,MAAM,SAAS,MAAM,KAAK,IAAI,SAAS,QAAW,KAAK,IAAI,WAAW;AACtE,MAAI,QAAQ;AACV,OACE,KAAK,IAAI,UAAU,KAAA,KACnB,QAAQ,OAAO,UAAU,KAAK,IAAI,MAAM,EACxC;AAEA,SAAK,IAAI,QAAQ,QAAQ,0BAA0B;KACjD,WAAW;KACX,YAAY,KAAK,IAAI;KACrB,cAAc,KAAK,IAAI;KACxB,CAAC;AACF,SAAK,IAAI,MAAM,mBAAmB;IAClC,MAAM,QAAQ,MAAM,KAAK,cAAc;AACvC,UAAM,KAAK,IAAI,SAAS,QAAQ,KAAK,IAAI,YAAY;KACnD;KACA,UAAU,KAAK,KAAK;KACrB,CAAC;AACF,WAAO;;GAGT,MAAM,KAAK,KAAK,qBAAqB,OAAO;AAC5C,OAAI,KAAK,IAAI,UAAW,MAAK,IAAI,UAAU,GAAG;AAC9C,QAAK,IAAI,QAAQ,QAAQ,eAAe;IACtC,WAAW;IACX,YAAY,KAAK,IAAI;IACrB,cAAc,KAAK,IAAI;IACxB,CAAC;AACF,QAAK,IAAI,MAAM,iBAAiB,OAAO;AACvC,UAAO,OAAO;;AAIhB,OAAK,IAAI,QAAQ,QAAQ,mBAAmB;GAC1C,WAAW;GACX,YAAY,KAAK,IAAI;GACrB,cAAc,KAAK,IAAI;GACxB,CAAC;AACF,OAAK,IAAI,MAAM,mBAAmB;EAClC,MAAM,QAAQ,MAAM,KAAK,cAAc;EACvC,MAAM,WAAW,KAAK,KAAK;EAC3B,MAAM,OAAO,KAAK,IAAI,SAAS,QAAQ,KAAK,IAAI,YAAY;GAC1D;GACA;GACD,CAAC;AACF,MAAI,KAAK,IAAI,UACX,MAAK,IAAI,UAAU,KAAK;MAExB,OAAM;AAER,SAAO;;CAGT,MAAc,qBACZ,MACA,QACe;AACf,MAAI;GACF,MAAM,OAAO,MAAM,KAAK,SAAS,KAAK;AACtC,OAAI,CAAC,KAAM;AAEX,OADW,KAAK,IAAI,OAAO,gBAAgB,KACrC,KAAK,OAAO,iBAAiB;IACjC,MAAM,OAAO,MAAM,KAAK,YAAY,MAAM,KAAK;AAC/C,UAAM,KAAK,uBAAuB,KAAK;AACvC,SAAK,IAAI,QAAQ,QAAQ,sBAAsB;KAC7C,WAAW;KACX;KACA,YAAY,KAAK,IAAI;KACrB,iBAAiB,OAAO;KACzB,CAAC;AACF,SAAK,IAAI,MAAM,qBAAqB,MAAM,KAAK;AAC/C,UAAM,KAAK,iBAAiB,MAAM,KAAK;cAC9B,KAAK,IAAI,UAAU,KAAA,GAAW;AAEvC,UAAM,KAAK,IAAI,SAAS,QAAQ,KAAK,IAAI,YAAY,MAAM;KACzD,GAAG;KACH,UAAU,KAAK,KAAK;KACrB,CAAC;AACF,SAAK,IAAI,QAAQ,QAAQ,uBAAuB;KAC9C,WAAW;KACX;KACA,YAAY,KAAK,IAAI;KACtB,CAAC;;WAEG,KAAK;GACZ,MAAM,SAAS,WAAW,IAAI,GAC1B,MACA,IAAI,SAAS;IACX,MAAM;IACN,SAAS;IACT,OAAO;IACP,SAAS;KACP,WAAW;KACX,YAAY,KAAK,IAAI;KACrB;KACD;IACF,CAAC;AACN,QAAK,IAAI,MAAM,aAAa,QAAQ;IAAE,OAAO;IAAa;IAAM,CAAC;AACjE,QAAK,IAAI,QAAQ,OACf,+BACA;IACE;IACA,YAAY,KAAK,IAAI;IACrB,MAAM,OAAO;IACb,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;IACxD,CACF;;;CAIL,MAAc,qBAAqB,QAA0C;AAC3E,MAAI;GACF,MAAM,QAAQ,MAAM,KAAK,cAAc;AACvC,OACE,KAAK,IAAI,OAAO,eAAe,MAAM,KACrC,KAAK,IAAI,OAAO,eAAe,OAAO,MAAM,EAC5C;IACA,MAAM,YAAY;KAAE;KAAO,UAAU,KAAK,KAAK;KAAE;AACjD,UAAM,KAAK,IAAI,SAAS,QAAQ,KAAK,IAAI,YAAY,UAAU;AAC/D,SAAK,IAAI,QAAQ,QACf,4BACA;KACE,WAAW;KACX,YAAY,KAAK,IAAI;KACtB,CACF;AACD,SAAK,IAAI,MAAM,yBAAyB,UAAU;cACzC,KAAK,IAAI,UAAU,KAAA,GAAW;AACvC,UAAM,KAAK,IAAI,SAAS,QAAQ,KAAK,IAAI,YAAY;KACnD,GAAG;KACH,UAAU,KAAK,KAAK;KACrB,CAAC;AACF,SAAK,IAAI,QAAQ,QAAQ,0BAA0B;KACjD,WAAW;KACX,YAAY,KAAK,IAAI;KACtB,CAAC;;WAEG,KAAK;GACZ,MAAM,SAAS,WAAW,IAAI,GAC1B,MACA,IAAI,SAAS;IACX,MAAM;IACN,SAAS;IACT,OAAO;IACP,SAAS;KACP,WAAW;KACX,YAAY,KAAK,IAAI;KACtB;IACF,CAAC;AACN,QAAK,IAAI,MAAM,aAAa,QAAQ,EAAE,OAAO,QAAQ,CAAC;AACtD,QAAK,IAAI,QAAQ,OACf,8BACA;IACE,YAAY,KAAK,IAAI;IACrB,MAAM,OAAO;IACb,OAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI;IACxD,CACF;;;CAIL,MAAc,eAA6B;AAgBzC,UAAO,MAfa,gBAEhB,KAAK,IAAI,OAAO,KAAK,EACnB,mBACE,KAAK,IAAI,kBAAkB,SAAS,IAChC,KAAK,IAAI,oBACT,KAAA,GACP,CAAC,EACJ;GACE,GAAG,KAAK,IAAI;GACZ,UAAU,SAAS,WAAW;AAC5B,SAAK,IAAI,QAAQ,OAAO,gBAAgB;KAAE;KAAS;KAAQ,CAAC;;GAE/D,CACF,EACY,QAAQ,SAAS;AAC5B,OAAI,KAAK,cAAc,KAAK,UAAW,QAAO;AAC9C,OACE,KAAK,IAAI,mBAAmB,SAAS,MACpC,CAAC,KAAK,UAAU,CAAC,KAAK,IAAI,mBAAmB,SAAS,KAAK,OAAO,EAEnE,QAAO;AACT,UAAO;IACP;;CAGJ,MAAc,SAAS,MAAiC;EACtD,MAAM,YAAY;GAChB,GAAG,KAAK,IAAI;GACZ,UAAU,SAAiB,WAAmB;AAC5C,SAAK,IAAI,QAAQ,OAAO,gBAAgB;KACtC;KACA;KACA;KACD,CAAC;;GAEL;EAGD,MAAM,iBACJ,KAAK,IAAI,OAAO,aAAa,KAAK,IAAI,YAAY;EAEpD,IAAI;EACJ,MAAM,aAAa,KAAK,IAAI,OAAO,YAAY,KAAK,KAAK,IAAI,OAAO;AACpE,MAAI,kBAAkB,WACpB,QAAO,MAAM,gBAAgB,WAAW,gBAAgB,KAAK,EAAE,UAAU;MAIzE,SAAO,MADW,gBAAgB,KAAK,IAAI,OAAO,MAAM,EAAE,UAAU,EACzD,MAAM,MAAM,EAAE,SAAS,KAAK,IAAI;AAG7C,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,KAAK,cAAc,KAAK,UAAW,QAAO;AAC9C,MACE,KAAK,IAAI,mBAAmB,SAAS,MACpC,CAAC,KAAK,UAAU,CAAC,KAAK,IAAI,mBAAmB,SAAS,KAAK,OAAO,EAEnE,QAAO;AAET,SAAO;;;AAIX,SAAS,aACP,MACA,OACS;AACT,MAAK,MAAM,OAAO,OAAO,KAAK,MAAM,EAA0B;EAC5D,MAAM,WAAW,MAAM;EACvB,MAAM,SAAS,KAAK;AACpB,MAAI,MAAM,QAAQ,SAAS;OACrB,CAAE,SAAgC,SAAS,OAAO,CAAE,QAAO;aAE3D,WAAW,SAAU,QAAO;;AAGpC,QAAO;;AAGT,SAAS,iBACP,OACA,MACK;AACL,KAAI,CAAC,KAAM,QAAO,sBAAsB,MAAM;CAC9C,IAAI,SAAS;AAEb,KAAI,KAAK,UAAU;EACjB,MAAM,QAAQ,IAAI,IAChB,MAAM,QAAQ,KAAK,SAAS,GAAG,KAAK,WAAW,CAAC,KAAK,SAAS,CAC/D;AACD,WAAS,OAAO,QAAQ,OAAO,GAAG,UAAU,QAAQ,MAAM,IAAI,GAAG,OAAO,CAAC;;AAG3E,KAAI,KAAK,KAAK;EACZ,MAAM,MAAM,KAAK;AACjB,WAAS,OAAO,QAAQ,OAAO;GAC7B,MAAM,OAAQ,GAA2B;AACzC,UAAO,MAAM,QAAQ,KAAK,IAAI,KAAK,SAAS,IAAI;IAChD;;AAGJ,KAAI,KAAK,OAAO;EACd,MAAM,QAAQ,KAAK;AACnB,WAAS,OAAO,QAAQ,OAAO,aAAa,IAAI,MAAM,CAAC;;AAGzD,KAAI,KAAK,OACP,UAAS,OAAO,OAAO,KAAK,OAAO;AAGrC,KAAI,KAAK,KACP,UAAS,CAAC,GAAG,OAAO,CAAC,KAAK,eAAe,KAAK,KAAK,CAAC;KAEpD,UAAS,sBAAsB,OAAO;CAGxC,MAAM,OAAO,KAAK,QAAQ;CAC1B,MAAM,QAAQ,KAAK;AACnB,KAAI,OAAO,KAAK,UAAU,KAAA,EACxB,UAAS,OAAO,MAAM,MAAM,UAAU,KAAA,IAAY,OAAO,QAAQ,KAAA,EAAU;AAG7E,QAAO;;;AAIT,SAAS,sBAAiD,OAAiB;AACzE,QAAO,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM;EAE/B,MAAM,KAAK,EAAE,eAAe,EAAE;EAC9B,MAAM,KAAK,EAAE,eAAe,EAAE;AAC9B,MAAI,OAAO,GAAI,QAAO;AACtB,SAAO,KAAK,KAAK,KAAK;GACtB;;AAGJ,SAAS,eACP,MACwB;AACxB,KAAI,KAAK,QAAS,QAAO,KAAK;CAC9B,MAAM,KAAK,KAAK;CAChB,MAAM,MAAM,KAAK,QAAQ,QAAQ,IAAI;AACrC,SAAQ,GAAG,MAAM;EACf,MAAM,KAAK,EAAE;EACb,MAAM,KAAK,EAAE;AACb,MAAI,OAAO,GAAI,QAAO;AACtB,MAAI,OAAO,KAAA,KAAa,OAAO,KAAM,QAAO;AAC5C,MAAI,OAAO,KAAA,KAAa,OAAO,KAAM,QAAO;AAC5C,MAAI,OAAO,OAAO,YAAY,OAAO,OAAO,SAC1C,QAAO,KAAK,KAAK,MAAM,CAAC;AAE1B,MAAI,OAAO,OAAO,YAAY,OAAO,OAAO,SAC1C,QAAO,KAAK,KAAK,MAAM,CAAC;AAE1B,QAAM,IAAI,SAAS;GACjB,MAAM;GACN,SAAS,IAAI,OAAO,GAAG,CAAC,aAAa,OAAO,GAAG;GAC/C,SAAS;IACP,WAAW;IACX,OAAO,OAAO,GAAG;IACjB,MAAM,OAAO;IACd;GACF,CAAC;;;;;ACznBN,MAAM,eAAe;CACnB,UAAU;CACV,YAAY;CACZ,gBAAgB;CACjB;;;;;AAMD,SAAS,mBAAmB,MAA6B;AACvD,KAAI,SAAS,4BAA6B,QAAO;AACjD,KAAI,SAAS,0BAA2B,QAAO;AAC/C,KAAI,SAAS,6BAA8B,QAAO;AAClD,KAAI,SAAS,0BAA2B,QAAO;AAC/C,QAAO;;;;;;;;;;AAWT,SAAgB,cACd,SACA,OAAuB,EAAE,EACY;CACrC,MAAM,WAAW,kBAAkB,KAAK,YAAY,aAAa,SAAS;CAC1E,MAAM,aAAa,KAAK,cAAc,aAAa;CACnD,MAAM,iBAAiB,KAAK,kBAAkB,aAAa;AAE3D,QAAO,OAAO,QAAoC;EAEhD,MAAM,OAAO,IADG,IAAI,IAAI,IACR,CAAC;AAEjB,MAAI,CAAC,KAAK,WAAW,SAAS,CAC5B,QAAO,IAAI,SAAS,aAAa,EAAE,QAAQ,KAAK,CAAC;EAEnD,MAAM,MAAM,KAAK,MAAM,SAAS,OAAO,IAAI;AAG3C,MAAI,IAAI,WAAW,SAAS,IAAI,WAAW,GAAG,WAAW,GAAG,EAAE;GAC5D,MAAM,OAAO,IAAI,MAAM,WAAW,SAAS,EAAE;AAC7C,OAAI,CAAC,KAAM,QAAO,IAAI,SAAS,eAAe,EAAE,QAAQ,KAAK,CAAC;GAC9D,MAAM,SAAS,MAAM,QAAQ,WAAW,IAAI,KAAK;AACjD,OAAI,CAAC,OAAQ,QAAO,IAAI,SAAS,aAAa,EAAE,QAAQ,KAAK,CAAC;GAC9D,MAAM,UAAU,IAAI,SAAS;AAC7B,OAAI,OAAO,YAAa,SAAQ,IAAI,gBAAgB,OAAO,YAAY;AACvE,WAAQ,IAAI,iBAAiB,sCAAsC;AACnE,UAAO,IAAI,SAAS,OAAO,MAAM,EAAE,SAAS,CAAC;;AAI/C,MAAI,IAAI,WAAW,UAAU,IAAI,WAAW,GAAG,eAAe,GAAG,EAAE;GACjE,MAAM,aAAa,IAAI,MAAM,eAAe,SAAS,EAAE;AACvD,OAAI,CAAC,cAAc,WAAW,SAAS,IAAI,CACzC,QAAO,IAAI,SACT,KAAK,UAAU;IAAE,IAAI;IAAO,QAAQ;IAAuB,CAAC,EAC5D;IAAE,QAAQ;IAAK,SAAS,EAAE,gBAAgB,oBAAoB;IAAE,CACjE;AAEH,OAAI;IACF,MAAM,QAAQ,MAAM,QAAQ,gBAC1B,YACA,KACA,KAAK,cACN;AACD,UAAM,QAAQ,WAAW,MAAM;AAC/B,WAAO,IAAI,SAAS,KAAK,UAAU;KAAE,IAAI;KAAM;KAAO,CAAC,EAAE;KACvD,QAAQ;KACR,SAAS,EAAE,gBAAgB,oBAAoB;KAChD,CAAC;YACK,KAAK;AACZ,QAAI,WAAW,IAAI,EAAE;KACnB,MAAM,SAAS,mBAAmB,IAAI,KAAK;AAC3C,SAAI,WAAW,KACb,QAAO,IAAI,SAAS,KAAK,UAAU;MAAE,IAAI;MAAO,MAAM,IAAI;MAAM,CAAC,EAAE;MACjE;MACA,SAAS,EAAE,gBAAgB,oBAAoB;MAChD,CAAC;;AAGN,UAAM;;;AAIV,SAAO,IAAI,SAAS,aAAa,EAAE,QAAQ,KAAK,CAAC;;;AAIrD,SAAS,kBAAkB,GAAmB;AAC5C,QAAO,EAAE,SAAS,IAAI,GAAG,EAAE,MAAM,GAAG,GAAG,GAAG;;;;ACrG5C,MAAM,2BAA2B;;;;;;;AAiCjC,SAAS,aACP,OACe;CACf,MAAM,WAAW,SAAS,EAAE;CAE5B,IAAI,MAAwB;CAC5B,IAAI,UAAU;CACd,IAAI,MAAqB;CACzB,IAAI,UAAU;CACd,IAAI,WAAW;CACf,IAAI,WAAW;AAEf,MAAK,MAAM,WAAW,UAAU;AAC9B,MAAI,CAAC,YAAY,QAAQ,QAAQ,SAAS,WAAW,IAAI,QAAQ,KAAK;AACpE,SAAM,QAAQ;AACd,aAAU,QAAQ;AAClB,cAAW;;AAEb,MAAI,CAAC,YAAY,QAAQ,QAAQ,SAAS,QAAQ,IAAI,QAAQ,KAAK;AACjE,SAAM,QAAQ;AACd,aAAU,QAAQ;AAClB,cAAW;;;AAIf,QAAO;EAAE;EAAK;EAAS;EAAK;EAAS,QAAQ;EAAU;;AAGzD,MAAM,kBAA4C;CAChD,OAAO;CACP,MAAM;CACN,MAAM;CACN,OAAO;CACR;;AAGD,SAAS,cACP,QACA,UACoB;AACpB,KAAI,CAAC,OAAQ,QAAO,KAAA;CACpB,MAAM,WAAW,gBAAgB;CACjC,MAAM,WAAmB,EAAE;AAC3B,MAAK,MAAM,SAAS;EAAC;EAAS;EAAQ;EAAQ;EAAQ,CACpD,KAAI,gBAAgB,UAAU,SAC5B,UAAS,SAAS,OAAO;AAG7B,QAAO;;;;;;;;;;;;;;;;;;;;;;AAuBT,SAAgB,UACd,MACc;AACd,KAAI,CAAC,KAAK,eAAe,OAAO,KAAK,KAAK,YAAY,CAAC,WAAW,EAChE,OAAM,IAAI,SAAS;EACjB,MAAM;EACN,SACE;EACF,SAAS,EAAE,WAAW,aAAa;EACpC,CAAC;AAGJ,MAAK,MAAM,CAAC,MAAM,QAAQ,OAAO,QAAQ,KAAK,YAAY,EAAE;AAC1D,MAAI,CAAC,IAAI,OACP,OAAM,IAAI,SAAS;GACjB,MAAM;GACN,SAAS,sBAAsB,KAAK;GACpC,SAAS;IAAE,WAAW;IAAa,YAAY;IAAM;GACtD,CAAC;AAEJ,MAAI,CAAC,IAAI,UACP,OAAM,IAAI,SAAS;GACjB,MAAM;GACN,SAAS,sBAAsB,KAAK;GACpC,SAAS;IAAE,WAAW;IAAa,YAAY;IAAM;GACtD,CAAC;;CAIN,MAAM,WAAW,aAAa,KAAK,MAAM;CACzC,MAAM,QAAQ,KAAK,KAAK;CACxB,MAAM,iBAAiB,KAAK,kBAAkB;CAC9C,MAAM,gBAAgB,KAAK;CAC3B,MAAM,aAAqC,KAAK;CAChD,MAAM,YAAY,KAAK;CACvB,MAAM,aAAiC,aACrC,KAAK,WAAW,EAAE,EAClB,KAAK,OACN;CACD,MAAM,SAAS,KAAK,WAChB,cAAc,YAAY,KAAK,SAAS,GACxC;CACJ,MAAM,QAAmC,WACvC,KAAK,WAAW,EAAE,EAClB,KAAK,OACL,OACD;CACD,MAAM,gBAAgB,KAAK,aAAa,iBAAiB;CACzD,MAAM,cAA2B;EAC/B,GAAG;EACH,GAAI,KAAK,eAAe,EAAE;EAC3B;CAED,MAAM,kBAAwC,EAAE;CAChD,MAAM,cAAiE,EAAE;AACzE,MAAK,MAAM,CAAC,MAAM,QAAQ,OAAO,QAAQ,KAAK,YAAY,EAAE;AAC1D,kBAAgB,KAAK,KAAyB;EAC9C,MAAM,SAAS,IAAI;EACnB,MAAM,WAAW,IAAI;EACrB,MAAM,kBAA6C,WAC/C,WAAW,CAAC;GAAE,MAAM,GAAG,KAAK;GAAU;GAAO,CAAC,EAAE,UAAU,OAAO,GACjE;EACJ,MAAM,YAA4C;GAChD;GACA;GACA,UAAU,SAAS;GACnB,cAAc,SAAS;GACvB,eAAe,SAAS;GACxB;GACA;GACA,OAAO;GACP;GACD;AAqBD,cAAY,QAAQ,IAAI,qBAAqB;GAnB3C,YAAY;GACZ;GACA,UAAU,SAAS;GACnB,cAAc,SAAS;GACvB,QAAQ;GACR,OAAO;GACP;GACA;GACA,mBAAmB,IAAI,oBACnB,CAAC,GAAG,IAAI,kBAAkB,GAC1B,EAAE;GACN,oBAAoB,IAAI,qBACpB,CAAC,GAAG,IAAI,mBAAmB,GAC3B,EAAE;GACN;GACA;GACA;GACA,WAAW,IAAI;GAE+B,CAAC;;CAGnD,MAAM,YAA0B;EAC9B,aAAa;EACb,MAAM,WAAW,OAAwC;AACvD,WAAQ,QAAQ,kBAAkB;IAChC,WAAW;IACX,cAAc,SAAS;IACxB,CAAC;AACF,SAAM,SAAS,IAAI,WAAW,SAAS,MAAM;;EAE/C,QAAQ,aAA8B;AACpC,UAAO,cACL;IACE,YAAY,SAAS;IACrB,MAAM,gBAAgB,YAAY,KAAK,eAAe;KACpD,MAAM,MAAM,KAAK,YAAY;AAC7B,SAAI,CAAC,IACH,OAAM,IAAI,SAAS;MACjB,MAAM;MACN,SAAS,uBAAuB;MAChC,SAAS;OAAE,WAAW;OAAmB;OAAY;MACtD,CAAC;KAEJ,MAAM,KAAK,IAAI;AACf,SAAI,CAAC,GAAG,aACN,OAAM,IAAI,SAAS;MACjB,MAAM;MACN,SAAS,eAAe,WAAW;MACnC,SAAS;OAAE,WAAW;OAAmB;OAAY;MACtD,CAAC;AAEJ,YAAO,GAAG,aAAa,KAAK,EAAE,QAAQ,eAAe,CAAC;;IAExD,aAAa,UAAU,UAAU,WAAW,MAAM;IACnD,EACD,YACD;;EAEH,eAAe,MAAM;AACnB,UAAO,SAAS,IAAI,IAAI,KAAK;;EAEhC;AAED,QAAO,OAAO,OACZ,OAAO,OAAO,KAAK,EACnB,aACA,UACD;;;;ACtQH,SAAgB,aACd,QACc;AACd,QAAO"}
|
|
@@ -138,9 +138,9 @@ interface MemoryCacheOptions extends MemoryDocumentOptions, MemoryImageOptions {
|
|
|
138
138
|
* プロセス再起動でクリアされるため、ローカル開発・SSG ビルド・テスト用途。
|
|
139
139
|
*
|
|
140
140
|
* @example
|
|
141
|
-
* cache: memoryCache({
|
|
141
|
+
* cache: [memoryCache({ maxItems: 1000 })]
|
|
142
142
|
*/
|
|
143
143
|
declare function memoryCache(options?: MemoryCacheOptions): CacheAdapter;
|
|
144
144
|
//#endregion
|
|
145
145
|
export { CacheAdapter as a, DataSource as c, PropertyDef as d, PropertyMap as f, memoryCache as i, InvalidateKind as l, MemoryDocumentOptions as n, DocumentCacheOps as o, WebhookConfig as p, MemoryImageOptions as r, ImageCacheOps as s, MemoryCacheOptions as t, InvalidateScope as u };
|
|
146
|
-
//# sourceMappingURL=memory-
|
|
146
|
+
//# sourceMappingURL=memory-BKDsuGVN.d.mts.map
|