@luckystack/docs-ui 0.4.1 → 0.5.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/CLAUDE.md +55 -55
- package/README.md +90 -90
- package/dist/{chunk-XFLDJWH2.js → chunk-3Y7IA5DO.js} +1 -1
- package/dist/chunk-3Y7IA5DO.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/register.js +1 -1
- package/dist/register.js.map +1 -1
- package/docs/extension-fields.md +169 -169
- package/docs/html-generation.md +194 -194
- package/docs/mounting.md +226 -226
- package/docs/theming.md +172 -172
- package/package.json +3 -3
- package/dist/chunk-XFLDJWH2.js.map +0 -1
package/docs/extension-fields.md
CHANGED
|
@@ -1,169 +1,169 @@
|
|
|
1
|
-
# Extension fields on `apiDocs.generated.json`
|
|
2
|
-
|
|
3
|
-
> Dev-only. These fields are read by the renderer in `@luckystack/docs-ui` only — the framework runtime never inspects them. They are a docs-presentation concern, not a routing concern.
|
|
4
|
-
|
|
5
|
-
`apiDocs.generated.json` is produced by `@luckystack/devkit`'s type-map emitter. The default template in `@luckystack/docs-ui` consumes a small, stable set of core fields and a handful of optional extension fields. Unknown fields pass through cleanly — the renderer ignores anything it doesn't recognize, so adding new metadata is forward-compatible.
|
|
6
|
-
|
|
7
|
-
This document covers:
|
|
8
|
-
|
|
9
|
-
- The top-level JSON shape (including the legacy fallback).
|
|
10
|
-
- Core fields the renderer reads at the top level of each endpoint entry.
|
|
11
|
-
- Optional extension fields rendered when present (`stream`, `owner`, `tags`, `deprecated`).
|
|
12
|
-
- How to add your own metadata via the devkit emitter hooks.
|
|
13
|
-
|
|
14
|
-
For the rendering pipeline that consumes these fields see `./html-generation.md`. For the route that serves the JSON see `./mounting.md`.
|
|
15
|
-
|
|
16
|
-
## Top-level JSON shape
|
|
17
|
-
|
|
18
|
-
The current artifact has two top-level maps — `apis` and `syncs` — each keyed by `page`. The value under each page is a **flat array** of endpoint entries (not a nested `name → version` object). Every entry carries its own `name`, `version`, and `path`:
|
|
19
|
-
|
|
20
|
-
```jsonc
|
|
21
|
-
{
|
|
22
|
-
"apis": {
|
|
23
|
-
"<page>": [
|
|
24
|
-
{
|
|
25
|
-
"page": "<page>",
|
|
26
|
-
"name": "<name>",
|
|
27
|
-
"version": "<version>",
|
|
28
|
-
"method": "POST",
|
|
29
|
-
"auth": { "login": true, "additional": [] },
|
|
30
|
-
"rateLimit": 60,
|
|
31
|
-
"input": "{ userId: string }",
|
|
32
|
-
"output": "{ status: 'success', result: { ... } }",
|
|
33
|
-
"stream": "never",
|
|
34
|
-
"path": "api/<page>/<name>/<version>",
|
|
35
|
-
"meta": { "owner": "@team", "tags": ["internal"] }
|
|
36
|
-
}
|
|
37
|
-
]
|
|
38
|
-
},
|
|
39
|
-
"syncs": {
|
|
40
|
-
"<page>": [
|
|
41
|
-
{
|
|
42
|
-
"page": "<page>",
|
|
43
|
-
"name": "<name>",
|
|
44
|
-
"version": "<version>",
|
|
45
|
-
"clientInput": "{ ... }",
|
|
46
|
-
"serverOutput": "{ ... }",
|
|
47
|
-
"clientOutput": "{ ... }",
|
|
48
|
-
"serverStream": "never",
|
|
49
|
-
"clientStream": "never",
|
|
50
|
-
"path": "sync/<page>/<name>/<version>"
|
|
51
|
-
}
|
|
52
|
-
]
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Legacy/forward fallback: if the root object is the apis map itself (no `apis` wrapper), the renderer normalizes via `data && data.apis ? data.apis : data`. Both shapes are accepted; new code should always emit the wrapped form.
|
|
58
|
-
|
|
59
|
-
The grouping is `page` → array of entries; `name`, `version`, `method`, `path`, etc. are fields ON each entry. This matches the file-based routing convention (`src/<page>/_api/<name>_v<version>.ts`). The renderer iterates the pages in object-iteration order and each page's array in array order; the emitter is responsible for any sorting.
|
|
60
|
-
|
|
61
|
-
## Core fields (always read by the renderer)
|
|
62
|
-
|
|
63
|
-
These are the fields used to build the always-visible row + the default expanded panel:
|
|
64
|
-
|
|
65
|
-
| Field | Type | Default in renderer | Purpose |
|
|
66
|
-
| --- | --- | --- | --- |
|
|
67
|
-
| `method` | `'GET' \| 'POST' \| 'PUT' \| 'DELETE'` (any string, uppercased) | `'POST'` | Method pill text + CSS class on `.method`. |
|
|
68
|
-
| `rateLimit` | `number \| false \| undefined` | `'default rate'` label | Numeric ⇒ `"<n>/min"`. `false` ⇒ `"no rate limit"`. `undefined` ⇒ `"default rate"`. |
|
|
69
|
-
| `auth` | `{ login?: boolean; additional?: Array<{ key: string; value?: unknown }> }` | `'public'` badge | `auth.login` becomes a `"login required"` badge. Each `additional[]` entry becomes a `"<key>"` or `"<key>=<JSON-stringified value>"` badge. Empty / missing ⇒ single `"public"` badge. |
|
|
70
|
-
| `input` | `string` (already-stringified TS shape) | `'{}'` | Rendered inside `<pre>` in the expanded panel. |
|
|
71
|
-
| `output` | `string` (already-stringified TS shape) | `'unknown'` | Rendered inside `<pre>` in the expanded panel. |
|
|
72
|
-
|
|
73
|
-
The renderer does not try to JSON-parse `input` / `output`. They are emitted as pre-formatted strings by the devkit emitter so the docs page can display the original TypeScript shape verbatim — including unions, mapped types, and template-literal types — without re-stringifying.
|
|
74
|
-
|
|
75
|
-
Unknown core fields fall through to their defaults silently; emit a missing `method`, for example, and the row shows `POST`.
|
|
76
|
-
|
|
77
|
-
## Optional extension fields
|
|
78
|
-
|
|
79
|
-
The default template renders these only when present. They are additive: omit any of them and the corresponding section is left out of the expanded panel.
|
|
80
|
-
|
|
81
|
-
### `stream` (top-level)
|
|
82
|
-
|
|
83
|
-
- Where: a **top-level** field on the entry (a sibling of `input` / `output`), **not** under `meta`. The renderer reads `entry.stream` and shows the section only when the value is truthy and not the `"never"` sentinel.
|
|
84
|
-
- Type: `string` (already-stringified TS shape, same convention as `input` / `output`).
|
|
85
|
-
- Renders as: a `stream` detail section with the value inside a `<pre>` block.
|
|
86
|
-
- Use for endpoints that stream chunks back to the client (e.g. SSE or chunked-transfer responses). The string typically describes the shape of a single chunk.
|
|
87
|
-
- Example payload field:
|
|
88
|
-
|
|
89
|
-
```jsonc
|
|
90
|
-
{
|
|
91
|
-
"stream": "{ delta: string } | { done: true }"
|
|
92
|
-
}
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### `meta.owner`
|
|
96
|
-
|
|
97
|
-
- Type: `string`.
|
|
98
|
-
- Renders as: an `owner` detail section with the raw string.
|
|
99
|
-
- Use for triage-friendly attribution — team name, Slack channel, GitHub handle, on-call rotation, etc. The value is escaped before injection so it is safe to include arbitrary text.
|
|
100
|
-
- Example:
|
|
101
|
-
|
|
102
|
-
```jsonc
|
|
103
|
-
{ "owner": "@platform-team (#help-platform)" }
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### `meta.tags`
|
|
107
|
-
|
|
108
|
-
- Type: `string[]` (or array of values coerced via `String(...)`).
|
|
109
|
-
- Renders as: inline badges in a `tags` detail section, space-separated.
|
|
110
|
-
- Use for cross-cutting categorization the page/name hierarchy can't express — `"v2-migration"`, `"internal"`, `"experimental"`, etc.
|
|
111
|
-
- Example:
|
|
112
|
-
|
|
113
|
-
```jsonc
|
|
114
|
-
{ "tags": ["experimental", "internal"] }
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
Note: the renderer treats `tags` as an `Array.isArray(value)` check; arbitrary iterables are not supported. Emit a plain array.
|
|
118
|
-
|
|
119
|
-
### `meta.deprecated`
|
|
120
|
-
|
|
121
|
-
- Type: `string | true` (anything truthy; `String(value)` is used for display).
|
|
122
|
-
- Renders as: a `deprecated` detail section with red text (`color: var(--delete)`).
|
|
123
|
-
- Use for a removal notice or migration pointer. The truthiness check means `"true"`, `"yes"`, `"removing v2026.05"`, etc. all render — but the renderer always shows the string value, so prefer a human-readable migration note over a bare boolean.
|
|
124
|
-
- Example:
|
|
125
|
-
|
|
126
|
-
```jsonc
|
|
127
|
-
{
|
|
128
|
-
"deprecated": "Removed in v2026.05 — use users/getProfile/v2 instead."
|
|
129
|
-
}
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
## Forward compatibility
|
|
133
|
-
|
|
134
|
-
- Adding a new field to a `meta` object is non-breaking. The renderer ignores it.
|
|
135
|
-
- Removing one of the four extension fields above does **not** break the renderer either — each is conditional on truthiness or array length.
|
|
136
|
-
- The renderer does no schema validation. Malformed top-level JSON (e.g. wrong types at the page/name/version levels) shows the page's empty state with a hint to regenerate.
|
|
137
|
-
|
|
138
|
-
If you need extra fields on the page, you have two options:
|
|
139
|
-
|
|
140
|
-
1. Extend the devkit emitter to include the field, then customize the rendering side via a `DocsTemplateBuilder` (see `./theming.md`) or, for trivial additions, contribute the field to this package.
|
|
141
|
-
2. Keep your metadata out-of-band (a sibling JSON file) and consume it from a custom template. The default renderer will not surface it, but a `DocsTemplateBuilder` can fetch both files.
|
|
142
|
-
|
|
143
|
-
## Producer contract — how fields are populated
|
|
144
|
-
|
|
145
|
-
`apiDocs.generated.json` is written by `@luckystack/devkit`'s type-map emitter on each dev-server reload and on every `npm run generateArtifacts`. Path resolution goes through `ProjectConfig.paths.generatedApiDocs` (consumed by `getGeneratedApiDocsPath()` in `@luckystack/core`).
|
|
146
|
-
|
|
147
|
-
Emission steps the devkit performs per endpoint:
|
|
148
|
-
|
|
149
|
-
1. Read the route file (`src/<page>/_api/<name>_v<version>.ts`) and extract exports: `method`, `rateLimit`, `auth`, the `ApiParams.data` interface (rendered into `input`), and the `main` return type (rendered into `output`).
|
|
150
|
-
2. Stringify the types into source-faithful strings — no re-stringification, no JSON.stringify of types.
|
|
151
|
-
3. Run the emitter's `emitterArtifacts` extension hooks (`packages/devkit/src/typeMap/emitterArtifacts.ts`). Hooks can attach `stream`, `owner`, `tags`, `deprecated`, or any new field.
|
|
152
|
-
4. Group by `page` (one flat array of entries per page, each entry carrying its own `name` / `version` / `path`) and write the JSON.
|
|
153
|
-
|
|
154
|
-
Consumer-side extension (e.g. reading a `@deprecated` JSDoc tag and mapping it to `meta.deprecated`) belongs in an emitter artifact hook, not in `@luckystack/docs-ui`. The docs-ui package only renders what the emitter writes.
|
|
155
|
-
|
|
156
|
-
## Validation behavior
|
|
157
|
-
|
|
158
|
-
- No schema validation. The renderer trusts the JSON shape.
|
|
159
|
-
- A missing artifact (`apiDocs.generated.json` not on disk) yields a `404 JSON` response from the `mountDocsUi` handler with `expectedAt` + `hint` — surfaced as an inline error message in the page.
|
|
160
|
-
- An empty / invalid `apis` map yields the page's empty state: `"No API docs available. Run npm run generateArtifacts to generate them."`.
|
|
161
|
-
- Per-endpoint malformations (e.g. `auth` is a string instead of an object) fall back to defaults silently. Bad data does not crash the page; it just shows degraded content.
|
|
162
|
-
|
|
163
|
-
## Related
|
|
164
|
-
|
|
165
|
-
- Rendering pipeline that consumes these fields: `./html-generation.md`
|
|
166
|
-
- Mount + route: `./mounting.md`
|
|
167
|
-
- Branding / custom templates: `./theming.md`
|
|
168
|
-
- AI summary + function index: `../CLAUDE.md`
|
|
169
|
-
- Producer: `packages/devkit/src/typeMap/emitter.ts` and `packages/devkit/src/typeMap/emitterArtifacts.ts`
|
|
1
|
+
# Extension fields on `apiDocs.generated.json`
|
|
2
|
+
|
|
3
|
+
> Dev-only. These fields are read by the renderer in `@luckystack/docs-ui` only — the framework runtime never inspects them. They are a docs-presentation concern, not a routing concern.
|
|
4
|
+
|
|
5
|
+
`apiDocs.generated.json` is produced by `@luckystack/devkit`'s type-map emitter. The default template in `@luckystack/docs-ui` consumes a small, stable set of core fields and a handful of optional extension fields. Unknown fields pass through cleanly — the renderer ignores anything it doesn't recognize, so adding new metadata is forward-compatible.
|
|
6
|
+
|
|
7
|
+
This document covers:
|
|
8
|
+
|
|
9
|
+
- The top-level JSON shape (including the legacy fallback).
|
|
10
|
+
- Core fields the renderer reads at the top level of each endpoint entry.
|
|
11
|
+
- Optional extension fields rendered when present (`stream`, `owner`, `tags`, `deprecated`).
|
|
12
|
+
- How to add your own metadata via the devkit emitter hooks.
|
|
13
|
+
|
|
14
|
+
For the rendering pipeline that consumes these fields see `./html-generation.md`. For the route that serves the JSON see `./mounting.md`.
|
|
15
|
+
|
|
16
|
+
## Top-level JSON shape
|
|
17
|
+
|
|
18
|
+
The current artifact has two top-level maps — `apis` and `syncs` — each keyed by `page`. The value under each page is a **flat array** of endpoint entries (not a nested `name → version` object). Every entry carries its own `name`, `version`, and `path`:
|
|
19
|
+
|
|
20
|
+
```jsonc
|
|
21
|
+
{
|
|
22
|
+
"apis": {
|
|
23
|
+
"<page>": [
|
|
24
|
+
{
|
|
25
|
+
"page": "<page>",
|
|
26
|
+
"name": "<name>",
|
|
27
|
+
"version": "<version>",
|
|
28
|
+
"method": "POST",
|
|
29
|
+
"auth": { "login": true, "additional": [] },
|
|
30
|
+
"rateLimit": 60,
|
|
31
|
+
"input": "{ userId: string }",
|
|
32
|
+
"output": "{ status: 'success', result: { ... } }",
|
|
33
|
+
"stream": "never",
|
|
34
|
+
"path": "api/<page>/<name>/<version>",
|
|
35
|
+
"meta": { "owner": "@team", "tags": ["internal"] }
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
"syncs": {
|
|
40
|
+
"<page>": [
|
|
41
|
+
{
|
|
42
|
+
"page": "<page>",
|
|
43
|
+
"name": "<name>",
|
|
44
|
+
"version": "<version>",
|
|
45
|
+
"clientInput": "{ ... }",
|
|
46
|
+
"serverOutput": "{ ... }",
|
|
47
|
+
"clientOutput": "{ ... }",
|
|
48
|
+
"serverStream": "never",
|
|
49
|
+
"clientStream": "never",
|
|
50
|
+
"path": "sync/<page>/<name>/<version>"
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Legacy/forward fallback: if the root object is the apis map itself (no `apis` wrapper), the renderer normalizes via `data && data.apis ? data.apis : data`. Both shapes are accepted; new code should always emit the wrapped form.
|
|
58
|
+
|
|
59
|
+
The grouping is `page` → array of entries; `name`, `version`, `method`, `path`, etc. are fields ON each entry. This matches the file-based routing convention (`src/<page>/_api/<name>_v<version>.ts`). The renderer iterates the pages in object-iteration order and each page's array in array order; the emitter is responsible for any sorting.
|
|
60
|
+
|
|
61
|
+
## Core fields (always read by the renderer)
|
|
62
|
+
|
|
63
|
+
These are the fields used to build the always-visible row + the default expanded panel:
|
|
64
|
+
|
|
65
|
+
| Field | Type | Default in renderer | Purpose |
|
|
66
|
+
| --- | --- | --- | --- |
|
|
67
|
+
| `method` | `'GET' \| 'POST' \| 'PUT' \| 'DELETE'` (any string, uppercased) | `'POST'` | Method pill text + CSS class on `.method`. |
|
|
68
|
+
| `rateLimit` | `number \| false \| undefined` | `'default rate'` label | Numeric ⇒ `"<n>/min"`. `false` ⇒ `"no rate limit"`. `undefined` ⇒ `"default rate"`. |
|
|
69
|
+
| `auth` | `{ login?: boolean; additional?: Array<{ key: string; value?: unknown }> }` | `'public'` badge | `auth.login` becomes a `"login required"` badge. Each `additional[]` entry becomes a `"<key>"` or `"<key>=<JSON-stringified value>"` badge. Empty / missing ⇒ single `"public"` badge. |
|
|
70
|
+
| `input` | `string` (already-stringified TS shape) | `'{}'` | Rendered inside `<pre>` in the expanded panel. |
|
|
71
|
+
| `output` | `string` (already-stringified TS shape) | `'unknown'` | Rendered inside `<pre>` in the expanded panel. |
|
|
72
|
+
|
|
73
|
+
The renderer does not try to JSON-parse `input` / `output`. They are emitted as pre-formatted strings by the devkit emitter so the docs page can display the original TypeScript shape verbatim — including unions, mapped types, and template-literal types — without re-stringifying.
|
|
74
|
+
|
|
75
|
+
Unknown core fields fall through to their defaults silently; emit a missing `method`, for example, and the row shows `POST`.
|
|
76
|
+
|
|
77
|
+
## Optional extension fields
|
|
78
|
+
|
|
79
|
+
The default template renders these only when present. They are additive: omit any of them and the corresponding section is left out of the expanded panel.
|
|
80
|
+
|
|
81
|
+
### `stream` (top-level)
|
|
82
|
+
|
|
83
|
+
- Where: a **top-level** field on the entry (a sibling of `input` / `output`), **not** under `meta`. The renderer reads `entry.stream` and shows the section only when the value is truthy and not the `"never"` sentinel.
|
|
84
|
+
- Type: `string` (already-stringified TS shape, same convention as `input` / `output`).
|
|
85
|
+
- Renders as: a `stream` detail section with the value inside a `<pre>` block.
|
|
86
|
+
- Use for endpoints that stream chunks back to the client (e.g. SSE or chunked-transfer responses). The string typically describes the shape of a single chunk.
|
|
87
|
+
- Example payload field:
|
|
88
|
+
|
|
89
|
+
```jsonc
|
|
90
|
+
{
|
|
91
|
+
"stream": "{ delta: string } | { done: true }"
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### `meta.owner`
|
|
96
|
+
|
|
97
|
+
- Type: `string`.
|
|
98
|
+
- Renders as: an `owner` detail section with the raw string.
|
|
99
|
+
- Use for triage-friendly attribution — team name, Slack channel, GitHub handle, on-call rotation, etc. The value is escaped before injection so it is safe to include arbitrary text.
|
|
100
|
+
- Example:
|
|
101
|
+
|
|
102
|
+
```jsonc
|
|
103
|
+
{ "owner": "@platform-team (#help-platform)" }
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### `meta.tags`
|
|
107
|
+
|
|
108
|
+
- Type: `string[]` (or array of values coerced via `String(...)`).
|
|
109
|
+
- Renders as: inline badges in a `tags` detail section, space-separated.
|
|
110
|
+
- Use for cross-cutting categorization the page/name hierarchy can't express — `"v2-migration"`, `"internal"`, `"experimental"`, etc.
|
|
111
|
+
- Example:
|
|
112
|
+
|
|
113
|
+
```jsonc
|
|
114
|
+
{ "tags": ["experimental", "internal"] }
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Note: the renderer treats `tags` as an `Array.isArray(value)` check; arbitrary iterables are not supported. Emit a plain array.
|
|
118
|
+
|
|
119
|
+
### `meta.deprecated`
|
|
120
|
+
|
|
121
|
+
- Type: `string | true` (anything truthy; `String(value)` is used for display).
|
|
122
|
+
- Renders as: a `deprecated` detail section with red text (`color: var(--delete)`).
|
|
123
|
+
- Use for a removal notice or migration pointer. The truthiness check means `"true"`, `"yes"`, `"removing v2026.05"`, etc. all render — but the renderer always shows the string value, so prefer a human-readable migration note over a bare boolean.
|
|
124
|
+
- Example:
|
|
125
|
+
|
|
126
|
+
```jsonc
|
|
127
|
+
{
|
|
128
|
+
"deprecated": "Removed in v2026.05 — use users/getProfile/v2 instead."
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Forward compatibility
|
|
133
|
+
|
|
134
|
+
- Adding a new field to a `meta` object is non-breaking. The renderer ignores it.
|
|
135
|
+
- Removing one of the four extension fields above does **not** break the renderer either — each is conditional on truthiness or array length.
|
|
136
|
+
- The renderer does no schema validation. Malformed top-level JSON (e.g. wrong types at the page/name/version levels) shows the page's empty state with a hint to regenerate.
|
|
137
|
+
|
|
138
|
+
If you need extra fields on the page, you have two options:
|
|
139
|
+
|
|
140
|
+
1. Extend the devkit emitter to include the field, then customize the rendering side via a `DocsTemplateBuilder` (see `./theming.md`) or, for trivial additions, contribute the field to this package.
|
|
141
|
+
2. Keep your metadata out-of-band (a sibling JSON file) and consume it from a custom template. The default renderer will not surface it, but a `DocsTemplateBuilder` can fetch both files.
|
|
142
|
+
|
|
143
|
+
## Producer contract — how fields are populated
|
|
144
|
+
|
|
145
|
+
`apiDocs.generated.json` is written by `@luckystack/devkit`'s type-map emitter on each dev-server reload and on every `npm run generateArtifacts`. Path resolution goes through `ProjectConfig.paths.generatedApiDocs` (consumed by `getGeneratedApiDocsPath()` in `@luckystack/core`).
|
|
146
|
+
|
|
147
|
+
Emission steps the devkit performs per endpoint:
|
|
148
|
+
|
|
149
|
+
1. Read the route file (`src/<page>/_api/<name>_v<version>.ts`) and extract exports: `method`, `rateLimit`, `auth`, the `ApiParams.data` interface (rendered into `input`), and the `main` return type (rendered into `output`).
|
|
150
|
+
2. Stringify the types into source-faithful strings — no re-stringification, no JSON.stringify of types.
|
|
151
|
+
3. Run the emitter's `emitterArtifacts` extension hooks (`packages/devkit/src/typeMap/emitterArtifacts.ts`). Hooks can attach `stream`, `owner`, `tags`, `deprecated`, or any new field.
|
|
152
|
+
4. Group by `page` (one flat array of entries per page, each entry carrying its own `name` / `version` / `path`) and write the JSON.
|
|
153
|
+
|
|
154
|
+
Consumer-side extension (e.g. reading a `@deprecated` JSDoc tag and mapping it to `meta.deprecated`) belongs in an emitter artifact hook, not in `@luckystack/docs-ui`. The docs-ui package only renders what the emitter writes.
|
|
155
|
+
|
|
156
|
+
## Validation behavior
|
|
157
|
+
|
|
158
|
+
- No schema validation. The renderer trusts the JSON shape.
|
|
159
|
+
- A missing artifact (`apiDocs.generated.json` not on disk) yields a `404 JSON` response from the `mountDocsUi` handler with `expectedAt` + `hint` — surfaced as an inline error message in the page.
|
|
160
|
+
- An empty / invalid `apis` map yields the page's empty state: `"No API docs available. Run npm run generateArtifacts to generate them."`.
|
|
161
|
+
- Per-endpoint malformations (e.g. `auth` is a string instead of an object) fall back to defaults silently. Bad data does not crash the page; it just shows degraded content.
|
|
162
|
+
|
|
163
|
+
## Related
|
|
164
|
+
|
|
165
|
+
- Rendering pipeline that consumes these fields: `./html-generation.md`
|
|
166
|
+
- Mount + route: `./mounting.md`
|
|
167
|
+
- Branding / custom templates: `./theming.md`
|
|
168
|
+
- AI summary + function index: `../CLAUDE.md`
|
|
169
|
+
- Producer: `packages/devkit/src/typeMap/emitter.ts` and `packages/devkit/src/typeMap/emitterArtifacts.ts`
|