@bettercms-ai/next 0.5.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.
@@ -0,0 +1,516 @@
1
+ import { PageMetaJson, ContentBlock, SiteSeoDefaults, DeliveryComponent } from '@bettercms-ai/types';
2
+ export { PageMetaJson, SiteSeoDefaults } from '@bettercms-ai/types';
3
+ import { Metadata } from 'next';
4
+ import { SeoInput, DeliveryForm } from '@bettercms-ai/sdk';
5
+ export { ResolvedSeo, SeoInput, resolveSeo } from '@bettercms-ai/sdk';
6
+ export { ImageFit, ImageFormat, ImageSource, ImageUrlBuilder, imageUrl, default as imageUrlBuilder } from '@bettercms-ai/image-url';
7
+ import { ContentBlock as ContentBlock$1, DeliveryComponent as DeliveryComponent$1 } from '@betttercms/types';
8
+ import { DeliveryForm as DeliveryForm$1 } from '@betttercms/sdk';
9
+
10
+ /**
11
+ * Public types for @bettercms-ai/next.
12
+ *
13
+ * `BetterCMSEntry` mirrors the envelope emitted by @bettercms-ai/codegen's generated
14
+ * file. The adapter normalizes the raw Delivery API response into exactly this shape,
15
+ * so `getEntry`/`listEntries` line up 1:1 with your generated model types.
16
+ */
17
+ /** Normalized error thrown by adapter reads. Mirrors the SDK's BetterCMSError, but kept
18
+ * local so the adapter carries no heavy runtime dependency into a consumer's bundle. */
19
+ declare class BetterCMSError extends Error {
20
+ readonly status: number;
21
+ readonly code: string;
22
+ constructor(message: string, status: number, code: string);
23
+ }
24
+ /**
25
+ * Delivery envelope around a model's typed `fields`. Returned by `getEntry`/`listEntries`.
26
+ * Identical to the `BetterCMSEntry<TFields>` your generated types declare.
27
+ *
28
+ * `publishedAt`/`updatedAt` are ISO-8601 strings, or `null` when absent — never a
29
+ * fabricated empty string (so `new Date(...)` is safe to attempt only on non-null).
30
+ */
31
+ interface BetterCMSEntry<TFields> {
32
+ readonly slug: string;
33
+ readonly status: "draft" | "published";
34
+ readonly fields: TFields;
35
+ readonly publishedAt: string | null;
36
+ readonly updatedAt: string | null;
37
+ }
38
+ interface BetterCMSConfig {
39
+ /** Workspace slug — the `:workspace` segment of the Delivery API path. */
40
+ workspace: string;
41
+ /** Optional delivery API key (sent as `Authorization: Bearer`). */
42
+ apiKey?: string;
43
+ /** Delivery API base. Default: `https://api.bettercms.ai/api/v1/delivery`. */
44
+ baseUrl?: string;
45
+ /** Preview API base. Default: derived from `baseUrl` (`/delivery` → `/preview`). */
46
+ previewBaseUrl?: string;
47
+ /**
48
+ * Default Next.js revalidation for reads (seconds). `false` = always fresh
49
+ * (`cache: "no-store"`). Per-call `revalidate` overrides this. Default: `60`.
50
+ */
51
+ revalidate?: number | false;
52
+ }
53
+ /** Per-read options shared by `getEntry` and `listEntries`. */
54
+ interface ReadOptions {
55
+ /** ISR window in seconds, or `false` for `cache: "no-store"`. Overrides config default. */
56
+ revalidate?: number | false;
57
+ /** Next.js cache tags for on-demand `revalidateTag()` invalidation. */
58
+ tags?: string[];
59
+ /** Reference hydration depth (0 = none, 1 = direct refs, 2 = nested). */
60
+ depth?: 0 | 1 | 2;
61
+ /** Field projection — only these field keys are returned. */
62
+ select?: string[];
63
+ }
64
+ /** Options for `getEntry`, including draft-preview access. */
65
+ interface GetEntryOptions extends ReadOptions {
66
+ /** Fetch draft content via the preview endpoint. Requires `previewToken`. */
67
+ preview?: boolean;
68
+ /** Signed preview token (from `POST .../preview-token`). */
69
+ previewToken?: string;
70
+ }
71
+ /** Options for `listEntries`. */
72
+ interface ListEntriesOptions extends ReadOptions {
73
+ /** 1-based page number. Default: 1. */
74
+ page?: number;
75
+ /** Items per page (max 100). Default: 20. */
76
+ perPage?: number;
77
+ }
78
+ /** Paginated result returned by `listEntries`. */
79
+ interface EntryList<TFields> {
80
+ items: BetterCMSEntry<TFields>[];
81
+ page: number;
82
+ perPage: number;
83
+ totalItems: number;
84
+ totalPages: number;
85
+ hasNextPage: boolean;
86
+ hasPreviousPage: boolean;
87
+ }
88
+
89
+ /**
90
+ * createBetterCMS — the Next.js delivery client.
91
+ *
92
+ * Reads run through the standard `fetch`, which the Next.js App Router patches to
93
+ * participate in the data cache. We pass `next: { revalidate, tags }` so content is
94
+ * cached/ISR'd and can be invalidated on publish via `revalidateTag()`.
95
+ *
96
+ * The raw Delivery API response is normalized into `BetterCMSEntry<TFields>` so the
97
+ * return types match what @bettercms-ai/codegen generates.
98
+ */
99
+
100
+ /** A published page with its block_json — feed `blocks` to `<BcmsBlocks>`.
101
+ * The SEO fields (FLO-302) feed `buildMetadata` for the route's `generateMetadata`. */
102
+ interface BetterCMSPage {
103
+ slug: string;
104
+ title: string;
105
+ metaTitle: string | null;
106
+ metaDescription: string | null;
107
+ /** Rich SEO (OG / Twitter / canonical / JSON-LD). `null` when unset. */
108
+ metaJson: PageMetaJson | null;
109
+ blocks: ContentBlock[];
110
+ publishedAt: string | null;
111
+ updatedAt: string | null;
112
+ }
113
+ /**
114
+ * The typed BetterCMS reader for Next.js.
115
+ *
116
+ * @typeParam Schema - your generated `BetterCMSSchema` (slug → fields registry). When
117
+ * supplied, `listEntries("blog")` is typed by the model's fields and the model name
118
+ * autocompletes. Defaults to an open record so it also works untyped.
119
+ */
120
+ interface BetterCMSNext<Schema extends Record<string, unknown>> {
121
+ /** Fetch one published entry by its content slug. Returns `null` when not found.
122
+ * Passing `select` narrows the result to `Partial<TFields>` (projection drops fields). */
123
+ getEntry<TFields = unknown>(slug: string, opts: GetEntryOptions & {
124
+ select: string[];
125
+ }): Promise<BetterCMSEntry<Partial<TFields>> | null>;
126
+ getEntry<TFields = unknown>(slug: string, opts?: GetEntryOptions): Promise<BetterCMSEntry<TFields> | null>;
127
+ /** List published entries, filtered to one model. Throws `BetterCMSError`
128
+ * (CONTENT_NOT_FOUND) for an unknown workspace/model — an empty model returns an
129
+ * empty page. Passing `select` narrows fields to `Partial`. */
130
+ listEntries<M extends keyof Schema & string>(model: M, opts: ListEntriesOptions & {
131
+ select: string[];
132
+ }): Promise<EntryList<Partial<Schema[M]>>>;
133
+ listEntries<M extends keyof Schema & string>(model: M, opts?: ListEntriesOptions): Promise<EntryList<Schema[M]>>;
134
+ listEntries<TFields = unknown>(model?: string, opts?: ListEntriesOptions): Promise<EntryList<TFields>>;
135
+ /** Fetch one published page (with its `blocks` for `<BcmsBlocks>`) by slug.
136
+ * Returns `null` when not found. */
137
+ getPage(slug: string, opts?: {
138
+ revalidate?: number | false;
139
+ tags?: string[];
140
+ }): Promise<BetterCMSPage | null>;
141
+ }
142
+ declare function createBetterCMS<Schema extends Record<string, unknown> = Record<string, unknown>>(config: BetterCMSConfig): BetterCMSNext<Schema>;
143
+
144
+ /**
145
+ * buildMetadata — map a BetterCMS page's SEO onto a Next.js `Metadata` object.
146
+ *
147
+ * Wire it into a route's `generateMetadata` so per-page Meta Title / Description and
148
+ * the rich OG / Twitter / canonical the dashboard saves drive the live `<head>`:
149
+ *
150
+ * import { createBetterCMS, buildMetadata } from "@bettercms-ai/next";
151
+ * const bcms = createBetterCMS({ workspace: "acme" });
152
+ *
153
+ * export async function generateMetadata({ params }): Promise<Metadata> {
154
+ * const page = await bcms.getPage(params.slug);
155
+ * if (!page) return {};
156
+ * return buildMetadata(page, siteDefaults); // siteDefaults optional
157
+ * }
158
+ *
159
+ * JSON-LD isn't part of Next's `Metadata`; render `resolveSeo(page, defaults).jsonLd`
160
+ * as a `<script type="application/ld+json">` in the page component (see the README).
161
+ *
162
+ * The merge (page-over-site) is delegated to `resolveSeo` (@bettercms-ai/sdk), the same
163
+ * resolver the Astro adapter uses — so every surface stays consistent.
164
+ */
165
+
166
+ /** Build a Next.js `Metadata` from a BetterCMS page, layering page meta over site defaults. */
167
+ declare function buildMetadata(page: SeoInput, siteDefaults?: SiteSeoDefaults): Metadata;
168
+
169
+ /**
170
+ * Build-time content snapshot — the writer + reader of `bcms-content.json`.
171
+ *
172
+ * Why this exists: a static (`output: "export"`) build must resolve all content at
173
+ * build time. If a page instead falls back to a live `cache: "no-store"` fetch, its
174
+ * route becomes dynamic and `output: "export"` silently drops it — you get an empty
175
+ * `out/`. The fix is a single build-time snapshot the pages read synchronously.
176
+ *
177
+ * The WRITER (`buildSnapshot`/`writeSnapshot`, used by the `bettercms-snapshot` bin)
178
+ * and the READER (`getContent`) share ONE serializer (`serializeSnapshot`/
179
+ * `parseSnapshot`) so the on-disk shape can never drift between the two halves.
180
+ *
181
+ * This is intentionally separate from {@link createBetterCMS}: that stays the live,
182
+ * network-backed client. `getContent` is the snapshot-preferring reader a static
183
+ * site uses. Existing consumers that deliberately rely on the live fallback are
184
+ * unaffected — only `getContent` enforces "snapshot or bust".
185
+ */
186
+
187
+ /** Bump when the on-disk shape changes incompatibly — readers reject mismatches. */
188
+ declare const SNAPSHOT_VERSION = 1;
189
+ /** Default snapshot filename, resolved against `process.cwd()`. */
190
+ declare const DEFAULT_SNAPSHOT_FILE = "bcms-content.json";
191
+ /** The full-site content snapshot. Entries are grouped by model slug so the reader
192
+ * can serve `listEntries(model)` offline without a per-entry model tag. */
193
+ interface ContentSnapshot {
194
+ version: number;
195
+ workspace: string;
196
+ generatedAt: string;
197
+ models: Record<string, BetterCMSEntry<unknown>[]>;
198
+ }
199
+ /** Serialize a snapshot to the canonical JSON form written to disk. */
200
+ declare function serializeSnapshot(snapshot: ContentSnapshot): string;
201
+ /** Parse + validate a snapshot JSON string. Throws on a version mismatch so a stale
202
+ * snapshot fails loudly rather than feeding the build the wrong shape. */
203
+ declare function parseSnapshot(json: string): ContentSnapshot;
204
+ /** Write a snapshot to `path` (relative paths resolve against cwd), creating dirs. */
205
+ declare function writeSnapshot(path: string, snapshot: ContentSnapshot): void;
206
+ /** Read + parse a snapshot from `path`. Returns `null` only when the file is absent;
207
+ * a present-but-corrupt/stale file throws (callers must not treat that as "missing"). */
208
+ declare function readSnapshot(path: string): ContentSnapshot | null;
209
+ /** Minimal client surface `buildSnapshot` needs — lets tests inject a fake. Kept
210
+ * non-generic so a simple fake (and the live client's matching overload) both fit. */
211
+ type ListOnly = {
212
+ listEntries(model?: string, opts?: ListEntriesOptions): Promise<EntryList<unknown>>;
213
+ };
214
+ /**
215
+ * Page through every model's published entries and assemble a full-site snapshot.
216
+ * `modelSlugs` come from the Management API (`fetchModels`) so the snapshot covers
217
+ * exactly this project's schema. Pass `client` in tests; defaults to the live client.
218
+ */
219
+ declare function buildSnapshot(opts: {
220
+ config: BetterCMSConfig;
221
+ modelSlugs: string[];
222
+ client?: ListOnly;
223
+ /** Stamped into `generatedAt`; injectable for deterministic tests. */
224
+ now?: string;
225
+ }): Promise<ContentSnapshot>;
226
+ /** Snapshot-backed content reader. Async so it can transparently fall back to the
227
+ * live client in preview without changing the call sites. */
228
+ interface ContentSource {
229
+ getEntry<TFields = unknown>(slug: string): Promise<BetterCMSEntry<TFields> | null>;
230
+ listEntries<TFields = unknown>(model?: string): Promise<EntryList<TFields>>;
231
+ }
232
+ /**
233
+ * The reader a static site should use. In a normal (export) build it reads the
234
+ * memoized `bcms-content.json` and serves entries from memory. If the snapshot is
235
+ * absent it THROWS a clear, actionable error — the whole point is to turn the old
236
+ * silent empty-`out/` failure into a loud build error. Set `BETTERCMS_PREVIEW=1`
237
+ * for a preview build that reads live instead.
238
+ */
239
+ declare function getContent<Schema extends Record<string, unknown> = Record<string, unknown>>(config: BetterCMSConfig, opts?: {
240
+ snapshotPath?: string;
241
+ }): ContentSource;
242
+
243
+ /**
244
+ * llms.txt generation — the AI-discovery surface of the deterministic floor.
245
+ *
246
+ * `generateLlmsTxt` is pure (models in → string out), so it is trivially testable and
247
+ * deterministic. `llmsTxtRoute` wraps it as an App Router `route.ts` handler. `fetchModels`
248
+ * pulls the live schema from the Management API for sites that want a self-updating file.
249
+ *
250
+ * Output follows the llmstxt.org convention: an H1 title, a `>` summary, then sections.
251
+ */
252
+ /** Minimal field shape needed to describe a model in llms.txt. */
253
+ interface LlmsModelField {
254
+ key: string;
255
+ label?: string;
256
+ type: string;
257
+ required?: boolean;
258
+ }
259
+ /** Minimal model shape — a subset of the Management API model row. */
260
+ interface LlmsModel {
261
+ slug: string;
262
+ name?: string;
263
+ description?: string | null;
264
+ fields: LlmsModelField[];
265
+ }
266
+ interface LlmsTxtOptions {
267
+ /** H1 title. Default: "BetterCMS content". */
268
+ title?: string;
269
+ /** One-line `>` summary under the title. */
270
+ description?: string;
271
+ /** Delivery API base used to render example endpoints. */
272
+ baseUrl?: string;
273
+ /** Workspace slug used in example endpoints. */
274
+ workspace?: string;
275
+ /** Extra free-form lines appended under a "## Notes" section. */
276
+ notes?: string[];
277
+ }
278
+ /**
279
+ * Render an llms.txt document describing the available content models. Deterministic:
280
+ * models are sorted by slug; field order is preserved as authored.
281
+ */
282
+ declare function generateLlmsTxt(models: LlmsModel[], opts?: LlmsTxtOptions): string;
283
+ /** Options for {@link llmsTxtRoute}. Provide one of `models` or `getModels`. */
284
+ interface LlmsTxtRouteConfig extends LlmsTxtOptions {
285
+ /** Static model list (e.g. fetched at build time). */
286
+ models?: LlmsModel[];
287
+ /** Dynamic provider — called per request (wrap in your own caching if needed). */
288
+ getModels?: () => Promise<LlmsModel[]> | LlmsModel[];
289
+ /** Cache-Control header value. Default: `public, max-age=3600`. */
290
+ cacheControl?: string;
291
+ }
292
+ /**
293
+ * Build an App Router route handler that serves llms.txt as `text/plain`.
294
+ *
295
+ * ```ts
296
+ * // app/llms.txt/route.ts
297
+ * import { llmsTxtRoute } from "@bettercms-ai/next";
298
+ * export const GET = llmsTxtRoute({ models, title: "Acme", workspace: "acme" });
299
+ * ```
300
+ */
301
+ declare function llmsTxtRoute(config: LlmsTxtRouteConfig): () => Promise<Response>;
302
+ /**
303
+ * Fetch content models from the Management API (`GET /management/content/models`).
304
+ * The key is project-scoped server-side, so this returns exactly this site's schema.
305
+ * Dependency-free so it runs in any runtime (Edge route, build script, Node).
306
+ */
307
+ declare function fetchModels(opts: {
308
+ /** Management API base, e.g. "https://api.bettercms.ai/api/v1". */
309
+ apiUrl: string;
310
+ /** A management-scoped key (content:manage). */
311
+ apiKey: string;
312
+ /** Optional fetch override (testing / custom runtime). */
313
+ fetchImpl?: typeof fetch;
314
+ }): Promise<LlmsModel[]>;
315
+
316
+ /**
317
+ * Draft-preview helpers. `next/headers` is imported dynamically so this module is safe
318
+ * to load outside a Next.js request scope (tests, build scripts) without throwing.
319
+ */
320
+ /** True when Next.js Draft Mode is enabled for the current request. */
321
+ declare function isDraftEnabled(): Promise<boolean>;
322
+ /**
323
+ * The preview token stored by `createDraftModeRoute`, or null. Pass it to
324
+ * `getEntry(slug, { preview: true, previewToken })` to read drafts.
325
+ */
326
+ declare function getPreviewToken(): Promise<string | null>;
327
+
328
+ /**
329
+ * createDraftModeRoute — an App Router route-handler factory for entering draft
330
+ * preview. Mount it at e.g. `app/api/draft/route.ts`:
331
+ *
332
+ * import { createDraftModeRoute } from "@bettercms-ai/next";
333
+ * export const { GET } = createDraftModeRoute({ apiUrl: "https://api.bettercms.ai" });
334
+ *
335
+ * Visiting `/api/draft?token=<jwt>&redirect=/path` validates the preview token
336
+ * against the backend (the signature authority), enables Next Draft Mode, stores
337
+ * the token in an httpOnly cookie, and redirects. Reads can then pass the token
338
+ * via `getPreviewToken()` to `getEntry({ preview: true, previewToken })`.
339
+ */
340
+ declare const PREVIEW_TOKEN_COOKIE = "bcms-preview-token";
341
+ interface DraftModeRouteConfig {
342
+ /** Backend base, e.g. `https://api.bettercms.ai`. */
343
+ apiUrl: string;
344
+ }
345
+ declare function createDraftModeRoute(config: DraftModeRouteConfig): {
346
+ GET: (request: Request) => Promise<Response>;
347
+ };
348
+ /** Exit draft mode and clear the preview-token cookie. */
349
+ declare function createDisableDraftRoute(): {
350
+ GET: (request: Request) => Promise<Response>;
351
+ };
352
+
353
+ /**
354
+ * Build-time reader for the forms the deploy Action injects into the content
355
+ * snapshot (schema "bcms-content/v1": `{ ...collections, forms, turnstileSiteKey }`).
356
+ *
357
+ * This is intentionally separate from `readSnapshot()` — that reads the
358
+ * `@bettercms-ai/next` "bettercms-snapshot" format (`{ version, models }`), which is a
359
+ * different file shape. Connected-repo sites on <name>.bettercms.site get the Action
360
+ * snapshot, so forms live here. Server/build-time only (uses node:fs).
361
+ */
362
+
363
+ interface SnapshotForms {
364
+ forms: DeliveryForm[];
365
+ /** Public Turnstile site key for any form with `turnstileEnabled`, or null. */
366
+ turnstileSiteKey: string | null;
367
+ }
368
+ /**
369
+ * Read forms from the Action's `bcms-content.json`. Missing file or no forms →
370
+ * an empty set (a content-only build never throws). `path` resolves against cwd.
371
+ */
372
+ declare function readForms(path?: string): SnapshotForms;
373
+ /** Find a single form by `id` or `name` from the Action snapshot. */
374
+ declare function getForm(idOrName: string, path?: string): DeliveryForm | undefined;
375
+
376
+ /**
377
+ * Build-time reader for the pages the deploy Action injects into the content
378
+ * snapshot (schema "bcms-content/v1": `{ ...collections, forms, pages, turnstileSiteKey }`).
379
+ *
380
+ * Sibling to `forms-read.ts`. Connected-repo sites on <name>.bettercms.site get the
381
+ * Action snapshot, so published pages (with their block_json) live here. Render a page
382
+ * with `<BcmsBlocks blocks={page.blocks} forms={readForms().forms} />`. The same data
383
+ * is also available at runtime via `createBetterCMS(...).getPage(slug)`.
384
+ * Server/build-time only (uses node:fs).
385
+ */
386
+
387
+ interface SnapshotPage {
388
+ slug: string;
389
+ title: string;
390
+ metaTitle: string | null;
391
+ metaDescription: string | null;
392
+ /** Rich SEO (OG / Twitter / canonical / JSON-LD) — FLO-302. Feed to `buildMetadata`. */
393
+ metaJson: PageMetaJson | null;
394
+ /** The page's block_json — pass to `<BcmsBlocks blocks={...} />`. */
395
+ blocks: ContentBlock[];
396
+ updatedAt: string | null;
397
+ publishedAt: string | null;
398
+ }
399
+ /**
400
+ * Read published pages from the Action's `bcms-content.json`. Missing file or no
401
+ * pages → an empty list (a pages-less build never throws). `path` resolves against cwd.
402
+ */
403
+ declare function readPages(path?: string): SnapshotPage[];
404
+ /** Find a single published page by `slug` from the Action snapshot. */
405
+ declare function getPage(slug: string, path?: string): SnapshotPage | undefined;
406
+
407
+ /**
408
+ * Build-time reader for the reusable components the deploy Action injects into the
409
+ * content snapshot (`bcms-content.json` → `components`). Mirrors `forms-read.ts`.
410
+ *
411
+ * Pass the result to `<BcmsBlocks components={readComponents()} … />` so `component`
412
+ * blocks resolve their definitions. Server/build-time only (uses node:fs).
413
+ */
414
+
415
+ /**
416
+ * Read components from the Action's `bcms-content.json`. Missing file or no
417
+ * components → an empty array (a content-only build never throws).
418
+ */
419
+ declare function readComponents(path?: string): DeliveryComponent[];
420
+ /** Find a single component by `id`, `slug`, or `name` from the Action snapshot. */
421
+ declare function getComponent(idOrSlug: string, path?: string): DeliveryComponent | undefined;
422
+
423
+ /**
424
+ * <BcmsForm> — render a BetterCMS form natively in React, the headless way.
425
+ *
426
+ * Pass a form straight from your content snapshot (`readSnapshot().forms[n]`) or
427
+ * the delivery API. It renders real inputs (no iframe), handles conditional
428
+ * `showIf` fields, URL-query prefill, the honeypot, an optional Turnstile widget,
429
+ * and submits via `@bettercms-ai/sdk`'s `submitForm`. All visible markup is
430
+ * unstyled and class-driven, so the host site owns the look.
431
+ */
432
+
433
+ interface BcmsFormProps {
434
+ /** A form from your content snapshot or the delivery API. */
435
+ form: DeliveryForm;
436
+ /** API origin. Defaults to the SDK default (https://api.bettercms.ai). */
437
+ apiBase?: string;
438
+ /** Turnstile site key — needed to render the widget when `form.turnstileEnabled`. */
439
+ turnstileSiteKey?: string;
440
+ /** Called with the new submission id after a successful submit (before any redirect). */
441
+ onSubmitted?: (submissionId: string) => void;
442
+ /** Class for the <form> element; field wrappers/status use `bcms-*` classes. */
443
+ className?: string;
444
+ }
445
+
446
+ /**
447
+ * <BcmsBlocks> — render a BetterCMS page's block_json natively in React.
448
+ *
449
+ * Pass the `blocks` from a page (delivery API `/content/:slug` → `entry.blocks`)
450
+ * plus the `forms` you read with `readForms()`. Every block type renders to plain,
451
+ * class-driven markup (the host site owns the styling); a `form` block resolves its
452
+ * `formId` against `forms` and renders `<BcmsForm>` inline exactly where the user
453
+ * placed it in the page builder. This is the turnkey "Webflow" path — forms show up
454
+ * wherever they were dropped on the page, with no manual wiring.
455
+ *
456
+ * Ships from its own "use client" entry (like <BcmsForm>) so it can be rendered from
457
+ * a Next App Router Server Component without breaking the RSC boundary.
458
+ */
459
+
460
+ interface BcmsBlocksProps {
461
+ /** A page's blocks (delivery API `/content/:slug` → `entry.blocks`). */
462
+ blocks: ContentBlock$1[];
463
+ /** Forms available to `form` blocks — from `readForms().forms`. */
464
+ forms?: DeliveryForm$1[];
465
+ /** Reusable components available to `component` blocks — from `readComponents()`. */
466
+ components?: DeliveryComponent$1[];
467
+ /** Turnstile site key — forwarded to `<BcmsForm>` for form blocks. */
468
+ turnstileSiteKey?: string;
469
+ /** API origin forwarded to `<BcmsForm>`. Defaults to the SDK default. */
470
+ apiBase?: string;
471
+ /** Class for the wrapping <div>. */
472
+ className?: string;
473
+ }
474
+
475
+ interface BcmsLiveProps {
476
+ /** Live SSE URL, e.g. from `defineBetterCMSLive().liveSrc`. */
477
+ src: string;
478
+ /** Refresh the route on each change event. Default true. */
479
+ refreshOnChange?: boolean;
480
+ /** Optional callback for custom handling (e.g. targeted revalidation). */
481
+ onChange?: (event: {
482
+ type: string;
483
+ slug: string;
484
+ tags: string[];
485
+ }) => void;
486
+ }
487
+
488
+ /**
489
+ * defineBetterCMSLive — build the live SSE URL for `<BcmsLive>` from your config.
490
+ * Server-safe (no React); pass the returned `liveSrc` to the client component.
491
+ */
492
+ interface BetterCMSLiveConfig {
493
+ /** Backend base, e.g. `https://api.bettercms.ai`. */
494
+ apiUrl: string;
495
+ workspace: string;
496
+ /** A content:read key — exposed to the browser via the SSE URL query. */
497
+ apiKey: string;
498
+ }
499
+ declare function defineBetterCMSLive(config: BetterCMSLiveConfig): {
500
+ liveSrc: string;
501
+ };
502
+
503
+ interface RevalidateRouteConfig {
504
+ /** Shared secret matching the project's revalidation webhook secret. */
505
+ secret?: string;
506
+ }
507
+ declare function createRevalidateRoute(config?: RevalidateRouteConfig): {
508
+ POST: (request: Request) => Promise<Response>;
509
+ };
510
+
511
+ interface VisualEditingProps {
512
+ /** Dashboard base for deep links. Defaults to https://dashboard.bettercms.ai. */
513
+ studioUrl?: string;
514
+ }
515
+
516
+ export { type BcmsBlocksProps, type BcmsFormProps, type BcmsLiveProps, type BetterCMSConfig, type BetterCMSEntry, BetterCMSError, type BetterCMSLiveConfig, type BetterCMSNext, type BetterCMSPage, type ContentSnapshot, type ContentSource, DEFAULT_SNAPSHOT_FILE, type DraftModeRouteConfig, type EntryList, type GetEntryOptions, type ListEntriesOptions, type LlmsModel, type LlmsModelField, type LlmsTxtOptions, type LlmsTxtRouteConfig, PREVIEW_TOKEN_COOKIE, type ReadOptions, type RevalidateRouteConfig, SNAPSHOT_VERSION, type SnapshotForms, type SnapshotPage, type VisualEditingProps, buildMetadata, buildSnapshot, createBetterCMS, createDisableDraftRoute, createDraftModeRoute, createRevalidateRoute, defineBetterCMSLive, fetchModels, generateLlmsTxt, getComponent, getContent, getForm, getPage, getPreviewToken, isDraftEnabled, llmsTxtRoute, parseSnapshot, readComponents, readForms, readPages, readSnapshot, serializeSnapshot, writeSnapshot };