@notion-headless-cms/core 0.3.20 → 0.3.22

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/index.d.mts CHANGED
@@ -1,7 +1,11 @@
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 CacheAdapter, c as DataSource, d as PropertyDef, f as PropertyMap, i as memoryCache, l as InvalidateKind, o as DocumentCacheOps, p as WebhookConfig, s as ImageCacheOps, t as MemoryCacheOptions, u as InvalidateScope } from "./memory-BT9rLPr1.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-CC_x98vG.mjs";
4
- import { a as Logger, i as definePlugin, n as mergeLoggers, o as CMSHooks, r as CMSPlugin, s as MaybePromise, t as mergeHooks } from "./hooks-C6F2PG8x.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";
6
+ import { MemoryCacheOptions, memoryCache } from "./cache/memory.mjs";
7
+ import { mergeHooks, mergeLoggers } from "./hooks.mjs";
8
+ import { NodePresetOptions, nodePreset } from "./preset/node.mjs";
5
9
 
6
10
  //#region src/types/collection.d.ts
7
11
  /**
@@ -136,162 +140,6 @@ interface CollectionClient<T extends BaseContentItem = BaseContentItem> {
136
140
  cache: CollectionCacheOps<T>;
137
141
  }
138
142
  //#endregion
139
- //#region src/types/sources.d.ts
140
- /**
141
- * CMS データソースアダプターのインターフェース。
142
- * 各アダプターパッケージ (`@notion-headless-cms/notion-source` 等) が実装し、
143
- * `createClient({ sources: { ... } })` に渡される。
144
- */
145
- interface CMSAdapter<C extends CollectionsConfig = CollectionsConfig> {
146
- readonly collections: C;
147
- }
148
- /**
149
- * アダプターパッケージが宣言マージで拡張する空インターフェース。
150
- * import するだけでキーが補完候補に現れる (Fastify プラグインと同じパターン)。
151
- *
152
- * @example
153
- * declare module "@notion-headless-cms/core" {
154
- * interface CMSSources {
155
- * notion?: CMSAdapter;
156
- * }
157
- * }
158
- */
159
- interface CMSSources {}
160
- type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
161
- /** 全ソースの collections を交差型でマージする。 */
162
- type MergeSourceCollections<S extends CMSSources> = UnionToIntersection<{ [K in keyof S]: S[K] extends CMSAdapter<infer C> ? C : never }[keyof S]>;
163
- //#endregion
164
- //#region src/types/config.d.ts
165
- /** `Logger` の出力を絞り込むログレベル。指定したレベル未満のログを抑制する。 */
166
- type LogLevel = "debug" | "info" | "warn" | "error";
167
- /**
168
- * renderer プラグインの不透明型。
169
- * core は unified / remark / rehype に依存せず、このリストをそのまま renderer に渡すだけ。
170
- */
171
- type RendererPluginList = unknown[];
172
- /**
173
- * render() オプション。core は renderer の実装を知らず、この型だけを扱う。
174
- * @notion-headless-cms/renderer の renderMarkdown() はこのシグネチャと構造的に互換。
175
- */
176
- interface RenderOptions {
177
- imageProxyBase?: string;
178
- cacheImage?: (url: string) => Promise<string>;
179
- remarkPlugins?: RendererPluginList;
180
- rehypePlugins?: RendererPluginList;
181
- }
182
- /** カスタムレンダラー関数の型。デフォルトは @notion-headless-cms/renderer の renderMarkdown。 */
183
- type RendererFn = (markdown: string, opts?: RenderOptions) => Promise<string>;
184
- /** レンダリング・コンテンツ処理設定。 */
185
- interface ContentConfig {
186
- /** 画像プロキシのベースURL。デフォルト: '/api/images' */
187
- imageProxyBase?: string;
188
- /** 追加する remark プラグイン。 */
189
- remarkPlugins?: RendererPluginList;
190
- /** 追加する rehype プラグイン。 */
191
- rehypePlugins?: RendererPluginList;
192
- }
193
- /** SWR(Stale-While-Revalidate)設定。 */
194
- interface SWRConfig {
195
- /** SWR の有効期間 (ミリ秒)。未設定時は TTL なし(失効まで stale を返す)。 */
196
- ttlMs?: number;
197
- }
198
- /** レートリミット・リトライ設定。 */
199
- interface RateLimiterConfig {
200
- /** 同時実行数の上限。デフォルト: 3 */
201
- maxConcurrent?: number;
202
- /** リトライ対象の HTTP ステータスコード。デフォルト: [429, 502, 503] */
203
- retryOn?: number[];
204
- /** 最大リトライ回数。デフォルト: 4 */
205
- maxRetries?: number;
206
- /** リトライ時の基準待機時間(ミリ秒)。デフォルト: 1000 */
207
- baseDelayMs?: number;
208
- }
209
- /**
210
- * コレクション 1 件の定義。CLI が生成する `nhc.ts` から `createClient` に渡される。
211
- *
212
- * `source` は notion-orm 等の DataSource 実装。
213
- * `slugField` / `statusField` は TS フィールド名 (DataSource の `properties` キーと一致)。
214
- */
215
- interface CollectionDef<T extends BaseContentItem = BaseContentItem> {
216
- /** Notion etc. のデータソース実装。 */
217
- source: DataSource<T>;
218
- /** slug として使う TS フィールド名 (必須)。`source.properties[slugField]` で Notion プロパティ名を解決する。 */
219
- slugField: string;
220
- /** ステータスとして使う TS フィールド名。 */
221
- statusField?: string;
222
- /** 公開扱いするステータス値。`list()` のデフォルト絞り込みに使う。 */
223
- publishedStatuses?: readonly string[];
224
- /** アクセス許可するステータス値。`get()` の閲覧可否判定に使う。 */
225
- accessibleStatuses?: readonly string[];
226
- /** コレクション固有のライフサイクルフック。グローバル hooks の後に実行される。 */
227
- hooks?: CMSHooks<T>;
228
- }
229
- /**
230
- * `createClient({ collections })` の map 型。
231
- * キーがコレクション名、値が `CollectionDef<T>`。
232
- */
233
- type CollectionsConfig = Record<string, CollectionDef<BaseContentItem>>;
234
- /** `CollectionsConfig` から各 T を抽出するユーティリティ型。 */
235
- type InferCollectionItem<C> = C extends CollectionDef<infer T> ? T : BaseContentItem;
236
- /**
237
- * `createClient()` の入力。
238
- * 通常は CLI が生成した `nhc.ts` の `createClient` がこの型をラップする。
239
- *
240
- * @example
241
- * createClient({
242
- * collections: {
243
- * posts: {
244
- * source: createNotionCollection({ token, dataSourceId, properties }),
245
- * slugField: "slug",
246
- * statusField: "status",
247
- * publishedStatuses: ["公開済み"],
248
- * }
249
- * },
250
- * cache: [memoryCache()],
251
- * swr: { ttlMs: 5 * 60_000 },
252
- * });
253
- */
254
- interface CreateClientOptions<C extends CollectionsConfig = CollectionsConfig, S extends CMSSources = CMSSources> {
255
- /**
256
- * データソースアダプター (`@notion-headless-cms/notion-source` 等) のマップ。
257
- * `sources` を指定する場合 `collections` は不要。両方指定された場合は `sources` が優先される。
258
- */
259
- sources?: S;
260
- /** コレクション定義のマップ。`sources` を使う場合は不要。 */
261
- collections?: C;
262
- /**
263
- * キャッシュアダプタ (配列)。未指定時はキャッシュなし。
264
- * - `memoryCache()` のように doc + image 両方を担当するもの
265
- * - `r2Cache()` (image のみ)、`kvCache()` (doc のみ) のように片側のみ担当するもの
266
- * - 複数 adapter を配列で組み合わせると、各 adapter の `handles` で振り分けられる
267
- */
268
- cache?: readonly CacheAdapter[];
269
- /** SWR(Stale-While-Revalidate)設定。 */
270
- swr?: SWRConfig;
271
- /**
272
- * Markdown→HTML レンダラー。
273
- * 省略時は `@notion-headless-cms/renderer` の `renderMarkdown` を動的 import で使用する。
274
- * カスタム実装も `RendererFn` 型を満たせば使用可能。
275
- */
276
- renderer?: RendererFn;
277
- /** 画像プロキシのベース URL。デフォルト `/api/images`。 */
278
- imageProxyBase?: string;
279
- /** Cloudflare Workers の `waitUntil` に相当する非同期処理の登録関数。 */
280
- waitUntil?: (p: Promise<unknown>) => void;
281
- /** ライフサイクルフック (全コレクション共通)。 */
282
- hooks?: CMSHooks<BaseContentItem>;
283
- /** プラグイン配列。 */
284
- plugins?: CMSPlugin<BaseContentItem>[];
285
- /** ロガー。 */
286
- logger?: Logger;
287
- /** ログレベルの下限。指定したレベル未満のログを内部で抑制する。 */
288
- logLevel?: LogLevel;
289
- /** レートリミット・リトライ設定。 */
290
- rateLimiter?: RateLimiterConfig;
291
- /** レンダリング・コンテンツ処理設定。 */
292
- content?: ContentConfig;
293
- }
294
- //#endregion
295
143
  //#region src/cache.d.ts
296
144
  /** 文字列をSHA-256でハッシュ化し、16進数文字列として返す。画像キーの生成に使用。 */
297
145
  declare function sha256Hex(input: string): Promise<string>;
@@ -345,65 +193,37 @@ interface HandlerAdapter {
345
193
  declare function createHandler(adapter: HandlerAdapter, opts?: HandlerOptions): (req: Request) => Promise<Response>;
346
194
  //#endregion
347
195
  //#region src/cms.d.ts
348
- /** `CMSClient<C>` — コレクション別アクセス + グローバル操作の合成型。 */
196
+ /** コレクション別アクセス + グローバル操作の合成型。 */
349
197
  type CMSClient<C extends CollectionsConfig> = { [K in keyof C]: CollectionClient<InferCollectionItem<C[K]>> } & CMSGlobalOps;
350
- /** `CMSClient` のグローバル名前空間。 */
351
198
  interface CMSGlobalOps {
352
- /** 登録されているコレクション名の一覧。 */
353
199
  readonly collections: readonly string[];
354
- /** 全コレクションまたは特定スコープのキャッシュを無効化する。 */
355
200
  invalidate(scope?: InvalidateScope): Promise<void>;
356
- /** Web Standard なルーティングハンドラ (画像プロキシ / webhook) を生成する。 */
201
+ /** Web Standard Request/Response ベースのルートハンドラ (画像プロキシ + webhook) */
357
202
  handler(opts?: HandlerOptions): (req: Request) => Promise<Response>;
358
- /** ハッシュキーでキャッシュ画像を取得する。 */
359
203
  getCachedImage(hash: string): Promise<StorageBinary | null>;
360
204
  /**
361
- * Notion 画像 URL を `{imageProxyBase}/{sha256}` 形式へ変換しキャッシュへ書き込む関数。
362
- * 画像キャッシュが未設定 (noop) の場合は `undefined`。react-renderer の
363
- * `resolveBlockImageUrls` などサーバー側で URL 書き換えに使う。
205
+ * Notion 画像 URL を `{imageProxyBase}/{sha256}` 形式へ変換しキャッシュへ書き込む。
206
+ * 画像キャッシュが未設定 (noop) の場合は `undefined`。
364
207
  */
365
208
  readonly cacheImage: ((url: string) => Promise<string>) | undefined;
366
- /**
367
- * 画像プロキシのベース URL (`createClient({ imageProxyBase })`)。
368
- * デフォルト `/api/images`。
369
- */
370
209
  readonly imageProxyBase: string;
371
210
  }
372
211
  /**
373
- * 複数の `CollectionDef` を束ねた CMS クライアントを生成する。
374
- *
375
- * 通常はユーザーが直接呼ぶことはなく、CLI 生成の `nhc.ts` の `createClient`
376
- * (低レベルのこの関数をラップしたもの) を経由する。
212
+ * CMS クライアントを生成する。
377
213
  *
378
214
  * @example
379
- * createClient({
380
- * collections: {
381
- * posts: {
382
- * source: createNotionCollection({ token, dataSourceId, properties }),
383
- * slugField: "slug",
384
- * statusField: "status",
385
- * publishedStatuses: ["公開済み"],
386
- * }
387
- * },
388
- * cache: [memoryCache()],
389
- * swr: { ttlMs: 5 * 60_000 },
215
+ * import { createClient, nodePreset } from "@notion-headless-cms/core";
216
+ * import { notionSource } from "@notion-headless-cms/notion-source";
217
+ * import { schema } from "./generated/nhc.schema";
218
+ *
219
+ * const cms = createClient({
220
+ * sources: { notion: notionSource({ schema, token: process.env.NOTION_TOKEN! }) },
221
+ * ...nodePreset(),
390
222
  * });
223
+ *
224
+ * const posts = await cms.posts.list();
391
225
  */
392
- declare function createClient<C extends CollectionsConfig = CollectionsConfig, S extends CMSSources = CMSSources>(opts: CreateClientOptions<C, S>): CMSClient<MergeSourceCollections<S> extends CollectionsConfig ? MergeSourceCollections<S> : C>;
393
- //#endregion
394
- //#region src/rendering.d.ts
395
- /** 本文レンダリングに必要な依存を束ねたコンテキスト。 */
396
- interface RenderContext<T extends BaseContentItem> {
397
- source: DataSource<T>;
398
- rendererFn: RendererFn | undefined;
399
- imgCache: ImageCacheOps;
400
- imgCacheName: string;
401
- hasImageCache: boolean;
402
- imageProxyBase: string;
403
- contentConfig: ContentConfig | undefined;
404
- hooks: CMSHooks<T>;
405
- logger: Logger | undefined;
406
- }
226
+ declare function createClient<S extends CMSSources = CMSSources>(opts: CreateClientOptions<S>): CMSClient<MergeSourceCollections<S> extends CollectionsConfig ? MergeSourceCollections<S> : CollectionsConfig>;
407
227
  //#endregion
408
228
  //#region src/retry.d.ts
409
229
  interface RetryConfig {
@@ -425,69 +245,5 @@ declare const DEFAULT_RETRY_CONFIG: RetryConfig;
425
245
  */
426
246
  declare function withRetry<T>(fn: () => Promise<T>, config: RetryConfig): Promise<T>;
427
247
  //#endregion
428
- //#region src/collection.d.ts
429
- /**
430
- * コレクション別キャッシュキーを生成する。
431
- * item: `{collection}:{slug}` / list: `{collection}`
432
- *
433
- * (Cache adapter 内部のキー戦略はアダプタごとに異なるが、
434
- * 表示や再計算用に core 側でも公開ヘルパーを提供する)
435
- */
436
- declare function collectionKey(collection: string, slug?: string): string;
437
- /** 単一コレクションの DataSource + SWR キャッシュ依存を束ねたコンテキスト。 */
438
- interface CollectionContext<T extends BaseContentItem> {
439
- collection: string;
440
- source: DataSource<T>;
441
- docCache: DocumentCacheOps;
442
- docCacheName: string;
443
- render: RenderContext<T>;
444
- hooks: CMSHooks<T>;
445
- logger: Logger | undefined;
446
- ttlMs: number | undefined;
447
- publishedStatuses: string[];
448
- accessibleStatuses: string[];
449
- retryConfig: RetryConfig;
450
- maxConcurrent: number;
451
- waitUntil: ((p: Promise<unknown>) => void) | undefined;
452
- /**
453
- * slug として使うフィールド名 (CLI 生成の `CollectionDef.slugField`)。
454
- * `source.properties[slugField].notion` を Notion プロパティ名として
455
- * `findByProp` を呼び出す。
456
- */
457
- slugField: string;
458
- }
459
- /** CollectionClient の実装。ユーザーは `createClient` 経由でインスタンスを受け取る。 */
460
- declare class CollectionClientImpl<T extends BaseContentItem> implements CollectionClient<T> {
461
- private readonly ctx;
462
- readonly cache: CollectionCacheOps<T>;
463
- constructor(ctx: CollectionContext<T>);
464
- find(slug: string, opts?: FindOptions): Promise<ItemWithContent<T> | null>;
465
- list(opts?: ListOptions<T>): Promise<T[]>;
466
- params(): Promise<string[]>;
467
- check(slug: string, currentVersion: string): Promise<CheckResult<T> | null>;
468
- adjacent(slug: string, opts?: AdjacencyOptions<T>): Promise<{
469
- prev: T | null;
470
- next: T | null;
471
- }>;
472
- private invalidateImpl;
473
- private invalidateItemImpl;
474
- private warmImpl;
475
- private persistMeta;
476
- private invalidateContentEntry;
477
- /**
478
- * 本文キャッシュをロードする。キャッシュが無いか、メタとの整合性が取れない場合は
479
- * 再生成して書き戻す。
480
- */
481
- private loadOrBuildContent;
482
- /** メタ既知の状態で本文だけバックグラウンド再生成する。エラーは onSwrError フックに通知する。 */
483
- private rebuildContentBg;
484
- private attachLazyContent;
485
- private fetchList;
486
- private checkAndUpdateItemBg;
487
- private checkAndUpdateListBg;
488
- private fetchListRaw;
489
- private fetchRaw;
490
- }
491
- //#endregion
492
- export { type AdjacencyOptions, type BaseContentItem, type BuiltInCMSErrorCode, type CMSAdapter, 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, CollectionClientImpl, type CollectionContext, type CollectionDef, type CollectionsConfig, 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 InferCollectionItem, type InlineNode, type InvalidateKind, type InvalidateScope, type ItemWithContent, type ListOptions, type LogLevel, type Logger, type MaybePromise, type MemoryCacheOptions, type MergeSourceCollections, 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, collectionKey, createClient, createHandler, definePlugin, isCMSError, isCMSErrorInNamespace, isStale, matchCMSError, memoryCache, mergeHooks, mergeLoggers, noopDocOps, noopImgOps, sha256Hex, withRetry };
248
+ 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 };
493
249
  //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import { memoryCache } from "./cache/memory.mjs";
2
2
  import { CMSError, isCMSError, isCMSErrorInNamespace, matchCMSError } from "./errors.mjs";
3
3
  import { mergeHooks, mergeLoggers } from "./hooks.mjs";
4
+ import { nodePreset } from "./preset/node.mjs";
4
5
  //#region src/cache.ts
5
6
  /** 文字列をSHA-256でハッシュ化し、16進数文字列として返す。画像キーの生成に使用。 */
6
7
  async function sha256Hex(input) {
@@ -159,16 +160,16 @@ function buildCacheImageFn(cache, cacheName, imageProxyBase, logger) {
159
160
  //#endregion
160
161
  //#region src/rendering.ts
161
162
  /**
162
- * `@notion-headless-cms/renderer` を動的 import してデフォルトレンダラーを返す。
163
+ * `@notion-headless-cms/markdown-html` を動的 import してデフォルトレンダラーを返す。
163
164
  * core のゼロ依存ルールを守るため静的 import は禁止。
164
165
  */
165
166
  async function loadDefaultRenderer() {
166
167
  try {
167
- return (await import("@notion-headless-cms/renderer")).renderMarkdown;
168
+ return (await import("@notion-headless-cms/markdown-html")).renderMarkdown;
168
169
  } catch {
169
170
  throw new CMSError({
170
171
  code: "core/config_invalid",
171
- message: "renderer が未指定で、@notion-headless-cms/renderer のロードにも失敗しました。 createClient の renderer オプションを指定するか、@notion-headless-cms/renderer をインストールしてください。",
172
+ message: "renderer が未指定で、@notion-headless-cms/markdown-html のロードにも失敗しました。 createClient の renderer オプションを指定するか、@notion-headless-cms/markdown-html をインストールしてください。",
172
173
  context: { operation: "loadDefaultRenderer" }
173
174
  });
174
175
  }
@@ -322,17 +323,6 @@ async function withRetry(fn, config) {
322
323
  }
323
324
  //#endregion
324
325
  //#region src/collection.ts
325
- /**
326
- * コレクション別キャッシュキーを生成する。
327
- * item: `{collection}:{slug}` / list: `{collection}`
328
- *
329
- * (Cache adapter 内部のキー戦略はアダプタごとに異なるが、
330
- * 表示や再計算用に core 側でも公開ヘルパーを提供する)
331
- */
332
- function collectionKey(collection, slug) {
333
- return slug ? `${collection}:${slug}` : collection;
334
- }
335
- /** CollectionClient の実装。ユーザーは `createClient` 経由でインスタンスを受け取る。 */
336
326
  var CollectionClientImpl = class {
337
327
  cache;
338
328
  constructor(ctx) {
@@ -492,21 +482,17 @@ var CollectionClientImpl = class {
492
482
  kind: "content"
493
483
  });
494
484
  }
495
- /**
496
- * 本文キャッシュをロードする。キャッシュが無いか、メタとの整合性が取れない場合は
497
- * 再生成して書き戻す。
498
- */
485
+ /** 本文キャッシュ。メタとの整合 (`notionUpdatedAt`) が崩れていれば再生成して書き戻す。 */
499
486
  async loadOrBuildContent(slug, item) {
500
487
  const expected = this.ctx.source.getLastModified(item);
501
488
  const cached = await this.ctx.docCache.getContent(this.ctx.collection, slug);
502
- const needsNotionBlocksBackfill = this.ctx.source.loadNotionBlocks !== void 0 && cached !== null && cached.notionBlocks === void 0;
503
- if (cached && cached.notionUpdatedAt === expected && !needsNotionBlocksBackfill) return cached;
489
+ if (cached && cached.notionUpdatedAt === expected) return cached;
504
490
  const fresh = await buildCachedItemContent(item, this.ctx.render);
505
491
  await this.ctx.docCache.setContent(this.ctx.collection, slug, fresh);
506
492
  this.ctx.hooks.onContentRevalidated?.(slug, fresh);
507
493
  return fresh;
508
494
  }
509
- /** メタ既知の状態で本文だけバックグラウンド再生成する。エラーは onSwrError フックに通知する。 */
495
+ /** メタ既知の状態で本文だけ再生成する。エラーは onSwrError フックに通知して握り潰す。 */
510
496
  async rebuildContentBg(slug, item) {
511
497
  try {
512
498
  const fresh = await buildCachedItemContent(item, this.ctx.render);
@@ -757,7 +743,7 @@ function applyListOptions(items, opts) {
757
743
  if (skip > 0 || limit !== void 0) result = result.slice(skip, limit !== void 0 ? skip + limit : void 0);
758
744
  return result;
759
745
  }
760
- /** publishedAt 降順、未設定の場合は lastEditedTime 降順でソートする。 */
746
+ /** publishedAt 降順 (未設定なら lastEditedTime 降順) でソートする。 */
761
747
  function sortByPublishedAtDesc(items) {
762
748
  return [...items].sort((a, b) => {
763
749
  const av = a.publishedAt ?? a.lastEditedTime;
@@ -796,10 +782,7 @@ const DEFAULT_OPTS = {
796
782
  imagesPath: "/images",
797
783
  revalidatePath: "/revalidate"
798
784
  };
799
- /**
800
- * CMSError のコードから HTTP ステータスコードを返す。
801
- * 既知の webhook エラーコードのみ対応し、それ以外は null を返す。
802
- */
785
+ /** Webhook 系の CMSError コードを HTTP ステータスへ写像する。未対応コードは null。 */
803
786
  function webhookErrorStatus(code) {
804
787
  if (code === "webhook/signature_invalid") return 401;
805
788
  if (code === "webhook/not_implemented") return 501;
@@ -876,10 +859,7 @@ function trimTrailingSlash(s) {
876
859
  //#region src/cms.ts
877
860
  const DEFAULT_IMAGE_PROXY_BASE = "/api/images";
878
861
  /**
879
- * `cache` オプションから document / image オペレーションを解決する。
880
- *
881
- * - 各 adapter の `handles` を見て先勝ち (最初に見つかったもの) で振り分ける
882
- * - 未指定なら両方 noop
862
+ * adapter の `handles` を見て先勝ちで document / image を割り当てる。未指定は両方 noop。
883
863
  */
884
864
  function resolveCache(cache) {
885
865
  const adapters = cache ?? [];
@@ -915,7 +895,6 @@ const LOG_LEVEL_ORDER = {
915
895
  warn: 2,
916
896
  error: 3
917
897
  };
918
- /** `logger` から `minLevel` 未満のレベルを除いた新しい Logger を返す。 */
919
898
  function applyLogLevel(logger, minLevel) {
920
899
  if (!logger) return void 0;
921
900
  const minOrder = LOG_LEVEL_ORDER[minLevel];
@@ -929,36 +908,31 @@ function applyLogLevel(logger, minLevel) {
929
908
  return filtered;
930
909
  }
931
910
  /**
932
- * 複数の `CollectionDef` を束ねた CMS クライアントを生成する。
933
- *
934
- * 通常はユーザーが直接呼ぶことはなく、CLI 生成の `nhc.ts` の `createClient`
935
- * (低レベルのこの関数をラップしたもの) を経由する。
911
+ * CMS クライアントを生成する。
936
912
  *
937
913
  * @example
938
- * createClient({
939
- * collections: {
940
- * posts: {
941
- * source: createNotionCollection({ token, dataSourceId, properties }),
942
- * slugField: "slug",
943
- * statusField: "status",
944
- * publishedStatuses: ["公開済み"],
945
- * }
946
- * },
947
- * cache: [memoryCache()],
948
- * swr: { ttlMs: 5 * 60_000 },
914
+ * import { createClient, nodePreset } from "@notion-headless-cms/core";
915
+ * import { notionSource } from "@notion-headless-cms/notion-source";
916
+ * import { schema } from "./generated/nhc.schema";
917
+ *
918
+ * const cms = createClient({
919
+ * sources: { notion: notionSource({ schema, token: process.env.NOTION_TOKEN! }) },
920
+ * ...nodePreset(),
949
921
  * });
922
+ *
923
+ * const posts = await cms.posts.list();
950
924
  */
951
925
  function createClient(opts) {
952
- let mergedFromSources;
926
+ const collectionsInput = {};
953
927
  if (opts.sources) {
954
- mergedFromSources = {};
955
- for (const adapter of Object.values(opts.sources)) if (adapter) Object.assign(mergedFromSources, adapter.collections);
928
+ for (const adapter of Object.values(opts.sources)) if (adapter) Object.assign(collectionsInput, adapter.collections);
956
929
  }
957
- const collectionsInput = mergedFromSources ?? opts.collections;
958
- if (!collectionsInput || Object.keys(collectionsInput).length === 0) throw new CMSError({
930
+ if (Object.keys(collectionsInput).length === 0) throw new CMSError({
959
931
  code: "core/config_invalid",
960
- message: "createClient: sources または collections に少なくとも 1 つのコレクションを指定してください。",
961
- context: { operation: "createClient" }
932
+ message: "createClient: sources に少なくとも 1 つのコレクションを指定してください。",
933
+ context: { operation: "createClient" },
934
+ nextSteps: ["notionSource({ schema, token }) を sources.notion に渡す", "`nhc generate` でスキーマを生成してから import する"],
935
+ docsUrl: "https://github.com/kjfsm/notion-headless-cms/blob/main/docs/quickstart.md"
962
936
  });
963
937
  for (const [name, def] of Object.entries(collectionsInput)) {
964
938
  if (!def.source) throw new CMSError({
@@ -967,7 +941,8 @@ function createClient(opts) {
967
941
  context: {
968
942
  operation: "createClient",
969
943
  collection: name
970
- }
944
+ },
945
+ nextSteps: ["notionSource(...) を sources に渡しているか確認する"]
971
946
  });
972
947
  if (!def.slugField) throw new CMSError({
973
948
  code: "core/config_invalid",
@@ -975,7 +950,8 @@ function createClient(opts) {
975
950
  context: {
976
951
  operation: "createClient",
977
952
  collection: name
978
- }
953
+ },
954
+ nextSteps: [`nhc.config.ts の ${name} コレクションに slugField を設定する`]
979
955
  });
980
956
  }
981
957
  const cacheRes = resolveCache(opts.cache);
@@ -1080,6 +1056,6 @@ function definePlugin(plugin) {
1080
1056
  return plugin;
1081
1057
  }
1082
1058
  //#endregion
1083
- export { CMSError, CollectionClientImpl, DEFAULT_RETRY_CONFIG, collectionKey, createClient, createHandler, definePlugin, isCMSError, isCMSErrorInNamespace, isStale, matchCMSError, memoryCache, mergeHooks, mergeLoggers, noopDocOps, noopImgOps, sha256Hex, withRetry };
1059
+ export { CMSError, DEFAULT_RETRY_CONFIG, createClient, createHandler, definePlugin, isCMSError, isCMSErrorInNamespace, isStale, matchCMSError, memoryCache, mergeHooks, mergeLoggers, nodePreset, noopDocOps, noopImgOps, sha256Hex, withRetry };
1084
1060
 
1085
1061
  //# sourceMappingURL=index.mjs.map