te.js 2.0.3 → 2.1.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.
Files changed (68) hide show
  1. package/README.md +197 -187
  2. package/auto-docs/analysis/handler-analyzer.js +58 -58
  3. package/auto-docs/analysis/source-resolver.js +101 -101
  4. package/auto-docs/constants.js +37 -37
  5. package/auto-docs/docs-llm/index.js +7 -0
  6. package/auto-docs/{llm → docs-llm}/prompts.js +222 -222
  7. package/auto-docs/{llm → docs-llm}/provider.js +132 -187
  8. package/auto-docs/index.js +146 -146
  9. package/auto-docs/openapi/endpoint-processor.js +277 -277
  10. package/auto-docs/openapi/generator.js +107 -107
  11. package/auto-docs/openapi/level3.js +131 -131
  12. package/auto-docs/openapi/spec-builders.js +244 -244
  13. package/auto-docs/ui/docs-ui.js +186 -186
  14. package/auto-docs/utils/logger.js +17 -17
  15. package/auto-docs/utils/strip-usage.js +10 -10
  16. package/cli/docs-command.js +315 -315
  17. package/cli/fly-command.js +71 -71
  18. package/cli/index.js +56 -56
  19. package/database/index.js +165 -165
  20. package/database/mongodb.js +146 -146
  21. package/database/redis.js +201 -201
  22. package/docs/README.md +36 -36
  23. package/docs/ammo.md +362 -362
  24. package/docs/api-reference.md +490 -489
  25. package/docs/auto-docs.md +216 -215
  26. package/docs/cli.md +152 -152
  27. package/docs/configuration.md +275 -233
  28. package/docs/database.md +390 -391
  29. package/docs/error-handling.md +438 -417
  30. package/docs/file-uploads.md +333 -334
  31. package/docs/getting-started.md +214 -215
  32. package/docs/middleware.md +355 -356
  33. package/docs/rate-limiting.md +393 -394
  34. package/docs/routing.md +302 -302
  35. package/package.json +62 -62
  36. package/rate-limit/algorithms/fixed-window.js +141 -141
  37. package/rate-limit/algorithms/sliding-window.js +147 -147
  38. package/rate-limit/algorithms/token-bucket.js +115 -115
  39. package/rate-limit/base.js +165 -165
  40. package/rate-limit/index.js +147 -147
  41. package/rate-limit/storage/base.js +104 -104
  42. package/rate-limit/storage/memory.js +101 -101
  43. package/rate-limit/storage/redis.js +88 -88
  44. package/server/ammo/body-parser.js +220 -220
  45. package/server/ammo/dispatch-helper.js +103 -103
  46. package/server/ammo/enhancer.js +57 -57
  47. package/server/ammo.js +454 -356
  48. package/server/endpoint.js +97 -74
  49. package/server/error.js +9 -9
  50. package/server/errors/code-context.js +125 -0
  51. package/server/errors/llm-error-service.js +140 -0
  52. package/server/files/helper.js +33 -33
  53. package/server/files/uploader.js +143 -143
  54. package/server/handler.js +158 -113
  55. package/server/target.js +185 -175
  56. package/server/targets/middleware-validator.js +22 -22
  57. package/server/targets/path-validator.js +21 -21
  58. package/server/targets/registry.js +160 -160
  59. package/server/targets/shoot-validator.js +21 -21
  60. package/te.js +428 -363
  61. package/utils/auto-register.js +17 -17
  62. package/utils/configuration.js +64 -64
  63. package/utils/errors-llm-config.js +84 -0
  64. package/utils/request-logger.js +43 -43
  65. package/utils/status-codes.js +82 -82
  66. package/utils/tejas-entrypoint-html.js +18 -18
  67. package/auto-docs/llm/index.js +0 -6
  68. package/auto-docs/llm/parse.js +0 -88
@@ -1,222 +1,222 @@
1
- /**
2
- * Prompt builders for auto-documentation LLM calls.
3
- * Each function returns the prompt string; provider calls analyze(prompt).
4
- */
5
-
6
- import {
7
- PROMPT_HANDLER_SLICE,
8
- PROMPT_HANDLER_SLICE_WITH_DEPS,
9
- PROMPT_DEPENDENCY_SLICE,
10
- PROMPT_GROUP_CODE_LIMIT,
11
- PROMPT_GROUP_CODE_LIMIT_WITH_DEPS,
12
- PROMPT_GROUP_SNIPPET_CHARS,
13
- } from '../constants.js';
14
-
15
- /** Shared rule block for enhance prompts: required vs optional and format. */
16
- const ENHANCE_RULES = `CRITICAL - Required vs optional: Infer from the code which parameters are required and which are optional. Look for: validation that throws or returns error when missing; checks like !payload.field or !ammo.payload.x; required in schema or JSDoc; or optional/undefined/default handling. For every body property and every query parameter you list, you MUST set "required": true or "required": false explicitly. Do not omit "required".
17
-
18
- Respond with ONLY a single JSON object (no markdown, no explanation). Use this shape:`;
19
-
20
- /** JSON shape example for single-endpoint enhance. */
21
- const ENHANCE_RESPONSE_SHAPE = `{
22
- "summary": "Short one-line description",
23
- "description": "Optional longer description",
24
- "request": {
25
- "body": {
26
- "fieldName": { "type": "string", "description": "...", "required": true }
27
- },
28
- "query": {
29
- "paramName": { "type": "string", "description": "...", "required": false }
30
- }
31
- },
32
- "response": { "200": { "description": "Success" }, "201": { "description": "Created" } }
33
- }
34
- - For every field in request.body and request.query set "required": true or "required": false based on code context.
35
- - Include "format" when relevant (e.g. "email", "date-time", "binary").
36
- - Omit "request" or "response" if not applicable. Keep summary under 80 characters.`;
37
-
38
- /** JSON shape example for per-method enhance. */
39
- const PER_METHOD_RESPONSE_SHAPE = `{
40
- "get": { "summary": "...", "description": "...", "response": { "200": { "description": "..." } } },
41
- "put": { "summary": "...", "description": "...", "request": { "body": { "name": { "type": "string", "required": true }, "email": { "type": "string", "format": "email", "required": true } } }, "response": { "200": { "description": "..." } } },
42
- "delete": { "summary": "...", "description": "...", "response": { "204": { "description": "..." } } }
43
- }`;
44
-
45
- /**
46
- * Build handler snippet and related-files section for endpoint prompts. Single place for limits and formatting.
47
- * @param {object} endpointInfo - { handlerSource?, dependencySources? }
48
- * @returns {{ handlerSnippet: string, relatedSection: string }}
49
- */
50
- export function buildEndpointPromptContext(endpointInfo) {
51
- const { handlerSource = '', dependencySources = '' } = endpointInfo;
52
- const handlerLimit = dependencySources ? PROMPT_HANDLER_SLICE_WITH_DEPS : PROMPT_HANDLER_SLICE;
53
- const handlerSnippet = (handlerSource || '').slice(0, handlerLimit);
54
- const relatedSection = dependencySources
55
- ? `
56
-
57
- Related source files (target and dependencies):
58
- \`\`\`javascript
59
- ${dependencySources.slice(0, PROMPT_DEPENDENCY_SLICE)}
60
- \`\`\`
61
- `
62
- : '';
63
- return { handlerSnippet, relatedSection };
64
- }
65
-
66
- /**
67
- * Build prompt for summarizing a target group (tag name + description).
68
- * @param {string} groupId
69
- * @param {Array<object>} endpointsInfo - { path, methods, summary?, description?, handlerSource?, dependencySources? }
70
- * @param {string} [dependencySources]
71
- * @returns {string}
72
- */
73
- export function buildSummarizeGroupPrompt(groupId, endpointsInfo, dependencySources = '') {
74
- if (!endpointsInfo?.length) return '';
75
- const list = endpointsInfo
76
- .map((e) => `- ${e.path} [${(e.methods || []).join(', ')}]: ${e.summary || e.description || '—'}`)
77
- .join('\n');
78
- const codeSnippets = endpointsInfo
79
- .filter((e) => e.handlerSource)
80
- .map((e) => `Path ${e.path}:\n${(e.handlerSource || '').slice(0, PROMPT_GROUP_SNIPPET_CHARS)}`)
81
- .join('\n\n');
82
- const codeLimit = dependencySources ? PROMPT_GROUP_CODE_LIMIT_WITH_DEPS : PROMPT_GROUP_CODE_LIMIT;
83
- const { relatedSection } = buildEndpointPromptContext({ dependencySources });
84
- return `You are an API documentation assistant. A single source file (group) exposes these endpoints:
85
-
86
- Group id: ${groupId}
87
-
88
- Endpoints:
89
- ${list}
90
-
91
- Handler code (excerpts):
92
- \`\`\`javascript
93
- ${codeSnippets.slice(0, codeLimit)}
94
- \`\`\`
95
- ${relatedSection}
96
-
97
- Write a SHORT paragraph (2–4 sentences) describing what this group of endpoints does as a whole. Focus on the domain and purpose, not the HTTP details. Respond with ONLY a single JSON object:
98
- { "name": "Human-readable group name", "description": "Your paragraph here." }
99
- Use "name" as a short label (e.g. "Users", "Health & routes"); keep description under 300 characters.`;
100
- }
101
-
102
- /**
103
- * Build prompt for enhancing a single endpoint (OpenAPI-style metadata).
104
- * @param {object} endpointInfo - { path, methods, metadata?, handlerSource?, dependencySources? }
105
- * @returns {string}
106
- */
107
- export function buildEnhanceEndpointPrompt(endpointInfo) {
108
- const { path, methods = [], metadata = {} } = endpointInfo;
109
- const { handlerSnippet, relatedSection } = buildEndpointPromptContext(endpointInfo);
110
- const existing = Object.keys(metadata).length ? `Existing metadata: ${JSON.stringify(metadata)}\n` : '';
111
- return `You are an API documentation assistant. Given an HTTP endpoint and its source code, suggest OpenAPI-style metadata.
112
-
113
- Endpoint path: ${path}
114
- HTTP methods: ${methods.join(', ')}
115
- ${existing}Handler source (for context):
116
- \`\`\`javascript
117
- ${handlerSnippet}
118
- \`\`\`
119
- ${relatedSection}
120
-
121
- ${ENHANCE_RULES}
122
- ${ENHANCE_RESPONSE_SHAPE}`;
123
- }
124
-
125
- /**
126
- * Build prompt for method-specific endpoint metadata (per-method summary, request, response).
127
- * @param {object} endpointInfo - { path, methods, metadata?, handlerSource?, dependencySources? }
128
- * @returns {string}
129
- */
130
- export function buildEnhanceEndpointPerMethodPrompt(endpointInfo) {
131
- const { path, methods = [], metadata = {} } = endpointInfo;
132
- const { handlerSnippet, relatedSection } = buildEndpointPromptContext(endpointInfo);
133
- const methodsLower = methods.map((m) => m.toLowerCase());
134
- const existing = Object.keys(metadata).length ? `Existing metadata: ${JSON.stringify(metadata)}\n` : '';
135
- return `You are an API documentation assistant. This endpoint supports multiple HTTP methods. Provide METHOD-SPECIFIC metadata so each method is documented accurately.
136
-
137
- Endpoint path: ${path}
138
- HTTP methods: ${methods.join(', ')}
139
-
140
- ${existing}Handler source (for context):
141
- \`\`\`javascript
142
- ${handlerSnippet}
143
- \`\`\`
144
- ${relatedSection}
145
-
146
- RULES:
147
- - Return ONE JSON object keyed by lowercase method name: "get", "put", "post", "delete", "patch", "head", "options".
148
- - Include a key for EACH of: ${methodsLower.map((m) => `"${m}"`).join(', ')}.
149
- - For each method provide: "summary" (one line, method-specific, e.g. "Get user by id" for GET, "Update user" for PUT), optional "description", optional "request" (only for methods that accept a body: put, post, patch; omit for get, delete, head, options), and "response" (ONLY the status codes that THIS method returns — e.g. GET returns 200, DELETE returns 204; do not list 204 under PUT or 200 under DELETE unless the handler returns it for that branch).
150
- - For request.body, set "required": true or "required": false on each field based on code. Include "format" when relevant (e.g. "email").
151
- - Each method's response object must only list the HTTP status codes that that specific method returns.
152
-
153
- Respond with ONLY a single JSON object (no markdown, no explanation). Shape:
154
- ${PER_METHOD_RESPONSE_SHAPE}`;
155
- }
156
-
157
- /**
158
- * Build prompt for reordering tag groups by importance.
159
- * @param {object} spec - OpenAPI 3 spec with spec.tags
160
- * @returns {string}
161
- */
162
- export function buildReorderTagsPrompt(spec) {
163
- const tags = spec?.tags ?? [];
164
- if (!tags.length) return '';
165
- const list = tags
166
- .map((t) => `- "${t.name}"${t.description ? `: ${t.description.slice(0, 200)}` : ''}`)
167
- .join('\n');
168
- return `You are an API documentation assistant. This API has the following tag groups (categories):
169
-
170
- ${list}
171
-
172
- Reorder these groups by importance for a reader exploring the API. Put the most important or central groups first (e.g. main resources, auth), and utility or secondary groups last (e.g. health, admin).
173
-
174
- Respond with ONLY a JSON array of the tag names in the desired order. Example: ["Users", "Auth", "Health & routes"]
175
- Do not include any other text or markdown.`;
176
- }
177
-
178
- /**
179
- * Build prompt for generating the API overview Markdown page.
180
- * @param {object} spec - OpenAPI 3 spec with info, tags, paths
181
- * @param {object} [options] - { title?, version?, description? }
182
- * @returns {string}
183
- */
184
- export function buildOverviewPrompt(spec, options = {}) {
185
- const info = spec?.info ?? {};
186
- const title = options.title ?? info.title ?? 'API';
187
- const version = options.version ?? info.version ?? '1.0.0';
188
- const description = options.description ?? info.description ?? '';
189
- const tags = spec?.tags ?? [];
190
- const paths = spec?.paths ?? {};
191
- const tagList = tags.map((t) => `- **${t.name}**: ${t.description || '—'}`).join('\n');
192
- const pathList = Object.entries(paths)
193
- .slice(0, 50)
194
- .map(([p, ops]) => {
195
- const methods = Object.keys(ops)
196
- .filter((k) => ['get', 'post', 'put', 'delete', 'patch', 'head', 'options'].includes(k))
197
- .join(', ')
198
- .toUpperCase();
199
- return `- ${p} [${methods}]`;
200
- })
201
- .join('\n');
202
-
203
- return `You are an API documentation assistant. Generate a single comprehensive Markdown document for this API.
204
-
205
- API title: ${title}
206
- Version: ${version}
207
- ${description ? `Description: ${description.slice(0, 500)}\n` : ''}
208
-
209
- Tag groups (API areas):
210
- ${tagList}
211
-
212
- Sample of endpoints (path and methods):
213
- ${pathList}
214
-
215
- Write a Markdown document that includes:
216
- 1. A short **project summary** (what this API does).
217
- 2. **APIs available**: a high-level list of the tag groups and what they cover.
218
- 3. **Key endpoints** or "Getting started": suggest a few important paths (e.g. health check, main resources).
219
- 4. Any other brief sections that fit (e.g. Authentication, Rate limits) only if clearly inferable from the spec; otherwise omit.
220
-
221
- Use clear headings (##, ###). Keep the document concise (under 2 pages). Output ONLY the Markdown, no surrounding explanation or code fence.`;
222
- }
1
+ /**
2
+ * Prompt builders for auto-documentation LLM calls.
3
+ * Each function returns the prompt string; provider calls analyze(prompt).
4
+ */
5
+
6
+ import {
7
+ PROMPT_HANDLER_SLICE,
8
+ PROMPT_HANDLER_SLICE_WITH_DEPS,
9
+ PROMPT_DEPENDENCY_SLICE,
10
+ PROMPT_GROUP_CODE_LIMIT,
11
+ PROMPT_GROUP_CODE_LIMIT_WITH_DEPS,
12
+ PROMPT_GROUP_SNIPPET_CHARS,
13
+ } from '../constants.js';
14
+
15
+ /** Shared rule block for enhance prompts: required vs optional and format. */
16
+ const ENHANCE_RULES = `CRITICAL - Required vs optional: Infer from the code which parameters are required and which are optional. Look for: validation that throws or returns error when missing; checks like !payload.field or !ammo.payload.x; required in schema or JSDoc; or optional/undefined/default handling. For every body property and every query parameter you list, you MUST set "required": true or "required": false explicitly. Do not omit "required".
17
+
18
+ Respond with ONLY a single JSON object (no markdown, no explanation). Use this shape:`;
19
+
20
+ /** JSON shape example for single-endpoint enhance. */
21
+ const ENHANCE_RESPONSE_SHAPE = `{
22
+ "summary": "Short one-line description",
23
+ "description": "Optional longer description",
24
+ "request": {
25
+ "body": {
26
+ "fieldName": { "type": "string", "description": "...", "required": true }
27
+ },
28
+ "query": {
29
+ "paramName": { "type": "string", "description": "...", "required": false }
30
+ }
31
+ },
32
+ "response": { "200": { "description": "Success" }, "201": { "description": "Created" } }
33
+ }
34
+ - For every field in request.body and request.query set "required": true or "required": false based on code context.
35
+ - Include "format" when relevant (e.g. "email", "date-time", "binary").
36
+ - Omit "request" or "response" if not applicable. Keep summary under 80 characters.`;
37
+
38
+ /** JSON shape example for per-method enhance. */
39
+ const PER_METHOD_RESPONSE_SHAPE = `{
40
+ "get": { "summary": "...", "description": "...", "response": { "200": { "description": "..." } } },
41
+ "put": { "summary": "...", "description": "...", "request": { "body": { "name": { "type": "string", "required": true }, "email": { "type": "string", "format": "email", "required": true } } }, "response": { "200": { "description": "..." } } },
42
+ "delete": { "summary": "...", "description": "...", "response": { "204": { "description": "..." } } }
43
+ }`;
44
+
45
+ /**
46
+ * Build handler snippet and related-files section for endpoint prompts. Single place for limits and formatting.
47
+ * @param {object} endpointInfo - { handlerSource?, dependencySources? }
48
+ * @returns {{ handlerSnippet: string, relatedSection: string }}
49
+ */
50
+ export function buildEndpointPromptContext(endpointInfo) {
51
+ const { handlerSource = '', dependencySources = '' } = endpointInfo;
52
+ const handlerLimit = dependencySources ? PROMPT_HANDLER_SLICE_WITH_DEPS : PROMPT_HANDLER_SLICE;
53
+ const handlerSnippet = (handlerSource || '').slice(0, handlerLimit);
54
+ const relatedSection = dependencySources
55
+ ? `
56
+
57
+ Related source files (target and dependencies):
58
+ \`\`\`javascript
59
+ ${dependencySources.slice(0, PROMPT_DEPENDENCY_SLICE)}
60
+ \`\`\`
61
+ `
62
+ : '';
63
+ return { handlerSnippet, relatedSection };
64
+ }
65
+
66
+ /**
67
+ * Build prompt for summarizing a target group (tag name + description).
68
+ * @param {string} groupId
69
+ * @param {Array<object>} endpointsInfo - { path, methods, summary?, description?, handlerSource?, dependencySources? }
70
+ * @param {string} [dependencySources]
71
+ * @returns {string}
72
+ */
73
+ export function buildSummarizeGroupPrompt(groupId, endpointsInfo, dependencySources = '') {
74
+ if (!endpointsInfo?.length) return '';
75
+ const list = endpointsInfo
76
+ .map((e) => `- ${e.path} [${(e.methods || []).join(', ')}]: ${e.summary || e.description || '—'}`)
77
+ .join('\n');
78
+ const codeSnippets = endpointsInfo
79
+ .filter((e) => e.handlerSource)
80
+ .map((e) => `Path ${e.path}:\n${(e.handlerSource || '').slice(0, PROMPT_GROUP_SNIPPET_CHARS)}`)
81
+ .join('\n\n');
82
+ const codeLimit = dependencySources ? PROMPT_GROUP_CODE_LIMIT_WITH_DEPS : PROMPT_GROUP_CODE_LIMIT;
83
+ const { relatedSection } = buildEndpointPromptContext({ dependencySources });
84
+ return `You are an API documentation assistant. A single source file (group) exposes these endpoints:
85
+
86
+ Group id: ${groupId}
87
+
88
+ Endpoints:
89
+ ${list}
90
+
91
+ Handler code (excerpts):
92
+ \`\`\`javascript
93
+ ${codeSnippets.slice(0, codeLimit)}
94
+ \`\`\`
95
+ ${relatedSection}
96
+
97
+ Write a SHORT paragraph (2–4 sentences) describing what this group of endpoints does as a whole. Focus on the domain and purpose, not the HTTP details. Respond with ONLY a single JSON object:
98
+ { "name": "Human-readable group name", "description": "Your paragraph here." }
99
+ Use "name" as a short label (e.g. "Users", "Health & routes"); keep description under 300 characters.`;
100
+ }
101
+
102
+ /**
103
+ * Build prompt for enhancing a single endpoint (OpenAPI-style metadata).
104
+ * @param {object} endpointInfo - { path, methods, metadata?, handlerSource?, dependencySources? }
105
+ * @returns {string}
106
+ */
107
+ export function buildEnhanceEndpointPrompt(endpointInfo) {
108
+ const { path, methods = [], metadata = {} } = endpointInfo;
109
+ const { handlerSnippet, relatedSection } = buildEndpointPromptContext(endpointInfo);
110
+ const existing = Object.keys(metadata).length ? `Existing metadata: ${JSON.stringify(metadata)}\n` : '';
111
+ return `You are an API documentation assistant. Given an HTTP endpoint and its source code, suggest OpenAPI-style metadata.
112
+
113
+ Endpoint path: ${path}
114
+ HTTP methods: ${methods.join(', ')}
115
+ ${existing}Handler source (for context):
116
+ \`\`\`javascript
117
+ ${handlerSnippet}
118
+ \`\`\`
119
+ ${relatedSection}
120
+
121
+ ${ENHANCE_RULES}
122
+ ${ENHANCE_RESPONSE_SHAPE}`;
123
+ }
124
+
125
+ /**
126
+ * Build prompt for method-specific endpoint metadata (per-method summary, request, response).
127
+ * @param {object} endpointInfo - { path, methods, metadata?, handlerSource?, dependencySources? }
128
+ * @returns {string}
129
+ */
130
+ export function buildEnhanceEndpointPerMethodPrompt(endpointInfo) {
131
+ const { path, methods = [], metadata = {} } = endpointInfo;
132
+ const { handlerSnippet, relatedSection } = buildEndpointPromptContext(endpointInfo);
133
+ const methodsLower = methods.map((m) => m.toLowerCase());
134
+ const existing = Object.keys(metadata).length ? `Existing metadata: ${JSON.stringify(metadata)}\n` : '';
135
+ return `You are an API documentation assistant. This endpoint supports multiple HTTP methods. Provide METHOD-SPECIFIC metadata so each method is documented accurately.
136
+
137
+ Endpoint path: ${path}
138
+ HTTP methods: ${methods.join(', ')}
139
+
140
+ ${existing}Handler source (for context):
141
+ \`\`\`javascript
142
+ ${handlerSnippet}
143
+ \`\`\`
144
+ ${relatedSection}
145
+
146
+ RULES:
147
+ - Return ONE JSON object keyed by lowercase method name: "get", "put", "post", "delete", "patch", "head", "options".
148
+ - Include a key for EACH of: ${methodsLower.map((m) => `"${m}"`).join(', ')}.
149
+ - For each method provide: "summary" (one line, method-specific, e.g. "Get user by id" for GET, "Update user" for PUT), optional "description", optional "request" (only for methods that accept a body: put, post, patch; omit for get, delete, head, options), and "response" (ONLY the status codes that THIS method returns — e.g. GET returns 200, DELETE returns 204; do not list 204 under PUT or 200 under DELETE unless the handler returns it for that branch).
150
+ - For request.body, set "required": true or "required": false on each field based on code. Include "format" when relevant (e.g. "email").
151
+ - Each method's response object must only list the HTTP status codes that that specific method returns.
152
+
153
+ Respond with ONLY a single JSON object (no markdown, no explanation). Shape:
154
+ ${PER_METHOD_RESPONSE_SHAPE}`;
155
+ }
156
+
157
+ /**
158
+ * Build prompt for reordering tag groups by importance.
159
+ * @param {object} spec - OpenAPI 3 spec with spec.tags
160
+ * @returns {string}
161
+ */
162
+ export function buildReorderTagsPrompt(spec) {
163
+ const tags = spec?.tags ?? [];
164
+ if (!tags.length) return '';
165
+ const list = tags
166
+ .map((t) => `- "${t.name}"${t.description ? `: ${t.description.slice(0, 200)}` : ''}`)
167
+ .join('\n');
168
+ return `You are an API documentation assistant. This API has the following tag groups (categories):
169
+
170
+ ${list}
171
+
172
+ Reorder these groups by importance for a reader exploring the API. Put the most important or central groups first (e.g. main resources, auth), and utility or secondary groups last (e.g. health, admin).
173
+
174
+ Respond with ONLY a JSON array of the tag names in the desired order. Example: ["Users", "Auth", "Health & routes"]
175
+ Do not include any other text or markdown.`;
176
+ }
177
+
178
+ /**
179
+ * Build prompt for generating the API overview Markdown page.
180
+ * @param {object} spec - OpenAPI 3 spec with info, tags, paths
181
+ * @param {object} [options] - { title?, version?, description? }
182
+ * @returns {string}
183
+ */
184
+ export function buildOverviewPrompt(spec, options = {}) {
185
+ const info = spec?.info ?? {};
186
+ const title = options.title ?? info.title ?? 'API';
187
+ const version = options.version ?? info.version ?? '1.0.0';
188
+ const description = options.description ?? info.description ?? '';
189
+ const tags = spec?.tags ?? [];
190
+ const paths = spec?.paths ?? {};
191
+ const tagList = tags.map((t) => `- **${t.name}**: ${t.description || '—'}`).join('\n');
192
+ const pathList = Object.entries(paths)
193
+ .slice(0, 50)
194
+ .map(([p, ops]) => {
195
+ const methods = Object.keys(ops)
196
+ .filter((k) => ['get', 'post', 'put', 'delete', 'patch', 'head', 'options'].includes(k))
197
+ .join(', ')
198
+ .toUpperCase();
199
+ return `- ${p} [${methods}]`;
200
+ })
201
+ .join('\n');
202
+
203
+ return `You are an API documentation assistant. Generate a single comprehensive Markdown document for this API.
204
+
205
+ API title: ${title}
206
+ Version: ${version}
207
+ ${description ? `Description: ${description.slice(0, 500)}\n` : ''}
208
+
209
+ Tag groups (API areas):
210
+ ${tagList}
211
+
212
+ Sample of endpoints (path and methods):
213
+ ${pathList}
214
+
215
+ Write a Markdown document that includes:
216
+ 1. A short **project summary** (what this API does).
217
+ 2. **APIs available**: a high-level list of the tag groups and what they cover.
218
+ 3. **Key endpoints** or "Getting started": suggest a few important paths (e.g. health check, main resources).
219
+ 4. Any other brief sections that fit (e.g. Authentication, Rate limits) only if clearly inferable from the spec; otherwise omit.
220
+
221
+ Use clear headings (##, ###). Keep the document concise (under 2 pages). Output ONLY the Markdown, no surrounding explanation or code fence.`;
222
+ }