@bettercms-ai/sdk 1.6.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/README.md +126 -0
- package/dist/index.cjs +1616 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1072 -0
- package/dist/index.d.ts +1072 -0
- package/dist/index.js +1507 -0
- package/dist/index.js.map +1 -0
- package/package.json +39 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1072 @@
|
|
|
1
|
+
import * as _bettercms_ai_types from '@bettercms-ai/types';
|
|
2
|
+
import { ContentPageResult, Content, ContentModelField, BetterCMSErrorCode, AuthUser, AuthSession, SignInInput, SignUpInput, Perspective, DeliveryEntry, DeliveryList, DeliveryPage, PageMetaJson, SiteSeoDefaults, Page, MediaAsset, Form, FormSubmission, Workspace, ApiKey, ApiKeyPermission, ApiKeyTokenType } from '@bettercms-ai/types';
|
|
3
|
+
export { ApiError, ApiKey, ApiKeyPermission, ApiKeyTokenType, AssertEqual, AuthSession, AuthUser, BetterCMSErrorCode, BlockType, ButtonBlock, ButtonProps, ColumnsBlock, ColumnsProps, Content, ContentBlock, ContentEntry, ContentModel, ContentModelField, ContentPageResult, ContentResponse, DeepReadonly, DeliveryEntry, DeliveryList, DeliveryPage, Form, FormBlock, FormField, FormProps, FormSubmission, HeadingBlock, HeadingProps, ImageBlock, ImageProps, MediaAsset, Page, PageMetaJson, PaginatedResult, Perspective, Redirect, SignInInput, SignUpInput, SiteSeoDefaults, SpacerBlock, SpacerProps, TextBlock, TextProps, VideoBlock, VideoProps, Workspace, getBlockType, isBlock } from '@bettercms-ai/types';
|
|
4
|
+
export { ImageFit, ImageFormat, ImageSource, ImageUrlBuilder, ImageUrlOptions, imageUrl, default as imageUrlBuilder } from '@bettercms-ai/image-url';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Options for {@link BetterCMSDeliveryClient.listContent}.
|
|
8
|
+
*/
|
|
9
|
+
interface ListContentOptions {
|
|
10
|
+
/** Page number (1-based). Defaults to 1. */
|
|
11
|
+
page?: number;
|
|
12
|
+
/** Items per page. Defaults to 20. */
|
|
13
|
+
perPage?: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Fetches a paginated list of published content pages.
|
|
17
|
+
*/
|
|
18
|
+
declare function listContent(client: BetterCMSDeliveryClient, opts?: ListContentOptions): Promise<ContentPageResult["data"]>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Options for {@link BetterCMSDeliveryClient.listContentAll}.
|
|
22
|
+
*/
|
|
23
|
+
interface ListContentAllOptions extends ListContentOptions {
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* An async iterator that transparently auto-paginates through all content.
|
|
27
|
+
*
|
|
28
|
+
* Usage:
|
|
29
|
+
* ```ts
|
|
30
|
+
* for await (const item of client.listContentAll()) { ... }
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* Or with the `all()` convenience:
|
|
34
|
+
* ```ts
|
|
35
|
+
* const items = await client.listContentAll().all({ limit: 500 });
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
declare function listContentAll(client: BetterCMSDeliveryClient, opts?: ListContentAllOptions): {
|
|
39
|
+
/**
|
|
40
|
+
* Async iterator — yields Content items page by page.
|
|
41
|
+
*/
|
|
42
|
+
[Symbol.asyncIterator](): AsyncIterableIterator<Content>;
|
|
43
|
+
/**
|
|
44
|
+
* Convenience: collect all items into an array, optionally limited.
|
|
45
|
+
*/
|
|
46
|
+
all(options?: {
|
|
47
|
+
limit?: number;
|
|
48
|
+
}): Promise<Content[]>;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
declare class BetterCMSDeliveryClient {
|
|
52
|
+
readonly workspace: string;
|
|
53
|
+
readonly baseUrl: string;
|
|
54
|
+
private readonly apiKey?;
|
|
55
|
+
constructor(opts: {
|
|
56
|
+
workspace: string;
|
|
57
|
+
apiKey?: string;
|
|
58
|
+
baseUrl: string;
|
|
59
|
+
});
|
|
60
|
+
getContent(slug: string): Promise<_bettercms_ai_types.Content>;
|
|
61
|
+
listContent(opts?: Parameters<typeof listContent>[1]): Promise<_bettercms_ai_types.PaginatedResult<_bettercms_ai_types.Content>>;
|
|
62
|
+
listContentAll(opts?: Parameters<typeof listContentAll>[1]): {
|
|
63
|
+
[Symbol.asyncIterator](): AsyncIterableIterator<_bettercms_ai_types.Content>;
|
|
64
|
+
all(options?: {
|
|
65
|
+
limit?: number;
|
|
66
|
+
}): Promise<_bettercms_ai_types.Content[]>;
|
|
67
|
+
};
|
|
68
|
+
/** Build the full URL for a content path. */
|
|
69
|
+
url(path: string): string;
|
|
70
|
+
/** Build request headers, including auth if provided. */
|
|
71
|
+
headers(): Record<string, string>;
|
|
72
|
+
/**
|
|
73
|
+
* Generic fetch helper — wraps a request with network-error handling
|
|
74
|
+
* and throws a typed BetterCMSError on non-OK responses.
|
|
75
|
+
*/
|
|
76
|
+
fetchJSON<T>(url: string): Promise<T>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* SDK webhook methods — create, list, delete, test webhooks.
|
|
81
|
+
*/
|
|
82
|
+
/**
|
|
83
|
+
* Webhook event types supported by BetterCMS.
|
|
84
|
+
*/
|
|
85
|
+
type WebhookEvent = "content.publish" | "content.unpublish" | "content.created" | "content.updated" | "content.deleted" | "page.published" | "page.unpublished" | "page.created" | "page.updated" | "page.deleted" | "form.submitted" | "api_key.created" | "api_key.deleted";
|
|
86
|
+
/**
|
|
87
|
+
* Webhook record returned from the API (without secret).
|
|
88
|
+
*/
|
|
89
|
+
interface Webhook {
|
|
90
|
+
id: string;
|
|
91
|
+
projectId: string;
|
|
92
|
+
workspaceId: string;
|
|
93
|
+
name: string;
|
|
94
|
+
url: string;
|
|
95
|
+
events: WebhookEvent[];
|
|
96
|
+
isActive: boolean;
|
|
97
|
+
createdAt: string;
|
|
98
|
+
updatedAt: string;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Webhook with secret — returned only on create.
|
|
102
|
+
*/
|
|
103
|
+
interface WebhookWithSecret extends Webhook {
|
|
104
|
+
secret: string;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Input for creating a webhook.
|
|
108
|
+
*/
|
|
109
|
+
interface CreateWebhookInput {
|
|
110
|
+
name: string;
|
|
111
|
+
url: string;
|
|
112
|
+
events?: WebhookEvent[];
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Input for updating a webhook.
|
|
116
|
+
*/
|
|
117
|
+
interface UpdateWebhookInput {
|
|
118
|
+
name?: string;
|
|
119
|
+
url?: string;
|
|
120
|
+
events?: WebhookEvent[];
|
|
121
|
+
isActive?: boolean;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Paginated list response.
|
|
125
|
+
*/
|
|
126
|
+
interface WebhookListResponse {
|
|
127
|
+
data: Webhook[];
|
|
128
|
+
pagination: {
|
|
129
|
+
page: number;
|
|
130
|
+
limit: number;
|
|
131
|
+
total: number;
|
|
132
|
+
totalPages: number;
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* BetterCMSAdminClient — typed SDK client for admin CRUD operations.
|
|
138
|
+
*
|
|
139
|
+
* Extends BetterCMSDeliveryClient but overrides:
|
|
140
|
+
* - `headers()` — uses Bearer token auth instead of API key
|
|
141
|
+
* - `fetchJSON()` — adds retry on 429/503 and 10s timeout per request
|
|
142
|
+
*
|
|
143
|
+
* @see openspec/changes/flo-6-backend-sdk-admin/specs/sdk-admin-client/spec.md
|
|
144
|
+
*/
|
|
145
|
+
|
|
146
|
+
interface BetterCMSAdminOptions {
|
|
147
|
+
/** Base URL of the Admin API. Defaults to https://api.bettercms.ai/v1 */
|
|
148
|
+
baseUrl?: string;
|
|
149
|
+
/** BetterAuth session Bearer token. */
|
|
150
|
+
token: string;
|
|
151
|
+
/** Request timeout per attempt in ms. Defaults to 10000. */
|
|
152
|
+
timeout?: number;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Admin SDK client. Extends BetterCMSDeliveryClient so it reuses
|
|
156
|
+
* workspace, baseUrl, url() from the base. Overrides headers() and
|
|
157
|
+
* fetchJSON() for Bearer auth + retry/timeout.
|
|
158
|
+
*/
|
|
159
|
+
declare class BetterCMSAdminClient extends BetterCMSDeliveryClient {
|
|
160
|
+
readonly timeout: number;
|
|
161
|
+
private readonly _token;
|
|
162
|
+
constructor(options: BetterCMSAdminOptions);
|
|
163
|
+
headers(): Record<string, string>;
|
|
164
|
+
/**
|
|
165
|
+
* Admin fetchJSON with retry (429, 503) and per-request timeout (AbortController).
|
|
166
|
+
* Each retry gets a fresh timeout.
|
|
167
|
+
*/
|
|
168
|
+
fetchJSON<T>(url: string, init?: RequestInit): Promise<T>;
|
|
169
|
+
/** Build the admin API URL. */
|
|
170
|
+
url(path: string): string;
|
|
171
|
+
/** Webhook management methods. */
|
|
172
|
+
webhooks(): {
|
|
173
|
+
list: (options?: {
|
|
174
|
+
page?: number;
|
|
175
|
+
limit?: number;
|
|
176
|
+
}) => Promise<WebhookListResponse>;
|
|
177
|
+
create: (input: CreateWebhookInput) => Promise<WebhookWithSecret>;
|
|
178
|
+
get: (webhookId: string) => Promise<{
|
|
179
|
+
data: Webhook;
|
|
180
|
+
}>;
|
|
181
|
+
update: (webhookId: string, input: UpdateWebhookInput) => Promise<{
|
|
182
|
+
data: Webhook;
|
|
183
|
+
}>;
|
|
184
|
+
delete: (webhookId: string) => Promise<{
|
|
185
|
+
data: {
|
|
186
|
+
id: string;
|
|
187
|
+
};
|
|
188
|
+
}>;
|
|
189
|
+
test: (webhookId: string, event?: WebhookEvent) => Promise<{
|
|
190
|
+
data: {
|
|
191
|
+
delivered: boolean;
|
|
192
|
+
status?: number;
|
|
193
|
+
};
|
|
194
|
+
}>;
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Management API SDK methods — programmatic content-model + content-entry authoring.
|
|
200
|
+
*
|
|
201
|
+
* These call /api/v1/management/content/* (key-auth, content:manage scope). The MCP
|
|
202
|
+
* server wraps these. Mostly additive (create + update); delete is supported via
|
|
203
|
+
* soft-delete (reversible, audit-logged) so agents can clean up their own mistakes.
|
|
204
|
+
*/
|
|
205
|
+
|
|
206
|
+
/** Minimal client surface these methods need (satisfied by BetterCMSManagementClient). */
|
|
207
|
+
interface ManagementClient {
|
|
208
|
+
fetchJSON: <T>(url: string, init?: RequestInit) => Promise<T>;
|
|
209
|
+
url: (path: string) => string;
|
|
210
|
+
}
|
|
211
|
+
/** A content model row as returned by the Management API (the full DB record). */
|
|
212
|
+
interface ManagedContentModel {
|
|
213
|
+
id: string;
|
|
214
|
+
workspaceId: string;
|
|
215
|
+
projectId: string | null;
|
|
216
|
+
name: string;
|
|
217
|
+
slug: string;
|
|
218
|
+
description: string | null;
|
|
219
|
+
fields: ContentModelField[];
|
|
220
|
+
createdAt: string;
|
|
221
|
+
updatedAt: string;
|
|
222
|
+
deletedAt: string | null;
|
|
223
|
+
}
|
|
224
|
+
/** A content entry row as returned by the Management API (the full DB record). */
|
|
225
|
+
interface ManagedContentEntry {
|
|
226
|
+
id: string;
|
|
227
|
+
workspaceId: string;
|
|
228
|
+
projectId: string | null;
|
|
229
|
+
contentModelId: string;
|
|
230
|
+
pageId: string | null;
|
|
231
|
+
slug: string;
|
|
232
|
+
status: "draft" | "published";
|
|
233
|
+
data: Record<string, unknown>;
|
|
234
|
+
publishedAt: string | null;
|
|
235
|
+
optimisticVersion: number;
|
|
236
|
+
createdAt: string;
|
|
237
|
+
updatedAt: string;
|
|
238
|
+
deletedAt: string | null;
|
|
239
|
+
}
|
|
240
|
+
/** A page row as returned by the Management API. A page carries its own typed
|
|
241
|
+
* schema (`fields`) plus `pageType`, and is project-scoped from the key. */
|
|
242
|
+
interface ManagedPage {
|
|
243
|
+
id: string;
|
|
244
|
+
workspaceId: string;
|
|
245
|
+
projectId: string | null;
|
|
246
|
+
title: string;
|
|
247
|
+
slug: string;
|
|
248
|
+
metaTitle: string | null;
|
|
249
|
+
metaDescription: string | null;
|
|
250
|
+
status: "draft" | "published";
|
|
251
|
+
pageType: "singleton" | "dynamic" | null;
|
|
252
|
+
fields: ContentModelField[];
|
|
253
|
+
optimisticVersion: number;
|
|
254
|
+
createdAt: string;
|
|
255
|
+
updatedAt: string;
|
|
256
|
+
deletedAt: string | null;
|
|
257
|
+
}
|
|
258
|
+
interface CreateManagedPageInput {
|
|
259
|
+
title: string;
|
|
260
|
+
slug: string;
|
|
261
|
+
pageType?: "singleton" | "dynamic";
|
|
262
|
+
fields?: ContentModelField[];
|
|
263
|
+
metaTitle?: string;
|
|
264
|
+
metaDescription?: string;
|
|
265
|
+
status?: "draft" | "published";
|
|
266
|
+
}
|
|
267
|
+
/** Append fields to an existing page's schema (additive — see addPageFields). */
|
|
268
|
+
interface AddPageFieldsInput {
|
|
269
|
+
addFields: ContentModelField[];
|
|
270
|
+
}
|
|
271
|
+
interface CreateModelInput {
|
|
272
|
+
name: string;
|
|
273
|
+
slug: string;
|
|
274
|
+
description?: string;
|
|
275
|
+
fields?: ContentModelField[];
|
|
276
|
+
projectId?: string;
|
|
277
|
+
}
|
|
278
|
+
interface UpdateModelInput {
|
|
279
|
+
name?: string;
|
|
280
|
+
slug?: string;
|
|
281
|
+
description?: string;
|
|
282
|
+
fields?: ContentModelField[];
|
|
283
|
+
}
|
|
284
|
+
interface CreateEntryInput {
|
|
285
|
+
contentModelId: string;
|
|
286
|
+
slug?: string;
|
|
287
|
+
status?: "draft" | "published";
|
|
288
|
+
data?: Record<string, unknown>;
|
|
289
|
+
pageId?: string | null;
|
|
290
|
+
}
|
|
291
|
+
interface UpdateEntryInput {
|
|
292
|
+
slug?: string;
|
|
293
|
+
status?: "draft" | "published";
|
|
294
|
+
data?: Record<string, unknown>;
|
|
295
|
+
pageId?: string | null;
|
|
296
|
+
}
|
|
297
|
+
/** Set a page's field VALUES (the actual content). For a singleton, upserts its one entry. */
|
|
298
|
+
interface SetPageContentInput {
|
|
299
|
+
data: Record<string, unknown>;
|
|
300
|
+
status?: "draft" | "published";
|
|
301
|
+
}
|
|
302
|
+
/** Filter for listing content entries. */
|
|
303
|
+
interface ListEntriesFilter {
|
|
304
|
+
modelId?: string;
|
|
305
|
+
pageId?: string;
|
|
306
|
+
status?: "draft" | "published";
|
|
307
|
+
}
|
|
308
|
+
/** List content models (schemas) in the key's workspace. Excludes soft-deleted. */
|
|
309
|
+
declare function listModels(client: ManagementClient): Promise<ManagedContentModel[]>;
|
|
310
|
+
/** Get a single content model by id. Needed for additive read-modify-write (e.g. add_field). */
|
|
311
|
+
declare function getModel(client: ManagementClient, id: string): Promise<ManagedContentModel>;
|
|
312
|
+
/** List pages in the key's project (or workspace-level if the key is not project-scoped). */
|
|
313
|
+
declare function listManagedPages(client: ManagementClient): Promise<ManagedPage[]>;
|
|
314
|
+
/** Create a page (the page-first schema: title/slug/pageType/fields). Project-scoped from the key.
|
|
315
|
+
* Supports both `singleton` and `dynamic` pageType. */
|
|
316
|
+
declare function createManagedPage(client: ManagementClient, input: CreateManagedPageInput): Promise<ManagedPage>;
|
|
317
|
+
/**
|
|
318
|
+
* Append fields to an existing page's schema (additive read-append). The API
|
|
319
|
+
* rejects keys that already exist, so this never overwrites or retypes a field.
|
|
320
|
+
* Project-scoped from the key. Returns the updated page.
|
|
321
|
+
*/
|
|
322
|
+
declare function addPageFields(client: ManagementClient, id: string, input: AddPageFieldsInput): Promise<ManagedPage>;
|
|
323
|
+
/** Get a single page by id INCLUDING its field schema + pageType. */
|
|
324
|
+
declare function getManagedPage(client: ManagementClient, id: string): Promise<ManagedPage>;
|
|
325
|
+
/**
|
|
326
|
+
* Set a page's field VALUES. For a SINGLETON page this creates or updates its one
|
|
327
|
+
* page-backed content entry (idempotent); for a dynamic page it upserts the entry
|
|
328
|
+
* keyed by the page. Returns the resulting entry.
|
|
329
|
+
*/
|
|
330
|
+
declare function setPageContent(client: ManagementClient, id: string, input: SetPageContentInput): Promise<ManagedContentEntry>;
|
|
331
|
+
/** Append fields to a content model's schema (additive — rejects existing keys). */
|
|
332
|
+
declare function addModelFields(client: ManagementClient, id: string, input: AddPageFieldsInput): Promise<ManagedContentModel>;
|
|
333
|
+
/** List content entries (incl. drafts), optionally filtered by model/page/status. */
|
|
334
|
+
declare function listEntries(client: ManagementClient, filter?: ListEntriesFilter): Promise<ManagedContentEntry[]>;
|
|
335
|
+
/** Get a single content entry by id INCLUDING its data (field values), incl. drafts. */
|
|
336
|
+
declare function getEntry(client: ManagementClient, id: string): Promise<ManagedContentEntry>;
|
|
337
|
+
/** Create a content model (schema). */
|
|
338
|
+
declare function createModel(client: ManagementClient, input: CreateModelInput): Promise<ManagedContentModel>;
|
|
339
|
+
/** Update a content model (additive — fields/name/slug/description). */
|
|
340
|
+
declare function updateModel(client: ManagementClient, id: string, input: UpdateModelInput): Promise<ManagedContentModel>;
|
|
341
|
+
/** Create a content entry under a model. */
|
|
342
|
+
declare function createEntry(client: ManagementClient, input: CreateEntryInput): Promise<ManagedContentEntry>;
|
|
343
|
+
/**
|
|
344
|
+
* Update a content entry. If `opts.ifMatch` (current optimisticVersion) is supplied,
|
|
345
|
+
* the update is optimistic-locked; omit it for programmatic last-write-wins.
|
|
346
|
+
*/
|
|
347
|
+
declare function updateEntry(client: ManagementClient, id: string, input: UpdateEntryInput, opts?: {
|
|
348
|
+
ifMatch?: number;
|
|
349
|
+
}): Promise<ManagedContentEntry>;
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Management API SDK method — media upload (P2).
|
|
353
|
+
*
|
|
354
|
+
* uploadAsset() resolves bytes from a local file path OR a remote URL, then POSTs
|
|
355
|
+
* them to /api/v1/management/media (media:write scope) and returns the stored
|
|
356
|
+
* asset's stable CDN URL. The MCP `upload_asset` tool wraps this so an AI agent
|
|
357
|
+
* can put repo/remote images into the Media Library and reference the URL in an
|
|
358
|
+
* entry's image field.
|
|
359
|
+
*
|
|
360
|
+
* Local-file reads use a lazy `node:` import so browser bundles that never call
|
|
361
|
+
* the localPath branch don't pull Node built-ins.
|
|
362
|
+
*/
|
|
363
|
+
|
|
364
|
+
interface UploadAssetInput {
|
|
365
|
+
/** Path to a local file (e.g. a repo image). Mutually exclusive with `url`. */
|
|
366
|
+
localPath?: string;
|
|
367
|
+
/** Remote image URL to ingest. Mutually exclusive with `localPath`. */
|
|
368
|
+
url?: string;
|
|
369
|
+
/** Override the stored filename (defaults to the source basename). */
|
|
370
|
+
filename?: string;
|
|
371
|
+
altText?: string;
|
|
372
|
+
caption?: string;
|
|
373
|
+
/** Target Media Library folder id (defaults to project root). */
|
|
374
|
+
folderId?: string;
|
|
375
|
+
}
|
|
376
|
+
interface UploadedAsset {
|
|
377
|
+
id: string;
|
|
378
|
+
url: string;
|
|
379
|
+
key: string;
|
|
380
|
+
filename: string;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/** Semantic error codes used by the SDK. */
|
|
384
|
+
declare const ErrorCodes: {
|
|
385
|
+
readonly CONTENT_NOT_FOUND: "CONTENT_NOT_FOUND";
|
|
386
|
+
readonly UNAUTHORIZED: "UNAUTHORIZED";
|
|
387
|
+
readonly FORBIDDEN: "FORBIDDEN";
|
|
388
|
+
readonly RATE_LIMITED: "RATE_LIMITED";
|
|
389
|
+
readonly INTERNAL_ERROR: "INTERNAL_ERROR";
|
|
390
|
+
readonly VALIDATION_ERROR: "VALIDATION_ERROR";
|
|
391
|
+
readonly NETWORK_ERROR: "NETWORK_ERROR";
|
|
392
|
+
readonly ADMIN_NOT_IMPLEMENTED: "ADMIN_NOT_IMPLEMENTED";
|
|
393
|
+
};
|
|
394
|
+
/**
|
|
395
|
+
* BetterCMSError — normalized SDK error with a semantic code.
|
|
396
|
+
*/
|
|
397
|
+
declare class BetterCMSError extends Error {
|
|
398
|
+
readonly status: number;
|
|
399
|
+
readonly code: BetterCMSErrorCode;
|
|
400
|
+
/**
|
|
401
|
+
* Raw machine-readable `code` from the response body (e.g. "PROJECT_DELETED"),
|
|
402
|
+
* when the API supplies one. Distinct from `code` (which is derived from the
|
|
403
|
+
* HTTP status), this lets callers branch on a specific server condition that
|
|
404
|
+
* shares a status with others (e.g. 409 slug-conflict vs 409 project-deleted).
|
|
405
|
+
*/
|
|
406
|
+
readonly bodyCode?: string;
|
|
407
|
+
constructor(message: string, status: number, code: BetterCMSErrorCode, bodyCode?: string);
|
|
408
|
+
toJSON(): {
|
|
409
|
+
name: string;
|
|
410
|
+
message: string;
|
|
411
|
+
status: number;
|
|
412
|
+
code: BetterCMSErrorCode;
|
|
413
|
+
bodyCode: string | undefined;
|
|
414
|
+
};
|
|
415
|
+
/**
|
|
416
|
+
* Factory — creates a BetterCMSError from a failed fetch Response. Reads the
|
|
417
|
+
* body's `message` (or `error`) for the human message and `code` for a
|
|
418
|
+
* machine-readable condition the SDK surfaces as `bodyCode`.
|
|
419
|
+
*/
|
|
420
|
+
static from(res: Response): Promise<BetterCMSError>;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Public forms — the headless integration surface for sites built on or imported
|
|
425
|
+
* into BetterCMS (the "render it yourself" model, like Sanity's delivery API).
|
|
426
|
+
*
|
|
427
|
+
* Form definitions ship in the build-time content snapshot (`bcms-content.json` →
|
|
428
|
+
* `forms` / `turnstileSiteKey`) and from `GET /api/v1/delivery/:workspace/forms`.
|
|
429
|
+
* A site renders them with its own components (see `@bettercms-ai/next`'s <BcmsForm>)
|
|
430
|
+
* and posts here. No API key — the endpoint is Turnstile-gated (Webflow model).
|
|
431
|
+
*
|
|
432
|
+
* This module is the single source of truth for the delivered form shape + the
|
|
433
|
+
* pure logic (conditional visibility, initial values) the renderers share, so the
|
|
434
|
+
* vanilla embed and the React component never drift.
|
|
435
|
+
*/
|
|
436
|
+
|
|
437
|
+
/** Field types the delivery API serializes (superset of the admin builder). */
|
|
438
|
+
type DeliveryFormFieldType = "text" | "email" | "phone" | "url" | "number" | "date" | "textarea" | "select" | "checkbox" | "consent" | "hidden";
|
|
439
|
+
interface DeliveryFormField {
|
|
440
|
+
key: string;
|
|
441
|
+
label: string;
|
|
442
|
+
type: DeliveryFormFieldType;
|
|
443
|
+
placeholder?: string;
|
|
444
|
+
required?: boolean;
|
|
445
|
+
options?: string[];
|
|
446
|
+
defaultValue?: string;
|
|
447
|
+
/** Conditional visibility — render this field only when another field equals `equals`. */
|
|
448
|
+
showIf?: {
|
|
449
|
+
field: string;
|
|
450
|
+
equals: string;
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
/** A form as delivered to a published/imported site. Mirrors the delivery `/forms` payload. */
|
|
454
|
+
interface DeliveryForm {
|
|
455
|
+
id: string;
|
|
456
|
+
name: string;
|
|
457
|
+
fields: DeliveryFormField[];
|
|
458
|
+
submitLabel?: string;
|
|
459
|
+
successMessage?: string;
|
|
460
|
+
redirectUrl?: string;
|
|
461
|
+
turnstileEnabled?: boolean;
|
|
462
|
+
honeypotField?: string | null;
|
|
463
|
+
}
|
|
464
|
+
type FormValue = string | boolean;
|
|
465
|
+
type FormValues = Record<string, FormValue>;
|
|
466
|
+
/**
|
|
467
|
+
* Conditional visibility. A `showIf` field is shown only when its trigger field's
|
|
468
|
+
* current value equals the target; non-conditional fields are always shown. The
|
|
469
|
+
* renderer must skip hidden fields when collecting/validating so a hidden required
|
|
470
|
+
* field never blocks submit.
|
|
471
|
+
*/
|
|
472
|
+
declare function shouldShowField(field: DeliveryFormField, values: FormValues): boolean;
|
|
473
|
+
/**
|
|
474
|
+
* Build the initial value map: checkbox/consent default to false, every other field
|
|
475
|
+
* to its `defaultValue`, with `prefill` (e.g. URL query params) taking precedence.
|
|
476
|
+
*/
|
|
477
|
+
declare function formInitialValues(form: DeliveryForm, prefill?: Record<string, string>): FormValues;
|
|
478
|
+
interface SubmitFormOptions {
|
|
479
|
+
formId: string;
|
|
480
|
+
data: FormValues;
|
|
481
|
+
/** Cloudflare Turnstile token; required when the form has `turnstileEnabled`. */
|
|
482
|
+
turnstileToken?: string;
|
|
483
|
+
/** API origin. Defaults to https://api.bettercms.ai. */
|
|
484
|
+
baseUrl?: string;
|
|
485
|
+
/** Injectable fetch for tests / non-browser runtimes. */
|
|
486
|
+
fetchImpl?: typeof fetch;
|
|
487
|
+
}
|
|
488
|
+
interface SubmitFormResult {
|
|
489
|
+
id: string;
|
|
490
|
+
}
|
|
491
|
+
/** A submit failure that carries per-field validation messages from the API (HTTP 400). */
|
|
492
|
+
type FormSubmitError = BetterCMSError & {
|
|
493
|
+
fieldErrors?: Record<string, string>;
|
|
494
|
+
};
|
|
495
|
+
/**
|
|
496
|
+
* Submit a public form. Resolves with the new submission id, or throws a
|
|
497
|
+
* BetterCMSError — field-level validation errors are attached as `.fieldErrors`.
|
|
498
|
+
*/
|
|
499
|
+
declare function submitForm(opts: SubmitFormOptions): Promise<SubmitFormResult>;
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Management API SDK methods — read-only form discovery.
|
|
503
|
+
*
|
|
504
|
+
* These call /api/v1/management/forms (key-auth, content:manage scope). The MCP
|
|
505
|
+
* server wraps these so an agent can list/read the forms built in the dashboard
|
|
506
|
+
* and then embed the matching `<BcmsForm>` into the user's site code. Read-only by
|
|
507
|
+
* design — form authoring + Google Sheets "integrations" stay dashboard-only.
|
|
508
|
+
*/
|
|
509
|
+
|
|
510
|
+
/** A form as returned by the Management API — the public, render-only projection. */
|
|
511
|
+
interface ManagedForm {
|
|
512
|
+
id: string;
|
|
513
|
+
projectId: string | null;
|
|
514
|
+
name: string;
|
|
515
|
+
description: string | null;
|
|
516
|
+
fields: DeliveryFormField[];
|
|
517
|
+
submitLabel?: string | null;
|
|
518
|
+
successMessage?: string | null;
|
|
519
|
+
redirectUrl?: string | null;
|
|
520
|
+
turnstileEnabled?: boolean | null;
|
|
521
|
+
honeypotField?: string | null;
|
|
522
|
+
}
|
|
523
|
+
/** Create/update input — mirrors the Management API form schema (fields are passed through). */
|
|
524
|
+
interface ManagedFormInput {
|
|
525
|
+
name?: string;
|
|
526
|
+
description?: string;
|
|
527
|
+
fields?: DeliveryFormField[];
|
|
528
|
+
submitLabel?: string;
|
|
529
|
+
successMessage?: string;
|
|
530
|
+
redirectUrl?: string;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Management API SDK methods — reusable components (discover + author).
|
|
535
|
+
*
|
|
536
|
+
* Call /api/v1/management/components (key-auth, content:manage). The MCP server wraps
|
|
537
|
+
* these so an agent can list/read components and author new ones (blockJson + props).
|
|
538
|
+
*/
|
|
539
|
+
|
|
540
|
+
/** A component as returned by the Management API (render + author shape). */
|
|
541
|
+
interface ManagedComponent {
|
|
542
|
+
id: string;
|
|
543
|
+
projectId: string | null;
|
|
544
|
+
name: string;
|
|
545
|
+
slug: string;
|
|
546
|
+
category: string;
|
|
547
|
+
description: string | null;
|
|
548
|
+
blockJson: unknown[];
|
|
549
|
+
props: unknown[];
|
|
550
|
+
thumbnail?: string | null;
|
|
551
|
+
}
|
|
552
|
+
/** Create/update input. `blockJson` is the ContentBlock tree; `props` the override allowlist. */
|
|
553
|
+
interface ManagedComponentInput {
|
|
554
|
+
name?: string;
|
|
555
|
+
slug?: string;
|
|
556
|
+
category?: string;
|
|
557
|
+
description?: string;
|
|
558
|
+
blockJson?: unknown[];
|
|
559
|
+
props?: unknown[];
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
interface BetterCMSManagementOptions {
|
|
563
|
+
/** Base URL of the Management API. Defaults to https://api.bettercms.ai/v1 */
|
|
564
|
+
baseUrl?: string;
|
|
565
|
+
/** A content:manage scoped API key. */
|
|
566
|
+
apiKey: string;
|
|
567
|
+
/** Request timeout per attempt in ms. Defaults to 10000. */
|
|
568
|
+
timeout?: number;
|
|
569
|
+
}
|
|
570
|
+
declare class BetterCMSManagementClient extends BetterCMSDeliveryClient {
|
|
571
|
+
readonly timeout: number;
|
|
572
|
+
private readonly _apiKey;
|
|
573
|
+
constructor(options: BetterCMSManagementOptions);
|
|
574
|
+
headers(): Record<string, string>;
|
|
575
|
+
/** Management API URL: baseUrl + path (path is workspace-scoped via the key). */
|
|
576
|
+
url(path: string): string;
|
|
577
|
+
/** fetchJSON with retry (429, 503) + per-request timeout (mirrors the admin client). */
|
|
578
|
+
fetchJSON<T>(url: string, init?: RequestInit): Promise<T>;
|
|
579
|
+
listModels(): Promise<ManagedContentModel[]>;
|
|
580
|
+
getModel(id: string): Promise<ManagedContentModel>;
|
|
581
|
+
createModel(input: CreateModelInput): Promise<ManagedContentModel>;
|
|
582
|
+
updateModel(id: string, input: UpdateModelInput): Promise<ManagedContentModel>;
|
|
583
|
+
listPages(): Promise<ManagedPage[]>;
|
|
584
|
+
/** Get one page by id INCLUDING its field schema + pageType. */
|
|
585
|
+
getPage(id: string): Promise<ManagedPage>;
|
|
586
|
+
createPage(input: CreateManagedPageInput): Promise<ManagedPage>;
|
|
587
|
+
/** Append fields to an existing page (additive — the API rejects existing keys). */
|
|
588
|
+
addPageFields(id: string, input: AddPageFieldsInput): Promise<ManagedPage>;
|
|
589
|
+
/** Set a page's field VALUES — singleton pages get/update their one entry. */
|
|
590
|
+
setPageContent(id: string, input: SetPageContentInput): Promise<ManagedContentEntry>;
|
|
591
|
+
/** Append fields to a content model (additive — rejects existing keys). */
|
|
592
|
+
addModelFields(id: string, input: AddPageFieldsInput): Promise<ManagedContentModel>;
|
|
593
|
+
createEntry(input: CreateEntryInput): Promise<ManagedContentEntry>;
|
|
594
|
+
updateEntry(id: string, input: UpdateEntryInput, opts?: {
|
|
595
|
+
ifMatch?: number;
|
|
596
|
+
}): Promise<ManagedContentEntry>;
|
|
597
|
+
/** Soft-delete a page and its content entries. */
|
|
598
|
+
deletePage(id: string): Promise<{
|
|
599
|
+
id: string;
|
|
600
|
+
}>;
|
|
601
|
+
/** Soft-delete a single content entry. */
|
|
602
|
+
deleteEntry(id: string): Promise<{
|
|
603
|
+
id: string;
|
|
604
|
+
}>;
|
|
605
|
+
/** Soft-delete a content model and its entries. */
|
|
606
|
+
deleteModel(id: string): Promise<{
|
|
607
|
+
id: string;
|
|
608
|
+
}>;
|
|
609
|
+
/** List content entries (incl. drafts), optionally filtered by model/page/status. */
|
|
610
|
+
listEntries(filter?: ListEntriesFilter): Promise<ManagedContentEntry[]>;
|
|
611
|
+
/** Get one content entry by id INCLUDING its data (incl. drafts). */
|
|
612
|
+
getEntry(id: string): Promise<ManagedContentEntry>;
|
|
613
|
+
uploadAsset(input: UploadAssetInput): Promise<UploadedAsset>;
|
|
614
|
+
/** List forms in the key's project (or workspace-level). */
|
|
615
|
+
listForms(): Promise<ManagedForm[]>;
|
|
616
|
+
/** Get one form by id INCLUDING its field schema + settings. */
|
|
617
|
+
getForm(id: string): Promise<ManagedForm>;
|
|
618
|
+
/** Create a form in the key's project. */
|
|
619
|
+
createForm(input: ManagedFormInput): Promise<ManagedForm>;
|
|
620
|
+
/** Update a form by id (passing `fields` replaces the array). */
|
|
621
|
+
updateForm(id: string, input: ManagedFormInput): Promise<ManagedForm>;
|
|
622
|
+
listComponents(): Promise<ManagedComponent[]>;
|
|
623
|
+
getComponent(id: string): Promise<ManagedComponent>;
|
|
624
|
+
createComponent(input: ManagedComponentInput): Promise<ManagedComponent>;
|
|
625
|
+
updateComponent(id: string, input: ManagedComponentInput): Promise<ManagedComponent>;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
interface AuthResult {
|
|
629
|
+
token: string;
|
|
630
|
+
user: AuthUser;
|
|
631
|
+
session: AuthSession;
|
|
632
|
+
admin: BetterCMSAdminClient;
|
|
633
|
+
signOut(): Promise<void>;
|
|
634
|
+
}
|
|
635
|
+
/** Sign in with email + password. Returns a session ready for API calls. */
|
|
636
|
+
declare function signIn(input: SignInInput): Promise<AuthResult>;
|
|
637
|
+
/** Sign up with email + password + name. Returns a session ready for API calls. */
|
|
638
|
+
declare function signUp(input: SignUpInput): Promise<AuthResult>;
|
|
639
|
+
/**
|
|
640
|
+
* Initiate Google OAuth sign-in (browser flow) or verify a Google ID token (programmatic).
|
|
641
|
+
*
|
|
642
|
+
* Browser flow: Returns { url } — redirect the user to this URL.
|
|
643
|
+
* Programmatic: Pass { idToken: { token: "<google-id-token>" } } — returns AuthResult directly.
|
|
644
|
+
*/
|
|
645
|
+
declare function signInWithGoogle(options?: {
|
|
646
|
+
idToken?: {
|
|
647
|
+
token: string;
|
|
648
|
+
nonce?: string;
|
|
649
|
+
};
|
|
650
|
+
callbackURL?: string;
|
|
651
|
+
}): Promise<AuthResult & {
|
|
652
|
+
url?: string;
|
|
653
|
+
redirect?: boolean;
|
|
654
|
+
}>;
|
|
655
|
+
/**
|
|
656
|
+
* Initiate GitHub OAuth sign-in (browser flow) or verify a GitHub OAuth token (programmatic).
|
|
657
|
+
*
|
|
658
|
+
* Browser flow: Returns { url } — redirect the user to this URL.
|
|
659
|
+
* Programmatic: Pass { idToken: { token: "<github-access-token>" } } — returns AuthResult directly.
|
|
660
|
+
*/
|
|
661
|
+
declare function signInWithGithub(options?: {
|
|
662
|
+
idToken?: {
|
|
663
|
+
token: string;
|
|
664
|
+
nonce?: string;
|
|
665
|
+
};
|
|
666
|
+
callbackURL?: string;
|
|
667
|
+
}): Promise<AuthResult & {
|
|
668
|
+
url?: string;
|
|
669
|
+
redirect?: boolean;
|
|
670
|
+
}>;
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Options for {@link BetterCMS.site}.
|
|
674
|
+
*/
|
|
675
|
+
interface BetterCMSSiteOptions {
|
|
676
|
+
/** The workspace slug (e.g. "rahul-project-one"). */
|
|
677
|
+
workspace: string;
|
|
678
|
+
/** Optional API key for authenticated requests. */
|
|
679
|
+
apiKey?: string;
|
|
680
|
+
/** Base URL of the Delivery API. Defaults to https://api.bettercms.ai/v1. */
|
|
681
|
+
baseUrl?: string;
|
|
682
|
+
}
|
|
683
|
+
/**
|
|
684
|
+
* BetterCMS factory — returns a typed client for the Delivery API.
|
|
685
|
+
*/
|
|
686
|
+
declare const BetterCMS: {
|
|
687
|
+
/**
|
|
688
|
+
* Returns a client for fetching published content.
|
|
689
|
+
* `apiKey` is optional — public content works without one.
|
|
690
|
+
*/
|
|
691
|
+
site(options: BetterCMSSiteOptions): BetterCMSDeliveryClient;
|
|
692
|
+
/**
|
|
693
|
+
* Returns a typed client for admin CRUD operations.
|
|
694
|
+
* Requires a BetterAuth session Bearer token.
|
|
695
|
+
*/
|
|
696
|
+
admin(options: BetterCMSAdminOptions): BetterCMSAdminClient;
|
|
697
|
+
/**
|
|
698
|
+
* Returns a typed client for the Management API (programmatic schema + content
|
|
699
|
+
* authoring). Requires a content:manage scoped API key.
|
|
700
|
+
*/
|
|
701
|
+
management(options: BetterCMSManagementOptions): BetterCMSManagementClient;
|
|
702
|
+
/**
|
|
703
|
+
* Sign in with email + password. Returns a session ready for API calls.
|
|
704
|
+
*/
|
|
705
|
+
auth(options: SignInInput): Promise<AuthResult>;
|
|
706
|
+
/**
|
|
707
|
+
* Sign up with email + password + name. Returns a session ready for API calls.
|
|
708
|
+
*/
|
|
709
|
+
signUp(options: SignUpInput): Promise<AuthResult>;
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* createClient — framework-agnostic typed read client for the Delivery API.
|
|
714
|
+
*
|
|
715
|
+
* Shared by @bettercms-ai/astro and @bettercms-ai/next so the fetch/typing/retry
|
|
716
|
+
* logic lives in one place. Read-only; for schema authoring use the
|
|
717
|
+
* Management client, for sessions use the Admin client.
|
|
718
|
+
*
|
|
719
|
+
* const client = createClient({ apiUrl, workspace, apiKey });
|
|
720
|
+
* await client.getPage("home");
|
|
721
|
+
* await client.listEntries({ model: "post", perPage: 50 });
|
|
722
|
+
*/
|
|
723
|
+
|
|
724
|
+
interface CreateClientOptions {
|
|
725
|
+
/** Backend base, e.g. `https://api.bettercms.ai` (no trailing slash needed). */
|
|
726
|
+
apiUrl: string;
|
|
727
|
+
/** Workspace slug. */
|
|
728
|
+
workspace: string;
|
|
729
|
+
/** Delivery API key. Use a `content:read:draft` key for drafts. */
|
|
730
|
+
apiKey?: string;
|
|
731
|
+
/** Optional project scope for forms. */
|
|
732
|
+
projectId?: string;
|
|
733
|
+
/** `published` (default) or `drafts`. */
|
|
734
|
+
perspective?: Perspective;
|
|
735
|
+
/**
|
|
736
|
+
* Preview-token JWT for token-based single-document draft reads via the
|
|
737
|
+
* `/preview/:slug` endpoint. Only used when `perspective === "drafts"`.
|
|
738
|
+
*/
|
|
739
|
+
previewToken?: string;
|
|
740
|
+
/** Embed visual-editing stega provenance in draft reads (preview lane only). */
|
|
741
|
+
stega?: boolean;
|
|
742
|
+
/** Per-attempt timeout in ms. Defaults to 10000. */
|
|
743
|
+
timeout?: number;
|
|
744
|
+
}
|
|
745
|
+
interface ListEntriesOptions {
|
|
746
|
+
/** Filter by content-model slug. */
|
|
747
|
+
model?: string;
|
|
748
|
+
page?: number;
|
|
749
|
+
perPage?: number;
|
|
750
|
+
/** Reference hydration depth (lists cap at 1). */
|
|
751
|
+
depth?: 0 | 1;
|
|
752
|
+
/** Projection — field names to return. */
|
|
753
|
+
select?: string[];
|
|
754
|
+
}
|
|
755
|
+
interface GetEntryOptions {
|
|
756
|
+
depth?: 0 | 1 | 2;
|
|
757
|
+
select?: string[];
|
|
758
|
+
}
|
|
759
|
+
interface ListPagesOptions {
|
|
760
|
+
page?: number;
|
|
761
|
+
perPage?: number;
|
|
762
|
+
}
|
|
763
|
+
interface BetterCMSReadClient {
|
|
764
|
+
readonly perspective: Perspective;
|
|
765
|
+
getEntry<T = Record<string, unknown>>(slug: string, opts?: GetEntryOptions): Promise<DeliveryEntry<T>>;
|
|
766
|
+
listEntries<T = Record<string, unknown>>(opts?: ListEntriesOptions): Promise<DeliveryList<DeliveryEntry<T>>>;
|
|
767
|
+
getPage(slug: string): Promise<DeliveryPage | null>;
|
|
768
|
+
listPages(opts?: ListPagesOptions): Promise<DeliveryList<DeliveryPage>>;
|
|
769
|
+
listForms(opts?: {
|
|
770
|
+
projectId?: string;
|
|
771
|
+
}): Promise<{
|
|
772
|
+
items: DeliveryForm[];
|
|
773
|
+
turnstileSiteKey: string | null;
|
|
774
|
+
}>;
|
|
775
|
+
/** Build a deterministic Next/cache tag for a resource. */
|
|
776
|
+
tag(resource: "entry" | "page" | "forms", id?: string): string;
|
|
777
|
+
}
|
|
778
|
+
declare function createClient(options: CreateClientOptions): BetterCMSReadClient;
|
|
779
|
+
|
|
780
|
+
/** Provenance describing where a string came from, for editor deep-linking. */
|
|
781
|
+
interface StegaPayload {
|
|
782
|
+
/** Workspace slug. */
|
|
783
|
+
ws: string;
|
|
784
|
+
type: "entry" | "page";
|
|
785
|
+
/** Entry/page id. */
|
|
786
|
+
id: string;
|
|
787
|
+
slug: string;
|
|
788
|
+
/** Field key (or block id) the value belongs to. */
|
|
789
|
+
field: string;
|
|
790
|
+
}
|
|
791
|
+
/** Append an invisible provenance payload to a string. */
|
|
792
|
+
declare function encodeStega(clean: string, payload: StegaPayload): string;
|
|
793
|
+
/** Extract `{ clean, payload }` from a stega-encoded string, or null if absent. */
|
|
794
|
+
declare function decodeStega(value: string): {
|
|
795
|
+
clean: string;
|
|
796
|
+
payload: StegaPayload;
|
|
797
|
+
} | null;
|
|
798
|
+
/** Remove any stega payload, returning the clean string. */
|
|
799
|
+
declare function stripStega(value: string): string;
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* resolveSeo — framework-agnostic page-over-site SEO merge for headless consumers.
|
|
803
|
+
*
|
|
804
|
+
* The single source of truth for "per-page meta layered over project defaults",
|
|
805
|
+
* mirroring the server renderer's `mergeSeoMeta` so `*.bettercms.site` and headless
|
|
806
|
+
* sites (@bettercms-ai/next, @bettercms-ai/astro) emit identical <head> SEO. Pure: empty
|
|
807
|
+
* inputs collapse to empty strings ("omit the tag"), never fabricated values.
|
|
808
|
+
*
|
|
809
|
+
* const seo = resolveSeo(page, siteDefaults);
|
|
810
|
+
* // → { title, description, canonical, og, twitter, jsonLd }
|
|
811
|
+
*
|
|
812
|
+
* Next consumers map this to a `Metadata` via `buildMetadata` (@bettercms-ai/next);
|
|
813
|
+
* Astro consumers read the fields straight into `<head>`.
|
|
814
|
+
*/
|
|
815
|
+
|
|
816
|
+
/** Minimal page shape `resolveSeo` reads — a `DeliveryPage`/`BetterCMSPage` satisfies it. */
|
|
817
|
+
interface SeoInput {
|
|
818
|
+
title: string;
|
|
819
|
+
metaTitle?: string | null;
|
|
820
|
+
metaDescription?: string | null;
|
|
821
|
+
metaJson?: PageMetaJson | null;
|
|
822
|
+
}
|
|
823
|
+
/** Fully-resolved SEO values, page-over-site. Empty string ⇒ omit that tag. */
|
|
824
|
+
interface ResolvedSeo {
|
|
825
|
+
title: string;
|
|
826
|
+
description: string;
|
|
827
|
+
/** Canonical URL — page's own `canonical`, else "". Consumers may supply their own base. */
|
|
828
|
+
canonical: string;
|
|
829
|
+
og: {
|
|
830
|
+
title: string;
|
|
831
|
+
description: string;
|
|
832
|
+
type: string;
|
|
833
|
+
image: string;
|
|
834
|
+
url: string;
|
|
835
|
+
};
|
|
836
|
+
twitter: {
|
|
837
|
+
card: string;
|
|
838
|
+
title: string;
|
|
839
|
+
description: string;
|
|
840
|
+
image: string;
|
|
841
|
+
site: string;
|
|
842
|
+
};
|
|
843
|
+
/** JSON-LD nodes (site schema first, then the page's), already flattened + de-empted. */
|
|
844
|
+
jsonLd: Array<Record<string, unknown>>;
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
847
|
+
* Merge a page's own SEO over the site defaults. Identical precedence to the server's
|
|
848
|
+
* `mergeSeoMeta`: page meta wins, then site defaults, then sensible fallbacks.
|
|
849
|
+
*/
|
|
850
|
+
declare function resolveSeo(page: SeoInput, defaults?: SiteSeoDefaults): ResolvedSeo;
|
|
851
|
+
|
|
852
|
+
/**
|
|
853
|
+
* Public site search — the headless surface for the visitor-facing search box.
|
|
854
|
+
*
|
|
855
|
+
* Queries `GET /api/v1/delivery/search?project=…&q=…`, the same key-less public
|
|
856
|
+
* endpoint the embeddable widget uses. Powers <BcmsSearch> in @bettercms-ai/next
|
|
857
|
+
* and @bettercms-ai/astro, so the component and any custom UI never drift.
|
|
858
|
+
*
|
|
859
|
+
* `snippet` is HTML-safe: the server escapes the field content and keeps only the
|
|
860
|
+
* <mark> highlight wrappers, so it can be rendered with dangerouslySetInnerHTML.
|
|
861
|
+
*/
|
|
862
|
+
interface SearchHit {
|
|
863
|
+
title: string;
|
|
864
|
+
slug: string;
|
|
865
|
+
type: "page" | "entry";
|
|
866
|
+
/** HTML-safe snippet with <mark> highlights around matched terms. */
|
|
867
|
+
snippet: string;
|
|
868
|
+
/** Site-relative URL, e.g. "/about". */
|
|
869
|
+
url: string;
|
|
870
|
+
}
|
|
871
|
+
interface SearchOptions {
|
|
872
|
+
/** The project id (from the dashboard's Site Search settings). */
|
|
873
|
+
project: string;
|
|
874
|
+
/** The query string. Calls with fewer than 1 char resolve to []. */
|
|
875
|
+
q: string;
|
|
876
|
+
/** Max hits, 1–20 (default 8). */
|
|
877
|
+
limit?: number;
|
|
878
|
+
/** API origin. Defaults to https://api.bettercms.ai. */
|
|
879
|
+
baseUrl?: string;
|
|
880
|
+
/** Injectable fetch for tests / non-browser runtimes. */
|
|
881
|
+
fetchImpl?: typeof fetch;
|
|
882
|
+
/** AbortSignal so callers can cancel a stale in-flight search. */
|
|
883
|
+
signal?: AbortSignal;
|
|
884
|
+
}
|
|
885
|
+
/** Search a published site. Resolves with hits, or [] on an empty query / network error. */
|
|
886
|
+
declare function search(opts: SearchOptions): Promise<SearchHit[]>;
|
|
887
|
+
|
|
888
|
+
/**
|
|
889
|
+
* Admin pages methods.
|
|
890
|
+
* @see openspec/changes/flo-6-backend-sdk-admin/specs/sdk-admin-client/spec.md
|
|
891
|
+
*/
|
|
892
|
+
|
|
893
|
+
interface PageListOptions {
|
|
894
|
+
page?: number;
|
|
895
|
+
limit?: number;
|
|
896
|
+
}
|
|
897
|
+
interface CreatePageInput {
|
|
898
|
+
title: string;
|
|
899
|
+
slug: string;
|
|
900
|
+
metaTitle?: string;
|
|
901
|
+
metaDescription?: string;
|
|
902
|
+
status?: "draft" | "published";
|
|
903
|
+
}
|
|
904
|
+
interface UpdatePageInput extends Partial<CreatePageInput> {
|
|
905
|
+
}
|
|
906
|
+
declare function listPages(client: BetterCMSAdminClient, opts?: PageListOptions): Promise<Page[]>;
|
|
907
|
+
declare function getPage(client: BetterCMSAdminClient, id: string): Promise<Page>;
|
|
908
|
+
declare function createPage(client: BetterCMSAdminClient, data: CreatePageInput): Promise<Page>;
|
|
909
|
+
declare function updatePage(client: BetterCMSAdminClient, id: string, data: UpdatePageInput): Promise<Page>;
|
|
910
|
+
declare function deletePage(client: BetterCMSAdminClient, id: string): Promise<void>;
|
|
911
|
+
declare function publishPage(client: BetterCMSAdminClient, id: string): Promise<Page>;
|
|
912
|
+
|
|
913
|
+
/**
|
|
914
|
+
* Admin media methods.
|
|
915
|
+
* @see openspec/changes/flo-6-backend-sdk-admin/specs/sdk-admin-client/spec.md
|
|
916
|
+
*/
|
|
917
|
+
|
|
918
|
+
interface MediaListOptions {
|
|
919
|
+
page?: number;
|
|
920
|
+
limit?: number;
|
|
921
|
+
}
|
|
922
|
+
interface MediaMetadata {
|
|
923
|
+
alt?: string;
|
|
924
|
+
caption?: string;
|
|
925
|
+
}
|
|
926
|
+
declare function listMedia(client: BetterCMSAdminClient, opts?: MediaListOptions): Promise<MediaAsset[]>;
|
|
927
|
+
declare function getMedia(client: BetterCMSAdminClient, id: string): Promise<MediaAsset>;
|
|
928
|
+
declare function uploadMedia(client: BetterCMSAdminClient, file: File, metadata?: MediaMetadata): Promise<MediaAsset>;
|
|
929
|
+
declare function deleteMedia(client: BetterCMSAdminClient, id: string): Promise<void>;
|
|
930
|
+
|
|
931
|
+
/**
|
|
932
|
+
* Admin forms methods.
|
|
933
|
+
* @see openspec/changes/flo-6-backend-sdk-admin/specs/sdk-admin-client/spec.md
|
|
934
|
+
*/
|
|
935
|
+
|
|
936
|
+
interface FormListOptions {
|
|
937
|
+
page?: number;
|
|
938
|
+
limit?: number;
|
|
939
|
+
}
|
|
940
|
+
interface CreateFormInput {
|
|
941
|
+
name: string;
|
|
942
|
+
description?: string;
|
|
943
|
+
fields?: FormFieldInput[];
|
|
944
|
+
submitLabel?: string;
|
|
945
|
+
successMessage?: string;
|
|
946
|
+
redirectUrl?: string;
|
|
947
|
+
webhookUrl?: string;
|
|
948
|
+
}
|
|
949
|
+
interface FormFieldInput {
|
|
950
|
+
key: string;
|
|
951
|
+
label: string;
|
|
952
|
+
type: "text" | "email" | "textarea" | "select" | "checkbox" | "number";
|
|
953
|
+
placeholder?: string;
|
|
954
|
+
required?: boolean;
|
|
955
|
+
options?: string[];
|
|
956
|
+
}
|
|
957
|
+
interface UpdateFormInput extends Partial<Omit<CreateFormInput, "fields">> {
|
|
958
|
+
fields?: FormFieldInput[];
|
|
959
|
+
}
|
|
960
|
+
declare function listForms(client: BetterCMSAdminClient, opts?: FormListOptions): Promise<Form[]>;
|
|
961
|
+
declare function getForm(client: BetterCMSAdminClient, id: string): Promise<Form>;
|
|
962
|
+
declare function createForm(client: BetterCMSAdminClient, data: CreateFormInput): Promise<Form>;
|
|
963
|
+
declare function updateForm(client: BetterCMSAdminClient, id: string, data: UpdateFormInput): Promise<Form>;
|
|
964
|
+
declare function deleteForm(client: BetterCMSAdminClient, id: string): Promise<void>;
|
|
965
|
+
|
|
966
|
+
/**
|
|
967
|
+
* Admin submissions methods.
|
|
968
|
+
* @see openspec/changes/flo-6-backend-sdk-admin/specs/sdk-admin-client/spec.md
|
|
969
|
+
*/
|
|
970
|
+
|
|
971
|
+
interface SubmissionListOptions {
|
|
972
|
+
page?: number;
|
|
973
|
+
limit?: number;
|
|
974
|
+
}
|
|
975
|
+
declare function listSubmissions(client: BetterCMSAdminClient, opts?: SubmissionListOptions): Promise<FormSubmission[]>;
|
|
976
|
+
declare function getSubmission(client: BetterCMSAdminClient, id: string): Promise<FormSubmission>;
|
|
977
|
+
declare function deleteSubmission(client: BetterCMSAdminClient, id: string): Promise<void>;
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* Admin workspaces methods.
|
|
981
|
+
* @see openspec/changes/flo-6-backend-sdk-admin/specs/sdk-admin-client/spec.md
|
|
982
|
+
*/
|
|
983
|
+
|
|
984
|
+
interface CreateWorkspaceInput {
|
|
985
|
+
name: string;
|
|
986
|
+
slug: string;
|
|
987
|
+
customDomain?: string;
|
|
988
|
+
planId: string;
|
|
989
|
+
}
|
|
990
|
+
interface UpdateWorkspaceInput {
|
|
991
|
+
name?: string;
|
|
992
|
+
slug?: string;
|
|
993
|
+
customDomain?: string | null;
|
|
994
|
+
}
|
|
995
|
+
declare function listWorkspaces(client: BetterCMSAdminClient): Promise<Workspace[]>;
|
|
996
|
+
declare function getWorkspace(client: BetterCMSAdminClient, id: string): Promise<Workspace>;
|
|
997
|
+
declare function createWorkspace(client: BetterCMSAdminClient, data: CreateWorkspaceInput): Promise<Workspace>;
|
|
998
|
+
declare function updateWorkspace(client: BetterCMSAdminClient, id: string, data: UpdateWorkspaceInput): Promise<Workspace>;
|
|
999
|
+
declare function deleteWorkspace(client: BetterCMSAdminClient, id: string): Promise<void>;
|
|
1000
|
+
|
|
1001
|
+
/**
|
|
1002
|
+
* Admin API keys methods.
|
|
1003
|
+
* @see openspec/changes/flo-6-backend-sdk-admin/specs/sdk-admin-client/spec.md
|
|
1004
|
+
*/
|
|
1005
|
+
|
|
1006
|
+
/** ApiKey with raw key — returned only on creation or regeneration. */
|
|
1007
|
+
interface ApiKeyWithRaw extends ApiKey {
|
|
1008
|
+
key: string;
|
|
1009
|
+
}
|
|
1010
|
+
interface CreateApiKeyInput {
|
|
1011
|
+
name: string;
|
|
1012
|
+
permissions?: ApiKeyPermission[];
|
|
1013
|
+
tokenType?: ApiKeyTokenType;
|
|
1014
|
+
description?: string;
|
|
1015
|
+
expiresAt?: string;
|
|
1016
|
+
rateLimit?: number;
|
|
1017
|
+
}
|
|
1018
|
+
interface UpdateApiKeyInput {
|
|
1019
|
+
name?: string;
|
|
1020
|
+
permissions?: ApiKeyPermission[];
|
|
1021
|
+
tokenType?: ApiKeyTokenType;
|
|
1022
|
+
description?: string | null;
|
|
1023
|
+
expiresAt?: string | null;
|
|
1024
|
+
rateLimit?: number | null;
|
|
1025
|
+
}
|
|
1026
|
+
interface ApiKeyUsage {
|
|
1027
|
+
keyId: string;
|
|
1028
|
+
name: string;
|
|
1029
|
+
lastUsedAt: string | null;
|
|
1030
|
+
createdAt: string;
|
|
1031
|
+
totalRequests: number;
|
|
1032
|
+
}
|
|
1033
|
+
declare function listApiKeys(client: BetterCMSAdminClient): Promise<ApiKey[]>;
|
|
1034
|
+
declare function createApiKey(client: BetterCMSAdminClient, data: CreateApiKeyInput): Promise<ApiKeyWithRaw>;
|
|
1035
|
+
declare function updateApiKey(client: BetterCMSAdminClient, id: string, data: UpdateApiKeyInput): Promise<ApiKey>;
|
|
1036
|
+
declare function regenerateApiKey(client: BetterCMSAdminClient, id: string): Promise<ApiKeyWithRaw>;
|
|
1037
|
+
declare function getApiKeyUsage(client: BetterCMSAdminClient, id: string): Promise<ApiKeyUsage>;
|
|
1038
|
+
declare function revokeApiKey(client: BetterCMSAdminClient, id: string): Promise<void>;
|
|
1039
|
+
|
|
1040
|
+
/**
|
|
1041
|
+
* Admin members methods.
|
|
1042
|
+
* @see openspec/changes/flo-6-backend-sdk-admin/specs/sdk-admin-client/spec.md
|
|
1043
|
+
*/
|
|
1044
|
+
|
|
1045
|
+
type MemberRole = "owner" | "admin" | "editor" | "viewer";
|
|
1046
|
+
interface Member {
|
|
1047
|
+
id: string;
|
|
1048
|
+
userId: string;
|
|
1049
|
+
email: string | null;
|
|
1050
|
+
name: string | null;
|
|
1051
|
+
role: MemberRole;
|
|
1052
|
+
joinedAt: string;
|
|
1053
|
+
}
|
|
1054
|
+
interface PendingInvite {
|
|
1055
|
+
id: string;
|
|
1056
|
+
email: string;
|
|
1057
|
+
role: MemberRole;
|
|
1058
|
+
expiresAt: string;
|
|
1059
|
+
}
|
|
1060
|
+
declare function listMembers(client: BetterCMSAdminClient, workspaceId: string, opts?: {
|
|
1061
|
+
page?: number;
|
|
1062
|
+
limit?: number;
|
|
1063
|
+
}): Promise<Member[]>;
|
|
1064
|
+
declare function getMember(client: BetterCMSAdminClient, workspaceId: string, memberId: string): Promise<Member>;
|
|
1065
|
+
declare function inviteMember(client: BetterCMSAdminClient, workspaceId: string, data: {
|
|
1066
|
+
email: string;
|
|
1067
|
+
role: MemberRole;
|
|
1068
|
+
}): Promise<PendingInvite>;
|
|
1069
|
+
declare function updateMemberRole(client: BetterCMSAdminClient, workspaceId: string, memberId: string, role: MemberRole): Promise<Member>;
|
|
1070
|
+
declare function removeMember(client: BetterCMSAdminClient, workspaceId: string, memberId: string): Promise<void>;
|
|
1071
|
+
|
|
1072
|
+
export { type AddPageFieldsInput, type ApiKeyUsage, type ApiKeyWithRaw, type AuthResult, BetterCMSError as BCMSClientError, ErrorCodes as BCMSErrorCodes, BetterCMS, BetterCMSAdminClient, type BetterCMSAdminOptions, BetterCMSDeliveryClient, BetterCMSError, BetterCMSManagementClient, type BetterCMSManagementOptions, type BetterCMSReadClient, type BetterCMSSiteOptions, type CreateApiKeyInput, type CreateClientOptions, type CreateEntryInput, type CreateFormInput, type CreateManagedPageInput, type CreateModelInput, type CreatePageInput, type CreateWorkspaceInput, type DeliveryForm, type DeliveryFormField, type DeliveryFormFieldType, ErrorCodes, type FormFieldInput, type FormListOptions, type FormSubmitError, type FormValue, type FormValues, type GetEntryOptions, type ListContentAllOptions, type ListContentOptions, type ListEntriesFilter, type ListEntriesOptions, type ListPagesOptions, type ManagedComponent, type ManagedComponentInput, type ManagedContentEntry, type ManagedContentModel, type ManagedForm, type ManagedFormInput, type ManagedPage, type ManagementClient, type MediaListOptions, type MediaMetadata, type Member, type MemberRole, type PageListOptions, type PendingInvite, type ResolvedSeo, type SearchHit, type SearchOptions, type SeoInput, type SetPageContentInput, type StegaPayload, type SubmissionListOptions, type SubmitFormOptions, type SubmitFormResult, type UpdateApiKeyInput, type UpdateEntryInput, type UpdateFormInput, type UpdateModelInput, type UpdatePageInput, type UpdateWorkspaceInput, type UploadAssetInput, type UploadedAsset, addModelFields, addPageFields, createApiKey, createClient, createEntry, createForm, createManagedPage, createModel, createPage, createWorkspace, decodeStega, deleteForm, deleteMedia, deletePage, deleteSubmission, deleteWorkspace, encodeStega, formInitialValues, getApiKeyUsage, getEntry, getForm, getManagedPage, getMedia, getMember, getModel, getPage, getSubmission, getWorkspace, inviteMember, listApiKeys, listEntries, listForms, listManagedPages, listMedia, listMembers, listModels, listPages, listSubmissions, listWorkspaces, publishPage, regenerateApiKey, removeMember, resolveSeo, revokeApiKey, search, setPageContent, shouldShowField, signIn, signInWithGithub, signInWithGoogle, signUp, stripStega, submitForm, updateApiKey, updateEntry, updateForm, updateMemberRole, updateModel, updatePage, updateWorkspace, uploadMedia };
|