@fullevent/node 0.0.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/dist/index.d.mts +1096 -0
- package/dist/index.d.ts +1096 -0
- package/dist/index.js +573 -0
- package/dist/index.mjs +543 -0
- package/package.json +23 -0
- package/scripts/generate-docs.ts +917 -0
- package/src/builder.ts +307 -0
- package/src/client.ts +325 -0
- package/src/index.ts +272 -0
- package/src/middleware/express.ts +193 -0
- package/src/middleware/hono.ts +395 -0
- package/tsconfig.json +11 -0
|
@@ -0,0 +1,917 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* SDK Documentation Generator
|
|
5
|
+
*
|
|
6
|
+
* Extracts TSDoc comments from the SDK source and generates MDX files
|
|
7
|
+
* for the Fumadocs documentation site.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* npx tsx scripts/generate-docs.ts
|
|
11
|
+
* npm run generate:docs
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import * as fs from 'fs';
|
|
15
|
+
import * as path from 'path';
|
|
16
|
+
import * as ts from 'typescript';
|
|
17
|
+
|
|
18
|
+
// Configuration
|
|
19
|
+
const SDK_SRC = path.join(__dirname, '../src');
|
|
20
|
+
const DOCS_OUTPUT = path.join(__dirname, '../../../apps/docs/content/docs/sdk-reference');
|
|
21
|
+
|
|
22
|
+
// Types for extracted documentation
|
|
23
|
+
interface DocParam {
|
|
24
|
+
name: string;
|
|
25
|
+
type: string;
|
|
26
|
+
description: string;
|
|
27
|
+
optional: boolean;
|
|
28
|
+
defaultValue?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface DocExample {
|
|
32
|
+
title?: string;
|
|
33
|
+
code: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface DocMethod {
|
|
37
|
+
name: string;
|
|
38
|
+
signature: string;
|
|
39
|
+
description: string;
|
|
40
|
+
remarks?: string;
|
|
41
|
+
params: DocParam[];
|
|
42
|
+
returns?: { type: string; description: string };
|
|
43
|
+
examples: DocExample[];
|
|
44
|
+
category?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface DocProperty {
|
|
48
|
+
name: string;
|
|
49
|
+
type: string;
|
|
50
|
+
description: string;
|
|
51
|
+
remarks?: string;
|
|
52
|
+
optional: boolean;
|
|
53
|
+
defaultValue?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface DocInterface {
|
|
57
|
+
name: string;
|
|
58
|
+
description: string;
|
|
59
|
+
remarks?: string;
|
|
60
|
+
properties: DocProperty[];
|
|
61
|
+
examples: DocExample[];
|
|
62
|
+
category?: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
interface DocClass {
|
|
66
|
+
name: string;
|
|
67
|
+
description: string;
|
|
68
|
+
remarks?: string;
|
|
69
|
+
constructor?: DocMethod;
|
|
70
|
+
methods: DocMethod[];
|
|
71
|
+
properties: DocProperty[];
|
|
72
|
+
examples: DocExample[];
|
|
73
|
+
category?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface DocFunction {
|
|
77
|
+
name: string;
|
|
78
|
+
signature: string;
|
|
79
|
+
description: string;
|
|
80
|
+
remarks?: string;
|
|
81
|
+
params: DocParam[];
|
|
82
|
+
returns?: { type: string; description: string };
|
|
83
|
+
examples: DocExample[];
|
|
84
|
+
category?: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
interface ExtractedDocs {
|
|
88
|
+
packageDescription: string;
|
|
89
|
+
classes: DocClass[];
|
|
90
|
+
interfaces: DocInterface[];
|
|
91
|
+
functions: DocFunction[];
|
|
92
|
+
types: DocInterface[];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Helper to extract JSDoc comment
|
|
96
|
+
function getJSDocComment(node: ts.Node, sourceFile: ts.SourceFile): string {
|
|
97
|
+
const commentRanges = ts.getLeadingCommentRanges(sourceFile.text, node.pos);
|
|
98
|
+
if (!commentRanges) return '';
|
|
99
|
+
|
|
100
|
+
for (const range of commentRanges) {
|
|
101
|
+
const comment = sourceFile.text.slice(range.pos, range.end);
|
|
102
|
+
if (comment.startsWith('/**')) {
|
|
103
|
+
return comment;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return '';
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Parse JSDoc tags from comment
|
|
110
|
+
function parseJSDoc(comment: string): {
|
|
111
|
+
description: string;
|
|
112
|
+
remarks?: string;
|
|
113
|
+
params: Map<string, string>;
|
|
114
|
+
returns?: string;
|
|
115
|
+
examples: { title?: string; code: string }[];
|
|
116
|
+
defaultValue?: string;
|
|
117
|
+
category?: string;
|
|
118
|
+
see?: string[];
|
|
119
|
+
} {
|
|
120
|
+
if (!comment) {
|
|
121
|
+
return { description: '', params: new Map(), examples: [] };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Remove /** and */ and clean up
|
|
125
|
+
const lines = comment
|
|
126
|
+
.replace(/^\/\*\*\s*/, '')
|
|
127
|
+
.replace(/\s*\*\/$/, '')
|
|
128
|
+
.split('\n')
|
|
129
|
+
.map(line => line.replace(/^\s*\*\s?/, ''));
|
|
130
|
+
|
|
131
|
+
let description = '';
|
|
132
|
+
let remarks = '';
|
|
133
|
+
let returns = '';
|
|
134
|
+
let defaultValue = '';
|
|
135
|
+
let category = '';
|
|
136
|
+
const params = new Map<string, string>();
|
|
137
|
+
const examples: { title?: string; code: string }[] = [];
|
|
138
|
+
const see: string[] = [];
|
|
139
|
+
|
|
140
|
+
let currentTag = '';
|
|
141
|
+
let currentContent = '';
|
|
142
|
+
let inExample = false;
|
|
143
|
+
let exampleTitle = '';
|
|
144
|
+
let exampleContent = '';
|
|
145
|
+
|
|
146
|
+
for (const line of lines) {
|
|
147
|
+
if (line.startsWith('@')) {
|
|
148
|
+
// Save previous tag content
|
|
149
|
+
if (currentTag === 'remarks') remarks = currentContent.trim();
|
|
150
|
+
if (currentTag === 'returns') returns = currentContent.trim();
|
|
151
|
+
if (currentTag === 'defaultValue') defaultValue = currentContent.trim().replace(/^`|`$/g, '');
|
|
152
|
+
if (currentTag === 'category') category = currentContent.trim();
|
|
153
|
+
if (inExample && exampleContent) {
|
|
154
|
+
// Extract code from code blocks
|
|
155
|
+
let code = exampleContent.trim();
|
|
156
|
+
if (code.startsWith('```')) {
|
|
157
|
+
code = code.replace(/^```\w*\n?/, '').replace(/\n?```$/, '');
|
|
158
|
+
}
|
|
159
|
+
examples.push({ title: exampleTitle || undefined, code: code.trim() });
|
|
160
|
+
exampleContent = '';
|
|
161
|
+
exampleTitle = '';
|
|
162
|
+
}
|
|
163
|
+
inExample = false;
|
|
164
|
+
|
|
165
|
+
const tagMatch = line.match(/^@(\w+)(?:\s+(.*))?$/);
|
|
166
|
+
if (tagMatch) {
|
|
167
|
+
currentTag = tagMatch[1];
|
|
168
|
+
currentContent = tagMatch[2] || '';
|
|
169
|
+
|
|
170
|
+
if (currentTag === 'param') {
|
|
171
|
+
const paramMatch = currentContent.match(/^(\w+)\s*-?\s*(.*)$/);
|
|
172
|
+
if (paramMatch) {
|
|
173
|
+
params.set(paramMatch[1], paramMatch[2]);
|
|
174
|
+
}
|
|
175
|
+
currentTag = '';
|
|
176
|
+
} else if (currentTag === 'example') {
|
|
177
|
+
inExample = true;
|
|
178
|
+
// Check if the line after @example is a title (not a code block)
|
|
179
|
+
exampleTitle = currentContent.trim();
|
|
180
|
+
exampleContent = '';
|
|
181
|
+
} else if (currentTag === 'see') {
|
|
182
|
+
see.push(currentContent);
|
|
183
|
+
currentTag = '';
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
} else if (inExample) {
|
|
187
|
+
exampleContent += '\n' + line;
|
|
188
|
+
} else if (currentTag) {
|
|
189
|
+
currentContent += '\n' + line;
|
|
190
|
+
} else if (!currentTag && line.trim()) {
|
|
191
|
+
description += (description ? '\n' : '') + line;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Save final tag content
|
|
196
|
+
if (currentTag === 'remarks') remarks = currentContent.trim();
|
|
197
|
+
if (currentTag === 'returns') returns = currentContent.trim();
|
|
198
|
+
if (currentTag === 'defaultValue') defaultValue = currentContent.trim().replace(/^`|`$/g, '');
|
|
199
|
+
if (currentTag === 'category') category = currentContent.trim();
|
|
200
|
+
if (inExample && exampleContent) {
|
|
201
|
+
let code = exampleContent.trim();
|
|
202
|
+
if (code.startsWith('```')) {
|
|
203
|
+
code = code.replace(/^```\w*\n?/, '').replace(/\n?```$/, '');
|
|
204
|
+
}
|
|
205
|
+
examples.push({ title: exampleTitle || undefined, code: code.trim() });
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return { description, remarks, params, returns, examples, defaultValue, category, see };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Get type string from TypeScript node
|
|
212
|
+
function getTypeString(node: ts.TypeNode | undefined, sourceFile: ts.SourceFile): string {
|
|
213
|
+
if (!node) return 'unknown';
|
|
214
|
+
return node.getText(sourceFile);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Extract documentation from a source file
|
|
218
|
+
function extractFromFile(filePath: string): ExtractedDocs {
|
|
219
|
+
const sourceCode = fs.readFileSync(filePath, 'utf-8');
|
|
220
|
+
const sourceFile = ts.createSourceFile(
|
|
221
|
+
path.basename(filePath),
|
|
222
|
+
sourceCode,
|
|
223
|
+
ts.ScriptTarget.Latest,
|
|
224
|
+
true
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
const docs: ExtractedDocs = {
|
|
228
|
+
packageDescription: '',
|
|
229
|
+
classes: [],
|
|
230
|
+
interfaces: [],
|
|
231
|
+
functions: [],
|
|
232
|
+
types: [],
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// Check for @packageDocumentation
|
|
236
|
+
const fileComment = getJSDocComment(sourceFile, sourceFile);
|
|
237
|
+
if (fileComment.includes('@packageDocumentation')) {
|
|
238
|
+
const parsed = parseJSDoc(fileComment);
|
|
239
|
+
docs.packageDescription = parsed.description;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function visit(node: ts.Node) {
|
|
243
|
+
const comment = getJSDocComment(node, sourceFile);
|
|
244
|
+
const parsed = parseJSDoc(comment);
|
|
245
|
+
|
|
246
|
+
// Skip internal items
|
|
247
|
+
if (comment.includes('@internal')) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (ts.isClassDeclaration(node) && node.name) {
|
|
252
|
+
const classDoc: DocClass = {
|
|
253
|
+
name: node.name.text,
|
|
254
|
+
description: parsed.description,
|
|
255
|
+
remarks: parsed.remarks,
|
|
256
|
+
methods: [],
|
|
257
|
+
properties: [],
|
|
258
|
+
examples: parsed.examples,
|
|
259
|
+
category: parsed.category,
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
for (const member of node.members) {
|
|
263
|
+
const memberComment = getJSDocComment(member, sourceFile);
|
|
264
|
+
if (memberComment.includes('@internal')) continue;
|
|
265
|
+
|
|
266
|
+
const memberParsed = parseJSDoc(memberComment);
|
|
267
|
+
|
|
268
|
+
if (ts.isConstructorDeclaration(member)) {
|
|
269
|
+
const params: DocParam[] = member.parameters.map(p => ({
|
|
270
|
+
name: p.name.getText(sourceFile),
|
|
271
|
+
type: getTypeString(p.type, sourceFile),
|
|
272
|
+
description: memberParsed.params.get(p.name.getText(sourceFile)) || '',
|
|
273
|
+
optional: !!p.questionToken || !!p.initializer,
|
|
274
|
+
defaultValue: p.initializer?.getText(sourceFile),
|
|
275
|
+
}));
|
|
276
|
+
|
|
277
|
+
classDoc.constructor = {
|
|
278
|
+
name: 'constructor',
|
|
279
|
+
signature: `new ${classDoc.name}(${params.map(p => `${p.name}${p.optional ? '?' : ''}: ${p.type}`).join(', ')})`,
|
|
280
|
+
description: memberParsed.description,
|
|
281
|
+
params,
|
|
282
|
+
examples: memberParsed.examples,
|
|
283
|
+
};
|
|
284
|
+
} else if (ts.isMethodDeclaration(member) && member.name) {
|
|
285
|
+
const methodName = member.name.getText(sourceFile);
|
|
286
|
+
if (methodName.startsWith('_')) continue; // Skip private methods
|
|
287
|
+
|
|
288
|
+
const params: DocParam[] = member.parameters.map(p => ({
|
|
289
|
+
name: p.name.getText(sourceFile),
|
|
290
|
+
type: getTypeString(p.type, sourceFile),
|
|
291
|
+
description: memberParsed.params.get(p.name.getText(sourceFile)) || '',
|
|
292
|
+
optional: !!p.questionToken || !!p.initializer,
|
|
293
|
+
defaultValue: p.initializer?.getText(sourceFile),
|
|
294
|
+
}));
|
|
295
|
+
|
|
296
|
+
const returnType = member.type ? getTypeString(member.type, sourceFile) : 'void';
|
|
297
|
+
|
|
298
|
+
classDoc.methods.push({
|
|
299
|
+
name: methodName,
|
|
300
|
+
signature: `${methodName}(${params.map(p => `${p.name}${p.optional ? '?' : ''}: ${p.type}`).join(', ')}): ${returnType}`,
|
|
301
|
+
description: memberParsed.description,
|
|
302
|
+
remarks: memberParsed.remarks,
|
|
303
|
+
params,
|
|
304
|
+
returns: memberParsed.returns ? { type: returnType, description: memberParsed.returns } : undefined,
|
|
305
|
+
examples: memberParsed.examples,
|
|
306
|
+
});
|
|
307
|
+
} else if (ts.isPropertyDeclaration(member) && member.name) {
|
|
308
|
+
const propName = member.name.getText(sourceFile);
|
|
309
|
+
if (propName.startsWith('_') || member.modifiers?.some(m => m.kind === ts.SyntaxKind.PrivateKeyword)) continue;
|
|
310
|
+
|
|
311
|
+
classDoc.properties.push({
|
|
312
|
+
name: propName,
|
|
313
|
+
type: getTypeString(member.type, sourceFile),
|
|
314
|
+
description: memberParsed.description,
|
|
315
|
+
remarks: memberParsed.remarks,
|
|
316
|
+
optional: !!member.questionToken,
|
|
317
|
+
defaultValue: memberParsed.defaultValue,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
docs.classes.push(classDoc);
|
|
323
|
+
} else if (ts.isInterfaceDeclaration(node) && node.name) {
|
|
324
|
+
const interfaceDoc: DocInterface = {
|
|
325
|
+
name: node.name.text,
|
|
326
|
+
description: parsed.description,
|
|
327
|
+
remarks: parsed.remarks,
|
|
328
|
+
properties: [],
|
|
329
|
+
examples: parsed.examples,
|
|
330
|
+
category: parsed.category,
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
for (const member of node.members) {
|
|
334
|
+
const memberComment = getJSDocComment(member, sourceFile);
|
|
335
|
+
const memberParsed = parseJSDoc(memberComment);
|
|
336
|
+
|
|
337
|
+
if (ts.isPropertySignature(member) && member.name) {
|
|
338
|
+
interfaceDoc.properties.push({
|
|
339
|
+
name: member.name.getText(sourceFile),
|
|
340
|
+
type: getTypeString(member.type, sourceFile),
|
|
341
|
+
description: memberParsed.description,
|
|
342
|
+
remarks: memberParsed.remarks,
|
|
343
|
+
optional: !!member.questionToken,
|
|
344
|
+
defaultValue: memberParsed.defaultValue,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
docs.interfaces.push(interfaceDoc);
|
|
350
|
+
} else if (ts.isFunctionDeclaration(node) && node.name) {
|
|
351
|
+
const params: DocParam[] = node.parameters.map(p => ({
|
|
352
|
+
name: p.name.getText(sourceFile),
|
|
353
|
+
type: getTypeString(p.type, sourceFile),
|
|
354
|
+
description: parsed.params.get(p.name.getText(sourceFile)) || '',
|
|
355
|
+
optional: !!p.questionToken || !!p.initializer,
|
|
356
|
+
defaultValue: p.initializer?.getText(sourceFile),
|
|
357
|
+
}));
|
|
358
|
+
|
|
359
|
+
const returnType = node.type ? getTypeString(node.type, sourceFile) : 'void';
|
|
360
|
+
|
|
361
|
+
docs.functions.push({
|
|
362
|
+
name: node.name.text,
|
|
363
|
+
signature: `${node.name.text}(${params.map(p => `${p.name}${p.optional ? '?' : ''}: ${p.type}`).join(', ')}): ${returnType}`,
|
|
364
|
+
description: parsed.description,
|
|
365
|
+
remarks: parsed.remarks,
|
|
366
|
+
params,
|
|
367
|
+
returns: parsed.returns ? { type: returnType, description: parsed.returns } : undefined,
|
|
368
|
+
examples: parsed.examples,
|
|
369
|
+
category: parsed.category,
|
|
370
|
+
});
|
|
371
|
+
} else if (ts.isVariableStatement(node)) {
|
|
372
|
+
// Handle exported const functions like `export const wideLogger = ...`
|
|
373
|
+
for (const decl of node.declarationList.declarations) {
|
|
374
|
+
if (ts.isIdentifier(decl.name) && decl.initializer && ts.isArrowFunction(decl.initializer)) {
|
|
375
|
+
const funcComment = getJSDocComment(node, sourceFile);
|
|
376
|
+
if (funcComment.includes('@internal')) continue;
|
|
377
|
+
|
|
378
|
+
const funcParsed = parseJSDoc(funcComment);
|
|
379
|
+
const arrow = decl.initializer;
|
|
380
|
+
|
|
381
|
+
const params: DocParam[] = arrow.parameters.map(p => ({
|
|
382
|
+
name: p.name.getText(sourceFile),
|
|
383
|
+
type: getTypeString(p.type, sourceFile),
|
|
384
|
+
description: funcParsed.params.get(p.name.getText(sourceFile)) || '',
|
|
385
|
+
optional: !!p.questionToken || !!p.initializer,
|
|
386
|
+
}));
|
|
387
|
+
|
|
388
|
+
const returnType = arrow.type ? getTypeString(arrow.type, sourceFile) : 'void';
|
|
389
|
+
|
|
390
|
+
docs.functions.push({
|
|
391
|
+
name: decl.name.text,
|
|
392
|
+
signature: `${decl.name.text}(${params.map(p => `${p.name}${p.optional ? '?' : ''}: ${p.type}`).join(', ')}): ${returnType}`,
|
|
393
|
+
description: funcParsed.description,
|
|
394
|
+
remarks: funcParsed.remarks,
|
|
395
|
+
params,
|
|
396
|
+
returns: funcParsed.returns ? { type: returnType, description: funcParsed.returns } : undefined,
|
|
397
|
+
examples: funcParsed.examples,
|
|
398
|
+
category: funcParsed.category,
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
} else if (ts.isTypeAliasDeclaration(node) && node.name) {
|
|
403
|
+
// Handle type aliases
|
|
404
|
+
const typeDoc: DocInterface = {
|
|
405
|
+
name: node.name.text,
|
|
406
|
+
description: parsed.description,
|
|
407
|
+
remarks: parsed.remarks,
|
|
408
|
+
properties: [],
|
|
409
|
+
examples: parsed.examples,
|
|
410
|
+
category: parsed.category,
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
if (ts.isTypeLiteralNode(node.type)) {
|
|
414
|
+
for (const member of node.type.members) {
|
|
415
|
+
const memberComment = getJSDocComment(member, sourceFile);
|
|
416
|
+
const memberParsed = parseJSDoc(memberComment);
|
|
417
|
+
|
|
418
|
+
if (ts.isPropertySignature(member) && member.name) {
|
|
419
|
+
typeDoc.properties.push({
|
|
420
|
+
name: member.name.getText(sourceFile),
|
|
421
|
+
type: getTypeString(member.type, sourceFile),
|
|
422
|
+
description: memberParsed.description,
|
|
423
|
+
remarks: memberParsed.remarks,
|
|
424
|
+
optional: !!member.questionToken,
|
|
425
|
+
defaultValue: memberParsed.defaultValue,
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
docs.types.push(typeDoc);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
ts.forEachChild(node, visit);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
visit(sourceFile);
|
|
438
|
+
return docs;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Helper to convert {@link Name} to markdown links
|
|
442
|
+
function convertJSDocLinks(str: string): string {
|
|
443
|
+
// Map of known symbols to their doc paths
|
|
444
|
+
const linkMap: Record<string, string> = {
|
|
445
|
+
'wideLogger': '/docs/sdk-reference/middleware/widelogger',
|
|
446
|
+
'expressWideLogger': '/docs/sdk-reference/middleware/expresswidelogger',
|
|
447
|
+
'WideEvent': '/docs/sdk-reference/interfaces/wideevent',
|
|
448
|
+
'WideEventBuilder': '/docs/sdk-reference/classes/wideeventbuilder',
|
|
449
|
+
'FullEvent': '/docs/sdk-reference/classes/fullevent',
|
|
450
|
+
'FullEventConfig': '/docs/sdk-reference/interfaces/fulleventconfig',
|
|
451
|
+
'HttpRequestProperties': '/docs/sdk-reference/interfaces/httprequestproperties',
|
|
452
|
+
'SamplingConfig': '/docs/sdk-reference/interfaces/samplingconfig',
|
|
453
|
+
'WideLoggerConfig': '/docs/sdk-reference/interfaces/wideloggerconfig',
|
|
454
|
+
'WideEventVariables': '/docs/sdk-reference/types/wideeventvariables',
|
|
455
|
+
};
|
|
456
|
+
|
|
457
|
+
return str.replace(/\{@link\s+(\w+)\}/g, (_, name) => {
|
|
458
|
+
const path = linkMap[name];
|
|
459
|
+
if (path) {
|
|
460
|
+
return `[${name}](${path})`;
|
|
461
|
+
}
|
|
462
|
+
return `\`${name}\``;
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Helper to escape JSX special characters
|
|
467
|
+
function escapeJSX(str: string): string {
|
|
468
|
+
return str
|
|
469
|
+
.replace(/\{/g, '{')
|
|
470
|
+
.replace(/\}/g, '}')
|
|
471
|
+
.replace(/</g, '<')
|
|
472
|
+
.replace(/>/g, '>')
|
|
473
|
+
.replace(/`/g, "'"); // Replace backticks with single quotes for JSX attributes
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Helper to format code for JSX (preserve code blocks but escape for JSX)
|
|
477
|
+
function formatCodeForJSX(code: string): string {
|
|
478
|
+
return code.replace(/`/g, '\\`');
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Helper to clean default value for display (remove backticks)
|
|
482
|
+
function cleanDefaultValue(val: string): string {
|
|
483
|
+
return val.replace(/`/g, '').replace(/'/g, '');
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Helper to format examples for markdown
|
|
487
|
+
function formatExamples(examples: DocExample[]): string {
|
|
488
|
+
return examples.map(ex => {
|
|
489
|
+
let result = '';
|
|
490
|
+
if (ex.title) {
|
|
491
|
+
result += `### ${ex.title}\n\n`;
|
|
492
|
+
}
|
|
493
|
+
result += '```typescript\n' + ex.code + '\n```';
|
|
494
|
+
return result;
|
|
495
|
+
}).join('\n\n');
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Generate MDX content for a class (interactive accordion style)
|
|
499
|
+
function generateClassMDX(cls: DocClass): string {
|
|
500
|
+
const firstExample = cls.examples[0]?.code || '';
|
|
501
|
+
const description = convertJSDocLinks(cls.description);
|
|
502
|
+
|
|
503
|
+
let mdx = `---
|
|
504
|
+
title: ${cls.name}
|
|
505
|
+
description: ${description.split('\n')[0]}
|
|
506
|
+
---
|
|
507
|
+
|
|
508
|
+
# ${cls.name}
|
|
509
|
+
|
|
510
|
+
${description}
|
|
511
|
+
|
|
512
|
+
`;
|
|
513
|
+
|
|
514
|
+
if (cls.remarks) {
|
|
515
|
+
mdx += `${convertJSDocLinks(cls.remarks)}
|
|
516
|
+
|
|
517
|
+
`;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if (cls.examples.length > 0) {
|
|
521
|
+
mdx += `## Usage
|
|
522
|
+
|
|
523
|
+
${formatExamples(cls.examples)}
|
|
524
|
+
|
|
525
|
+
`;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Methods as accordion
|
|
529
|
+
if (cls.methods.length > 0 || cls.constructor) {
|
|
530
|
+
mdx += `## API Reference
|
|
531
|
+
|
|
532
|
+
<APIReference>
|
|
533
|
+
`;
|
|
534
|
+
|
|
535
|
+
// Constructor
|
|
536
|
+
if (cls.constructor) {
|
|
537
|
+
const ctorParams = cls.constructor.params;
|
|
538
|
+
const ctorExample = cls.constructor.examples[0]?.code || `const instance = new ${cls.name}(${ctorParams.map(p => p.name).join(', ')});`;
|
|
539
|
+
|
|
540
|
+
mdx += `
|
|
541
|
+
<APIMethod name="new ${cls.name}(${ctorParams.map(p => p.name).join(', ')})" category="${cls.name}">
|
|
542
|
+
<APIMethodContent>
|
|
543
|
+
<APIMethodLeft>
|
|
544
|
+
<APIDescription>
|
|
545
|
+
${cls.constructor.description}
|
|
546
|
+
</APIDescription>
|
|
547
|
+
${ctorParams.length > 0 ? `
|
|
548
|
+
<APIParameters>
|
|
549
|
+
${ctorParams.map(p => `<APIParam name="${p.name}" type="${escapeJSX(p.type)}" ${p.optional ? '' : 'required'}${p.defaultValue ? ` defaultValue="${p.defaultValue}"` : ''}>
|
|
550
|
+
${p.description}
|
|
551
|
+
</APIParam>`).join('\n')}
|
|
552
|
+
</APIParameters>
|
|
553
|
+
` : ''}
|
|
554
|
+
</APIMethodLeft>
|
|
555
|
+
<APIMethodRight>
|
|
556
|
+
<APISignature>{\`${formatCodeForJSX(cls.constructor.signature)}\`}</APISignature>
|
|
557
|
+
<APIExamples>{\`${formatCodeForJSX(ctorExample)}\`}</APIExamples>
|
|
558
|
+
</APIMethodRight>
|
|
559
|
+
</APIMethodContent>
|
|
560
|
+
</APIMethod>
|
|
561
|
+
`;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// Methods
|
|
565
|
+
for (const method of cls.methods) {
|
|
566
|
+
const methodParams = method.params;
|
|
567
|
+
const methodExample = method.examples[0]?.code || `instance.${method.name}(${methodParams.map(p => p.name).join(', ')});`;
|
|
568
|
+
const note = method.remarks ? method.remarks.split('\n')[0] : '';
|
|
569
|
+
|
|
570
|
+
mdx += `
|
|
571
|
+
<APIMethod name="${cls.name.toLowerCase()}.${method.name}(${methodParams.map(p => p.name).join(', ')})" category="${cls.name}">
|
|
572
|
+
<APIMethodContent>
|
|
573
|
+
<APIMethodLeft>
|
|
574
|
+
<APIDescription${note ? ` note="${escapeJSX(note)}"` : ''}>
|
|
575
|
+
${method.description}
|
|
576
|
+
</APIDescription>
|
|
577
|
+
${methodParams.length > 0 ? `
|
|
578
|
+
<APIParameters>
|
|
579
|
+
${methodParams.map(p => `<APIParam name="${p.name}" type="${escapeJSX(p.type)}" ${p.optional ? '' : 'required'}${p.defaultValue ? ` defaultValue="${p.defaultValue}"` : ''}>
|
|
580
|
+
${p.description}
|
|
581
|
+
</APIParam>`).join('\n')}
|
|
582
|
+
</APIParameters>
|
|
583
|
+
` : ''}
|
|
584
|
+
${method.returns ? `<APIReturns type="${escapeJSX(method.returns.type)}">${method.returns.description}</APIReturns>` : ''}
|
|
585
|
+
</APIMethodLeft>
|
|
586
|
+
<APIMethodRight>
|
|
587
|
+
<APISignature>{\`${formatCodeForJSX(method.signature)}\`}</APISignature>
|
|
588
|
+
<APIExamples>{\`${formatCodeForJSX(methodExample)}\`}</APIExamples>
|
|
589
|
+
</APIMethodRight>
|
|
590
|
+
</APIMethodContent>
|
|
591
|
+
</APIMethod>
|
|
592
|
+
`;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
mdx += `
|
|
596
|
+
</APIReference>
|
|
597
|
+
`;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
return mdx;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// Generate MDX content for an interface (interactive style)
|
|
604
|
+
function generateInterfaceMDX(iface: DocInterface): string {
|
|
605
|
+
const description = convertJSDocLinks(iface.description);
|
|
606
|
+
|
|
607
|
+
let mdx = `---
|
|
608
|
+
title: ${iface.name}
|
|
609
|
+
description: ${description.split('\n')[0] || 'Interface documentation'}
|
|
610
|
+
---
|
|
611
|
+
|
|
612
|
+
# ${iface.name}
|
|
613
|
+
|
|
614
|
+
${description}
|
|
615
|
+
|
|
616
|
+
`;
|
|
617
|
+
|
|
618
|
+
if (iface.remarks) {
|
|
619
|
+
mdx += `${convertJSDocLinks(iface.remarks)}
|
|
620
|
+
|
|
621
|
+
`;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
if (iface.examples.length > 0) {
|
|
625
|
+
mdx += `## Usage
|
|
626
|
+
|
|
627
|
+
${formatExamples(iface.examples)}
|
|
628
|
+
|
|
629
|
+
`;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
if (iface.properties.length > 0) {
|
|
633
|
+
mdx += `## Properties
|
|
634
|
+
|
|
635
|
+
<APIReference>
|
|
636
|
+
`;
|
|
637
|
+
|
|
638
|
+
for (const prop of iface.properties) {
|
|
639
|
+
const propDesc = prop.description || '';
|
|
640
|
+
const cleanedDefault = prop.defaultValue ? cleanDefaultValue(prop.defaultValue) : '';
|
|
641
|
+
|
|
642
|
+
mdx += `
|
|
643
|
+
<APIMethod name="${iface.name}.${prop.name}" category="${iface.name}">
|
|
644
|
+
<APIMethodContent>
|
|
645
|
+
<APIMethodLeft>
|
|
646
|
+
<APIDescription>
|
|
647
|
+
${propDesc}
|
|
648
|
+
${prop.remarks ? `\n${prop.remarks}` : ''}
|
|
649
|
+
</APIDescription>
|
|
650
|
+
<APIParameters>
|
|
651
|
+
<APIParam name="${prop.name}" type="${escapeJSX(prop.type)}" ${prop.optional ? '' : 'required'}${cleanedDefault ? ` defaultValue="${escapeJSX(cleanedDefault)}"` : ''}>
|
|
652
|
+
${propDesc}
|
|
653
|
+
</APIParam>
|
|
654
|
+
</APIParameters>
|
|
655
|
+
</APIMethodLeft>
|
|
656
|
+
<APIMethodRight>
|
|
657
|
+
<APISignature>{\`${prop.name}${prop.optional ? '?' : ''}: ${escapeJSX(prop.type)}\`}</APISignature>
|
|
658
|
+
${cleanedDefault ? `<APIExamples>{\`// Default: ${formatCodeForJSX(cleanedDefault)}\`}</APIExamples>` : ''}
|
|
659
|
+
</APIMethodRight>
|
|
660
|
+
</APIMethodContent>
|
|
661
|
+
</APIMethod>
|
|
662
|
+
`;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
mdx += `
|
|
666
|
+
</APIReference>
|
|
667
|
+
`;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
return mdx;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// Generate MDX content for a function (interactive style)
|
|
674
|
+
function generateFunctionMDX(func: DocFunction): string {
|
|
675
|
+
const funcExample = func.examples[0]?.code || `${func.name}(${func.params.map(p => p.name).join(', ')});`;
|
|
676
|
+
const description = convertJSDocLinks(func.description);
|
|
677
|
+
const remarks = func.remarks ? convertJSDocLinks(func.remarks) : '';
|
|
678
|
+
const note = remarks ? remarks.split('\n')[0] : '';
|
|
679
|
+
|
|
680
|
+
let mdx = `---
|
|
681
|
+
title: ${func.name}
|
|
682
|
+
description: ${description.split('\n')[0]}
|
|
683
|
+
---
|
|
684
|
+
|
|
685
|
+
# ${func.name}
|
|
686
|
+
|
|
687
|
+
${description}
|
|
688
|
+
|
|
689
|
+
`;
|
|
690
|
+
|
|
691
|
+
if (remarks) {
|
|
692
|
+
mdx += `${remarks}
|
|
693
|
+
|
|
694
|
+
`;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
mdx += `## API Reference
|
|
698
|
+
|
|
699
|
+
<APIReference>
|
|
700
|
+
<APIMethod name="${func.name}(${func.params.map(p => p.name).join(', ')})" category="Function">
|
|
701
|
+
<APIMethodContent>
|
|
702
|
+
<APIMethodLeft>
|
|
703
|
+
<APIDescription${note ? ` note="${escapeJSX(note)}"` : ''}>
|
|
704
|
+
${description}
|
|
705
|
+
</APIDescription>
|
|
706
|
+
${func.params.length > 0 ? `
|
|
707
|
+
<APIParameters>
|
|
708
|
+
${func.params.map(p => `<APIParam name="${p.name}" type="${escapeJSX(p.type)}" ${p.optional ? '' : 'required'}${p.defaultValue ? ` defaultValue="${p.defaultValue}"` : ''}>
|
|
709
|
+
${p.description}
|
|
710
|
+
</APIParam>`).join('\n')}
|
|
711
|
+
</APIParameters>
|
|
712
|
+
` : ''}
|
|
713
|
+
${func.returns ? `<APIReturns type="${escapeJSX(func.returns.type)}">${func.returns.description}</APIReturns>` : ''}
|
|
714
|
+
</APIMethodLeft>
|
|
715
|
+
<APIMethodRight>
|
|
716
|
+
<APISignature>{\`${formatCodeForJSX(func.signature)}\`}</APISignature>
|
|
717
|
+
<APIExamples>{\`${formatCodeForJSX(funcExample)}\`}</APIExamples>
|
|
718
|
+
</APIMethodRight>
|
|
719
|
+
</APIMethodContent>
|
|
720
|
+
</APIMethod>
|
|
721
|
+
</APIReference>
|
|
722
|
+
`;
|
|
723
|
+
|
|
724
|
+
// Additional examples if there are more
|
|
725
|
+
if (func.examples.length > 1) {
|
|
726
|
+
mdx += `
|
|
727
|
+
## More Examples
|
|
728
|
+
|
|
729
|
+
${formatExamples(func.examples.slice(1))}
|
|
730
|
+
`;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
return mdx;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// Main function
|
|
737
|
+
async function main() {
|
|
738
|
+
console.log('📚 Generating SDK documentation...\n');
|
|
739
|
+
|
|
740
|
+
// Ensure output directory exists
|
|
741
|
+
if (!fs.existsSync(DOCS_OUTPUT)) {
|
|
742
|
+
fs.mkdirSync(DOCS_OUTPUT, { recursive: true });
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
// Source files to process
|
|
746
|
+
const sourceFiles = [
|
|
747
|
+
'index.ts',
|
|
748
|
+
'client.ts',
|
|
749
|
+
'builder.ts',
|
|
750
|
+
'middleware/hono.ts',
|
|
751
|
+
'middleware/express.ts',
|
|
752
|
+
];
|
|
753
|
+
|
|
754
|
+
const allDocs: ExtractedDocs = {
|
|
755
|
+
packageDescription: '',
|
|
756
|
+
classes: [],
|
|
757
|
+
interfaces: [],
|
|
758
|
+
functions: [],
|
|
759
|
+
types: [],
|
|
760
|
+
};
|
|
761
|
+
|
|
762
|
+
// Extract docs from each file
|
|
763
|
+
for (const file of sourceFiles) {
|
|
764
|
+
const filePath = path.join(SDK_SRC, file);
|
|
765
|
+
if (fs.existsSync(filePath)) {
|
|
766
|
+
console.log(` Processing ${file}...`);
|
|
767
|
+
const docs = extractFromFile(filePath);
|
|
768
|
+
|
|
769
|
+
if (docs.packageDescription) allDocs.packageDescription = docs.packageDescription;
|
|
770
|
+
allDocs.classes.push(...docs.classes);
|
|
771
|
+
allDocs.interfaces.push(...docs.interfaces);
|
|
772
|
+
allDocs.functions.push(...docs.functions);
|
|
773
|
+
allDocs.types.push(...docs.types);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
console.log(`\n Found:`);
|
|
778
|
+
console.log(` - ${allDocs.classes.length} classes`);
|
|
779
|
+
console.log(` - ${allDocs.interfaces.length} interfaces`);
|
|
780
|
+
console.log(` - ${allDocs.functions.length} functions`);
|
|
781
|
+
console.log(` - ${allDocs.types.length} types`);
|
|
782
|
+
|
|
783
|
+
// Filter interfaces for index page too (skip Express's Request extension)
|
|
784
|
+
const validInterfaces = allDocs.interfaces.filter(i =>
|
|
785
|
+
(i.description || i.properties.length > 0 || i.examples.length > 0) &&
|
|
786
|
+
i.name !== 'Request'
|
|
787
|
+
);
|
|
788
|
+
|
|
789
|
+
// Generate index page
|
|
790
|
+
const indexMDX = `---
|
|
791
|
+
title: SDK Reference
|
|
792
|
+
description: Complete API reference for the FullEvent Node.js SDK
|
|
793
|
+
---
|
|
794
|
+
|
|
795
|
+
# SDK Reference
|
|
796
|
+
|
|
797
|
+
${allDocs.packageDescription || 'Complete API reference for the FullEvent Node.js SDK.'}
|
|
798
|
+
|
|
799
|
+
## Classes
|
|
800
|
+
|
|
801
|
+
${allDocs.classes.map(c => `- [${c.name}](/docs/sdk-reference/classes/${c.name.toLowerCase()}) - ${c.description.split('\n')[0]}`).join('\n')}
|
|
802
|
+
|
|
803
|
+
## Middleware
|
|
804
|
+
|
|
805
|
+
${allDocs.functions.filter(f => f.category === 'Middleware').map(f => `- [${f.name}](/docs/sdk-reference/middleware/${f.name.toLowerCase()}) - ${f.description.split('\n')[0]}`).join('\n')}
|
|
806
|
+
|
|
807
|
+
## Interfaces
|
|
808
|
+
|
|
809
|
+
${validInterfaces.map(i => `- [${i.name}](/docs/sdk-reference/interfaces/${i.name.toLowerCase()}) - ${i.description.split('\n')[0]}`).join('\n')}
|
|
810
|
+
|
|
811
|
+
## Types
|
|
812
|
+
|
|
813
|
+
${allDocs.types.map(t => `- [${t.name}](/docs/sdk-reference/types/${t.name.toLowerCase()}) - ${t.description.split('\n')[0]}`).join('\n')}
|
|
814
|
+
`;
|
|
815
|
+
|
|
816
|
+
fs.writeFileSync(path.join(DOCS_OUTPUT, 'index.mdx'), indexMDX);
|
|
817
|
+
console.log(`\n Generated index.mdx`);
|
|
818
|
+
|
|
819
|
+
// Create subdirectories
|
|
820
|
+
const subdirs = ['classes', 'interfaces', 'middleware', 'types'];
|
|
821
|
+
for (const subdir of subdirs) {
|
|
822
|
+
const subdirPath = path.join(DOCS_OUTPUT, subdir);
|
|
823
|
+
if (!fs.existsSync(subdirPath)) {
|
|
824
|
+
fs.mkdirSync(subdirPath, { recursive: true });
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
// Generate class pages
|
|
829
|
+
for (const cls of allDocs.classes) {
|
|
830
|
+
const mdx = generateClassMDX(cls);
|
|
831
|
+
const filename = `${cls.name.toLowerCase()}.mdx`;
|
|
832
|
+
fs.writeFileSync(path.join(DOCS_OUTPUT, 'classes', filename), mdx);
|
|
833
|
+
console.log(` Generated classes/${filename}`);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
// Generate interface pages (skip empty/internal interfaces like Express's Request declaration)
|
|
837
|
+
const filteredInterfaces = allDocs.interfaces.filter(i =>
|
|
838
|
+
(i.description || i.properties.length > 0 || i.examples.length > 0) &&
|
|
839
|
+
i.name !== 'Request' // Skip Express's Request extension
|
|
840
|
+
);
|
|
841
|
+
for (const iface of filteredInterfaces) {
|
|
842
|
+
const mdx = generateInterfaceMDX(iface);
|
|
843
|
+
const filename = `${iface.name.toLowerCase()}.mdx`;
|
|
844
|
+
fs.writeFileSync(path.join(DOCS_OUTPUT, 'interfaces', filename), mdx);
|
|
845
|
+
console.log(` Generated interfaces/${filename}`);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// Generate function/middleware pages
|
|
849
|
+
for (const func of allDocs.functions) {
|
|
850
|
+
const mdx = generateFunctionMDX(func);
|
|
851
|
+
const filename = `${func.name.toLowerCase()}.mdx`;
|
|
852
|
+
const subdir = func.category === 'Middleware' ? 'middleware' : 'functions';
|
|
853
|
+
const subdirPath = path.join(DOCS_OUTPUT, subdir);
|
|
854
|
+
if (!fs.existsSync(subdirPath)) {
|
|
855
|
+
fs.mkdirSync(subdirPath, { recursive: true });
|
|
856
|
+
}
|
|
857
|
+
fs.writeFileSync(path.join(subdirPath, filename), mdx);
|
|
858
|
+
console.log(` Generated ${subdir}/${filename}`);
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
// Generate type pages
|
|
862
|
+
for (const type of allDocs.types) {
|
|
863
|
+
const mdx = generateInterfaceMDX(type);
|
|
864
|
+
const filename = `${type.name.toLowerCase()}.mdx`;
|
|
865
|
+
fs.writeFileSync(path.join(DOCS_OUTPUT, 'types', filename), mdx);
|
|
866
|
+
console.log(` Generated types/${filename}`);
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// Generate meta.json for Fumadocs navigation
|
|
870
|
+
const middlewareFuncs = allDocs.functions.filter(f => f.category === 'Middleware');
|
|
871
|
+
|
|
872
|
+
const meta = {
|
|
873
|
+
title: 'SDK Reference',
|
|
874
|
+
pages: [
|
|
875
|
+
'...', // Auto-sort remaining pages
|
|
876
|
+
],
|
|
877
|
+
// Define the order with separators
|
|
878
|
+
defaultOpen: true,
|
|
879
|
+
};
|
|
880
|
+
|
|
881
|
+
fs.writeFileSync(path.join(DOCS_OUTPUT, 'meta.json'), JSON.stringify(meta, null, 2));
|
|
882
|
+
console.log(` Generated meta.json`);
|
|
883
|
+
|
|
884
|
+
// Generate meta.json for classes subdirectory
|
|
885
|
+
const classesMeta = {
|
|
886
|
+
title: 'Classes',
|
|
887
|
+
pages: allDocs.classes.map(c => c.name.toLowerCase()),
|
|
888
|
+
};
|
|
889
|
+
fs.writeFileSync(path.join(DOCS_OUTPUT, 'classes', 'meta.json'), JSON.stringify(classesMeta, null, 2));
|
|
890
|
+
|
|
891
|
+
// Generate meta.json for interfaces subdirectory
|
|
892
|
+
const interfacesMeta = {
|
|
893
|
+
title: 'Interfaces',
|
|
894
|
+
pages: validInterfaces.map(i => i.name.toLowerCase()),
|
|
895
|
+
};
|
|
896
|
+
fs.writeFileSync(path.join(DOCS_OUTPUT, 'interfaces', 'meta.json'), JSON.stringify(interfacesMeta, null, 2));
|
|
897
|
+
|
|
898
|
+
// Generate meta.json for middleware subdirectory
|
|
899
|
+
const middlewareMeta = {
|
|
900
|
+
title: 'Middleware',
|
|
901
|
+
pages: middlewareFuncs.map(f => f.name.toLowerCase()),
|
|
902
|
+
};
|
|
903
|
+
fs.writeFileSync(path.join(DOCS_OUTPUT, 'middleware', 'meta.json'), JSON.stringify(middlewareMeta, null, 2));
|
|
904
|
+
|
|
905
|
+
// Generate meta.json for types subdirectory
|
|
906
|
+
const typesMeta = {
|
|
907
|
+
title: 'Types',
|
|
908
|
+
pages: allDocs.types.map(t => t.name.toLowerCase()),
|
|
909
|
+
};
|
|
910
|
+
fs.writeFileSync(path.join(DOCS_OUTPUT, 'types', 'meta.json'), JSON.stringify(typesMeta, null, 2));
|
|
911
|
+
|
|
912
|
+
console.log('\n✅ Documentation generated successfully!');
|
|
913
|
+
console.log(` Output: ${DOCS_OUTPUT}`);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
main().catch(console.error);
|
|
917
|
+
|