Skip to content

Commit ad7fa16

Browse files
authored
docs: Document API Stability and Compatibility (#757)
To prepare for a 1.0 beta release, we need to communicate which parts of the SDK are stable and which are experimental. This update establishes a system for marking experimental APIs directly within the documentation. We've implemented a convention where experimental pages or specific functions are marked with an 'Experimental' badge. This is handled by adding an `experimental: true` flag to the frontmatter of relevant documentation pages, which then conditionally renders the badge in the page title. This provides clear, inline guidance to users about the stability of different features.
1 parent 57c99a9 commit ad7fa16

File tree

12 files changed

+381
-33
lines changed

12 files changed

+381
-33
lines changed

.cursor/rules/user/justin.mdc

Lines changed: 121 additions & 0 deletions
Large diffs are not rendered by default.

.gitignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,5 +187,3 @@ smoke-test-artifacts
187187
# But we also don't want to commit the cursor rules to this repo
188188
starters/standard/.cursor/rules/*
189189
starters/standard/.pnpm-store
190-
191-
.cursor/rules/user
Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
# Work Log: 2025-09-20 - Documenting API Stability
2+
3+
## 1. Problem Definition & Goal
4+
5+
The SDK's public APIs lack clear documentation regarding their stability. Users, particularly those considering the SDK for production use, have no way of knowing which features are considered stable and which are experimental and subject to breaking changes. This can lead to user friction and uncertainty.
6+
7+
The goal is to investigate the SDK's public API surface, research best practices for communicating API stability, and implement a clear, maintainable documentation system that explicitly labels features as "Stable" or "Experimental".
8+
9+
## 2. Investigation: Discovering the Public API
10+
11+
The first step was to create a comprehensive list of all public-facing APIs. This was a multi-step process.
12+
13+
### 2.1. `package.json` Exports
14+
15+
I started by analyzing the `exports` map in `sdk/package.json`. This provided the primary, official entry points for the package. I filtered out internal entry points (those prefixed with `__`) to focus on the intended public surface.
16+
17+
### 2.2. Source Code Analysis
18+
19+
I then traced each of these entry points back to their source files in `sdk/src/` to determine exactly what functions, classes, and types were being re-exported. This analysis produced a detailed list of every public API.
20+
21+
## 3. The Solution: A Hybrid Documentation Strategy
22+
23+
After identifying the APIs, the next step was to determine the best way to communicate their stability. The final chosen strategy uses a central page to define stability and inline labels to mark specific experimental APIs.
24+
25+
### 3.1. The Strategy
26+
27+
1. **A Central "API Stability" Page:** A page at `docs/src/content/docs/stability.mdx` defines what "Stable" and "Experimental" mean. It establishes the rule that all APIs are considered stable by default unless marked with an inline "Experimental" label.
28+
2. **Inline Labels:** For APIs classified as experimental, a small, inline `<Badge>` component is placed directly next to the API's title in the documentation. This provides clear, in-context warnings without large, intrusive banners.
29+
30+
### 3.2. Implementation Details
31+
32+
- Created `docs/src/content/docs/stability.mdx`.
33+
- Added a link to the new page in the sidebar in `docs/astro.config.mjs`.
34+
- Added experimental labels to the documentation for `renderToString`, `renderToStream`, `initClientNavigation`, and `Turnstile`.
35+
36+
## 4. Final Public API List & Stability Classification
37+
38+
This is the final, revised list of public APIs based on user feedback, along with their stability and documentation status.
39+
40+
| Entrypoint | API | Stability | Documented? |
41+
| ---------------------- | ------------------------- | -------------- | --------------------------------------------------- |
42+
| **`rwsdk/vite`** | `redwood()` | Stable | Yes |
43+
| **`rwsdk/worker`** | `defineApp()` | Stable | Yes |
44+
| | `renderToString()` | **Experimental** | Yes (`core/react-server-components.mdx`) |
45+
| | `renderToStream()` | **Experimental** | Yes (`core/react-server-components.mdx`) |
46+
| | `registerServerReference` | Internal | No |
47+
| | `rscActionHandler` | Internal | No |
48+
| | `getRequestInfo` | Internal | No |
49+
| **`rwsdk/client`** | `initClient()` | Stable | Yes (`reference/sdk-client.mdx`) |
50+
| | `initClientNavigation()` | **Experimental** | Yes (`guides/frontend/client-side-nav.mdx`) |
51+
| | `createServerReference` | Internal | No |
52+
| **`rwsdk/router`** | (all exports) | Stable | Yes (`core/routing.mdx`, `reference/sdk-router.mdx`)|
53+
| **`rwsdk/auth`** | (all exports) | Stable | Yes (`core/authentication.mdx`) |
54+
| **`rwsdk/turnstile`** | (all exports) | **Experimental** | Yes (`core/authentication.mdx`) |
55+
| **`rwsdk/db`** | (all exports) | **Experimental** | Yes (`core/database.mdx`, `core/database-do.mdx`) |
56+
| **`rwsdk/realtime/*`** | (all exports) | **Experimental** | Yes (`core/realtime.mdx`) |
57+
| **`rwsdk/debug`** | `debug()` | **Experimental** | No |
58+
| **`rwsdk/constants`** | `IS_DEV` | Internal | No |
59+
60+
## 5. Badge Rendering Fix
61+
62+
**Issue**: Badges in page titles were rendering as plain text instead of HTML components.
63+
64+
**Root Cause**: JSX components (`<Badge>`) were placed in frontmatter `title` fields, which are YAML metadata and don't support JSX rendering.
65+
66+
**Solution**: Moved badges from frontmatter titles to H1 headings in the content:
67+
- `database.mdx`: `title: Database (D1) <Badge...>``# Database (D1) <Badge...>`
68+
- `database-do.mdx`: `title: Database (Durable Objects) <Badge...>``# Database (Durable Objects) <Badge...>`
69+
- `realtime.mdx`: `title: Realtime <Badge...>``# Realtime <Badge...>`
70+
71+
This allows the badges to render properly as components while maintaining the visual hierarchy.
72+
73+
## 6. MDX Parsing Error Fix
74+
75+
**Issue**: MDX error in `core/react-server-components.mdx` - "Plugin 'Code caption' caused an error in its 'preprocessCode' hook. Error message: Expected a valid non-empty non-negative number[], but got [-1]"
76+
77+
**Root Cause**: Multiple syntax issues in the MDX file:
78+
1. `---` lines inside code blocks (lines 53-56 and 80-82) breaking the MDX parser
79+
2. Missing experimental badges for `renderToStream()` and `renderToString()`
80+
3. Incomplete code block at the end of the file
81+
82+
**Solution**:
83+
- Moved explanatory text outside of code blocks
84+
- Added experimental badge to the combined `renderToStream()` and `renderToString()` section
85+
- Fixed malformed code block syntax
86+
- Removed stray closing ``` at the end
87+
88+
The page now loads successfully without MDX parsing errors.
89+
90+
## 7. Duplicate Title Fix
91+
92+
**Issue**: Pages with experimental badges were showing duplicate titles (e.g., "Realtime" followed by "Realtime Experimental").
93+
94+
**Root Cause**: Starlight automatically generates an H1 from the frontmatter `title`, and we were adding another H1 with the badge (`# Realtime <Badge>`), creating duplicate headings.
95+
96+
**Solution**: Removed the manual H1 headings and placed the badge as the first element in the content, right after the auto-generated title. This provides clear visual indication without duplication:
97+
- `realtime.mdx`: Removed `# Realtime <Badge>`, kept just `<Badge>` at the top
98+
- `database.mdx`: Removed `# Database (D1) <Badge>`, kept just `<Badge>` at the top
99+
- `database-do.mdx`: Removed `# Database (Durable Objects) <Badge>`, kept just `<Badge>` at the top
100+
101+
All pages now display correctly with single titles and prominent experimental badges.
102+
103+
## 9. Schema Extension Solution
104+
105+
**Issue**: The `experimental` frontmatter field was not being recognized by Starlight, showing as `undefined` in the custom PageTitle component.
106+
107+
**Root Cause**: Starlight's default `docsSchema()` only includes predefined fields. Custom frontmatter fields need to be explicitly defined in the content schema.
108+
109+
**Solution**: Extended Starlight's schema in `content.config.ts`:
110+
```typescript
111+
schema: docsSchema({
112+
extend: z.object({
113+
experimental: z.boolean().optional(),
114+
}),
115+
}),
116+
```
117+
118+
**Result**: The experimental badges now render correctly on all experimental pages:
119+
- `realtime.mdx`: Shows "Realtime Experimental"
120+
- `database.mdx`: Shows "Database (D1) Experimental"
121+
- `database-do.mdx`: Shows "Database (Durable Objects) Experimental"
122+
- Non-experimental pages (e.g., routing) correctly show no badges
123+
124+
The component override system is working perfectly, providing a clean, maintainable solution for marking experimental APIs.
125+
126+
## 8. Proper Badge Integration via Component Override
127+
128+
**Issue**: The previous badge placement approaches (frontmatter titles, manual H1s, separate elements) didn't look quite right visually and weren't integrated properly into Starlight's title rendering.
129+
130+
**Solution**: Implemented Starlight's component override system to customize title rendering:
131+
132+
1. **Created custom PageTitle component** (`src/components/PageTitle.astro`):
133+
- Accesses frontmatter via `Astro.locals.starlightRoute.entry.data`
134+
- Conditionally renders badge when `experimental: true` is set
135+
- Maintains proper styling and accessibility (id="_top")
136+
137+
2. **Configured component override** in `astro.config.mjs`:
138+
- Added `components: { PageTitle: './src/components/PageTitle.astro' }`
139+
- Overrides Starlight's default title rendering
140+
141+
3. **Updated frontmatter approach**:
142+
- Added `experimental: true` field to experimental pages
143+
- Removed manual badge elements from content
144+
- Clean separation of metadata and content
145+
146+
**Result**: Badges now render seamlessly integrated into the page titles, looking natural and professional without duplication or visual inconsistencies. The approach is maintainable and follows Starlight's recommended patterns.
147+
148+
## 10. Missing Import Fix
149+
150+
**Issue**: `authentication.mdx` was throwing a runtime error: "importedCodeStandardWorker is not defined"
151+
152+
**Root Cause**: The file was using `importedCodeStandardWorker` in a code block but missing the import statement.
153+
154+
**Solution**: Added the missing import:
155+
```typescript
156+
import importedCodeStandardWorker from "../../../../../starters/standard/src/worker.tsx?raw";
157+
```
158+
159+
**Result**: Authentication page now loads successfully without runtime errors.
160+
161+
## 11. Improved Badge Placement for Turnstile
162+
163+
**Issue**: The experimental badge for Turnstile was placed inline with the Cloudflare documentation link, making it less contextually clear.
164+
165+
**Solution**: Moved the experimental badge to a more appropriate location:
166+
- **Removed** badge from inline text: `[Cloudflare Turnstile](https://developers.cloudflare.com/turnstile/) <Badge...>`
167+
- **Added** badge to section heading: `## Optional: Bot Protection with Turnstile <Badge text="Experimental" type="caution" />`
168+
169+
**Result**: The experimental badge now clearly indicates that the entire Turnstile feature is experimental, rather than just the documentation link.
170+
171+
## 12. Individual API Method Badge Placement
172+
173+
**Issue**: The experimental badge for `renderToStream()` and `renderToString()` was placed on a combined heading, making it unclear which specific methods were experimental.
174+
175+
**Solution**: Moved experimental badges to individual API method signatures:
176+
- **Changed** from: `### renderToStream() and renderToString() <Badge...>`
177+
- **To**: Individual badges on each method:
178+
- `#### renderToStream(element[, options]): Promise<ReadableStream> <Badge text="Experimental" type="caution" />`
179+
- `#### renderToString(element[, options]): Promise<string> <Badge text="Experimental" type="caution" />`
180+
181+
**Result**: Each API method is now clearly marked as experimental at the point of reference, providing better granular information to developers.
182+
183+
## 13. Missing Import Fix for Compatibility Page
184+
185+
**Issue**: `compatibility.mdx` was throwing a runtime error: "Expected component Aside to be defined: you likely forgot to import, pass, or provide it."
186+
187+
**Root Cause**: The file was using the `<Aside>` component but missing the import statement.
188+
189+
**Solution**: Added the missing import:
190+
```typescript
191+
import { Aside } from "@astrojs/starlight/components";
192+
```
193+
194+
**Result**: Compatibility page now loads successfully without runtime errors.
195+
196+
## PR Description
197+
198+
To prepare for a 1.0 beta release, we need to communicate which parts of the SDK are stable and which are experimental. This update establishes a system for marking experimental APIs directly within the documentation. We've implemented a convention where experimental pages or specific functions are marked with an 'Experimental' badge. This is handled by adding an `experimental: true` flag to the frontmatter of relevant documentation pages, which then conditionally renders the badge in the page title. This provides clear, inline guidance to users about the stability of different features.

docs/astro.config.mjs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ export default defineConfig({
99
integrations: [
1010
starlight({
1111
plugins: [starlightLlmsTxt()],
12+
components: {
13+
// Override the default PageTitle component to support experimental badges
14+
PageTitle: "./src/components/PageTitle.astro",
15+
},
1216
expressiveCode: {
13-
themes: ['github-dark', 'github-light'],
17+
themes: ["github-dark", "github-light"],
1418
shiki: {
1519
bundledLangs: ["bash", "ts", "tsx"],
1620
},
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
import { Badge } from "@astrojs/starlight/components";
3+
4+
const { title, experimental } = Astro.locals.starlightRoute.entry.data;
5+
---
6+
7+
<h1 id="_top">
8+
{title}
9+
{experimental && <Badge text="Experimental" type="caution" />}
10+
</h1>
11+
12+
<style>
13+
h1 {
14+
color: var(--sl-color-white);
15+
font-size: var(--sl-text-h1);
16+
font-weight: 600;
17+
line-height: var(--sl-line-height-headings);
18+
margin-bottom: 1rem;
19+
}
20+
</style>

docs/src/content.config.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ import { docsSchema } from "@astrojs/starlight/schema";
55
export const collections = {
66
docs: defineCollection({
77
loader: docsLoader(),
8-
schema: docsSchema(),
8+
schema: docsSchema({
9+
extend: z.object({
10+
experimental: z.boolean().optional(),
11+
}),
12+
}),
913
}),
1014
};

docs/src/content/docs/core/authentication.mdx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
---
22
title: Authentication
3-
description: Learn about the authentication mechanisms in the standard starter, including passkeys, session management, and bot protection.
3+
description: Secure your application with sessions and passwordless login.
44
---
55

66
import {
77
Aside,
88
Tabs,
9+
TabItem,
910
LinkCard,
1011
Code,
12+
Badge,
1113
} from "@astrojs/starlight/components";
12-
13-
import importedCodeStandardWorker from "../../../../../starters/standard/src/worker.tsx?raw";
1414
import importedCodeStandardAuthFunctions from "../../../../../starters/standard/src/app/pages/user/functions.ts?raw";
1515
import importedCodeStandardLogin from "../../../../../starters/standard/src/app/pages/user/Login.tsx?raw";
16+
import importedCodeStandardWorker from "../../../../../starters/standard/src/worker.tsx?raw";
17+
18+
## Email & Password
19+
20+
RedwoodSDK's built-in authentication uses sessions, which are stored in a
1621

1722
We've baked authentication right into the [**standard starter**](https://github.com/redwoodjs/sdk/tree/main/starters/standard), giving you everything you need to handle users, sessions, and logins out of the box. The standard starter uses **passkeys ([WebAuthn](https://webauthn.guide/))** for passwordless authentication (keys can be shared on multiple devices), **session persistence via [Cloudflare Durable Objects](https://developers.cloudflare.com/durable-objects/)**, and **bot protection with [Cloudflare Turnstile](https://developers.cloudflare.com/turnstile/)**. The database layer is powered by **[Cloudflare D1](https://developers.cloudflare.com/d1/)** and **[Prisma](https://www.prisma.io/)**.
1823

@@ -85,7 +90,7 @@ npx wrangler secret put AUTH_SECRET_KEY
8590

8691
Never use the same secret key for development and production environments, and avoid committing your secret keys to version control.
8792

88-
## Optional: Bot Protection with Turnstile
93+
## Optional: Bot Protection with Turnstile <Badge text="Experimental" type="caution" />
8994

9095
You can optionally enable bot protection for user registration using Cloudflare Turnstile. To enable this:
9196

docs/src/content/docs/core/database-do.mdx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
---
22
title: Database (Durable Objects)
33
description: Build isolated, zero-setup databases using SQLite Durable Objects and SQL-based queries with full TypeScript support.
4+
experimental: true
45
---
56

6-
import { Aside, Code, LinkCard } from "@astrojs/starlight/components";
7+
import { Aside, Code, LinkCard, Badge } from "@astrojs/starlight/components";
78

89
The SDK includes a built-in database solution using **SQLite Durable Objects** and **Kysely** for SQL queries. Create isolated databases at runtime with minimal setup.
910

10-
<Aside type="caution">
11-
This is a **preview feature**. The API is experimental and may change as we gather feedback from early adopters.
12-
</Aside>
13-
1411
---
1512

1613
## Motivation

docs/src/content/docs/core/database.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
---
22
title: Database (D1)
33
description: Store & retrieve data from the database.
4+
experimental: true
45
---
56

6-
import { Aside, Tabs, TabItem, LinkCard } from "@astrojs/starlight/components";
7+
import { Aside, Tabs, TabItem, LinkCard, Badge } from "@astrojs/starlight/components";
78

89
<Aside type="caution">
910
The [Quick Start Guide](/getting-started/quick-start) uses the **minimal

0 commit comments

Comments
 (0)