@notion-headless-cms/core 0.1.3 → 0.2.0

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.
@@ -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/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, 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): Promise<string> {\n\tconst hash = await sha256Hex(notionUrl);\n\tconst proxyUrl = `${imageProxyBase}/${hash}`;\n\n\tconst existing = await cache.get(hash);\n\tif (existing) return proxyUrl;\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} 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): (notionUrl: string) => Promise<string> {\n\treturn (notionUrl) => fetchAndCacheImage(cache, notionUrl, imageProxyBase);\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 * キャッシュに保存する CachedItem を拡張し、`blocks` を同梱できるようにする。\n * `CachedItem<T>` の構造互換性を保ちつつ blocks を optional で載せる。\n */\nexport type CachedItemWithBlocks<T extends BaseContentItem> = CachedItem<T> & {\n\tblocks?: ContentBlock[];\n\tmarkdown?: string;\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<CachedItemWithBlocks<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)\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: CachedItemWithBlocks<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 CachedItemWithBlocks<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 * adapter-cloudflare / adapter-node は 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\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}\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 && !isStale(cached.cachedAt, this.ctx.ttlMs)) {\n\t\t\tthis.ctx.hooks.onCacheHit?.(slug, cached);\n\t\t\treturn this.attachContent(cached.item, cached);\n\t\t}\n\n\t\tthis.ctx.hooks.onCacheMiss?.(slug);\n\t\tconst item = await this.findRaw(slug);\n\t\tif (!item) return null;\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\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\t// blocks が CachedItem に乗っていれば優先的に採用する\n\t\tconst maybeBlocks = (cached as CachedItem<T> & { blocks?: ContentBlock[] })\n\t\t\t.blocks;\n\t\tif (maybeBlocks) blocksCache = maybeBlocks;\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 && !isStale(cached.cachedAt, this.ctx.ttlMs)) {\n\t\t\tthis.ctx.hooks.onListCacheHit?.(cached.items, cached.cachedAt);\n\t\t\treturn cached.items;\n\t\t}\n\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 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 item = await withRetry(() => this.ctx.source.findBySlug(slug), {\n\t\t\t...this.ctx.retryConfig,\n\t\t\tonRetry: (attempt, status) => {\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\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 { noopDocumentCache, noopImageCache } from \"./cache/noop\";\nimport { CollectionClientImpl, type CollectionContext } from \"./collection\";\nimport { createHandler, type HandlerOptions } from \"./handler\";\nimport { mergeHooks, mergeLoggers } from \"./hooks\";\nimport type { RenderContext } from \"./rendering\";\nimport type { RetryConfig } from \"./retry\";\nimport { DEFAULT_RETRY_CONFIG } from \"./retry\";\nimport type {\n\tBaseContentItem,\n\tCacheConfig,\n\tCMSHooks,\n\tCollectionClient,\n\tCreateCMSOptions,\n\tDataSource,\n\tDataSourceMap,\n\tDocumentCacheAdapter,\n\tImageCacheAdapter,\n\tInferDataSourceItem,\n\tInvalidateScope,\n\tLogger,\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\tconst listKey = collection;\n\n\treturn {\n\t\tname: `${base.name}@${collection}`,\n\t\tasync getList() {\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: キー別 namespace\n\t\t\tconst anyBase = base as any;\n\t\t\tif (typeof anyBase.getListByKey === \"function\") {\n\t\t\t\treturn anyBase.getListByKey(listKey);\n\t\t\t}\n\t\t\treturn base.getList();\n\t\t},\n\t\tasync setList(data) {\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: キー別 namespace\n\t\t\tconst anyBase = base as any;\n\t\t\tif (typeof anyBase.setListByKey === \"function\") {\n\t\t\t\treturn anyBase.setListByKey(listKey, data);\n\t\t\t}\n\t\t\treturn base.setList(data);\n\t\t},\n\t\tasync getItem(slug) {\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: キー別 namespace\n\t\t\tconst anyBase = base as any;\n\t\t\tif (typeof anyBase.getItemByKey === \"function\") {\n\t\t\t\treturn anyBase.getItemByKey(itemKey(slug));\n\t\t\t}\n\t\t\treturn base.getItem(slug);\n\t\t},\n\t\tasync setItem(slug, data) {\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: キー別 namespace\n\t\t\tconst anyBase = base as any;\n\t\t\tif (typeof anyBase.setItemByKey === \"function\") {\n\t\t\t\treturn anyBase.setItemByKey(itemKey(slug), data);\n\t\t\t}\n\t\t\treturn base.setItem(slug, data);\n\t\t},\n\t\tasync invalidate(scope) {\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 && !(\"collection\" in scope)) {\n\t\t\t\treturn base.invalidate({ collection, slug: scope.slug });\n\t\t\t}\n\t\t\treturn base.invalidate(scope);\n\t\t},\n\t};\n}\n\n/**\n * 複数の DataSource を束ねた CMS クライアントを生成する。\n *\n * @example\n * const cms = createCMS({\n * dataSources: {\n * posts: createNotionCollection({ token, databaseId, schema }),\n * },\n * cache: { document, image, ttlMs: 60_000 },\n * });\n * const post = await cms.posts.getItem(\"my-slug\");\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 Error(\n\t\t\t\"createCMS: dataSources に少なくとも1つのコレクションを指定してください。\",\n\t\t);\n\t}\n\n\tconst baseDocCache = resolveDocumentCache(opts.cache);\n\tconst imgCache = resolveImageCache(opts.cache);\n\tconst hasImageCache = hasImageCacheConfigured(opts.cache);\n\tconst ttlMs = resolveTtl(opts.cache);\n\tconst imageProxyBase =\n\t\topts.content?.imageProxyBase ?? DEFAULT_IMAGE_PROXY_BASE;\n\tconst contentConfig = opts.content;\n\tconst rendererFn: RendererFn | undefined = opts.renderer;\n\tconst waitUntil = opts.waitUntil;\n\tconst logger: Logger | undefined = mergeLoggers(\n\t\topts.plugins ?? [],\n\t\topts.logger,\n\t);\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\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\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,\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,\n\t\t\tlogger,\n\t\t\tttlMs,\n\t\t\tpublishedStatuses: source.publishedStatuses\n\t\t\t\t? [...source.publishedStatuses]\n\t\t\t\t: [],\n\t\t\taccessibleStatuses: source.accessibleStatuses\n\t\t\t\t? [...source.accessibleStatuses]\n\t\t\t\t: [],\n\t\t\tretryConfig,\n\t\t\tmaxConcurrent,\n\t\t\twaitUntil,\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\tif (!baseDocCache.invalidate) return;\n\t\t\tawait baseDocCache.invalidate(scope ?? \"all\");\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, 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): Promise<string> {\n\tconst hash = await sha256Hex(notionUrl);\n\tconst proxyUrl = `${imageProxyBase}/${hash}`;\n\n\tconst existing = await cache.get(hash);\n\tif (existing) return proxyUrl;\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} 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): (notionUrl: string) => Promise<string> {\n\treturn (notionUrl) => fetchAndCacheImage(cache, notionUrl, imageProxyBase);\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 * キャッシュに保存する CachedItem を拡張し、`blocks` を同梱できるようにする。\n * `CachedItem<T>` の構造互換性を保ちつつ blocks を optional で載せる。\n */\nexport type CachedItemWithBlocks<T extends BaseContentItem> = CachedItem<T> & {\n\tblocks?: ContentBlock[];\n\tmarkdown?: string;\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<CachedItemWithBlocks<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)\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: CachedItemWithBlocks<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 CachedItemWithBlocks<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 * adapter-cloudflare / adapter-node は 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\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}\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 && !isStale(cached.cachedAt, this.ctx.ttlMs)) {\n\t\t\tthis.ctx.hooks.onCacheHit?.(slug, cached);\n\t\t\treturn this.attachContent(cached.item, cached);\n\t\t}\n\n\t\tthis.ctx.hooks.onCacheMiss?.(slug);\n\t\tconst item = await this.findRaw(slug);\n\t\tif (!item) return null;\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\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\t// blocks が CachedItem に乗っていれば優先的に採用する\n\t\tconst maybeBlocks = (cached as CachedItem<T> & { blocks?: ContentBlock[] })\n\t\t\t.blocks;\n\t\tif (maybeBlocks) blocksCache = maybeBlocks;\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 && !isStale(cached.cachedAt, this.ctx.ttlMs)) {\n\t\t\tthis.ctx.hooks.onListCacheHit?.(cached.items, cached.cachedAt);\n\t\t\treturn cached.items;\n\t\t}\n\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 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 item = await withRetry(() => this.ctx.source.findBySlug(slug), {\n\t\t\t...this.ctx.retryConfig,\n\t\t\tonRetry: (attempt, status) => {\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\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\tCMSHooks,\n\tCollectionClient,\n\tCreateCMSOptions,\n\tDataSource,\n\tDataSourceMap,\n\tDocumentCacheAdapter,\n\tImageCacheAdapter,\n\tInferDataSourceItem,\n\tInvalidateScope,\n\tLogger,\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\tconst listKey = collection;\n\n\treturn {\n\t\tname: `${base.name}@${collection}`,\n\t\tasync getList() {\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: キー別 namespace\n\t\t\tconst anyBase = base as any;\n\t\t\tif (typeof anyBase.getListByKey === \"function\") {\n\t\t\t\treturn anyBase.getListByKey(listKey);\n\t\t\t}\n\t\t\treturn base.getList();\n\t\t},\n\t\tasync setList(data) {\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: キー別 namespace\n\t\t\tconst anyBase = base as any;\n\t\t\tif (typeof anyBase.setListByKey === \"function\") {\n\t\t\t\treturn anyBase.setListByKey(listKey, data);\n\t\t\t}\n\t\t\treturn base.setList(data);\n\t\t},\n\t\tasync getItem(slug) {\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: キー別 namespace\n\t\t\tconst anyBase = base as any;\n\t\t\tif (typeof anyBase.getItemByKey === \"function\") {\n\t\t\t\treturn anyBase.getItemByKey(itemKey(slug));\n\t\t\t}\n\t\t\treturn base.getItem(slug);\n\t\t},\n\t\tasync setItem(slug, data) {\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: キー別 namespace\n\t\t\tconst anyBase = base as any;\n\t\t\tif (typeof anyBase.setItemByKey === \"function\") {\n\t\t\t\treturn anyBase.setItemByKey(itemKey(slug), data);\n\t\t\t}\n\t\t\treturn base.setItem(slug, data);\n\t\t},\n\t\tasync invalidate(scope) {\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\treturn base.invalidate(scope);\n\t\t},\n\t};\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\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 logger: Logger | undefined = mergeLoggers(\n\t\topts.plugins ?? [],\n\t\topts.logger,\n\t);\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\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\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,\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,\n\t\t\tlogger,\n\t\t\tttlMs,\n\t\t\tpublishedStatuses: source.publishedStatuses\n\t\t\t\t? [...source.publishedStatuses]\n\t\t\t\t: [],\n\t\t\taccessibleStatuses: source.accessibleStatuses\n\t\t\t\t? [...source.accessibleStatuses]\n\t\t\t\t: [],\n\t\t\tretryConfig,\n\t\t\tmaxConcurrent,\n\t\t\twaitUntil,\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\tif (!baseDocCache.invalidate) return;\n\t\t\tawait baseDocCache.invalidate(scope ?? \"all\");\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.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "Core CMS engine for notion-headless-cms — fetch, transform, cache with stale-while-revalidate strategy",
5
5
  "keywords": [
6
6
  "notion",
@@ -55,7 +55,7 @@
55
55
  "provenance": true
56
56
  },
57
57
  "peerDependencies": {
58
- "@notion-headless-cms/renderer": "0.1.2"
58
+ "@notion-headless-cms/renderer": "0.1.3"
59
59
  },
60
60
  "peerDependenciesMeta": {
61
61
  "@notion-headless-cms/renderer": {
@@ -63,7 +63,7 @@
63
63
  }
64
64
  },
65
65
  "devDependencies": {
66
- "@notion-headless-cms/renderer": "0.1.2"
66
+ "@notion-headless-cms/renderer": "0.1.3"
67
67
  },
68
68
  "scripts": {
69
69
  "build": "tsdown src/index.ts src/errors.ts src/hooks.ts src/cache/memory.ts src/cache/noop.ts --format esm --dts --sourcemap --out-dir dist",
@@ -1,45 +0,0 @@
1
- import { a as StorageBinary, i as CachedItemList, r as CachedItem, t as BaseContentItem } from "./content-BrwEY2_p.mjs";
2
-
3
- //#region src/types/cache.d.ts
4
- /**
5
- * キャッシュ無効化のスコープ。
6
- * v1 API では `{ collection, slug? }` が主形式。
7
- * 旧 `{ tag }` 形式は後方互換のため残す。
8
- */
9
- type InvalidateScope = "all" | {
10
- slug: string;
11
- } | {
12
- tag: string;
13
- } | {
14
- collection: string;
15
- } | {
16
- collection: string;
17
- slug: string;
18
- };
19
- /** ドキュメントキャッシュを抽象化するインターフェース。 */
20
- interface DocumentCacheAdapter<T extends BaseContentItem = BaseContentItem> {
21
- readonly name: string;
22
- getList(): Promise<CachedItemList<T> | null>;
23
- setList(data: CachedItemList<T>): Promise<void>;
24
- getItem(slug: string): Promise<CachedItem<T> | null>;
25
- setItem(slug: string, data: CachedItem<T>): Promise<void>;
26
- invalidate?(scope: InvalidateScope): Promise<void>;
27
- }
28
- /** 画像キャッシュを抽象化するインターフェース。 */
29
- interface ImageCacheAdapter {
30
- readonly name: string;
31
- get(hash: string): Promise<StorageBinary | null>;
32
- set(hash: string, data: ArrayBuffer, contentType: string): Promise<void>;
33
- }
34
- /**
35
- * キャッシュ設定。`"disabled"` を渡すと完全にキャッシュを無効化する。
36
- * オブジェクトの場合、document / image それぞれ独立したアダプタを差し込める。
37
- */
38
- type CacheConfig<T extends BaseContentItem = BaseContentItem> = "disabled" | {
39
- document?: DocumentCacheAdapter<T>;
40
- image?: ImageCacheAdapter; /** キャッシュの有効期間(ミリ秒)。未設定の場合はTTLなし。 */
41
- ttlMs?: number;
42
- };
43
- //#endregion
44
- export { InvalidateScope as i, DocumentCacheAdapter as n, ImageCacheAdapter as r, CacheConfig as t };
45
- //# sourceMappingURL=cache-B-MG4yyg.d.mts.map