@notion-headless-cms/core 0.3.24 → 0.4.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.
package/dist/hooks.mjs CHANGED
@@ -82,7 +82,34 @@ function mergeLoggers(plugins, directLogger) {
82
82
  }
83
83
  return merged;
84
84
  }
85
+ /**
86
+ * 既存 Logger をラップし、全ログコンテキストに `traceId` を自動で付与する。
87
+ * 呼び出し側が明示的に traceId を渡した場合はその値を優先する。
88
+ *
89
+ * `createClient` がクライアント単位の trace ID を発行し、ネストした操作
90
+ * (list / find / SWR 再生成 / retry) で同じ ID をログに伝搬するために使う。
91
+ */
92
+ function withTraceId(logger, traceId) {
93
+ if (!logger) return void 0;
94
+ const wrapped = {};
95
+ for (const level of [
96
+ "debug",
97
+ "info",
98
+ "warn",
99
+ "error"
100
+ ]) {
101
+ const fn = logger[level];
102
+ if (!fn) continue;
103
+ wrapped[level] = (message, context) => {
104
+ fn(message, {
105
+ traceId,
106
+ ...context ?? {}
107
+ });
108
+ };
109
+ }
110
+ return wrapped;
111
+ }
85
112
  //#endregion
86
- export { mergeHooks, mergeLoggers };
113
+ export { mergeHooks, mergeLoggers, withTraceId };
87
114
 
88
115
  //# sourceMappingURL=hooks.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.mjs","names":[],"sources":["../src/hooks.ts"],"sourcesContent":["import type { BaseContentItem } from \"./types/content\";\nimport type { CMSHooks, MaybePromise } from \"./types/hooks\";\nimport type { Logger } from \"./types/logger\";\nimport type { CMSPlugin } from \"./types/plugin\";\n\n/**\n * プラグイン配列とダイレクトフックを合成して単一の CMSHooks を返す。\n * beforeCacheMeta / beforeCacheContent / afterRender はパイプライン(前の出力が次の入力)。\n * オブザーバー系は全員に同じ値を渡し、例外は logger に流して握りつぶす。\n */\nexport function mergeHooks<T extends BaseContentItem>(\n plugins: CMSPlugin<T>[],\n directHooks?: CMSHooks<T>,\n logger?: Logger,\n): CMSHooks<T> {\n const allHooks: CMSHooks<T>[] = [\n ...plugins.map((p) => p.hooks ?? {}),\n ...(directHooks ? [directHooks] : []),\n ];\n\n if (allHooks.length === 0) return {};\n\n return {\n beforeCacheMeta: buildMetaPipeline(allHooks),\n beforeCacheContent: buildContentPipeline(allHooks),\n afterRender: buildRenderPipeline(allHooks),\n onCacheHit: buildObserver(allHooks, \"onCacheHit\", logger),\n onCacheMiss: buildObserver(allHooks, \"onCacheMiss\", logger),\n onCacheRevalidated: buildObserver(allHooks, \"onCacheRevalidated\", logger),\n onContentRevalidated: buildObserver(\n allHooks,\n \"onContentRevalidated\",\n logger,\n ),\n onListCacheHit: buildObserver(allHooks, \"onListCacheHit\", logger),\n onListCacheMiss: buildObserver(allHooks, \"onListCacheMiss\", logger),\n onListCacheRevalidated: buildObserver(\n allHooks,\n \"onListCacheRevalidated\",\n logger,\n ),\n onError: buildObserver(allHooks, \"onError\", logger),\n onRenderStart: buildObserver(allHooks, \"onRenderStart\", logger),\n onRenderEnd: buildObserver(allHooks, \"onRenderEnd\", logger),\n };\n}\n\nfunction buildMetaPipeline<T extends BaseContentItem>(\n hooks: CMSHooks<T>[],\n): CMSHooks<T>[\"beforeCacheMeta\"] {\n const fns = hooks\n .map((h) => h.beforeCacheMeta)\n .filter(Boolean) as NonNullable<CMSHooks<T>[\"beforeCacheMeta\"]>[];\n if (fns.length === 0) return undefined;\n return async (meta) => {\n let current = meta;\n for (const fn of fns) {\n current = await (fn(current) as MaybePromise<typeof meta>);\n }\n return current;\n };\n}\n\nfunction buildContentPipeline<T extends BaseContentItem>(\n hooks: CMSHooks<T>[],\n): CMSHooks<T>[\"beforeCacheContent\"] {\n const fns = hooks\n .map((h) => h.beforeCacheContent)\n .filter(Boolean) as NonNullable<CMSHooks<T>[\"beforeCacheContent\"]>[];\n if (fns.length === 0) return undefined;\n return async (content, item) => {\n let current = content;\n for (const fn of fns) {\n current = await (fn(current, item) as MaybePromise<typeof content>);\n }\n return current;\n };\n}\n\nfunction buildRenderPipeline<T extends BaseContentItem>(\n hooks: CMSHooks<T>[],\n): CMSHooks<T>[\"afterRender\"] {\n const fns = hooks.map((h) => h.afterRender).filter(Boolean) as NonNullable<\n CMSHooks<T>[\"afterRender\"]\n >[];\n if (fns.length === 0) return undefined;\n return async (html, item) => {\n let current = html;\n for (const fn of fns) {\n current = await (fn(current, item) as MaybePromise<string>);\n }\n return current;\n };\n}\n\nfunction buildObserver<T extends BaseContentItem, K extends keyof CMSHooks<T>>(\n hooks: CMSHooks<T>[],\n key: K,\n logger?: Logger,\n): CMSHooks<T>[K] {\n const fns = hooks.map((h) => h[key]).filter(Boolean);\n if (fns.length === 0) return undefined;\n return ((...args: unknown[]) => {\n for (const fn of fns) {\n try {\n (fn as (...a: unknown[]) => void)(...args);\n } catch (err) {\n logger?.error?.(\"観測フックで例外が発生\", {\n hook: String(key),\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n }) as CMSHooks<T>[K];\n}\n\n/** プラグイン配列とダイレクトロガーを合成して単一の Logger を返す。 */\nexport function mergeLoggers(\n plugins: Array<{ logger?: Partial<Logger> }>,\n directLogger?: Logger,\n): Logger | undefined {\n const loggers: Partial<Logger>[] = [\n ...plugins.map((p) => p.logger ?? {}),\n ...(directLogger ? [directLogger] : []),\n ];\n if (loggers.length === 0) return undefined;\n\n const merged: Logger = {};\n for (const level of [\"debug\", \"info\", \"warn\", \"error\"] as const) {\n const fns = loggers.map((l) => l[level]).filter(Boolean) as NonNullable<\n Logger[typeof level]\n >[];\n if (fns.length > 0) {\n merged[level] = (message, context) => {\n for (const fn of fns) fn(message, context);\n };\n }\n }\n return merged;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"hooks.mjs","names":[],"sources":["../src/hooks.ts"],"sourcesContent":["import type { BaseContentItem } from \"./types/content\";\nimport type { CMSHooks, MaybePromise } from \"./types/hooks\";\nimport type { Logger } from \"./types/logger\";\nimport type { CMSPlugin } from \"./types/plugin\";\n\n/**\n * プラグイン配列とダイレクトフックを合成して単一の CMSHooks を返す。\n * beforeCacheMeta / beforeCacheContent / afterRender はパイプライン(前の出力が次の入力)。\n * オブザーバー系は全員に同じ値を渡し、例外は logger に流して握りつぶす。\n */\nexport function mergeHooks<T extends BaseContentItem>(\n plugins: CMSPlugin<T>[],\n directHooks?: CMSHooks<T>,\n logger?: Logger,\n): CMSHooks<T> {\n const allHooks: CMSHooks<T>[] = [\n ...plugins.map((p) => p.hooks ?? {}),\n ...(directHooks ? [directHooks] : []),\n ];\n\n if (allHooks.length === 0) return {};\n\n return {\n beforeCacheMeta: buildMetaPipeline(allHooks),\n beforeCacheContent: buildContentPipeline(allHooks),\n afterRender: buildRenderPipeline(allHooks),\n onCacheHit: buildObserver(allHooks, \"onCacheHit\", logger),\n onCacheMiss: buildObserver(allHooks, \"onCacheMiss\", logger),\n onCacheRevalidated: buildObserver(allHooks, \"onCacheRevalidated\", logger),\n onContentRevalidated: buildObserver(\n allHooks,\n \"onContentRevalidated\",\n logger,\n ),\n onListCacheHit: buildObserver(allHooks, \"onListCacheHit\", logger),\n onListCacheMiss: buildObserver(allHooks, \"onListCacheMiss\", logger),\n onListCacheRevalidated: buildObserver(\n allHooks,\n \"onListCacheRevalidated\",\n logger,\n ),\n onError: buildObserver(allHooks, \"onError\", logger),\n onRenderStart: buildObserver(allHooks, \"onRenderStart\", logger),\n onRenderEnd: buildObserver(allHooks, \"onRenderEnd\", logger),\n };\n}\n\nfunction buildMetaPipeline<T extends BaseContentItem>(\n hooks: CMSHooks<T>[],\n): CMSHooks<T>[\"beforeCacheMeta\"] {\n const fns = hooks\n .map((h) => h.beforeCacheMeta)\n .filter(Boolean) as NonNullable<CMSHooks<T>[\"beforeCacheMeta\"]>[];\n if (fns.length === 0) return undefined;\n return async (meta) => {\n let current = meta;\n for (const fn of fns) {\n current = await (fn(current) as MaybePromise<typeof meta>);\n }\n return current;\n };\n}\n\nfunction buildContentPipeline<T extends BaseContentItem>(\n hooks: CMSHooks<T>[],\n): CMSHooks<T>[\"beforeCacheContent\"] {\n const fns = hooks\n .map((h) => h.beforeCacheContent)\n .filter(Boolean) as NonNullable<CMSHooks<T>[\"beforeCacheContent\"]>[];\n if (fns.length === 0) return undefined;\n return async (content, item) => {\n let current = content;\n for (const fn of fns) {\n current = await (fn(current, item) as MaybePromise<typeof content>);\n }\n return current;\n };\n}\n\nfunction buildRenderPipeline<T extends BaseContentItem>(\n hooks: CMSHooks<T>[],\n): CMSHooks<T>[\"afterRender\"] {\n const fns = hooks.map((h) => h.afterRender).filter(Boolean) as NonNullable<\n CMSHooks<T>[\"afterRender\"]\n >[];\n if (fns.length === 0) return undefined;\n return async (html, item) => {\n let current = html;\n for (const fn of fns) {\n current = await (fn(current, item) as MaybePromise<string>);\n }\n return current;\n };\n}\n\nfunction buildObserver<T extends BaseContentItem, K extends keyof CMSHooks<T>>(\n hooks: CMSHooks<T>[],\n key: K,\n logger?: Logger,\n): CMSHooks<T>[K] {\n const fns = hooks.map((h) => h[key]).filter(Boolean);\n if (fns.length === 0) return undefined;\n return ((...args: unknown[]) => {\n for (const fn of fns) {\n try {\n (fn as (...a: unknown[]) => void)(...args);\n } catch (err) {\n logger?.error?.(\"観測フックで例外が発生\", {\n hook: String(key),\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n }) as CMSHooks<T>[K];\n}\n\n/** プラグイン配列とダイレクトロガーを合成して単一の Logger を返す。 */\nexport function mergeLoggers(\n plugins: Array<{ logger?: Partial<Logger> }>,\n directLogger?: Logger,\n): Logger | undefined {\n const loggers: Partial<Logger>[] = [\n ...plugins.map((p) => p.logger ?? {}),\n ...(directLogger ? [directLogger] : []),\n ];\n if (loggers.length === 0) return undefined;\n\n const merged: Logger = {};\n for (const level of [\"debug\", \"info\", \"warn\", \"error\"] as const) {\n const fns = loggers.map((l) => l[level]).filter(Boolean) as NonNullable<\n Logger[typeof level]\n >[];\n if (fns.length > 0) {\n merged[level] = (message, context) => {\n for (const fn of fns) fn(message, context);\n };\n }\n }\n return merged;\n}\n\n/**\n * 既存 Logger をラップし、全ログコンテキストに `traceId` を自動で付与する。\n * 呼び出し側が明示的に traceId を渡した場合はその値を優先する。\n *\n * `createClient` がクライアント単位の trace ID を発行し、ネストした操作\n * (list / find / SWR 再生成 / retry) で同じ ID をログに伝搬するために使う。\n */\nexport function withTraceId(\n logger: Logger | undefined,\n traceId: string,\n): Logger | undefined {\n if (!logger) return undefined;\n const wrapped: Logger = {};\n for (const level of [\"debug\", \"info\", \"warn\", \"error\"] as const) {\n const fn = logger[level];\n if (!fn) continue;\n wrapped[level] = (message, context) => {\n fn(message, { traceId, ...(context ?? {}) });\n };\n }\n return wrapped;\n}\n"],"mappings":"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"}
package/dist/index.d.mts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { a as CachedItemMeta, c as ContentResult, i as CachedItemList, l as ImageRef, n as CMSSchemaProperties, o as StorageBinary, r as CachedItemContent, s as ContentBlock, t as BaseContentItem, u as InlineNode } from "./content-DwsfWZao.mjs";
2
- import { a as InvalidateKind, c as PropertyMap, i as DataSource, l as WebhookConfig, n as DocumentCacheOps, o as InvalidateScope, r as ImageCacheOps, s as PropertyDef, t as CacheAdapter } from "./cache-DS81aOcC.mjs";
3
- import { a as isCMSError, i as CMSErrorContext, n as CMSError, o as isCMSErrorInNamespace, r as CMSErrorCode, s as matchCMSError, t as BuiltInCMSErrorCode } from "./errors-DTt9ii0i.mjs";
4
- import { a as MaybePromise, i as CMSHooks, n as definePlugin, r as Logger, t as CMSPlugin } from "./plugin-B795Ok3X.mjs";
5
- import { a as InferCollectionItem, c as RenderOptions, d as SWRConfig, i as CreateClientOptions, l as RendererFn, m as MergeSourceCollections, n as CollectionsConfig, o as LogLevel, p as CMSSources, r as ContentConfig, s as RateLimiterConfig, u as RendererPluginList } from "./config-D4JQ_pmq.mjs";
2
+ import { a as ImageCacheOps, c as InvalidateScope, d as WebhookConfig, i as DocumentCacheOps, l as PropertyDef, n as CacheAdapterStats, o as DataSource, r as CacheAreaStats, s as InvalidateKind, t as CacheAdapter, u as PropertyMap } from "./cache-v9jTMnYd.mjs";
3
+ import { a as isCMSError, i as CMSErrorContext, n as CMSError, o as isCMSErrorInNamespace, r as CMSErrorCode, s as matchCMSError, t as BuiltInCMSErrorCode } from "./errors-DcNErfYk.mjs";
4
+ import { a as CMSHooks, i as Logger, n as definePlugin, o as MaybePromise, r as LogContext, t as CMSPlugin } from "./plugin-BmrOz8T6.mjs";
5
+ import { a as InferCollectionItem, c as RenderOptions, d as SWRConfig, i as CreateClientOptions, l as RendererFn, m as MergeSourceCollections, n as CollectionsConfig, o as LogLevel, p as CMSSources, r as ContentConfig, s as RateLimiterConfig, u as RendererPluginList } from "./config-DuaVFspk.mjs";
6
6
  import { MemoryCacheOptions, memoryCache } from "./cache/memory.mjs";
7
7
  import { mergeHooks, mergeLoggers } from "./hooks.mjs";
8
8
  import { NodePresetOptions, nodePreset } from "./preset/node.mjs";
@@ -204,8 +204,36 @@ declare function createHandler(adapter: HandlerAdapter, opts?: HandlerOptions):
204
204
  //#region src/cms.d.ts
205
205
  /** コレクション別アクセス + グローバル操作の合成型。 */
206
206
  type CMSClient<C extends CollectionsConfig> = { [K in keyof C]: CollectionClient<InferCollectionItem<C[K]>> } & CMSGlobalOps;
207
+ /**
208
+ * `cms.stats()` が返す集約済みキャッシュ統計。
209
+ * 各 adapter の `stats()` 戻り値をそのまま配列で保持しつつ、ヒット率を算出する。
210
+ */
211
+ interface CMSStats {
212
+ /** クライアント単位の trace ID (`createClient` で発行)。 */
213
+ traceId: string;
214
+ /** ドキュメントキャッシュの集計 (`handles: ["document"]` の adapter の `stats()` 戻り値)。 */
215
+ document?: {
216
+ adapter: string;
217
+ hits: number;
218
+ misses: number;
219
+ entries?: number;
220
+ sizeBytes?: number; /** 0〜1。`hits + misses === 0` のときは 0。 */
221
+ hitRate: number;
222
+ };
223
+ /** 画像キャッシュの集計 (`handles: ["image"]` の adapter の `stats()` 戻り値)。 */
224
+ image?: {
225
+ adapter: string;
226
+ hits: number;
227
+ misses: number;
228
+ entries?: number;
229
+ sizeBytes?: number;
230
+ hitRate: number;
231
+ };
232
+ }
207
233
  interface CMSGlobalOps {
208
234
  readonly collections: readonly string[];
235
+ /** クライアント単位の trace ID (`createClient` で発行)。 */
236
+ readonly traceId: string;
209
237
  invalidate(scope?: InvalidateScope): Promise<void>;
210
238
  /** Web Standard な Request/Response ベースのルートハンドラ (画像プロキシ + webhook)。 */
211
239
  handler(opts?: HandlerOptions): (req: Request) => Promise<Response>;
@@ -216,6 +244,12 @@ interface CMSGlobalOps {
216
244
  */
217
245
  readonly cacheImage: ((url: string) => Promise<string>) | undefined;
218
246
  readonly imageProxyBase: string;
247
+ /**
248
+ * ドキュメント / 画像キャッシュのヒット・ミス・サイズを集約して返す。
249
+ * 各キャッシュアダプタの `stats()` を呼ぶだけで副作用はない。
250
+ * `stats()` を実装していない adapter は集計から除外される (noop など)。
251
+ */
252
+ stats(): Promise<CMSStats>;
219
253
  }
220
254
  /**
221
255
  * CMS クライアントを生成する。
@@ -241,7 +275,12 @@ interface RetryConfig {
241
275
  baseDelayMs: number;
242
276
  /** true のとき指数バックオフにランダムジッターを加える(Thundering Herd 対策)。デフォルト: true */
243
277
  jitter?: boolean;
244
- onRetry?: (attempt: number, status: number) => void;
278
+ /**
279
+ * リトライ前に呼ばれるフック。`attempt` は 1 始まり、`status` はリトライ対象の HTTP ステータス、
280
+ * `delayMs` は次回試行までの実際の待機時間 (ジッター反映後)。
281
+ * 既存呼び出しは `attempt` / `status` だけで動くよう `delayMs` は省略可。
282
+ */
283
+ onRetry?: (attempt: number, status: number, delayMs?: number) => void;
245
284
  }
246
285
  declare const DEFAULT_RETRY_CONFIG: RetryConfig;
247
286
  /**
@@ -254,5 +293,5 @@ declare const DEFAULT_RETRY_CONFIG: RetryConfig;
254
293
  */
255
294
  declare function withRetry<T>(fn: () => Promise<T>, config: RetryConfig): Promise<T>;
256
295
  //#endregion
257
- export { type AdjacencyOptions, type BaseContentItem, type BuiltInCMSErrorCode, type CMSClient, CMSError, type CMSErrorCode, type CMSErrorContext, type CMSGlobalOps, type CMSHooks, type CMSPlugin, type CMSSchemaProperties, type CMSSources, type CacheAdapter, type CachedItemContent, type CachedItemList, type CachedItemMeta, type CheckResult, type CollectionCacheOps, type CollectionClient, type ContentBlock, type ContentConfig, type ContentResult, type CreateClientOptions, DEFAULT_RETRY_CONFIG, type DataSource, type DocumentCacheOps, type FindOptions, type HandlerAdapter, type HandlerOptions, type ImageCacheOps, type ImageRef, type InlineNode, type InvalidateKind, type InvalidateScope, type ItemWithContent, type ListOptions, type LogLevel, type Logger, type MaybePromise, type MemoryCacheOptions, type NodePresetOptions, type PropertyDef, type PropertyMap, type RateLimiterConfig, type RenderOptions, type RendererFn, type RendererPluginList, type RetryConfig, type SWRConfig, type SortOption, type StorageBinary, type WarmOptions, type WarmResult, type WebhookConfig, createClient, createHandler, definePlugin, isCMSError, isCMSErrorInNamespace, isStale, matchCMSError, memoryCache, mergeHooks, mergeLoggers, nodePreset, noopDocOps, noopImgOps, sha256Hex, withRetry };
296
+ export { type AdjacencyOptions, type BaseContentItem, type BuiltInCMSErrorCode, type CMSClient, CMSError, type CMSErrorCode, type CMSErrorContext, type CMSGlobalOps, type CMSHooks, type CMSPlugin, type CMSSchemaProperties, type CMSSources, type CacheAdapter, type CacheAdapterStats, type CacheAreaStats, type CachedItemContent, type CachedItemList, type CachedItemMeta, type CheckResult, type CollectionCacheOps, type CollectionClient, type ContentBlock, type ContentConfig, type ContentResult, type CreateClientOptions, DEFAULT_RETRY_CONFIG, type DataSource, type DocumentCacheOps, type FindOptions, type HandlerAdapter, type HandlerOptions, type ImageCacheOps, type ImageRef, type InlineNode, type InvalidateKind, type InvalidateScope, type ItemWithContent, type ListOptions, type LogContext, type LogLevel, type Logger, type MaybePromise, type MemoryCacheOptions, type NodePresetOptions, type PropertyDef, type PropertyMap, type RateLimiterConfig, type RenderOptions, type RendererFn, type RendererPluginList, type RetryConfig, type SWRConfig, type SortOption, type StorageBinary, type WarmOptions, type WarmResult, type WebhookConfig, createClient, createHandler, definePlugin, isCMSError, isCMSErrorInNamespace, isStale, matchCMSError, memoryCache, mergeHooks, mergeLoggers, nodePreset, noopDocOps, noopImgOps, sha256Hex, withRetry };
258
297
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { memoryCache } from "./cache/memory.mjs";
2
2
  import { CMSError, isCMSError, isCMSErrorInNamespace, matchCMSError } from "./errors.mjs";
3
- import { mergeHooks, mergeLoggers } from "./hooks.mjs";
3
+ import { mergeHooks, mergeLoggers, withTraceId } from "./hooks.mjs";
4
4
  import { nodePreset } from "./preset/node.mjs";
5
5
  //#region src/cache.ts
6
6
  /** 文字列をSHA-256でハッシュ化し、16進数文字列として返す。画像キーの生成に使用。 */
@@ -231,8 +231,9 @@ async function buildCachedItemContent(item, ctx) {
231
231
  if (ctx.source.loadNotionBlocks) try {
232
232
  notionBlocks = await ctx.source.loadNotionBlocks(item);
233
233
  } catch (err) {
234
- if (isCMSError(err)) throw err;
235
- throw new CMSError({
234
+ if (isCMSError(err) && err.is("source/blocks_unsupported")) notionBlocks = void 0;
235
+ else if (isCMSError(err)) throw err;
236
+ else throw new CMSError({
236
237
  code: "source/load_blocks_failed",
237
238
  message: "Failed to load Notion blocks from source.",
238
239
  cause: err,
@@ -313,9 +314,9 @@ async function withRetry(fn, config) {
313
314
  if (status === void 0 || !config.retryOn.includes(status)) throw err;
314
315
  lastError = err;
315
316
  if (attempt < config.maxRetries) {
316
- config.onRetry?.(attempt + 1, status);
317
317
  const jitterFactor = config.jitter !== false ? .5 + Math.random() * .5 : 1;
318
318
  const delay = config.baseDelayMs * 2 ** attempt * jitterFactor;
319
+ config.onRetry?.(attempt + 1, status, delay);
319
320
  await new Promise((resolve) => setTimeout(resolve, delay));
320
321
  }
321
322
  }
@@ -682,10 +683,11 @@ var CollectionClientImpl = class {
682
683
  async fetchListRaw() {
683
684
  return (await withRetry(() => this.ctx.source.list({ publishedStatuses: this.ctx.publishedStatuses.length > 0 ? this.ctx.publishedStatuses : void 0 }), {
684
685
  ...this.ctx.retryConfig,
685
- onRetry: (attempt, status) => {
686
+ onRetry: (attempt, status, delayMs) => {
686
687
  this.ctx.logger?.warn?.("list() リトライ中", {
687
688
  attempt,
688
- status
689
+ status,
690
+ backoffMs: delayMs
689
691
  });
690
692
  }
691
693
  })).filter((item) => {
@@ -697,11 +699,12 @@ var CollectionClientImpl = class {
697
699
  async fetchRaw(slug) {
698
700
  const retryOpts = {
699
701
  ...this.ctx.retryConfig,
700
- onRetry: (attempt, status) => {
702
+ onRetry: (attempt, status, delayMs) => {
701
703
  this.ctx.logger?.warn?.("find() リトライ中", {
702
704
  attempt,
703
705
  status,
704
- slug
706
+ slug,
707
+ backoffMs: delayMs
705
708
  });
706
709
  }
707
710
  };
@@ -869,35 +872,49 @@ function trimTrailingSlash(s) {
869
872
  const DEFAULT_IMAGE_PROXY_BASE = "/api/images";
870
873
  /**
871
874
  * adapter の `handles` を見て先勝ちで document / image を割り当てる。未指定は両方 noop。
875
+ * `cms.stats()` から元 adapter の `stats()` を呼びたいので、解決元 adapter も保持する。
872
876
  */
873
877
  function resolveCache(cache) {
874
878
  const adapters = cache ?? [];
875
879
  let doc = noopDocOps;
876
880
  let docName = "noop-document";
881
+ let docAdapter;
877
882
  let img = noopImgOps;
878
883
  let imgName = "noop-image";
884
+ let imgAdapter;
879
885
  let docFound = false;
880
886
  let imgFound = false;
881
887
  for (const adapter of adapters) {
882
888
  if (!docFound && adapter.handles.includes("document") && adapter.doc) {
883
889
  doc = adapter.doc;
884
890
  docName = adapter.name;
891
+ docAdapter = adapter;
885
892
  docFound = true;
886
893
  }
887
894
  if (!imgFound && adapter.handles.includes("image") && adapter.img) {
888
895
  img = adapter.img;
889
896
  imgName = adapter.name;
897
+ imgAdapter = adapter;
890
898
  imgFound = true;
891
899
  }
892
900
  }
893
901
  return {
894
902
  doc,
895
903
  docName,
904
+ docAdapter,
896
905
  img,
897
906
  imgName,
907
+ imgAdapter,
898
908
  hasImg: imgFound
899
909
  };
900
910
  }
911
+ /**
912
+ * 衝突しにくい短い trace ID を生成する。`{epoch36}-{rand36}` の 10〜12 文字程度。
913
+ * core はゼロ依存ルールに従い node:crypto を静的 import しないため、Math.random ベースで十分。
914
+ */
915
+ function generateTraceId() {
916
+ return `${Date.now().toString(36)}-${Math.floor(Math.random() * 36 ** 6).toString(36).padStart(6, "0")}`;
917
+ }
901
918
  const LOG_LEVEL_ORDER = {
902
919
  debug: 0,
903
920
  info: 1,
@@ -970,7 +987,9 @@ function createClient(opts) {
970
987
  const rendererFn = opts.renderer;
971
988
  const waitUntil = opts.waitUntil;
972
989
  const baseLogger = mergeLoggers(opts.plugins ?? [], opts.logger);
973
- const logger = opts.logLevel ? applyLogLevel(baseLogger, opts.logLevel) : baseLogger;
990
+ const traceId = generateTraceId();
991
+ const tracedLogger = withTraceId(baseLogger, traceId);
992
+ const logger = opts.logLevel ? applyLogLevel(tracedLogger, opts.logLevel) : tracedLogger;
974
993
  const hooks = mergeHooks(opts.plugins ?? [], opts.hooks, logger);
975
994
  const maxConcurrent = opts.rateLimiter?.maxConcurrent ?? 3;
976
995
  const retryConfig = {
@@ -1019,6 +1038,45 @@ function createClient(opts) {
1019
1038
  collections: collectionNames,
1020
1039
  cacheImage: cacheRes.hasImg ? buildCacheImageFn(cacheRes.img, cacheRes.imgName, imageProxyBase, logger) : void 0,
1021
1040
  imageProxyBase,
1041
+ traceId,
1042
+ async stats() {
1043
+ const stats = { traceId };
1044
+ const adapterCache = /* @__PURE__ */ new Map();
1045
+ const ensure = (adapter) => {
1046
+ if (!adapter?.stats) return void 0;
1047
+ const cached = adapterCache.get(adapter);
1048
+ if (cached) return cached;
1049
+ const fresh = adapter.stats();
1050
+ adapterCache.set(adapter, fresh);
1051
+ return fresh;
1052
+ };
1053
+ const docPromise = ensure(cacheRes.docAdapter);
1054
+ const imgPromise = ensure(cacheRes.imgAdapter);
1055
+ const computeHitRate = (h, m) => h + m === 0 ? 0 : h / (h + m);
1056
+ if (docPromise) {
1057
+ const docStats = await docPromise;
1058
+ if (docStats.doc) stats.document = {
1059
+ adapter: docStats.name ?? cacheRes.docName,
1060
+ hits: docStats.doc.hits,
1061
+ misses: docStats.doc.misses,
1062
+ entries: docStats.doc.entries,
1063
+ sizeBytes: docStats.doc.sizeBytes,
1064
+ hitRate: computeHitRate(docStats.doc.hits, docStats.doc.misses)
1065
+ };
1066
+ }
1067
+ if (imgPromise) {
1068
+ const imgStats = await imgPromise;
1069
+ if (imgStats.img) stats.image = {
1070
+ adapter: imgStats.name ?? cacheRes.imgName,
1071
+ hits: imgStats.img.hits,
1072
+ misses: imgStats.img.misses,
1073
+ entries: imgStats.img.entries,
1074
+ sizeBytes: imgStats.img.sizeBytes,
1075
+ hitRate: computeHitRate(imgStats.img.hits, imgStats.img.misses)
1076
+ };
1077
+ }
1078
+ return stats;
1079
+ },
1022
1080
  async invalidate(scope) {
1023
1081
  logger?.debug?.("グローバルキャッシュを無効化", {
1024
1082
  operation: "invalidate",