@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.
Files changed (71) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +103 -0
  3. package/dist/chunk-3ZX4WIE3.js +2984 -0
  4. package/dist/chunk-3ZX4WIE3.js.map +1 -0
  5. package/dist/chunk-JCYR6RPE.js +31 -0
  6. package/dist/chunk-JCYR6RPE.js.map +1 -0
  7. package/dist/chunk-ZKOOKLZ3.js +124 -0
  8. package/dist/chunk-ZKOOKLZ3.js.map +1 -0
  9. package/dist/cli.js +534 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/generator.cjs +508 -0
  12. package/dist/generator.cjs.map +1 -0
  13. package/dist/generator.d.cts +122 -0
  14. package/dist/generator.d.ts +122 -0
  15. package/dist/generator.js +177 -0
  16. package/dist/generator.js.map +1 -0
  17. package/dist/index.cjs +3024 -0
  18. package/dist/index.cjs.map +1 -0
  19. package/dist/index.d.cts +1182 -0
  20. package/dist/index.d.ts +1182 -0
  21. package/dist/index.js +3 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/navigation-CGqFIPlP.d.cts +498 -0
  24. package/dist/navigation-CGqFIPlP.d.ts +498 -0
  25. package/dist/openapi-types-CJ6p5Cux.d.cts +78 -0
  26. package/dist/openapi-types-CJ6p5Cux.d.ts +78 -0
  27. package/dist/ui/api-try-it.cjs +654 -0
  28. package/dist/ui/api-try-it.cjs.map +1 -0
  29. package/dist/ui/api-try-it.d.cts +78 -0
  30. package/dist/ui/api-try-it.d.ts +78 -0
  31. package/dist/ui/api-try-it.js +509 -0
  32. package/dist/ui/api-try-it.js.map +1 -0
  33. package/dist/ui/ask-ai.cjs +810 -0
  34. package/dist/ui/ask-ai.cjs.map +1 -0
  35. package/dist/ui/ask-ai.d.cts +57 -0
  36. package/dist/ui/ask-ai.d.ts +57 -0
  37. package/dist/ui/ask-ai.js +808 -0
  38. package/dist/ui/ask-ai.js.map +1 -0
  39. package/dist/ui/copy-page.cjs +312 -0
  40. package/dist/ui/copy-page.cjs.map +1 -0
  41. package/dist/ui/copy-page.d.cts +33 -0
  42. package/dist/ui/copy-page.d.ts +33 -0
  43. package/dist/ui/copy-page.js +183 -0
  44. package/dist/ui/copy-page.js.map +1 -0
  45. package/dist/ui/mermaid.cjs +363 -0
  46. package/dist/ui/mermaid.cjs.map +1 -0
  47. package/dist/ui/mermaid.d.cts +13 -0
  48. package/dist/ui/mermaid.d.ts +13 -0
  49. package/dist/ui/mermaid.js +361 -0
  50. package/dist/ui/mermaid.js.map +1 -0
  51. package/dist/ui.cjs +661 -0
  52. package/dist/ui.cjs.map +1 -0
  53. package/dist/ui.d.cts +428 -0
  54. package/dist/ui.d.ts +428 -0
  55. package/dist/ui.js +537 -0
  56. package/dist/ui.js.map +1 -0
  57. package/package.json +145 -0
  58. package/patches/fumadocs-openapi.patch +173 -0
  59. package/skills/AGENTS-section.md +36 -0
  60. package/skills/SKILL.md +363 -0
  61. package/styles/api-reference.css +1417 -0
  62. package/styles/ask-ai.css +563 -0
  63. package/styles/auth.css +462 -0
  64. package/styles/docs.css +247 -0
  65. package/styles/home.css +376 -0
  66. package/templates/init/content/docs/index.mdx.tmpl +52 -0
  67. package/templates/init/content/docs/meta.json.tmpl +3 -0
  68. package/templates/init/content/docs.json.tmpl +12 -0
  69. package/templates/init/content/nav.json.tmpl +7 -0
  70. package/templates/init/docs.config.ts.tmpl +36 -0
  71. package/templates/init/env.example.tmpl +15 -0
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { AI_PROVIDER_MODELS, AI_PROVIDER_NAMES, AiConfigError, DocsConfigError, InvalidNavigationError, OpenApiMergeError, ProxyTargetError, SESSION_COOKIE, assertAllowedTarget, buildMetadata, buildRobots, buildSearchIndex, buildSessionCookie, buildSitemap, buildThemeCss, clearSessionCookie, createAskAiRoute, createAuthGate, createEmailSignIn, createGoogleAuth, createMarkdownRoute, createSecretSignIn, createSignOut, createTryItProxy, createTryItProxyRoute, defineDocsConfig, isAllowedEmail, mdxToMarkdown, mergeOpenApiDocuments, openApiOperationToMarkdown, parseNavigation, planDocsTree, readCookie, resolveAiConfig, searchIndex, signSession, unwrapProxyTarget, validateDocsConfig, verifySession } from './chunk-3ZX4WIE3.js';
2
+ //# sourceMappingURL=index.js.map
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
@@ -0,0 +1,498 @@
1
+ /**
2
+ * The curated model registry.
3
+ *
4
+ * A docs site names a provider, and optionally a model. The framework — not the
5
+ * site — owns which models are known good for grounded documentation answers,
6
+ * which one is the default, and which environment variable holds the key. A
7
+ * short allow-list is deliberate: an unknown or retired model id must fail with
8
+ * a readable error at startup, not with a provider `404` on a reader's first
9
+ * question.
10
+ *
11
+ * The lists grow as providers ship models. Adding one is a one-line change here.
12
+ *
13
+ * @packageDocumentation
14
+ */
15
+ /**
16
+ * The AI providers `docs-core` speaks to.
17
+ *
18
+ * @public
19
+ */
20
+ type AiProviderName = 'anthropic' | 'openai';
21
+ /**
22
+ * What the framework knows about one provider: the models it accepts, the model
23
+ * it picks when a site names none, and the environment variable it reads the API
24
+ * key from by default.
25
+ *
26
+ * @public
27
+ */
28
+ interface AiProviderModels {
29
+ /** Model used when `ai.model` is omitted. */
30
+ readonly defaultModel: string;
31
+ /** Exhaustive list of accepted model ids, best-first for documentation. */
32
+ readonly models: readonly string[];
33
+ /** Env var read when `ai.apiKeyEnv` is omitted. */
34
+ readonly defaultApiKeyEnv: string;
35
+ }
36
+ /**
37
+ * Every provider name, in a stable order — for error messages and docs tables.
38
+ *
39
+ * @public
40
+ */
41
+ declare const AI_PROVIDER_NAMES: readonly AiProviderName[];
42
+ /**
43
+ * The curated registry, keyed by provider.
44
+ *
45
+ * @public
46
+ */
47
+ declare const AI_PROVIDER_MODELS: Readonly<Record<AiProviderName, AiProviderModels>>;
48
+
49
+ /**
50
+ * The Ask AI configuration boundary.
51
+ *
52
+ * A site declares `ai: { provider: 'anthropic' }` and is done: every other
53
+ * field has a framework default. That convenience only works if the boundary is
54
+ * strict, so this module validates each field and fails with a message that
55
+ * names the field, the value it received, and the accepted values. Nothing
56
+ * downstream re-checks: {@link resolveAiConfig} is the single gate, and it runs
57
+ * when the route is built, not when a reader asks a question.
58
+ *
59
+ * Secrets never appear here. Config names the environment variable; the server
60
+ * reads the value at request time.
61
+ *
62
+ * @packageDocumentation
63
+ */
64
+
65
+ /**
66
+ * The Ask AI settings a site supplies. Only `provider` is required.
67
+ *
68
+ * @public
69
+ */
70
+ interface AiConfig {
71
+ /** Which provider answers questions. */
72
+ readonly provider: AiProviderName;
73
+ /** Model id, validated against the provider's allow-list. Provider default when omitted. */
74
+ readonly model?: string;
75
+ /** Env var holding the API key. Provider default when omitted (e.g. `ANTHROPIC_API_KEY`). */
76
+ readonly apiKeyEnv?: string;
77
+ /** Questions per minute per client IP. Default 10. Always enforced. */
78
+ readonly rateLimitPerMinute?: number;
79
+ /** How many documentation pages are retrieved per question. Default 6. */
80
+ readonly maxContextPages?: number;
81
+ /** Extra instructions appended to the framework's grounded system prompt. */
82
+ readonly systemPrompt?: string;
83
+ }
84
+ /**
85
+ * An {@link AiConfig} with every default filled in and every value checked.
86
+ * Frozen, so a caller cannot mutate a validated configuration.
87
+ *
88
+ * @public
89
+ */
90
+ interface ResolvedAiConfig {
91
+ readonly provider: AiProviderName;
92
+ readonly model: string;
93
+ /** The env var the server reads the key from. */
94
+ readonly apiKeyEnv: string;
95
+ /** The provider's default env var, for error messages and documentation. */
96
+ readonly defaultApiKeyEnv: string;
97
+ readonly rateLimitPerMinute: number;
98
+ readonly maxContextPages: number;
99
+ /** Present only when the site supplied extra instructions. */
100
+ readonly systemPrompt?: string;
101
+ }
102
+ /**
103
+ * Thrown when `ai` configuration is missing, malformed, or out of range. The
104
+ * message names the exact field and the accepted values.
105
+ *
106
+ * @public
107
+ */
108
+ declare class AiConfigError extends Error {
109
+ constructor(message: string);
110
+ }
111
+ /**
112
+ * Validate a site's `ai` configuration and fill in the framework defaults.
113
+ *
114
+ * Call it once, when the route is built. It throws rather than returning an
115
+ * error object so a misconfigured site fails at startup — a reader must never
116
+ * discover a typo in `docs.config.ts` by asking a question.
117
+ *
118
+ * @param config - the raw `ai` block from a site's configuration
119
+ * @returns the frozen, fully defaulted configuration
120
+ * @throws {@link AiConfigError} when any field is missing, malformed, or out of range
121
+ * @public
122
+ */
123
+ declare function resolveAiConfig(config: AiConfig): ResolvedAiConfig;
124
+
125
+ /**
126
+ * The configuration seam.
127
+ *
128
+ * A consuming docs site supplies exactly one {@link DocsConfig}. It carries the
129
+ * per-site values that must never live in the framework: branding, the auth
130
+ * mode, and — critically — the allow-list of API origins the try-it proxy may
131
+ * forward to. `docs-core` owns the behaviour; the site owns these values.
132
+ *
133
+ * The boundary is guarded: {@link defineDocsConfig} runs
134
+ * {@link validateDocsConfig} on every value it is given, so a typo fails when
135
+ * the site builds rather than when a reader loads a page. A site's config file
136
+ * may be JavaScript, or built from JSON, so nothing here trusts the static
137
+ * types — every field is checked at runtime.
138
+ *
139
+ * @packageDocumentation
140
+ */
141
+
142
+ /**
143
+ * Google OAuth provider settings. Secrets never live in config: these name the
144
+ * environment variables the framework reads at runtime.
145
+ *
146
+ * @public
147
+ */
148
+ interface GoogleProviderConfig {
149
+ /** Env var holding the OAuth client id. Default `GOOGLE_CLIENT_ID`. */
150
+ readonly clientIdEnv?: string;
151
+ /** Env var holding the OAuth client secret. Default `GOOGLE_CLIENT_SECRET`. */
152
+ readonly clientSecretEnv?: string;
153
+ /** OAuth callback path. Default `/api/auth/google/callback`. */
154
+ readonly redirectPath?: string;
155
+ }
156
+ /**
157
+ * Shared-secret provider settings. The secret is one value for the whole site,
158
+ * read from the environment at runtime — config only names the variable.
159
+ *
160
+ * @public
161
+ */
162
+ interface SecretProviderConfig {
163
+ /** Env var holding the shared access secret. Default `DOCS_ACCESS_SECRET`. */
164
+ readonly secretEnv?: string;
165
+ }
166
+ /**
167
+ * The sign-in providers a workspace site offers. Enable any combination.
168
+ *
169
+ * @public
170
+ */
171
+ interface WorkspaceProviders {
172
+ /** Email sign-in: a verified email in an allowed domain gets a session. */
173
+ readonly email?: boolean;
174
+ /**
175
+ * Shared-secret sign-in: an allowed-domain email plus one site-wide secret.
176
+ * Stronger than email alone, so when enabled it supersedes the email provider
177
+ * (offering email-only too would let a reader skip the secret).
178
+ */
179
+ readonly secret?: SecretProviderConfig;
180
+ /** Google OAuth sign-in, restricted to the allowed domains. */
181
+ readonly google?: GoogleProviderConfig;
182
+ }
183
+ /**
184
+ * Gate the docs to signed-in members of the allowed email domains.
185
+ *
186
+ * @public
187
+ */
188
+ interface WorkspaceAuthConfig {
189
+ readonly mode: 'workspace';
190
+ /** Email domains permitted to sign in, e.g. `['blaaiz.com']`. */
191
+ readonly allowedDomains: readonly string[];
192
+ /** Which identity providers are offered. */
193
+ readonly providers: WorkspaceProviders;
194
+ /** Where unauthenticated requests are redirected. Default `/signin`. */
195
+ readonly signInPath?: string;
196
+ /** Env var holding the session-signing secret. Default `DOCS_SESSION_SECRET`. */
197
+ readonly sessionSecretEnv?: string;
198
+ /** Session lifetime in seconds. Default 8 hours. */
199
+ readonly sessionTtlSeconds?: number;
200
+ /** Set the `Secure` cookie flag. Default `true`; set `false` for local http. */
201
+ readonly secureCookies?: boolean;
202
+ }
203
+ /**
204
+ * How a docs site gates access.
205
+ *
206
+ * - `'public'` — anyone may read the docs and use the playground.
207
+ * - {@link WorkspaceAuthConfig} — only signed-in members of the allowed domains.
208
+ *
209
+ * @public
210
+ */
211
+ type AuthConfig = 'public' | WorkspaceAuthConfig;
212
+ /**
213
+ * A colour palette a site declares. Every token is optional; the framework
214
+ * falls back to its defaults for anything omitted. The site owns the values;
215
+ * the framework maps them to its CSS variables (see `buildThemeCss`).
216
+ *
217
+ * @public
218
+ */
219
+ interface ThemePalette {
220
+ /** Primary accent — active nav, links, buttons, focus rings. */
221
+ readonly primary?: string;
222
+ /** Page background. */
223
+ readonly background?: string;
224
+ /** Body text. */
225
+ readonly foreground?: string;
226
+ /** Panel/card surfaces. */
227
+ readonly card?: string;
228
+ /** Hairline borders and dividers. */
229
+ readonly border?: string;
230
+ /** Secondary/muted text. */
231
+ readonly muted?: string;
232
+ }
233
+ /**
234
+ * Light and dark logo image paths, resolved by the consuming site.
235
+ *
236
+ * @public
237
+ */
238
+ interface ThemeLogo {
239
+ readonly light: string;
240
+ readonly dark: string;
241
+ }
242
+ /**
243
+ * Brand configuration a site supplies. Colours and the logo live here — in the
244
+ * site — not in the framework.
245
+ *
246
+ * @public
247
+ */
248
+ interface ThemeConfig {
249
+ /** Display name. */
250
+ readonly name: string;
251
+ /** Light + dark logo paths (served by the site). */
252
+ readonly logo?: ThemeLogo;
253
+ /** Absolute or site-relative path to the favicon. */
254
+ readonly favicon?: string;
255
+ /** Palette applied to the light theme (and as the base for dark). */
256
+ readonly colors?: ThemePalette;
257
+ /** Palette overrides applied to the dark theme. */
258
+ readonly darkColors?: ThemePalette;
259
+ }
260
+ /**
261
+ * Security-critical: the exhaustive set of upstream API origins the try-it
262
+ * proxy is permitted to forward requests to. Any target outside this list is
263
+ * rejected with `403`. This is what keeps the proxy from becoming an open
264
+ * relay — see {@link https://github.com/blaaiz/docs-core | the proxy design}.
265
+ *
266
+ * @public
267
+ */
268
+ interface ProxyConfig {
269
+ /** Allowed upstream origins, e.g. `["https://api-dev.example.com"]`. */
270
+ readonly allowedOrigins: readonly string[];
271
+ /**
272
+ * Forwards per minute per client IP. Default 60. Enforced by both
273
+ * `createTryItProxy` and `createTryItProxyRoute`; pass the value through when
274
+ * you mount the route.
275
+ */
276
+ readonly rateLimitPerMinute?: number;
277
+ }
278
+ /**
279
+ * Search-engine and social-sharing metadata a site supplies. Everything is
280
+ * optional; the framework fills sensible defaults from {@link ThemeConfig.name}.
281
+ * The site owns these values — the canonical origin, the sharing image — because
282
+ * they are deployment facts, not framework behaviour.
283
+ *
284
+ * @public
285
+ */
286
+ interface SeoConfig {
287
+ /**
288
+ * The canonical public origin, e.g. `https://docs.example.com`. Enables
289
+ * absolute canonical URLs, the sitemap, and Open Graph URLs. Without it those
290
+ * fall back to relative URLs, which is correct but weaker for SEO.
291
+ */
292
+ readonly siteUrl?: string;
293
+ /**
294
+ * Title template for inner pages, with `%s` standing in for the page title,
295
+ * e.g. `'%s | Example Docs'`. Default `'%s · <theme.name>'`. The home title is
296
+ * {@link ThemeConfig.name} verbatim.
297
+ */
298
+ readonly titleTemplate?: string;
299
+ /** Default meta description, used where a page declares none. */
300
+ readonly description?: string;
301
+ /** Site-relative or absolute path to the default Open Graph / Twitter image. */
302
+ readonly ogImage?: string;
303
+ /** `@handle` for Twitter card attribution. */
304
+ readonly twitter?: string;
305
+ /** Default keywords applied site-wide. Pages may add their own. */
306
+ readonly keywords?: readonly string[];
307
+ /**
308
+ * Ask crawlers not to index the site (a private or pre-launch docs site).
309
+ * Default `false`. Auth-gated sites usually want `true`.
310
+ */
311
+ readonly noindex?: boolean;
312
+ }
313
+ /**
314
+ * Optional features a site opts into. Every flag defaults to `false`, so a site
315
+ * gets a feature only by asking for it. The framework ships the behaviour; the
316
+ * site decides whether to switch it on.
317
+ *
318
+ * @public
319
+ */
320
+ interface FeaturesConfig {
321
+ /**
322
+ * Show the "Copy page / View as Markdown" control on each doc page, and serve
323
+ * the plain-Markdown route it links to. Lets a reader hand the page to an LLM.
324
+ * Default `false`.
325
+ */
326
+ readonly copyPage?: boolean;
327
+ }
328
+ /**
329
+ * The complete, validated configuration for one docs site.
330
+ *
331
+ * @public
332
+ */
333
+ interface DocsConfig {
334
+ readonly theme: ThemeConfig;
335
+ readonly auth: AuthConfig;
336
+ readonly proxy: ProxyConfig;
337
+ /** Opt-in features. Omit it entirely and every feature stays off. */
338
+ readonly features?: FeaturesConfig;
339
+ /** Search-engine and social-sharing metadata. Omit for name-only defaults. */
340
+ readonly seo?: SeoConfig;
341
+ /**
342
+ * Ask AI settings. Omit the block and the feature stays off. Naming a
343
+ * provider is a complete setup, because every other field has a framework
344
+ * default. The API key never lives here — config names the environment
345
+ * variable, and the server reads it at runtime.
346
+ */
347
+ readonly ai?: AiConfig;
348
+ }
349
+ /**
350
+ * Thrown when a site's {@link DocsConfig} is malformed. The message names the
351
+ * exact field, the value it received, and the accepted values.
352
+ *
353
+ * The `ai` block is the one exception: {@link validateDocsConfig} delegates it
354
+ * to `resolveAiConfig`, which raises an `AiConfigError` instead. Both extend
355
+ * `Error`, so a site's build fails the same way either way.
356
+ *
357
+ * @public
358
+ */
359
+ declare class DocsConfigError extends Error {
360
+ constructor(message: string);
361
+ }
362
+ /**
363
+ * Validate a site's configuration and return it.
364
+ *
365
+ * {@link defineDocsConfig} calls it, so a site rarely calls it directly. It is
366
+ * exported for a site that loads its configuration from somewhere else — JSON,
367
+ * a remote source, a JavaScript file with no type-checking — and needs the same
368
+ * guarantee.
369
+ *
370
+ * Every field is checked at runtime, and the message names the exact field:
371
+ * `auth.mode must be 'workspace', got 'workspase'.` The `ai` block is delegated
372
+ * to `resolveAiConfig`, the single gate for Ask AI settings, so its messages are
373
+ * identical to the ones the Ask AI route raises.
374
+ *
375
+ * @param config - the site configuration, from any source
376
+ * @returns the same object, typed as {@link DocsConfig}
377
+ * @throws {@link DocsConfigError} when any field outside `ai` is missing or malformed
378
+ * @public
379
+ */
380
+ declare function validateDocsConfig(config: unknown): DocsConfig;
381
+ /**
382
+ * Identity helper that gives a site's `docs.config.ts` full type-checking and
383
+ * editor completion without importing types by hand.
384
+ *
385
+ * It also validates: every field is checked at runtime by
386
+ * {@link validateDocsConfig}, so a malformed value fails when the site builds,
387
+ * with a message that names the field. A JavaScript config file gets the same
388
+ * protection as a TypeScript one.
389
+ *
390
+ * @example
391
+ * ```ts
392
+ * import { defineDocsConfig } from '@blaaiz/docs-core';
393
+ *
394
+ * export default defineDocsConfig({
395
+ * theme: {
396
+ * name: 'Blaaiz',
397
+ * logo: { light: '/logo-light.png', dark: '/logo-dark.png' },
398
+ * colors: { primary: '#4c63f5' },
399
+ * darkColors: { background: '#0a0a0b', card: '#141416' },
400
+ * },
401
+ * auth: {
402
+ * mode: 'workspace',
403
+ * allowedDomains: ['blaaiz.com'],
404
+ * providers: { email: true, google: {} },
405
+ * },
406
+ * proxy: { allowedOrigins: ['https://api-dev.blaaiz.com'] },
407
+ * });
408
+ * ```
409
+ *
410
+ * @param config - the site configuration
411
+ * @returns the same configuration, typed as {@link DocsConfig}
412
+ * @throws {@link DocsConfigError} when any field outside `ai` is missing or malformed
413
+ * @public
414
+ */
415
+ declare function defineDocsConfig(config: DocsConfig): DocsConfig;
416
+
417
+ /**
418
+ * The normalized navigation model.
419
+ *
420
+ * A consuming site authors navigation in a Mintlify `docs.json`. The
421
+ * `docs-json` adapter parses that shape into this normalized tree, which the
422
+ * rest of the framework (rendering, search, the API reference) consumes. Keep
423
+ * this model free of any Mintlify-specific detail: it is the neutral contract.
424
+ *
425
+ * @packageDocumentation
426
+ */
427
+ /**
428
+ * An HTTP method that an API reference page documents.
429
+ *
430
+ * @public
431
+ */
432
+ type NavMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS' | 'TRACE';
433
+ /**
434
+ * A prose documentation page backed by an MDX/content file.
435
+ *
436
+ * @public
437
+ */
438
+ interface DocPage {
439
+ readonly kind: 'doc';
440
+ /** Path to the content file, relative to the site root, without extension. */
441
+ readonly file: string;
442
+ }
443
+ /**
444
+ * An API reference page backed by a single-operation OpenAPI file.
445
+ *
446
+ * @public
447
+ */
448
+ interface OpenApiPage {
449
+ readonly kind: 'openapi';
450
+ /** Path to the `*-openapi.json` file, relative to the site root. */
451
+ readonly file: string;
452
+ /** The operation's HTTP method. */
453
+ readonly method: NavMethod;
454
+ /** The operation's API route, e.g. `/api/user/login`. */
455
+ readonly apiPath: string;
456
+ }
457
+ /**
458
+ * A page in the navigation tree.
459
+ *
460
+ * @public
461
+ */
462
+ type NavPage = DocPage | OpenApiPage;
463
+ /**
464
+ * A named group of pages and/or nested groups. Groups may nest to any depth.
465
+ *
466
+ * @public
467
+ */
468
+ interface NavGroup {
469
+ readonly kind: 'group';
470
+ readonly title: string;
471
+ readonly items: readonly NavItem[];
472
+ }
473
+ /**
474
+ * Any node inside a tab: a page or a group.
475
+ *
476
+ * @public
477
+ */
478
+ type NavItem = NavPage | NavGroup;
479
+ /**
480
+ * A top-level navigation tab.
481
+ *
482
+ * @public
483
+ */
484
+ interface NavTab {
485
+ readonly kind: 'tab';
486
+ readonly title: string;
487
+ /** Optional icon name, passed through verbatim from the source. */
488
+ readonly icon?: string;
489
+ readonly items: readonly NavItem[];
490
+ }
491
+ /**
492
+ * The complete normalized navigation: an ordered list of tabs.
493
+ *
494
+ * @public
495
+ */
496
+ type Navigation = readonly NavTab[];
497
+
498
+ export { type AiConfig as A, type DocsConfig as D, type FeaturesConfig as F, type GoogleProviderConfig as G, type Navigation as N, type OpenApiPage as O, type ProxyConfig as P, type ResolvedAiConfig as R, type SecretProviderConfig as S, type ThemeConfig as T, type WorkspaceAuthConfig as W, AI_PROVIDER_MODELS as a, AI_PROVIDER_NAMES as b, AiConfigError as c, type AiProviderModels as d, type AiProviderName as e, type AuthConfig as f, type DocPage as g, DocsConfigError as h, type NavGroup as i, type NavItem as j, type NavMethod as k, type NavPage as l, type NavTab as m, type SeoConfig as n, type ThemeLogo as o, type ThemePalette as p, type WorkspaceProviders as q, defineDocsConfig as r, resolveAiConfig as s, validateDocsConfig as v };