te.js 2.1.0 → 2.1.2

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 (70) hide show
  1. package/README.md +197 -196
  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 -7
  6. package/auto-docs/docs-llm/prompts.js +222 -222
  7. package/auto-docs/docs-llm/provider.js +132 -132
  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/cors/index.js +71 -0
  20. package/database/index.js +165 -165
  21. package/database/mongodb.js +146 -146
  22. package/database/redis.js +201 -201
  23. package/docs/README.md +36 -36
  24. package/docs/ammo.md +362 -362
  25. package/docs/api-reference.md +490 -490
  26. package/docs/auto-docs.md +216 -216
  27. package/docs/cli.md +152 -152
  28. package/docs/configuration.md +275 -275
  29. package/docs/database.md +390 -390
  30. package/docs/error-handling.md +438 -438
  31. package/docs/file-uploads.md +333 -333
  32. package/docs/getting-started.md +214 -214
  33. package/docs/middleware.md +355 -355
  34. package/docs/rate-limiting.md +393 -393
  35. package/docs/routing.md +302 -302
  36. package/lib/llm/client.js +73 -0
  37. package/lib/llm/index.js +7 -0
  38. package/lib/llm/parse.js +89 -0
  39. package/package.json +64 -62
  40. package/rate-limit/algorithms/fixed-window.js +141 -141
  41. package/rate-limit/algorithms/sliding-window.js +147 -147
  42. package/rate-limit/algorithms/token-bucket.js +115 -115
  43. package/rate-limit/base.js +165 -165
  44. package/rate-limit/index.js +147 -147
  45. package/rate-limit/storage/base.js +104 -104
  46. package/rate-limit/storage/memory.js +101 -101
  47. package/rate-limit/storage/redis.js +88 -88
  48. package/server/ammo/body-parser.js +220 -220
  49. package/server/ammo/dispatch-helper.js +103 -103
  50. package/server/ammo/enhancer.js +57 -57
  51. package/server/ammo.js +454 -415
  52. package/server/endpoint.js +97 -74
  53. package/server/error.js +9 -9
  54. package/server/errors/code-context.js +125 -125
  55. package/server/errors/llm-error-service.js +140 -140
  56. package/server/files/helper.js +33 -33
  57. package/server/files/uploader.js +143 -143
  58. package/server/handler.js +158 -119
  59. package/server/target.js +185 -175
  60. package/server/targets/middleware-validator.js +22 -22
  61. package/server/targets/path-validator.js +21 -21
  62. package/server/targets/registry.js +160 -160
  63. package/server/targets/shoot-validator.js +21 -21
  64. package/te.js +428 -402
  65. package/utils/auto-register.js +17 -17
  66. package/utils/configuration.js +64 -64
  67. package/utils/errors-llm-config.js +84 -84
  68. package/utils/request-logger.js +43 -43
  69. package/utils/status-codes.js +82 -82
  70. package/utils/tejas-entrypoint-html.js +18 -18
@@ -1,244 +1,244 @@
1
- /**
2
- * Pure OpenAPI 3.0 schema and operation builders for auto-documentation.
3
- * No LLM, file I/O, or registry dependencies.
4
- */
5
-
6
- import { ALL_METHODS } from '../analysis/handler-analyzer.js';
7
- import { METHOD_KEYS } from '../constants.js';
8
-
9
- /**
10
- * Returns true if the object is method-keyed metadata (keys are HTTP methods, values are { summary?, description?, request?, response? }).
11
- * @param {object} obj - Parsed LLM response
12
- * @returns {boolean}
13
- */
14
- export function isMethodKeyed(obj) {
15
- if (!obj || typeof obj !== 'object') return false;
16
- for (const key of Object.keys(obj)) {
17
- if (METHOD_KEYS.has(key.toLowerCase())) {
18
- const val = obj[key];
19
- if (val && typeof val === 'object' && (val.summary != null || val.response != null)) return true;
20
- }
21
- }
22
- return false;
23
- }
24
-
25
- /**
26
- * Convert te.js path pattern to OpenAPI path (e.g. /users/:id -> /users/{id}).
27
- * @param {string} path - Route path possibly containing :param segments
28
- * @returns {string}
29
- */
30
- export function toOpenAPIPath(path) {
31
- if (!path || typeof path !== 'string') return '/';
32
- return path.replace(/:([^/]+)/g, '{$1}');
33
- }
34
-
35
- /**
36
- * Extract path parameter definitions from a te.js path for OpenAPI.
37
- * @param {string} path - Route path (e.g. /users/:id)
38
- * @returns {Array<{ name: string, in: string, required: boolean, schema: object }>}
39
- */
40
- export function getPathParameters(path) {
41
- if (!path || typeof path !== 'string') return [];
42
- const params = [];
43
- const segmentRegex = /:([^/]+)/g;
44
- let match;
45
- while ((match = segmentRegex.exec(path)) !== null) {
46
- params.push({
47
- name: match[1],
48
- in: 'path',
49
- required: true,
50
- description: `Path parameter: ${match[1]}`,
51
- schema: { type: 'string' },
52
- });
53
- }
54
- return params;
55
- }
56
-
57
- /**
58
- * Build OpenAPI query parameter list from request.query metadata.
59
- * @param {object} queryMeta - e.g. { limit: { type: 'integer', required: false }, q: { type: 'string', required: true } }
60
- * @returns {Array<{ name: string, in: string, required: boolean, description?: string, schema: object }>}
61
- */
62
- export function getQueryParameters(queryMeta) {
63
- if (!queryMeta || typeof queryMeta !== 'object') return [];
64
- const params = [];
65
- for (const [name, spec] of Object.entries(queryMeta)) {
66
- if (!spec || typeof spec !== 'object' || !spec.type) continue;
67
- params.push({
68
- name,
69
- in: 'query',
70
- required: spec.required === true || spec.required === 'true',
71
- ...(spec.description && { description: spec.description }),
72
- schema: { type: spec.type, ...(spec.format && { format: spec.format }) },
73
- });
74
- }
75
- return params;
76
- }
77
-
78
- /**
79
- * Convert simple metadata schema (field -> { type, description? }) to OpenAPI schema.
80
- * @param {object} meta - e.g. { name: { type: 'string' }, email: { type: 'string' } }
81
- * @returns {object} OpenAPI schema with properties
82
- */
83
- export function buildSchemaFromMetadata(meta) {
84
- if (!meta || typeof meta !== 'object') return {};
85
- const properties = {};
86
- const required = [];
87
- for (const [key, value] of Object.entries(meta)) {
88
- if (value && typeof value === 'object' && value.type) {
89
- properties[key] = {
90
- type: value.type,
91
- ...(value.description && { description: value.description }),
92
- ...(value.format && { format: value.format }),
93
- };
94
- if (value.required === true || value.required === 'true') required.push(key);
95
- }
96
- }
97
- if (Object.keys(properties).length === 0) return {};
98
- return {
99
- type: 'object',
100
- properties,
101
- ...(required.length > 0 && { required }),
102
- };
103
- }
104
-
105
- /**
106
- * Build request body schema for an operation from metadata or empty.
107
- * @param {object} requestMeta - metadata.request (body schema)
108
- * @returns {object|undefined} OpenAPI requestBody or undefined
109
- */
110
- export function buildRequestBody(requestMeta) {
111
- if (!requestMeta?.body || typeof requestMeta.body !== 'object') return undefined;
112
- const schema = buildSchemaFromMetadata(requestMeta.body);
113
- if (!schema || Object.keys(schema).length === 0) return undefined;
114
- return {
115
- required: true,
116
- content: {
117
- 'application/json': {
118
- schema,
119
- },
120
- },
121
- };
122
- }
123
-
124
- /**
125
- * Build response object for an operation from metadata.
126
- * @param {object} responseMeta - metadata.response (e.g. { 200: { description, schema? }, 201: { ... } })
127
- * @returns {object} OpenAPI responses
128
- */
129
- export function buildResponses(responseMeta) {
130
- const responses = {};
131
- if (!responseMeta || typeof responseMeta !== 'object') {
132
- responses['200'] = { description: 'Success' };
133
- return responses;
134
- }
135
- for (const [code, spec] of Object.entries(responseMeta)) {
136
- if (!spec || typeof spec !== 'object') continue;
137
- responses[String(code)] = {
138
- description: spec.description || `Response ${code}`,
139
- ...(spec.schema && {
140
- content: {
141
- 'application/json': {
142
- schema: typeof spec.schema === 'object' && spec.schema.type
143
- ? spec.schema
144
- : { type: 'object' },
145
- },
146
- },
147
- }),
148
- };
149
- }
150
- if (Object.keys(responses).length === 0) {
151
- responses['200'] = { description: 'Success' };
152
- }
153
- return responses;
154
- }
155
-
156
- /**
157
- * Returns true when the endpoint accepts all standard HTTP methods (method-agnostic).
158
- * @param {string[]} methods
159
- * @returns {boolean}
160
- */
161
- export function isMethodAgnostic(methods) {
162
- if (!Array.isArray(methods) || methods.length !== ALL_METHODS.length) return false;
163
- const set = new Set(methods.map((m) => m.toUpperCase()));
164
- return ALL_METHODS.every((m) => set.has(m));
165
- }
166
-
167
- /**
168
- * Build a single OpenAPI operation for one HTTP method.
169
- * @param {string} method - HTTP method (GET, POST, etc.)
170
- * @param {object} meta - Merged metadata (explicit + LLM-enhanced): summary, description?, request?, response?
171
- * @param {Array} pathParams - Path parameters for this path
172
- * @param {object} [options] - { methodAgnostic?: boolean } - when true, description notes all accepted methods
173
- * @returns {object} OpenAPI operation object
174
- */
175
- export function buildOperation(method, meta, pathParams, options = {}) {
176
- const { methodAgnostic = false } = options;
177
- let description = meta.description || '';
178
- if (methodAgnostic) {
179
- const methodList = ALL_METHODS.join(', ');
180
- description = description
181
- ? `${description}\n\nAccepts any HTTP method: ${methodList}.`
182
- : `Accepts any HTTP method: ${methodList}.`;
183
- }
184
- const queryParams = getQueryParameters(meta.request?.query);
185
- const allParams = [...pathParams, ...queryParams];
186
- const op = {
187
- summary: meta.summary || '',
188
- ...(description && { description }),
189
- parameters: allParams.length > 0 ? allParams : undefined,
190
- };
191
- const methodUpper = method.toUpperCase();
192
- const body = buildRequestBody(meta.request);
193
- if (body && (methodAgnostic || (methodUpper !== 'GET' && methodUpper !== 'HEAD'))) {
194
- op.requestBody = body;
195
- }
196
- op.responses = buildResponses(meta.response);
197
- return op;
198
- }
199
-
200
- /**
201
- * Merge explicit endpoint metadata with LLM-enhanced result.
202
- * @param {object} explicit - From endpoint.getMetadata()
203
- * @param {object} enhanced - From llm.enhanceEndpointDocs() or per-method enhancer
204
- * @param {object} [options] - { preferEnhanced?: boolean } - when true (level 2), LLM response wins over explicit
205
- * @returns {object}
206
- */
207
- export function mergeMetadata(explicit, enhanced, options = {}) {
208
- const preferEnhanced = options.preferEnhanced === true;
209
- const summary = preferEnhanced
210
- ? (enhanced?.summary ?? explicit?.summary ?? '')
211
- : (explicit?.summary ?? enhanced?.summary ?? '');
212
- const description = preferEnhanced
213
- ? (enhanced?.description ?? explicit?.description ?? '')
214
- : (explicit?.description ?? enhanced?.description ?? '');
215
- const request = preferEnhanced
216
- ? (enhanced?.request ?? explicit?.request)
217
- : (explicit?.request ?? enhanced?.request);
218
- const response = preferEnhanced
219
- ? (enhanced?.response ?? explicit?.response)
220
- : (explicit?.response ?? enhanced?.response);
221
- return {
222
- summary: summary || 'Endpoint',
223
- description: description || undefined,
224
- ...(request && { request }),
225
- ...(response && { response }),
226
- };
227
- }
228
-
229
- /**
230
- * Merge method-keyed metadata for a method-agnostic endpoint into a single meta (preferred order).
231
- * @param {Map<string,object>} metaByMethod
232
- * @param {string[]} methods
233
- * @param {object} fallbackMeta
234
- * @returns {object}
235
- */
236
- export function mergeMethodAgnosticMeta(metaByMethod, methods, fallbackMeta) {
237
- const preferredOrder = ['post', 'put', 'patch', 'get', 'delete', 'head', 'options'];
238
- for (const k of preferredOrder) {
239
- const m = metaByMethod.get(k);
240
- if (m && (m.summary || m.description)) return m;
241
- }
242
- const first = metaByMethod.values().next().value;
243
- return first || fallbackMeta;
244
- }
1
+ /**
2
+ * Pure OpenAPI 3.0 schema and operation builders for auto-documentation.
3
+ * No LLM, file I/O, or registry dependencies.
4
+ */
5
+
6
+ import { ALL_METHODS } from '../analysis/handler-analyzer.js';
7
+ import { METHOD_KEYS } from '../constants.js';
8
+
9
+ /**
10
+ * Returns true if the object is method-keyed metadata (keys are HTTP methods, values are { summary?, description?, request?, response? }).
11
+ * @param {object} obj - Parsed LLM response
12
+ * @returns {boolean}
13
+ */
14
+ export function isMethodKeyed(obj) {
15
+ if (!obj || typeof obj !== 'object') return false;
16
+ for (const key of Object.keys(obj)) {
17
+ if (METHOD_KEYS.has(key.toLowerCase())) {
18
+ const val = obj[key];
19
+ if (val && typeof val === 'object' && (val.summary != null || val.response != null)) return true;
20
+ }
21
+ }
22
+ return false;
23
+ }
24
+
25
+ /**
26
+ * Convert te.js path pattern to OpenAPI path (e.g. /users/:id -> /users/{id}).
27
+ * @param {string} path - Route path possibly containing :param segments
28
+ * @returns {string}
29
+ */
30
+ export function toOpenAPIPath(path) {
31
+ if (!path || typeof path !== 'string') return '/';
32
+ return path.replace(/:([^/]+)/g, '{$1}');
33
+ }
34
+
35
+ /**
36
+ * Extract path parameter definitions from a te.js path for OpenAPI.
37
+ * @param {string} path - Route path (e.g. /users/:id)
38
+ * @returns {Array<{ name: string, in: string, required: boolean, schema: object }>}
39
+ */
40
+ export function getPathParameters(path) {
41
+ if (!path || typeof path !== 'string') return [];
42
+ const params = [];
43
+ const segmentRegex = /:([^/]+)/g;
44
+ let match;
45
+ while ((match = segmentRegex.exec(path)) !== null) {
46
+ params.push({
47
+ name: match[1],
48
+ in: 'path',
49
+ required: true,
50
+ description: `Path parameter: ${match[1]}`,
51
+ schema: { type: 'string' },
52
+ });
53
+ }
54
+ return params;
55
+ }
56
+
57
+ /**
58
+ * Build OpenAPI query parameter list from request.query metadata.
59
+ * @param {object} queryMeta - e.g. { limit: { type: 'integer', required: false }, q: { type: 'string', required: true } }
60
+ * @returns {Array<{ name: string, in: string, required: boolean, description?: string, schema: object }>}
61
+ */
62
+ export function getQueryParameters(queryMeta) {
63
+ if (!queryMeta || typeof queryMeta !== 'object') return [];
64
+ const params = [];
65
+ for (const [name, spec] of Object.entries(queryMeta)) {
66
+ if (!spec || typeof spec !== 'object' || !spec.type) continue;
67
+ params.push({
68
+ name,
69
+ in: 'query',
70
+ required: spec.required === true || spec.required === 'true',
71
+ ...(spec.description && { description: spec.description }),
72
+ schema: { type: spec.type, ...(spec.format && { format: spec.format }) },
73
+ });
74
+ }
75
+ return params;
76
+ }
77
+
78
+ /**
79
+ * Convert simple metadata schema (field -> { type, description? }) to OpenAPI schema.
80
+ * @param {object} meta - e.g. { name: { type: 'string' }, email: { type: 'string' } }
81
+ * @returns {object} OpenAPI schema with properties
82
+ */
83
+ export function buildSchemaFromMetadata(meta) {
84
+ if (!meta || typeof meta !== 'object') return {};
85
+ const properties = {};
86
+ const required = [];
87
+ for (const [key, value] of Object.entries(meta)) {
88
+ if (value && typeof value === 'object' && value.type) {
89
+ properties[key] = {
90
+ type: value.type,
91
+ ...(value.description && { description: value.description }),
92
+ ...(value.format && { format: value.format }),
93
+ };
94
+ if (value.required === true || value.required === 'true') required.push(key);
95
+ }
96
+ }
97
+ if (Object.keys(properties).length === 0) return {};
98
+ return {
99
+ type: 'object',
100
+ properties,
101
+ ...(required.length > 0 && { required }),
102
+ };
103
+ }
104
+
105
+ /**
106
+ * Build request body schema for an operation from metadata or empty.
107
+ * @param {object} requestMeta - metadata.request (body schema)
108
+ * @returns {object|undefined} OpenAPI requestBody or undefined
109
+ */
110
+ export function buildRequestBody(requestMeta) {
111
+ if (!requestMeta?.body || typeof requestMeta.body !== 'object') return undefined;
112
+ const schema = buildSchemaFromMetadata(requestMeta.body);
113
+ if (!schema || Object.keys(schema).length === 0) return undefined;
114
+ return {
115
+ required: true,
116
+ content: {
117
+ 'application/json': {
118
+ schema,
119
+ },
120
+ },
121
+ };
122
+ }
123
+
124
+ /**
125
+ * Build response object for an operation from metadata.
126
+ * @param {object} responseMeta - metadata.response (e.g. { 200: { description, schema? }, 201: { ... } })
127
+ * @returns {object} OpenAPI responses
128
+ */
129
+ export function buildResponses(responseMeta) {
130
+ const responses = {};
131
+ if (!responseMeta || typeof responseMeta !== 'object') {
132
+ responses['200'] = { description: 'Success' };
133
+ return responses;
134
+ }
135
+ for (const [code, spec] of Object.entries(responseMeta)) {
136
+ if (!spec || typeof spec !== 'object') continue;
137
+ responses[String(code)] = {
138
+ description: spec.description || `Response ${code}`,
139
+ ...(spec.schema && {
140
+ content: {
141
+ 'application/json': {
142
+ schema: typeof spec.schema === 'object' && spec.schema.type
143
+ ? spec.schema
144
+ : { type: 'object' },
145
+ },
146
+ },
147
+ }),
148
+ };
149
+ }
150
+ if (Object.keys(responses).length === 0) {
151
+ responses['200'] = { description: 'Success' };
152
+ }
153
+ return responses;
154
+ }
155
+
156
+ /**
157
+ * Returns true when the endpoint accepts all standard HTTP methods (method-agnostic).
158
+ * @param {string[]} methods
159
+ * @returns {boolean}
160
+ */
161
+ export function isMethodAgnostic(methods) {
162
+ if (!Array.isArray(methods) || methods.length !== ALL_METHODS.length) return false;
163
+ const set = new Set(methods.map((m) => m.toUpperCase()));
164
+ return ALL_METHODS.every((m) => set.has(m));
165
+ }
166
+
167
+ /**
168
+ * Build a single OpenAPI operation for one HTTP method.
169
+ * @param {string} method - HTTP method (GET, POST, etc.)
170
+ * @param {object} meta - Merged metadata (explicit + LLM-enhanced): summary, description?, request?, response?
171
+ * @param {Array} pathParams - Path parameters for this path
172
+ * @param {object} [options] - { methodAgnostic?: boolean } - when true, description notes all accepted methods
173
+ * @returns {object} OpenAPI operation object
174
+ */
175
+ export function buildOperation(method, meta, pathParams, options = {}) {
176
+ const { methodAgnostic = false } = options;
177
+ let description = meta.description || '';
178
+ if (methodAgnostic) {
179
+ const methodList = ALL_METHODS.join(', ');
180
+ description = description
181
+ ? `${description}\n\nAccepts any HTTP method: ${methodList}.`
182
+ : `Accepts any HTTP method: ${methodList}.`;
183
+ }
184
+ const queryParams = getQueryParameters(meta.request?.query);
185
+ const allParams = [...pathParams, ...queryParams];
186
+ const op = {
187
+ summary: meta.summary || '',
188
+ ...(description && { description }),
189
+ parameters: allParams.length > 0 ? allParams : undefined,
190
+ };
191
+ const methodUpper = method.toUpperCase();
192
+ const body = buildRequestBody(meta.request);
193
+ if (body && (methodAgnostic || (methodUpper !== 'GET' && methodUpper !== 'HEAD'))) {
194
+ op.requestBody = body;
195
+ }
196
+ op.responses = buildResponses(meta.response);
197
+ return op;
198
+ }
199
+
200
+ /**
201
+ * Merge explicit endpoint metadata with LLM-enhanced result.
202
+ * @param {object} explicit - From endpoint.getMetadata()
203
+ * @param {object} enhanced - From llm.enhanceEndpointDocs() or per-method enhancer
204
+ * @param {object} [options] - { preferEnhanced?: boolean } - when true (level 2), LLM response wins over explicit
205
+ * @returns {object}
206
+ */
207
+ export function mergeMetadata(explicit, enhanced, options = {}) {
208
+ const preferEnhanced = options.preferEnhanced === true;
209
+ const summary = preferEnhanced
210
+ ? (enhanced?.summary ?? explicit?.summary ?? '')
211
+ : (explicit?.summary ?? enhanced?.summary ?? '');
212
+ const description = preferEnhanced
213
+ ? (enhanced?.description ?? explicit?.description ?? '')
214
+ : (explicit?.description ?? enhanced?.description ?? '');
215
+ const request = preferEnhanced
216
+ ? (enhanced?.request ?? explicit?.request)
217
+ : (explicit?.request ?? enhanced?.request);
218
+ const response = preferEnhanced
219
+ ? (enhanced?.response ?? explicit?.response)
220
+ : (explicit?.response ?? enhanced?.response);
221
+ return {
222
+ summary: summary || 'Endpoint',
223
+ description: description || undefined,
224
+ ...(request && { request }),
225
+ ...(response && { response }),
226
+ };
227
+ }
228
+
229
+ /**
230
+ * Merge method-keyed metadata for a method-agnostic endpoint into a single meta (preferred order).
231
+ * @param {Map<string,object>} metaByMethod
232
+ * @param {string[]} methods
233
+ * @param {object} fallbackMeta
234
+ * @returns {object}
235
+ */
236
+ export function mergeMethodAgnosticMeta(metaByMethod, methods, fallbackMeta) {
237
+ const preferredOrder = ['post', 'put', 'patch', 'get', 'delete', 'head', 'options'];
238
+ for (const k of preferredOrder) {
239
+ const m = metaByMethod.get(k);
240
+ if (m && (m.summary || m.description)) return m;
241
+ }
242
+ const first = metaByMethod.values().next().value;
243
+ return first || fallbackMeta;
244
+ }