busabase-cms-sdk 0.1.3

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,24 @@
1
+ import { o as PageVO } from "./types-QKFW2jvT.js";
2
+ import { MarkdownProps } from "fumadocs-core/content/md";
3
+ import { ReactNode } from "react";
4
+ import { TOCItemType } from "fumadocs-core/toc";
5
+ //#region src/fumadocs.d.ts
6
+ interface SafeMarkdownProps {
7
+ children: string;
8
+ components?: MarkdownProps["components"];
9
+ }
10
+ /** Render stored Markdown without MDX execution or raw HTML passthrough. */
11
+ declare const SafeMarkdown: ({ children, components }: SafeMarkdownProps) => Promise<ReactNode>;
12
+ declare const getSafeMarkdownToc: (markdown: string) => Promise<TOCItemType[]>;
13
+ /** Sanitize stored Landing Page HTML before passing it to `dangerouslySetInnerHTML`. */
14
+ declare const sanitizeLandingPageHtml: (html: string) => string;
15
+ /**
16
+ * Sanitize a CMS Page's stored body for injection. Lives here, next to the sanitizer it wraps,
17
+ * rather than in `busabase-cms-sdk/integration`: `sanitizeLandingPageHtml` drags in remark, rehype
18
+ * and sanitize-html, and only the component that actually injects the HTML should pay for that.
19
+ *
20
+ * Call this immediately before injecting — the sanitize step must never move up to the caller.
21
+ */
22
+ declare const getSanitizedCmsPageBody: (page: Pick<PageVO, "body">) => string;
23
+ //#endregion
24
+ export { SafeMarkdown, SafeMarkdownProps, getSafeMarkdownToc, getSanitizedCmsPageBody, sanitizeLandingPageHtml };
@@ -0,0 +1,188 @@
1
+ import { createMarkdownRenderer } from "fumadocs-core/content/md";
2
+ import { getTableOfContents } from "fumadocs-core/content/toc";
3
+ import { remarkHeading } from "fumadocs-core/mdx-plugins/remark-heading";
4
+ import rehypeSanitize from "rehype-sanitize";
5
+ import remarkGfm from "remark-gfm";
6
+ import sanitizeHtml from "sanitize-html";
7
+ //#region src/fumadocs.ts
8
+ const safeMarkdownRenderer = createMarkdownRenderer({
9
+ remarkPlugins: [remarkGfm, remarkHeading],
10
+ rehypePlugins: [rehypeSanitize]
11
+ });
12
+ /** Render stored Markdown without MDX execution or raw HTML passthrough. */
13
+ const SafeMarkdown = async ({ children, components }) => safeMarkdownRenderer.MarkdownServer({
14
+ children,
15
+ components
16
+ });
17
+ const getSafeMarkdownToc = async (markdown) => getTableOfContents(markdown, [remarkGfm]);
18
+ const safeCssValue = /^(?!.*(?:url|expression|@import|javascript))[-a-zA-Z0-9#(),.%\s/]+$/i;
19
+ const safeLength = /^(?!.*(?:url|expression|@import|javascript))(?:0|auto|none|(?:min|max|clamp|calc)\([^;{}]+\)|[0-9.]+(?:px|rem|em|%|vh|vw|ch))$/i;
20
+ /** Sanitize stored Landing Page HTML before passing it to `dangerouslySetInnerHTML`. */
21
+ const sanitizeLandingPageHtml = (html) => sanitizeHtml(html, {
22
+ allowedTags: [
23
+ "article",
24
+ "section",
25
+ "div",
26
+ "span",
27
+ "p",
28
+ "h1",
29
+ "h2",
30
+ "h3",
31
+ "h4",
32
+ "h5",
33
+ "h6",
34
+ "a",
35
+ "strong",
36
+ "em",
37
+ "b",
38
+ "i",
39
+ "s",
40
+ "blockquote",
41
+ "code",
42
+ "pre",
43
+ "ul",
44
+ "ol",
45
+ "li",
46
+ "dl",
47
+ "dt",
48
+ "dd",
49
+ "table",
50
+ "thead",
51
+ "tbody",
52
+ "tfoot",
53
+ "tr",
54
+ "th",
55
+ "td",
56
+ "figure",
57
+ "figcaption",
58
+ "picture",
59
+ "source",
60
+ "img",
61
+ "details",
62
+ "summary",
63
+ "hr",
64
+ "br"
65
+ ],
66
+ allowedAttributes: {
67
+ "*": [
68
+ "id",
69
+ "style",
70
+ "role",
71
+ "aria-label",
72
+ "aria-labelledby",
73
+ "aria-describedby"
74
+ ],
75
+ a: [
76
+ "href",
77
+ "name",
78
+ "target",
79
+ "rel",
80
+ "title"
81
+ ],
82
+ img: [
83
+ "src",
84
+ "alt",
85
+ "title",
86
+ "width",
87
+ "height",
88
+ "loading",
89
+ "decoding"
90
+ ],
91
+ source: [
92
+ "src",
93
+ "srcset",
94
+ "media",
95
+ "type",
96
+ "width",
97
+ "height"
98
+ ],
99
+ td: ["colspan", "rowspan"],
100
+ th: [
101
+ "colspan",
102
+ "rowspan",
103
+ "scope"
104
+ ]
105
+ },
106
+ allowedSchemes: [
107
+ "http",
108
+ "https",
109
+ "mailto"
110
+ ],
111
+ allowedSchemesByTag: {
112
+ img: ["http", "https"],
113
+ source: ["http", "https"]
114
+ },
115
+ allowProtocolRelative: false,
116
+ enforceHtmlBoundary: true,
117
+ allowedStyles: { "*": {
118
+ display: [/^(?:block|inline|inline-block|flex|inline-flex|grid|none)$/],
119
+ "flex-direction": [/^(?:row|row-reverse|column|column-reverse)$/],
120
+ "flex-wrap": [/^(?:nowrap|wrap|wrap-reverse)$/],
121
+ "align-items": [/^(?:normal|stretch|center|start|end|flex-start|flex-end|baseline)$/],
122
+ "justify-content": [/^(?:normal|stretch|center|start|end|flex-start|flex-end|space-between|space-around|space-evenly)$/],
123
+ "grid-template-columns": [safeCssValue],
124
+ "grid-template-rows": [safeCssValue],
125
+ "grid-column": [safeCssValue],
126
+ "grid-row": [safeCssValue],
127
+ gap: [safeLength],
128
+ "column-gap": [safeLength],
129
+ "row-gap": [safeLength],
130
+ width: [safeLength],
131
+ "min-width": [safeLength],
132
+ "max-width": [safeLength],
133
+ height: [safeLength],
134
+ "min-height": [safeLength],
135
+ "max-height": [safeLength],
136
+ margin: [safeCssValue],
137
+ "margin-top": [safeLength],
138
+ "margin-right": [safeLength],
139
+ "margin-bottom": [safeLength],
140
+ "margin-left": [safeLength],
141
+ padding: [safeCssValue],
142
+ "padding-top": [safeLength],
143
+ "padding-right": [safeLength],
144
+ "padding-bottom": [safeLength],
145
+ "padding-left": [safeLength],
146
+ color: [safeCssValue],
147
+ background: [safeCssValue],
148
+ "background-color": [safeCssValue],
149
+ border: [safeCssValue],
150
+ "border-bottom": [safeCssValue],
151
+ "border-left": [safeCssValue],
152
+ "border-right": [safeCssValue],
153
+ "border-top": [safeCssValue],
154
+ "border-color": [safeCssValue],
155
+ "border-width": [safeLength],
156
+ "border-style": [/^(?:none|solid|dashed|dotted)$/],
157
+ "border-radius": [safeLength],
158
+ "font-size": [safeLength],
159
+ "font-family": [safeCssValue],
160
+ "font-weight": [/^(?:normal|bold|[1-9]00)$/],
161
+ "line-height": [safeCssValue],
162
+ "text-align": [/^(?:start|end|left|right|center|justify)$/],
163
+ "text-decoration": [safeCssValue],
164
+ "text-transform": [/^(?:none|capitalize|uppercase|lowercase)$/],
165
+ "object-fit": [/^(?:contain|cover|fill|none|scale-down)$/],
166
+ overflow: [/^(?:visible|hidden|clip|scroll|auto)$/],
167
+ "overflow-x": [/^(?:visible|hidden|clip|scroll|auto)$/],
168
+ "overflow-y": [/^(?:visible|hidden|clip|scroll|auto)$/],
169
+ opacity: [/^(?:0(?:\.\d+)?|1(?:\.0+)?)$/]
170
+ } },
171
+ transformTags: { a: (tagName, attribs) => ({
172
+ tagName,
173
+ attribs: attribs.target === "_blank" ? {
174
+ ...attribs,
175
+ rel: "noopener noreferrer"
176
+ } : attribs
177
+ }) }
178
+ });
179
+ /**
180
+ * Sanitize a CMS Page's stored body for injection. Lives here, next to the sanitizer it wraps,
181
+ * rather than in `busabase-cms-sdk/integration`: `sanitizeLandingPageHtml` drags in remark, rehype
182
+ * and sanitize-html, and only the component that actually injects the HTML should pay for that.
183
+ *
184
+ * Call this immediately before injecting — the sanitize step must never move up to the caller.
185
+ */
186
+ const getSanitizedCmsPageBody = (page) => sanitizeLandingPageHtml(page.body);
187
+ //#endregion
188
+ export { SafeMarkdown, getSafeMarkdownToc, getSanitizedCmsPageBody, sanitizeLandingPageHtml };
@@ -0,0 +1,33 @@
1
+ import { C as taxonomyFieldsDTOSchema, S as tagVOSchema, _ as pageVOSchema, a as PageFieldsDTO, b as relationFieldsDTOSchema, c as PostVO, d as TagVO, f as attachmentFieldsDTOSchema, g as pageFieldsDTOSchema, h as categoryVOSchema, i as CategoryVO, l as RelationFieldsDTO, m as categoryFieldsDTOSchema, n as AttachmentVO, o as PageVO, p as attachmentVOSchema, r as CategoryFieldsDTO, s as PostFieldsDTO, t as AttachmentFieldsDTO, u as TagFieldsDTO, v as postFieldsDTOSchema, x as tagFieldsDTOSchema, y as postVOSchema } from "./types-QKFW2jvT.js";
2
+ import { $ as BusabaseCmsSetupError, A as BUSABASE_CMS_SCHEMA_PROFILES, B as i18nName, C as createBusabaseCms, D as mapPublishedPostRecord, E as mapPublishedPageRecord, F as BusabaseCmsFieldDefinition, G as BusabaseCmsFieldOptions, H as BusabaseCmsBase, I as BusabaseCmsFieldsOverride, J as BusabaseCmsSource, K as BusabaseCmsNode, L as BusabaseCmsFolderMetadata, M as BusabaseCmsBaseDefinition, N as BusabaseCmsBaseIds, O as BUSABASE_CMS_METADATA_KEY, P as BusabaseCmsBaseRole, Q as BusabaseCmsSchemaDriftError, R as BusabaseCmsSchemaProfile, S as InvalidCmsRecordIssue, T as mapActiveTagRecord, U as BusabaseCmsClient, V as replaceField, W as BusabaseCmsField, X as createBusabaseCmsSourceFromConfig, Y as createBusabaseCmsSource, Z as BusabaseCmsError, _ as CmsRecordKind, a as buildCmsCanonicalPath, b as DEFAULT_POSTS_BASE_SLUG, c as filterCmsPostsByTaxonomy, d as normalizeCmsPath, f as parseCmsCanonicalPath, g as BusabaseCmsTaxonomyCollection, h as BusabaseCmsPathCollection, i as CmsTaxonomyKind, j as BUSABASE_CMS_SCHEMA_VERSION, k as BUSABASE_CMS_ROLES, l as isCmsBlogPostPath, m as BusabaseCmsOptions, n as CmsCanonicalPathOptions, o as buildCmsTaxonomyArchivePath, p as BusabaseCms, q as BusabaseCmsRecord, r as CmsPathHelpers, s as createCmsPathHelpers, t as CmsCanonicalPath, u as isCmsContentForLocale, v as DEFAULT_CATEGORIES_BASE_SLUG, w as mapActiveCategoryRecord, x as DEFAULT_TAGS_BASE_SLUG, y as DEFAULT_PAGES_BASE_SLUG, z as getBusabaseCmsBaseDefinition } from "./routing-VU56uuJ0.js";
3
+ //#region src/fallback.d.ts
4
+ /**
5
+ * The "read from Busabase, fall back to bundled content on any failure" wrapper — every app
6
+ * consuming busabase-cms re-implemented this same try/catch/console.warn a couple of times
7
+ * (once for list-shaped reads, once for single-record reads). One generic version, one place
8
+ * to change the log format.
9
+ */
10
+ declare const readCmsOrFallback: <T>(operation: (() => Promise<T>) | null | undefined, fallback: T, label: string) => Promise<T>;
11
+ //#endregion
12
+ //#region src/links.d.ts
13
+ /** Keep rendered CMS attachments on protocols browsers can navigate safely. */
14
+ declare const getSafeCmsExternalUrl: (value: string) => string | null;
15
+ //#endregion
16
+ //#region src/provision.d.ts
17
+ /**
18
+ * `profile` is a caller-chosen label persisted in Folder metadata and used only for
19
+ * mismatch detection (see `assertMetadataProfile`) — the SDK never branches on its value.
20
+ * A caller using a profile other than `"standard"` must supply `fieldsOverride` to describe
21
+ * that profile's actual field shape, and may list `legacyOptionalFields` to grandfather in a
22
+ * field that predates a later required-field tightening.
23
+ */
24
+ interface BusabaseCmsSchemaConfig {
25
+ profile: BusabaseCmsSchemaProfile;
26
+ fieldsOverride?: BusabaseCmsFieldsOverride;
27
+ legacyOptionalFields?: Array<{
28
+ role: BusabaseCmsBaseRole;
29
+ slug: string;
30
+ }>;
31
+ }
32
+ //#endregion
33
+ export { AttachmentFieldsDTO, AttachmentVO, BUSABASE_CMS_METADATA_KEY, BUSABASE_CMS_ROLES, BUSABASE_CMS_SCHEMA_PROFILES, BUSABASE_CMS_SCHEMA_VERSION, type BusabaseCms, type BusabaseCmsBase, type BusabaseCmsBaseDefinition, type BusabaseCmsBaseIds, type BusabaseCmsBaseRole, type BusabaseCmsClient, BusabaseCmsError, type BusabaseCmsField, type BusabaseCmsFieldDefinition, type BusabaseCmsFieldOptions, type BusabaseCmsFieldsOverride, type BusabaseCmsFolderMetadata, type BusabaseCmsNode, type BusabaseCmsOptions, type BusabaseCmsPathCollection, type BusabaseCmsRecord, type BusabaseCmsSchemaConfig, BusabaseCmsSchemaDriftError, type BusabaseCmsSchemaProfile, BusabaseCmsSetupError, type BusabaseCmsSource, type BusabaseCmsTaxonomyCollection, CategoryFieldsDTO, CategoryVO, CmsCanonicalPath, CmsCanonicalPathOptions, CmsPathHelpers, type CmsRecordKind, CmsTaxonomyKind, DEFAULT_CATEGORIES_BASE_SLUG, DEFAULT_PAGES_BASE_SLUG, DEFAULT_POSTS_BASE_SLUG, DEFAULT_TAGS_BASE_SLUG, type InvalidCmsRecordIssue, PageFieldsDTO, PageVO, PostFieldsDTO, PostVO, RelationFieldsDTO, TagFieldsDTO, TagVO, attachmentFieldsDTOSchema, attachmentVOSchema, buildCmsCanonicalPath, buildCmsTaxonomyArchivePath, i18nName as busabaseCmsFieldI18nName, categoryFieldsDTOSchema, categoryVOSchema, createBusabaseCms, createBusabaseCmsSource, createBusabaseCmsSourceFromConfig, createCmsPathHelpers, filterCmsPostsByTaxonomy, getBusabaseCmsBaseDefinition, getSafeCmsExternalUrl, isCmsBlogPostPath, isCmsContentForLocale, mapActiveCategoryRecord, mapActiveTagRecord, mapPublishedPageRecord, mapPublishedPostRecord, normalizeCmsPath, pageFieldsDTOSchema, pageVOSchema, parseCmsCanonicalPath, postFieldsDTOSchema, postVOSchema, readCmsOrFallback, relationFieldsDTOSchema, replaceField, tagFieldsDTOSchema, tagVOSchema, taxonomyFieldsDTOSchema };
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ import { A as replaceField, C as createBusabaseCmsSourceFromConfig, D as BUSABASE_CMS_SCHEMA_VERSION, E as BUSABASE_CMS_SCHEMA_PROFILES, M as BusabaseCmsSchemaDriftError, N as BusabaseCmsSetupError, O as getBusabaseCmsBaseDefinition, S as createBusabaseCmsSource, T as BUSABASE_CMS_ROLES, _ as postVOSchema, a as createBusabaseCms, b as tagVOSchema, c as mapPublishedPageRecord, d as attachmentVOSchema, f as categoryFieldsDTOSchema, g as postFieldsDTOSchema, h as pageVOSchema, i as DEFAULT_TAGS_BASE_SLUG, j as BusabaseCmsError, k as i18nName, l as mapPublishedPostRecord, m as pageFieldsDTOSchema, n as DEFAULT_PAGES_BASE_SLUG, o as mapActiveCategoryRecord, p as categoryVOSchema, r as DEFAULT_POSTS_BASE_SLUG, s as mapActiveTagRecord, t as DEFAULT_CATEGORIES_BASE_SLUG, u as attachmentFieldsDTOSchema, v as relationFieldsDTOSchema, w as BUSABASE_CMS_METADATA_KEY, x as taxonomyFieldsDTOSchema, y as tagFieldsDTOSchema } from "./content-BXgsWQjQ.js";
2
+ import { a as isCmsBlogPostPath, c as parseCmsCanonicalPath, i as filterCmsPostsByTaxonomy, l as readCmsOrFallback, n as buildCmsTaxonomyArchivePath, o as isCmsContentForLocale, r as createCmsPathHelpers, s as normalizeCmsPath, t as buildCmsCanonicalPath } from "./routing-C3lkVAZ8.js";
3
+ //#region src/links.ts
4
+ /** Keep rendered CMS attachments on protocols browsers can navigate safely. */
5
+ const getSafeCmsExternalUrl = (value) => {
6
+ try {
7
+ const url = new URL(value);
8
+ return url.protocol === "http:" || url.protocol === "https:" ? url.toString() : null;
9
+ } catch {
10
+ return null;
11
+ }
12
+ };
13
+ //#endregion
14
+ export { BUSABASE_CMS_METADATA_KEY, BUSABASE_CMS_ROLES, BUSABASE_CMS_SCHEMA_PROFILES, BUSABASE_CMS_SCHEMA_VERSION, BusabaseCmsError, BusabaseCmsSchemaDriftError, BusabaseCmsSetupError, DEFAULT_CATEGORIES_BASE_SLUG, DEFAULT_PAGES_BASE_SLUG, DEFAULT_POSTS_BASE_SLUG, DEFAULT_TAGS_BASE_SLUG, attachmentFieldsDTOSchema, attachmentVOSchema, buildCmsCanonicalPath, buildCmsTaxonomyArchivePath, i18nName as busabaseCmsFieldI18nName, categoryFieldsDTOSchema, categoryVOSchema, createBusabaseCms, createBusabaseCmsSource, createBusabaseCmsSourceFromConfig, createCmsPathHelpers, filterCmsPostsByTaxonomy, getBusabaseCmsBaseDefinition, getSafeCmsExternalUrl, isCmsBlogPostPath, isCmsContentForLocale, mapActiveCategoryRecord, mapActiveTagRecord, mapPublishedPageRecord, mapPublishedPostRecord, normalizeCmsPath, pageFieldsDTOSchema, pageVOSchema, parseCmsCanonicalPath, postFieldsDTOSchema, postVOSchema, readCmsOrFallback, relationFieldsDTOSchema, replaceField, tagFieldsDTOSchema, tagVOSchema, taxonomyFieldsDTOSchema };
@@ -0,0 +1,251 @@
1
+ import { c as PostVO, d as TagVO, i as CategoryVO, o as PageVO } from "../types-QKFW2jvT.js";
2
+ import { S as InvalidCmsRecordIssue, i as CmsTaxonomyKind, p as BusabaseCms, r as CmsPathHelpers, t as CmsCanonicalPath } from "../routing-VU56uuJ0.js";
3
+ import "server-only";
4
+ //#region src/integration/config.d.ts
5
+ /** Next data-cache `revalidate` used when an app does not override it. */
6
+ declare const DEFAULT_CMS_REVALIDATE_SECONDS = 300;
7
+ /**
8
+ * The four Base slugs used ONLY when `BUSABASE_CMS_FOLDER_ID` is unset. Without a Folder id,
9
+ * `createBusabaseCms` otherwise falls back to the SDK's own generic, SHARED default Base slugs
10
+ * (busabase-cms-posts / -pages / -categories / -tags), which would silently mix content with
11
+ * any other app pointed at the same Busabase space without a Folder id configured — so an app
12
+ * that wants its own Bases MUST pass its own app-prefixed slugs here.
13
+ *
14
+ * Each slug can still be overridden at deploy time by `BUSABASE_CMS_{POSTS,PAGES,CATEGORIES,
15
+ * TAGS}_BASE_SLUG`; what is configured here is only the default.
16
+ */
17
+ interface CmsIntegrationBaseSlugs {
18
+ posts: string;
19
+ pages: string;
20
+ categories: string;
21
+ tags: string;
22
+ }
23
+ interface CmsIntegrationCacheTags {
24
+ posts?: string[];
25
+ pages?: string[];
26
+ categories?: string[];
27
+ tags?: string[];
28
+ }
29
+ interface CmsIntegrationCacheConfig {
30
+ /** Seconds; `false` disables time-based revalidation. Defaults to 300. */
31
+ revalidate?: number | false;
32
+ /**
33
+ * `revalidateTag` targets per collection. Defaults to
34
+ * `["<cacheNamespace>:cms-<collection>"]`.
35
+ */
36
+ tags?: CmsIntegrationCacheTags;
37
+ }
38
+ interface CmsIntegrationConfig {
39
+ /** Human-readable app name, used only in degrade-safe warning log labels (e.g. "ProductReady"). */
40
+ appLabel: string;
41
+ /**
42
+ * Stable, unique-per-app slug (e.g. "productready", "sandock-cloud", "busabase-cloud").
43
+ *
44
+ * This is the first discriminator in the Next data-cache key prefix and the default
45
+ * `revalidateTag` namespace. It exists so that two apps deployed against the SAME Busabase
46
+ * host + space can never read each other's cached CMS entries, no matter how their Folder id
47
+ * or Base slugs are (mis)configured.
48
+ */
49
+ cacheNamespace: string;
50
+ supportedLocales: readonly string[];
51
+ defaultLocale: string;
52
+ /**
53
+ * Caller-owned profile label (see src/schema.ts) — MUST be unique per app so a shared
54
+ * Busabase Folder never mismatches the apps' provisioned field shapes.
55
+ */
56
+ schemaProfile: string;
57
+ /** Default Base slugs, used without a Folder id and overridable per deploy (see above). */
58
+ baseSlugs: CmsIntegrationBaseSlugs;
59
+ cache?: CmsIntegrationCacheConfig;
60
+ /** Passed straight through to `createBusabaseCms`. Leave unset for the SDK default. */
61
+ invalidRecords?: "skip" | "throw";
62
+ onInvalidRecord?: (issue: InvalidCmsRecordIssue) => void;
63
+ }
64
+ /** The fully-resolved, env-derived CMS target. `null` when the integration is off. */
65
+ interface ResolvedCmsConfig {
66
+ baseUrl: string;
67
+ apiKey: string;
68
+ spaceId: string;
69
+ folderId: string | null;
70
+ baseSlugs: CmsIntegrationBaseSlugs;
71
+ }
72
+ /**
73
+ * Pure `BUSABASE_CMS_*` env-var gate. All three of host/key/space are required: you genuinely
74
+ * cannot talk to a Busabase space without an API key, so a partial configuration is treated as
75
+ * "off" rather than as a guaranteed-failing read on every request.
76
+ *
77
+ * Re-reads the environment on every call — deliberately never memoized, unlike the client.
78
+ */
79
+ declare const readCmsEnvConfig: (defaultBaseSlugs: CmsIntegrationBaseSlugs, env?: Record<string, string | undefined>) => ResolvedCmsConfig | null;
80
+ //#endregion
81
+ //#region src/integration/client.d.ts
82
+ /**
83
+ * Explicit Next data-cache key prefix. Two reasons it is never left to the SDK's default:
84
+ *
85
+ * 1. App identity — the SDK's own default prefix (see `resolveBusabaseCmsCacheKeyPrefix`) is
86
+ * derived purely from the CMS target (host / space / folder / profile / Base slugs) and
87
+ * carries no app identity at all. `cacheNamespace` (unique per app) is the first element
88
+ * here, so no two apps can ever share an entry even when they read the very same Bases.
89
+ * 2. Re-pointing safety — folder id, schema profile and all four Base slugs are part of the
90
+ * key, so changing any of them at deploy time starts from a cold cache instead of serving
91
+ * content read out of a different Base.
92
+ *
93
+ * The API key is deliberately NOT part of the key (it is a secret, and it does not change
94
+ * which records are addressed).
95
+ */
96
+ declare const buildCmsCacheKeyPrefix: (config: Pick<CmsIntegrationConfig, "cacheNamespace" | "schemaProfile">, resolved: ResolvedCmsConfig) => string[];
97
+ /** `revalidateTag` targets per collection, defaulting to `<cacheNamespace>:cms-<collection>`. */
98
+ declare const resolveCmsCacheTags: (config: Pick<CmsIntegrationConfig, "cacheNamespace" | "cache">) => Required<CmsIntegrationCacheTags>;
99
+ interface CmsClientProvider {
100
+ /** `null` when the integration is unconfigured — callers degrade to bundled content. */
101
+ getCms: () => BusabaseCms | null;
102
+ /** Throws when the integration is unconfigured. Prefer `getCms` + `readCmsOrFallback`. */
103
+ requireCms: () => BusabaseCms;
104
+ getCmsConfig: () => ResolvedCmsConfig | null;
105
+ isBusabaseCmsEnabled: () => boolean;
106
+ }
107
+ declare const createCmsClientProvider: (config: CmsIntegrationConfig) => CmsClientProvider;
108
+ //#endregion
109
+ //#region src/integration/pages.d.ts
110
+ interface CmsPageReads {
111
+ /** Raw reads — throw when the integration is unconfigured. Prefer the `*OrFallback` variants. */
112
+ listBusabaseLandingPages: () => Promise<PageVO[]>;
113
+ getBusabaseLandingPageByPath: (path: string) => Promise<PageVO | null>;
114
+ listBusabaseLandingPagesOrFallback: () => Promise<PageVO[]>;
115
+ getBusabaseLandingPageByPathOrFallback: (path: string) => Promise<PageVO | null>;
116
+ }
117
+ declare const createCmsPageReads: ({ getCms, requireCms }: CmsClientProvider, appLabel: string) => CmsPageReads;
118
+ /** The argument object an app's own `generatePageMetadata` helper accepts. */
119
+ interface CmsPageMetadataOptions {
120
+ title: string;
121
+ description: string;
122
+ path: string;
123
+ lang: string;
124
+ type: "website" | "article";
125
+ }
126
+ /** The slice of a `CmsIntegration` the Page helpers depend on. */
127
+ interface CmsPageHelpersIntegration {
128
+ buildCmsPath: (locale: string, path: string | readonly string[]) => string | null;
129
+ parseCmsPath: (path: string) => CmsCanonicalPath | null;
130
+ isCmsContentForLocale: (item: {
131
+ locale: string;
132
+ path: string;
133
+ }, locale: string) => boolean;
134
+ getBusabaseLandingPageByPathOrFallback: (path: string) => Promise<PageVO | null>;
135
+ }
136
+ interface CmsPageHelpersOptions<TMetadata> {
137
+ integration: CmsPageHelpersIntegration;
138
+ /**
139
+ * The app's own metadata helper. Injected and typed structurally so the SDK does not depend
140
+ * on any app's site config.
141
+ */
142
+ generatePageMetadata: (options: CmsPageMetadataOptions) => TMetadata;
143
+ }
144
+ interface CmsPageHelpers<TMetadata> {
145
+ getCmsPageForRequest: (lang: string, path: string | readonly string[]) => Promise<PageVO | null>;
146
+ generateCmsPageMetadata: (page: PageVO, lang: string) => TMetadata | Record<string, never>;
147
+ }
148
+ declare const createCmsPageHelpers: <TMetadata>({ integration, generatePageMetadata }: CmsPageHelpersOptions<TMetadata>) => CmsPageHelpers<TMetadata>;
149
+ //#endregion
150
+ //#region src/integration/posts.d.ts
151
+ interface CmsPostReads {
152
+ /** Raw reads — throw when the integration is unconfigured. Prefer the `*OrFallback` variants. */
153
+ listBusabaseBlogPosts: () => Promise<PostVO[]>;
154
+ getBusabaseBlogPostByPath: (path: string) => Promise<PostVO | null>;
155
+ listBusabaseBlogPostsOrFallback: () => Promise<PostVO[]>;
156
+ getBusabaseBlogPostByPathOrFallback: (path: string) => Promise<PostVO | null>;
157
+ }
158
+ declare const createCmsPostReads: ({ getCms, requireCms }: CmsClientProvider, appLabel: string) => CmsPostReads;
159
+ interface BlogCardContent {
160
+ url: string;
161
+ title: string;
162
+ description?: string;
163
+ date?: string | Date;
164
+ author?: string;
165
+ image?: string;
166
+ }
167
+ /**
168
+ * Merge Post card sources in priority order, using canonical paths as identity — the earliest
169
+ * source to claim a path wins. Lets an app render CMS Posts and bundled MDX posts in one grid
170
+ * without a CMS post and its local original showing up twice.
171
+ */
172
+ declare const mergeBlogCardsByPath: (...sources: readonly BlogCardContent[][]) => BlogCardContent[];
173
+ /**
174
+ * Structural shape of the fumadocs `loader()` result the resolver needs. Typed structurally
175
+ * (rather than importing an app's `~/lib/source`) so the SDK stays free of app internals;
176
+ * `TPage` flows the app's own local-MDX page type through untouched.
177
+ */
178
+ interface LocalPostSourceLike<TPage> {
179
+ getPage: (slugs: string[], locale: string) => TPage | undefined;
180
+ }
181
+ interface ResolvedCmsPostPageBase {
182
+ requestedLocale: string;
183
+ contentLocale: string;
184
+ isLocaleFallback: boolean;
185
+ canonicalPath: string;
186
+ }
187
+ type ResolvedCmsPostPage<TPage> = (ResolvedCmsPostPageBase & {
188
+ source: "busabase";
189
+ content: PostVO;
190
+ }) | (ResolvedCmsPostPageBase & {
191
+ source: "local";
192
+ content: TPage;
193
+ });
194
+ interface CmsPostResolverDependencies<TPage> {
195
+ getCmsPost: (locale: string, slugs: string[]) => Promise<PostVO | null>;
196
+ getLocalPost: (locale: string, slugs: string[]) => TPage | undefined;
197
+ }
198
+ /** The slice of a `CmsIntegration` the Post resolver depends on. */
199
+ interface CmsPostResolverIntegration {
200
+ buildCmsPath: (locale: string, path: string | readonly string[]) => string | null;
201
+ parseCmsPath: (path: string) => CmsCanonicalPath | null;
202
+ isCmsContentForLocale: (item: {
203
+ locale: string;
204
+ path: string;
205
+ }, locale: string) => boolean;
206
+ getBusabaseBlogPostByPathOrFallback: (path: string) => Promise<PostVO | null>;
207
+ }
208
+ interface CmsPostResolverOptions<TPage> {
209
+ integration: CmsPostResolverIntegration;
210
+ localSource: LocalPostSourceLike<TPage>;
211
+ }
212
+ interface CmsPostResolver<TPage> {
213
+ resolvePostPage: (requestedLocale: string, slugPath: string) => Promise<ResolvedCmsPostPage<TPage> | null>;
214
+ resolvePostPageWithDependencies: (requestedLocale: string, slugPath: string, dependencies: CmsPostResolverDependencies<TPage>) => Promise<ResolvedCmsPostPage<TPage> | null>;
215
+ }
216
+ declare const createCmsPostResolver: <TPage>({ integration, localSource }: CmsPostResolverOptions<TPage>) => CmsPostResolver<TPage>;
217
+ //#endregion
218
+ //#region src/integration/taxonomy.d.ts
219
+ interface CmsTaxonomyReads {
220
+ /** Raw reads — throw when the integration is unconfigured. Prefer the `*OrFallback` variants. */
221
+ listBusabaseCategories: () => Promise<CategoryVO[]>;
222
+ listBusabaseCategoriesOrFallback: () => Promise<CategoryVO[]>;
223
+ listBusabaseTags: () => Promise<TagVO[]>;
224
+ listBusabaseTagsOrFallback: () => Promise<TagVO[]>;
225
+ }
226
+ declare const createCmsTaxonomyReads: ({ getCms, requireCms }: CmsClientProvider, appLabel: string) => CmsTaxonomyReads;
227
+ //#endregion
228
+ //#region src/integration/index.d.ts
229
+ /**
230
+ * The whole per-app Busabase CMS surface, bound once to one app's locale / schema / Base
231
+ * config: canonical-path helpers, the env gate, and the Post / Page / taxonomy reads.
232
+ */
233
+ interface CmsIntegration extends CmsPostReads, CmsPageReads, CmsTaxonomyReads {
234
+ cmsPathHelpers: CmsPathHelpers;
235
+ buildCmsPath: (locale: string, path: string | readonly string[]) => string | null;
236
+ parseCmsPath: (path: string) => CmsCanonicalPath | null;
237
+ isCmsContentForLocale: (item: {
238
+ locale: string;
239
+ path: string;
240
+ }, locale: string) => boolean;
241
+ buildCmsTaxonomyArchivePath: (kind: CmsTaxonomyKind, taxonomy: {
242
+ locale: string;
243
+ slug: string;
244
+ }) => string | null;
245
+ isBusabaseCmsEnabled: () => boolean;
246
+ /** Re-reads the environment on every call — never memoized, unlike the client itself. */
247
+ getCmsConfig: () => ResolvedCmsConfig | null;
248
+ }
249
+ declare const createCmsIntegration: (config: CmsIntegrationConfig) => CmsIntegration;
250
+ //#endregion
251
+ export { type BlogCardContent, type CmsClientProvider, CmsIntegration, type CmsIntegrationBaseSlugs, type CmsIntegrationCacheConfig, type CmsIntegrationCacheTags, type CmsIntegrationConfig, type CmsPageHelpers, type CmsPageHelpersIntegration, type CmsPageHelpersOptions, type CmsPageMetadataOptions, type CmsPageReads, type CmsPostReads, type CmsPostResolver, type CmsPostResolverDependencies, type CmsPostResolverIntegration, type CmsPostResolverOptions, type CmsTaxonomyReads, DEFAULT_CMS_REVALIDATE_SECONDS, type LocalPostSourceLike, type ResolvedCmsConfig, type ResolvedCmsPostPage, buildCmsCacheKeyPrefix, createCmsClientProvider, createCmsIntegration, createCmsPageHelpers, createCmsPageReads, createCmsPostReads, createCmsPostResolver, createCmsTaxonomyReads, mergeBlogCardsByPath, readCmsEnvConfig, resolveCmsCacheTags };