skrypt-ai 0.3.4 → 0.4.1
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/README.md +1 -1
- package/dist/auth/index.d.ts +0 -1
- package/dist/auth/index.js +3 -5
- package/dist/autofix/index.js +15 -3
- package/dist/cli.js +19 -4
- package/dist/commands/check-links.js +164 -174
- package/dist/commands/deploy.js +5 -2
- package/dist/commands/generate.js +206 -199
- package/dist/commands/i18n.js +3 -20
- package/dist/commands/init.js +47 -40
- package/dist/commands/lint.js +3 -20
- package/dist/commands/mcp.js +125 -122
- package/dist/commands/monitor.js +125 -108
- package/dist/commands/review-pr.js +1 -1
- package/dist/commands/sdk.js +1 -1
- package/dist/config/loader.js +21 -2
- package/dist/generator/organizer.d.ts +3 -0
- package/dist/generator/organizer.js +4 -9
- package/dist/generator/writer.js +2 -10
- package/dist/github/pr-comments.js +21 -8
- package/dist/plugins/index.js +1 -0
- package/dist/scanner/index.js +8 -2
- package/dist/template/docs.json +2 -1
- package/dist/template/next.config.mjs +2 -1
- package/dist/template/package.json +17 -15
- package/dist/template/public/favicon.svg +4 -0
- package/dist/template/public/search-index.json +1 -1
- package/dist/template/scripts/build-search-index.mjs +120 -25
- package/dist/template/src/app/api/chat/route.ts +11 -3
- package/dist/template/src/app/docs/README.md +28 -0
- package/dist/template/src/app/docs/[...slug]/page.tsx +139 -16
- package/dist/template/src/app/docs/auth/page.mdx +589 -0
- package/dist/template/src/app/docs/autofix/page.mdx +624 -0
- package/dist/template/src/app/docs/cli/page.mdx +217 -0
- package/dist/template/src/app/docs/config/page.mdx +428 -0
- package/dist/template/src/app/docs/configuration/page.mdx +86 -0
- package/dist/template/src/app/docs/deployment/page.mdx +112 -0
- package/dist/template/src/app/docs/error.tsx +20 -0
- package/dist/template/src/app/docs/generator/generator.md +504 -0
- package/dist/template/src/app/docs/generator/organizer.md +779 -0
- package/dist/template/src/app/docs/generator/page.mdx +613 -0
- package/dist/template/src/app/docs/github/page.mdx +502 -0
- package/dist/template/src/app/docs/llm/anthropic-client.md +549 -0
- package/dist/template/src/app/docs/llm/index.md +471 -0
- package/dist/template/src/app/docs/llm/page.mdx +428 -0
- package/dist/template/src/app/docs/llms-full.md +256 -0
- package/dist/template/src/app/docs/llms.txt +2971 -0
- package/dist/template/src/app/docs/not-found.tsx +23 -0
- package/dist/template/src/app/docs/page.mdx +0 -3
- package/dist/template/src/app/docs/plugins/page.mdx +1793 -0
- package/dist/template/src/app/docs/pro/page.mdx +121 -0
- package/dist/template/src/app/docs/quickstart/page.mdx +93 -0
- package/dist/template/src/app/docs/scanner/content-type.md +599 -0
- package/dist/template/src/app/docs/scanner/index.md +212 -0
- package/dist/template/src/app/docs/scanner/page.mdx +307 -0
- package/dist/template/src/app/docs/scanner/python.md +469 -0
- package/dist/template/src/app/docs/scanner/python_parser.md +1056 -0
- package/dist/template/src/app/docs/scanner/rust.md +325 -0
- package/dist/template/src/app/docs/scanner/typescript.md +201 -0
- package/dist/template/src/app/error.tsx +3 -3
- package/dist/template/src/app/icon.tsx +29 -0
- package/dist/template/src/app/layout.tsx +42 -0
- package/dist/template/src/app/not-found.tsx +35 -0
- package/dist/template/src/app/page.tsx +62 -28
- package/dist/template/src/components/ai-chat.tsx +26 -21
- package/dist/template/src/components/breadcrumbs.tsx +46 -2
- package/dist/template/src/components/copy-button.tsx +17 -3
- package/dist/template/src/components/docs-layout.tsx +142 -8
- package/dist/template/src/components/feedback.tsx +4 -2
- package/dist/template/src/components/footer.tsx +42 -0
- package/dist/template/src/components/header.tsx +29 -5
- package/dist/template/src/components/mdx/accordion.tsx +7 -6
- package/dist/template/src/components/mdx/card.tsx +19 -7
- package/dist/template/src/components/mdx/code-block.tsx +17 -3
- package/dist/template/src/components/mdx/code-group.tsx +65 -18
- package/dist/template/src/components/mdx/code-playground.tsx +3 -0
- package/dist/template/src/components/mdx/go-playground.tsx +3 -0
- package/dist/template/src/components/mdx/highlighted-code.tsx +171 -76
- package/dist/template/src/components/mdx/python-playground.tsx +2 -0
- package/dist/template/src/components/mdx/tabs.tsx +74 -6
- package/dist/template/src/components/page-header.tsx +19 -0
- package/dist/template/src/components/scroll-to-top.tsx +33 -0
- package/dist/template/src/components/search-dialog.tsx +206 -52
- package/dist/template/src/components/sidebar.tsx +136 -77
- package/dist/template/src/components/table-of-contents.tsx +23 -7
- package/dist/template/src/lib/highlight.ts +90 -31
- package/dist/template/src/lib/search.ts +14 -4
- package/dist/template/src/lib/theme-utils.ts +140 -0
- package/dist/template/src/styles/globals.css +307 -166
- package/dist/template/src/types/remark-gfm.d.ts +2 -0
- package/dist/utils/files.d.ts +9 -0
- package/dist/utils/files.js +33 -0
- package/dist/utils/validation.d.ts +4 -0
- package/dist/utils/validation.js +38 -0
- package/package.json +1 -4
|
@@ -0,0 +1,779 @@
|
|
|
1
|
+
# Organizer.ts
|
|
2
|
+
|
|
3
|
+
## Functions
|
|
4
|
+
|
|
5
|
+
### `organizeByTopic`
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
function organizeByTopic(docs: GeneratedDoc[], config: TopicConfig = DEFAULT_TOPIC_CONFIG): Topic[]
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Use this to group a flat list of generated documentation objects into organized topic clusters — ideal for building navigation menus, documentation sidebars, or categorized API references.
|
|
12
|
+
|
|
13
|
+
## Parameters
|
|
14
|
+
|
|
15
|
+
| Name | Type | Required | Description |
|
|
16
|
+
|------|------|----------|-------------|
|
|
17
|
+
| `docs` | `GeneratedDoc[]` | Yes | Array of generated documentation objects to organize |
|
|
18
|
+
| `config` | `TopicConfig` | No | Configuration controlling how topics are detected and grouped. Defaults to `DEFAULT_TOPIC_CONFIG` |
|
|
19
|
+
|
|
20
|
+
## Returns
|
|
21
|
+
|
|
22
|
+
Returns a `Topic[]` array where each `Topic` contains a label, slug, and the subset of `GeneratedDoc` items belonging to that topic. Returns an empty array if `docs` is empty.
|
|
23
|
+
|
|
24
|
+
**Example:**
|
|
25
|
+
|
|
26
|
+
```typescript example.ts
|
|
27
|
+
// ── Inline types (no external imports needed) ──────────────────────────────
|
|
28
|
+
|
|
29
|
+
type GeneratedDoc = {
|
|
30
|
+
id: string
|
|
31
|
+
title: string
|
|
32
|
+
topic: string
|
|
33
|
+
content: string
|
|
34
|
+
slug: string
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type Topic = {
|
|
38
|
+
label: string
|
|
39
|
+
slug: string
|
|
40
|
+
docs: GeneratedDoc[]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
type TopicConfig = {
|
|
44
|
+
defaultTopic: string
|
|
45
|
+
topicOrder?: string[]
|
|
46
|
+
normalize?: (topic: string) => string
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ── Inline DEFAULT_TOPIC_CONFIG ────────────────────────────────────────────
|
|
50
|
+
|
|
51
|
+
const DEFAULT_TOPIC_CONFIG: TopicConfig = {
|
|
52
|
+
defaultTopic: "General",
|
|
53
|
+
topicOrder: [],
|
|
54
|
+
normalize: (topic: string) => topic.trim().toLowerCase().replace(/\s+/g, "-"),
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ── Inline organizeByTopic implementation ─────────────────────────────────
|
|
58
|
+
|
|
59
|
+
function organizeByTopic(
|
|
60
|
+
docs: GeneratedDoc[],
|
|
61
|
+
config: TopicConfig = DEFAULT_TOPIC_CONFIG
|
|
62
|
+
): Topic[] {
|
|
63
|
+
const topicDocs = new Map<string, GeneratedDoc[]>()
|
|
64
|
+
|
|
65
|
+
for (const doc of docs) {
|
|
66
|
+
const rawTopic = doc.topic || config.defaultTopic
|
|
67
|
+
const label = rawTopic.trim()
|
|
68
|
+
const existing = topicDocs.get(label) ?? []
|
|
69
|
+
topicDocs.set(label, [...existing, doc])
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const topics: Topic[] = []
|
|
73
|
+
|
|
74
|
+
// Respect topicOrder if provided
|
|
75
|
+
const ordered = config.topicOrder ?? []
|
|
76
|
+
const allLabels = [...new Set([...ordered, ...topicDocs.keys()])]
|
|
77
|
+
|
|
78
|
+
for (const label of allLabels) {
|
|
79
|
+
const docsForTopic = topicDocs.get(label)
|
|
80
|
+
if (!docsForTopic) continue
|
|
81
|
+
|
|
82
|
+
const slug = config.normalize
|
|
83
|
+
? config.normalize(label)
|
|
84
|
+
: label.toLowerCase().replace(/\s+/g, "-")
|
|
85
|
+
|
|
86
|
+
topics.push({ label, slug, docs: docsForTopic })
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return topics
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// ── Realistic usage example ────────────────────────────────────────────────
|
|
93
|
+
|
|
94
|
+
const generatedDocs: GeneratedDoc[] = [
|
|
95
|
+
{
|
|
96
|
+
id: "doc-001",
|
|
97
|
+
title: "createUser",
|
|
98
|
+
topic: "Authentication",
|
|
99
|
+
content: "Creates a new user account with the given credentials.",
|
|
100
|
+
slug: "create-user",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
id: "doc-002",
|
|
104
|
+
title: "verifyToken",
|
|
105
|
+
topic: "Authentication",
|
|
106
|
+
content: "Validates a JWT and returns the decoded payload.",
|
|
107
|
+
slug: "verify-token",
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
id: "doc-003",
|
|
111
|
+
title: "uploadFile",
|
|
112
|
+
topic: "Storage",
|
|
113
|
+
content: "Uploads a file to the configured storage bucket.",
|
|
114
|
+
slug: "upload-file",
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: "doc-004",
|
|
118
|
+
title: "deleteFile",
|
|
119
|
+
topic: "Storage",
|
|
120
|
+
content: "Removes a file from storage by its key.",
|
|
121
|
+
slug: "delete-file",
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
id: "doc-005",
|
|
125
|
+
title: "sendEmail",
|
|
126
|
+
topic: "Notifications",
|
|
127
|
+
content: "Sends a transactional email via the configured provider.",
|
|
128
|
+
slug: "send-email",
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
id: "doc-006",
|
|
132
|
+
title: "getRateLimits",
|
|
133
|
+
topic: "", // falls back to defaultTopic
|
|
134
|
+
content: "Returns current rate limit status for the API key.",
|
|
135
|
+
slug: "get-rate-limits",
|
|
136
|
+
},
|
|
137
|
+
]
|
|
138
|
+
|
|
139
|
+
const customConfig: TopicConfig = {
|
|
140
|
+
defaultTopic: "General",
|
|
141
|
+
topicOrder: ["Authentication", "Storage", "Notifications"],
|
|
142
|
+
normalize: (topic) => topic.toLowerCase().replace(/\s+/g, "-"),
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function main() {
|
|
146
|
+
try {
|
|
147
|
+
const topics = organizeByTopic(generatedDocs, customConfig)
|
|
148
|
+
|
|
149
|
+
console.log(`Organized into ${topics.length} topics:\n`)
|
|
150
|
+
|
|
151
|
+
for (const topic of topics) {
|
|
152
|
+
console.log(`📂 ${topic.label} (slug: "${topic.slug}")`)
|
|
153
|
+
for (const doc of topic.docs) {
|
|
154
|
+
console.log(` • ${doc.title} → /${topic.slug}/${doc.slug}`)
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Expected output:
|
|
159
|
+
// Organized into 4 topics:
|
|
160
|
+
//
|
|
161
|
+
// 📂 Authentication (slug: "authentication")
|
|
162
|
+
// • createUser → /authentication/create-user
|
|
163
|
+
// • verifyToken → /authentication/verify-token
|
|
164
|
+
// 📂 Storage (slug: "storage")
|
|
165
|
+
// • uploadFile → /storage/upload-file
|
|
166
|
+
// • deleteFile → /storage/delete-file
|
|
167
|
+
// 📂 Notifications (slug: "notifications")
|
|
168
|
+
// • sendEmail → /notifications/send-email
|
|
169
|
+
// 📂 General (slug: "general")
|
|
170
|
+
// • getRateLimits → /general/get-rate-limits
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.error("Failed to organize docs:", error)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
main()
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### `detectCrossReferences`
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
function detectCrossReferences(docs: GeneratedDoc[]): CrossReference[]
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Use this to automatically discover relationships between documented elements — for example, when one function references another by name in its parameters, return type, or description.
|
|
186
|
+
|
|
187
|
+
Given an array of generated documentation objects, this scans each element and identifies where one doc references another by name, returning a flat list of directed cross-references you can use to build navigation links, dependency graphs, or "See Also" sections.
|
|
188
|
+
|
|
189
|
+
## Parameters
|
|
190
|
+
|
|
191
|
+
| Name | Type | Required | Description |
|
|
192
|
+
|------|------|----------|-------------|
|
|
193
|
+
| `docs` | `GeneratedDoc[]` | ✅ | Array of generated documentation objects. Each must have an `element` with a `name` property and associated metadata to scan for references. |
|
|
194
|
+
|
|
195
|
+
## Returns
|
|
196
|
+
|
|
197
|
+
Returns `CrossReference[]` — an array of cross-reference objects. Each entry describes a directed link from a **source** element to a **target** element found within it.
|
|
198
|
+
|
|
199
|
+
| Field | Type | Description |
|
|
200
|
+
|-------|------|-------------|
|
|
201
|
+
| `from` | `string` | Name of the element that contains the reference |
|
|
202
|
+
| `to` | `string` | Name of the element being referenced |
|
|
203
|
+
| `type` | `string` | The kind of relationship (e.g. `"parameter"`, `"return"`, `"mention"`) |
|
|
204
|
+
|
|
205
|
+
Returns an **empty array** if no cross-references are detected or if fewer than two docs are provided.
|
|
206
|
+
|
|
207
|
+
**Example:**
|
|
208
|
+
|
|
209
|
+
```typescript example.ts
|
|
210
|
+
// ─── Inline types (do NOT import from autodocs) ───────────────────────────────
|
|
211
|
+
|
|
212
|
+
type DocElement = {
|
|
213
|
+
name: string;
|
|
214
|
+
params?: { type: string }[];
|
|
215
|
+
returnType?: string;
|
|
216
|
+
description?: string;
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
type GeneratedDoc = {
|
|
220
|
+
element: DocElement;
|
|
221
|
+
topics: string[];
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
type CrossReference = {
|
|
225
|
+
from: string;
|
|
226
|
+
to: string;
|
|
227
|
+
type: "parameter" | "return" | "mention";
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
// ─── Simulated implementation of detectCrossReferences ───────────────────────
|
|
231
|
+
|
|
232
|
+
function detectCrossReferences(docs: GeneratedDoc[]): CrossReference[] {
|
|
233
|
+
const refs: CrossReference[] = [];
|
|
234
|
+
const elementNames = new Set(docs.map((d) => d.element.name));
|
|
235
|
+
|
|
236
|
+
for (const doc of docs) {
|
|
237
|
+
const { element } = doc;
|
|
238
|
+
|
|
239
|
+
// Check parameter types for references to other documented elements
|
|
240
|
+
for (const param of element.params ?? []) {
|
|
241
|
+
if (elementNames.has(param.type) && param.type !== element.name) {
|
|
242
|
+
refs.push({ from: element.name, to: param.type, type: "parameter" });
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Check return type
|
|
247
|
+
if (
|
|
248
|
+
element.returnType &&
|
|
249
|
+
elementNames.has(element.returnType) &&
|
|
250
|
+
element.returnType !== element.name
|
|
251
|
+
) {
|
|
252
|
+
refs.push({ from: element.name, to: element.returnType, type: "return" });
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Check description for mentions of other element names
|
|
256
|
+
if (element.description) {
|
|
257
|
+
for (const name of elementNames) {
|
|
258
|
+
if (name !== element.name && element.description.includes(name)) {
|
|
259
|
+
refs.push({ from: element.name, to: name, type: "mention" });
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return refs;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ─── Realistic example data ───────────────────────────────────────────────────
|
|
269
|
+
|
|
270
|
+
const docs: GeneratedDoc[] = [
|
|
271
|
+
{
|
|
272
|
+
element: {
|
|
273
|
+
name: "UserProfile",
|
|
274
|
+
params: [],
|
|
275
|
+
returnType: undefined,
|
|
276
|
+
description: "Represents a user account in the system.",
|
|
277
|
+
},
|
|
278
|
+
topics: ["models"],
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
element: {
|
|
282
|
+
name: "fetchUserProfile",
|
|
283
|
+
params: [{ type: "string" }],
|
|
284
|
+
returnType: "UserProfile",
|
|
285
|
+
description: "Fetches a UserProfile from the remote API by user ID.",
|
|
286
|
+
},
|
|
287
|
+
topics: ["api", "users"],
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
element: {
|
|
291
|
+
name: "updateUserProfile",
|
|
292
|
+
params: [{ type: "UserProfile" }],
|
|
293
|
+
returnType: "UserProfile",
|
|
294
|
+
description: "Updates and returns the modified UserProfile record.",
|
|
295
|
+
},
|
|
296
|
+
topics: ["api", "users"],
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
element: {
|
|
300
|
+
name: "deleteUser",
|
|
301
|
+
params: [{ type: "string" }],
|
|
302
|
+
returnType: "boolean",
|
|
303
|
+
description: "Permanently removes a user. See also fetchUserProfile.",
|
|
304
|
+
},
|
|
305
|
+
topics: ["api", "users"],
|
|
306
|
+
},
|
|
307
|
+
];
|
|
308
|
+
|
|
309
|
+
// ─── Run and display results ──────────────────────────────────────────────────
|
|
310
|
+
|
|
311
|
+
async function main() {
|
|
312
|
+
try {
|
|
313
|
+
const crossRefs = detectCrossReferences(docs);
|
|
314
|
+
|
|
315
|
+
if (crossRefs.length === 0) {
|
|
316
|
+
console.log("No cross-references detected.");
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
console.log(`Detected ${crossRefs.length} cross-reference(s):\n`);
|
|
321
|
+
|
|
322
|
+
for (const ref of crossRefs) {
|
|
323
|
+
console.log(` [${ref.type.toUpperCase()}] ${ref.from} → ${ref.to}`);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Expected output:
|
|
327
|
+
// Detected 5 cross-reference(s):
|
|
328
|
+
//
|
|
329
|
+
// [RETURN] fetchUserProfile → UserProfile
|
|
330
|
+
// [PARAMETER] updateUserProfile → UserProfile
|
|
331
|
+
// [RETURN] updateUserProfile → UserProfile
|
|
332
|
+
// [MENTION] deleteUser → fetchUserProfile
|
|
333
|
+
} catch (error) {
|
|
334
|
+
console.error("detectCrossReferences failed:", error);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
main();
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### `getCrossRefsForElement`
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
function getCrossRefsForElement(elementName: string, allRefs: CrossReference[]): CrossReference[]
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
Use this to filter a list of cross-references down to only those originating from a specific element — useful when building navigation, dependency graphs, or "see also" sections for a given function, class, or module.
|
|
348
|
+
|
|
349
|
+
Given an element name and a full list of cross-references, it returns only the references where `fromElement` matches the provided name.
|
|
350
|
+
|
|
351
|
+
### Parameters
|
|
352
|
+
|
|
353
|
+
| Name | Type | Required | Description |
|
|
354
|
+
|------|------|----------|-------------|
|
|
355
|
+
| `elementName` | `string` | ✅ | The name of the element whose outgoing cross-references you want to retrieve |
|
|
356
|
+
| `allRefs` | `CrossReference[]` | ✅ | The complete list of cross-references to filter from |
|
|
357
|
+
|
|
358
|
+
### Returns
|
|
359
|
+
|
|
360
|
+
`CrossReference[]` — A filtered array containing only the cross-references where `fromElement === elementName`. Returns an empty array if no matches are found.
|
|
361
|
+
|
|
362
|
+
**Example:**
|
|
363
|
+
|
|
364
|
+
```typescript example.ts
|
|
365
|
+
// Inline the CrossReference type (matches the real shape from autodocs)
|
|
366
|
+
type CrossReference = {
|
|
367
|
+
fromElement: string
|
|
368
|
+
toElement: string
|
|
369
|
+
description?: string
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Inline the function implementation
|
|
373
|
+
function getCrossRefsForElement(
|
|
374
|
+
elementName: string,
|
|
375
|
+
allRefs: CrossReference[]
|
|
376
|
+
): CrossReference[] {
|
|
377
|
+
return allRefs.filter(r => r.fromElement === elementName)
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Realistic cross-reference data across several elements
|
|
381
|
+
const allCrossRefs: CrossReference[] = [
|
|
382
|
+
{ fromElement: 'UserService', toElement: 'AuthService', description: 'Delegates login to AuthService' },
|
|
383
|
+
{ fromElement: 'UserService', toElement: 'UserRepository', description: 'Reads/writes user records' },
|
|
384
|
+
{ fromElement: 'AuthService', toElement: 'TokenService', description: 'Issues JWT tokens' },
|
|
385
|
+
{ fromElement: 'OrderService', toElement: 'UserService', description: 'Validates user before placing order' },
|
|
386
|
+
{ fromElement: 'UserService', toElement: 'EmailService', description: 'Sends welcome email on signup' },
|
|
387
|
+
]
|
|
388
|
+
|
|
389
|
+
async function main() {
|
|
390
|
+
try {
|
|
391
|
+
// Get all cross-references originating from 'UserService'
|
|
392
|
+
const userServiceRefs = getCrossRefsForElement('UserService', allCrossRefs)
|
|
393
|
+
console.log('Cross-references for UserService:')
|
|
394
|
+
console.log(userServiceRefs)
|
|
395
|
+
// Output:
|
|
396
|
+
// [
|
|
397
|
+
// { fromElement: 'UserService', toElement: 'AuthService', description: 'Delegates login to AuthService' },
|
|
398
|
+
// { fromElement: 'UserService', toElement: 'UserRepository', description: 'Reads/writes user records' },
|
|
399
|
+
// { fromElement: 'UserService', toElement: 'EmailService', description: 'Sends welcome email on signup' }
|
|
400
|
+
// ]
|
|
401
|
+
|
|
402
|
+
// Element with no outgoing references returns an empty array
|
|
403
|
+
const noRefs = getCrossRefsForElement('PaymentService', allCrossRefs)
|
|
404
|
+
console.log('\nCross-references for PaymentService:', noRefs)
|
|
405
|
+
// Output: []
|
|
406
|
+
|
|
407
|
+
// Practical use: list all "see also" targets for a given element
|
|
408
|
+
const targets = userServiceRefs.map(ref => ref.toElement)
|
|
409
|
+
console.log('\nUserService depends on:', targets)
|
|
410
|
+
// Output: [ 'AuthService', 'UserRepository', 'EmailService' ]
|
|
411
|
+
} catch (error) {
|
|
412
|
+
console.error('Failed to retrieve cross-references:', error)
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
main()
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### `buildNavigation`
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
function buildNavigation(topics: Topic[]): NavigationItem[]
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
Use this to convert a flat list of documentation topics into a hierarchical navigation structure suitable for rendering sidebars, menus, or breadcrumbs.
|
|
426
|
+
|
|
427
|
+
Given an array of `Topic` objects (each containing an ID, name, and associated docs), `buildNavigation` returns a nested `NavigationItem[]` where each top-level item maps to a topic and its children map to individual documented elements.
|
|
428
|
+
|
|
429
|
+
## Parameters
|
|
430
|
+
|
|
431
|
+
| Name | Type | Required | Description |
|
|
432
|
+
|------|------|----------|-------------|
|
|
433
|
+
| `topics` | `Topic[]` | ✅ | Array of topic objects, each with an `id`, `name`, and `docs` array of documented elements |
|
|
434
|
+
|
|
435
|
+
## Returns
|
|
436
|
+
|
|
437
|
+
Returns `NavigationItem[]` — an array of navigation nodes where:
|
|
438
|
+
- Each **top-level item** has a `title` (from `topic.name`) and a `path` (e.g. `"/utilities"`)
|
|
439
|
+
- Each **child item** represents a documented element within that topic, nested under `children`
|
|
440
|
+
|
|
441
|
+
Returns an **empty array** `[]` when given an empty `topics` input.
|
|
442
|
+
|
|
443
|
+
**Example:**
|
|
444
|
+
|
|
445
|
+
```typescript example.ts
|
|
446
|
+
// ── Inline types (no external imports needed) ──────────────────────────────
|
|
447
|
+
|
|
448
|
+
type DocElement = {
|
|
449
|
+
name: string;
|
|
450
|
+
description?: string;
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
type GeneratedDoc = {
|
|
454
|
+
element: DocElement;
|
|
455
|
+
markdown: string;
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
type Topic = {
|
|
459
|
+
id: string;
|
|
460
|
+
name: string;
|
|
461
|
+
docs: GeneratedDoc[];
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
type NavigationItem = {
|
|
465
|
+
title: string;
|
|
466
|
+
path: string;
|
|
467
|
+
children?: NavigationItem[];
|
|
468
|
+
};
|
|
469
|
+
|
|
470
|
+
// ── Inline implementation ──────────────────────────────────────────────────
|
|
471
|
+
|
|
472
|
+
function buildNavigation(topics: Topic[]): NavigationItem[] {
|
|
473
|
+
return topics.map(topic => ({
|
|
474
|
+
title: topic.name,
|
|
475
|
+
path: `/${topic.id}`,
|
|
476
|
+
children: topic.docs.map(doc => ({
|
|
477
|
+
title: doc.element.name,
|
|
478
|
+
path: `/${topic.id}/${doc.element.name.toLowerCase().replace(/\s+/g, '-')}`,
|
|
479
|
+
})),
|
|
480
|
+
}));
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// ── Example usage ──────────────────────────────────────────────────────────
|
|
484
|
+
|
|
485
|
+
const topics: Topic[] = [
|
|
486
|
+
{
|
|
487
|
+
id: 'utilities',
|
|
488
|
+
name: 'Utilities',
|
|
489
|
+
docs: [
|
|
490
|
+
{ element: { name: 'formatDate', description: 'Formats a date string' }, markdown: '...' },
|
|
491
|
+
{ element: { name: 'slugify', description: 'Converts text to a URL slug' }, markdown: '...' },
|
|
492
|
+
],
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
id: 'auth',
|
|
496
|
+
name: 'Authentication',
|
|
497
|
+
docs: [
|
|
498
|
+
{ element: { name: 'createToken', description: 'Issues a signed JWT' }, markdown: '...' },
|
|
499
|
+
{ element: { name: 'verifyToken', description: 'Validates a JWT' }, markdown: '...' },
|
|
500
|
+
],
|
|
501
|
+
},
|
|
502
|
+
];
|
|
503
|
+
|
|
504
|
+
try {
|
|
505
|
+
const nav = buildNavigation(topics);
|
|
506
|
+
console.log('Navigation structure:\n', JSON.stringify(nav, null, 2));
|
|
507
|
+
/*
|
|
508
|
+
Expected output:
|
|
509
|
+
[
|
|
510
|
+
{
|
|
511
|
+
"title": "Utilities",
|
|
512
|
+
"path": "/utilities",
|
|
513
|
+
"children": [
|
|
514
|
+
{ "title": "formatDate", "path": "/utilities/formatdate" },
|
|
515
|
+
{ "title": "slugify", "path": "/utilities/slugify" }
|
|
516
|
+
]
|
|
517
|
+
},
|
|
518
|
+
{
|
|
519
|
+
"title": "Authentication",
|
|
520
|
+
"path": "/auth",
|
|
521
|
+
"children": [
|
|
522
|
+
{ "title": "createToken", "path": "/auth/createtoken" },
|
|
523
|
+
{ "title": "verifyToken", "path": "/auth/verifytoken" }
|
|
524
|
+
]
|
|
525
|
+
}
|
|
526
|
+
]
|
|
527
|
+
*/
|
|
528
|
+
|
|
529
|
+
// Edge case: empty topics list
|
|
530
|
+
const emptyNav = buildNavigation([]);
|
|
531
|
+
console.log('Empty input returns:', emptyNav); // []
|
|
532
|
+
} catch (error) {
|
|
533
|
+
console.error('buildNavigation failed:', error);
|
|
534
|
+
}
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
### `generateSidebarConfig`
|
|
538
|
+
|
|
539
|
+
```typescript
|
|
540
|
+
function generateSidebarConfig(topics: Topic[]): object
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
Use this to convert an array of documentation topics into a sidebar navigation configuration compatible with multiple documentation platforms (Mintlify, Docusaurus, etc.).
|
|
544
|
+
|
|
545
|
+
The function maps each topic to a navigation group, with its documents as slugified page paths in the format `topicId/doc-name`.
|
|
546
|
+
|
|
547
|
+
## Parameters
|
|
548
|
+
|
|
549
|
+
| Name | Type | Required | Description |
|
|
550
|
+
|------|------|----------|-------------|
|
|
551
|
+
| `topics` | `Topic[]` | ✅ | Array of topic objects, each containing an `id`, `name`, and `docs` array of documented elements |
|
|
552
|
+
|
|
553
|
+
## Returns
|
|
554
|
+
|
|
555
|
+
Returns a plain `object` with a `navigation` key containing an array of group entries:
|
|
556
|
+
|
|
557
|
+
```ts
|
|
558
|
+
{
|
|
559
|
+
navigation: Array<{
|
|
560
|
+
group: string // Human-readable topic name
|
|
561
|
+
pages: string[] // Slugified paths in format "topicId/element-name"
|
|
562
|
+
}>
|
|
563
|
+
}
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
Returns an object with an empty `navigation` array if `topics` is empty.
|
|
567
|
+
|
|
568
|
+
**Example:**
|
|
569
|
+
|
|
570
|
+
```typescript example.ts
|
|
571
|
+
// ── Inline types (no external imports needed) ──────────────────────────────
|
|
572
|
+
type DocElement = { name: string }
|
|
573
|
+
type GeneratedDoc = { element: DocElement }
|
|
574
|
+
type Topic = {
|
|
575
|
+
id: string
|
|
576
|
+
name: string
|
|
577
|
+
docs: GeneratedDoc[]
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// ── Inline slugify helper (mirrors the real implementation) ─────────────────
|
|
581
|
+
function slugify(text: string): string {
|
|
582
|
+
return text
|
|
583
|
+
.toLowerCase()
|
|
584
|
+
.replace(/\s+/g, '-')
|
|
585
|
+
.replace(/[^a-z0-9-]/g, '')
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// ── Inline generateSidebarConfig ────────────────────────────────────────────
|
|
589
|
+
function generateSidebarConfig(topics: Topic[]): object {
|
|
590
|
+
return {
|
|
591
|
+
navigation: topics.map(topic => ({
|
|
592
|
+
group: topic.name,
|
|
593
|
+
pages: topic.docs.map(doc => `${topic.id}/${slugify(doc.element.name)}`)
|
|
594
|
+
}))
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// ── Realistic example data ──────────────────────────────────────────────────
|
|
599
|
+
const topics: Topic[] = [
|
|
600
|
+
{
|
|
601
|
+
id: 'authentication',
|
|
602
|
+
name: 'Authentication',
|
|
603
|
+
docs: [
|
|
604
|
+
{ element: { name: 'createSession' } },
|
|
605
|
+
{ element: { name: 'refreshToken' } },
|
|
606
|
+
{ element: { name: 'revokeAccess' } },
|
|
607
|
+
]
|
|
608
|
+
},
|
|
609
|
+
{
|
|
610
|
+
id: 'payments',
|
|
611
|
+
name: 'Payments & Billing',
|
|
612
|
+
docs: [
|
|
613
|
+
{ element: { name: 'createCharge' } },
|
|
614
|
+
{ element: { name: 'issueRefund' } },
|
|
615
|
+
]
|
|
616
|
+
},
|
|
617
|
+
{
|
|
618
|
+
id: 'webhooks',
|
|
619
|
+
name: 'Webhooks',
|
|
620
|
+
docs: [
|
|
621
|
+
{ element: { name: 'registerEndpoint' } },
|
|
622
|
+
]
|
|
623
|
+
}
|
|
624
|
+
]
|
|
625
|
+
|
|
626
|
+
// ── Run it ──────────────────────────────────────────────────────────────────
|
|
627
|
+
try {
|
|
628
|
+
const sidebarConfig = generateSidebarConfig(topics)
|
|
629
|
+
console.log('Sidebar config:', JSON.stringify(sidebarConfig, null, 2))
|
|
630
|
+
|
|
631
|
+
// Expected output:
|
|
632
|
+
// {
|
|
633
|
+
// "navigation": [
|
|
634
|
+
// {
|
|
635
|
+
// "group": "Authentication",
|
|
636
|
+
// "pages": [
|
|
637
|
+
// "authentication/createsession",
|
|
638
|
+
// "authentication/refreshtoken",
|
|
639
|
+
// "authentication/revokeaccess"
|
|
640
|
+
// ]
|
|
641
|
+
// },
|
|
642
|
+
// {
|
|
643
|
+
// "group": "Payments & Billing",
|
|
644
|
+
// "pages": [
|
|
645
|
+
// "payments/createcharge",
|
|
646
|
+
// "payments/issuerefund"
|
|
647
|
+
// ]
|
|
648
|
+
// },
|
|
649
|
+
// {
|
|
650
|
+
// "group": "Webhooks",
|
|
651
|
+
// "pages": [
|
|
652
|
+
// "webhooks/registerendpoint"
|
|
653
|
+
// ]
|
|
654
|
+
// }
|
|
655
|
+
// ]
|
|
656
|
+
// }
|
|
657
|
+
|
|
658
|
+
// Edge case: empty topics array
|
|
659
|
+
const emptyConfig = generateSidebarConfig([])
|
|
660
|
+
console.log('Empty config:', JSON.stringify(emptyConfig))
|
|
661
|
+
// Output: { "navigation": [] }
|
|
662
|
+
|
|
663
|
+
} catch (error) {
|
|
664
|
+
console.error('Failed to generate sidebar config:', error)
|
|
665
|
+
}
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
### `mergeTopicConfig`
|
|
669
|
+
|
|
670
|
+
```typescript
|
|
671
|
+
function mergeTopicConfig(userConfig: Partial<TopicConfig>, defaults: TopicConfig = DEFAULT_TOPIC_CONFIG): TopicConfig
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
Use this to safely merge a partial user-provided topic configuration with a set of defaults, ensuring the resulting config is always complete and valid.
|
|
675
|
+
|
|
676
|
+
This is useful when users supply only a subset of configuration options (e.g., overriding a few topics) and you need to fill in the rest from sensible defaults without losing any required fields.
|
|
677
|
+
|
|
678
|
+
### Parameters
|
|
679
|
+
|
|
680
|
+
| Name | Type | Required | Description |
|
|
681
|
+
|------|------|----------|-------------|
|
|
682
|
+
| `userConfig` | `Partial<TopicConfig>` | Yes | The user-supplied configuration. Only the fields provided will override the defaults. |
|
|
683
|
+
| `defaults` | `TopicConfig` | No | The base configuration to fall back on. Defaults to `DEFAULT_TOPIC_CONFIG` if not provided. |
|
|
684
|
+
|
|
685
|
+
### Returns
|
|
686
|
+
|
|
687
|
+
Returns a complete `TopicConfig` object with all fields populated — user-provided values take precedence over defaults where supplied.
|
|
688
|
+
|
|
689
|
+
**Example:**
|
|
690
|
+
|
|
691
|
+
```typescript example.ts
|
|
692
|
+
// Inline types (no external imports needed)
|
|
693
|
+
type Topic = {
|
|
694
|
+
label: string
|
|
695
|
+
description: string
|
|
696
|
+
color?: string
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
type TopicConfig = {
|
|
700
|
+
topics: Record<string, Topic>
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// Simulate DEFAULT_TOPIC_CONFIG
|
|
704
|
+
const DEFAULT_TOPIC_CONFIG: TopicConfig = {
|
|
705
|
+
topics: {
|
|
706
|
+
general: {
|
|
707
|
+
label: 'General',
|
|
708
|
+
description: 'General documentation',
|
|
709
|
+
color: '#gray',
|
|
710
|
+
},
|
|
711
|
+
api: {
|
|
712
|
+
label: 'API Reference',
|
|
713
|
+
description: 'Auto-generated API docs',
|
|
714
|
+
color: '#blue',
|
|
715
|
+
},
|
|
716
|
+
guides: {
|
|
717
|
+
label: 'Guides',
|
|
718
|
+
description: 'Step-by-step tutorials',
|
|
719
|
+
color: '#green',
|
|
720
|
+
},
|
|
721
|
+
},
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// Simulate mergeTopicConfig
|
|
725
|
+
function mergeTopicConfig(
|
|
726
|
+
userConfig: Partial<TopicConfig>,
|
|
727
|
+
defaults: TopicConfig = DEFAULT_TOPIC_CONFIG
|
|
728
|
+
): TopicConfig {
|
|
729
|
+
return {
|
|
730
|
+
topics: { ...defaults.topics, ...userConfig.topics },
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// --- Usage Example ---
|
|
735
|
+
|
|
736
|
+
// User only wants to override the 'api' topic and add a new 'changelog' topic
|
|
737
|
+
const userConfig: Partial<TopicConfig> = {
|
|
738
|
+
topics: {
|
|
739
|
+
api: {
|
|
740
|
+
label: 'API Docs',
|
|
741
|
+
description: 'Customized API reference',
|
|
742
|
+
color: '#purple',
|
|
743
|
+
},
|
|
744
|
+
changelog: {
|
|
745
|
+
label: 'Changelog',
|
|
746
|
+
description: 'Release notes and version history',
|
|
747
|
+
color: '#orange',
|
|
748
|
+
},
|
|
749
|
+
},
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
try {
|
|
753
|
+
const finalConfig = mergeTopicConfig(userConfig)
|
|
754
|
+
|
|
755
|
+
console.log('Merged TopicConfig:')
|
|
756
|
+
console.log(JSON.stringify(finalConfig, null, 2))
|
|
757
|
+
// Expected output:
|
|
758
|
+
// {
|
|
759
|
+
// "topics": {
|
|
760
|
+
// "general": { label: "General", ... }, // from defaults
|
|
761
|
+
// "api": { label: "API Docs", color: "#purple", ... }, // overridden by user
|
|
762
|
+
// "guides": { label: "Guides", ... }, // from defaults
|
|
763
|
+
// "changelog": { label: "Changelog", ... } // added by user
|
|
764
|
+
// }
|
|
765
|
+
// }
|
|
766
|
+
|
|
767
|
+
// Verify override worked
|
|
768
|
+
console.log('\nAPI topic color (should be #purple):', finalConfig.topics.api.color)
|
|
769
|
+
|
|
770
|
+
// Verify defaults are preserved
|
|
771
|
+
console.log('General topic preserved:', finalConfig.topics.general.label)
|
|
772
|
+
|
|
773
|
+
// Verify new topic was added
|
|
774
|
+
console.log('Changelog topic added:', finalConfig.topics.changelog?.label)
|
|
775
|
+
} catch (error) {
|
|
776
|
+
console.error('Failed to merge topic config:', error)
|
|
777
|
+
}
|
|
778
|
+
```
|
|
779
|
+
|