@luckystack/docs-ui 0.1.4 → 0.1.5
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/CHANGELOG.md +14 -14
- package/LICENSE +21 -21
- package/docs/extension-fields.md +150 -150
- package/docs/html-generation.md +190 -190
- package/docs/mounting.md +221 -221
- package/docs/theming.md +172 -172
- package/package.json +55 -55
package/CHANGELOG.md
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to `@luckystack/docs-ui` are documented in this file.
|
|
4
|
-
|
|
5
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
-
|
|
8
|
-
## [Unreleased]
|
|
9
|
-
|
|
10
|
-
## [0.1.0]
|
|
11
|
-
|
|
12
|
-
### Added
|
|
13
|
-
|
|
14
|
-
- Initial public release as part of the LuckyStack package split.
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@luckystack/docs-ui` are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0]
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Initial public release as part of the LuckyStack package split.
|
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Mathijs van Melick
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mathijs van Melick
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/docs/extension-fields.md
CHANGED
|
@@ -1,150 +1,150 @@
|
|
|
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 from each endpoint's `meta` object.
|
|
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 is:
|
|
19
|
-
|
|
20
|
-
```jsonc
|
|
21
|
-
{
|
|
22
|
-
"apis": {
|
|
23
|
-
"<page>": {
|
|
24
|
-
"<name>": {
|
|
25
|
-
"<version>": {
|
|
26
|
-
"method": "POST",
|
|
27
|
-
"auth": { "login": true, "additional": [] },
|
|
28
|
-
"rateLimit": 60,
|
|
29
|
-
"input": "{ userId: string }",
|
|
30
|
-
"output": "{ status: 'success', result: { ... } }",
|
|
31
|
-
// ...optional extension fields...
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
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.
|
|
40
|
-
|
|
41
|
-
The grouping levels — `page` → `name` → `version` — match the file-based routing convention (`src/<page>/_api/<name>_v<version>.ts`). The renderer iterates these in object-iteration order; the emitter is responsible for any sorting.
|
|
42
|
-
|
|
43
|
-
## Core fields (always read by the renderer)
|
|
44
|
-
|
|
45
|
-
These are the fields used to build the always-visible row + the default expanded panel:
|
|
46
|
-
|
|
47
|
-
| Field | Type | Default in renderer | Purpose |
|
|
48
|
-
| --- | --- | --- | --- |
|
|
49
|
-
| `meta.method` | `'GET' \| 'POST' \| 'PUT' \| 'DELETE'` (any string, uppercased) | `'POST'` | Method pill text + CSS class on `.method`. |
|
|
50
|
-
| `meta.rateLimit` | `number \| false \| undefined` | `'default rate'` label | Numeric ⇒ `"<n>/min"`. `false` ⇒ `"no rate limit"`. `undefined` ⇒ `"default rate"`. |
|
|
51
|
-
| `meta.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. |
|
|
52
|
-
| `meta.input` | `string` (already-stringified TS shape) | `'{}'` | Rendered inside `<pre>` in the expanded panel. |
|
|
53
|
-
| `meta.output` | `string` (already-stringified TS shape) | `'unknown'` | Rendered inside `<pre>` in the expanded panel. |
|
|
54
|
-
|
|
55
|
-
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.
|
|
56
|
-
|
|
57
|
-
Unknown core fields fall through to their defaults silently; emit a missing `method`, for example, and the row shows `POST`.
|
|
58
|
-
|
|
59
|
-
## Optional extension fields
|
|
60
|
-
|
|
61
|
-
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.
|
|
62
|
-
|
|
63
|
-
### `meta.stream`
|
|
64
|
-
|
|
65
|
-
- Type: `string` (already-stringified TS shape, same convention as `input` / `output`).
|
|
66
|
-
- Renders as: a `stream` detail section with the value inside a `<pre>` block.
|
|
67
|
-
- 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.
|
|
68
|
-
- Example payload field:
|
|
69
|
-
|
|
70
|
-
```jsonc
|
|
71
|
-
{
|
|
72
|
-
"stream": "{ delta: string } | { done: true }"
|
|
73
|
-
}
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### `meta.owner`
|
|
77
|
-
|
|
78
|
-
- Type: `string`.
|
|
79
|
-
- Renders as: an `owner` detail section with the raw string.
|
|
80
|
-
- 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.
|
|
81
|
-
- Example:
|
|
82
|
-
|
|
83
|
-
```jsonc
|
|
84
|
-
{ "owner": "@platform-team (#help-platform)" }
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
### `meta.tags`
|
|
88
|
-
|
|
89
|
-
- Type: `string[]` (or array of values coerced via `String(...)`).
|
|
90
|
-
- Renders as: inline badges in a `tags` detail section, space-separated.
|
|
91
|
-
- Use for cross-cutting categorization the page/name hierarchy can't express — `"v2-migration"`, `"internal"`, `"experimental"`, etc.
|
|
92
|
-
- Example:
|
|
93
|
-
|
|
94
|
-
```jsonc
|
|
95
|
-
{ "tags": ["experimental", "internal"] }
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
Note: the renderer treats `tags` as an `Array.isArray(value)` check; arbitrary iterables are not supported. Emit a plain array.
|
|
99
|
-
|
|
100
|
-
### `meta.deprecated`
|
|
101
|
-
|
|
102
|
-
- Type: `string | true` (anything truthy; `String(value)` is used for display).
|
|
103
|
-
- Renders as: a `deprecated` detail section with red text (`color: var(--delete)`).
|
|
104
|
-
- 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.
|
|
105
|
-
- Example:
|
|
106
|
-
|
|
107
|
-
```jsonc
|
|
108
|
-
{
|
|
109
|
-
"deprecated": "Removed in v2026.05 — use users/getProfile/v2 instead."
|
|
110
|
-
}
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
## Forward compatibility
|
|
114
|
-
|
|
115
|
-
- Adding a new field to a `meta` object is non-breaking. The renderer ignores it.
|
|
116
|
-
- Removing one of the four extension fields above does **not** break the renderer either — each is conditional on truthiness or array length.
|
|
117
|
-
- 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.
|
|
118
|
-
|
|
119
|
-
If you need extra fields on the page, you have two options:
|
|
120
|
-
|
|
121
|
-
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.
|
|
122
|
-
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.
|
|
123
|
-
|
|
124
|
-
## Producer contract — how fields are populated
|
|
125
|
-
|
|
126
|
-
`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`).
|
|
127
|
-
|
|
128
|
-
Emission steps the devkit performs per endpoint:
|
|
129
|
-
|
|
130
|
-
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`).
|
|
131
|
-
2. Stringify the types into source-faithful strings — no re-stringification, no JSON.stringify of types.
|
|
132
|
-
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.
|
|
133
|
-
4. Group by `page` → `name` → `version` and write the JSON.
|
|
134
|
-
|
|
135
|
-
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.
|
|
136
|
-
|
|
137
|
-
## Validation behavior
|
|
138
|
-
|
|
139
|
-
- No schema validation. The renderer trusts the JSON shape.
|
|
140
|
-
- 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.
|
|
141
|
-
- An empty / invalid `apis` map yields the page's empty state: `"No API docs available. Run npm run generateArtifacts to generate them."`.
|
|
142
|
-
- 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.
|
|
143
|
-
|
|
144
|
-
## Related
|
|
145
|
-
|
|
146
|
-
- Rendering pipeline that consumes these fields: `./html-generation.md`
|
|
147
|
-
- Mount + route: `./mounting.md`
|
|
148
|
-
- Branding / custom templates: `./theming.md`
|
|
149
|
-
- AI summary + function index: `../CLAUDE.md`
|
|
150
|
-
- 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 from each endpoint's `meta` object.
|
|
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 is:
|
|
19
|
+
|
|
20
|
+
```jsonc
|
|
21
|
+
{
|
|
22
|
+
"apis": {
|
|
23
|
+
"<page>": {
|
|
24
|
+
"<name>": {
|
|
25
|
+
"<version>": {
|
|
26
|
+
"method": "POST",
|
|
27
|
+
"auth": { "login": true, "additional": [] },
|
|
28
|
+
"rateLimit": 60,
|
|
29
|
+
"input": "{ userId: string }",
|
|
30
|
+
"output": "{ status: 'success', result: { ... } }",
|
|
31
|
+
// ...optional extension fields...
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
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.
|
|
40
|
+
|
|
41
|
+
The grouping levels — `page` → `name` → `version` — match the file-based routing convention (`src/<page>/_api/<name>_v<version>.ts`). The renderer iterates these in object-iteration order; the emitter is responsible for any sorting.
|
|
42
|
+
|
|
43
|
+
## Core fields (always read by the renderer)
|
|
44
|
+
|
|
45
|
+
These are the fields used to build the always-visible row + the default expanded panel:
|
|
46
|
+
|
|
47
|
+
| Field | Type | Default in renderer | Purpose |
|
|
48
|
+
| --- | --- | --- | --- |
|
|
49
|
+
| `meta.method` | `'GET' \| 'POST' \| 'PUT' \| 'DELETE'` (any string, uppercased) | `'POST'` | Method pill text + CSS class on `.method`. |
|
|
50
|
+
| `meta.rateLimit` | `number \| false \| undefined` | `'default rate'` label | Numeric ⇒ `"<n>/min"`. `false` ⇒ `"no rate limit"`. `undefined` ⇒ `"default rate"`. |
|
|
51
|
+
| `meta.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. |
|
|
52
|
+
| `meta.input` | `string` (already-stringified TS shape) | `'{}'` | Rendered inside `<pre>` in the expanded panel. |
|
|
53
|
+
| `meta.output` | `string` (already-stringified TS shape) | `'unknown'` | Rendered inside `<pre>` in the expanded panel. |
|
|
54
|
+
|
|
55
|
+
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.
|
|
56
|
+
|
|
57
|
+
Unknown core fields fall through to their defaults silently; emit a missing `method`, for example, and the row shows `POST`.
|
|
58
|
+
|
|
59
|
+
## Optional extension fields
|
|
60
|
+
|
|
61
|
+
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.
|
|
62
|
+
|
|
63
|
+
### `meta.stream`
|
|
64
|
+
|
|
65
|
+
- Type: `string` (already-stringified TS shape, same convention as `input` / `output`).
|
|
66
|
+
- Renders as: a `stream` detail section with the value inside a `<pre>` block.
|
|
67
|
+
- 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.
|
|
68
|
+
- Example payload field:
|
|
69
|
+
|
|
70
|
+
```jsonc
|
|
71
|
+
{
|
|
72
|
+
"stream": "{ delta: string } | { done: true }"
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### `meta.owner`
|
|
77
|
+
|
|
78
|
+
- Type: `string`.
|
|
79
|
+
- Renders as: an `owner` detail section with the raw string.
|
|
80
|
+
- 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.
|
|
81
|
+
- Example:
|
|
82
|
+
|
|
83
|
+
```jsonc
|
|
84
|
+
{ "owner": "@platform-team (#help-platform)" }
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### `meta.tags`
|
|
88
|
+
|
|
89
|
+
- Type: `string[]` (or array of values coerced via `String(...)`).
|
|
90
|
+
- Renders as: inline badges in a `tags` detail section, space-separated.
|
|
91
|
+
- Use for cross-cutting categorization the page/name hierarchy can't express — `"v2-migration"`, `"internal"`, `"experimental"`, etc.
|
|
92
|
+
- Example:
|
|
93
|
+
|
|
94
|
+
```jsonc
|
|
95
|
+
{ "tags": ["experimental", "internal"] }
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Note: the renderer treats `tags` as an `Array.isArray(value)` check; arbitrary iterables are not supported. Emit a plain array.
|
|
99
|
+
|
|
100
|
+
### `meta.deprecated`
|
|
101
|
+
|
|
102
|
+
- Type: `string | true` (anything truthy; `String(value)` is used for display).
|
|
103
|
+
- Renders as: a `deprecated` detail section with red text (`color: var(--delete)`).
|
|
104
|
+
- 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.
|
|
105
|
+
- Example:
|
|
106
|
+
|
|
107
|
+
```jsonc
|
|
108
|
+
{
|
|
109
|
+
"deprecated": "Removed in v2026.05 — use users/getProfile/v2 instead."
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Forward compatibility
|
|
114
|
+
|
|
115
|
+
- Adding a new field to a `meta` object is non-breaking. The renderer ignores it.
|
|
116
|
+
- Removing one of the four extension fields above does **not** break the renderer either — each is conditional on truthiness or array length.
|
|
117
|
+
- 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.
|
|
118
|
+
|
|
119
|
+
If you need extra fields on the page, you have two options:
|
|
120
|
+
|
|
121
|
+
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.
|
|
122
|
+
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.
|
|
123
|
+
|
|
124
|
+
## Producer contract — how fields are populated
|
|
125
|
+
|
|
126
|
+
`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`).
|
|
127
|
+
|
|
128
|
+
Emission steps the devkit performs per endpoint:
|
|
129
|
+
|
|
130
|
+
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`).
|
|
131
|
+
2. Stringify the types into source-faithful strings — no re-stringification, no JSON.stringify of types.
|
|
132
|
+
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.
|
|
133
|
+
4. Group by `page` → `name` → `version` and write the JSON.
|
|
134
|
+
|
|
135
|
+
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.
|
|
136
|
+
|
|
137
|
+
## Validation behavior
|
|
138
|
+
|
|
139
|
+
- No schema validation. The renderer trusts the JSON shape.
|
|
140
|
+
- 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.
|
|
141
|
+
- An empty / invalid `apis` map yields the page's empty state: `"No API docs available. Run npm run generateArtifacts to generate them."`.
|
|
142
|
+
- 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.
|
|
143
|
+
|
|
144
|
+
## Related
|
|
145
|
+
|
|
146
|
+
- Rendering pipeline that consumes these fields: `./html-generation.md`
|
|
147
|
+
- Mount + route: `./mounting.md`
|
|
148
|
+
- Branding / custom templates: `./theming.md`
|
|
149
|
+
- AI summary + function index: `../CLAUDE.md`
|
|
150
|
+
- Producer: `packages/devkit/src/typeMap/emitter.ts` and `packages/devkit/src/typeMap/emitterArtifacts.ts`
|