@blaaiz/docs-core 0.1.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/LICENSE +21 -0
- package/README.md +103 -0
- package/dist/chunk-3ZX4WIE3.js +2984 -0
- package/dist/chunk-3ZX4WIE3.js.map +1 -0
- package/dist/chunk-JCYR6RPE.js +31 -0
- package/dist/chunk-JCYR6RPE.js.map +1 -0
- package/dist/chunk-ZKOOKLZ3.js +124 -0
- package/dist/chunk-ZKOOKLZ3.js.map +1 -0
- package/dist/cli.js +534 -0
- package/dist/cli.js.map +1 -0
- package/dist/generator.cjs +508 -0
- package/dist/generator.cjs.map +1 -0
- package/dist/generator.d.cts +122 -0
- package/dist/generator.d.ts +122 -0
- package/dist/generator.js +177 -0
- package/dist/generator.js.map +1 -0
- package/dist/index.cjs +3024 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1182 -0
- package/dist/index.d.ts +1182 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/navigation-CGqFIPlP.d.cts +498 -0
- package/dist/navigation-CGqFIPlP.d.ts +498 -0
- package/dist/openapi-types-CJ6p5Cux.d.cts +78 -0
- package/dist/openapi-types-CJ6p5Cux.d.ts +78 -0
- package/dist/ui/api-try-it.cjs +654 -0
- package/dist/ui/api-try-it.cjs.map +1 -0
- package/dist/ui/api-try-it.d.cts +78 -0
- package/dist/ui/api-try-it.d.ts +78 -0
- package/dist/ui/api-try-it.js +509 -0
- package/dist/ui/api-try-it.js.map +1 -0
- package/dist/ui/ask-ai.cjs +810 -0
- package/dist/ui/ask-ai.cjs.map +1 -0
- package/dist/ui/ask-ai.d.cts +57 -0
- package/dist/ui/ask-ai.d.ts +57 -0
- package/dist/ui/ask-ai.js +808 -0
- package/dist/ui/ask-ai.js.map +1 -0
- package/dist/ui/copy-page.cjs +312 -0
- package/dist/ui/copy-page.cjs.map +1 -0
- package/dist/ui/copy-page.d.cts +33 -0
- package/dist/ui/copy-page.d.ts +33 -0
- package/dist/ui/copy-page.js +183 -0
- package/dist/ui/copy-page.js.map +1 -0
- package/dist/ui/mermaid.cjs +363 -0
- package/dist/ui/mermaid.cjs.map +1 -0
- package/dist/ui/mermaid.d.cts +13 -0
- package/dist/ui/mermaid.d.ts +13 -0
- package/dist/ui/mermaid.js +361 -0
- package/dist/ui/mermaid.js.map +1 -0
- package/dist/ui.cjs +661 -0
- package/dist/ui.cjs.map +1 -0
- package/dist/ui.d.cts +428 -0
- package/dist/ui.d.ts +428 -0
- package/dist/ui.js +537 -0
- package/dist/ui.js.map +1 -0
- package/package.json +145 -0
- package/patches/fumadocs-openapi.patch +173 -0
- package/skills/AGENTS-section.md +36 -0
- package/skills/SKILL.md +363 -0
- package/styles/api-reference.css +1417 -0
- package/styles/ask-ai.css +563 -0
- package/styles/auth.css +462 -0
- package/styles/docs.css +247 -0
- package/styles/home.css +376 -0
- package/templates/init/content/docs/index.mdx.tmpl +52 -0
- package/templates/init/content/docs/meta.json.tmpl +3 -0
- package/templates/init/content/docs.json.tmpl +12 -0
- package/templates/init/content/nav.json.tmpl +7 -0
- package/templates/init/docs.config.ts.tmpl +36 -0
- package/templates/init/env.example.tmpl +15 -0
package/skills/SKILL.md
ADDED
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: docs-core
|
|
3
|
+
description: Work on a documentation site built with @blaaiz/docs-core. Use whenever a task touches docs.config.ts, content/docs.json, content/docs/**, or an import from @blaaiz/docs-core — adding or reordering doc pages, tabs, and groups; publishing an OpenAPI endpoint with the try-it playground; changing theme colours, the logo, or the favicon; setting up public or workspace authentication; turning on Ask AI, copy-page, search, or SEO; or fixing an error the framework raises.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# docs-core
|
|
7
|
+
|
|
8
|
+
`@blaaiz/docs-core` is a documentation framework that wraps Fumadocs and adds the
|
|
9
|
+
parts every docs site repeats: navigation, theming, access control, an OpenAPI
|
|
10
|
+
reference with a try-it playground, LLM-ready page export, search, SEO, and Ask
|
|
11
|
+
AI. It has zero runtime dependencies.
|
|
12
|
+
|
|
13
|
+
**The golden rule: the core owns the logic, the site owns config and content.**
|
|
14
|
+
If a thing is the same for every organization, it is inside the package. If it
|
|
15
|
+
changes per organization, it is in the site.
|
|
16
|
+
|
|
17
|
+
So: **never edit anything under `node_modules/@blaaiz/docs-core`.** Every change
|
|
18
|
+
you make lands in `docs.config.ts`, `content/docs.json`, a content file, or one
|
|
19
|
+
of the site's own wiring files. A change that seems to need a package edit is
|
|
20
|
+
either a config field you have not found yet, or a framework change that belongs
|
|
21
|
+
in the `docs-core` repository — say so instead of patching the install.
|
|
22
|
+
|
|
23
|
+
## The files that matter
|
|
24
|
+
|
|
25
|
+
| File | Owned by | Edit it to |
|
|
26
|
+
| ----------------------------------- | ------------- | ------------------------------------------------------------------------------ |
|
|
27
|
+
| `docs.config.ts` | the site | Change the brand, the access mode, the proxy allow-list, features, SEO, Ask AI |
|
|
28
|
+
| `content/docs.json` | the site | Change what is published and in what order: tabs, groups, pages, API endpoints |
|
|
29
|
+
| `content/docs/**/*.mdx` | the site | Write or edit prose |
|
|
30
|
+
| `content/**/*-openapi.json` | the site | Add or change an API endpoint's spec |
|
|
31
|
+
| `content/docs/**/meta.json` | see below | Sidebar order |
|
|
32
|
+
| `content/nav.json` | see below | Top-navigation links, one per tab |
|
|
33
|
+
| `content/api-methods.json` | the generator | Sidebar method badges — never by hand |
|
|
34
|
+
| `content/docs/api/**` | the generator | Generated endpoint pages — never by hand |
|
|
35
|
+
| `app/**`, `lib/**`, `middleware.ts` | the site | Wiring: routes, layouts, the page tree |
|
|
36
|
+
|
|
37
|
+
`meta.json` and `nav.json` are hand-written on a prose-only site. On a site with
|
|
38
|
+
an API reference the generator writes them from `docs.json` on every run, so
|
|
39
|
+
hand edits are lost — change `docs.json` instead. A site runs the generator when
|
|
40
|
+
its `package.json` has a `predev` / `prebuild` script calling
|
|
41
|
+
`@blaaiz/docs-core/generator`; check that first.
|
|
42
|
+
|
|
43
|
+
## docs.config.ts
|
|
44
|
+
|
|
45
|
+
One call to `defineDocsConfig`. It returns its argument unchanged; it exists for
|
|
46
|
+
type-checking and completion.
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { defineDocsConfig } from '@blaaiz/docs-core';
|
|
50
|
+
|
|
51
|
+
export default defineDocsConfig({
|
|
52
|
+
theme: { name: 'Example Docs', colors: { primary: '#4c63f5' } },
|
|
53
|
+
auth: 'public',
|
|
54
|
+
proxy: { allowedOrigins: [] },
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`theme`, `auth`, and `proxy` are required. `features`, `seo`, and `ai` are
|
|
59
|
+
optional, and omitting one leaves everything it controls off.
|
|
60
|
+
|
|
61
|
+
### theme
|
|
62
|
+
|
|
63
|
+
| Field | Type | Default | Notes |
|
|
64
|
+
| ------------ | ----------------- | ------------------ | --------------------------------------------------- |
|
|
65
|
+
| `name` | string | — | Required. Display name, and the base for SEO titles |
|
|
66
|
+
| `logo` | `{ light, dark }` | none | Image paths the site serves |
|
|
67
|
+
| `favicon` | string | none | Path the site serves |
|
|
68
|
+
| `colors` | palette | framework defaults | Light theme, and the base for dark |
|
|
69
|
+
| `darkColors` | palette | inherits `colors` | Dark-theme overrides only |
|
|
70
|
+
|
|
71
|
+
A palette accepts `primary`, `background`, `foreground`, `card`, `border`, and
|
|
72
|
+
`muted`. Every token is optional. `buildThemeCss(config.theme)` turns the palette
|
|
73
|
+
into CSS variables; the site's root layout injects the result.
|
|
74
|
+
|
|
75
|
+
### auth
|
|
76
|
+
|
|
77
|
+
`'public'` or a workspace block. Public mode needs no middleware, no sign-in
|
|
78
|
+
page, and no auth routes.
|
|
79
|
+
|
|
80
|
+
| Field | Type | Default | Notes |
|
|
81
|
+
| ------------------- | ------------- | --------------------- | ---------------------------------- |
|
|
82
|
+
| `mode` | `'workspace'` | — | Required |
|
|
83
|
+
| `allowedDomains` | string[] | — | Email domains permitted to sign in |
|
|
84
|
+
| `providers` | object | — | Any combination of the three below |
|
|
85
|
+
| `signInPath` | string | `/signin` | Where the gate redirects |
|
|
86
|
+
| `sessionSecretEnv` | string | `DOCS_SESSION_SECRET` | Env var holding the signing key |
|
|
87
|
+
| `sessionTtlSeconds` | number | 8 hours | Session lifetime |
|
|
88
|
+
| `secureCookies` | boolean | `true` | Set `false` only for local http |
|
|
89
|
+
|
|
90
|
+
Providers:
|
|
91
|
+
|
|
92
|
+
| Provider | Shape | Env vars |
|
|
93
|
+
| -------- | --------------------------------------------------- | ------------------------------------------ |
|
|
94
|
+
| `email` | `true` | — |
|
|
95
|
+
| `secret` | `{ secretEnv? }` | `DOCS_ACCESS_SECRET` |
|
|
96
|
+
| `google` | `{ clientIdEnv?, clientSecretEnv?, redirectPath? }` | `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` |
|
|
97
|
+
|
|
98
|
+
`secret` supersedes `email` when both are enabled: offering email-only as well
|
|
99
|
+
would let a reader skip the secret. `google`'s `redirectPath` defaults to
|
|
100
|
+
`/api/auth/google/callback`.
|
|
101
|
+
|
|
102
|
+
### proxy
|
|
103
|
+
|
|
104
|
+
| Field | Type | Notes |
|
|
105
|
+
| -------------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
106
|
+
| `allowedOrigins` | string[] | Security-critical. The exhaustive list of upstream origins the try-it playground may reach. Anything else is refused |
|
|
107
|
+
| `rateLimitPerMinute` | number | Forwards per minute per client IP. Default 60. Enforced by both proxy factories |
|
|
108
|
+
|
|
109
|
+
The proxy forwards the reader's own credentials and injects none of its own. It
|
|
110
|
+
also refuses loopback, private, and link-local hosts, so it cannot be used as an
|
|
111
|
+
SSRF relay. An empty `allowedOrigins` is correct for a site with no API
|
|
112
|
+
reference.
|
|
113
|
+
|
|
114
|
+
Pass the limit through when you mount the route, or the default of 60 applies:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
export const { GET, POST, PUT, PATCH, DELETE, OPTIONS } = createTryItProxyRoute({
|
|
118
|
+
allowedOrigins: config.proxy.allowedOrigins,
|
|
119
|
+
rateLimitPerMinute: config.proxy.rateLimitPerMinute,
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
A caller over the limit gets `429`, a `Retry-After` header, and the body
|
|
124
|
+
`{ "error": "Too many requests. Try again in 60 seconds.", "retryAfterSeconds": 60 }`.
|
|
125
|
+
The key is the client IP: the first entry of `x-forwarded-for`, then
|
|
126
|
+
`x-real-ip`, then one shared bucket. The window is in memory per server process.
|
|
127
|
+
|
|
128
|
+
### features
|
|
129
|
+
|
|
130
|
+
| Field | Default | Effect |
|
|
131
|
+
| ---------- | ------- | ------------------------------------------------------------------------------ |
|
|
132
|
+
| `copyPage` | `false` | Shows the "Copy page / View as Markdown" control and serves the Markdown route |
|
|
133
|
+
|
|
134
|
+
### seo
|
|
135
|
+
|
|
136
|
+
| Field | Default | Notes |
|
|
137
|
+
| --------------- | --------------------- | ------------------------------------------------------------------------ |
|
|
138
|
+
| `siteUrl` | none | Canonical origin. Without it, canonical and Open Graph URLs are relative |
|
|
139
|
+
| `titleTemplate` | `'%s · <theme.name>'` | `%s` is the page title. The home title is `theme.name` verbatim |
|
|
140
|
+
| `description` | none | Used where a page declares none |
|
|
141
|
+
| `ogImage` | none | Default sharing image |
|
|
142
|
+
| `twitter` | none | `@handle` for card attribution |
|
|
143
|
+
| `keywords` | none | Applied site-wide |
|
|
144
|
+
| `noindex` | `false` | Set `true` on a private or pre-launch site |
|
|
145
|
+
|
|
146
|
+
### ai
|
|
147
|
+
|
|
148
|
+
Naming a provider is a complete setup. Every other field has a default.
|
|
149
|
+
|
|
150
|
+
| Field | Default | Notes |
|
|
151
|
+
| -------------------- | -------------------------------------- | -------------------------------------------- |
|
|
152
|
+
| `provider` | — | Required: `'anthropic'` or `'openai'` |
|
|
153
|
+
| `model` | `claude-sonnet-5` / `gpt-5-mini` | Validated against the provider's allow-list |
|
|
154
|
+
| `apiKeyEnv` | `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` | The env var, never the key |
|
|
155
|
+
| `rateLimitPerMinute` | `10` | Per client IP, 1–600. Always enforced |
|
|
156
|
+
| `maxContextPages` | `6` | Pages retrieved per question, 1–20 |
|
|
157
|
+
| `systemPrompt` | none | Extra instructions, 4000 characters or fewer |
|
|
158
|
+
|
|
159
|
+
Allowed models:
|
|
160
|
+
|
|
161
|
+
- **anthropic** — `claude-sonnet-5`, `claude-haiku-4-5`, `claude-opus-5`,
|
|
162
|
+
`claude-opus-4-8`, `claude-sonnet-4-6`
|
|
163
|
+
- **openai** — `gpt-5-mini`, `gpt-5`, `gpt-5.2`, `gpt-5-nano`, `gpt-4.1`,
|
|
164
|
+
`gpt-4.1-mini`, `gpt-4o`, `gpt-4o-mini`
|
|
165
|
+
|
|
166
|
+
The whole `ai` block is validated when the route is built, so a typo fails the
|
|
167
|
+
build rather than a reader's question. An API key never belongs in this file.
|
|
168
|
+
|
|
169
|
+
## content/docs.json
|
|
170
|
+
|
|
171
|
+
The navigation, and the only thing that decides what is published.
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"name": "Example Docs",
|
|
176
|
+
"navigation": {
|
|
177
|
+
"tabs": [
|
|
178
|
+
{ "tab": "Home", "icon": "house", "pages": ["index", "quickstart"] },
|
|
179
|
+
{
|
|
180
|
+
"tab": "Guides",
|
|
181
|
+
"icon": "book",
|
|
182
|
+
"groups": [
|
|
183
|
+
{ "group": "Getting started", "pages": ["guides/install", "guides/config"] },
|
|
184
|
+
{ "group": "Advanced", "pages": ["guides/webhooks"] }
|
|
185
|
+
]
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"tab": "API reference",
|
|
189
|
+
"icon": "code",
|
|
190
|
+
"groups": [
|
|
191
|
+
{
|
|
192
|
+
"group": "Authentication",
|
|
193
|
+
"pages": [
|
|
194
|
+
"api-reference/user/auth/login-openapi.json POST /api/user/login",
|
|
195
|
+
"api-reference/user/auth/register-openapi.json POST /api/user/register"
|
|
196
|
+
]
|
|
197
|
+
}
|
|
198
|
+
]
|
|
199
|
+
}
|
|
200
|
+
]
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Rules the parser enforces:
|
|
206
|
+
|
|
207
|
+
- A tab needs a string `tab` title. It carries either `pages` or `groups`, never
|
|
208
|
+
both — `groups` wins if both appear.
|
|
209
|
+
- A group needs a string `group` title and a `pages` array. Groups nest: a
|
|
210
|
+
`pages` array may hold group objects.
|
|
211
|
+
- A page string is either a content path relative to `content/docs`, without the
|
|
212
|
+
`.mdx` extension, or an OpenAPI entry shaped `<file> <METHOD> <route>`.
|
|
213
|
+
- `icon` is optional on a tab, and the site resolves the name through `docsIcon`.
|
|
214
|
+
- Order in the file is order in the sidebar.
|
|
215
|
+
|
|
216
|
+
## Task recipes
|
|
217
|
+
|
|
218
|
+
### Add a doc page
|
|
219
|
+
|
|
220
|
+
1. Create `content/docs/<path>.mdx` with a frontmatter `title` and `description`.
|
|
221
|
+
2. Add `"<path>"` to the right `pages` array in `content/docs.json`, in the
|
|
222
|
+
position you want it.
|
|
223
|
+
3. On a prose-only site, add the same entry to the folder's `meta.json` `pages`
|
|
224
|
+
array. On an API site, re-run the generator instead.
|
|
225
|
+
4. Run the typecheck and the build.
|
|
226
|
+
|
|
227
|
+
### Add a tab
|
|
228
|
+
|
|
229
|
+
1. Add a tab object to `navigation.tabs` in `content/docs.json`, with `tab`,
|
|
230
|
+
`icon`, and either `pages` or `groups`.
|
|
231
|
+
2. Create every page the tab lists.
|
|
232
|
+
3. Add the tab's folder to the root `content/docs/meta.json` `pages` array, and
|
|
233
|
+
give the folder its own `meta.json` with `"root": true`, a `title`, and an
|
|
234
|
+
`icon`. On an API site, re-run the generator instead.
|
|
235
|
+
4. Add the tab to `content/nav.json` — `{ text, url, icon }`, pointing at its
|
|
236
|
+
first page. The generator writes this file on an API site.
|
|
237
|
+
|
|
238
|
+
### Add an API endpoint
|
|
239
|
+
|
|
240
|
+
1. Create one OpenAPI file holding one operation. The name must end in
|
|
241
|
+
`-openapi.json`. Give the operation an `operationId`: it becomes the page
|
|
242
|
+
slug. Without one, the slug is derived from `METHOD path`.
|
|
243
|
+
2. Add the entry to `content/docs.json` in the group where it belongs:
|
|
244
|
+
`"<file> <METHOD> <route>"`, with the file path relative to `content`.
|
|
245
|
+
3. Re-run the generator: `pnpm generate:api`, or whatever script calls
|
|
246
|
+
`generateDocsTree`. It merges the specs, writes one page per declared
|
|
247
|
+
endpoint into its group folder, and rewrites `meta.json`, `nav.json`, and
|
|
248
|
+
`api-methods.json`.
|
|
249
|
+
4. Check the page renders and the try-it panel targets an allow-listed origin.
|
|
250
|
+
|
|
251
|
+
An operation that exists in a spec but is not declared in `docs.json` gets no
|
|
252
|
+
page. The navigation decides what is published. The API folder is deleted and
|
|
253
|
+
rebuilt on every run, so a removed endpoint leaves no stale page.
|
|
254
|
+
|
|
255
|
+
### Change theme colours
|
|
256
|
+
|
|
257
|
+
Edit `theme.colors` and `theme.darkColors` in `docs.config.ts`. Nothing else:
|
|
258
|
+
the site's layout already turns the palette into CSS variables. Set `primary` in
|
|
259
|
+
both palettes — a light-theme accent rarely has enough contrast on a dark
|
|
260
|
+
background.
|
|
261
|
+
|
|
262
|
+
### Enable Ask AI
|
|
263
|
+
|
|
264
|
+
1. Add one line to `docs.config.ts`: `ai: { provider: 'anthropic' }`.
|
|
265
|
+
2. Set `ANTHROPIC_API_KEY` (or `OPENAI_API_KEY`) in the server environment. Never
|
|
266
|
+
in `docs.config.ts`, never in a `NEXT_PUBLIC_` variable.
|
|
267
|
+
3. Mount the route. It needs the Node runtime, because the corpus is read from
|
|
268
|
+
the MDX files on disk:
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
// app/api/ai/route.ts
|
|
272
|
+
import { createAskAiRoute } from '@blaaiz/docs-core';
|
|
273
|
+
import config from '@/docs.config';
|
|
274
|
+
import { askAiDocuments, loadAskAiPageMarkdown } from '@/lib/ask-ai';
|
|
275
|
+
|
|
276
|
+
export const runtime = 'nodejs';
|
|
277
|
+
|
|
278
|
+
export const { POST } = createAskAiRoute({
|
|
279
|
+
config: config.ai!,
|
|
280
|
+
siteName: config.theme.name,
|
|
281
|
+
documents: askAiDocuments,
|
|
282
|
+
loadPageMarkdown: loadAskAiPageMarkdown,
|
|
283
|
+
});
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
4. Mount the panel once in the docs layout, and import its stylesheet in
|
|
287
|
+
`app/globals.css`:
|
|
288
|
+
|
|
289
|
+
```tsx
|
|
290
|
+
import { AskAi } from '@blaaiz/docs-core/ui/ask-ai';
|
|
291
|
+
// <AskAi endpoint="/api/ai" /> — endpoint defaults to /api/ai
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
```css
|
|
295
|
+
@import '@blaaiz/docs-core/styles/ask-ai.css';
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
`AskAi` accepts `endpoint`, `suggestions`, `label`, and `placeholder`, and all
|
|
299
|
+
four have defaults.
|
|
300
|
+
|
|
301
|
+
### Enable copy page
|
|
302
|
+
|
|
303
|
+
1. Set `features: { copyPage: true }` in `docs.config.ts`.
|
|
304
|
+
2. Render `CopyPageButton` from `@blaaiz/docs-core/ui/copy-page` in the page
|
|
305
|
+
component, guarded by `config.features?.copyPage`.
|
|
306
|
+
3. Serve the Markdown: `createMarkdownRoute({ read })` in
|
|
307
|
+
`app/md/[[...slug]]/route.ts`, where `read` returns a page's raw MDX.
|
|
308
|
+
|
|
309
|
+
### Set up workspace auth with the shared secret
|
|
310
|
+
|
|
311
|
+
1. In `docs.config.ts`:
|
|
312
|
+
|
|
313
|
+
```ts
|
|
314
|
+
auth: {
|
|
315
|
+
mode: 'workspace',
|
|
316
|
+
allowedDomains: ['example.com'],
|
|
317
|
+
providers: { secret: { secretEnv: 'DOCS_ACCESS_SECRET' } },
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
2. Set `DOCS_SESSION_SECRET` (a long random value — `openssl rand -hex 32`) and
|
|
322
|
+
`DOCS_ACCESS_SECRET` in the server environment.
|
|
323
|
+
3. Mount `createSecretSignIn` on a route, and `createAuthGate` in
|
|
324
|
+
`middleware.ts`. Import `@blaaiz/docs-core/styles/auth.css` for `AuthScreen`.
|
|
325
|
+
4. Check the middleware matcher covers every route that must be gated. A matcher
|
|
326
|
+
that misses a route leaves that route open.
|
|
327
|
+
5. Consider `seo: { noindex: true }` on a gated site.
|
|
328
|
+
|
|
329
|
+
## Verification habits
|
|
330
|
+
|
|
331
|
+
- Run the site's `typecheck` and `build` after every change. A config mistake
|
|
332
|
+
usually fails at build time by design.
|
|
333
|
+
- After an API change, re-run the generator and confirm the page count it
|
|
334
|
+
reports matches the endpoints you declared.
|
|
335
|
+
- Never hand-edit `content/docs/api/**`, `content/api-methods.json`, or — on a
|
|
336
|
+
site that runs the generator — `meta.json` and `nav.json`. They are outputs.
|
|
337
|
+
- Check a content change in the browser at `/docs`: sidebar order, the top nav,
|
|
338
|
+
and search.
|
|
339
|
+
- A palette change needs a look in both light and dark mode.
|
|
340
|
+
|
|
341
|
+
## Troubleshooting
|
|
342
|
+
|
|
343
|
+
Each message below is raised verbatim by the framework.
|
|
344
|
+
|
|
345
|
+
| Message | Cause and fix |
|
|
346
|
+
| ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ |
|
|
347
|
+
| `ai.provider must be one of 'anthropic', 'openai', got '…'.` | Typo in `docs.config.ts`. Use one of the two names |
|
|
348
|
+
| `ai.model must be one of '…' for provider '…', got '…'.` | The model is not on that provider's allow-list. Use a listed id, or leave `model` out for the default |
|
|
349
|
+
| `Ask AI is not configured: set the ANTHROPIC_API_KEY environment variable on the server.` | The route is mounted but the key is missing. Set the env var named by `ai.apiKeyEnv` where the server runs — not in `docs.config.ts` |
|
|
350
|
+
| `Target origin is not allow-listed: https://api.example.com` | The try-it request targets an origin missing from `proxy.allowedOrigins`. Add it, exactly, scheme included |
|
|
351
|
+
| `Blocked host: localhost` | The proxy refuses loopback, private, and link-local hosts on purpose. Point the playground at a reachable public origin |
|
|
352
|
+
| `docs.json navigation must have a "tabs" array.` | `content/docs.json` is missing `navigation.tabs`. The older Mintlify `navigation` array form is not supported |
|
|
353
|
+
| `Navigation entry at navigation.tabs[N].pages[M] must be a page string or a group object.` | That entry is neither a string nor an object with a `group` key. A common cause is `{ "page": "…" }` |
|
|
354
|
+
| `Group at <path> is missing a string "group" title.` | Add the `group` title, or move a bare string into `pages` |
|
|
355
|
+
| `Duplicate operation POST /api/x across inputs.` | Two OpenAPI files define the same method on the same route. The merge refuses to overwrite silently — delete one |
|
|
356
|
+
| `Each OpenAPI input must have a "paths" object.` | One spec file is not a real OpenAPI document. Even a one-operation file needs `paths` |
|
|
357
|
+
| `[docs-core] docs.json references POST /api/x, but <file> defines no such operation.` | The `docs.json` entry and the spec disagree on method or route. Fix whichever is wrong |
|
|
358
|
+
|
|
359
|
+
Error classes are exported, so a site can branch on them:
|
|
360
|
+
`InvalidNavigationError`, `OpenApiMergeError`, `ProxyTargetError`, and
|
|
361
|
+
`AiConfigError`.
|
|
362
|
+
|
|
363
|
+
Full documentation: <https://docs-core.blaaiz.dev>
|