@notion-headless-cms/core 0.3.5 → 0.3.7

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/errors.d.mts CHANGED
@@ -1,4 +1,28 @@
1
1
  //#region src/errors.d.ts
2
+ /**
3
+ * ライブラリ組み込みの CMS エラーコード。
4
+ *
5
+ * | コード | 発生条件 |
6
+ * |---|---|
7
+ * | `core/config_invalid` | 設定不備(token 未設定など) |
8
+ * | `core/schema_invalid` | schema/mapping の型不整合 |
9
+ * | `core/notion_orm_missing` | `@notion-headless-cms/notion-orm` の動的ロード失敗 |
10
+ * | `source/fetch_items_failed` | `DataSource.list()` 失敗 |
11
+ * | `source/fetch_item_failed` | `DataSource.findByProp()` 失敗 |
12
+ * | `source/load_markdown_failed` | `DataSource.loadMarkdown()` 失敗 |
13
+ * | `cache/io_failed` | document / image キャッシュの I/O 失敗 |
14
+ * | `cache/image_fetch_failed` | Notion 画像の HTTP 取得失敗 |
15
+ * | `renderer/failed` | Markdown → HTML 変換失敗 |
16
+ * | `cli/config_invalid` | `nhc.config.ts` の内容不整合 |
17
+ * | `cli/config_load_failed` | 設定ファイルの読み込み / 評価失敗 |
18
+ * | `cli/schema_invalid` | CLI が受け取ったスキーマ / マッピング不整合 |
19
+ * | `cli/generate_failed` | `nhc generate` の処理失敗 |
20
+ * | `cli/init_failed` | `nhc init` の処理失敗 |
21
+ * | `cli/notion_api_failed` | CLI が Notion API を呼び出す際の失敗 |
22
+ * | `cli/env_file_not_found` | `--env-file` で指定したファイルが存在しない |
23
+ *
24
+ * サードパーティアダプタが独自コードを追加したい場合は `CMSErrorCode` を参照。
25
+ */
2
26
  type BuiltInCMSErrorCode = "core/config_invalid" | "core/schema_invalid" | "core/notion_orm_missing" | "source/fetch_items_failed" | "source/fetch_item_failed" | "source/load_markdown_failed" | "cache/io_failed" | "cache/image_fetch_failed" | "renderer/failed" | "cli/config_invalid" | "cli/config_load_failed" | "cli/schema_invalid" | "cli/generate_failed" | "cli/init_failed" | "cli/notion_api_failed" | "cli/env_file_not_found";
3
27
  /**
4
28
  * CMS エラーコード。
@@ -28,5 +52,5 @@ declare function isCMSError(error: unknown): error is CMSError;
28
52
  /** エラーコードが特定の名前空間に属するかを判定する(例: "source/")。 */
29
53
  declare function isCMSErrorInNamespace(error: unknown, namespace: string): error is CMSError;
30
54
  //#endregion
31
- export { CMSError, CMSErrorCode, CMSErrorContext, isCMSError, isCMSErrorInNamespace };
55
+ export { BuiltInCMSErrorCode, CMSError, CMSErrorCode, CMSErrorContext, isCMSError, isCMSErrorInNamespace };
32
56
  //# sourceMappingURL=errors.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["type BuiltInCMSErrorCode =\n\t| \"core/config_invalid\"\n\t| \"core/schema_invalid\"\n\t| \"core/notion_orm_missing\"\n\t| \"source/fetch_items_failed\"\n\t| \"source/fetch_item_failed\"\n\t| \"source/load_markdown_failed\"\n\t| \"cache/io_failed\"\n\t| \"cache/image_fetch_failed\"\n\t| \"renderer/failed\"\n\t| \"cli/config_invalid\"\n\t| \"cli/config_load_failed\"\n\t| \"cli/schema_invalid\"\n\t| \"cli/generate_failed\"\n\t| \"cli/init_failed\"\n\t| \"cli/notion_api_failed\"\n\t| \"cli/env_file_not_found\";\n\n/**\n * CMS エラーコード。\n * `BuiltInCMSErrorCode` のリテラル補完を維持しつつ、\n * サードパーティアダプタが独自コードを定義できるよう `string & {}` で拡張可能にする。\n */\nexport type CMSErrorCode = BuiltInCMSErrorCode | (string & {});\n\nexport interface CMSErrorContext {\n\toperation: string;\n\tslug?: string;\n\tdataSourceId?: string;\n\tpageId?: string;\n\t[key: string]: string | number | boolean | null | undefined;\n}\n\nexport class CMSError extends Error {\n\treadonly code: CMSErrorCode;\n\toverride readonly cause?: unknown;\n\treadonly context: CMSErrorContext;\n\n\tconstructor(params: {\n\t\tcode: CMSErrorCode;\n\t\tmessage: string;\n\t\tcause?: unknown;\n\t\tcontext: CMSErrorContext;\n\t}) {\n\t\tsuper(params.message, { cause: params.cause });\n\t\tthis.name = \"CMSError\";\n\t\tthis.code = params.code;\n\t\tthis.cause = params.cause;\n\t\tthis.context = params.context;\n\t}\n}\n\nexport function isCMSError(error: unknown): error is CMSError {\n\treturn error instanceof CMSError;\n}\n\n/** エラーコードが特定の名前空間に属するかを判定する(例: \"source/\")。 */\nexport function isCMSErrorInNamespace(\n\terror: unknown,\n\tnamespace: string,\n): error is CMSError {\n\treturn isCMSError(error) && error.code.startsWith(namespace);\n}\n"],"mappings":";AAiCA,IAAa,WAAb,cAA8B,MAAM;CACnC;CACA;CACA;CAEA,YAAY,QAKT;AACF,QAAM,OAAO,SAAS,EAAE,OAAO,OAAO,OAAO,CAAC;AAC9C,OAAK,OAAO;AACZ,OAAK,OAAO,OAAO;AACnB,OAAK,QAAQ,OAAO;AACpB,OAAK,UAAU,OAAO;;;AAIxB,SAAgB,WAAW,OAAmC;AAC7D,QAAO,iBAAiB;;;AAIzB,SAAgB,sBACf,OACA,WACoB;AACpB,QAAO,WAAW,MAAM,IAAI,MAAM,KAAK,WAAW,UAAU"}
1
+ {"version":3,"file":"errors.mjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * ライブラリ組み込みの CMS エラーコード。\n *\n * | コード | 発生条件 |\n * |---|---|\n * | `core/config_invalid` | 設定不備(token 未設定など) |\n * | `core/schema_invalid` | schema/mapping の型不整合 |\n * | `core/notion_orm_missing` | `@notion-headless-cms/notion-orm` の動的ロード失敗 |\n * | `source/fetch_items_failed` | `DataSource.list()` 失敗 |\n * | `source/fetch_item_failed` | `DataSource.findByProp()` 失敗 |\n * | `source/load_markdown_failed` | `DataSource.loadMarkdown()` 失敗 |\n * | `cache/io_failed` | document / image キャッシュの I/O 失敗 |\n * | `cache/image_fetch_failed` | Notion 画像の HTTP 取得失敗 |\n * | `renderer/failed` | Markdown → HTML 変換失敗 |\n * | `cli/config_invalid` | `nhc.config.ts` の内容不整合 |\n * | `cli/config_load_failed` | 設定ファイルの読み込み / 評価失敗 |\n * | `cli/schema_invalid` | CLI が受け取ったスキーマ / マッピング不整合 |\n * | `cli/generate_failed` | `nhc generate` の処理失敗 |\n * | `cli/init_failed` | `nhc init` の処理失敗 |\n * | `cli/notion_api_failed` | CLI が Notion API を呼び出す際の失敗 |\n * | `cli/env_file_not_found` | `--env-file` で指定したファイルが存在しない |\n *\n * サードパーティアダプタが独自コードを追加したい場合は `CMSErrorCode` を参照。\n */\nexport type BuiltInCMSErrorCode =\n\t| \"core/config_invalid\"\n\t| \"core/schema_invalid\"\n\t| \"core/notion_orm_missing\"\n\t| \"source/fetch_items_failed\"\n\t| \"source/fetch_item_failed\"\n\t| \"source/load_markdown_failed\"\n\t| \"cache/io_failed\"\n\t| \"cache/image_fetch_failed\"\n\t| \"renderer/failed\"\n\t| \"cli/config_invalid\"\n\t| \"cli/config_load_failed\"\n\t| \"cli/schema_invalid\"\n\t| \"cli/generate_failed\"\n\t| \"cli/init_failed\"\n\t| \"cli/notion_api_failed\"\n\t| \"cli/env_file_not_found\";\n\n/**\n * CMS エラーコード。\n * `BuiltInCMSErrorCode` のリテラル補完を維持しつつ、\n * サードパーティアダプタが独自コードを定義できるよう `string & {}` で拡張可能にする。\n */\nexport type CMSErrorCode = BuiltInCMSErrorCode | (string & {});\n\nexport interface CMSErrorContext {\n\toperation: string;\n\tslug?: string;\n\tdataSourceId?: string;\n\tpageId?: string;\n\t[key: string]: string | number | boolean | null | undefined;\n}\n\nexport class CMSError extends Error {\n\treadonly code: CMSErrorCode;\n\toverride readonly cause?: unknown;\n\treadonly context: CMSErrorContext;\n\n\tconstructor(params: {\n\t\tcode: CMSErrorCode;\n\t\tmessage: string;\n\t\tcause?: unknown;\n\t\tcontext: CMSErrorContext;\n\t}) {\n\t\tsuper(params.message, { cause: params.cause });\n\t\tthis.name = \"CMSError\";\n\t\tthis.code = params.code;\n\t\tthis.cause = params.cause;\n\t\tthis.context = params.context;\n\t}\n}\n\nexport function isCMSError(error: unknown): error is CMSError {\n\treturn error instanceof CMSError;\n}\n\n/** エラーコードが特定の名前空間に属するかを判定する(例: \"source/\")。 */\nexport function isCMSErrorInNamespace(\n\terror: unknown,\n\tnamespace: string,\n): error is CMSError {\n\treturn isCMSError(error) && error.code.startsWith(namespace);\n}\n"],"mappings":";AAyDA,IAAa,WAAb,cAA8B,MAAM;CACnC;CACA;CACA;CAEA,YAAY,QAKT;AACF,QAAM,OAAO,SAAS,EAAE,OAAO,OAAO,OAAO,CAAC;AAC9C,OAAK,OAAO;AACZ,OAAK,OAAO,OAAO;AACnB,OAAK,QAAQ,OAAO;AACpB,OAAK,UAAU,OAAO;;;AAIxB,SAAgB,WAAW,OAAmC;AAC7D,QAAO,iBAAiB;;;AAIzB,SAAgB,sBACf,OACA,WACoB;AACpB,QAAO,WAAW,MAAM,IAAI,MAAM,KAAK,WAAW,UAAU"}
package/dist/index.d.mts CHANGED
@@ -3,7 +3,7 @@ import { a as CollectionConfig, c as InferCollectionItem, d as PropertyMap, f as
3
3
  import { a as Logger, i as definePlugin, n as mergeLoggers, o as CMSHooks, r as CMSPlugin, s as MaybePromise, t as mergeHooks } from "./hooks-D8Lgf-Co.mjs";
4
4
  import { MemoryDocumentCacheOptions, MemoryImageCacheOptions, memoryDocumentCache, memoryImageCache } from "./cache/memory.mjs";
5
5
  import { noopDocumentCache, noopImageCache } from "./cache/noop.mjs";
6
- import { CMSError, CMSErrorCode, CMSErrorContext, isCMSError, isCMSErrorInNamespace } from "./errors.mjs";
6
+ import { BuiltInCMSErrorCode, CMSError, CMSErrorCode, CMSErrorContext, isCMSError, isCMSErrorInNamespace } from "./errors.mjs";
7
7
 
8
8
  //#region src/types/collection.d.ts
9
9
  /** 並び順指定。 */
@@ -36,15 +36,54 @@ interface AdjacencyOptions<T extends BaseContentItem = BaseContentItem> {
36
36
  type ItemWithContent<T extends BaseContentItem> = T & {
37
37
  content: ContentResult;
38
38
  };
39
+ /**
40
+ * `getList` の戻り値。アイテム配列とバージョン文字列を含む。
41
+ * version は DataSource.getListVersion() が生成するフィルタ済みアイテムの識別子。
42
+ */
43
+ interface GetListResult<T extends BaseContentItem = BaseContentItem> {
44
+ items: T[];
45
+ /** フィルタ適用後のアイテム群を識別するバージョン文字列。 */
46
+ version: string;
47
+ }
48
+ /**
49
+ * `checkForUpdate` の戻り値。
50
+ * changed: true の場合は最新の ItemWithContent を含む。
51
+ */
52
+ type CheckForUpdateResult<T extends BaseContentItem = BaseContentItem> = {
53
+ changed: false;
54
+ } | {
55
+ changed: true;
56
+ item: ItemWithContent<T>;
57
+ };
58
+ /**
59
+ * `checkListForUpdate` の戻り値。
60
+ * changed: true の場合は最新のアイテム配列とバージョンを含む。
61
+ */
62
+ type CheckListForUpdateResult<T extends BaseContentItem = BaseContentItem> = {
63
+ changed: false;
64
+ } | {
65
+ changed: true;
66
+ items: T[];
67
+ version: string;
68
+ };
39
69
  /**
40
70
  * コレクション別の CMS クライアント。
41
71
  * `cms.posts.getItem(slug)` のようにアクセスする。
42
72
  */
43
73
  interface CollectionClient<T extends BaseContentItem = BaseContentItem> {
44
- /** スラッグで単件取得 (本文込み)。キャッシュを経由 (SWR)。 */
74
+ /**
75
+ * スラッグで単件取得 (本文込み)。
76
+ *
77
+ * キャッシュヒット時はキャッシュを即時返却する (SWR)。
78
+ * TTL が切れている場合はブロッキングで Notion から再取得する。
79
+ * TTL 未設定の場合はキャッシュを即時返却しバックグラウンドで差分チェックする。
80
+ * 明示的に同期リフレッシュしたい場合は {@link revalidate} を先に呼ぶ。
81
+ *
82
+ * @returns キャッシュまたは Notion から取得したアイテム。存在しない場合は null。
83
+ */
45
84
  getItem(slug: string): Promise<ItemWithContent<T> | null>;
46
- /** 公開済みアイテム一覧 (本文なし、一覧ページ向け)。 */
47
- getList(opts?: GetListOptions<T>): Promise<T[]>;
85
+ /** 公開済みアイテム一覧 (本文なし、一覧ページ向け)。items とバージョン文字列を返す。 */
86
+ getList(opts?: GetListOptions<T>): Promise<GetListResult<T>>;
48
87
  /** Next App Router の `generateStaticParams` 向け。 */
49
88
  getStaticParams(): Promise<{
50
89
  slug: string;
@@ -56,10 +95,31 @@ interface CollectionClient<T extends BaseContentItem = BaseContentItem> {
56
95
  prev: T | null;
57
96
  next: T | null;
58
97
  }>;
59
- /** 指定スコープのキャッシュを無効化する。 */
60
- revalidate(scope?: "all" | {
98
+ /**
99
+ * 指定スラッグのアイテムキャッシュを無効化する。
100
+ * 次回の getItem 呼び出しで Notion から再取得される。
101
+ */
102
+ revalidate(slug: string): Promise<void>;
103
+ /** コレクション全体のキャッシュを無効化する。 */
104
+ revalidateAll(): Promise<void>;
105
+ /**
106
+ * 指定アイテムが since 以降に更新されたか確認する。
107
+ * 内部で revalidate → getItem を実行し updatedAt を比較する。
108
+ * 更新があった場合は最新の ItemWithContent を返す。
109
+ */
110
+ checkForUpdate(args: {
61
111
  slug: string;
62
- }): Promise<void>;
112
+ since: string;
113
+ }): Promise<CheckForUpdateResult<T>>;
114
+ /**
115
+ * リスト全体が since 以降に更新されたか確認する。
116
+ * 内部で revalidateAll → getList を実行しバージョンを比較する。
117
+ * 更新があった場合は最新の items と version を返す。
118
+ */
119
+ checkListForUpdate(args: {
120
+ since: string;
121
+ filter?: GetListOptions<T>;
122
+ }): Promise<CheckListForUpdateResult<T>>;
63
123
  /** 全コンテンツをプリフェッチしてキャッシュに保存。 */
64
124
  prefetch(opts?: {
65
125
  concurrency?: number;
@@ -330,7 +390,14 @@ interface RetryConfig {
330
390
  onRetry?: (attempt: number, status: number) => void;
331
391
  }
332
392
  declare const DEFAULT_RETRY_CONFIG: RetryConfig;
333
- /** 指数バックオフ(オプションでジッター付き)でリトライする。retryOn に含まれる HTTP エラーのみ対象。 */
393
+ /**
394
+ * 指数バックオフ(オプションでジッター付き)でリトライする。
395
+ *
396
+ * `config.retryOn` に含まれるステータスコードを持つエラーのみリトライ対象。
397
+ * 遅延は `baseDelayMs * 2^attempt` の指数バックオフ。
398
+ * `jitter` が `true`(デフォルト)の場合、0.5〜1.0 の乱数係数を乗算して
399
+ * Thundering Herd を防ぐ。`false` にすると確定的な遅延になる。
400
+ */
334
401
  declare function withRetry<T>(fn: () => Promise<T>, config: RetryConfig): Promise<T>;
335
402
  //#endregion
336
403
  //#region src/collection.d.ts
@@ -366,7 +433,7 @@ declare class CollectionClientImpl<T extends BaseContentItem> implements Collect
366
433
  private readonly ctx;
367
434
  constructor(ctx: CollectionContext<T>);
368
435
  getItem(slug: string): Promise<ItemWithContent<T> | null>;
369
- getList(opts?: GetListOptions<T>): Promise<T[]>;
436
+ getList(opts?: GetListOptions<T>): Promise<GetListResult<T>>;
370
437
  getStaticParams(): Promise<{
371
438
  slug: string;
372
439
  }[]>;
@@ -375,9 +442,22 @@ declare class CollectionClientImpl<T extends BaseContentItem> implements Collect
375
442
  prev: T | null;
376
443
  next: T | null;
377
444
  }>;
378
- revalidate(scope?: "all" | {
445
+ revalidate(slug: string): Promise<void>;
446
+ revalidateAll(): Promise<void>;
447
+ checkForUpdate({
448
+ slug,
449
+ since
450
+ }: {
379
451
  slug: string;
380
- }): Promise<void>;
452
+ since: string;
453
+ }): Promise<CheckForUpdateResult<T>>;
454
+ checkListForUpdate({
455
+ since,
456
+ filter
457
+ }: {
458
+ since: string;
459
+ filter?: GetListOptions<T>;
460
+ }): Promise<CheckListForUpdateResult<T>>;
381
461
  prefetch(opts?: {
382
462
  concurrency?: number;
383
463
  onProgress?: (done: number, total: number) => void;
@@ -423,5 +503,5 @@ interface NodePresetOptions {
423
503
  */
424
504
  declare function nodePreset(opts?: NodePresetOptions): Pick<CreateCMSOptions, "cache" | "renderer">;
425
505
  //#endregion
426
- export { type AdjacencyOptions, type BaseContentItem, type CMSClient, CMSError, type CMSErrorCode, type CMSErrorContext, type CMSGlobalOps, type CMSHooks, type CMSPlugin, type CMSSchema, type CMSSchemaProperties, type CacheConfig, type CachedItem, type CachedItemList, type CollectionClient, CollectionClientImpl, type CollectionConfig, type CollectionContext, type CollectionSemantics, type ContentBlock, type ContentConfig, type ContentResult, type CreateCMSOptions, DEFAULT_RETRY_CONFIG, type DataSource, type DataSourceFactory, type DataSourceMap, type DocumentCacheAdapter, type GetListOptions, type HandlerAdapter, type HandlerOptions, type ImageCacheAdapter, type ImageRef, type InferCollectionItem, type InferDataSourceItem, type InlineNode, type InvalidateScope, type ItemWithContent, type Logger, type MaybePromise, type MemoryDocumentCacheOptions, type MemoryImageCacheOptions, type NodePresetOptions, type PropertyDef, type PropertyMap, type RateLimiterConfig, type RenderOptions, type RendererFn, type RendererPluginList, type RetryConfig, type SortOption, type StorageBinary, type WebhookConfig, collectionKey, createCMS, createHandler, definePlugin, isCMSError, isCMSErrorInNamespace, isStale, memoryDocumentCache, memoryImageCache, mergeHooks, mergeLoggers, nodePreset, noopDocumentCache, noopImageCache, sha256Hex, withRetry };
506
+ export { type AdjacencyOptions, type BaseContentItem, type BuiltInCMSErrorCode, type CMSClient, CMSError, type CMSErrorCode, type CMSErrorContext, type CMSGlobalOps, type CMSHooks, type CMSPlugin, type CMSSchema, type CMSSchemaProperties, type CacheConfig, type CachedItem, type CachedItemList, type CheckForUpdateResult, type CheckListForUpdateResult, type CollectionClient, CollectionClientImpl, type CollectionConfig, type CollectionContext, type CollectionSemantics, type ContentBlock, type ContentConfig, type ContentResult, type CreateCMSOptions, DEFAULT_RETRY_CONFIG, type DataSource, type DataSourceFactory, type DataSourceMap, type DocumentCacheAdapter, type GetListOptions, type GetListResult, type HandlerAdapter, type HandlerOptions, type ImageCacheAdapter, type ImageRef, type InferCollectionItem, type InferDataSourceItem, type InlineNode, type InvalidateScope, type ItemWithContent, type Logger, type MaybePromise, type MemoryDocumentCacheOptions, type MemoryImageCacheOptions, type NodePresetOptions, type PropertyDef, type PropertyMap, type RateLimiterConfig, type RenderOptions, type RendererFn, type RendererPluginList, type RetryConfig, type SortOption, type StorageBinary, type WebhookConfig, collectionKey, createCMS, createHandler, definePlugin, isCMSError, isCMSErrorInNamespace, isStale, memoryDocumentCache, memoryImageCache, mergeHooks, mergeLoggers, nodePreset, noopDocumentCache, noopImageCache, sha256Hex, withRetry };
427
507
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -80,7 +80,14 @@ async function fetchAndCacheImage(cache, notionUrl, imageProxyBase, logger) {
80
80
  }
81
81
  return proxyUrl;
82
82
  }
83
- /** ImageCacheAdapter と imageProxyBase から cacheImage 関数を構築するファクトリ。 */
83
+ /**
84
+ * ImageCacheAdapter と imageProxyBase から cacheImage 関数を構築するファクトリ。
85
+ *
86
+ * 返り値の関数は Notion 画像 URL を受け取り、SHA-256 ハッシュをキャッシュキーとして
87
+ * {@link ImageCacheAdapter} に保存後、フロントエンドへの配信用プロキシ URL を返す。
88
+ * Content-Type はレスポンスヘッダまたは URL の拡張子から推測する。
89
+ * タイムアウトは 10 秒固定。
90
+ */
84
91
  function buildCacheImageFn(cache, imageProxyBase, logger) {
85
92
  return (notionUrl) => fetchAndCacheImage(cache, notionUrl, imageProxyBase, logger);
86
93
  }
@@ -193,7 +200,14 @@ const DEFAULT_RETRY_CONFIG = {
193
200
  baseDelayMs: 1e3,
194
201
  jitter: true
195
202
  };
196
- /** 指数バックオフ(オプションでジッター付き)でリトライする。retryOn に含まれる HTTP エラーのみ対象。 */
203
+ /**
204
+ * 指数バックオフ(オプションでジッター付き)でリトライする。
205
+ *
206
+ * `config.retryOn` に含まれるステータスコードを持つエラーのみリトライ対象。
207
+ * 遅延は `baseDelayMs * 2^attempt` の指数バックオフ。
208
+ * `jitter` が `true`(デフォルト)の場合、0.5〜1.0 の乱数係数を乗算して
209
+ * Thundering Herd を防ぐ。`false` にすると確定的な遅延になる。
210
+ */
197
211
  async function withRetry(fn, config) {
198
212
  let lastError;
199
213
  for (let attempt = 0; attempt <= config.maxRetries; attempt++) try {
@@ -277,7 +291,11 @@ var CollectionClientImpl = class {
277
291
  return this.attachContent(entry.item, entry);
278
292
  }
279
293
  async getList(opts) {
280
- return applyGetListOptions(await this.fetchList(), opts);
294
+ const items = applyGetListOptions(await this.fetchList(), opts);
295
+ return {
296
+ items,
297
+ version: this.ctx.source.getListVersion(items)
298
+ };
281
299
  }
282
300
  async getStaticParams() {
283
301
  return (await this.fetchList()).map((item) => ({ slug: item.slug }));
@@ -297,20 +315,46 @@ var CollectionClientImpl = class {
297
315
  next: index < items.length - 1 ? items[index + 1] ?? null : null
298
316
  };
299
317
  }
300
- async revalidate(scope) {
301
- this.ctx.logger?.debug?.("キャッシュを無効化", {
318
+ async revalidate(slug) {
319
+ this.ctx.logger?.debug?.("アイテムキャッシュを無効化", {
302
320
  operation: "revalidate",
303
321
  collection: this.ctx.collection,
304
322
  cacheAdapter: this.ctx.docCache.name,
305
- slug: typeof scope === "object" ? scope.slug : void 0
323
+ slug
306
324
  });
307
325
  if (!this.ctx.docCache.invalidate) return;
308
- if (scope === void 0 || scope === "all") await this.ctx.docCache.invalidate({ collection: this.ctx.collection });
309
- else await this.ctx.docCache.invalidate({
326
+ await this.ctx.docCache.invalidate({
310
327
  collection: this.ctx.collection,
311
- slug: scope.slug
328
+ slug
312
329
  });
313
330
  }
331
+ async revalidateAll() {
332
+ this.ctx.logger?.debug?.("コレクション全体のキャッシュを無効化", {
333
+ operation: "revalidateAll",
334
+ collection: this.ctx.collection,
335
+ cacheAdapter: this.ctx.docCache.name
336
+ });
337
+ if (!this.ctx.docCache.invalidate) return;
338
+ await this.ctx.docCache.invalidate({ collection: this.ctx.collection });
339
+ }
340
+ async checkForUpdate({ slug, since }) {
341
+ await this.revalidate(slug);
342
+ const item = await this.getItem(slug);
343
+ if (!item) return { changed: false };
344
+ return item.updatedAt !== since ? {
345
+ changed: true,
346
+ item
347
+ } : { changed: false };
348
+ }
349
+ async checkListForUpdate({ since, filter }) {
350
+ await this.revalidateAll();
351
+ const { items, version } = await this.getList(filter);
352
+ return version !== since ? {
353
+ changed: true,
354
+ items,
355
+ version
356
+ } : { changed: false };
357
+ }
314
358
  async prefetch(opts) {
315
359
  const items = await this.fetchListRaw();
316
360
  const concurrency = opts?.concurrency ?? this.ctx.maxConcurrent;
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/cache.ts","../src/image.ts","../src/rendering.ts","../src/retry.ts","../src/collection.ts","../src/handler.ts","../src/preset-node.ts","../src/cms.ts","../src/types/plugin.ts"],"sourcesContent":["/** 文字列をSHA-256でハッシュ化し、16進数文字列として返す。画像キーの生成に使用。 */\nexport async function sha256Hex(input: string): Promise<string> {\n\tconst data = new TextEncoder().encode(input);\n\tconst hash = await crypto.subtle.digest(\"SHA-256\", data);\n\treturn Array.from(new Uint8Array(hash))\n\t\t.map((b) => b.toString(16).padStart(2, \"0\"))\n\t\t.join(\"\");\n}\n\n/**\n * キャッシュが有効期限切れかどうかを判定する。\n * ttlMs が未指定の場合は常に false(無期限有効)を返す。\n */\nexport function isStale(cachedAt: number, ttlMs?: number): boolean {\n\tif (ttlMs === undefined) return false;\n\treturn Date.now() - cachedAt > ttlMs;\n}\n","import { sha256Hex } from \"./cache\";\nimport { CMSError, isCMSError } from \"./errors\";\nimport type { ImageCacheAdapter, Logger, StorageBinary } from \"./types/index\";\n\n/** レスポンスヘッダまたはURLの拡張子からContent-Typeを推測する。 */\nfunction inferContentType(\n\turl: string,\n\tresponseContentType: string | null,\n): string {\n\tif (responseContentType?.startsWith(\"image/\")) {\n\t\treturn responseContentType.split(\";\")[0].trim();\n\t}\n\tif (url.includes(\".png\")) return \"image/png\";\n\tif (url.includes(\".gif\")) return \"image/gif\";\n\tif (url.includes(\".webp\")) return \"image/webp\";\n\treturn \"image/jpeg\";\n}\n\n/**\n * Notion画像URLをfetchしてImageCacheAdapterにキャッシュし、プロキシURL を返す。\n * 既存キャッシュがあれば再fetchしない。\n */\nasync function fetchAndCacheImage(\n\tcache: ImageCacheAdapter,\n\tnotionUrl: string,\n\timageProxyBase: string,\n\tlogger?: Logger,\n): Promise<string> {\n\tconst hash = await sha256Hex(notionUrl);\n\tconst proxyUrl = `${imageProxyBase}/${hash}`;\n\n\tconst existing = await cache.get(hash);\n\tif (existing) {\n\t\tlogger?.debug?.(\"画像キャッシュヒット\", {\n\t\t\toperation: \"fetchAndCacheImage\",\n\t\t\tcacheAdapter: cache.name,\n\t\t\timageHash: hash,\n\t\t});\n\t\treturn proxyUrl;\n\t}\n\n\tlogger?.debug?.(\"画像キャッシュミス、Notion からフェッチ\", {\n\t\toperation: \"fetchAndCacheImage\",\n\t\tcacheAdapter: cache.name,\n\t\timageHash: hash,\n\t});\n\n\ttry {\n\t\tconst response = await fetch(notionUrl, {\n\t\t\tsignal: AbortSignal.timeout(10_000),\n\t\t});\n\t\tif (!response.ok) {\n\t\t\tthrow new CMSError({\n\t\t\t\tcode: \"cache/image_fetch_failed\",\n\t\t\t\tmessage: `Failed to fetch Notion image: HTTP ${response.status}`,\n\t\t\t\tcontext: {\n\t\t\t\t\toperation: \"fetchAndCacheImage\",\n\t\t\t\t\tnotionUrl,\n\t\t\t\t\thttpStatus: response.status,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\tconst data = await response.arrayBuffer();\n\t\tconst contentType = inferContentType(\n\t\t\tnotionUrl,\n\t\t\tresponse.headers.get(\"content-type\"),\n\t\t);\n\t\tawait cache.set(hash, data, contentType);\n\t\tlogger?.debug?.(\"画像をキャッシュに保存\", {\n\t\t\toperation: \"fetchAndCacheImage\",\n\t\t\tcacheAdapter: cache.name,\n\t\t\timageHash: hash,\n\t\t});\n\t} catch (err) {\n\t\tif (isCMSError(err)) throw err;\n\t\tthrow new CMSError({\n\t\t\tcode: \"cache/io_failed\",\n\t\t\tmessage: \"Failed to fetch or cache Notion image.\",\n\t\t\tcause: err,\n\t\t\tcontext: { operation: \"fetchAndCacheImage\", notionUrl },\n\t\t});\n\t}\n\n\treturn proxyUrl;\n}\n\n/** ImageCacheAdapter と imageProxyBase から cacheImage 関数を構築するファクトリ。 */\nexport function buildCacheImageFn(\n\tcache: ImageCacheAdapter,\n\timageProxyBase: string,\n\tlogger?: Logger,\n): (notionUrl: string) => Promise<string> {\n\treturn (notionUrl) =>\n\t\tfetchAndCacheImage(cache, notionUrl, imageProxyBase, logger);\n}\n\nexport type { StorageBinary };\n","import type { ContentBlock } from \"./content/blocks\";\nimport { CMSError, isCMSError } from \"./errors\";\nimport { buildCacheImageFn } from \"./image\";\nimport type {\n\tBaseContentItem,\n\tCachedItem,\n\tCMSHooks,\n\tContentConfig,\n\tDataSource,\n\tImageCacheAdapter,\n\tLogger,\n\tRendererFn,\n} from \"./types/index\";\n\n/** `buildCachedItem` に必要な CMS の依存を束ねたコンテキスト。 */\nexport interface RenderContext<T extends BaseContentItem> {\n\tsource: DataSource<T>;\n\trendererFn: RendererFn | undefined;\n\timgCache: ImageCacheAdapter;\n\thasImageCache: boolean;\n\timageProxyBase: string;\n\tcontentConfig: ContentConfig | undefined;\n\thooks: CMSHooks<T>;\n\tlogger: Logger | undefined;\n}\n\n/**\n * コンテンツアイテムをソースから Markdown ロード → blocks 生成 → HTML レンダリング\n * → フック適用まで実行し、キャッシュ保存用の `CachedItem` を返す。\n */\nexport async function buildCachedItem<T extends BaseContentItem>(\n\titem: T,\n\tctx: RenderContext<T>,\n): Promise<CachedItem<T>> {\n\tconst start = Date.now();\n\tctx.logger?.info?.(\"コンテンツのレンダリング開始\", {\n\t\tslug: item.slug,\n\t\tpageId: item.id,\n\t});\n\tctx.hooks.onRenderStart?.(item.slug);\n\n\tlet markdown: string;\n\ttry {\n\t\tmarkdown = await ctx.source.loadMarkdown(item);\n\t} catch (err) {\n\t\tif (isCMSError(err)) throw err;\n\t\tthrow new CMSError({\n\t\t\tcode: \"source/load_markdown_failed\",\n\t\t\tmessage: \"Failed to load markdown from source.\",\n\t\t\tcause: err,\n\t\t\tcontext: {\n\t\t\t\toperation: \"buildCachedItem:loadMarkdown\",\n\t\t\t\tpageId: item.id,\n\t\t\t\tslug: item.slug,\n\t\t\t},\n\t\t});\n\t}\n\n\tlet blocks: ContentBlock[] = [];\n\ttry {\n\t\tblocks = await ctx.source.loadBlocks(item);\n\t} catch (err) {\n\t\tctx.logger?.warn?.(\"loadBlocks に失敗したため raw フォールバック\", {\n\t\t\tslug: item.slug,\n\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t});\n\t\tblocks = [];\n\t}\n\n\tconst cacheImage = ctx.hasImageCache\n\t\t? buildCacheImageFn(ctx.imgCache, ctx.imageProxyBase, ctx.logger)\n\t\t: undefined;\n\n\tlet html: string;\n\tconst rendererFn = ctx.rendererFn ?? (await loadDefaultRenderer());\n\ttry {\n\t\thtml = await rendererFn(markdown, {\n\t\t\timageProxyBase: ctx.imageProxyBase,\n\t\t\tcacheImage,\n\t\t\tremarkPlugins: ctx.contentConfig?.remarkPlugins,\n\t\t\trehypePlugins: ctx.contentConfig?.rehypePlugins,\n\t\t});\n\t} catch (err) {\n\t\tif (isCMSError(err)) throw err;\n\t\tthrow new CMSError({\n\t\t\tcode: \"renderer/failed\",\n\t\t\tmessage: \"Failed to render markdown.\",\n\t\t\tcause: err,\n\t\t\tcontext: {\n\t\t\t\toperation: \"buildCachedItem:renderMarkdown\",\n\t\t\t\tpageId: item.id,\n\t\t\t\tslug: item.slug,\n\t\t\t},\n\t\t});\n\t}\n\n\tif (ctx.hooks.afterRender) {\n\t\thtml = await ctx.hooks.afterRender(html, item);\n\t}\n\n\tlet result: CachedItem<T> = {\n\t\thtml,\n\t\tblocks,\n\t\tmarkdown,\n\t\titem,\n\t\tnotionUpdatedAt: ctx.source.getLastModified(item),\n\t\tcachedAt: Date.now(),\n\t};\n\n\tif (ctx.hooks.beforeCache) {\n\t\tresult = (await ctx.hooks.beforeCache(result)) as CachedItem<T>;\n\t}\n\n\tconst durationMs = Date.now() - start;\n\tctx.logger?.info?.(\"コンテンツのレンダリング完了\", {\n\t\tslug: item.slug,\n\t\tdurationMs,\n\t});\n\tctx.hooks.onRenderEnd?.(item.slug, durationMs);\n\n\treturn result;\n}\n\n/**\n * renderer オプション未指定時のフォールバック。\n * @notion-headless-cms/renderer を動的 import する。\n * createCMS({ renderer }) で明示注入された場合はこのパスを通らない。\n */\nexport async function loadDefaultRenderer(): Promise<RendererFn> {\n\ttry {\n\t\tconst mod = await import(\"@notion-headless-cms/renderer\");\n\t\treturn mod.renderMarkdown as RendererFn;\n\t} catch (err) {\n\t\tthrow new CMSError({\n\t\t\tcode: \"renderer/failed\",\n\t\t\tmessage:\n\t\t\t\t\"renderer オプションが未指定で @notion-headless-cms/renderer が見つかりません。\" +\n\t\t\t\t\" createCMS({ renderer }) でレンダラーを注入するか、@notion-headless-cms/renderer をインストールしてください。\",\n\t\t\tcause: err,\n\t\t\tcontext: { operation: \"loadDefaultRenderer\" },\n\t\t});\n\t}\n}\n","export interface RetryConfig {\n\tretryOn: number[];\n\tmaxRetries: number;\n\tbaseDelayMs: number;\n\t/** true のとき指数バックオフにランダムジッターを加える(Thundering Herd 対策)。デフォルト: true */\n\tjitter?: boolean;\n\tonRetry?: (attempt: number, status: number) => void;\n}\n\nexport const DEFAULT_RETRY_CONFIG: RetryConfig = {\n\tretryOn: [429, 502, 503],\n\tmaxRetries: 4,\n\tbaseDelayMs: 1000,\n\tjitter: true,\n};\n\n/** 指数バックオフ(オプションでジッター付き)でリトライする。retryOn に含まれる HTTP エラーのみ対象。 */\nexport async function withRetry<T>(\n\tfn: () => Promise<T>,\n\tconfig: RetryConfig,\n): Promise<T> {\n\tlet lastError: unknown;\n\tfor (let attempt = 0; attempt <= config.maxRetries; attempt++) {\n\t\ttry {\n\t\t\treturn await fn();\n\t\t} catch (err) {\n\t\t\tconst status = (err as { status?: number }).status;\n\t\t\tif (status === undefined || !config.retryOn.includes(status)) {\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t\tlastError = err;\n\t\t\tif (attempt < config.maxRetries) {\n\t\t\t\tconfig.onRetry?.(attempt + 1, status);\n\t\t\t\tconst jitterFactor =\n\t\t\t\t\tconfig.jitter !== false ? 0.5 + Math.random() * 0.5 : 1;\n\t\t\t\tconst delay = config.baseDelayMs * 2 ** attempt * jitterFactor;\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, delay));\n\t\t\t}\n\t\t}\n\t}\n\tthrow lastError;\n}\n","import { isStale } from \"./cache\";\nimport type { ContentBlock, ContentResult } from \"./content/blocks\";\nimport type { RenderContext } from \"./rendering\";\nimport { buildCachedItem } from \"./rendering\";\nimport type { RetryConfig } from \"./retry\";\nimport { withRetry } from \"./retry\";\nimport type {\n\tAdjacencyOptions,\n\tBaseContentItem,\n\tCachedItem,\n\tCachedItemList,\n\tCMSHooks,\n\tCollectionClient,\n\tDataSource,\n\tDocumentCacheAdapter,\n\tGetListOptions,\n\tItemWithContent,\n\tLogger,\n\tSortOption,\n} from \"./types/index\";\n\n/**\n * コレクション別キャッシュキーを生成する。\n * item: `{collection}:{slug}` / list: `{collection}`\n */\nexport function collectionKey(collection: string, slug?: string): string {\n\treturn slug ? `${collection}:${slug}` : collection;\n}\n\n/** 単一コレクションの DataSource + SWR キャッシュ依存を束ねたコンテキスト。 */\nexport interface CollectionContext<T extends BaseContentItem> {\n\tcollection: string;\n\tsource: DataSource<T>;\n\tdocCache: DocumentCacheAdapter<T>;\n\trender: RenderContext<T>;\n\thooks: CMSHooks<T>;\n\tlogger: Logger | undefined;\n\tttlMs: number | undefined;\n\tpublishedStatuses: string[];\n\taccessibleStatuses: string[];\n\tretryConfig: RetryConfig;\n\tmaxConcurrent: number;\n\twaitUntil: ((p: Promise<unknown>) => void) | undefined;\n\t/**\n\t * slug として使うフィールド名。\n\t * `createCMS({ collections })` で指定した値。\n\t * 設定時は `source.properties[slugField].notion` を Notion プロパティ名として\n\t * `findByProp` を呼び出す。\n\t */\n\tslugField?: string;\n}\n\n/** CollectionClient の実装。ユーザーは `createCMS` 経由でインスタンスを受け取る。 */\nexport class CollectionClientImpl<T extends BaseContentItem>\n\timplements CollectionClient<T>\n{\n\tconstructor(private readonly ctx: CollectionContext<T>) {}\n\n\t// ── 基本取得 ──────────────────────────────────────────────────────────\n\n\tasync getItem(slug: string): Promise<ItemWithContent<T> | null> {\n\t\tconst cached = await this.ctx.docCache.getItem(slug);\n\t\tif (cached) {\n\t\t\tif (\n\t\t\t\tthis.ctx.ttlMs !== undefined &&\n\t\t\t\tisStale(cached.cachedAt, this.ctx.ttlMs)\n\t\t\t) {\n\t\t\t\t// TTL 設定あり + 期限切れ: ブロッキングフェッチ(stale を返さない)\n\t\t\t\tthis.ctx.logger?.debug?.(\"キャッシュ期限切れ(TTL)、フェッチ\", {\n\t\t\t\t\toperation: \"getItem\",\n\t\t\t\t\tslug,\n\t\t\t\t\tcollection: this.ctx.collection,\n\t\t\t\t\tcacheAdapter: this.ctx.docCache.name,\n\t\t\t\t});\n\t\t\t\tthis.ctx.hooks.onCacheMiss?.(slug);\n\t\t\t\tconst item = await this.findRaw(slug);\n\t\t\t\tif (!item) return null;\n\t\t\t\tconst entry = await buildCachedItem(item, this.ctx.render);\n\t\t\t\tawait this.ctx.docCache.setItem(slug, entry);\n\t\t\t\treturn this.attachContent(entry.item, entry);\n\t\t\t}\n\t\t\t// TTL 未設定 or 期限内: キャッシュを即時返却し、バックグラウンドで差分チェック\n\t\t\tconst bg = this.checkAndUpdateItemBg(slug, cached);\n\t\t\tif (this.ctx.waitUntil) {\n\t\t\t\tthis.ctx.waitUntil(bg);\n\t\t\t}\n\t\t\tthis.ctx.logger?.debug?.(\"キャッシュヒット\", {\n\t\t\t\toperation: \"getItem\",\n\t\t\t\tslug,\n\t\t\t\tcollection: this.ctx.collection,\n\t\t\t\tcacheAdapter: this.ctx.docCache.name,\n\t\t\t\tcachedAt: cached.cachedAt,\n\t\t\t});\n\t\t\tthis.ctx.hooks.onCacheHit?.(slug, cached);\n\t\t\treturn this.attachContent(cached.item, cached);\n\t\t}\n\n\t\t// キャッシュなし: 同期フェッチ\n\t\tthis.ctx.logger?.debug?.(\"キャッシュミス、フェッチ\", {\n\t\t\toperation: \"getItem\",\n\t\t\tslug,\n\t\t\tcollection: this.ctx.collection,\n\t\t\tcacheAdapter: this.ctx.docCache.name,\n\t\t});\n\t\tthis.ctx.hooks.onCacheMiss?.(slug);\n\t\tconst item = await this.findRaw(slug);\n\t\tif (!item) {\n\t\t\tthis.ctx.logger?.debug?.(\"アイテムが見つかりません\", {\n\t\t\t\toperation: \"getItem\",\n\t\t\t\tslug,\n\t\t\t\tcollection: this.ctx.collection,\n\t\t\t});\n\t\t\treturn null;\n\t\t}\n\n\t\tconst entry = await buildCachedItem(item, this.ctx.render);\n\t\tconst save = this.ctx.docCache.setItem(slug, entry);\n\t\tif (this.ctx.waitUntil) {\n\t\t\tthis.ctx.waitUntil(save);\n\t\t} else {\n\t\t\tawait save;\n\t\t}\n\n\t\treturn this.attachContent(entry.item, entry);\n\t}\n\n\tasync getList(opts?: GetListOptions<T>): Promise<T[]> {\n\t\tconst items = await this.fetchList();\n\t\treturn applyGetListOptions(items, opts);\n\t}\n\n\t// ── SSG / ナビゲーション ─────────────────────────────────────────────\n\n\tasync getStaticParams(): Promise<{ slug: string }[]> {\n\t\tconst items = await this.fetchList();\n\t\treturn items.map((item) => ({ slug: item.slug }));\n\t}\n\n\tasync getStaticPaths(): Promise<string[]> {\n\t\tconst items = await this.fetchList();\n\t\treturn items.map((item) => item.slug);\n\t}\n\n\tasync adjacent(\n\t\tslug: string,\n\t\topts?: AdjacencyOptions<T>,\n\t): Promise<{ prev: T | null; next: T | null }> {\n\t\tconst items = applyGetListOptions(await this.fetchList(), {\n\t\t\tsort: opts?.sort,\n\t\t});\n\t\tconst index = items.findIndex((it) => it.slug === slug);\n\t\tif (index === -1) return { prev: null, next: null };\n\t\treturn {\n\t\t\tprev: index > 0 ? (items[index - 1] ?? null) : null,\n\t\t\tnext: index < items.length - 1 ? (items[index + 1] ?? null) : null,\n\t\t};\n\t}\n\n\t// ── キャッシュ ────────────────────────────────────────────────────────\n\n\tasync revalidate(scope?: \"all\" | { slug: string }): Promise<void> {\n\t\tthis.ctx.logger?.debug?.(\"キャッシュを無効化\", {\n\t\t\toperation: \"revalidate\",\n\t\t\tcollection: this.ctx.collection,\n\t\t\tcacheAdapter: this.ctx.docCache.name,\n\t\t\tslug: typeof scope === \"object\" ? scope.slug : undefined,\n\t\t});\n\t\tif (!this.ctx.docCache.invalidate) return;\n\t\tif (scope === undefined || scope === \"all\") {\n\t\t\tawait this.ctx.docCache.invalidate({ collection: this.ctx.collection });\n\t\t} else {\n\t\t\tawait this.ctx.docCache.invalidate({\n\t\t\t\tcollection: this.ctx.collection,\n\t\t\t\tslug: scope.slug,\n\t\t\t});\n\t\t}\n\t}\n\n\tasync prefetch(opts?: {\n\t\tconcurrency?: number;\n\t\tonProgress?: (done: number, total: number) => void;\n\t}): Promise<{ ok: number; failed: number }> {\n\t\tconst items = await this.fetchListRaw();\n\t\tconst concurrency = opts?.concurrency ?? this.ctx.maxConcurrent;\n\t\tlet ok = 0;\n\t\tlet failed = 0;\n\n\t\tfor (let i = 0; i < items.length; i += concurrency) {\n\t\t\tconst chunk = items.slice(i, i + concurrency);\n\t\t\tawait Promise.all(\n\t\t\t\tchunk.map(async (item) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst rendered = await buildCachedItem(item, this.ctx.render);\n\t\t\t\t\t\tawait this.ctx.docCache.setItem(item.slug, rendered);\n\t\t\t\t\t\tok++;\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tfailed++;\n\t\t\t\t\t\tthis.ctx.logger?.warn?.(\n\t\t\t\t\t\t\t\"prefetch: アイテムの事前レンダリングに失敗\",\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tslug: item.slug,\n\t\t\t\t\t\t\t\tpageId: item.id,\n\t\t\t\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t);\n\t\t\topts?.onProgress?.(Math.min(i + concurrency, items.length), items.length);\n\t\t}\n\n\t\tawait this.ctx.docCache.setList({ items, cachedAt: Date.now() });\n\t\treturn { ok, failed };\n\t}\n\n\t// ── 内部 ──────────────────────────────────────────────────────────────\n\n\tprivate attachContent(item: T, cached: CachedItem<T>): ItemWithContent<T> {\n\t\tconst ctx = this.ctx;\n\t\tlet blocksCache: ContentBlock[] | undefined;\n\t\tlet htmlCache: string | undefined = cached.html;\n\t\tlet markdownCache: string | undefined;\n\n\t\tconst content: ContentResult = {\n\t\t\tget blocks(): ContentBlock[] {\n\t\t\t\tif (!blocksCache) {\n\t\t\t\t\t// lazy: キャッシュに blocks が保存されていない場合、簡易な single-raw に落とす\n\t\t\t\t\tblocksCache = [\n\t\t\t\t\t\t{ type: \"raw\", html: cached.html } satisfies ContentBlock,\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t\treturn blocksCache;\n\t\t\t},\n\t\t\tasync html(): Promise<string> {\n\t\t\t\tif (htmlCache !== undefined) return htmlCache;\n\t\t\t\thtmlCache = cached.html;\n\t\t\t\treturn htmlCache;\n\t\t\t},\n\t\t\tasync markdown(): Promise<string> {\n\t\t\t\tif (markdownCache !== undefined) return markdownCache;\n\t\t\t\tmarkdownCache = await ctx.source.loadMarkdown(item);\n\t\t\t\treturn markdownCache;\n\t\t\t},\n\t\t};\n\n\t\tif (cached.blocks) blocksCache = cached.blocks;\n\n\t\treturn Object.assign(Object.create(null) as object, item, {\n\t\t\tcontent,\n\t\t}) as ItemWithContent<T>;\n\t}\n\n\tprivate async fetchList(): Promise<T[]> {\n\t\tconst cached = await this.ctx.docCache.getList();\n\t\tif (cached) {\n\t\t\tif (\n\t\t\t\tthis.ctx.ttlMs !== undefined &&\n\t\t\t\tisStale(cached.cachedAt, this.ctx.ttlMs)\n\t\t\t) {\n\t\t\t\t// TTL 設定あり + 期限切れ: ブロッキングフェッチ(stale を返さない)\n\t\t\t\tthis.ctx.logger?.debug?.(\"リストキャッシュ期限切れ(TTL)、フェッチ\", {\n\t\t\t\t\toperation: \"getList\",\n\t\t\t\t\tcollection: this.ctx.collection,\n\t\t\t\t\tcacheAdapter: this.ctx.docCache.name,\n\t\t\t\t});\n\t\t\t\tthis.ctx.hooks.onListCacheMiss?.();\n\t\t\t\tconst items = await this.fetchListRaw();\n\t\t\t\tawait this.ctx.docCache.setList({ items, cachedAt: Date.now() });\n\t\t\t\treturn items;\n\t\t\t}\n\t\t\t// TTL 未設定 or 期限内: キャッシュを即時返却し、バックグラウンドで差分チェック\n\t\t\tconst bg = this.checkAndUpdateListBg(cached);\n\t\t\tif (this.ctx.waitUntil) {\n\t\t\t\tthis.ctx.waitUntil(bg);\n\t\t\t}\n\t\t\tthis.ctx.logger?.debug?.(\"リストキャッシュヒット\", {\n\t\t\t\toperation: \"getList\",\n\t\t\t\tcollection: this.ctx.collection,\n\t\t\t\tcacheAdapter: this.ctx.docCache.name,\n\t\t\t});\n\t\t\tthis.ctx.hooks.onListCacheHit?.(cached);\n\t\t\treturn cached.items;\n\t\t}\n\n\t\t// キャッシュなし: 同期フェッチ\n\t\tthis.ctx.logger?.debug?.(\"リストキャッシュミス、フェッチ\", {\n\t\t\toperation: \"getList\",\n\t\t\tcollection: this.ctx.collection,\n\t\t\tcacheAdapter: this.ctx.docCache.name,\n\t\t});\n\t\tthis.ctx.hooks.onListCacheMiss?.();\n\t\tconst items = await this.fetchListRaw();\n\t\tconst cachedAt = Date.now();\n\t\tconst save = this.ctx.docCache.setList({ items, cachedAt });\n\t\tif (this.ctx.waitUntil) {\n\t\t\tthis.ctx.waitUntil(save);\n\t\t} else {\n\t\t\tawait save;\n\t\t}\n\t\treturn items;\n\t}\n\n\tprivate async checkAndUpdateItemBg(\n\t\tslug: string,\n\t\tcached: CachedItem<T>,\n\t): Promise<void> {\n\t\ttry {\n\t\t\tconst item = await this.findRaw(slug);\n\t\t\tif (!item) return;\n\t\t\tif (this.ctx.source.getLastModified(item) !== cached.notionUpdatedAt) {\n\t\t\t\t// 更新あり: 再レンダリングしてキャッシュを差し替える\n\t\t\t\tconst entry = await buildCachedItem(item, this.ctx.render);\n\t\t\t\tawait this.ctx.docCache.setItem(slug, entry);\n\t\t\t\tthis.ctx.logger?.debug?.(\"SWR: 差分を検出、キャッシュを差し替え\", {\n\t\t\t\t\toperation: \"getItem:bg\",\n\t\t\t\t\tslug,\n\t\t\t\t\tcollection: this.ctx.collection,\n\t\t\t\t\tnotionUpdatedAt: cached.notionUpdatedAt,\n\t\t\t\t});\n\t\t\t\tthis.ctx.hooks.onCacheRevalidated?.(slug, entry);\n\t\t\t} else if (this.ctx.ttlMs !== undefined) {\n\t\t\t\t// 変更なし + TTL あり: cachedAt をリセットして次回の期限切れを先送りする\n\t\t\t\tawait this.ctx.docCache.setItem(slug, {\n\t\t\t\t\t...cached,\n\t\t\t\t\tcachedAt: Date.now(),\n\t\t\t\t});\n\t\t\t\tthis.ctx.logger?.debug?.(\"SWR: 差分なし、TTL をリセット\", {\n\t\t\t\t\toperation: \"getItem:bg\",\n\t\t\t\t\tslug,\n\t\t\t\t\tcollection: this.ctx.collection,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tthis.ctx.logger?.warn?.(\n\t\t\t\t\"SWR: アイテムのバックグラウンド差分チェックに失敗\",\n\t\t\t\t{\n\t\t\t\t\tslug,\n\t\t\t\t\tcollection: this.ctx.collection,\n\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate async checkAndUpdateListBg(cached: CachedItemList<T>): Promise<void> {\n\t\ttry {\n\t\t\tconst items = await this.fetchListRaw();\n\t\t\tif (\n\t\t\t\tthis.ctx.source.getListVersion(items) !==\n\t\t\t\tthis.ctx.source.getListVersion(cached.items)\n\t\t\t) {\n\t\t\t\t// 更新あり: リストを差し替える\n\t\t\t\tconst listEntry = { items, cachedAt: Date.now() };\n\t\t\t\tawait this.ctx.docCache.setList(listEntry);\n\t\t\t\tthis.ctx.logger?.debug?.(\n\t\t\t\t\t\"SWR: リスト差分を検出、キャッシュを差し替え\",\n\t\t\t\t\t{\n\t\t\t\t\t\toperation: \"getList:bg\",\n\t\t\t\t\t\tcollection: this.ctx.collection,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\tthis.ctx.hooks.onListCacheRevalidated?.(listEntry);\n\t\t\t} else if (this.ctx.ttlMs !== undefined) {\n\t\t\t\t// 変更なし + TTL あり: cachedAt をリセットする\n\t\t\t\tawait this.ctx.docCache.setList({\n\t\t\t\t\t...cached,\n\t\t\t\t\tcachedAt: Date.now(),\n\t\t\t\t});\n\t\t\t\tthis.ctx.logger?.debug?.(\"SWR: リスト差分なし、TTL をリセット\", {\n\t\t\t\t\toperation: \"getList:bg\",\n\t\t\t\t\tcollection: this.ctx.collection,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tthis.ctx.logger?.warn?.(\n\t\t\t\t\"SWR: リストのバックグラウンド差分チェックに失敗\",\n\t\t\t\t{\n\t\t\t\t\tcollection: this.ctx.collection,\n\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate fetchListRaw(): Promise<T[]> {\n\t\treturn withRetry(\n\t\t\t() =>\n\t\t\t\tthis.ctx.source.list({\n\t\t\t\t\tpublishedStatuses:\n\t\t\t\t\t\tthis.ctx.publishedStatuses.length > 0\n\t\t\t\t\t\t\t? this.ctx.publishedStatuses\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t}),\n\t\t\t{\n\t\t\t\t...this.ctx.retryConfig,\n\t\t\t\tonRetry: (attempt, status) => {\n\t\t\t\t\tthis.ctx.logger?.warn?.(\"getList() リトライ中\", { attempt, status });\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t}\n\n\tprivate async findRaw(slug: string): Promise<T | null> {\n\t\tconst retryOpts = {\n\t\t\t...this.ctx.retryConfig,\n\t\t\tonRetry: (attempt: number, status: number) => {\n\t\t\t\tthis.ctx.logger?.warn?.(\"getItem() リトライ中\", {\n\t\t\t\t\tattempt,\n\t\t\t\t\tstatus,\n\t\t\t\t\tslug,\n\t\t\t\t});\n\t\t\t},\n\t\t};\n\n\t\t// slug フィールドが指定され、DataSource が findByProp を持つ場合は\n\t\t// Notion プロパティ名を解決して効率的なフィルタクエリを実行する。\n\t\tconst slugField = this.ctx.slugField;\n\t\tconst notionPropName = slugField\n\t\t\t? this.ctx.source.properties?.[slugField]?.notion\n\t\t\t: undefined;\n\n\t\tlet item: T | null;\n\t\tconst findByProp = this.ctx.source.findByProp?.bind(this.ctx.source);\n\t\tif (notionPropName && findByProp) {\n\t\t\titem = await withRetry(() => findByProp(notionPropName, slug), retryOpts);\n\t\t} else {\n\t\t\t// フォールバック: list して線形探索\n\t\t\tconst all = await withRetry(() => this.ctx.source.list(), retryOpts);\n\t\t\titem = all.find((i) => i.slug === slug) ?? null;\n\t\t}\n\n\t\tif (!item) return null;\n\t\tif (\n\t\t\tthis.ctx.accessibleStatuses.length > 0 &&\n\t\t\t(!item.status || !this.ctx.accessibleStatuses.includes(item.status))\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\t\treturn item;\n\t}\n}\n\nfunction applyGetListOptions<T extends BaseContentItem>(\n\titems: T[],\n\topts?: GetListOptions<T>,\n): T[] {\n\tif (!opts) return items;\n\tlet result = items;\n\n\tif (opts.statuses && opts.statuses.length > 0) {\n\t\tconst allow = new Set(opts.statuses);\n\t\tresult = result.filter(\n\t\t\t(it) => it.status !== undefined && allow.has(it.status),\n\t\t);\n\t}\n\n\tif (opts.tag) {\n\t\tconst tag = opts.tag;\n\t\tresult = result.filter((it) => {\n\t\t\tconst tags = (it as unknown as { tags?: unknown }).tags;\n\t\t\treturn Array.isArray(tags) && tags.includes(tag);\n\t\t});\n\t}\n\n\tif (opts.where) {\n\t\tconst where = opts.where;\n\t\tresult = result.filter((it) =>\n\t\t\tObject.entries(where).every(\n\t\t\t\t([key, value]) => (it as Record<string, unknown>)[key] === value,\n\t\t\t),\n\t\t);\n\t}\n\n\tif (opts.sort) {\n\t\tresult = [...result].sort(makeComparator(opts.sort));\n\t}\n\n\tconst skip = opts.skip ?? 0;\n\tconst limit = opts.limit;\n\tif (skip > 0 || limit !== undefined) {\n\t\tresult = result.slice(skip, limit !== undefined ? skip + limit : undefined);\n\t}\n\n\treturn result;\n}\n\nfunction makeComparator<T extends BaseContentItem>(\n\tsort: SortOption<T>,\n): (a: T, b: T) => number {\n\tconst by = sort.by;\n\tconst dir = sort.direction === \"asc\" ? 1 : -1;\n\treturn (a, b) => {\n\t\tconst av = (a as Record<string, unknown>)[by];\n\t\tconst bv = (b as Record<string, unknown>)[by];\n\t\tif (av === bv) return 0;\n\t\tif (av === undefined) return 1;\n\t\tif (bv === undefined) return -1;\n\t\t// biome-ignore lint/suspicious/noExplicitAny: 汎用比較\n\t\treturn (av as any) > (bv as any) ? dir : -dir;\n\t};\n}\n","import type { ImageCacheAdapter, InvalidateScope } from \"./types/index\";\n\n/** `$handler()` の挙動設定。 */\nexport interface HandlerOptions {\n\t/** マウントするベースパス。デフォルト `/api/cms`。 */\n\tbasePath?: string;\n\t/** 画像プロキシのパス (basePath 相対)。デフォルト `/images/:hash`。 */\n\timagesPath?: string;\n\t/** revalidate webhook のパス (basePath 相対)。デフォルト `/revalidate`。 */\n\trevalidatePath?: string;\n\t/** Webhook 署名検証用シークレット (未指定なら検証スキップ)。 */\n\twebhookSecret?: string;\n\t/** デフォルト実装を無効化する場合 true。 */\n\tdisabled?: boolean;\n}\n\n/** `$handler()` が内部で依存する CMS 機能の最小セット。 */\nexport interface HandlerAdapter {\n\timageCache: ImageCacheAdapter;\n\t/** コレクション名で DataSource を取り出し parseWebhook にフォワードする。 */\n\tparseWebhook(\n\t\treq: Request,\n\t\twebhookSecret: string | undefined,\n\t): Promise<InvalidateScope | null>;\n\trevalidate(scope: InvalidateScope): Promise<void>;\n}\n\nconst DEFAULT_OPTS = {\n\tbasePath: \"/api/cms\",\n\timagesPath: \"/images\",\n\trevalidatePath: \"/revalidate\",\n} as const;\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` — Webhook 受信 + $revalidate()\n */\nexport function createHandler(\n\tadapter: HandlerAdapter,\n\topts: HandlerOptions = {},\n): (req: Request) => Promise<Response> {\n\tconst basePath = trimTrailingSlash(opts.basePath ?? DEFAULT_OPTS.basePath);\n\tconst imagesPath = opts.imagesPath ?? DEFAULT_OPTS.imagesPath;\n\tconst revalidatePath = opts.revalidatePath ?? DEFAULT_OPTS.revalidatePath;\n\n\treturn async (req: Request): Promise<Response> => {\n\t\tconst url = new URL(req.url);\n\t\tconst path = url.pathname;\n\n\t\tif (!path.startsWith(basePath)) {\n\t\t\treturn new Response(\"Not Found\", { status: 404 });\n\t\t}\n\t\tconst rel = path.slice(basePath.length) || \"/\";\n\n\t\t// 画像: GET {basePath}/images/:hash\n\t\tif (req.method === \"GET\" && rel.startsWith(`${imagesPath}/`)) {\n\t\t\tconst hash = rel.slice(imagesPath.length + 1);\n\t\t\tif (!hash) return new Response(\"Bad Request\", { status: 400 });\n\t\t\tconst object = await adapter.imageCache.get(hash);\n\t\t\tif (!object) return new Response(\"Not Found\", { status: 404 });\n\t\t\tconst headers = new Headers();\n\t\t\tif (object.contentType) headers.set(\"content-type\", object.contentType);\n\t\t\theaders.set(\"cache-control\", \"public, max-age=31536000, immutable\");\n\t\t\treturn new Response(object.data, { headers });\n\t\t}\n\n\t\t// Revalidate: POST {basePath}/revalidate\n\t\tif (req.method === \"POST\" && rel === revalidatePath) {\n\t\t\tconst scope = await adapter.parseWebhook(req, opts.webhookSecret);\n\t\t\tif (!scope) {\n\t\t\t\treturn new Response(JSON.stringify({ ok: false, reason: \"invalid\" }), {\n\t\t\t\t\tstatus: 400,\n\t\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\t});\n\t\t\t}\n\t\t\tawait adapter.revalidate(scope);\n\t\t\treturn new Response(JSON.stringify({ ok: true, scope }), {\n\t\t\t\tstatus: 200,\n\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t});\n\t\t}\n\n\t\treturn new Response(\"Not Found\", { status: 404 });\n\t};\n}\n\nfunction trimTrailingSlash(s: string): string {\n\treturn s.endsWith(\"/\") ? s.slice(0, -1) : s;\n}\n","import { memoryDocumentCache, memoryImageCache } from \"./cache/memory\";\nimport type { CacheConfig, CreateCMSOptions, RendererFn } from \"./types/index\";\n\n/** `nodePreset()` のオプション。 */\nexport interface NodePresetOptions {\n\t/**\n\t * キャッシュ設定。\n\t * - 省略時: memoryDocumentCache + memoryImageCache をデフォルト有効化\n\t * - `\"disabled\"`: キャッシュを完全無効化\n\t * - オブジェクト: 任意の cache adapter を差し込む\n\t */\n\tcache?: CacheConfig | \"disabled\";\n\t/** SWR の TTL (ミリ秒)。`cache` をオブジェクトで渡した場合はそちらが優先される。 */\n\tttlMs?: number;\n\t/** カスタムレンダラー。未指定時は core が @notion-headless-cms/renderer を動的ロード。 */\n\trenderer?: RendererFn;\n}\n\n/**\n * Node.js ランタイム向けの `createCMS` オプションプリセット。\n * メモリキャッシュをデフォルト有効にした `{ cache, renderer }` を返す。\n *\n * @example\n * import { createCMS, nodePreset } from \"@notion-headless-cms/core\";\n * import { cmsDataSources } from \"./generated/cms-schema\";\n *\n * const cms = createCMS({\n * ...nodePreset({ ttlMs: 5 * 60_000 }),\n * dataSources: cmsDataSources,\n * });\n */\nexport function nodePreset(\n\topts: NodePresetOptions = {},\n): Pick<CreateCMSOptions, \"cache\" | \"renderer\"> {\n\tif (opts.cache === \"disabled\") {\n\t\treturn { cache: undefined, renderer: opts.renderer };\n\t}\n\treturn {\n\t\tcache: opts.cache ?? {\n\t\t\tdocument: memoryDocumentCache(),\n\t\t\timage: memoryImageCache(),\n\t\t\tttlMs: opts.ttlMs,\n\t\t},\n\t\trenderer: opts.renderer,\n\t};\n}\n","import { noopDocumentCache, noopImageCache } 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 { nodePreset } from \"./preset-node\";\nimport type { RenderContext } from \"./rendering\";\nimport type { RetryConfig } from \"./retry\";\nimport { DEFAULT_RETRY_CONFIG } from \"./retry\";\nimport type {\n\tBaseContentItem,\n\tCacheConfig,\n\tCachedItemList,\n\tCMSHooks,\n\tCollectionClient,\n\tCollectionSemantics,\n\tCreateCMSOptions,\n\tDataSource,\n\tDataSourceMap,\n\tDocumentCacheAdapter,\n\tImageCacheAdapter,\n\tInferDataSourceItem,\n\tInvalidateScope,\n\tLogger,\n\tLogLevel,\n\tRendererFn,\n} from \"./types/index\";\n\nconst DEFAULT_IMAGE_PROXY_BASE = \"/api/images\";\n\n/** `CMSClient<D>` — コレクション別アクセス + グローバル操作の合成型。 */\nexport type CMSClient<D extends DataSourceMap> = {\n\t[K in keyof D]: CollectionClient<InferDataSourceItem<D[K]>>;\n} & CMSGlobalOps<D>;\n\n/** `CMSClient` のグローバル名前空間。`$` プレフィックス。 */\nexport interface CMSGlobalOps<D extends DataSourceMap> {\n\t/** 登録されているコレクション名の一覧。 */\n\treadonly $collections: readonly (keyof D & string)[];\n\t/** 全コレクションまたは特定コレクションのキャッシュを無効化する。 */\n\t$revalidate(scope?: InvalidateScope): Promise<void>;\n\t/** Web Standard なルーティングハンドラ (画像プロキシ / webhook) を生成する。 */\n\t$handler(opts?: HandlerOptions): (req: Request) => Promise<Response>;\n\t/** ハッシュキーでキャッシュ画像を取得する。 */\n\t$getCachedImage(hash: string): ReturnType<ImageCacheAdapter[\"get\"]>;\n}\n\nfunction resolveDocumentCache(\n\tcache: CacheConfig | undefined,\n\t// biome-ignore lint/suspicious/noExplicitAny: 横断的に利用\n): DocumentCacheAdapter<any> {\n\tif (!cache || cache === \"disabled\" || !cache.document) {\n\t\treturn noopDocumentCache();\n\t}\n\treturn cache.document;\n}\n\nfunction resolveImageCache(cache: CacheConfig | undefined): ImageCacheAdapter {\n\tif (!cache || cache === \"disabled\" || !cache.image) {\n\t\treturn noopImageCache();\n\t}\n\treturn cache.image;\n}\n\nfunction resolveTtl(cache: CacheConfig | undefined): number | undefined {\n\tif (!cache || cache === \"disabled\") return undefined;\n\treturn cache.ttlMs;\n}\n\nfunction hasImageCacheConfigured(cache: CacheConfig | undefined): boolean {\n\tif (!cache || cache === \"disabled\") return false;\n\treturn !!cache.image;\n}\n\n/**\n * `{collection}:{slug}` キー空間で動作するコレクション別キャッシュビューを生成する。\n * 単一の `DocumentCacheAdapter` に複数コレクションを同居させるためのアダプタ。\n */\nfunction scopeDocumentCache<T extends BaseContentItem>(\n\t// biome-ignore lint/suspicious/noExplicitAny: 共有ストレージのため\n\tbase: DocumentCacheAdapter<any>,\n\tcollection: string,\n): DocumentCacheAdapter<T> {\n\tconst itemKey = (slug: string): string => `${collection}:${slug}`;\n\t// リストはコレクション別にクロージャ変数で管理する。\n\t// base.getList/setList はコレクション名前空間を持たないため、\n\t// 複数コレクションが同じ base を共有すると上書きし合うバグが起きる。\n\t// 初回アクセスのみ base から読み込むことで pre-populate されたキャッシュを活かしつつ、\n\t// 以降の読み書きはコレクション固有のスロットに限定する。\n\tlet listSlot: CachedItemList<T> | null = null;\n\tlet listInitialized = false;\n\n\treturn {\n\t\tname: `${base.name}@${collection}`,\n\t\tgetList: async () => {\n\t\t\tif (!listInitialized) {\n\t\t\t\tlistInitialized = true;\n\t\t\t\tlistSlot = (await base.getList()) as CachedItemList<T> | null;\n\t\t\t}\n\t\t\treturn listSlot;\n\t\t},\n\t\tsetList: (data) => {\n\t\t\tlistSlot = data as CachedItemList<T>;\n\t\t\tlistInitialized = true;\n\t\t\treturn Promise.resolve();\n\t\t},\n\t\tgetItem: (slug) => base.getItem(itemKey(slug)),\n\t\tsetItem: (slug, data) => base.setItem(itemKey(slug), data),\n\t\tasync invalidate(scope) {\n\t\t\tlistSlot = null;\n\t\t\tlistInitialized = true; // 無効化後は base を再読みしない\n\t\t\tif (!base.invalidate) return;\n\t\t\tif (scope === \"all\") {\n\t\t\t\treturn base.invalidate({ collection });\n\t\t\t}\n\t\t\tif (\"slug\" in scope) {\n\t\t\t\t// アイテムは `{collection}:{slug}` で保存されるため正しいキーに変換する\n\t\t\t\treturn base.invalidate({\n\t\t\t\t\tcollection: scope.collection,\n\t\t\t\t\tslug: itemKey(scope.slug),\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn base.invalidate(scope);\n\t\t},\n\t};\n}\n\nconst LOG_LEVEL_ORDER: Record<LogLevel, number> = {\n\tdebug: 0,\n\tinfo: 1,\n\twarn: 2,\n\terror: 3,\n};\n\n/** `logger` から `minLevel` 未満のレベルを除いた新しい Logger を返す。 */\nfunction applyLogLevel(\n\tlogger: Logger | undefined,\n\tminLevel: LogLevel,\n): Logger | undefined {\n\tif (!logger) return undefined;\n\tconst minOrder = LOG_LEVEL_ORDER[minLevel];\n\tconst filtered: Logger = {};\n\tfor (const level of [\"debug\", \"info\", \"warn\", \"error\"] as const) {\n\t\tif (LOG_LEVEL_ORDER[level] >= minOrder) {\n\t\t\tfiltered[level] = logger[level];\n\t\t}\n\t}\n\treturn filtered;\n}\n\n/**\n * `preset` オプションを解決して `cache` / `renderer` のデフォルトを補完する内部関数。\n * 明示的な `cache` / `renderer` がある場合はそちらが優先される。\n * `preset` 未指定時は opts をそのまま返す。\n */\nfunction resolvePreset<D extends DataSourceMap>(\n\topts: CreateCMSOptions<D>,\n): CreateCMSOptions<D> {\n\tif (opts.preset === \"disabled\") {\n\t\treturn { ...opts, cache: undefined };\n\t}\n\tif (opts.preset === \"node\") {\n\t\tconst presetResult = nodePreset({ ttlMs: opts.ttlMs });\n\t\treturn {\n\t\t\t...opts,\n\t\t\tcache: opts.cache ?? presetResult.cache,\n\t\t\trenderer: opts.renderer ?? presetResult.renderer,\n\t\t};\n\t}\n\treturn opts;\n}\n\n/**\n * 複数の DataSource を束ねた CMS クライアントを生成する。\n *\n * @example\n * // Node.js(preset を使った簡潔な記法)\n * const cms = createCMS({ dataSources: cmsDataSources, preset: \"node\", ttlMs: 5 * 60_000 });\n *\n * @example\n * // 従来の spread パターン(引き続き動作する)\n * const cms = createCMS({ ...nodePreset({ ttlMs: 5 * 60_000 }), dataSources: cmsDataSources });\n *\n * @example\n * // キャッシュを細かく指定する場合\n * const cms = createCMS({\n * dataSources,\n * cache: { document, image, ttlMs: 60_000 },\n * });\n */\nexport function createCMS<D extends DataSourceMap>(\n\topts: CreateCMSOptions<D>,\n): CMSClient<D> {\n\tif (!opts.dataSources || Object.keys(opts.dataSources).length === 0) {\n\t\tthrow new CMSError({\n\t\t\tcode: \"core/config_invalid\",\n\t\t\tmessage:\n\t\t\t\t\"createCMS: dataSources に少なくとも1つのコレクションを指定してください。\",\n\t\t\tcontext: { operation: \"createCMS\" },\n\t\t});\n\t}\n\n\t// collections が指定されたコレクションは slug が必須。\n\tfor (const [name, col] of Object.entries(opts.collections ?? {})) {\n\t\tconst c = col as CollectionSemantics | undefined;\n\t\tif (c && !c.slug) {\n\t\t\tthrow new CMSError({\n\t\t\t\tcode: \"core/config_invalid\",\n\t\t\t\tmessage: `createCMS: コレクション \"${name}\" の collections.slug は必須です。slug として使うフィールド名を指定してください。`,\n\t\t\t\tcontext: { operation: \"createCMS\", collection: name },\n\t\t\t});\n\t\t}\n\t}\n\n\tconst resolved = resolvePreset(opts);\n\n\tconst baseDocCache = resolveDocumentCache(resolved.cache);\n\tconst imgCache = resolveImageCache(resolved.cache);\n\tconst hasImageCache = hasImageCacheConfigured(resolved.cache);\n\tconst ttlMs = resolveTtl(resolved.cache);\n\tconst imageProxyBase =\n\t\topts.content?.imageProxyBase ?? DEFAULT_IMAGE_PROXY_BASE;\n\tconst contentConfig = opts.content;\n\tconst rendererFn: RendererFn | undefined = resolved.renderer;\n\tconst waitUntil = opts.waitUntil;\n\tconst baseLogger: Logger | undefined = mergeLoggers(\n\t\topts.plugins ?? [],\n\t\topts.logger,\n\t);\n\tconst logger = opts.logLevel\n\t\t? applyLogLevel(baseLogger, opts.logLevel)\n\t\t: baseLogger;\n\tconst hooks: CMSHooks<BaseContentItem> = mergeHooks(\n\t\topts.plugins ?? [],\n\t\topts.hooks,\n\t\tlogger,\n\t);\n\tconst maxConcurrent = opts.rateLimiter?.maxConcurrent ?? 3;\n\tconst retryConfig: RetryConfig = {\n\t\t...DEFAULT_RETRY_CONFIG,\n\t\t...(opts.rateLimiter ?? {}),\n\t};\n\n\tconst collectionNames = Object.keys(opts.dataSources) as (keyof D & string)[];\n\n\t// biome-ignore lint/suspicious/noExplicitAny: 各 T を保持\n\tconst collections: Record<string, CollectionClient<any>> = {};\n\t// biome-ignore lint/suspicious/noExplicitAny: 横断的に利用\n\tconst scopedCaches: DocumentCacheAdapter<any>[] = [];\n\tfor (const name of collectionNames) {\n\t\tconst source = opts.dataSources[name] as DataSource<BaseContentItem>;\n\t\tconst scopedCache = scopeDocumentCache<BaseContentItem>(baseDocCache, name);\n\t\tscopedCaches.push(scopedCache);\n\t\tconst col = opts.collections?.[name] as CollectionSemantics | undefined;\n\t\tconst colHooks = col?.hooks as CMSHooks<BaseContentItem> | undefined;\n\t\tconst collectionHooks: CMSHooks<BaseContentItem> = colHooks\n\t\t\t? mergeHooks([{ name: `${name}:global`, hooks }], colHooks, logger)\n\t\t\t: hooks;\n\t\tconst renderCtx: RenderContext<BaseContentItem> = {\n\t\t\tsource,\n\t\t\trendererFn,\n\t\t\timgCache,\n\t\t\thasImageCache,\n\t\t\timageProxyBase,\n\t\t\tcontentConfig,\n\t\t\thooks: collectionHooks,\n\t\t\tlogger,\n\t\t};\n\t\tconst ctx: CollectionContext<BaseContentItem> = {\n\t\t\tcollection: name,\n\t\t\tsource,\n\t\t\tdocCache: scopedCache,\n\t\t\trender: renderCtx,\n\t\t\thooks: collectionHooks,\n\t\t\tlogger,\n\t\t\tttlMs,\n\t\t\t// 公開条件は CollectionSemantics(createCMS の collections オプション)が権威\n\t\t\tpublishedStatuses: col?.publishedStatuses\n\t\t\t\t? [...col.publishedStatuses]\n\t\t\t\t: [],\n\t\t\taccessibleStatuses: col?.accessibleStatuses\n\t\t\t\t? [...col.accessibleStatuses]\n\t\t\t\t: [],\n\t\t\tretryConfig,\n\t\t\tmaxConcurrent,\n\t\t\twaitUntil,\n\t\t\tslugField: col?.slug,\n\t\t};\n\t\tcollections[name] = new CollectionClientImpl(ctx);\n\t}\n\n\tconst globalOps: CMSGlobalOps<D> = {\n\t\t$collections: collectionNames,\n\t\tasync $revalidate(scope?: InvalidateScope): Promise<void> {\n\t\t\tlogger?.debug?.(\"グローバルキャッシュを無効化\", {\n\t\t\t\toperation: \"$revalidate\",\n\t\t\t\tcacheAdapter: baseDocCache.name,\n\t\t\t});\n\t\t\t// baseDocCache を直接呼ばず各スコープキャッシュ経由で呼ぶ。\n\t\t\t// 直接呼ぶと scopeDocumentCache の listSlot がクリアされず stale になる。\n\t\t\tfor (const cache of scopedCaches) {\n\t\t\t\tif (!cache.invalidate) continue;\n\t\t\t\tawait cache.invalidate(scope ?? \"all\");\n\t\t\t}\n\t\t},\n\t\t$handler(handlerOpts?: HandlerOptions) {\n\t\t\treturn createHandler(\n\t\t\t\t{\n\t\t\t\t\timageCache: imgCache,\n\t\t\t\t\tparseWebhook: async (req, webhookSecret) => {\n\t\t\t\t\t\t// 各 DataSource の parseWebhook を順に試す\n\t\t\t\t\t\tfor (const name of collectionNames) {\n\t\t\t\t\t\t\tconst ds = opts.dataSources[name] as DataSource<BaseContentItem>;\n\t\t\t\t\t\t\tif (ds.parseWebhook) {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tconst scope = await ds.parseWebhook(req.clone(), {\n\t\t\t\t\t\t\t\t\t\tsecret: webhookSecret,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\treturn scope;\n\t\t\t\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t\t\t\tlogger?.warn?.(\"parseWebhook 失敗\", {\n\t\t\t\t\t\t\t\t\t\tcollection: name,\n\t\t\t\t\t\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// フォールバック: { slug } だけの汎用 JSON body\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst body = (await req.json()) as {\n\t\t\t\t\t\t\t\tslug?: string;\n\t\t\t\t\t\t\t\tcollection?: string;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tif (body.slug && body.collection) {\n\t\t\t\t\t\t\t\treturn { collection: body.collection, slug: body.slug };\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (body.collection) {\n\t\t\t\t\t\t\t\treturn { collection: body.collection };\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// ignore\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t},\n\t\t\t\t\trevalidate: (scope) => globalOps.$revalidate(scope),\n\t\t\t\t},\n\t\t\t\thandlerOpts,\n\t\t\t);\n\t\t},\n\t\t$getCachedImage(hash) {\n\t\t\treturn imgCache.get(hash);\n\t\t},\n\t};\n\n\treturn Object.assign(\n\t\tObject.create(null) as object,\n\t\tcollections,\n\t\tglobalOps,\n\t) as CMSClient<D>;\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\tname: string;\n\thooks?: CMSHooks<T>;\n\tlogger?: Partial<Logger>;\n}\n\nexport function definePlugin<T extends BaseContentItem>(\n\tplugin: CMSPlugin<T>,\n): CMSPlugin<T> {\n\treturn plugin;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/cache.ts","../src/image.ts","../src/rendering.ts","../src/retry.ts","../src/collection.ts","../src/handler.ts","../src/preset-node.ts","../src/cms.ts","../src/types/plugin.ts"],"sourcesContent":["/** 文字列をSHA-256でハッシュ化し、16進数文字列として返す。画像キーの生成に使用。 */\nexport async function sha256Hex(input: string): Promise<string> {\n\tconst data = new TextEncoder().encode(input);\n\tconst hash = await crypto.subtle.digest(\"SHA-256\", data);\n\treturn Array.from(new Uint8Array(hash))\n\t\t.map((b) => b.toString(16).padStart(2, \"0\"))\n\t\t.join(\"\");\n}\n\n/**\n * キャッシュが有効期限切れかどうかを判定する。\n * ttlMs が未指定の場合は常に false(無期限有効)を返す。\n */\nexport function isStale(cachedAt: number, ttlMs?: number): boolean {\n\tif (ttlMs === undefined) return false;\n\treturn Date.now() - cachedAt > ttlMs;\n}\n","import { sha256Hex } from \"./cache\";\nimport { CMSError, isCMSError } from \"./errors\";\nimport type { ImageCacheAdapter, Logger, StorageBinary } from \"./types/index\";\n\n/** レスポンスヘッダまたはURLの拡張子からContent-Typeを推測する。 */\nfunction inferContentType(\n\turl: string,\n\tresponseContentType: string | null,\n): string {\n\tif (responseContentType?.startsWith(\"image/\")) {\n\t\treturn responseContentType.split(\";\")[0].trim();\n\t}\n\tif (url.includes(\".png\")) return \"image/png\";\n\tif (url.includes(\".gif\")) return \"image/gif\";\n\tif (url.includes(\".webp\")) return \"image/webp\";\n\treturn \"image/jpeg\";\n}\n\n/**\n * Notion画像URLをfetchしてImageCacheAdapterにキャッシュし、プロキシURL を返す。\n * 既存キャッシュがあれば再fetchしない。\n */\nasync function fetchAndCacheImage(\n\tcache: ImageCacheAdapter,\n\tnotionUrl: string,\n\timageProxyBase: string,\n\tlogger?: Logger,\n): Promise<string> {\n\tconst hash = await sha256Hex(notionUrl);\n\tconst proxyUrl = `${imageProxyBase}/${hash}`;\n\n\tconst existing = await cache.get(hash);\n\tif (existing) {\n\t\tlogger?.debug?.(\"画像キャッシュヒット\", {\n\t\t\toperation: \"fetchAndCacheImage\",\n\t\t\tcacheAdapter: cache.name,\n\t\t\timageHash: hash,\n\t\t});\n\t\treturn proxyUrl;\n\t}\n\n\tlogger?.debug?.(\"画像キャッシュミス、Notion からフェッチ\", {\n\t\toperation: \"fetchAndCacheImage\",\n\t\tcacheAdapter: cache.name,\n\t\timageHash: hash,\n\t});\n\n\ttry {\n\t\tconst response = await fetch(notionUrl, {\n\t\t\tsignal: AbortSignal.timeout(10_000),\n\t\t});\n\t\tif (!response.ok) {\n\t\t\tthrow new CMSError({\n\t\t\t\tcode: \"cache/image_fetch_failed\",\n\t\t\t\tmessage: `Failed to fetch Notion image: HTTP ${response.status}`,\n\t\t\t\tcontext: {\n\t\t\t\t\toperation: \"fetchAndCacheImage\",\n\t\t\t\t\tnotionUrl,\n\t\t\t\t\thttpStatus: response.status,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\tconst data = await response.arrayBuffer();\n\t\tconst contentType = inferContentType(\n\t\t\tnotionUrl,\n\t\t\tresponse.headers.get(\"content-type\"),\n\t\t);\n\t\tawait cache.set(hash, data, contentType);\n\t\tlogger?.debug?.(\"画像をキャッシュに保存\", {\n\t\t\toperation: \"fetchAndCacheImage\",\n\t\t\tcacheAdapter: cache.name,\n\t\t\timageHash: hash,\n\t\t});\n\t} catch (err) {\n\t\tif (isCMSError(err)) throw err;\n\t\tthrow new CMSError({\n\t\t\tcode: \"cache/io_failed\",\n\t\t\tmessage: \"Failed to fetch or cache Notion image.\",\n\t\t\tcause: err,\n\t\t\tcontext: { operation: \"fetchAndCacheImage\", notionUrl },\n\t\t});\n\t}\n\n\treturn proxyUrl;\n}\n\n/**\n * ImageCacheAdapter と imageProxyBase から cacheImage 関数を構築するファクトリ。\n *\n * 返り値の関数は Notion 画像 URL を受け取り、SHA-256 ハッシュをキャッシュキーとして\n * {@link ImageCacheAdapter} に保存後、フロントエンドへの配信用プロキシ URL を返す。\n * Content-Type はレスポンスヘッダまたは URL の拡張子から推測する。\n * タイムアウトは 10 秒固定。\n */\nexport function buildCacheImageFn(\n\tcache: ImageCacheAdapter,\n\timageProxyBase: string,\n\tlogger?: Logger,\n): (notionUrl: string) => Promise<string> {\n\treturn (notionUrl) =>\n\t\tfetchAndCacheImage(cache, notionUrl, imageProxyBase, logger);\n}\n\nexport type { StorageBinary };\n","import type { ContentBlock } from \"./content/blocks\";\nimport { CMSError, isCMSError } from \"./errors\";\nimport { buildCacheImageFn } from \"./image\";\nimport type {\n\tBaseContentItem,\n\tCachedItem,\n\tCMSHooks,\n\tContentConfig,\n\tDataSource,\n\tImageCacheAdapter,\n\tLogger,\n\tRendererFn,\n} from \"./types/index\";\n\n/** `buildCachedItem` に必要な CMS の依存を束ねたコンテキスト。 */\nexport interface RenderContext<T extends BaseContentItem> {\n\tsource: DataSource<T>;\n\trendererFn: RendererFn | undefined;\n\timgCache: ImageCacheAdapter;\n\thasImageCache: boolean;\n\timageProxyBase: string;\n\tcontentConfig: ContentConfig | undefined;\n\thooks: CMSHooks<T>;\n\tlogger: Logger | undefined;\n}\n\n/**\n * コンテンツアイテムをソースから Markdown ロード → blocks 生成 → HTML レンダリング\n * → フック適用まで実行し、キャッシュ保存用の `CachedItem` を返す。\n */\nexport async function buildCachedItem<T extends BaseContentItem>(\n\titem: T,\n\tctx: RenderContext<T>,\n): Promise<CachedItem<T>> {\n\tconst start = Date.now();\n\tctx.logger?.info?.(\"コンテンツのレンダリング開始\", {\n\t\tslug: item.slug,\n\t\tpageId: item.id,\n\t});\n\tctx.hooks.onRenderStart?.(item.slug);\n\n\tlet markdown: string;\n\ttry {\n\t\tmarkdown = await ctx.source.loadMarkdown(item);\n\t} catch (err) {\n\t\tif (isCMSError(err)) throw err;\n\t\tthrow new CMSError({\n\t\t\tcode: \"source/load_markdown_failed\",\n\t\t\tmessage: \"Failed to load markdown from source.\",\n\t\t\tcause: err,\n\t\t\tcontext: {\n\t\t\t\toperation: \"buildCachedItem:loadMarkdown\",\n\t\t\t\tpageId: item.id,\n\t\t\t\tslug: item.slug,\n\t\t\t},\n\t\t});\n\t}\n\n\tlet blocks: ContentBlock[] = [];\n\ttry {\n\t\tblocks = await ctx.source.loadBlocks(item);\n\t} catch (err) {\n\t\tctx.logger?.warn?.(\"loadBlocks に失敗したため raw フォールバック\", {\n\t\t\tslug: item.slug,\n\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t});\n\t\tblocks = [];\n\t}\n\n\tconst cacheImage = ctx.hasImageCache\n\t\t? buildCacheImageFn(ctx.imgCache, ctx.imageProxyBase, ctx.logger)\n\t\t: undefined;\n\n\tlet html: string;\n\tconst rendererFn = ctx.rendererFn ?? (await loadDefaultRenderer());\n\ttry {\n\t\thtml = await rendererFn(markdown, {\n\t\t\timageProxyBase: ctx.imageProxyBase,\n\t\t\tcacheImage,\n\t\t\tremarkPlugins: ctx.contentConfig?.remarkPlugins,\n\t\t\trehypePlugins: ctx.contentConfig?.rehypePlugins,\n\t\t});\n\t} catch (err) {\n\t\tif (isCMSError(err)) throw err;\n\t\tthrow new CMSError({\n\t\t\tcode: \"renderer/failed\",\n\t\t\tmessage: \"Failed to render markdown.\",\n\t\t\tcause: err,\n\t\t\tcontext: {\n\t\t\t\toperation: \"buildCachedItem:renderMarkdown\",\n\t\t\t\tpageId: item.id,\n\t\t\t\tslug: item.slug,\n\t\t\t},\n\t\t});\n\t}\n\n\tif (ctx.hooks.afterRender) {\n\t\thtml = await ctx.hooks.afterRender(html, item);\n\t}\n\n\tlet result: CachedItem<T> = {\n\t\thtml,\n\t\tblocks,\n\t\tmarkdown,\n\t\titem,\n\t\tnotionUpdatedAt: ctx.source.getLastModified(item),\n\t\tcachedAt: Date.now(),\n\t};\n\n\tif (ctx.hooks.beforeCache) {\n\t\tresult = (await ctx.hooks.beforeCache(result)) as CachedItem<T>;\n\t}\n\n\tconst durationMs = Date.now() - start;\n\tctx.logger?.info?.(\"コンテンツのレンダリング完了\", {\n\t\tslug: item.slug,\n\t\tdurationMs,\n\t});\n\tctx.hooks.onRenderEnd?.(item.slug, durationMs);\n\n\treturn result;\n}\n\n/**\n * renderer オプション未指定時のフォールバック。\n * @notion-headless-cms/renderer を動的 import する。\n * createCMS({ renderer }) で明示注入された場合はこのパスを通らない。\n */\nasync function loadDefaultRenderer(): Promise<RendererFn> {\n\ttry {\n\t\tconst mod = await import(\"@notion-headless-cms/renderer\");\n\t\treturn mod.renderMarkdown as RendererFn;\n\t} catch (err) {\n\t\tthrow new CMSError({\n\t\t\tcode: \"renderer/failed\",\n\t\t\tmessage:\n\t\t\t\t\"renderer オプションが未指定で @notion-headless-cms/renderer が見つかりません。\" +\n\t\t\t\t\" createCMS({ renderer }) でレンダラーを注入するか、@notion-headless-cms/renderer をインストールしてください。\",\n\t\t\tcause: err,\n\t\t\tcontext: { operation: \"loadDefaultRenderer\" },\n\t\t});\n\t}\n}\n","export interface RetryConfig {\n\tretryOn: number[];\n\tmaxRetries: number;\n\tbaseDelayMs: number;\n\t/** true のとき指数バックオフにランダムジッターを加える(Thundering Herd 対策)。デフォルト: true */\n\tjitter?: boolean;\n\tonRetry?: (attempt: number, status: number) => void;\n}\n\nexport const DEFAULT_RETRY_CONFIG: RetryConfig = {\n\tretryOn: [429, 502, 503],\n\tmaxRetries: 4,\n\tbaseDelayMs: 1000,\n\tjitter: 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\tfn: () => Promise<T>,\n\tconfig: RetryConfig,\n): Promise<T> {\n\tlet lastError: unknown;\n\tfor (let attempt = 0; attempt <= config.maxRetries; attempt++) {\n\t\ttry {\n\t\t\treturn await fn();\n\t\t} catch (err) {\n\t\t\tconst status = (err as { status?: number }).status;\n\t\t\tif (status === undefined || !config.retryOn.includes(status)) {\n\t\t\t\tthrow err;\n\t\t\t}\n\t\t\tlastError = err;\n\t\t\tif (attempt < config.maxRetries) {\n\t\t\t\tconfig.onRetry?.(attempt + 1, status);\n\t\t\t\tconst jitterFactor =\n\t\t\t\t\tconfig.jitter !== false ? 0.5 + Math.random() * 0.5 : 1;\n\t\t\t\tconst delay = config.baseDelayMs * 2 ** attempt * jitterFactor;\n\t\t\t\tawait new Promise((resolve) => setTimeout(resolve, delay));\n\t\t\t}\n\t\t}\n\t}\n\tthrow lastError;\n}\n","import { isStale } from \"./cache\";\nimport type { ContentBlock, ContentResult } from \"./content/blocks\";\nimport type { RenderContext } from \"./rendering\";\nimport { buildCachedItem } from \"./rendering\";\nimport type { RetryConfig } from \"./retry\";\nimport { withRetry } from \"./retry\";\nimport type {\n\tAdjacencyOptions,\n\tBaseContentItem,\n\tCachedItem,\n\tCachedItemList,\n\tCheckForUpdateResult,\n\tCheckListForUpdateResult,\n\tCMSHooks,\n\tCollectionClient,\n\tDataSource,\n\tDocumentCacheAdapter,\n\tGetListOptions,\n\tGetListResult,\n\tItemWithContent,\n\tLogger,\n\tSortOption,\n} from \"./types/index\";\n\n/**\n * コレクション別キャッシュキーを生成する。\n * item: `{collection}:{slug}` / list: `{collection}`\n */\nexport function collectionKey(collection: string, slug?: string): string {\n\treturn slug ? `${collection}:${slug}` : collection;\n}\n\n/** 単一コレクションの DataSource + SWR キャッシュ依存を束ねたコンテキスト。 */\nexport interface CollectionContext<T extends BaseContentItem> {\n\tcollection: string;\n\tsource: DataSource<T>;\n\tdocCache: DocumentCacheAdapter<T>;\n\trender: RenderContext<T>;\n\thooks: CMSHooks<T>;\n\tlogger: Logger | undefined;\n\tttlMs: number | undefined;\n\tpublishedStatuses: string[];\n\taccessibleStatuses: string[];\n\tretryConfig: RetryConfig;\n\tmaxConcurrent: number;\n\twaitUntil: ((p: Promise<unknown>) => void) | undefined;\n\t/**\n\t * slug として使うフィールド名。\n\t * `createCMS({ collections })` で指定した値。\n\t * 設定時は `source.properties[slugField].notion` を Notion プロパティ名として\n\t * `findByProp` を呼び出す。\n\t */\n\tslugField?: string;\n}\n\n/** CollectionClient の実装。ユーザーは `createCMS` 経由でインスタンスを受け取る。 */\nexport class CollectionClientImpl<T extends BaseContentItem>\n\timplements CollectionClient<T>\n{\n\tconstructor(private readonly ctx: CollectionContext<T>) {}\n\n\t// ── 基本取得 ──────────────────────────────────────────────────────────\n\n\tasync getItem(slug: string): Promise<ItemWithContent<T> | null> {\n\t\tconst cached = await this.ctx.docCache.getItem(slug);\n\t\tif (cached) {\n\t\t\tif (\n\t\t\t\tthis.ctx.ttlMs !== undefined &&\n\t\t\t\tisStale(cached.cachedAt, this.ctx.ttlMs)\n\t\t\t) {\n\t\t\t\t// TTL 設定あり + 期限切れ: ブロッキングフェッチ(stale を返さない)\n\t\t\t\tthis.ctx.logger?.debug?.(\"キャッシュ期限切れ(TTL)、フェッチ\", {\n\t\t\t\t\toperation: \"getItem\",\n\t\t\t\t\tslug,\n\t\t\t\t\tcollection: this.ctx.collection,\n\t\t\t\t\tcacheAdapter: this.ctx.docCache.name,\n\t\t\t\t});\n\t\t\t\tthis.ctx.hooks.onCacheMiss?.(slug);\n\t\t\t\tconst item = await this.findRaw(slug);\n\t\t\t\tif (!item) return null;\n\t\t\t\tconst entry = await buildCachedItem(item, this.ctx.render);\n\t\t\t\tawait this.ctx.docCache.setItem(slug, entry);\n\t\t\t\treturn this.attachContent(entry.item, entry);\n\t\t\t}\n\t\t\t// TTL 未設定 or 期限内: キャッシュを即時返却し、バックグラウンドで差分チェック\n\t\t\tconst bg = this.checkAndUpdateItemBg(slug, cached);\n\t\t\tif (this.ctx.waitUntil) {\n\t\t\t\tthis.ctx.waitUntil(bg);\n\t\t\t}\n\t\t\tthis.ctx.logger?.debug?.(\"キャッシュヒット\", {\n\t\t\t\toperation: \"getItem\",\n\t\t\t\tslug,\n\t\t\t\tcollection: this.ctx.collection,\n\t\t\t\tcacheAdapter: this.ctx.docCache.name,\n\t\t\t\tcachedAt: cached.cachedAt,\n\t\t\t});\n\t\t\tthis.ctx.hooks.onCacheHit?.(slug, cached);\n\t\t\treturn this.attachContent(cached.item, cached);\n\t\t}\n\n\t\t// キャッシュなし: 同期フェッチ\n\t\tthis.ctx.logger?.debug?.(\"キャッシュミス、フェッチ\", {\n\t\t\toperation: \"getItem\",\n\t\t\tslug,\n\t\t\tcollection: this.ctx.collection,\n\t\t\tcacheAdapter: this.ctx.docCache.name,\n\t\t});\n\t\tthis.ctx.hooks.onCacheMiss?.(slug);\n\t\tconst item = await this.findRaw(slug);\n\t\tif (!item) {\n\t\t\tthis.ctx.logger?.debug?.(\"アイテムが見つかりません\", {\n\t\t\t\toperation: \"getItem\",\n\t\t\t\tslug,\n\t\t\t\tcollection: this.ctx.collection,\n\t\t\t});\n\t\t\treturn null;\n\t\t}\n\n\t\tconst entry = await buildCachedItem(item, this.ctx.render);\n\t\tconst save = this.ctx.docCache.setItem(slug, entry);\n\t\tif (this.ctx.waitUntil) {\n\t\t\tthis.ctx.waitUntil(save);\n\t\t} else {\n\t\t\tawait save;\n\t\t}\n\n\t\treturn this.attachContent(entry.item, entry);\n\t}\n\n\tasync getList(opts?: GetListOptions<T>): Promise<GetListResult<T>> {\n\t\tconst allItems = await this.fetchList();\n\t\tconst items = applyGetListOptions(allItems, opts);\n\t\tconst version = this.ctx.source.getListVersion(items);\n\t\treturn { items, version };\n\t}\n\n\t// ── SSG / ナビゲーション ─────────────────────────────────────────────\n\n\tasync getStaticParams(): Promise<{ slug: string }[]> {\n\t\tconst items = await this.fetchList();\n\t\treturn items.map((item) => ({ slug: item.slug }));\n\t}\n\n\tasync getStaticPaths(): Promise<string[]> {\n\t\tconst items = await this.fetchList();\n\t\treturn items.map((item) => item.slug);\n\t}\n\n\tasync adjacent(\n\t\tslug: string,\n\t\topts?: AdjacencyOptions<T>,\n\t): Promise<{ prev: T | null; next: T | null }> {\n\t\tconst items = applyGetListOptions(await this.fetchList(), {\n\t\t\tsort: opts?.sort,\n\t\t});\n\t\tconst index = items.findIndex((it) => it.slug === slug);\n\t\tif (index === -1) return { prev: null, next: null };\n\t\treturn {\n\t\t\tprev: index > 0 ? (items[index - 1] ?? null) : null,\n\t\t\tnext: index < items.length - 1 ? (items[index + 1] ?? null) : null,\n\t\t};\n\t}\n\n\t// ── キャッシュ ────────────────────────────────────────────────────────\n\n\tasync revalidate(slug: string): Promise<void> {\n\t\tthis.ctx.logger?.debug?.(\"アイテムキャッシュを無効化\", {\n\t\t\toperation: \"revalidate\",\n\t\t\tcollection: this.ctx.collection,\n\t\t\tcacheAdapter: this.ctx.docCache.name,\n\t\t\tslug,\n\t\t});\n\t\tif (!this.ctx.docCache.invalidate) return;\n\t\tawait this.ctx.docCache.invalidate({\n\t\t\tcollection: this.ctx.collection,\n\t\t\tslug,\n\t\t});\n\t}\n\n\tasync revalidateAll(): Promise<void> {\n\t\tthis.ctx.logger?.debug?.(\"コレクション全体のキャッシュを無効化\", {\n\t\t\toperation: \"revalidateAll\",\n\t\t\tcollection: this.ctx.collection,\n\t\t\tcacheAdapter: this.ctx.docCache.name,\n\t\t});\n\t\tif (!this.ctx.docCache.invalidate) return;\n\t\tawait this.ctx.docCache.invalidate({ collection: this.ctx.collection });\n\t}\n\n\tasync checkForUpdate({\n\t\tslug,\n\t\tsince,\n\t}: {\n\t\tslug: string;\n\t\tsince: string;\n\t}): Promise<CheckForUpdateResult<T>> {\n\t\tawait this.revalidate(slug);\n\t\tconst item = await this.getItem(slug);\n\t\tif (!item) return { changed: false };\n\t\treturn item.updatedAt !== since\n\t\t\t? { changed: true, item }\n\t\t\t: { changed: false };\n\t}\n\n\tasync checkListForUpdate({\n\t\tsince,\n\t\tfilter,\n\t}: {\n\t\tsince: string;\n\t\tfilter?: GetListOptions<T>;\n\t}): Promise<CheckListForUpdateResult<T>> {\n\t\tawait this.revalidateAll();\n\t\tconst { items, version } = await this.getList(filter);\n\t\treturn version !== since\n\t\t\t? { changed: true, items, version }\n\t\t\t: { changed: false };\n\t}\n\n\tasync prefetch(opts?: {\n\t\tconcurrency?: number;\n\t\tonProgress?: (done: number, total: number) => void;\n\t}): Promise<{ ok: number; failed: number }> {\n\t\tconst items = await this.fetchListRaw();\n\t\tconst concurrency = opts?.concurrency ?? this.ctx.maxConcurrent;\n\t\tlet ok = 0;\n\t\tlet failed = 0;\n\n\t\tfor (let i = 0; i < items.length; i += concurrency) {\n\t\t\tconst chunk = items.slice(i, i + concurrency);\n\t\t\tawait Promise.all(\n\t\t\t\tchunk.map(async (item) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst rendered = await buildCachedItem(item, this.ctx.render);\n\t\t\t\t\t\tawait this.ctx.docCache.setItem(item.slug, rendered);\n\t\t\t\t\t\tok++;\n\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\tfailed++;\n\t\t\t\t\t\tthis.ctx.logger?.warn?.(\n\t\t\t\t\t\t\t\"prefetch: アイテムの事前レンダリングに失敗\",\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tslug: item.slug,\n\t\t\t\t\t\t\t\tpageId: item.id,\n\t\t\t\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}),\n\t\t\t);\n\t\t\topts?.onProgress?.(Math.min(i + concurrency, items.length), items.length);\n\t\t}\n\n\t\tawait this.ctx.docCache.setList({ items, cachedAt: Date.now() });\n\t\treturn { ok, failed };\n\t}\n\n\t// ── 内部 ──────────────────────────────────────────────────────────────\n\n\tprivate attachContent(item: T, cached: CachedItem<T>): ItemWithContent<T> {\n\t\tconst ctx = this.ctx;\n\t\tlet blocksCache: ContentBlock[] | undefined;\n\t\tlet htmlCache: string | undefined = cached.html;\n\t\tlet markdownCache: string | undefined;\n\n\t\tconst content: ContentResult = {\n\t\t\tget blocks(): ContentBlock[] {\n\t\t\t\tif (!blocksCache) {\n\t\t\t\t\t// lazy: キャッシュに blocks が保存されていない場合、簡易な single-raw に落とす\n\t\t\t\t\tblocksCache = [\n\t\t\t\t\t\t{ type: \"raw\", html: cached.html } satisfies ContentBlock,\n\t\t\t\t\t];\n\t\t\t\t}\n\t\t\t\treturn blocksCache;\n\t\t\t},\n\t\t\tasync html(): Promise<string> {\n\t\t\t\tif (htmlCache !== undefined) return htmlCache;\n\t\t\t\thtmlCache = cached.html;\n\t\t\t\treturn htmlCache;\n\t\t\t},\n\t\t\tasync markdown(): Promise<string> {\n\t\t\t\tif (markdownCache !== undefined) return markdownCache;\n\t\t\t\tmarkdownCache = await ctx.source.loadMarkdown(item);\n\t\t\t\treturn markdownCache;\n\t\t\t},\n\t\t};\n\n\t\tif (cached.blocks) blocksCache = cached.blocks;\n\n\t\treturn Object.assign(Object.create(null) as object, item, {\n\t\t\tcontent,\n\t\t}) as ItemWithContent<T>;\n\t}\n\n\tprivate async fetchList(): Promise<T[]> {\n\t\tconst cached = await this.ctx.docCache.getList();\n\t\tif (cached) {\n\t\t\tif (\n\t\t\t\tthis.ctx.ttlMs !== undefined &&\n\t\t\t\tisStale(cached.cachedAt, this.ctx.ttlMs)\n\t\t\t) {\n\t\t\t\t// TTL 設定あり + 期限切れ: ブロッキングフェッチ(stale を返さない)\n\t\t\t\tthis.ctx.logger?.debug?.(\"リストキャッシュ期限切れ(TTL)、フェッチ\", {\n\t\t\t\t\toperation: \"getList\",\n\t\t\t\t\tcollection: this.ctx.collection,\n\t\t\t\t\tcacheAdapter: this.ctx.docCache.name,\n\t\t\t\t});\n\t\t\t\tthis.ctx.hooks.onListCacheMiss?.();\n\t\t\t\tconst items = await this.fetchListRaw();\n\t\t\t\tawait this.ctx.docCache.setList({ items, cachedAt: Date.now() });\n\t\t\t\treturn items;\n\t\t\t}\n\t\t\t// TTL 未設定 or 期限内: キャッシュを即時返却し、バックグラウンドで差分チェック\n\t\t\tconst bg = this.checkAndUpdateListBg(cached);\n\t\t\tif (this.ctx.waitUntil) {\n\t\t\t\tthis.ctx.waitUntil(bg);\n\t\t\t}\n\t\t\tthis.ctx.logger?.debug?.(\"リストキャッシュヒット\", {\n\t\t\t\toperation: \"getList\",\n\t\t\t\tcollection: this.ctx.collection,\n\t\t\t\tcacheAdapter: this.ctx.docCache.name,\n\t\t\t});\n\t\t\tthis.ctx.hooks.onListCacheHit?.(cached);\n\t\t\treturn cached.items;\n\t\t}\n\n\t\t// キャッシュなし: 同期フェッチ\n\t\tthis.ctx.logger?.debug?.(\"リストキャッシュミス、フェッチ\", {\n\t\t\toperation: \"getList\",\n\t\t\tcollection: this.ctx.collection,\n\t\t\tcacheAdapter: this.ctx.docCache.name,\n\t\t});\n\t\tthis.ctx.hooks.onListCacheMiss?.();\n\t\tconst items = await this.fetchListRaw();\n\t\tconst cachedAt = Date.now();\n\t\tconst save = this.ctx.docCache.setList({ items, cachedAt });\n\t\tif (this.ctx.waitUntil) {\n\t\t\tthis.ctx.waitUntil(save);\n\t\t} else {\n\t\t\tawait save;\n\t\t}\n\t\treturn items;\n\t}\n\n\tprivate async checkAndUpdateItemBg(\n\t\tslug: string,\n\t\tcached: CachedItem<T>,\n\t): Promise<void> {\n\t\ttry {\n\t\t\tconst item = await this.findRaw(slug);\n\t\t\tif (!item) return;\n\t\t\tif (this.ctx.source.getLastModified(item) !== cached.notionUpdatedAt) {\n\t\t\t\t// 更新あり: 再レンダリングしてキャッシュを差し替える\n\t\t\t\tconst entry = await buildCachedItem(item, this.ctx.render);\n\t\t\t\tawait this.ctx.docCache.setItem(slug, entry);\n\t\t\t\tthis.ctx.logger?.debug?.(\"SWR: 差分を検出、キャッシュを差し替え\", {\n\t\t\t\t\toperation: \"getItem:bg\",\n\t\t\t\t\tslug,\n\t\t\t\t\tcollection: this.ctx.collection,\n\t\t\t\t\tnotionUpdatedAt: cached.notionUpdatedAt,\n\t\t\t\t});\n\t\t\t\tthis.ctx.hooks.onCacheRevalidated?.(slug, entry);\n\t\t\t} else if (this.ctx.ttlMs !== undefined) {\n\t\t\t\t// 変更なし + TTL あり: cachedAt をリセットして次回の期限切れを先送りする\n\t\t\t\tawait this.ctx.docCache.setItem(slug, {\n\t\t\t\t\t...cached,\n\t\t\t\t\tcachedAt: Date.now(),\n\t\t\t\t});\n\t\t\t\tthis.ctx.logger?.debug?.(\"SWR: 差分なし、TTL をリセット\", {\n\t\t\t\t\toperation: \"getItem:bg\",\n\t\t\t\t\tslug,\n\t\t\t\t\tcollection: this.ctx.collection,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tthis.ctx.logger?.warn?.(\n\t\t\t\t\"SWR: アイテムのバックグラウンド差分チェックに失敗\",\n\t\t\t\t{\n\t\t\t\t\tslug,\n\t\t\t\t\tcollection: this.ctx.collection,\n\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate async checkAndUpdateListBg(cached: CachedItemList<T>): Promise<void> {\n\t\ttry {\n\t\t\tconst items = await this.fetchListRaw();\n\t\t\tif (\n\t\t\t\tthis.ctx.source.getListVersion(items) !==\n\t\t\t\tthis.ctx.source.getListVersion(cached.items)\n\t\t\t) {\n\t\t\t\t// 更新あり: リストを差し替える\n\t\t\t\tconst listEntry = { items, cachedAt: Date.now() };\n\t\t\t\tawait this.ctx.docCache.setList(listEntry);\n\t\t\t\tthis.ctx.logger?.debug?.(\n\t\t\t\t\t\"SWR: リスト差分を検出、キャッシュを差し替え\",\n\t\t\t\t\t{\n\t\t\t\t\t\toperation: \"getList:bg\",\n\t\t\t\t\t\tcollection: this.ctx.collection,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\tthis.ctx.hooks.onListCacheRevalidated?.(listEntry);\n\t\t\t} else if (this.ctx.ttlMs !== undefined) {\n\t\t\t\t// 変更なし + TTL あり: cachedAt をリセットする\n\t\t\t\tawait this.ctx.docCache.setList({\n\t\t\t\t\t...cached,\n\t\t\t\t\tcachedAt: Date.now(),\n\t\t\t\t});\n\t\t\t\tthis.ctx.logger?.debug?.(\"SWR: リスト差分なし、TTL をリセット\", {\n\t\t\t\t\toperation: \"getList:bg\",\n\t\t\t\t\tcollection: this.ctx.collection,\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tthis.ctx.logger?.warn?.(\n\t\t\t\t\"SWR: リストのバックグラウンド差分チェックに失敗\",\n\t\t\t\t{\n\t\t\t\t\tcollection: this.ctx.collection,\n\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t}\n\n\tprivate fetchListRaw(): Promise<T[]> {\n\t\treturn withRetry(\n\t\t\t() =>\n\t\t\t\tthis.ctx.source.list({\n\t\t\t\t\tpublishedStatuses:\n\t\t\t\t\t\tthis.ctx.publishedStatuses.length > 0\n\t\t\t\t\t\t\t? this.ctx.publishedStatuses\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t}),\n\t\t\t{\n\t\t\t\t...this.ctx.retryConfig,\n\t\t\t\tonRetry: (attempt, status) => {\n\t\t\t\t\tthis.ctx.logger?.warn?.(\"getList() リトライ中\", { attempt, status });\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t}\n\n\tprivate async findRaw(slug: string): Promise<T | null> {\n\t\tconst retryOpts = {\n\t\t\t...this.ctx.retryConfig,\n\t\t\tonRetry: (attempt: number, status: number) => {\n\t\t\t\tthis.ctx.logger?.warn?.(\"getItem() リトライ中\", {\n\t\t\t\t\tattempt,\n\t\t\t\t\tstatus,\n\t\t\t\t\tslug,\n\t\t\t\t});\n\t\t\t},\n\t\t};\n\n\t\t// slug フィールドが指定され、DataSource が findByProp を持つ場合は\n\t\t// Notion プロパティ名を解決して効率的なフィルタクエリを実行する。\n\t\tconst slugField = this.ctx.slugField;\n\t\tconst notionPropName = slugField\n\t\t\t? this.ctx.source.properties?.[slugField]?.notion\n\t\t\t: undefined;\n\n\t\tlet item: T | null;\n\t\tconst findByProp = this.ctx.source.findByProp?.bind(this.ctx.source);\n\t\tif (notionPropName && findByProp) {\n\t\t\titem = await withRetry(() => findByProp(notionPropName, slug), retryOpts);\n\t\t} else {\n\t\t\t// フォールバック: list して線形探索\n\t\t\tconst all = await withRetry(() => this.ctx.source.list(), retryOpts);\n\t\t\titem = all.find((i) => i.slug === slug) ?? null;\n\t\t}\n\n\t\tif (!item) return null;\n\t\tif (\n\t\t\tthis.ctx.accessibleStatuses.length > 0 &&\n\t\t\t(!item.status || !this.ctx.accessibleStatuses.includes(item.status))\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\t\treturn item;\n\t}\n}\n\nfunction applyGetListOptions<T extends BaseContentItem>(\n\titems: T[],\n\topts?: GetListOptions<T>,\n): T[] {\n\tif (!opts) return items;\n\tlet result = items;\n\n\tif (opts.statuses && opts.statuses.length > 0) {\n\t\tconst allow = new Set(opts.statuses);\n\t\tresult = result.filter(\n\t\t\t(it) => it.status !== undefined && allow.has(it.status),\n\t\t);\n\t}\n\n\tif (opts.tag) {\n\t\tconst tag = opts.tag;\n\t\tresult = result.filter((it) => {\n\t\t\tconst tags = (it as unknown as { tags?: unknown }).tags;\n\t\t\treturn Array.isArray(tags) && tags.includes(tag);\n\t\t});\n\t}\n\n\tif (opts.where) {\n\t\tconst where = opts.where;\n\t\tresult = result.filter((it) =>\n\t\t\tObject.entries(where).every(\n\t\t\t\t([key, value]) => (it as Record<string, unknown>)[key] === value,\n\t\t\t),\n\t\t);\n\t}\n\n\tif (opts.sort) {\n\t\tresult = [...result].sort(makeComparator(opts.sort));\n\t}\n\n\tconst skip = opts.skip ?? 0;\n\tconst limit = opts.limit;\n\tif (skip > 0 || limit !== undefined) {\n\t\tresult = result.slice(skip, limit !== undefined ? skip + limit : undefined);\n\t}\n\n\treturn result;\n}\n\nfunction makeComparator<T extends BaseContentItem>(\n\tsort: SortOption<T>,\n): (a: T, b: T) => number {\n\tconst by = sort.by;\n\tconst dir = sort.direction === \"asc\" ? 1 : -1;\n\treturn (a, b) => {\n\t\tconst av = (a as Record<string, unknown>)[by];\n\t\tconst bv = (b as Record<string, unknown>)[by];\n\t\tif (av === bv) return 0;\n\t\tif (av === undefined) return 1;\n\t\tif (bv === undefined) return -1;\n\t\t// biome-ignore lint/suspicious/noExplicitAny: 汎用比較\n\t\treturn (av as any) > (bv as any) ? dir : -dir;\n\t};\n}\n","import type { ImageCacheAdapter, InvalidateScope } from \"./types/index\";\n\n/** `$handler()` の挙動設定。 */\nexport interface HandlerOptions {\n\t/** マウントするベースパス。デフォルト `/api/cms`。 */\n\tbasePath?: string;\n\t/** 画像プロキシのパス (basePath 相対)。デフォルト `/images/:hash`。 */\n\timagesPath?: string;\n\t/** revalidate webhook のパス (basePath 相対)。デフォルト `/revalidate`。 */\n\trevalidatePath?: string;\n\t/** Webhook 署名検証用シークレット (未指定なら検証スキップ)。 */\n\twebhookSecret?: string;\n\t/** デフォルト実装を無効化する場合 true。 */\n\tdisabled?: boolean;\n}\n\n/** `$handler()` が内部で依存する CMS 機能の最小セット。 */\nexport interface HandlerAdapter {\n\timageCache: ImageCacheAdapter;\n\t/** コレクション名で DataSource を取り出し parseWebhook にフォワードする。 */\n\tparseWebhook(\n\t\treq: Request,\n\t\twebhookSecret: string | undefined,\n\t): Promise<InvalidateScope | null>;\n\trevalidate(scope: InvalidateScope): Promise<void>;\n}\n\nconst DEFAULT_OPTS = {\n\tbasePath: \"/api/cms\",\n\timagesPath: \"/images\",\n\trevalidatePath: \"/revalidate\",\n} as const;\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` — Webhook 受信 + $revalidate()\n */\nexport function createHandler(\n\tadapter: HandlerAdapter,\n\topts: HandlerOptions = {},\n): (req: Request) => Promise<Response> {\n\tconst basePath = trimTrailingSlash(opts.basePath ?? DEFAULT_OPTS.basePath);\n\tconst imagesPath = opts.imagesPath ?? DEFAULT_OPTS.imagesPath;\n\tconst revalidatePath = opts.revalidatePath ?? DEFAULT_OPTS.revalidatePath;\n\n\treturn async (req: Request): Promise<Response> => {\n\t\tconst url = new URL(req.url);\n\t\tconst path = url.pathname;\n\n\t\tif (!path.startsWith(basePath)) {\n\t\t\treturn new Response(\"Not Found\", { status: 404 });\n\t\t}\n\t\tconst rel = path.slice(basePath.length) || \"/\";\n\n\t\t// 画像: GET {basePath}/images/:hash\n\t\tif (req.method === \"GET\" && rel.startsWith(`${imagesPath}/`)) {\n\t\t\tconst hash = rel.slice(imagesPath.length + 1);\n\t\t\tif (!hash) return new Response(\"Bad Request\", { status: 400 });\n\t\t\tconst object = await adapter.imageCache.get(hash);\n\t\t\tif (!object) return new Response(\"Not Found\", { status: 404 });\n\t\t\tconst headers = new Headers();\n\t\t\tif (object.contentType) headers.set(\"content-type\", object.contentType);\n\t\t\theaders.set(\"cache-control\", \"public, max-age=31536000, immutable\");\n\t\t\treturn new Response(object.data, { headers });\n\t\t}\n\n\t\t// Revalidate: POST {basePath}/revalidate\n\t\tif (req.method === \"POST\" && rel === revalidatePath) {\n\t\t\tconst scope = await adapter.parseWebhook(req, opts.webhookSecret);\n\t\t\tif (!scope) {\n\t\t\t\treturn new Response(JSON.stringify({ ok: false, reason: \"invalid\" }), {\n\t\t\t\t\tstatus: 400,\n\t\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t\t});\n\t\t\t}\n\t\t\tawait adapter.revalidate(scope);\n\t\t\treturn new Response(JSON.stringify({ ok: true, scope }), {\n\t\t\t\tstatus: 200,\n\t\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\t});\n\t\t}\n\n\t\treturn new Response(\"Not Found\", { status: 404 });\n\t};\n}\n\nfunction trimTrailingSlash(s: string): string {\n\treturn s.endsWith(\"/\") ? s.slice(0, -1) : s;\n}\n","import { memoryDocumentCache, memoryImageCache } from \"./cache/memory\";\nimport type { CacheConfig, CreateCMSOptions, RendererFn } from \"./types/index\";\n\n/** `nodePreset()` のオプション。 */\nexport interface NodePresetOptions {\n\t/**\n\t * キャッシュ設定。\n\t * - 省略時: memoryDocumentCache + memoryImageCache をデフォルト有効化\n\t * - `\"disabled\"`: キャッシュを完全無効化\n\t * - オブジェクト: 任意の cache adapter を差し込む\n\t */\n\tcache?: CacheConfig | \"disabled\";\n\t/** SWR の TTL (ミリ秒)。`cache` をオブジェクトで渡した場合はそちらが優先される。 */\n\tttlMs?: number;\n\t/** カスタムレンダラー。未指定時は core が @notion-headless-cms/renderer を動的ロード。 */\n\trenderer?: RendererFn;\n}\n\n/**\n * Node.js ランタイム向けの `createCMS` オプションプリセット。\n * メモリキャッシュをデフォルト有効にした `{ cache, renderer }` を返す。\n *\n * @example\n * import { createCMS, nodePreset } from \"@notion-headless-cms/core\";\n * import { cmsDataSources } from \"./generated/cms-schema\";\n *\n * const cms = createCMS({\n * ...nodePreset({ ttlMs: 5 * 60_000 }),\n * dataSources: cmsDataSources,\n * });\n */\nexport function nodePreset(\n\topts: NodePresetOptions = {},\n): Pick<CreateCMSOptions, \"cache\" | \"renderer\"> {\n\tif (opts.cache === \"disabled\") {\n\t\treturn { cache: undefined, renderer: opts.renderer };\n\t}\n\treturn {\n\t\tcache: opts.cache ?? {\n\t\t\tdocument: memoryDocumentCache(),\n\t\t\timage: memoryImageCache(),\n\t\t\tttlMs: opts.ttlMs,\n\t\t},\n\t\trenderer: opts.renderer,\n\t};\n}\n","import { noopDocumentCache, noopImageCache } 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 { nodePreset } from \"./preset-node\";\nimport type { RenderContext } from \"./rendering\";\nimport type { RetryConfig } from \"./retry\";\nimport { DEFAULT_RETRY_CONFIG } from \"./retry\";\nimport type {\n\tBaseContentItem,\n\tCacheConfig,\n\tCachedItemList,\n\tCMSHooks,\n\tCollectionClient,\n\tCollectionSemantics,\n\tCreateCMSOptions,\n\tDataSource,\n\tDataSourceMap,\n\tDocumentCacheAdapter,\n\tImageCacheAdapter,\n\tInferDataSourceItem,\n\tInvalidateScope,\n\tLogger,\n\tLogLevel,\n\tRendererFn,\n} from \"./types/index\";\n\nconst DEFAULT_IMAGE_PROXY_BASE = \"/api/images\";\n\n/** `CMSClient<D>` — コレクション別アクセス + グローバル操作の合成型。 */\nexport type CMSClient<D extends DataSourceMap> = {\n\t[K in keyof D]: CollectionClient<InferDataSourceItem<D[K]>>;\n} & CMSGlobalOps<D>;\n\n/** `CMSClient` のグローバル名前空間。`$` プレフィックス。 */\nexport interface CMSGlobalOps<D extends DataSourceMap> {\n\t/** 登録されているコレクション名の一覧。 */\n\treadonly $collections: readonly (keyof D & string)[];\n\t/** 全コレクションまたは特定コレクションのキャッシュを無効化する。 */\n\t$revalidate(scope?: InvalidateScope): Promise<void>;\n\t/** Web Standard なルーティングハンドラ (画像プロキシ / webhook) を生成する。 */\n\t$handler(opts?: HandlerOptions): (req: Request) => Promise<Response>;\n\t/** ハッシュキーでキャッシュ画像を取得する。 */\n\t$getCachedImage(hash: string): ReturnType<ImageCacheAdapter[\"get\"]>;\n}\n\nfunction resolveDocumentCache(\n\tcache: CacheConfig | undefined,\n\t// biome-ignore lint/suspicious/noExplicitAny: 横断的に利用\n): DocumentCacheAdapter<any> {\n\tif (!cache || cache === \"disabled\" || !cache.document) {\n\t\treturn noopDocumentCache();\n\t}\n\treturn cache.document;\n}\n\nfunction resolveImageCache(cache: CacheConfig | undefined): ImageCacheAdapter {\n\tif (!cache || cache === \"disabled\" || !cache.image) {\n\t\treturn noopImageCache();\n\t}\n\treturn cache.image;\n}\n\nfunction resolveTtl(cache: CacheConfig | undefined): number | undefined {\n\tif (!cache || cache === \"disabled\") return undefined;\n\treturn cache.ttlMs;\n}\n\nfunction hasImageCacheConfigured(cache: CacheConfig | undefined): boolean {\n\tif (!cache || cache === \"disabled\") return false;\n\treturn !!cache.image;\n}\n\n/**\n * `{collection}:{slug}` キー空間で動作するコレクション別キャッシュビューを生成する。\n * 単一の `DocumentCacheAdapter` に複数コレクションを同居させるためのアダプタ。\n */\nfunction scopeDocumentCache<T extends BaseContentItem>(\n\t// biome-ignore lint/suspicious/noExplicitAny: 共有ストレージのため\n\tbase: DocumentCacheAdapter<any>,\n\tcollection: string,\n): DocumentCacheAdapter<T> {\n\tconst itemKey = (slug: string): string => `${collection}:${slug}`;\n\t// リストはコレクション別にクロージャ変数で管理する。\n\t// base.getList/setList はコレクション名前空間を持たないため、\n\t// 複数コレクションが同じ base を共有すると上書きし合うバグが起きる。\n\t// 初回アクセスのみ base から読み込むことで pre-populate されたキャッシュを活かしつつ、\n\t// 以降の読み書きはコレクション固有のスロットに限定する。\n\tlet listSlot: CachedItemList<T> | null = null;\n\tlet listInitialized = false;\n\n\treturn {\n\t\tname: `${base.name}@${collection}`,\n\t\tgetList: async () => {\n\t\t\tif (!listInitialized) {\n\t\t\t\tlistInitialized = true;\n\t\t\t\tlistSlot = (await base.getList()) as CachedItemList<T> | null;\n\t\t\t}\n\t\t\treturn listSlot;\n\t\t},\n\t\tsetList: (data) => {\n\t\t\tlistSlot = data as CachedItemList<T>;\n\t\t\tlistInitialized = true;\n\t\t\treturn Promise.resolve();\n\t\t},\n\t\tgetItem: (slug) => base.getItem(itemKey(slug)),\n\t\tsetItem: (slug, data) => base.setItem(itemKey(slug), data),\n\t\tasync invalidate(scope) {\n\t\t\tlistSlot = null;\n\t\t\tlistInitialized = true; // 無効化後は base を再読みしない\n\t\t\tif (!base.invalidate) return;\n\t\t\tif (scope === \"all\") {\n\t\t\t\treturn base.invalidate({ collection });\n\t\t\t}\n\t\t\tif (\"slug\" in scope) {\n\t\t\t\t// アイテムは `{collection}:{slug}` で保存されるため正しいキーに変換する\n\t\t\t\treturn base.invalidate({\n\t\t\t\t\tcollection: scope.collection,\n\t\t\t\t\tslug: itemKey(scope.slug),\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn base.invalidate(scope);\n\t\t},\n\t};\n}\n\nconst LOG_LEVEL_ORDER: Record<LogLevel, number> = {\n\tdebug: 0,\n\tinfo: 1,\n\twarn: 2,\n\terror: 3,\n};\n\n/** `logger` から `minLevel` 未満のレベルを除いた新しい Logger を返す。 */\nfunction applyLogLevel(\n\tlogger: Logger | undefined,\n\tminLevel: LogLevel,\n): Logger | undefined {\n\tif (!logger) return undefined;\n\tconst minOrder = LOG_LEVEL_ORDER[minLevel];\n\tconst filtered: Logger = {};\n\tfor (const level of [\"debug\", \"info\", \"warn\", \"error\"] as const) {\n\t\tif (LOG_LEVEL_ORDER[level] >= minOrder) {\n\t\t\tfiltered[level] = logger[level];\n\t\t}\n\t}\n\treturn filtered;\n}\n\n/**\n * `preset` オプションを解決して `cache` / `renderer` のデフォルトを補完する内部関数。\n * 明示的な `cache` / `renderer` がある場合はそちらが優先される。\n * `preset` 未指定時は opts をそのまま返す。\n */\nfunction resolvePreset<D extends DataSourceMap>(\n\topts: CreateCMSOptions<D>,\n): CreateCMSOptions<D> {\n\tif (opts.preset === \"disabled\") {\n\t\treturn { ...opts, cache: undefined };\n\t}\n\tif (opts.preset === \"node\") {\n\t\tconst presetResult = nodePreset({ ttlMs: opts.ttlMs });\n\t\treturn {\n\t\t\t...opts,\n\t\t\tcache: opts.cache ?? presetResult.cache,\n\t\t\trenderer: opts.renderer ?? presetResult.renderer,\n\t\t};\n\t}\n\treturn opts;\n}\n\n/**\n * 複数の DataSource を束ねた CMS クライアントを生成する。\n *\n * @example\n * // Node.js(preset を使った簡潔な記法)\n * const cms = createCMS({ dataSources: cmsDataSources, preset: \"node\", ttlMs: 5 * 60_000 });\n *\n * @example\n * // 従来の spread パターン(引き続き動作する)\n * const cms = createCMS({ ...nodePreset({ ttlMs: 5 * 60_000 }), dataSources: cmsDataSources });\n *\n * @example\n * // キャッシュを細かく指定する場合\n * const cms = createCMS({\n * dataSources,\n * cache: { document, image, ttlMs: 60_000 },\n * });\n */\nexport function createCMS<D extends DataSourceMap>(\n\topts: CreateCMSOptions<D>,\n): CMSClient<D> {\n\tif (!opts.dataSources || Object.keys(opts.dataSources).length === 0) {\n\t\tthrow new CMSError({\n\t\t\tcode: \"core/config_invalid\",\n\t\t\tmessage:\n\t\t\t\t\"createCMS: dataSources に少なくとも1つのコレクションを指定してください。\",\n\t\t\tcontext: { operation: \"createCMS\" },\n\t\t});\n\t}\n\n\t// collections が指定されたコレクションは slug が必須。\n\tfor (const [name, col] of Object.entries(opts.collections ?? {})) {\n\t\tconst c = col as CollectionSemantics | undefined;\n\t\tif (c && !c.slug) {\n\t\t\tthrow new CMSError({\n\t\t\t\tcode: \"core/config_invalid\",\n\t\t\t\tmessage: `createCMS: コレクション \"${name}\" の collections.slug は必須です。slug として使うフィールド名を指定してください。`,\n\t\t\t\tcontext: { operation: \"createCMS\", collection: name },\n\t\t\t});\n\t\t}\n\t}\n\n\tconst resolved = resolvePreset(opts);\n\n\tconst baseDocCache = resolveDocumentCache(resolved.cache);\n\tconst imgCache = resolveImageCache(resolved.cache);\n\tconst hasImageCache = hasImageCacheConfigured(resolved.cache);\n\tconst ttlMs = resolveTtl(resolved.cache);\n\tconst imageProxyBase =\n\t\topts.content?.imageProxyBase ?? DEFAULT_IMAGE_PROXY_BASE;\n\tconst contentConfig = opts.content;\n\tconst rendererFn: RendererFn | undefined = resolved.renderer;\n\tconst waitUntil = opts.waitUntil;\n\tconst baseLogger: Logger | undefined = mergeLoggers(\n\t\topts.plugins ?? [],\n\t\topts.logger,\n\t);\n\tconst logger = opts.logLevel\n\t\t? applyLogLevel(baseLogger, opts.logLevel)\n\t\t: baseLogger;\n\tconst hooks: CMSHooks<BaseContentItem> = mergeHooks(\n\t\topts.plugins ?? [],\n\t\topts.hooks,\n\t\tlogger,\n\t);\n\tconst maxConcurrent = opts.rateLimiter?.maxConcurrent ?? 3;\n\tconst retryConfig: RetryConfig = {\n\t\t...DEFAULT_RETRY_CONFIG,\n\t\t...(opts.rateLimiter ?? {}),\n\t};\n\n\tconst collectionNames = Object.keys(opts.dataSources) as (keyof D & string)[];\n\n\t// biome-ignore lint/suspicious/noExplicitAny: 各 T を保持\n\tconst collections: Record<string, CollectionClient<any>> = {};\n\t// biome-ignore lint/suspicious/noExplicitAny: 横断的に利用\n\tconst scopedCaches: DocumentCacheAdapter<any>[] = [];\n\tfor (const name of collectionNames) {\n\t\tconst source = opts.dataSources[name] as DataSource<BaseContentItem>;\n\t\tconst scopedCache = scopeDocumentCache<BaseContentItem>(baseDocCache, name);\n\t\tscopedCaches.push(scopedCache);\n\t\tconst col = opts.collections?.[name] as CollectionSemantics | undefined;\n\t\tconst colHooks = col?.hooks as CMSHooks<BaseContentItem> | undefined;\n\t\tconst collectionHooks: CMSHooks<BaseContentItem> = colHooks\n\t\t\t? mergeHooks([{ name: `${name}:global`, hooks }], colHooks, logger)\n\t\t\t: hooks;\n\t\tconst renderCtx: RenderContext<BaseContentItem> = {\n\t\t\tsource,\n\t\t\trendererFn,\n\t\t\timgCache,\n\t\t\thasImageCache,\n\t\t\timageProxyBase,\n\t\t\tcontentConfig,\n\t\t\thooks: collectionHooks,\n\t\t\tlogger,\n\t\t};\n\t\tconst ctx: CollectionContext<BaseContentItem> = {\n\t\t\tcollection: name,\n\t\t\tsource,\n\t\t\tdocCache: scopedCache,\n\t\t\trender: renderCtx,\n\t\t\thooks: collectionHooks,\n\t\t\tlogger,\n\t\t\tttlMs,\n\t\t\t// 公開条件は CollectionSemantics(createCMS の collections オプション)が権威\n\t\t\tpublishedStatuses: col?.publishedStatuses\n\t\t\t\t? [...col.publishedStatuses]\n\t\t\t\t: [],\n\t\t\taccessibleStatuses: col?.accessibleStatuses\n\t\t\t\t? [...col.accessibleStatuses]\n\t\t\t\t: [],\n\t\t\tretryConfig,\n\t\t\tmaxConcurrent,\n\t\t\twaitUntil,\n\t\t\tslugField: col?.slug,\n\t\t};\n\t\tcollections[name] = new CollectionClientImpl(ctx);\n\t}\n\n\tconst globalOps: CMSGlobalOps<D> = {\n\t\t$collections: collectionNames,\n\t\tasync $revalidate(scope?: InvalidateScope): Promise<void> {\n\t\t\tlogger?.debug?.(\"グローバルキャッシュを無効化\", {\n\t\t\t\toperation: \"$revalidate\",\n\t\t\t\tcacheAdapter: baseDocCache.name,\n\t\t\t});\n\t\t\t// baseDocCache を直接呼ばず各スコープキャッシュ経由で呼ぶ。\n\t\t\t// 直接呼ぶと scopeDocumentCache の listSlot がクリアされず stale になる。\n\t\t\tfor (const cache of scopedCaches) {\n\t\t\t\tif (!cache.invalidate) continue;\n\t\t\t\tawait cache.invalidate(scope ?? \"all\");\n\t\t\t}\n\t\t},\n\t\t$handler(handlerOpts?: HandlerOptions) {\n\t\t\treturn createHandler(\n\t\t\t\t{\n\t\t\t\t\timageCache: imgCache,\n\t\t\t\t\tparseWebhook: async (req, webhookSecret) => {\n\t\t\t\t\t\t// 各 DataSource の parseWebhook を順に試す\n\t\t\t\t\t\tfor (const name of collectionNames) {\n\t\t\t\t\t\t\tconst ds = opts.dataSources[name] as DataSource<BaseContentItem>;\n\t\t\t\t\t\t\tif (ds.parseWebhook) {\n\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\tconst scope = await ds.parseWebhook(req.clone(), {\n\t\t\t\t\t\t\t\t\t\tsecret: webhookSecret,\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\treturn scope;\n\t\t\t\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t\t\t\tlogger?.warn?.(\"parseWebhook 失敗\", {\n\t\t\t\t\t\t\t\t\t\tcollection: name,\n\t\t\t\t\t\t\t\t\t\terror: err instanceof Error ? err.message : String(err),\n\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// フォールバック: { slug } だけの汎用 JSON body\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tconst body = (await req.json()) as {\n\t\t\t\t\t\t\t\tslug?: string;\n\t\t\t\t\t\t\t\tcollection?: string;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tif (body.slug && body.collection) {\n\t\t\t\t\t\t\t\treturn { collection: body.collection, slug: body.slug };\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (body.collection) {\n\t\t\t\t\t\t\t\treturn { collection: body.collection };\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// ignore\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t},\n\t\t\t\t\trevalidate: (scope) => globalOps.$revalidate(scope),\n\t\t\t\t},\n\t\t\t\thandlerOpts,\n\t\t\t);\n\t\t},\n\t\t$getCachedImage(hash) {\n\t\t\treturn imgCache.get(hash);\n\t\t},\n\t};\n\n\treturn Object.assign(\n\t\tObject.create(null) as object,\n\t\tcollections,\n\t\tglobalOps,\n\t) as CMSClient<D>;\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\tname: string;\n\thooks?: CMSHooks<T>;\n\tlogger?: Partial<Logger>;\n}\n\nexport function definePlugin<T extends BaseContentItem>(\n\tplugin: CMSPlugin<T>,\n): CMSPlugin<T> {\n\treturn plugin;\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@notion-headless-cms/core",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "description": "Core CMS engine for notion-headless-cms — fetch, transform, cache with stale-while-revalidate strategy",
5
5
  "keywords": [
6
6
  "notion",