dogsbay 0.2.0-beta.2 → 0.2.0-beta.21

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.
@@ -0,0 +1,294 @@
1
+ ---
2
+ name: dogsbay:multi-source
3
+ description: How sources[] aggregation works in Dogsbay — multiple content sources with locale, version, and namespace axes producing unified URLs and per-source nav. Use when configuring multi-source sites, multi-version setups, or i18n.
4
+ ---
5
+
6
+ # Multi-source aggregation
7
+
8
+ Dogsbay treats versioning, i18n, and multi-repo aggregation as
9
+ the **same problem with different metadata keys**. One mechanism
10
+ solves all three: each entry in `content.sources[]` carries
11
+ optional `name`, `version`, `locale` metadata, and the
12
+ aggregator activates a URL axis when ≥2 sources have distinct
13
+ values.
14
+
15
+ ## Three axes, one URL convention
16
+
17
+ ```
18
+ /<basePath>/<locale>/<version>/<namespace>/<page-slug>
19
+ ```
20
+
21
+ Each axis is **omitted from URLs when not in use**. The base
22
+ case (single locale, version, namespace) keeps URLs flat:
23
+ `/docs/intro`. Add axes by configuring sources; the URL grows
24
+ left-to-right in canonical order.
25
+
26
+ | Configuration | URL example |
27
+ |---|---|
28
+ | Single source, no axis metadata | `/docs/intro` |
29
+ | i18n only | `/docs/en/intro`, `/docs/fr/intro` |
30
+ | Versioning only | `/docs/v1/intro`, `/docs/latest/intro` |
31
+ | Locale + version | `/docs/en/v2/intro`, `/docs/fr/latest/intro` |
32
+ | Multi-namespace | `/docs/api-reference/intro`, `/docs/handbook/intro` |
33
+ | All three | `/docs/en/v2/api-reference/intro` |
34
+
35
+ Order: locale → version → namespace. Locale outermost matches
36
+ universal convention (Mintlify, Starlight, Cloudflare Docs).
37
+ Version inside locale because each locale has its own version
38
+ cadence.
39
+
40
+ ## When does an axis activate?
41
+
42
+ The aggregator inspects the configured sources at build time:
43
+
44
+ - **Locale axis** active when ≥2 distinct `locale:` values appear
45
+ - **Version axis** active when ≥2 distinct `version:` values appear
46
+ - **Namespace axis** active when ≥2 distinct `name:` values appear
47
+
48
+ `undefined` counts as a distinct bucket. So:
49
+
50
+ - One source with `locale: en`, one with no `locale:` → locale
51
+ axis IS active. The unspecified source's pages stay flat
52
+ (`/docs/page`); the `en:` source's pages get `/docs/en/page`.
53
+ - Five sources all with `locale: en` → locale axis is NOT active.
54
+ The metadata is uniform; no prefix needed.
55
+
56
+ This produces the "current + archived" / "default + i18n" patterns
57
+ naturally. Adding versioning to one source never silently
58
+ rewrites URLs in the unversioned baseline.
59
+
60
+ ## Source descriptor
61
+
62
+ ```yaml
63
+ content:
64
+ sources:
65
+ # Local filesystem
66
+ - name: docs
67
+ path: ./content/v1
68
+ version: v1
69
+ locale: en
70
+ from: dogsbay-md
71
+ primary: true
72
+
73
+ # Git checkout — fetched at build time
74
+ - name: api-reference
75
+ git: https://github.com/acme/api-docs.git
76
+ ref: v1.0
77
+ subdir: docs # optional path within the repo
78
+ version: v1
79
+
80
+ # Same source as a different version
81
+ - name: docs
82
+ path: ./content/v2
83
+ version: v2
84
+ locale: en
85
+ ```
86
+
87
+ | Field | Required | Notes |
88
+ |---|---|---|
89
+ | `path` OR `git` | yes (exactly one) | Origin |
90
+ | `ref` | required when `git:` is set | Branch / tag / sha |
91
+ | `subdir` | no | Subdirectory within resolved source |
92
+ | `name` | no | Namespace key |
93
+ | `version` | no | Version label |
94
+ | `locale` | no | BCP-47 locale tag |
95
+ | `from` | no | One of `auto`, `dogsbay-md`, `mkdocs`, `obsidian`, `starlight`, `mdx`, `openapi` |
96
+ | `nav` | no | Path to explicit nav file |
97
+ | `primary` | no | Marks this source for primary build mode |
98
+
99
+ ## Single canonical shape — `sources[]` only
100
+
101
+ Even one source uses the array. There is no `content.source:`
102
+ shorthand. Single-source pays one extra YAML line; in exchange
103
+ the schema, loader, and docs stay simple.
104
+
105
+ ```yaml
106
+ content:
107
+ sources:
108
+ - path: ./content
109
+ ```
110
+
111
+ ## Defaults for missing axis metadata
112
+
113
+ When an axis IS active but a source omits the key:
114
+
115
+ | Axis | Default |
116
+ |---|---|
117
+ | `locale` | falls back to `defaultLocale` if declared, else `undefined` (no segment) |
118
+ | `version` | falls back to `defaultVersion` if declared, else `undefined` (no segment) |
119
+ | `namespace` | `undefined` — no segment, even when other sources have names |
120
+
121
+ The `undefined`-as-distinct-bucket semantics let users mix
122
+ "current" + "archived" sources without explicit metadata on the
123
+ current one.
124
+
125
+ ## Build modes — `all` (default) vs `primary-only`
126
+
127
+ ```bash
128
+ dogsbay site build # all sources (default — full matrix)
129
+ dogsbay site build --primary-only # only sources marked primary: true
130
+ dogsbay site dev # primary-only (fast iteration)
131
+ dogsbay site dev --full # all sources (preview switcher chrome)
132
+ ```
133
+
134
+ `dogsbay site build` defaults to **publish mode** — every declared
135
+ source builds. Declaring `locales:`, `versions:`, or multiple
136
+ sources in config is a strong signal you want them all in
137
+ production; a writer who runs `site build` after declaring a
138
+ French locale should see French in the output, not silently dropped.
139
+
140
+ `--primary-only` is the opt-out for fast-iteration CI lint jobs
141
+ that don't need the full matrix (rare). When ≥1 source has
142
+ `primary: true`, primary-only loads just those. If NO source has
143
+ `primary: true`, the filter is inert (every source loads).
144
+
145
+ `dogsbay site dev` is the writer's iteration loop and defaults to
146
+ `primary-only` for speed. Use `--full` to preview the version /
147
+ locale switcher chrome locally.
148
+
149
+ The legacy `--publish` flag is kept as a deprecated no-op for
150
+ older scripts; publish IS the default now.
151
+
152
+ ## Namespace nav merging
153
+
154
+ Each source's nav becomes a top-level group in the unified
155
+ sidebar:
156
+
157
+ ```
158
+ Sidebar:
159
+ Docs ← namespace "docs" group
160
+ Getting started
161
+ Concepts
162
+
163
+ API reference ← namespace "api-reference" group
164
+ Authentication
165
+ Endpoints
166
+
167
+ ```
168
+
169
+ Sources without an explicit `name:` contribute their nav flat
170
+ (no group wrapper).
171
+
172
+ ## Per-page axis metadata
173
+
174
+ When ANY axis is active, every imported page gets
175
+ `page.multiSource` populated:
176
+
177
+ ```ts
178
+ {
179
+ namespace?: string; // when namespace axis active
180
+ version?: string; // when version axis active
181
+ locale?: string; // when locale axis active
182
+ originalSlug: string; // slug as the importer produced it
183
+ }
184
+ ```
185
+
186
+ Used by:
187
+
188
+ - The version-switcher UI (cross-version equivalents)
189
+ - The locale-switcher UI (translated equivalents)
190
+ - Hreflang / canonical tag emission
191
+ - The agent-readiness `.md` mirror with axis context
192
+
193
+ ## Common patterns
194
+
195
+ ### Versioning markdown sources
196
+
197
+ ```yaml
198
+ content:
199
+ sources:
200
+ - path: ./content # current — flat URLs
201
+ from: dogsbay-md
202
+ primary: true
203
+
204
+ - path: ./archive/v1
205
+ from: dogsbay-md
206
+ version: v1 # archived — /docs/v1/...
207
+
208
+ - path: ./archive/v0
209
+ from: dogsbay-md
210
+ version: v0 # archived — /docs/v0/...
211
+
212
+ versions:
213
+ - id: latest
214
+ label: Latest
215
+ default: true
216
+ - id: v1
217
+ label: v1
218
+ - id: v0
219
+ label: v0 (legacy)
220
+ eol: 2025-12-31 # tombstones in switcher
221
+ ```
222
+
223
+ ### Mixed prose + API ref with independent versions
224
+
225
+ ```yaml
226
+ content:
227
+ sources:
228
+ # Evergreen prose
229
+ - path: ./content
230
+ from: dogsbay-md
231
+
232
+ # API ref — date-pinned
233
+ - name: api
234
+ path: ./openapi/2024-04-10.yaml
235
+ from: openapi
236
+ version: "2024-04-10"
237
+ primary: true
238
+
239
+ - name: api
240
+ path: ./openapi/2023-12-01.yaml
241
+ from: openapi
242
+ version: "2023-12-01"
243
+ ```
244
+
245
+ URLs:
246
+ ```
247
+ /docs/ ← prose, versionless
248
+ /docs/api/2024-04-10/ ← API root, latest spec
249
+ /docs/api/2024-04-10/pets/list-pets ← operation
250
+ /docs/api/2023-12-01/pets/list-pets ← same op, prior version
251
+ ```
252
+
253
+ ### Multi-locale with git origin
254
+
255
+ ```yaml
256
+ content:
257
+ sources:
258
+ - path: ./content
259
+ locale: en
260
+ primary: true
261
+
262
+ - git: https://github.com/acme/docs-fr.git
263
+ ref: main
264
+ locale: fr
265
+
266
+ - git: https://github.com/acme/docs-pt-BR.git
267
+ ref: main
268
+ locale: pt-BR
269
+
270
+ locales:
271
+ - id: en
272
+ label: English
273
+ - id: fr
274
+ label: Français
275
+ - id: pt-BR
276
+ label: Português (Brasil)
277
+ ```
278
+
279
+ URLs: `/docs/en/...`, `/docs/fr/...`, `/docs/pt-BR/...`. Plus a
280
+ default-locale redirect from `/docs/...` to `/docs/en/...`.
281
+
282
+ ## Common mistakes
283
+
284
+ - ❌ Setting `version:` on one source and not declaring the
285
+ version in `versions:[]` — fails validation.
286
+ - ❌ Mixing `defaultVersion: latest` with no `version:` field on a
287
+ source — the source's pages get `/docs/latest/...` URLs even
288
+ when you wanted them flat. Drop `defaultVersion` for the
289
+ current-baseline pattern.
290
+ - ❌ Hand-editing `multiSource.namespace` in frontmatter — the
291
+ aggregator overwrites it on every build.
292
+ - ❌ Trying to emit nav hrefs with hand-written axis prefixes —
293
+ the aggregator does this; importers should write hrefs
294
+ relative to their content root.
@@ -0,0 +1,107 @@
1
+ ---
2
+ name: dogsbay:nav-file
3
+ description: Write or edit a Dogsbay nav file (nav.yml, nav.yaml, or nav.json) with the correct single-key-map syntax. Use when the user asks to add pages to the sidebar, reorder the sidebar, create grouped sections, or fix a malformed nav file.
4
+ ---
5
+
6
+ # Writing a Dogsbay nav file
7
+
8
+ Dogsbay's sidebar nav lives in the content directory under one of:
9
+
10
+ - `nav.json` (highest precedence)
11
+ - `nav.yml`
12
+ - `nav.yaml`
13
+
14
+ The loader auto-detects in that order. Most users pick `nav.yml` because YAML is friendlier to edit.
15
+
16
+ ## Format — single-key maps
17
+
18
+ Each entry is a **single-key map** where the key is the sidebar label and the value is one of:
19
+
20
+ - A file path relative to `content/` — leaf link
21
+ - An absolute URL (`http://`, `https://`, etc.) — external leaf
22
+ - A list of child entries — group
23
+
24
+ ```yaml
25
+ - Home: index.md
26
+ - Getting started: getting-started.md
27
+ - Guides:
28
+ - Configuration: guides/configuration.md
29
+ - Deployment: guides/deployment.md
30
+ - Source: https://github.com/your-org/your-repo
31
+ ```
32
+
33
+ ## What NOT to write
34
+
35
+ This is the most common mistake. The loader will print a "malformed" warning and fall back to directory-scan order:
36
+
37
+ ```yaml
38
+ # ❌ WRONG — multi-key map
39
+ - label: Home
40
+ href: /docs/
41
+
42
+ # ❌ WRONG — using href for internal pages
43
+ - Home: /docs/
44
+ - Getting started: /docs/getting-started
45
+
46
+ # ❌ WRONG — missing .md extension on file paths
47
+ - Home: index
48
+ ```
49
+
50
+ The validator throws `Nav entry must have exactly one key at [N]. Got 2: ["label","href"]` for the first form.
51
+
52
+ ## URL derivation — never write absolute paths for internal pages
53
+
54
+ URLs are composed at build time from the configured `basePath`, the active multi-source axes (locale / version / namespace), and the file path. Always use the **file path relative to the content directory**, never a hand-written URL.
55
+
56
+ If the user has `basePath: /docs` and a markdown source named `api`:
57
+
58
+ - `index.md` → `/docs/api/`
59
+ - `getting-started.md` → `/docs/api/getting-started`
60
+ - `guides/configuration.md` → `/docs/api/guides/configuration`
61
+
62
+ Hand-writing `/docs/getting-started` works today but breaks when the user changes `basePath`, adds a version axis, or activates locales. Always use the file path.
63
+
64
+ ## Groups vs leaf-with-children
65
+
66
+ A group has no associated page — clicking the label expands the children. To make the GROUP itself link to a page (a section landing page), use this combined shape:
67
+
68
+ ```yaml
69
+ - Guides:
70
+ - guides/index.md # bare string entry — sets the group's href to this page
71
+ - Configuration: guides/configuration.md
72
+ - Deployment: guides/deployment.md
73
+ ```
74
+
75
+ The leading bare-string entry is parsed as the group's own `href`/`file`. Without it, the group is an expand-only label.
76
+
77
+ ## External URLs
78
+
79
+ Use absolute URLs for external destinations. The loader treats anything starting with `http://`, `https://`, or `mailto:` as an external link (no axis prefixing applied):
80
+
81
+ ```yaml
82
+ - API reference: api/index.md
83
+ - GitHub: https://github.com/your-org/your-repo
84
+ - Status: https://status.your-org.com
85
+ - Contact: mailto:hello@your-org.com
86
+ ```
87
+
88
+ ## When there's no nav file
89
+
90
+ If `content/nav.{json,yml,yaml}` doesn't exist, the loader does a directory scan and produces nav from filenames. The order is alphabetical with `index.md` first per directory. This works for prototyping but quickly stops being what the user wants — encourage authoring `nav.yml` early.
91
+
92
+ ## When the user moves or renames a page
93
+
94
+ Update three places:
95
+
96
+ 1. The file itself (move/rename in `content/`)
97
+ 2. The `nav.yml` entry (update the file path)
98
+ 3. Add a redirect in `dogsbay.config.yml` if the old URL was published
99
+
100
+ ```yaml
101
+ # In dogsbay.config.yml
102
+ site:
103
+ redirects:
104
+ /docs/old-page: /docs/new-page
105
+ ```
106
+
107
+ Skipping (3) breaks bookmarks and external links.
@@ -0,0 +1,237 @@
1
+ ---
2
+ name: dogsbay:openapi-source
3
+ description: Add an OpenAPI 3.x spec as a content source in a Dogsbay site. Renders endpoint pages inline alongside markdown prose. Use when configuring API reference, multi-version OpenAPI, or x-codeSamples.
4
+ ---
5
+
6
+ # OpenAPI as a content source
7
+
8
+ Dogsbay treats OpenAPI 3.x as a first-class content source. The
9
+ `@dogsbay/format-openapi` importer reads the spec, parses it
10
+ with `@scalar/openapi-parser` (handles dereferencing, includes
11
+ Swagger 2 conversion), and emits one `.astro` page per
12
+ operation plus tag-overview pages and a root info page.
13
+
14
+ The rendering is **inline** — endpoints render through real
15
+ Dogsbay components (`EndpointCard`, `ApiLayout`,
16
+ `ParameterTable`, `SchemaViewer`, `ResponseTabs`,
17
+ `CodeSamples`), not via an iframe.
18
+
19
+ ## Basic config
20
+
21
+ ```yaml
22
+ content:
23
+ sources:
24
+ - path: ./content # markdown prose
25
+ from: dogsbay-md
26
+
27
+ - name: api
28
+ path: ./openapi/petstore.yaml # path to spec file (or URL)
29
+ from: openapi
30
+ ```
31
+
32
+ Required: `from: openapi`. Auto-detection (`from: auto`) doesn't
33
+ fire for OpenAPI — extension-based detection is too ambiguous.
34
+
35
+ ## Path forms
36
+
37
+ The `path:` value can be:
38
+
39
+ - A direct file path (`./openapi/petstore.yaml`, `./spec.json`)
40
+ - A directory containing `openapi.{yaml,yml,json}` or
41
+ `spec.{yaml,yml,json}` — auto-detected
42
+ - An HTTP(S) URL — fetched at build time
43
+
44
+ YAML and JSON specs both work; the parser handles both.
45
+
46
+ ## URLs produced
47
+
48
+ With `name: api` and `basePath: /docs`:
49
+
50
+ ```
51
+ /docs/api/ ← root info page (info.title + description)
52
+ /docs/api/<tag-slug>/ ← per-tag overview
53
+ /docs/api/<tag-slug>/<operation-slug> ← one per operation
54
+ ```
55
+
56
+ Operation slug derivation:
57
+
58
+ - `operationId` if set (kebab-cased) — most public specs set it
59
+ - Fallback: `{method}-{path-slug}` from the wire path
60
+
61
+ Stripe-style URLs (`/docs/api/pets/list-pets`) come from
62
+ operationId. Method-path URLs (`/docs/api/get-pets-petid`) come
63
+ from the fallback. Either way, the path is stable.
64
+
65
+ ## Multi-version OpenAPI
66
+
67
+ The version axis works for OpenAPI sources just like markdown
68
+ sources. Per-source independent versioning:
69
+
70
+ ```yaml
71
+ content:
72
+ sources:
73
+ - path: ./content # markdown evergreen
74
+ from: dogsbay-md
75
+
76
+ - name: api
77
+ path: ./openapi/2024-04-10.yaml
78
+ from: openapi
79
+ version: "2024-04-10"
80
+
81
+ - name: api
82
+ path: ./openapi/2023-12-01.yaml
83
+ from: openapi
84
+ version: "2023-12-01"
85
+
86
+ versions:
87
+ - id: "2024-04-10"
88
+ label: "2024-04-10 (latest)"
89
+ default: true
90
+ - id: "2023-12-01"
91
+ label: "2023-12-01"
92
+ ```
93
+
94
+ Resulting URLs:
95
+
96
+ ```
97
+ /docs/ ← prose, versionless
98
+ /docs/api/2024-04-10/pets/list-pets
99
+ /docs/api/2023-12-01/pets/list-pets
100
+ ```
101
+
102
+ The version-switcher UI scopes to whichever pages have axis
103
+ activity — only `/docs/api/...` pages show the switcher; prose
104
+ stays flat.
105
+
106
+ This is the **Mintlify-style independent-axis model** (Stripe,
107
+ Twilio, GitHub all do this). API moves on its own cadence;
108
+ prose stays evergreen.
109
+
110
+ ## Hand-curated code samples — `x-codeSamples`
111
+
112
+ Auto-generated samples (curl + Python + TypeScript) are
113
+ emitted by default. To override with hand-written examples,
114
+ use the Redoc convention `x-codeSamples` on the operation:
115
+
116
+ ```yaml
117
+ paths:
118
+ /pets:
119
+ post:
120
+ summary: Create a pet
121
+ x-codeSamples:
122
+ - lang: shell
123
+ label: cURL
124
+ source: |
125
+ curl -X POST "https://api.example.com/v1/pets" \
126
+ -H "Authorization: Bearer $TOKEN" \
127
+ -d '{"name":"Rex"}'
128
+ - lang: typescript
129
+ source: |
130
+ await client.pets.create({ name: "Rex" });
131
+ ```
132
+
133
+ When `x-codeSamples` is present, it **replaces** the
134
+ auto-generated samples entirely (not added alongside). The
135
+ legacy `x-code-samples` (kebab) spelling is also accepted.
136
+
137
+ ## Deprecating an operation
138
+
139
+ Set `deprecated: true`:
140
+
141
+ ```yaml
142
+ paths:
143
+ /orders:
144
+ post:
145
+ summary: Place an order
146
+ deprecated: true
147
+
148
+ ```
149
+
150
+ The endpoint page renders a warning banner. The `.md` mirror
151
+ prefixes the operation with a `> [!WARNING]` callout so agents
152
+ see the deprecation status too.
153
+
154
+ ## Components on the rendered page
155
+
156
+ Each operation page emits an `<ApiLayout>` with two slots:
157
+
158
+ - **description**: `<EndpointCard>` containing `<EndpointSection>`
159
+ blocks for parameters / request body / responses, using
160
+ `<PropertyList>` (Stripe-style location-grouped) /
161
+ `<SchemaViewer>` / `<ResponseTabs>`
162
+ - **code**: `<ApiCodePanel>` with `<CodeSamples>` plus per-status
163
+ `<ExampleBlock>` panels with content-type-aware syntax
164
+ highlighting
165
+
166
+ The full URL bar at the top of the description column tints
167
+ its background to the HTTP method colour (Stripe / Redoc /
168
+ Mintlify convention) — GET = green, POST = blue, etc.
169
+
170
+ ## What's NOT yet supported
171
+
172
+ - **Multi-server picker** (`servers[]` with sandbox / production)
173
+ — currently uses `servers[0].url` only.
174
+ - **`x-internal` honour** — known limitation; see
175
+ `plans/openapi-builtin-followups.md`.
176
+ - **Live "Try it" execution** — out of scope; ships as a future
177
+ plugin.
178
+ - **AsyncAPI / WebSocket / gRPC** — different specs; separate
179
+ importer.
180
+
181
+ ## Common patterns
182
+
183
+ ### Single-source API ref
184
+
185
+ ```yaml
186
+ content:
187
+ sources:
188
+ - path: ./openapi/petstore.yaml
189
+ from: openapi
190
+ ```
191
+
192
+ URLs: `/docs/`, `/docs/<tag>/<operation>` (no namespace prefix
193
+ because there's only one source).
194
+
195
+ ### Multi-source: prose + API ref under /docs/api/
196
+
197
+ ```yaml
198
+ content:
199
+ sources:
200
+ - path: ./content
201
+ from: dogsbay-md
202
+ - name: api
203
+ path: ./openapi/spec.yaml
204
+ from: openapi
205
+ ```
206
+
207
+ The seminal Dogsbay-OpenAPI pattern. Prose stays flat at
208
+ `/docs/`; API reference under `/docs/api/`.
209
+
210
+ ### Remote OpenAPI URL
211
+
212
+ ```yaml
213
+ content:
214
+ sources:
215
+ - name: api
216
+ path: https://api.example.com/openapi.json
217
+ from: openapi
218
+ ```
219
+
220
+ Fetched at build time. Useful when the spec is generated
221
+ upstream (e.g. by a backend codebase) and the docs site
222
+ shouldn't have a vendored copy.
223
+
224
+ ## Common mistakes
225
+
226
+ - ❌ Forgetting `from: openapi` — auto-detection fails on `.yaml`
227
+ / `.json` (too ambiguous), the source falls through importers
228
+ and errors.
229
+ - ❌ Setting `name: api` and expecting to write `/api/foo`
230
+ manually in a markdown link — the namespace prefix is
231
+ build-time-resolved, write `./api/foo` (relative) or use the
232
+ full `${basePath}/api/...`.
233
+ - ❌ Per-version OpenAPI with the same `version:` value on both
234
+ sources — version axis doesn't activate; pages collide. Each
235
+ spec needs a distinct version label.
236
+ - ❌ Using `x-codeSamples` AND expecting auto-generated samples
237
+ alongside — vendor samples replace, not augment.