@notion-headless-cms/core 0.1.1 → 0.1.2
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.ts +54 -0
- package/dist/cache/memory.js +15 -0
- package/dist/cache/memory.js.map +1 -0
- package/dist/cache/noop.d.ts +9 -0
- package/dist/cache/noop.js +9 -0
- package/dist/cache/noop.js.map +1 -0
- package/dist/cache-DvbyemBK.d.ts +33 -0
- package/dist/chunk-4KGKWKKI.js +80 -0
- package/dist/chunk-4KGKWKKI.js.map +1 -0
- package/dist/chunk-6DG63XUF.js +42 -0
- package/dist/chunk-6DG63XUF.js.map +1 -0
- package/dist/chunk-6LHROEPI.js +104 -0
- package/dist/chunk-6LHROEPI.js.map +1 -0
- package/dist/chunk-V6ML4QE5.js +26 -0
- package/dist/chunk-V6ML4QE5.js.map +1 -0
- package/dist/content-Biqf0l_o.d.ts +51 -0
- package/dist/errors.d.ts +30 -0
- package/dist/errors.js +11 -0
- package/dist/errors.js.map +1 -0
- package/dist/hooks-B83RUclt.d.ts +41 -0
- package/dist/hooks.d.ts +2 -0
- package/dist/hooks.js +9 -0
- package/dist/hooks.js.map +1 -0
- package/dist/index.d.ts +57 -253
- package/dist/index.js +176 -344
- package/dist/index.js.map +1 -0
- package/package.json +29 -11
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cache.ts","../src/image.ts","../src/query.ts","../src/retry.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 { BaseContentItem } from \"./types/content\";\nimport type { DataSourceAdapter } from \"./types/source\";\n\nexport interface QueryResult<T> {\n\titems: T[];\n\ttotal: number;\n\tpage: number;\n\tperPage: number;\n\thasNext: boolean;\n\thasPrev: boolean;\n}\n\nexport class QueryBuilder<T extends BaseContentItem> {\n\tprivate readonly source: DataSourceAdapter<T>;\n\tprivate readonly defaultStatuses: string[];\n\n\tprivate _statuses: string[] = [];\n\tprivate _tags: string[] = [];\n\tprivate _predicate: ((item: T) => boolean) | undefined;\n\tprivate _sortField: (keyof T & string) | undefined;\n\tprivate _sortDir: \"asc\" | \"desc\" = \"asc\";\n\tprivate _page = 1;\n\tprivate _perPage = 20;\n\n\tconstructor(source: DataSourceAdapter<T>, defaultStatuses: string[] = []) {\n\t\tthis.source = source;\n\t\tthis.defaultStatuses = defaultStatuses;\n\t}\n\n\tstatus(s: string | string[]): this {\n\t\tthis._statuses = Array.isArray(s) ? s : [s];\n\t\treturn this;\n\t}\n\n\ttag(t: string | string[]): this {\n\t\tthis._tags = Array.isArray(t) ? t : [t];\n\t\treturn this;\n\t}\n\n\twhere(predicate: (item: T) => boolean): this {\n\t\tthis._predicate = predicate;\n\t\treturn this;\n\t}\n\n\tsortBy(field: keyof T & string, dir: \"asc\" | \"desc\" = \"asc\"): this {\n\t\tthis._sortField = field;\n\t\tthis._sortDir = dir;\n\t\treturn this;\n\t}\n\n\tpaginate(opts: { page: number; perPage: number }): this {\n\t\tthis._page = opts.page;\n\t\tthis._perPage = opts.perPage;\n\t\treturn this;\n\t}\n\n\tasync execute(): Promise<QueryResult<T>> {\n\t\tconst statuses =\n\t\t\tthis._statuses.length > 0\n\t\t\t\t? this._statuses\n\t\t\t\t: this.defaultStatuses.length > 0\n\t\t\t\t\t? this.defaultStatuses\n\t\t\t\t\t: undefined;\n\n\t\t// push-down: source.query が存在 かつ where() 未使用 → Notion API に委譲\n\t\tif (this.source.query && !this._predicate) {\n\t\t\tconst result = await this.source.query({\n\t\t\t\tfilter: {\n\t\t\t\t\tstatuses,\n\t\t\t\t\ttags: this._tags.length > 0 ? this._tags : undefined,\n\t\t\t\t},\n\t\t\t\tsort: this._sortField\n\t\t\t\t\t? [{ property: this._sortField, direction: this._sortDir }]\n\t\t\t\t\t: undefined,\n\t\t\t\tpageSize: this._perPage,\n\t\t\t});\n\t\t\tconst items = result.items;\n\t\t\treturn {\n\t\t\t\titems,\n\t\t\t\ttotal: items.length,\n\t\t\t\tpage: this._page,\n\t\t\t\tperPage: this._perPage,\n\t\t\t\thasNext: result.hasMore,\n\t\t\t\thasPrev: this._page > 1,\n\t\t\t};\n\t\t}\n\n\t\t// フォールバック: インメモリフィルタ\n\t\tlet items = await this.source.list({\n\t\t\tpublishedStatuses: statuses,\n\t\t});\n\n\t\tif (this._tags.length > 0) {\n\t\t\titems = items.filter((item) => {\n\t\t\t\tconst itemTags = (item as Record<string, unknown>).tags;\n\t\t\t\tif (!Array.isArray(itemTags)) return false;\n\t\t\t\treturn this._tags.some((tag) => (itemTags as string[]).includes(tag));\n\t\t\t});\n\t\t}\n\n\t\tif (this._predicate) {\n\t\t\titems = items.filter(this._predicate);\n\t\t}\n\n\t\tif (this._sortField) {\n\t\t\tconst field = this._sortField;\n\t\t\tconst dir = this._sortDir;\n\t\t\titems = [...items].sort((a, b) => {\n\t\t\t\tconst av = a[field] as string | number;\n\t\t\t\tconst bv = b[field] as string | number;\n\t\t\t\tconst cmp = av < bv ? -1 : av > bv ? 1 : 0;\n\t\t\t\treturn dir === \"asc\" ? cmp : -cmp;\n\t\t\t});\n\t\t}\n\n\t\tconst total = items.length;\n\t\tconst start = (this._page - 1) * this._perPage;\n\t\tconst paged = items.slice(start, start + this._perPage);\n\n\t\treturn {\n\t\t\titems: paged,\n\t\t\ttotal,\n\t\t\tpage: this._page,\n\t\t\tperPage: this._perPage,\n\t\t\thasNext: start + this._perPage < total,\n\t\t\thasPrev: this._page > 1,\n\t\t};\n\t}\n\n\tasync executeOne(): Promise<T | null> {\n\t\tconst result = await this.execute();\n\t\treturn result.items[0] ?? null;\n\t}\n\n\t/** 前後アイテムを返す。sortBy() で指定したソート順を適用する。 */\n\tasync adjacent(slug: string): Promise<{ prev: T | null; next: T | null }> {\n\t\tconst statuses =\n\t\t\tthis._statuses.length > 0\n\t\t\t\t? this._statuses\n\t\t\t\t: this.defaultStatuses.length > 0\n\t\t\t\t\t? this.defaultStatuses\n\t\t\t\t\t: undefined;\n\t\tlet items = await this.source.list({ publishedStatuses: statuses });\n\n\t\tif (this._sortField) {\n\t\t\tconst field = this._sortField;\n\t\t\tconst dir = this._sortDir;\n\t\t\titems = [...items].sort((a, b) => {\n\t\t\t\tconst av = a[field] as string | number;\n\t\t\t\tconst bv = b[field] as string | number;\n\t\t\t\tconst cmp = av < bv ? -1 : av > bv ? 1 : 0;\n\t\t\t\treturn dir === \"asc\" ? cmp : -cmp;\n\t\t\t});\n\t\t}\n\n\t\tconst idx = items.findIndex((item) => item.slug === slug);\n\t\tif (idx === -1) return { prev: null, next: null };\n\t\treturn {\n\t\t\tprev: idx > 0 ? items[idx - 1] : null,\n\t\t\tnext: idx < items.length - 1 ? items[idx + 1] : null,\n\t\t};\n\t}\n\n\t/** 最初の 1 件を返す。`.paginate({ page: 1, perPage: 1 }).executeOne()` の短縮形。 */\n\tfirst(): Promise<T | null> {\n\t\treturn this.paginate({ page: 1, perPage: 1 }).executeOne();\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 { noopDocumentCache, noopImageCache } from \"./cache/noop\";\nimport { CMSError, isCMSError } from \"./errors\";\nimport { mergeHooks, mergeLoggers } from \"./hooks\";\nimport { buildCacheImageFn } from \"./image\";\nimport { QueryBuilder } from \"./query\";\nimport type { RetryConfig } from \"./retry\";\nimport { DEFAULT_RETRY_CONFIG, withRetry } from \"./retry\";\nimport type {\n\tBaseContentItem,\n\tCacheConfig,\n\tCachedItem,\n\tCMSHooks,\n\tCreateCMSOptions,\n\tDataSourceAdapter,\n\tDocumentCacheAdapter,\n\tImageCacheAdapter,\n\tLogger,\n\tRendererFn,\n\tStorageBinary,\n} from \"./types/index\";\n\nconst DEFAULT_IMAGE_PROXY_BASE = \"/api/images\";\n\nfunction resolveDocumentCache<T extends BaseContentItem>(\n\tcache: CacheConfig<T> | undefined,\n): DocumentCacheAdapter<T> {\n\tif (!cache || cache === \"disabled\" || !cache.document) {\n\t\treturn noopDocumentCache<T>();\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/** キャッシュ系の公開名前空間。SWR 読み取りとキャッシュ管理を統合する。 */\nexport interface CacheAccessor<T extends BaseContentItem> {\n\t/** キャッシュ優先でコンテンツ一覧を返す(SWR)。 */\n\tgetList(): Promise<{ items: T[]; isStale: boolean; cachedAt: number }>;\n\t/** キャッシュ優先で単一コンテンツを返す(SWR)。HTML 付きの CachedItem を返す。 */\n\tget(slug: string): Promise<CachedItem<T> | null>;\n\t/** 全コンテンツを事前レンダリングしてキャッシュに保存する。 */\n\tprefetchAll(opts?: {\n\t\tconcurrency?: number;\n\t\tonProgress?: (done: number, total: number) => void;\n\t}): Promise<{ ok: number; failed: number }>;\n\t/** 指定スコープのキャッシュを無効化する。 */\n\trevalidate(scope?: \"all\" | { slug: string }): Promise<void>;\n\t/** Webhook ペイロードを元にキャッシュを同期する。 */\n\tsync(payload?: { slug?: string }): Promise<{ updated: string[] }>;\n\t/** リスト全体の変更を検知する。version はリスト取得時の buildListVersion の値。 */\n\tcheckList(\n\t\tversion: string,\n\t): Promise<{ changed: false } | { changed: true; items: T[] }>;\n\t/** 単一アイテムの変更を検知し、変更があれば再レンダリングしてキャッシュを更新する。 */\n\tcheckItem(\n\t\tslug: string,\n\t\tlastEdited: string,\n\t): Promise<\n\t\t| { changed: false }\n\t\t| { changed: true; html: string; item: T; notionUpdatedAt: string }\n\t>;\n}\n\n/**\n * Notion をバックエンドとして使う汎用ヘッドレス CMS クラス。\n *\n * @example\n * const cms = createCMS({\n * source: notionAdapter({ token: '...', dataSourceId: '...' }),\n * });\n * const items = await cms.list();\n * const entry = await cms.cache.get('my-post');\n */\nexport class CMS<T extends BaseContentItem = BaseContentItem> {\n\tprivate readonly source: DataSourceAdapter<T>;\n\tprivate readonly docCache: DocumentCacheAdapter<T>;\n\tprivate readonly imgCache: ImageCacheAdapter;\n\tprivate readonly hasImageCache: boolean;\n\tprivate readonly ttlMs: number | undefined;\n\tprivate readonly publishedStatuses: string[];\n\tprivate readonly accessibleStatuses: string[];\n\tprivate readonly imageProxyBase: string;\n\tprivate readonly contentConfig: CreateCMSOptions<T>[\"content\"];\n\tprivate readonly rendererFn: RendererFn | undefined;\n\tprivate readonly waitUntil: ((p: Promise<unknown>) => void) | undefined;\n\tprivate readonly hooks: CMSHooks<T>;\n\tprivate readonly logger: Logger | undefined;\n\tprivate readonly retryConfig: RetryConfig;\n\tprivate readonly maxConcurrent: number;\n\n\treadonly cache: CacheAccessor<T>;\n\n\tconstructor(opts: CreateCMSOptions<T>) {\n\t\tthis.source = opts.source;\n\t\tthis.docCache = resolveDocumentCache(opts.cache);\n\t\tthis.imgCache = resolveImageCache(opts.cache);\n\t\tthis.hasImageCache = hasImageCacheConfigured(opts.cache);\n\t\tthis.ttlMs = resolveTtl(opts.cache);\n\t\tthis.publishedStatuses =\n\t\t\topts.schema?.publishedStatuses ??\n\t\t\t(opts.source.publishedStatuses ? [...opts.source.publishedStatuses] : []);\n\t\tthis.accessibleStatuses =\n\t\t\topts.schema?.accessibleStatuses ??\n\t\t\t(opts.source.accessibleStatuses\n\t\t\t\t? [...opts.source.accessibleStatuses]\n\t\t\t\t: []);\n\t\tthis.imageProxyBase =\n\t\t\topts.content?.imageProxyBase ?? DEFAULT_IMAGE_PROXY_BASE;\n\t\tthis.contentConfig = opts.content;\n\t\tthis.rendererFn = opts.renderer;\n\t\tthis.waitUntil = opts.waitUntil;\n\t\tthis.logger = mergeLoggers(opts.plugins ?? [], opts.logger);\n\t\tthis.hooks = mergeHooks(opts.plugins ?? [], opts.hooks, this.logger);\n\t\tthis.maxConcurrent = opts.rateLimiter?.maxConcurrent ?? 3;\n\t\tthis.retryConfig = {\n\t\t\t...DEFAULT_RETRY_CONFIG,\n\t\t\t...(opts.rateLimiter ?? {}),\n\t\t};\n\n\t\tthis.cache = {\n\t\t\tgetList: this.cachedList.bind(this),\n\t\t\tget: this.cachedGet.bind(this),\n\t\t\tprefetchAll: this.prefetchAll.bind(this),\n\t\t\trevalidate: this.revalidate.bind(this),\n\t\t\tsync: this.syncFromWebhook.bind(this),\n\t\t\tcheckList: this.checkListUpdate.bind(this),\n\t\t\tcheckItem: this.checkItemUpdate.bind(this),\n\t\t};\n\t}\n\n\t// ── コンテンツ取得 ──────────────────────────────────────────────────────\n\n\t/** 公開済みコンテンツ一覧をソースから直接取得する。 */\n\tlist(): Promise<T[]> {\n\t\treturn withRetry(\n\t\t\t() =>\n\t\t\t\tthis.source.list({\n\t\t\t\t\tpublishedStatuses:\n\t\t\t\t\t\tthis.publishedStatuses.length > 0\n\t\t\t\t\t\t\t? this.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.retryConfig,\n\t\t\t\tonRetry: (attempt, status) => {\n\t\t\t\t\tthis.logger?.warn?.(\"list() リトライ中\", { attempt, status });\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t}\n\n\t/** スラッグでコンテンツをソースから直接取得する。 */\n\tasync find(slug: string): Promise<T | null> {\n\t\tconst item = await withRetry(() => this.source.findBySlug(slug), {\n\t\t\t...this.retryConfig,\n\t\t\tonRetry: (attempt, status) => {\n\t\t\t\tthis.logger?.warn?.(\"find() リトライ中\", { attempt, status, slug });\n\t\t\t},\n\t\t});\n\t\tif (!item) return null;\n\t\tif (\n\t\t\tthis.accessibleStatuses.length > 0 &&\n\t\t\t(!item.status || !this.accessibleStatuses.includes(item.status))\n\t\t) {\n\t\t\treturn null;\n\t\t}\n\t\treturn item;\n\t}\n\n\t/** 複数スラッグをまとめてソースから直接取得する。accessibleStatuses フィルタを適用する。 */\n\tasync findMany(slugs: string[]): Promise<Map<string, T>> {\n\t\tconst results = new Map<string, T>();\n\t\tawait Promise.all(\n\t\t\tslugs.map(async (slug) => {\n\t\t\t\tconst item = await this.find(slug);\n\t\t\t\tif (item) results.set(slug, item);\n\t\t\t}),\n\t\t);\n\t\treturn results;\n\t}\n\n\t/** アイテムが publishedStatuses に含まれるステータスかどうかを返す。 */\n\tisPublished(item: T): boolean {\n\t\tif (this.publishedStatuses.length === 0) return true;\n\t\treturn !!item.status && this.publishedStatuses.includes(item.status);\n\t}\n\n\t/** コンテンツを Markdown → HTML にレンダリングし、CachedItem として返す。キャッシュには保存しない。 */\n\tasync render(item: T): Promise<CachedItem<T>> {\n\t\treturn this.buildCachedItem(item);\n\t}\n\n\t/** QueryBuilder を返す。ステータス・タグ・ページネーションなどを連鎖で指定できる。 */\n\tquery(): QueryBuilder<T> {\n\t\treturn new QueryBuilder(this.source, this.publishedStatuses);\n\t}\n\n\t/** 静的生成用のスラッグ一覧を返す。 */\n\tasync getStaticSlugs(): Promise<string[]> {\n\t\tconst items = await this.list();\n\t\treturn items.map((item) => item.slug);\n\t}\n\n\t// ── 画像配信 ──────────────────────────────────────────────────────────\n\n\t/** ハッシュキーでキャッシュ画像を取得する。 */\n\tgetCachedImage(hash: string): Promise<StorageBinary | null> {\n\t\treturn this.imgCache.get(hash);\n\t}\n\n\t/** ハッシュキーでキャッシュ画像を Response として返す。 */\n\tasync createCachedImageResponse(hash: string): Promise<Response | null> {\n\t\tconst object = await this.imgCache.get(hash);\n\t\tif (!object) return null;\n\t\tconst headers = new Headers();\n\t\tif (object.contentType) headers.set(\"content-type\", object.contentType);\n\t\theaders.set(\"cache-control\", \"public, max-age=31536000, immutable\");\n\t\treturn new Response(object.data, { headers });\n\t}\n\n\t// ── キャッシュ優先取得(Stale-While-Revalidate) ─────────────────────\n\n\tprivate async cachedList(): Promise<{\n\t\titems: T[];\n\t\tisStale: boolean;\n\t\tcachedAt: number;\n\t}> {\n\t\tconst cached = await this.docCache.getList();\n\t\tif (cached && !isStale(cached.cachedAt, this.ttlMs)) {\n\t\t\tthis.hooks.onListCacheHit?.(cached.items, cached.cachedAt);\n\t\t\treturn { items: cached.items, isStale: false, cachedAt: cached.cachedAt };\n\t\t}\n\n\t\tthis.hooks.onListCacheMiss?.();\n\t\tconst items = await this.list();\n\t\tconst cachedAt = Date.now();\n\t\tconst save = this.docCache.setList({ items, cachedAt });\n\t\tif (this.waitUntil) {\n\t\t\tthis.waitUntil(save);\n\t\t} else {\n\t\t\tawait save;\n\t\t}\n\t\treturn { items, isStale: !!cached, cachedAt };\n\t}\n\n\tprivate async cachedGet(slug: string): Promise<CachedItem<T> | null> {\n\t\tconst cached = await this.docCache.getItem(slug);\n\t\tif (cached && !isStale(cached.cachedAt, this.ttlMs)) {\n\t\t\tthis.hooks.onCacheHit?.(slug, cached);\n\t\t\treturn cached;\n\t\t}\n\n\t\tthis.hooks.onCacheMiss?.(slug);\n\t\tconst item = await this.find(slug);\n\t\tif (!item) return null;\n\t\tconst entry = await this.buildCachedItem(item);\n\t\tconst save = this.docCache.setItem(slug, entry);\n\t\tif (this.waitUntil) {\n\t\t\tthis.waitUntil(save);\n\t\t} else {\n\t\t\tawait save;\n\t\t}\n\t\treturn entry;\n\t}\n\n\t// ── キャッシュ管理 ────────────────────────────────────────────────────\n\n\tprivate async prefetchAll(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.list();\n\t\tconst concurrency = opts?.concurrency ?? this.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 this.buildCachedItem(item);\n\t\t\t\t\t\tawait this.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.logger?.warn?.(\n\t\t\t\t\t\t\t\"prefetchAll: アイテムの事前レンダリングに失敗\",\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.docCache.setList({ items, cachedAt: Date.now() });\n\t\treturn { ok, failed };\n\t}\n\n\tprivate async revalidate(scope?: \"all\" | { slug: string }): Promise<void> {\n\t\tif (!this.docCache.invalidate) return;\n\t\tawait this.docCache.invalidate(scope ?? \"all\");\n\t}\n\n\tprivate async syncFromWebhook(payload?: {\n\t\tslug?: string;\n\t}): Promise<{ updated: string[] }> {\n\t\tconst updated: string[] = [];\n\n\t\tif (payload?.slug) {\n\t\t\tconst item = await this.find(payload.slug);\n\t\t\tif (item) {\n\t\t\t\tconst rendered = await this.buildCachedItem(item);\n\t\t\t\tawait this.docCache.setItem(item.slug, rendered);\n\t\t\t\tupdated.push(item.slug);\n\t\t\t}\n\t\t} else {\n\t\t\tconst result = await this.prefetchAll();\n\t\t\tif (result.ok > 0) {\n\t\t\t\tconst items = await this.list();\n\t\t\t\tfor (const item of items) updated.push(item.slug);\n\t\t\t}\n\t\t}\n\n\t\treturn { updated };\n\t}\n\n\tprivate async checkListUpdate(\n\t\tversion: string,\n\t): Promise<{ changed: false } | { changed: true; items: T[] }> {\n\t\tconst items = await this.list();\n\t\tconst serverVersion = buildListVersion(items);\n\t\tif (serverVersion === version) return { changed: false };\n\t\tawait this.docCache.setList({ items, cachedAt: Date.now() });\n\t\treturn { changed: true, items };\n\t}\n\n\tprivate async checkItemUpdate(\n\t\tslug: string,\n\t\tlastEdited: string,\n\t): Promise<\n\t\t| { changed: false }\n\t\t| { changed: true; html: string; item: T; notionUpdatedAt: string }\n\t> {\n\t\tconst item = await this.find(slug);\n\t\tif (!item) return { changed: false };\n\t\tif (!this.isPublished(item)) return { changed: false };\n\t\tif (item.updatedAt === lastEdited) return { changed: false };\n\n\t\tconst entry = await this.buildCachedItem(item);\n\t\tawait this.docCache.setItem(slug, entry);\n\n\t\treturn {\n\t\t\tchanged: true,\n\t\t\thtml: entry.html,\n\t\t\titem: entry.item,\n\t\t\tnotionUpdatedAt: entry.notionUpdatedAt,\n\t\t};\n\t}\n\n\t// ── プライベートヘルパー ────────────────────────────────────────────────\n\n\tprivate async buildCachedItem(item: T): Promise<CachedItem<T>> {\n\t\tconst start = Date.now();\n\t\tthis.logger?.info?.(\"コンテンツのレンダリング開始\", {\n\t\t\tslug: item.slug,\n\t\t\tpageId: item.id,\n\t\t});\n\t\tthis.hooks.onRenderStart?.(item.slug);\n\n\t\tlet markdown: string;\n\t\ttry {\n\t\t\tmarkdown = await this.source.loadMarkdown(item);\n\t\t} catch (err) {\n\t\t\tif (isCMSError(err)) throw err;\n\t\t\tthrow new CMSError({\n\t\t\t\tcode: \"source/load_markdown_failed\",\n\t\t\t\tmessage: \"Failed to load markdown from source.\",\n\t\t\t\tcause: err,\n\t\t\t\tcontext: {\n\t\t\t\t\toperation: \"buildCachedItem:loadMarkdown\",\n\t\t\t\t\tpageId: item.id,\n\t\t\t\t\tslug: item.slug,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\tconst cacheImage = this.hasImageCache\n\t\t\t? buildCacheImageFn(this.imgCache, this.imageProxyBase)\n\t\t\t: undefined;\n\n\t\tlet html: string;\n\t\tconst rendererFn = this.rendererFn ?? (await loadDefaultRenderer());\n\t\ttry {\n\t\t\thtml = await rendererFn(markdown, {\n\t\t\t\timageProxyBase: this.imageProxyBase,\n\t\t\t\tcacheImage,\n\t\t\t\tremarkPlugins: this.contentConfig?.remarkPlugins,\n\t\t\t\trehypePlugins: this.contentConfig?.rehypePlugins,\n\t\t\t});\n\t\t} catch (err) {\n\t\t\tif (isCMSError(err)) throw err;\n\t\t\tthrow new CMSError({\n\t\t\t\tcode: \"renderer/failed\",\n\t\t\t\tmessage: \"Failed to render markdown.\",\n\t\t\t\tcause: err,\n\t\t\t\tcontext: {\n\t\t\t\t\toperation: \"buildCachedItem:renderMarkdown\",\n\t\t\t\t\tpageId: item.id,\n\t\t\t\t\tslug: item.slug,\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\t// afterRender フック\n\t\tif (this.hooks.afterRender) {\n\t\t\thtml = await this.hooks.afterRender(html, item);\n\t\t}\n\n\t\tlet result: CachedItem<T> = {\n\t\t\thtml,\n\t\t\titem,\n\t\t\tnotionUpdatedAt: item.updatedAt,\n\t\t\tcachedAt: Date.now(),\n\t\t};\n\n\t\t// beforeCache フック\n\t\tif (this.hooks.beforeCache) {\n\t\t\tresult = await this.hooks.beforeCache(result);\n\t\t}\n\n\t\tconst durationMs = Date.now() - start;\n\t\tthis.logger?.info?.(\"コンテンツのレンダリング完了\", {\n\t\t\tslug: item.slug,\n\t\t\tdurationMs,\n\t\t});\n\t\tthis.hooks.onRenderEnd?.(item.slug, durationMs);\n\n\t\treturn result;\n\t}\n}\n\n/**\n * renderer オプション未指定時のフォールバック。\n * @notion-headless-cms/renderer を動的 import する。\n * adapter-cloudflare / adapter-node は renderer を明示注入するためこのパスは通らない。\n */\nasync function loadDefaultRenderer(): Promise<RendererFn> {\n\ttry {\n\t\tconst mod = await import(\"@notion-headless-cms/renderer\");\n\t\treturn mod.renderMarkdown;\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\nfunction buildListVersion<T extends BaseContentItem>(items: T[]): string {\n\treturn items.map((item) => `${item.id}:${item.updatedAt}`).join(\"|\");\n}\n\n/** 設定済みの CMS インスタンスを生成するファクトリ関数。 */\nexport function createCMS<T extends BaseContentItem = BaseContentItem>(\n\topts: CreateCMSOptions<T>,\n): CMS<T> {\n\treturn new CMS<T>(opts);\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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","names":["items"]}
|
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.2",
|
|
4
4
|
"description": "Core CMS engine for notion-headless-cms — fetch, transform, cache with stale-while-revalidate strategy",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"notion",
|
|
@@ -26,28 +26,46 @@
|
|
|
26
26
|
".": {
|
|
27
27
|
"import": "./dist/index.js",
|
|
28
28
|
"types": "./dist/index.d.ts"
|
|
29
|
+
},
|
|
30
|
+
"./errors": {
|
|
31
|
+
"import": "./dist/errors.js",
|
|
32
|
+
"types": "./dist/errors.d.ts"
|
|
33
|
+
},
|
|
34
|
+
"./hooks": {
|
|
35
|
+
"import": "./dist/hooks.js",
|
|
36
|
+
"types": "./dist/hooks.d.ts"
|
|
37
|
+
},
|
|
38
|
+
"./cache/memory": {
|
|
39
|
+
"import": "./dist/cache/memory.js",
|
|
40
|
+
"types": "./dist/cache/memory.d.ts"
|
|
41
|
+
},
|
|
42
|
+
"./cache/noop": {
|
|
43
|
+
"import": "./dist/cache/noop.js",
|
|
44
|
+
"types": "./dist/cache/noop.d.ts"
|
|
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
|
-
"registry": "https://registry.npmjs.org",
|
|
39
54
|
"access": "public"
|
|
40
55
|
},
|
|
41
|
-
"
|
|
42
|
-
"@
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"@notion-headless-cms/
|
|
46
|
-
|
|
47
|
-
|
|
56
|
+
"peerDependencies": {
|
|
57
|
+
"@notion-headless-cms/renderer": "0.1.1"
|
|
58
|
+
},
|
|
59
|
+
"peerDependenciesMeta": {
|
|
60
|
+
"@notion-headless-cms/renderer": {
|
|
61
|
+
"optional": true
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@notion-headless-cms/renderer": "0.1.1"
|
|
48
66
|
},
|
|
49
67
|
"scripts": {
|
|
50
|
-
"build": "tsup src/index.ts --format esm --dts --out-dir dist",
|
|
68
|
+
"build": "tsup 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
69
|
"typecheck": "tsc --noEmit",
|
|
52
70
|
"test": "vitest run"
|
|
53
71
|
}
|