@notion-headless-cms/core 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +145 -47
- package/dist/cache/memory.d.mts +52 -0
- package/dist/cache/memory.mjs +112 -0
- package/dist/cache/memory.mjs.map +1 -0
- package/dist/cache/noop.d.mts +11 -0
- package/dist/cache/noop.mjs +44 -0
- package/dist/cache/noop.mjs.map +1 -0
- package/dist/cache-B-MG4yyg.d.mts +45 -0
- package/dist/content-BrwEY2_p.d.mts +53 -0
- package/dist/errors.d.mts +32 -0
- package/dist/errors.mjs +24 -0
- package/dist/errors.mjs.map +1 -0
- package/dist/hooks-DCSAQkST.d.mts +60 -0
- package/dist/hooks.d.mts +2 -0
- package/dist/hooks.mjs +75 -0
- package/dist/hooks.mjs.map +1 -0
- package/dist/index.d.mts +449 -0
- package/dist/index.mjs +616 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +34 -15
- package/dist/index.d.ts +0 -474
- package/dist/index.js +0 -766
|
@@ -0,0 +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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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@notion-headless-cms/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Core CMS engine for notion-headless-cms — fetch, transform, cache with stale-while-revalidate strategy",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"notion",
|
|
@@ -17,37 +17,56 @@
|
|
|
17
17
|
},
|
|
18
18
|
"repository": {
|
|
19
19
|
"type": "git",
|
|
20
|
-
"url": "https://github.com/kjfsm/notion-headless-cms.git",
|
|
20
|
+
"url": "git+https://github.com/kjfsm/notion-headless-cms.git",
|
|
21
21
|
"directory": "packages/core"
|
|
22
22
|
},
|
|
23
23
|
"type": "module",
|
|
24
24
|
"sideEffects": false,
|
|
25
25
|
"exports": {
|
|
26
26
|
".": {
|
|
27
|
-
"
|
|
28
|
-
"
|
|
27
|
+
"types": "./dist/index.d.mts",
|
|
28
|
+
"import": "./dist/index.mjs"
|
|
29
|
+
},
|
|
30
|
+
"./errors": {
|
|
31
|
+
"types": "./dist/errors.d.mts",
|
|
32
|
+
"import": "./dist/errors.mjs"
|
|
33
|
+
},
|
|
34
|
+
"./hooks": {
|
|
35
|
+
"types": "./dist/hooks.d.mts",
|
|
36
|
+
"import": "./dist/hooks.mjs"
|
|
37
|
+
},
|
|
38
|
+
"./cache/memory": {
|
|
39
|
+
"types": "./dist/cache/memory.d.mts",
|
|
40
|
+
"import": "./dist/cache/memory.mjs"
|
|
41
|
+
},
|
|
42
|
+
"./cache/noop": {
|
|
43
|
+
"types": "./dist/cache/noop.d.mts",
|
|
44
|
+
"import": "./dist/cache/noop.mjs"
|
|
29
45
|
}
|
|
30
46
|
},
|
|
31
47
|
"files": [
|
|
32
48
|
"dist"
|
|
33
49
|
],
|
|
34
50
|
"engines": {
|
|
35
|
-
"node": ">=
|
|
51
|
+
"node": ">=24"
|
|
36
52
|
},
|
|
37
53
|
"publishConfig": {
|
|
38
|
-
"
|
|
39
|
-
"
|
|
54
|
+
"access": "public",
|
|
55
|
+
"provenance": true
|
|
40
56
|
},
|
|
41
|
-
"
|
|
42
|
-
"@
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"@notion-headless-cms/
|
|
46
|
-
|
|
47
|
-
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"@notion-headless-cms/renderer": "0.1.2"
|
|
59
|
+
},
|
|
60
|
+
"peerDependenciesMeta": {
|
|
61
|
+
"@notion-headless-cms/renderer": {
|
|
62
|
+
"optional": true
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@notion-headless-cms/renderer": "0.1.2"
|
|
48
67
|
},
|
|
49
68
|
"scripts": {
|
|
50
|
-
"build": "
|
|
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",
|
|
51
70
|
"typecheck": "tsc --noEmit",
|
|
52
71
|
"test": "vitest run"
|
|
53
72
|
}
|
package/dist/index.d.ts
DELETED
|
@@ -1,474 +0,0 @@
|
|
|
1
|
-
import { RendererFn } from '@notion-headless-cms/renderer';
|
|
2
|
-
import { BlockHandler } from '@notion-headless-cms/transformer';
|
|
3
|
-
import { PageObjectResponse, RichTextItemResponse } from '@notionhq/client/build/src/api-endpoints';
|
|
4
|
-
import { PluggableList } from 'unified';
|
|
5
|
-
|
|
6
|
-
/** 文字列をSHA-256でハッシュ化し、16進数文字列として返す。画像キーの生成に使用。 */
|
|
7
|
-
declare function sha256Hex(input: string): Promise<string>;
|
|
8
|
-
/**
|
|
9
|
-
* キャッシュが有効期限切れかどうかを判定する。
|
|
10
|
-
* ttlMs が未指定の場合は常に false(無期限有効)を返す。
|
|
11
|
-
*/
|
|
12
|
-
declare function isStale(cachedAt: number, ttlMs?: number): boolean;
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* ライブラリが動作するために必須なフィールド。
|
|
16
|
-
* 利用者はこのインターフェースを拡張して独自のコンテンツ型を定義する。
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* interface Post extends BaseContentItem {
|
|
20
|
-
* title: string;
|
|
21
|
-
* author: string;
|
|
22
|
-
* }
|
|
23
|
-
* createCMS<Post>({ source: notionAdapter({ ... }) })
|
|
24
|
-
*/
|
|
25
|
-
interface BaseContentItem {
|
|
26
|
-
id: string;
|
|
27
|
-
slug: string;
|
|
28
|
-
status: string;
|
|
29
|
-
publishedAt: string;
|
|
30
|
-
updatedAt: string;
|
|
31
|
-
}
|
|
32
|
-
/** ストレージにキャッシュされたレンダリング済みコンテンツ。 */
|
|
33
|
-
interface CachedItem<T extends BaseContentItem = BaseContentItem> {
|
|
34
|
-
html: string;
|
|
35
|
-
item: T;
|
|
36
|
-
notionUpdatedAt: string;
|
|
37
|
-
cachedAt: number;
|
|
38
|
-
}
|
|
39
|
-
/** ストレージにキャッシュされたコンテンツ一覧。 */
|
|
40
|
-
interface CachedItemList<T extends BaseContentItem = BaseContentItem> {
|
|
41
|
-
items: T[];
|
|
42
|
-
cachedAt: number;
|
|
43
|
-
}
|
|
44
|
-
/** ストレージから取得したバイナリオブジェクト。 */
|
|
45
|
-
interface StorageBinary {
|
|
46
|
-
data: ArrayBuffer;
|
|
47
|
-
contentType?: string;
|
|
48
|
-
}
|
|
49
|
-
/** Notionのプロパティ名マッピング(すべてオプション)。 */
|
|
50
|
-
interface CMSSchemaProperties {
|
|
51
|
-
/** Notionのスラッグプロパティ名。デフォルト: 'Slug' */
|
|
52
|
-
slug?: string;
|
|
53
|
-
/** Notionのステータスプロパティ名。デフォルト: 'Status' */
|
|
54
|
-
status?: string;
|
|
55
|
-
/** Notionの公開日プロパティ名。デフォルト: 'CreatedAt' */
|
|
56
|
-
date?: string;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/** ドキュメントキャッシュを抽象化するインターフェース。 */
|
|
60
|
-
interface DocumentCacheAdapter<T extends BaseContentItem = BaseContentItem> {
|
|
61
|
-
readonly name: string;
|
|
62
|
-
getList(): Promise<CachedItemList<T> | null>;
|
|
63
|
-
setList(data: CachedItemList<T>): Promise<void>;
|
|
64
|
-
getItem(slug: string): Promise<CachedItem<T> | null>;
|
|
65
|
-
setItem(slug: string, data: CachedItem<T>): Promise<void>;
|
|
66
|
-
invalidate?(scope: "all" | {
|
|
67
|
-
slug: string;
|
|
68
|
-
} | {
|
|
69
|
-
tag: string;
|
|
70
|
-
}): Promise<void>;
|
|
71
|
-
}
|
|
72
|
-
/** 画像キャッシュを抽象化するインターフェース。 */
|
|
73
|
-
interface ImageCacheAdapter {
|
|
74
|
-
readonly name: string;
|
|
75
|
-
get(hash: string): Promise<StorageBinary | null>;
|
|
76
|
-
set(hash: string, data: ArrayBuffer, contentType: string): Promise<void>;
|
|
77
|
-
}
|
|
78
|
-
/** キャッシュ設定オブジェクト。document / image それぞれ独立したアダプタを差し込める。 */
|
|
79
|
-
interface CacheConfig<T extends BaseContentItem = BaseContentItem> {
|
|
80
|
-
document?: DocumentCacheAdapter<T> | false;
|
|
81
|
-
image?: ImageCacheAdapter | false;
|
|
82
|
-
/** キャッシュの有効期間(ミリ秒)。未設定の場合はTTLなし。 */
|
|
83
|
-
ttlMs?: number;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
type MaybePromise<T> = T | Promise<T>;
|
|
87
|
-
interface CMSHooks<T extends BaseContentItem = BaseContentItem> {
|
|
88
|
-
beforeCache?: (item: CachedItem<T>) => MaybePromise<CachedItem<T>>;
|
|
89
|
-
afterRender?: (html: string, item: T) => MaybePromise<string>;
|
|
90
|
-
onCacheHit?: (slug: string, item: CachedItem<T>) => void;
|
|
91
|
-
onCacheMiss?: (slug: string) => void;
|
|
92
|
-
onListCacheHit?: (items: T[], cachedAt: number) => void;
|
|
93
|
-
onListCacheMiss?: () => void;
|
|
94
|
-
onError?: (error: Error) => void;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
interface Logger {
|
|
98
|
-
debug?: (message: string, context?: Record<string, unknown>) => void;
|
|
99
|
-
info?: (message: string, context?: Record<string, unknown>) => void;
|
|
100
|
-
warn?: (message: string, context?: Record<string, unknown>) => void;
|
|
101
|
-
error?: (message: string, context?: Record<string, unknown>) => void;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
interface CMSPlugin<T extends BaseContentItem = BaseContentItem> {
|
|
105
|
-
name: string;
|
|
106
|
-
hooks?: CMSHooks<T>;
|
|
107
|
-
logger?: Partial<Logger>;
|
|
108
|
-
}
|
|
109
|
-
declare function definePlugin<T extends BaseContentItem>(plugin: CMSPlugin<T>): CMSPlugin<T>;
|
|
110
|
-
|
|
111
|
-
/** ソース側クエリのフィルタ・ソート条件。 */
|
|
112
|
-
interface SourceQueryOptions {
|
|
113
|
-
filter?: {
|
|
114
|
-
statuses?: string[];
|
|
115
|
-
tags?: string[];
|
|
116
|
-
[key: string]: unknown;
|
|
117
|
-
};
|
|
118
|
-
sort?: {
|
|
119
|
-
property: string;
|
|
120
|
-
direction: "asc" | "desc";
|
|
121
|
-
}[];
|
|
122
|
-
pageSize?: number;
|
|
123
|
-
cursor?: string;
|
|
124
|
-
}
|
|
125
|
-
/** ソース側クエリの結果。 */
|
|
126
|
-
interface SourceQueryResult<T> {
|
|
127
|
-
items: T[];
|
|
128
|
-
hasMore: boolean;
|
|
129
|
-
nextCursor?: string;
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* コンテンツソース(Notion など)を抽象化するインターフェース。
|
|
133
|
-
* core は Notion の知識を持たず、DataSourceAdapter 経由でのみデータを取得する。
|
|
134
|
-
*/
|
|
135
|
-
interface DataSourceAdapter<T extends BaseContentItem = BaseContentItem> {
|
|
136
|
-
readonly name: string;
|
|
137
|
-
readonly publishedStatuses?: readonly string[];
|
|
138
|
-
readonly accessibleStatuses?: readonly string[];
|
|
139
|
-
list(opts?: {
|
|
140
|
-
publishedStatuses?: readonly string[];
|
|
141
|
-
}): Promise<T[]>;
|
|
142
|
-
findBySlug(slug: string): Promise<T | null>;
|
|
143
|
-
loadMarkdown(item: T): Promise<string>;
|
|
144
|
-
/** Notion 側でフィルタ・ソートを行うクエリ(オプション)。 */
|
|
145
|
-
query?(opts: SourceQueryOptions): Promise<SourceQueryResult<T>>;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/** スキーマ設定。公開ステータスのフィルタやプロパティ名マッピングを制御する。 */
|
|
149
|
-
interface SchemaConfig<T extends BaseContentItem = BaseContentItem> {
|
|
150
|
-
/**
|
|
151
|
-
* Notionページをコンテンツ型 T にマッピングするカスタム関数。
|
|
152
|
-
* 指定した場合 properties の設定は無視される(slug プロパティ名のみ例外)。
|
|
153
|
-
*/
|
|
154
|
-
mapItem?: (page: PageObjectResponse) => T;
|
|
155
|
-
/** mapItem 未使用時のプロパティ名マッピング。 */
|
|
156
|
-
properties?: CMSSchemaProperties;
|
|
157
|
-
/** list() で返す「公開済み」ステータス値の配列。デフォルト: [] (全件返す) */
|
|
158
|
-
publishedStatuses?: string[];
|
|
159
|
-
/** findBySlug() でアクセス可能なステータス値の配列。デフォルト: [] (全件許可) */
|
|
160
|
-
accessibleStatuses?: string[];
|
|
161
|
-
}
|
|
162
|
-
/** レンダリング・コンテンツ処理設定。 */
|
|
163
|
-
interface ContentConfig {
|
|
164
|
-
/** 画像プロキシのベースURL。デフォルト: '/api/images' */
|
|
165
|
-
imageProxyBase?: string;
|
|
166
|
-
/** 追加する remark プラグイン。 */
|
|
167
|
-
remarkPlugins?: PluggableList;
|
|
168
|
-
/** 追加する rehype プラグイン。 */
|
|
169
|
-
rehypePlugins?: PluggableList;
|
|
170
|
-
/** デフォルトのパイプラインを置き換えるカスタムレンダラー。 */
|
|
171
|
-
render?: RendererFn;
|
|
172
|
-
/** カスタムブロックハンドラーのマップ。Notionブロックタイプをキーとする。 */
|
|
173
|
-
blocks?: Record<string, BlockHandler>;
|
|
174
|
-
}
|
|
175
|
-
/** レートリミット・リトライ設定。 */
|
|
176
|
-
interface RateLimiterConfig {
|
|
177
|
-
/** 同時実行数の上限。デフォルト: 3 */
|
|
178
|
-
maxConcurrent?: number;
|
|
179
|
-
/** リトライ対象の HTTP ステータスコード。デフォルト: [429, 502, 503] */
|
|
180
|
-
retryOn?: number[];
|
|
181
|
-
/** 最大リトライ回数。デフォルト: 4 */
|
|
182
|
-
maxRetries?: number;
|
|
183
|
-
/** リトライ時の基準待機時間(ミリ秒)。デフォルト: 1000 */
|
|
184
|
-
baseDelayMs?: number;
|
|
185
|
-
}
|
|
186
|
-
/**
|
|
187
|
-
* createCMS() に渡すオプション。
|
|
188
|
-
* ジェネリクス型 T にカスタムコンテンツ型を指定できる(デフォルト: BaseContentItem)。
|
|
189
|
-
*/
|
|
190
|
-
interface CreateCMSOptions<T extends BaseContentItem = BaseContentItem> {
|
|
191
|
-
/** データソースアダプタ(Notion など)。 */
|
|
192
|
-
source: DataSourceAdapter<T>;
|
|
193
|
-
/** キャッシュ設定。未設定時はキャッシュなし。 */
|
|
194
|
-
cache?: CacheConfig<T>;
|
|
195
|
-
/** スキーマ・ステータス設定。 */
|
|
196
|
-
schema?: SchemaConfig<T>;
|
|
197
|
-
/** レンダリング・コンテンツ処理設定。 */
|
|
198
|
-
content?: ContentConfig;
|
|
199
|
-
/** Cloudflare Workers の waitUntil に相当する非同期処理の登録関数。 */
|
|
200
|
-
waitUntil?: (p: Promise<unknown>) => void;
|
|
201
|
-
/** ライフサイクルフック。 */
|
|
202
|
-
hooks?: CMSHooks<T>;
|
|
203
|
-
/** プラグイン配列。フックとロガーを組み合わせて提供できる。 */
|
|
204
|
-
plugins?: CMSPlugin<T>[];
|
|
205
|
-
/** ロガー。 */
|
|
206
|
-
logger?: Logger;
|
|
207
|
-
/** レートリミット・リトライ設定。 */
|
|
208
|
-
rateLimiter?: RateLimiterConfig;
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/** インメモリキャッシュ(ドキュメント用)を生成する。 */
|
|
212
|
-
declare function memoryDocumentCache<T extends BaseContentItem = BaseContentItem>(): DocumentCacheAdapter<T>;
|
|
213
|
-
/** インメモリキャッシュ(画像用)を生成する。 */
|
|
214
|
-
declare function memoryImageCache(): ImageCacheAdapter;
|
|
215
|
-
/**
|
|
216
|
-
* ドキュメントと画像の両方にインメモリキャッシュを返す便利関数。
|
|
217
|
-
* memoryCache() はドキュメントキャッシュを返す(後方互換)。
|
|
218
|
-
*/
|
|
219
|
-
declare function memoryCache<T extends BaseContentItem = BaseContentItem>(): DocumentCacheAdapter<T>;
|
|
220
|
-
|
|
221
|
-
/** 何もしないドキュメントキャッシュを返す(シングルトン)。 */
|
|
222
|
-
declare function noopDocumentCache<T extends BaseContentItem = BaseContentItem>(): DocumentCacheAdapter<T>;
|
|
223
|
-
/** 何もしない画像キャッシュを返す(シングルトン)。 */
|
|
224
|
-
declare function noopImageCache(): ImageCacheAdapter;
|
|
225
|
-
|
|
226
|
-
interface QueryResult<T> {
|
|
227
|
-
items: T[];
|
|
228
|
-
total: number;
|
|
229
|
-
page: number;
|
|
230
|
-
perPage: number;
|
|
231
|
-
hasNext: boolean;
|
|
232
|
-
hasPrev: boolean;
|
|
233
|
-
}
|
|
234
|
-
declare class QueryBuilder<T extends BaseContentItem> {
|
|
235
|
-
private readonly source;
|
|
236
|
-
private readonly defaultStatuses;
|
|
237
|
-
private _statuses;
|
|
238
|
-
private _tags;
|
|
239
|
-
private _predicate;
|
|
240
|
-
private _sortField;
|
|
241
|
-
private _sortDir;
|
|
242
|
-
private _page;
|
|
243
|
-
private _perPage;
|
|
244
|
-
constructor(source: DataSourceAdapter<T>, defaultStatuses?: string[]);
|
|
245
|
-
status(s: string | string[]): this;
|
|
246
|
-
tag(t: string | string[]): this;
|
|
247
|
-
where(predicate: (item: T) => boolean): this;
|
|
248
|
-
sortBy(field: keyof T & string, dir?: "asc" | "desc"): this;
|
|
249
|
-
paginate(opts: {
|
|
250
|
-
page: number;
|
|
251
|
-
perPage: number;
|
|
252
|
-
}): this;
|
|
253
|
-
execute(): Promise<QueryResult<T>>;
|
|
254
|
-
executeOne(): Promise<T | null>;
|
|
255
|
-
adjacent(slug: string): Promise<{
|
|
256
|
-
prev: T | null;
|
|
257
|
-
next: T | null;
|
|
258
|
-
}>;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/** キャッシュ優先アクセサ。 */
|
|
262
|
-
interface CachedAccessor<T extends BaseContentItem> {
|
|
263
|
-
list(): Promise<{
|
|
264
|
-
items: T[];
|
|
265
|
-
isStale: boolean;
|
|
266
|
-
cachedAt: number;
|
|
267
|
-
}>;
|
|
268
|
-
get(slug: string): Promise<CachedItem<T> | null>;
|
|
269
|
-
}
|
|
270
|
-
/** キャッシュ管理オペレーション。 */
|
|
271
|
-
interface CacheManager<T extends BaseContentItem> {
|
|
272
|
-
prefetchAll(opts?: {
|
|
273
|
-
concurrency?: number;
|
|
274
|
-
onProgress?: (done: number, total: number) => void;
|
|
275
|
-
}): Promise<{
|
|
276
|
-
ok: number;
|
|
277
|
-
failed: number;
|
|
278
|
-
}>;
|
|
279
|
-
revalidate(scope?: "all" | {
|
|
280
|
-
slug: string;
|
|
281
|
-
}): Promise<void>;
|
|
282
|
-
sync(payload?: {
|
|
283
|
-
slug?: string;
|
|
284
|
-
}): Promise<{
|
|
285
|
-
updated: string[];
|
|
286
|
-
}>;
|
|
287
|
-
checkList(version: string): Promise<{
|
|
288
|
-
changed: false;
|
|
289
|
-
} | {
|
|
290
|
-
changed: true;
|
|
291
|
-
items: T[];
|
|
292
|
-
}>;
|
|
293
|
-
checkItem(slug: string, lastEdited: string): Promise<{
|
|
294
|
-
changed: false;
|
|
295
|
-
} | {
|
|
296
|
-
changed: true;
|
|
297
|
-
html: string;
|
|
298
|
-
item: T;
|
|
299
|
-
notionUpdatedAt: string;
|
|
300
|
-
}>;
|
|
301
|
-
}
|
|
302
|
-
/**
|
|
303
|
-
* Notion をバックエンドとして使う汎用ヘッドレス CMS クラス。
|
|
304
|
-
*
|
|
305
|
-
* @example
|
|
306
|
-
* const cms = createCMS({
|
|
307
|
-
* source: notionAdapter({ token: '...', dataSourceId: '...' }),
|
|
308
|
-
* });
|
|
309
|
-
* const items = await cms.list();
|
|
310
|
-
*/
|
|
311
|
-
declare class CMS<T extends BaseContentItem = BaseContentItem> {
|
|
312
|
-
private readonly source;
|
|
313
|
-
private readonly docCache;
|
|
314
|
-
private readonly imgCache;
|
|
315
|
-
private readonly hasImageCache;
|
|
316
|
-
private readonly ttlMs;
|
|
317
|
-
private readonly publishedStatuses;
|
|
318
|
-
private readonly accessibleStatuses;
|
|
319
|
-
private readonly imageProxyBase;
|
|
320
|
-
private readonly contentConfig;
|
|
321
|
-
private readonly waitUntil;
|
|
322
|
-
private readonly hooks;
|
|
323
|
-
private readonly logger;
|
|
324
|
-
private readonly retryConfig;
|
|
325
|
-
readonly cached: CachedAccessor<T>;
|
|
326
|
-
readonly cache: CacheManager<T>;
|
|
327
|
-
constructor(opts: CreateCMSOptions<T>);
|
|
328
|
-
/** 公開済みコンテンツ一覧をソースから直接取得する。 */
|
|
329
|
-
list(): Promise<T[]>;
|
|
330
|
-
/** スラッグでコンテンツをソースから直接取得する。 */
|
|
331
|
-
find(slug: string): Promise<T | null>;
|
|
332
|
-
/** @deprecated find() を使用してください。 */
|
|
333
|
-
findBySlug(slug: string): Promise<T | null>;
|
|
334
|
-
/** アイテムが publishedStatuses に含まれるステータスかどうかを返す。 */
|
|
335
|
-
isPublished(item: T): boolean;
|
|
336
|
-
/** コンテンツを Markdown → HTML にレンダリングし、CachedItem として返す。 */
|
|
337
|
-
render(item: T): Promise<CachedItem<T>>;
|
|
338
|
-
/** QueryBuilder を返す。ステータス・タグ・ページネーションなどを連鎖で指定できる。 */
|
|
339
|
-
query(): QueryBuilder<T>;
|
|
340
|
-
/** 全コンテンツを事前レンダリングしてキャッシュに保存する。 */
|
|
341
|
-
prefetchAll(opts?: {
|
|
342
|
-
concurrency?: number;
|
|
343
|
-
onProgress?: (done: number, total: number) => void;
|
|
344
|
-
}): Promise<{
|
|
345
|
-
ok: number;
|
|
346
|
-
failed: number;
|
|
347
|
-
}>;
|
|
348
|
-
/** 静的生成用のスラッグ一覧を返す。 */
|
|
349
|
-
getStaticSlugs(): Promise<string[]>;
|
|
350
|
-
/** 指定スコープのキャッシュを無効化する。 */
|
|
351
|
-
revalidate(scope?: "all" | {
|
|
352
|
-
slug: string;
|
|
353
|
-
}): Promise<void>;
|
|
354
|
-
/** Webhook ペイロードを元にキャッシュを同期する。 */
|
|
355
|
-
syncFromWebhook(payload?: {
|
|
356
|
-
slug?: string;
|
|
357
|
-
}): Promise<{
|
|
358
|
-
updated: string[];
|
|
359
|
-
}>;
|
|
360
|
-
/** キャッシュ優先でコンテンツ一覧を返す(SWR)。 */
|
|
361
|
-
private cachedList;
|
|
362
|
-
/** キャッシュ優先で単一コンテンツを返す(SWR)。 */
|
|
363
|
-
private cachedGet;
|
|
364
|
-
checkListUpdate(version: string): Promise<{
|
|
365
|
-
changed: false;
|
|
366
|
-
} | {
|
|
367
|
-
changed: true;
|
|
368
|
-
items: T[];
|
|
369
|
-
}>;
|
|
370
|
-
checkItemUpdate(slug: string, lastEdited: string): Promise<{
|
|
371
|
-
changed: false;
|
|
372
|
-
} | {
|
|
373
|
-
changed: true;
|
|
374
|
-
html: string;
|
|
375
|
-
item: T;
|
|
376
|
-
notionUpdatedAt: string;
|
|
377
|
-
}>;
|
|
378
|
-
/** @deprecated cached.list() を使用してください。 */
|
|
379
|
-
getList(): Promise<{
|
|
380
|
-
items: T[];
|
|
381
|
-
listVersion: string;
|
|
382
|
-
}>;
|
|
383
|
-
/** @deprecated cached.get() を使用してください。 */
|
|
384
|
-
getItem(slug: string): Promise<CachedItem<T> | null>;
|
|
385
|
-
/** @deprecated cache.prefetchAll() を使用してください。 */
|
|
386
|
-
prefetchAllLegacy(opts?: {
|
|
387
|
-
concurrency?: number;
|
|
388
|
-
onProgress?: (done: number, total: number) => void;
|
|
389
|
-
}): Promise<{
|
|
390
|
-
ok: number;
|
|
391
|
-
failed: number;
|
|
392
|
-
}>;
|
|
393
|
-
/** @deprecated query().status(s).execute() を使用してください。 */
|
|
394
|
-
listByStatus(status: string | readonly string[]): Promise<T[]>;
|
|
395
|
-
/** @deprecated query().where(pred).execute() を使用してください。 */
|
|
396
|
-
where(predicate: (item: T) => boolean): Promise<T[]>;
|
|
397
|
-
/** @deprecated query().paginate(opts).execute() を使用してください。 */
|
|
398
|
-
paginate(opts: {
|
|
399
|
-
page: number;
|
|
400
|
-
perPage: number;
|
|
401
|
-
}): Promise<{
|
|
402
|
-
items: T[];
|
|
403
|
-
total: number;
|
|
404
|
-
page: number;
|
|
405
|
-
perPage: number;
|
|
406
|
-
hasNext: boolean;
|
|
407
|
-
}>;
|
|
408
|
-
/** @deprecated query().adjacent(slug) を使用してください。 */
|
|
409
|
-
getAdjacent(slug: string): Promise<{
|
|
410
|
-
prev: T | null;
|
|
411
|
-
next: T | null;
|
|
412
|
-
}>;
|
|
413
|
-
/** ハッシュキーでキャッシュ画像を取得する。 */
|
|
414
|
-
getCachedImage(hash: string): Promise<StorageBinary | null>;
|
|
415
|
-
/** ハッシュキーでキャッシュ画像を Response として返す。 */
|
|
416
|
-
createCachedImageResponse(hash: string): Promise<Response | null>;
|
|
417
|
-
private buildCachedItem;
|
|
418
|
-
}
|
|
419
|
-
/** 設定済みの CMS インスタンスを生成するファクトリ関数。 */
|
|
420
|
-
declare function createCMS<T extends BaseContentItem = BaseContentItem>(opts: CreateCMSOptions<T>): CMS<T>;
|
|
421
|
-
|
|
422
|
-
type CMSErrorCode = "CONFIG_INVALID" | "NOTION_ITEM_SCHEMA_INVALID" | "NOTION_FETCH_ITEMS_FAILED" | "NOTION_FETCH_ITEM_BY_SLUG_FAILED" | "NOTION_GET_BLOCKS_FAILED" | "NOTION_MARKDOWN_FETCH_FAILED" | "IMAGE_CACHE_FAILED" | "RENDERER_FAILED";
|
|
423
|
-
interface CMSErrorContext {
|
|
424
|
-
operation: string;
|
|
425
|
-
slug?: string;
|
|
426
|
-
dataSourceId?: string;
|
|
427
|
-
pageId?: string;
|
|
428
|
-
[key: string]: string | number | boolean | null | undefined;
|
|
429
|
-
}
|
|
430
|
-
declare class CMSError extends Error {
|
|
431
|
-
readonly code: CMSErrorCode;
|
|
432
|
-
readonly cause?: unknown;
|
|
433
|
-
readonly context: CMSErrorContext;
|
|
434
|
-
constructor(params: {
|
|
435
|
-
code: CMSErrorCode;
|
|
436
|
-
message: string;
|
|
437
|
-
cause?: unknown;
|
|
438
|
-
context: CMSErrorContext;
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
declare function isCMSError(error: unknown): error is CMSError;
|
|
442
|
-
|
|
443
|
-
/**
|
|
444
|
-
* プラグイン配列とダイレクトフックを合成して単一の CMSHooks を返す。
|
|
445
|
-
* beforeCache / afterRender はパイプライン(前の出力が次の入力)。
|
|
446
|
-
* onCacheHit などオブザーバー系は全員に同じ値を渡す。
|
|
447
|
-
*/
|
|
448
|
-
declare function mergeHooks<T extends BaseContentItem>(plugins: CMSPlugin<T>[], directHooks?: CMSHooks<T>): CMSHooks<T>;
|
|
449
|
-
/** プラグイン配列とダイレクトロガーを合成して単一の Logger を返す。 */
|
|
450
|
-
declare function mergeLoggers(plugins: Array<{
|
|
451
|
-
logger?: Partial<Logger>;
|
|
452
|
-
}>, directLogger?: Logger): Logger | undefined;
|
|
453
|
-
|
|
454
|
-
/** Notionリッチテキスト配列をプレーンテキストに結合する。 */
|
|
455
|
-
declare function getPlainText(items: RichTextItemResponse[] | undefined): string;
|
|
456
|
-
/**
|
|
457
|
-
* NotionのPageObjectResponseをデフォルトの BaseContentItem に変換する。
|
|
458
|
-
* 独自の拡張型(title などを含む)が必要な場合は、本関数の戻り値に
|
|
459
|
-
* 追加フィールドを足してカスタム mapItem を実装する。
|
|
460
|
-
*/
|
|
461
|
-
declare function mapItem(page: PageObjectResponse, props: Required<CMSSchemaProperties>): BaseContentItem;
|
|
462
|
-
|
|
463
|
-
interface RetryConfig {
|
|
464
|
-
maxConcurrent: number;
|
|
465
|
-
retryOn: number[];
|
|
466
|
-
maxRetries: number;
|
|
467
|
-
baseDelayMs: number;
|
|
468
|
-
onRetry?: (attempt: number, status: number) => void;
|
|
469
|
-
}
|
|
470
|
-
declare const DEFAULT_RETRY_CONFIG: RetryConfig;
|
|
471
|
-
/** 指数バックオフでリトライする。retryOn に含まれる HTTP エラーのみ対象。 */
|
|
472
|
-
declare function withRetry<T>(fn: () => Promise<T>, config: RetryConfig): Promise<T>;
|
|
473
|
-
|
|
474
|
-
export { type BaseContentItem, CMS, CMSError, type CMSErrorCode, type CMSErrorContext, type CMSHooks, type CMSPlugin, type CMSSchemaProperties, type CacheConfig, type CachedItem, type CachedItemList, type ContentConfig, type CreateCMSOptions, DEFAULT_RETRY_CONFIG, type DataSourceAdapter, type DocumentCacheAdapter, type ImageCacheAdapter, type Logger, type MaybePromise, QueryBuilder, type QueryResult, type RateLimiterConfig, type RetryConfig, type SchemaConfig, type SourceQueryOptions, type SourceQueryResult, type StorageBinary, createCMS, definePlugin, getPlainText, isCMSError, isStale, mapItem, memoryCache, memoryDocumentCache, memoryImageCache, mergeHooks, mergeLoggers, noopDocumentCache, noopImageCache, sha256Hex, withRetry };
|