froggy-docs 1.1.1 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +21 -0
- package/.github/workflows/froggy-docs.yml +59 -0
- package/ABOUT.md +99 -0
- package/MILESTONE.md +29 -17
- package/bin/froggy_docs.dart +3 -45
- package/frontend/lib/app.dart +487 -280
- package/frontend/lib/components/file_field.dart +75 -0
- package/frontend/web/app.js +224 -67
- package/frontend/web/froggy_docs.json +288 -4
- package/lib/froggy_docs.dart +1 -0
- package/lib/src/cli_runner.dart +123 -0
- package/lib/src/parser_engine.dart +351 -22
- package/lib/src/watcher_engine.dart +46 -14
- package/lib/src/web_server.dart +40 -17
- package/package.js +0 -0
- package/package.json +4 -4
- package/bin/froggy-docs +0 -0
|
@@ -89,15 +89,31 @@
|
|
|
89
89
|
},
|
|
90
90
|
"/api/login": {
|
|
91
91
|
"post": {
|
|
92
|
-
"summary": "User login
|
|
92
|
+
"summary": "User login",
|
|
93
93
|
"responses": {
|
|
94
94
|
"200": {
|
|
95
95
|
"description": "Successful response"
|
|
96
96
|
}
|
|
97
97
|
},
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
|
|
98
|
+
"requestBody": {
|
|
99
|
+
"content": {
|
|
100
|
+
"application/json": {
|
|
101
|
+
"schema": {
|
|
102
|
+
"type": "object",
|
|
103
|
+
"properties": {
|
|
104
|
+
"username": {
|
|
105
|
+
"type": "string",
|
|
106
|
+
"description": "Inferred from JSON"
|
|
107
|
+
},
|
|
108
|
+
"password": {
|
|
109
|
+
"type": "string",
|
|
110
|
+
"description": "Inferred from JSON"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
101
117
|
}
|
|
102
118
|
},
|
|
103
119
|
"/api/register": {
|
|
@@ -112,6 +128,265 @@
|
|
|
112
128
|
"Auth"
|
|
113
129
|
]
|
|
114
130
|
}
|
|
131
|
+
},
|
|
132
|
+
"/api/users": {
|
|
133
|
+
"get": {
|
|
134
|
+
"summary": "List users with pagination",
|
|
135
|
+
"responses": {
|
|
136
|
+
"200": {
|
|
137
|
+
"description": "Successful response"
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
"parameters": [
|
|
141
|
+
{
|
|
142
|
+
"name": "page",
|
|
143
|
+
"in": "query",
|
|
144
|
+
"description": "Page number",
|
|
145
|
+
"schema": {
|
|
146
|
+
"type": "int",
|
|
147
|
+
"default": 1
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"name": "limit",
|
|
152
|
+
"in": "query",
|
|
153
|
+
"description": "Items per page",
|
|
154
|
+
"schema": {
|
|
155
|
+
"type": "int",
|
|
156
|
+
"default": 10
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"name": "search",
|
|
161
|
+
"in": "query",
|
|
162
|
+
"description": "Search term",
|
|
163
|
+
"schema": {
|
|
164
|
+
"type": "string"
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
"post": {
|
|
170
|
+
"summary": "Create user",
|
|
171
|
+
"responses": {
|
|
172
|
+
"201": {
|
|
173
|
+
"description": "User created",
|
|
174
|
+
"content": {
|
|
175
|
+
"application/json": {
|
|
176
|
+
"schema": {
|
|
177
|
+
"type": "object"
|
|
178
|
+
},
|
|
179
|
+
"example": {
|
|
180
|
+
"id": 1
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
"400": {
|
|
186
|
+
"description": "Bad request",
|
|
187
|
+
"content": {
|
|
188
|
+
"application/json": {
|
|
189
|
+
"schema": {
|
|
190
|
+
"type": "object"
|
|
191
|
+
},
|
|
192
|
+
"example": {
|
|
193
|
+
"error": "Invalid input"
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
"/api/users/1": {
|
|
202
|
+
"delete": {
|
|
203
|
+
"summary": "Delete a user",
|
|
204
|
+
"responses": {
|
|
205
|
+
"200": {
|
|
206
|
+
"description": "Successful response"
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
"security": [
|
|
210
|
+
{
|
|
211
|
+
"bearerAuth": []
|
|
212
|
+
}
|
|
213
|
+
]
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
"/api/products": {
|
|
217
|
+
"get": {
|
|
218
|
+
"summary": "List products",
|
|
219
|
+
"responses": {
|
|
220
|
+
"200": {
|
|
221
|
+
"description": "Successful response"
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
"tags": [
|
|
225
|
+
"Products",
|
|
226
|
+
"Catalog"
|
|
227
|
+
]
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
"/api/upload": {
|
|
231
|
+
"post": {
|
|
232
|
+
"summary": "Upload user avatar",
|
|
233
|
+
"responses": {
|
|
234
|
+
"200": {
|
|
235
|
+
"description": "Successful response"
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
"requestBody": {
|
|
239
|
+
"content": {
|
|
240
|
+
"multipart/form-data": {
|
|
241
|
+
"schema": {
|
|
242
|
+
"type": "object",
|
|
243
|
+
"properties": {
|
|
244
|
+
"avatar": {
|
|
245
|
+
"type": "string",
|
|
246
|
+
"format": "binary",
|
|
247
|
+
"description": "User profile picture"
|
|
248
|
+
},
|
|
249
|
+
"document": {
|
|
250
|
+
"type": "string",
|
|
251
|
+
"format": "binary",
|
|
252
|
+
"description": "Optional PDF attachment"
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
"/api/profile": {
|
|
262
|
+
"post": {
|
|
263
|
+
"summary": "Update user profile with avatar",
|
|
264
|
+
"responses": {
|
|
265
|
+
"200": {
|
|
266
|
+
"description": "Successful response"
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
"requestBody": {
|
|
270
|
+
"content": {
|
|
271
|
+
"multipart/form-data": {
|
|
272
|
+
"schema": {
|
|
273
|
+
"type": "object",
|
|
274
|
+
"properties": {
|
|
275
|
+
"name": {
|
|
276
|
+
"type": "string",
|
|
277
|
+
"description": "User display name"
|
|
278
|
+
},
|
|
279
|
+
"bio": {
|
|
280
|
+
"type": "string",
|
|
281
|
+
"description": "User biography"
|
|
282
|
+
},
|
|
283
|
+
"avatar": {
|
|
284
|
+
"type": "string",
|
|
285
|
+
"format": "binary",
|
|
286
|
+
"description": "Profile picture"
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
"/api/data": {
|
|
296
|
+
"get": {
|
|
297
|
+
"summary": "Get data with custom headers",
|
|
298
|
+
"responses": {
|
|
299
|
+
"200": {
|
|
300
|
+
"description": "Successful response"
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
"parameters": [
|
|
304
|
+
{
|
|
305
|
+
"name": "X-Request-ID",
|
|
306
|
+
"in": "header",
|
|
307
|
+
"description": "Optional trace ID",
|
|
308
|
+
"schema": {
|
|
309
|
+
"type": "string"
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
"name": "X-Api-Version",
|
|
314
|
+
"in": "header",
|
|
315
|
+
"description": "API version",
|
|
316
|
+
"schema": {
|
|
317
|
+
"type": "string"
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
]
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
"/api/items": {
|
|
324
|
+
"post": {
|
|
325
|
+
"summary": "Create item in Go",
|
|
326
|
+
"responses": {
|
|
327
|
+
"200": {
|
|
328
|
+
"description": "Successful response"
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
"requestBody": {
|
|
332
|
+
"content": {
|
|
333
|
+
"application/json": {
|
|
334
|
+
"schema": {
|
|
335
|
+
"type": "object",
|
|
336
|
+
"properties": {
|
|
337
|
+
"title": {
|
|
338
|
+
"type": "string",
|
|
339
|
+
"description": "Item title"
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
"/api/ignored": {
|
|
349
|
+
"get": {
|
|
350
|
+
"summary": "This should be ignored",
|
|
351
|
+
"responses": {
|
|
352
|
+
"200": {
|
|
353
|
+
"description": "Successful response"
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
"/api/temp": {
|
|
359
|
+
"get": {
|
|
360
|
+
"summary": "Temporary endpoint",
|
|
361
|
+
"responses": {
|
|
362
|
+
"200": {
|
|
363
|
+
"description": "Successful response"
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
"/api/test": {
|
|
369
|
+
"get": {
|
|
370
|
+
"summary": "Test endpoint",
|
|
371
|
+
"responses": {
|
|
372
|
+
"200": {
|
|
373
|
+
"description": "Successful response"
|
|
374
|
+
}
|
|
375
|
+
},
|
|
376
|
+
"tags": [
|
|
377
|
+
"Test"
|
|
378
|
+
]
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
"/api/import": {
|
|
382
|
+
"post": {
|
|
383
|
+
"summary": "Import from body file",
|
|
384
|
+
"responses": {
|
|
385
|
+
"200": {
|
|
386
|
+
"description": "Successful response"
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
115
390
|
}
|
|
116
391
|
},
|
|
117
392
|
"components": {
|
|
@@ -129,6 +404,15 @@
|
|
|
129
404
|
},
|
|
130
405
|
{
|
|
131
406
|
"name": "Auth"
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
"name": "Products"
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
"name": "Catalog"
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
"name": "Test"
|
|
132
416
|
}
|
|
133
417
|
]
|
|
134
418
|
}
|
package/lib/froggy_docs.dart
CHANGED
package/lib/src/cli_runner.dart
CHANGED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import 'dart:io';
|
|
2
|
+
import 'package:args/args.dart';
|
|
3
|
+
import 'package:froggy_docs/src/watcher_engine.dart';
|
|
4
|
+
import 'package:froggy_docs/src/web_server.dart';
|
|
5
|
+
import 'package:froggy_docs/src/parser_engine.dart';
|
|
6
|
+
|
|
7
|
+
class CliRunner {
|
|
8
|
+
void run(List<String> arguments) async {
|
|
9
|
+
final parser = ArgParser();
|
|
10
|
+
parser.addCommand('watch');
|
|
11
|
+
parser.addCommand('serve');
|
|
12
|
+
parser.addCommand('build');
|
|
13
|
+
parser.addOption(
|
|
14
|
+
'port',
|
|
15
|
+
abbr: 'p',
|
|
16
|
+
help: 'Port for server',
|
|
17
|
+
defaultsTo: '8080',
|
|
18
|
+
);
|
|
19
|
+
parser.addOption(
|
|
20
|
+
'proxy',
|
|
21
|
+
abbr: 'x',
|
|
22
|
+
help: 'Proxy API requests to this URL (e.g., http://localhost:3000)',
|
|
23
|
+
defaultsTo: '',
|
|
24
|
+
);
|
|
25
|
+
parser.addOption(
|
|
26
|
+
'output',
|
|
27
|
+
abbr: 'o',
|
|
28
|
+
help: 'Output path for generated JSON spec (default: frontend/web/froggy_docs.json)',
|
|
29
|
+
defaultsTo: 'frontend/web/froggy_docs.json',
|
|
30
|
+
);
|
|
31
|
+
parser.addOption(
|
|
32
|
+
'ignore',
|
|
33
|
+
help: 'Glob pattern for paths to exclude from watching (e.g., "**/*.g.dart")',
|
|
34
|
+
defaultsTo: '',
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
final results = parser.parse(arguments);
|
|
39
|
+
|
|
40
|
+
final outputPath = results['output'] as String;
|
|
41
|
+
final ignorePattern = results['ignore'] as String;
|
|
42
|
+
|
|
43
|
+
if (results.command?.name == 'watch') {
|
|
44
|
+
print('🐸 FroggyDocs is watching your API project...');
|
|
45
|
+
final watcher = WatcherEngine(outputPath: outputPath, ignorePattern: ignorePattern);
|
|
46
|
+
watcher.startWatching(Directory.current.path);
|
|
47
|
+
} else if (results.command?.name == 'serve') {
|
|
48
|
+
final port = int.parse(results['port'] as String);
|
|
49
|
+
final proxyUrl = results['proxy'] as String;
|
|
50
|
+
print('🐸 Starting FroggyDocs server with live reload...');
|
|
51
|
+
if (proxyUrl.isNotEmpty) {
|
|
52
|
+
print('🔄 Proxy enabled: API requests will be forwarded to $proxyUrl');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
await startServer(port: port, proxyUrl: proxyUrl);
|
|
56
|
+
final watcher = WatcherEngine(outputPath: outputPath, ignorePattern: ignorePattern);
|
|
57
|
+
watcher.startWatching(Directory.current.path);
|
|
58
|
+
} else if (results.command?.name == 'build') {
|
|
59
|
+
print('🐸 Building static FroggyDocs site...');
|
|
60
|
+
final parser = ParserEngine();
|
|
61
|
+
parser.setOutputPath(outputPath);
|
|
62
|
+
_buildStaticSite(parser, Directory.current.path);
|
|
63
|
+
print('✅ Build complete. Output: $outputPath');
|
|
64
|
+
} else {
|
|
65
|
+
_printHelp();
|
|
66
|
+
}
|
|
67
|
+
} catch (e) {
|
|
68
|
+
print('Error: ${e.toString()}');
|
|
69
|
+
exit(1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
void _buildStaticSite(ParserEngine parser, String directoryPath) {
|
|
74
|
+
final dir = Directory(directoryPath);
|
|
75
|
+
try {
|
|
76
|
+
dir.listSync(recursive: true).forEach((entity) {
|
|
77
|
+
if (entity is File) {
|
|
78
|
+
final normalizedPath = entity.path.replaceAll('\\', '/');
|
|
79
|
+
if (!_shouldIgnore(normalizedPath)) {
|
|
80
|
+
parser.parseFile(entity.path);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
} catch (e) {
|
|
85
|
+
print('⚠️ Warning during build: $e');
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
bool _shouldIgnore(String path) {
|
|
90
|
+
return path.contains('/.dart_tool/') ||
|
|
91
|
+
path.contains('/frontend/') ||
|
|
92
|
+
path.contains('/node_modules/') ||
|
|
93
|
+
path.contains('/.git/') ||
|
|
94
|
+
path.endsWith('.json') ||
|
|
95
|
+
path.endsWith('.md');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
void _printHelp() {
|
|
99
|
+
print('''
|
|
100
|
+
🐸 FroggyDocs v1.0.0
|
|
101
|
+
|
|
102
|
+
Usage:
|
|
103
|
+
froggy_docs serve Start server with live documentation
|
|
104
|
+
froggy_docs watch Watch for changes and regenerate docs
|
|
105
|
+
froggy_docs build Generate static documentation without starting server
|
|
106
|
+
|
|
107
|
+
Options:
|
|
108
|
+
-p, --port <port> Port number (default: 8080)
|
|
109
|
+
-x, --proxy <url> Proxy API requests to this URL
|
|
110
|
+
-o, --output <path> Output path for generated JSON spec
|
|
111
|
+
(default: frontend/web/froggy_docs.json)
|
|
112
|
+
--ignore <glob> Glob pattern for paths to exclude from watching
|
|
113
|
+
(e.g., "**/*.g.dart")
|
|
114
|
+
-h, --help Show this help message
|
|
115
|
+
|
|
116
|
+
Examples:
|
|
117
|
+
froggy_docs serve --port 3000
|
|
118
|
+
froggy_docs serve --output docs/api.json
|
|
119
|
+
froggy_docs build --output docs/froggy_docs.json
|
|
120
|
+
froggy_docs watch --ignore "**/*.g.dart"
|
|
121
|
+
''');
|
|
122
|
+
}
|
|
123
|
+
}
|