|
| 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. |
0 commit comments