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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Busabase
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,145 @@
1
+ # busabase-cms-sdk
2
+
3
+ > **This is an npm SDK package, not a hosted system.** You install it into your own Next.js app —
4
+ > `"busabase-cms-sdk": "workspace:*"` inside this monorepo, or the published `busabase-cms-sdk` package
5
+ > from npm outside it — and call its functions from your own server code. It is not a standalone
6
+ > service, product, or thing you deploy on its own; it's a client library that talks to a Busabase
7
+ > workspace you already run or have access to.
8
+
9
+ Typed CMS adapters for the standard Busabase **Posts**, **Pages**, **Categories**, and **Tags**
10
+ Bases. The core API is framework-neutral; optional entry points add Next.js caching and safe
11
+ Fumadocs rendering.
12
+
13
+ ```ts
14
+ import { createBusabaseCms } from "busabase-cms-sdk";
15
+
16
+ const cms = createBusabaseCms({
17
+ config: {
18
+ baseUrl: process.env.BUSABASE_BASE_URL,
19
+ apiKey: process.env.BUSABASE_API_KEY,
20
+ spaceId: process.env.BUSABASE_SPACE_ID,
21
+ },
22
+ folderId: process.env.BUSABASE_CMS_FOLDER_ID,
23
+ lazyCreate: true,
24
+ schemaProfile: "standard",
25
+ });
26
+
27
+ const posts = await cms.posts.list();
28
+ const page = await cms.pages.getByPath("/use-cases/automation");
29
+ const categories = await cms.categories.list();
30
+ const tag = await cms.tags.getBySlug("nextjs");
31
+ ```
32
+
33
+ `folderId` is the preferred setup. The first read discovers direct child Bases and stores their
34
+ stable IDs in the Folder's `metadata.busabaseCms` namespace. Base display names and slugs may then
35
+ be renamed without breaking reads. With `lazyCreate: true`, missing Bases and fields are directly
36
+ materialized in Categories, Tags, Posts, Pages order; relation fields point to the resolved
37
+ taxonomy Base IDs. Existing extra fields are preserved. Incompatible field type, required state,
38
+ or critical options produce `BusabaseCmsSchemaDriftError` instead of a destructive conversion.
39
+ Provisioning first preflights every existing Base and field. If any existing schema drift is found,
40
+ it performs no Base, field, or metadata write; missing fields are applied only after that complete
41
+ read-only pass succeeds.
42
+
43
+ `schemaProfile` defaults to `standard`. Use `buda` for Buda's existing legacy contract: text cover
44
+ URLs, image-only attachments, required Page HTML and Hero JSON, and Buda-specific content fields.
45
+ Existing Buda Folders with the legacy optional Page body are explicitly accepted during adoption,
46
+ but newly created Buda Pages use the required body contract. The selected profile is stored beside
47
+ the Base IDs in `metadata.busabaseCms`; metadata from older versions without a profile is treated as
48
+ `standard`, and a later profile mismatch fails before any write. The Next.js cache namespace
49
+ includes both the Folder ID and profile.
50
+
51
+ Attachment validation is semantic rather than string-based. An explicit PNG/JPEG/WebP/SVG policy
52
+ is compatible with the standard `image/*` contract, and an existing policy may be stricter through
53
+ smaller file/count limits or fewer permitted MIME types. A broader policy still produces schema
54
+ drift. Likewise, an existing required field is compatible with an optional reader expectation, but
55
+ an optional field cannot satisfy a required provisioning contract outside the documented Buda Page
56
+ adoption exception. Relation targets and field types must still match exactly.
57
+
58
+ Schema bootstrap requires a write-capable API key and uses `autoMerge` for the already-approved
59
+ structure, leaving an `autoMerged` audit record without creating an approval task. This exception
60
+ applies only to schema bootstrap. Content creation and edits remain normal Busabase
61
+ ChangeRequests that require human review and merge.
62
+
63
+ Without `lazyCreate`, Folder mode never creates Bases or fields. It still writes the stable ID
64
+ mapping once when it adopts an existing standard four-Base structure, so Folder mode always needs
65
+ metadata write access; missing setup throws `BusabaseCmsSetupError`. Adoption only trusts standard
66
+ names, the Folder-derived slugs, or the legacy standard slugs, so an unrelated Base with similar
67
+ fields is never modified. A custom `source` may also provision lazily when it implements all
68
+ optional node, Base, field, and metadata methods; incomplete sources fail eagerly.
69
+
70
+ The legacy slug mode remains available when `folderId` is omitted. Its defaults are
71
+ `busabase-cms-posts`, `busabase-cms-pages`, `busabase-cms-categories`, and `busabase-cms-tags`;
72
+ override them through `baseSlugs` for an existing site-specific CMS such as Buda. Posts and Pages
73
+ must be canonical, active records whose `status` field is `published`; Categories and Tags only
74
+ need to be active. Invalid records are skipped with a warning by default; use
75
+ `invalidRecords: "throw"` for strict pipelines.
76
+
77
+ Relation columns are normalized from Busabase's single record id or record id array representation
78
+ into `categoryIds` and `tagIds`. Every VO also retains `rawFields` so an application adapter can
79
+ parse site-specific columns without widening the shared CMS contract.
80
+
81
+ The shared Page reader accepts the standard `template` values (`standard`, `landing`, `product`,
82
+ and `use-case`) as strings. The standard provisioning profile requires the field to match the live
83
+ Busabase CMS schema. The Buda profile omits it. The Buda adapter uses that profile when
84
+ `BUSABASE_CMS_FOLDER_ID` is configured; otherwise it keeps its four `buda-*` Base slugs. It parses
85
+ Buda-only fields such as `keywords`, legacy `meta-*` values, and structured landing-page sections
86
+ from `rawFields`.
87
+
88
+ ## Next.js
89
+
90
+ ```ts
91
+ import { createCachedBusabaseCms } from "busabase-cms-sdk/next";
92
+
93
+ export const cms = createCachedBusabaseCms({}, { revalidate: 300 });
94
+ ```
95
+
96
+ ## Fumadocs
97
+
98
+ ```tsx
99
+ import { SafeMarkdown, getSafeMarkdownToc, sanitizeLandingPageHtml } from "busabase-cms-sdk/fumadocs";
100
+
101
+ const toc = await getSafeMarkdownToc(post.body);
102
+ const body = await SafeMarkdown({ children: post.body });
103
+ const safeHtml = sanitizeLandingPageHtml(page.body);
104
+ ```
105
+
106
+ Stored content is always treated as untrusted. `SafeMarkdown` does not execute MDX or pass raw HTML,
107
+ and Page HTML must be sanitized before rendering.
108
+
109
+ ## Next.js integration layer
110
+
111
+ `busabase-cms-sdk/integration` is the batteries-included glue between this SDK and a Next.js app:
112
+ the `BUSABASE_CMS_*` env gate, the memoized cached client with a collision-proof cache key, the
113
+ Post / Page / taxonomy reads with degrade-safe fallbacks, cross-locale Post resolution, and Page
114
+ metadata generation. Bind it once per app.
115
+
116
+ ```ts
117
+ import { createCmsIntegration } from "busabase-cms-sdk/integration";
118
+
119
+ export const cms = createCmsIntegration({
120
+ appLabel: "ProductReady", // only used in degrade-safe warning logs
121
+ cacheNamespace: "productready", // unique per app: cache key prefix + revalidateTag namespace
122
+ supportedLocales: ["en", "zh-CN", "ja"],
123
+ defaultLocale: "en",
124
+ schemaProfile: "productready",
125
+ baseSlugs: {
126
+ posts: "productready-blog-posts",
127
+ pages: "productready-landing-pages",
128
+ categories: "productready-categories",
129
+ tags: "productready-tags",
130
+ },
131
+ });
132
+
133
+ // Reads degrade to bundled content instead of throwing; with the env vars unset they make no
134
+ // network attempt at all.
135
+ const posts = await cms.listBusabaseBlogPostsOrFallback();
136
+ ```
137
+
138
+ It reads `BUSABASE_CMS_BASE_URL`, `BUSABASE_CMS_API_KEY` and `BUSABASE_CMS_SPACE_ID` (all three
139
+ required — a partial configuration counts as "off"), plus the optional `BUSABASE_CMS_FOLDER_ID`
140
+ and the four `BUSABASE_CMS_{POSTS,PAGES,CATEGORIES,TAGS}_BASE_SLUG` per-deploy overrides.
141
+
142
+ `createCmsPostResolver` adds the "CMS Post → bundled MDX → English fallback" cascade for
143
+ `/blog/[...slug]` routes, and `createCmsPageHelpers` resolves a Page plus its metadata. The Page
144
+ body sanitizer stays in `busabase-cms-sdk/fumadocs` (`getSanitizedCmsPageBody`) so that consumers of
145
+ `busabase-cms-sdk/integration` do not pull remark/rehype/sanitize-html into their bundle.