@botlr/next 0.1.0 → 0.3.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/CHANGELOG.md +29 -0
- package/README.md +78 -20
- package/dist/index.cjs +580 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +336 -3
- package/dist/index.d.ts +336 -3
- package/dist/index.js +561 -4
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import { BotlrClient,
|
|
1
|
+
import { BotlrClientConfig, BotlrClient, PublisherScope, PageResponse, LayoutNode } from '@botlr/sdk';
|
|
2
2
|
export * from '@botlr/sdk';
|
|
3
|
-
export { BotlrConflictError, BotlrError, createBotlrClient } from '@botlr/sdk';
|
|
3
|
+
export { BotlrConflictError, BotlrError, createAssetUrl, createBotlrClient, getAssetFilename, getAssetId, getAssetUrl, isAssetData } from '@botlr/sdk';
|
|
4
|
+
import { Metadata } from 'next';
|
|
5
|
+
import * as react$1 from 'react';
|
|
6
|
+
import { ReactNode, JSX } from 'react';
|
|
7
|
+
import { ComponentRegistry } from '@botlr/react';
|
|
4
8
|
export * from '@botlr/react';
|
|
5
9
|
|
|
6
10
|
type GetBotlrParams = {
|
|
@@ -16,6 +20,8 @@ type GetBotlrParams = {
|
|
|
16
20
|
/** API origin; falls back to `BOTLR_API_URL`. */
|
|
17
21
|
baseUrl?: string;
|
|
18
22
|
fetch?: typeof fetch;
|
|
23
|
+
/** Retry policy for failed reads; see `BotlrClientConfig.retry`. */
|
|
24
|
+
retry?: BotlrClientConfig["retry"];
|
|
19
25
|
};
|
|
20
26
|
/**
|
|
21
27
|
* A Botlr client for the current request, for Server Components, route
|
|
@@ -46,6 +52,10 @@ declare const BOTLR_ENV: {
|
|
|
46
52
|
readonly apiKey: "BOTLR_API_KEY";
|
|
47
53
|
readonly appId: "BOTLR_APP_ID";
|
|
48
54
|
readonly pat: "BOTLR_PAT";
|
|
55
|
+
readonly orgSlug: "BOTLR_ORG_SLUG";
|
|
56
|
+
readonly projectSlug: "BOTLR_PROJECT_SLUG";
|
|
57
|
+
readonly appInstanceSlug: "BOTLR_APP_INSTANCE_SLUG";
|
|
58
|
+
readonly environment: "BOTLR_ENVIRONMENT";
|
|
49
59
|
};
|
|
50
60
|
declare const DEFAULT_API_URL = "http://localhost:8888";
|
|
51
61
|
/**
|
|
@@ -53,8 +63,331 @@ declare const DEFAULT_API_URL = "http://localhost:8888";
|
|
|
53
63
|
* no app id is known, since every Botlr request needs one.
|
|
54
64
|
*/
|
|
55
65
|
declare function configFromEnv(overrides?: Partial<BotlrClientConfig>): BotlrClientConfig;
|
|
66
|
+
/**
|
|
67
|
+
* The deployment environment used by visibility rules: `BOTLR_ENVIRONMENT`,
|
|
68
|
+
* then `ENVIRONMENT`, then `NODE_ENV`, then `fallback`.
|
|
69
|
+
*/
|
|
70
|
+
declare function environmentFromEnv(fallback?: string): string;
|
|
71
|
+
/**
|
|
72
|
+
* The `PublisherScope` for a locale from `BOTLR_ORG_SLUG`, `BOTLR_PROJECT_SLUG`,
|
|
73
|
+
* `BOTLR_APP_INSTANCE_SLUG` and `BOTLR_APP_ID`, with `overrides` winning.
|
|
74
|
+
* Missing slugs become empty strings rather than errors, since only asset
|
|
75
|
+
* URLs need them.
|
|
76
|
+
*/
|
|
77
|
+
declare function scopeFromEnv(overrides: Partial<PublisherScope> & Pick<PublisherScope, "locale">): PublisherScope;
|
|
56
78
|
|
|
57
79
|
/** The first language of an `Accept-Language` value, or `fallback`. */
|
|
58
80
|
declare function localeFromAcceptLanguage(header: string | null | undefined, fallback?: string): string;
|
|
59
81
|
|
|
60
|
-
|
|
82
|
+
type ResolvedPage = {
|
|
83
|
+
kind: "page";
|
|
84
|
+
pageData: PageResponse;
|
|
85
|
+
} | {
|
|
86
|
+
kind: "redirect";
|
|
87
|
+
destination: string;
|
|
88
|
+
} | {
|
|
89
|
+
kind: "404";
|
|
90
|
+
} | {
|
|
91
|
+
kind: "error";
|
|
92
|
+
message: string;
|
|
93
|
+
cause: unknown;
|
|
94
|
+
};
|
|
95
|
+
interface ResolvePageOptions {
|
|
96
|
+
/**
|
|
97
|
+
* Non-page content types rendered through a section: the resolved content
|
|
98
|
+
* is prepended to the `main` region as an inline section of the mapped
|
|
99
|
+
* type. Defaults to `{ article: "article" }`.
|
|
100
|
+
*/
|
|
101
|
+
contentSections?: Record<string, string>;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Resolves a slug to a page through `/api/resolve`, folding redirects, 404s
|
|
105
|
+
* and errors into one result and injecting article content into the layout.
|
|
106
|
+
* Not cached; see `createPageResolver`.
|
|
107
|
+
*/
|
|
108
|
+
declare function resolvePage(client: BotlrClient, slug: string, locale?: string, options?: ResolvePageOptions): Promise<ResolvedPage>;
|
|
109
|
+
/**
|
|
110
|
+
* `resolvePage` bound to a client and memoised per request with React
|
|
111
|
+
* `cache`, so `generateMetadata` and the page share one fetch.
|
|
112
|
+
*
|
|
113
|
+
* ```ts
|
|
114
|
+
* const resolvePage = createPageResolver(botlr);
|
|
115
|
+
* const resolved = await resolvePage("about/team", "en");
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
declare function createPageResolver(client: BotlrClient, options?: ResolvePageOptions): (slug: string, locale?: string) => Promise<ResolvedPage>;
|
|
119
|
+
|
|
120
|
+
interface PageMetadataOptions {
|
|
121
|
+
/**
|
|
122
|
+
* The site origin, for example `https://www.example.com`. Needed for the
|
|
123
|
+
* canonical URL and hreflang alternates; without it only an explicit
|
|
124
|
+
* `seo_meta.canonical` is emitted.
|
|
125
|
+
*/
|
|
126
|
+
siteUrl?: string;
|
|
127
|
+
/** URL prefix of the page's locale, for example `/en`. Empty for the root locale. */
|
|
128
|
+
folder?: string;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Next.js `Metadata` from a page's `seo_meta`: title, description, keywords,
|
|
132
|
+
* Open Graph, Twitter, robots, canonical and hreflang alternates.
|
|
133
|
+
*/
|
|
134
|
+
declare function buildPageMetadata(pageData: PageResponse, options?: PageMetadataOptions): Metadata;
|
|
135
|
+
|
|
136
|
+
type Layout = Record<string, LayoutNode[]>;
|
|
137
|
+
interface RenderCmsPageOptions {
|
|
138
|
+
pageData: PageResponse;
|
|
139
|
+
scope: PublisherScope;
|
|
140
|
+
/** Section type to component map used for every region. */
|
|
141
|
+
componentRegistry: ComponentRegistry;
|
|
142
|
+
/** Defaults to `"published"`; `"draft"` also renders draft sections. */
|
|
143
|
+
mode?: "draft" | "published";
|
|
144
|
+
/** Defaults to `scope.locale`. */
|
|
145
|
+
locale?: string;
|
|
146
|
+
/** Environment for visibility rules; defaults to `environmentFromEnv()`. */
|
|
147
|
+
environment?: string;
|
|
148
|
+
/** Used to fetch shared sections unless `fetchSharedSection` is given. */
|
|
149
|
+
client?: BotlrClient;
|
|
150
|
+
/** Fetch a shared section's page by id. Defaults to `client.getPageBySlug(id, locale)`. */
|
|
151
|
+
fetchSharedSection?: (id: string) => Promise<{
|
|
152
|
+
layout?: Partial<Layout>;
|
|
153
|
+
} | null>;
|
|
154
|
+
/** Show a fixed "draft/published preview" badge; for preview routes only. */
|
|
155
|
+
showModeBadge?: boolean;
|
|
156
|
+
/** Rendered when the template cannot be parsed. */
|
|
157
|
+
renderError?: (error: unknown) => ReactNode;
|
|
158
|
+
}
|
|
159
|
+
/** Ids of every shared section in the layout, grid columns included. */
|
|
160
|
+
declare function collectSharedSectionIds(nodes: LayoutNode[]): string[];
|
|
161
|
+
/**
|
|
162
|
+
* The template's `{{...}}` placeholders resolved: title, lang, theme css,
|
|
163
|
+
* `{{{REGION}}}` markers turned into text markers for the parser, and the
|
|
164
|
+
* body extracted.
|
|
165
|
+
*/
|
|
166
|
+
declare function prepareTemplate(templateHtml: string, pageData: PageResponse, templateCss: string, locale: string): string;
|
|
167
|
+
/** Template sections first, then the page's own, per region. */
|
|
168
|
+
declare function mergeTemplateLayout(pageData: PageResponse): Layout;
|
|
169
|
+
/**
|
|
170
|
+
* Renders a resolved page: picks the page template (page, then theme, then a
|
|
171
|
+
* built-in default), merges template and page layouts, applies visibility
|
|
172
|
+
* rules, fetches shared sections and mounts a `SectionRenderer` at every
|
|
173
|
+
* `{{{REGION}}}` marker.
|
|
174
|
+
*/
|
|
175
|
+
declare function renderCmsPage({ pageData, scope, componentRegistry, mode, locale, environment, client, fetchSharedSection, showModeBadge, renderError, }: RenderCmsPageOptions): Promise<JSX.Element>;
|
|
176
|
+
|
|
177
|
+
interface ThemeColorSet {
|
|
178
|
+
primary?: string;
|
|
179
|
+
primaryForeground?: string;
|
|
180
|
+
secondary?: string;
|
|
181
|
+
secondaryForeground?: string;
|
|
182
|
+
accent?: string;
|
|
183
|
+
accentForeground?: string;
|
|
184
|
+
background?: string;
|
|
185
|
+
foreground?: string;
|
|
186
|
+
muted?: string;
|
|
187
|
+
mutedForeground?: string;
|
|
188
|
+
border?: string;
|
|
189
|
+
input?: string;
|
|
190
|
+
ring?: string;
|
|
191
|
+
destructive?: string;
|
|
192
|
+
destructiveForeground?: string;
|
|
193
|
+
success?: string;
|
|
194
|
+
successForeground?: string;
|
|
195
|
+
warning?: string;
|
|
196
|
+
warningForeground?: string;
|
|
197
|
+
info?: string;
|
|
198
|
+
infoForeground?: string;
|
|
199
|
+
}
|
|
200
|
+
interface ThemeSettings {
|
|
201
|
+
colors?: {
|
|
202
|
+
light?: ThemeColorSet;
|
|
203
|
+
dark?: ThemeColorSet;
|
|
204
|
+
};
|
|
205
|
+
typography?: {
|
|
206
|
+
fontFamily?: {
|
|
207
|
+
heading?: string;
|
|
208
|
+
body?: string;
|
|
209
|
+
ui?: string;
|
|
210
|
+
code?: string;
|
|
211
|
+
sans?: string;
|
|
212
|
+
serif?: string;
|
|
213
|
+
mono?: string;
|
|
214
|
+
};
|
|
215
|
+
fontSize?: {
|
|
216
|
+
h1?: string;
|
|
217
|
+
h2?: string;
|
|
218
|
+
h3?: string;
|
|
219
|
+
h4?: string;
|
|
220
|
+
h5?: string;
|
|
221
|
+
h6?: string;
|
|
222
|
+
xs?: string;
|
|
223
|
+
sm?: string;
|
|
224
|
+
base?: string;
|
|
225
|
+
lg?: string;
|
|
226
|
+
xl?: string;
|
|
227
|
+
"2xl"?: string;
|
|
228
|
+
"3xl"?: string;
|
|
229
|
+
"4xl"?: string;
|
|
230
|
+
};
|
|
231
|
+
fontWeight?: {
|
|
232
|
+
normal?: string;
|
|
233
|
+
medium?: string;
|
|
234
|
+
semibold?: string;
|
|
235
|
+
bold?: string;
|
|
236
|
+
};
|
|
237
|
+
lineHeight?: {
|
|
238
|
+
tight?: string;
|
|
239
|
+
normal?: string;
|
|
240
|
+
relaxed?: string;
|
|
241
|
+
};
|
|
242
|
+
};
|
|
243
|
+
spacing?: {
|
|
244
|
+
xs?: string;
|
|
245
|
+
sm?: string;
|
|
246
|
+
md?: string;
|
|
247
|
+
lg?: string;
|
|
248
|
+
xl?: string;
|
|
249
|
+
"2xl"?: string;
|
|
250
|
+
};
|
|
251
|
+
borderRadius?: {
|
|
252
|
+
none?: string;
|
|
253
|
+
sm?: string;
|
|
254
|
+
md?: string;
|
|
255
|
+
lg?: string;
|
|
256
|
+
xl?: string;
|
|
257
|
+
full?: string;
|
|
258
|
+
};
|
|
259
|
+
shadows?: {
|
|
260
|
+
sm?: string;
|
|
261
|
+
md?: string;
|
|
262
|
+
lg?: string;
|
|
263
|
+
xl?: string;
|
|
264
|
+
};
|
|
265
|
+
layout?: {
|
|
266
|
+
containerMaxWidth?: string;
|
|
267
|
+
sidebarWidth?: string;
|
|
268
|
+
headerHeight?: string;
|
|
269
|
+
footerHeight?: string;
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Theme settings as CSS custom properties: common variables and light colors
|
|
274
|
+
* on `:root`, dark colors under `prefers-color-scheme: dark` and `.dark`.
|
|
275
|
+
*/
|
|
276
|
+
declare function themeSettingsToCss(settings?: ThemeSettings | Record<string, any>): string;
|
|
277
|
+
|
|
278
|
+
interface StaticTextFileOptions {
|
|
279
|
+
/** Defaults to the client's locale, then `en`. */
|
|
280
|
+
locale?: string;
|
|
281
|
+
/** Environment for visibility rules; defaults to `environmentFromEnv()`. */
|
|
282
|
+
environment?: string;
|
|
283
|
+
/** The section type whose content is served; defaults to `text`. */
|
|
284
|
+
sectionType?: string;
|
|
285
|
+
/** Section props tried in order for the text; defaults to `content`, `body`, `text`. */
|
|
286
|
+
propKeys?: string[];
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* A CMS-managed plain-text file such as `robots.txt`, `humans.txt` or
|
|
290
|
+
* `llms.txt`: the page at `slug`, its visible text sections concatenated.
|
|
291
|
+
* Serves `defaultContent` when the page has none or cannot be fetched.
|
|
292
|
+
*
|
|
293
|
+
* ```ts
|
|
294
|
+
* // app/robots.txt/route.ts
|
|
295
|
+
* export const GET = () => serveStaticTextFile(botlr, "robots.txt", defaultRobotsContent());
|
|
296
|
+
* ```
|
|
297
|
+
*/
|
|
298
|
+
declare function serveStaticTextFile(client: BotlrClient, slug: string, defaultContent: string, options?: StaticTextFileOptions): Promise<Response>;
|
|
299
|
+
/** Allow everything in production, disallow everything elsewhere. */
|
|
300
|
+
declare function defaultRobotsContent(environment?: string): string;
|
|
301
|
+
|
|
302
|
+
interface LocalePageConfig {
|
|
303
|
+
/** CMS locale code, for example `da` or `en`. */
|
|
304
|
+
locale: string;
|
|
305
|
+
/** URL prefix for this locale, for example `/en`. Empty for the root locale. */
|
|
306
|
+
folder?: string;
|
|
307
|
+
/** The client the pages are fetched with; usually one built with `createBotlrFromEnv`. */
|
|
308
|
+
client: BotlrClient;
|
|
309
|
+
componentRegistry: ComponentRegistry;
|
|
310
|
+
/** Overrides on the scope built from the environment. */
|
|
311
|
+
scope?: Partial<PublisherScope>;
|
|
312
|
+
/** The site origin, for canonical and hreflang metadata. */
|
|
313
|
+
siteUrl?: string;
|
|
314
|
+
/** Environment for visibility rules; defaults to `environmentFromEnv()`. */
|
|
315
|
+
environment?: string;
|
|
316
|
+
resolveOptions?: ResolvePageOptions;
|
|
317
|
+
/** Redirect kind for CMS redirects; defaults to `permanent` (308). */
|
|
318
|
+
redirect?: "permanent" | "temporary";
|
|
319
|
+
/** Page slugs fetched for `generateStaticParams`; defaults to 1000. */
|
|
320
|
+
staticParamsLimit?: number;
|
|
321
|
+
/**
|
|
322
|
+
* What happens when resolving a page throws (after the client's retries).
|
|
323
|
+
* `throw` rethrows, which fails `next build` and gives a 500 at request
|
|
324
|
+
* time; `render` shows an error box in place of the page. Defaults to
|
|
325
|
+
* `render` when `NODE_ENV` is `development` and `throw` otherwise.
|
|
326
|
+
*/
|
|
327
|
+
resolveErrors?: "throw" | "render";
|
|
328
|
+
/** Rendered in place of the page when `resolveErrors` is `render`. */
|
|
329
|
+
renderError?: (message: string) => ReactNode;
|
|
330
|
+
}
|
|
331
|
+
interface LocalePageProps {
|
|
332
|
+
params: Promise<{
|
|
333
|
+
slug?: string[];
|
|
334
|
+
}>;
|
|
335
|
+
searchParams?: Promise<Record<string, string | string[] | undefined>>;
|
|
336
|
+
}
|
|
337
|
+
/** The page slug for a catch-all route: the segments joined, or `index`. */
|
|
338
|
+
declare function pageSlugFromParams(params: LocalePageProps["params"]): Promise<string>;
|
|
339
|
+
/**
|
|
340
|
+
* Everything a static per-locale `[[...slug]]` route needs. The route file
|
|
341
|
+
* keeps only its segment config, which Next must read statically:
|
|
342
|
+
*
|
|
343
|
+
* ```ts
|
|
344
|
+
* // app/(root)/[[...slug]]/page.tsx
|
|
345
|
+
* export const revalidate = false;
|
|
346
|
+
* export const dynamicParams = false;
|
|
347
|
+
* const page = createLocalePage({ locale: "da", folder: "", client: botlr, componentRegistry });
|
|
348
|
+
* export const generateStaticParams = page.generateStaticParams;
|
|
349
|
+
* export const generateMetadata = page.generateMetadata;
|
|
350
|
+
* export default page.Page;
|
|
351
|
+
* ```
|
|
352
|
+
*/
|
|
353
|
+
declare function createLocalePage(config: LocalePageConfig): {
|
|
354
|
+
generateStaticParams: () => Promise<{
|
|
355
|
+
slug?: string[];
|
|
356
|
+
}[]>;
|
|
357
|
+
generateMetadata: ({ params }: LocalePageProps) => Promise<Metadata>;
|
|
358
|
+
Page: ({ params }: LocalePageProps) => Promise<react$1.JSX.Element>;
|
|
359
|
+
resolvePage: (slug: string, locale?: string) => Promise<ResolvedPage>;
|
|
360
|
+
scope: PublisherScope;
|
|
361
|
+
};
|
|
362
|
+
/**
|
|
363
|
+
* A locale folder that only aliases another, for example `/se/*` to `/sv/*`.
|
|
364
|
+
* Permanently redirects, keeping the rest of the path.
|
|
365
|
+
*/
|
|
366
|
+
declare function createLocaleAliasPage(targetFolder: string): ({ params }: LocalePageProps) => Promise<never>;
|
|
367
|
+
|
|
368
|
+
interface RateLimitConfig {
|
|
369
|
+
/** Max requests allowed in the window */
|
|
370
|
+
limit: number;
|
|
371
|
+
/** Window size in seconds */
|
|
372
|
+
window: number;
|
|
373
|
+
}
|
|
374
|
+
interface RateLimitResult {
|
|
375
|
+
allowed: boolean;
|
|
376
|
+
remaining: number;
|
|
377
|
+
/** Epoch ms when the window resets */
|
|
378
|
+
resetAt: number;
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* A fixed-window in-memory rate limit, per server instance. Enough to slow
|
|
382
|
+
* down form and forum spam; not shared across instances or restarts.
|
|
383
|
+
*
|
|
384
|
+
* ```ts
|
|
385
|
+
* const rl = rateLimit(`form:${getClientIp(request)}`, { limit: 5, window: 60 });
|
|
386
|
+
* if (!rl.allowed) return new Response("Too many requests", { status: 429 });
|
|
387
|
+
* ```
|
|
388
|
+
*/
|
|
389
|
+
declare function rateLimit(key: string, config: RateLimitConfig): RateLimitResult;
|
|
390
|
+
/** The client IP from `x-forwarded-for` or `x-real-ip`, or `"unknown"`. */
|
|
391
|
+
declare function getClientIp(request: Request): string;
|
|
392
|
+
|
|
393
|
+
export { BOTLR_ENV, DEFAULT_API_URL, type GetBotlrParams, type LocalePageConfig, type LocalePageProps, type PageMetadataOptions, type RateLimitConfig, type RateLimitResult, type RenderCmsPageOptions, type ResolvePageOptions, type ResolvedPage, type StaticTextFileOptions, type ThemeColorSet, type ThemeSettings, buildPageMetadata, collectSharedSectionIds, configFromEnv, createBotlrFromEnv, createLocaleAliasPage, createLocalePage, createPageResolver, defaultRobotsContent, environmentFromEnv, getBotlrForRoute, getCachedBotlr, getClientIp, localeFromAcceptLanguage, mergeTemplateLayout, pageSlugFromParams, prepareTemplate, rateLimit, renderCmsPage, resolvePage, scopeFromEnv, serveStaticTextFile, themeSettingsToCss };
|