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
package/server/ammo.js CHANGED
@@ -1,356 +1,454 @@
1
- import { statusAndData } from './ammo/dispatch-helper.js';
2
- import {
3
- isStatusCode,
4
- toStatusCode,
5
- toStatusMessage,
6
- } from '../utils/status-codes.js';
7
- import html from '../utils/tejas-entrypoint-html.js';
8
- import ammoEnhancer from './ammo/enhancer.js';
9
- import TejError from './error.js';
10
-
11
- /**
12
- * Ammo class for handling HTTP requests and responses.
13
- *
14
- * @description
15
- * Ammo is a utility class that simplifies HTTP request handling and response generation.
16
- * It provides methods for processing requests, sending responses, and handling errors.
17
- *
18
- * @example
19
- *
20
- * if (ammo.GET) {
21
- * ammo.fire(200, { message: 'Hello World' });
22
- * } else {
23
- * ammo.notAllowed();
24
- * }
25
- */
26
- class Ammo {
27
- /**
28
- * Creates a new Ammo instance.
29
- *
30
- * @param {http.IncomingMessage} req - The HTTP request object
31
- * @param {http.ServerResponse} res - The HTTP response object
32
- *
33
- * @description
34
- * Initializes a new Ammo instance with the provided request and response objects.
35
- * Sets up default values for various properties that will be populated by the enhance method.
36
- */
37
- constructor(req, res) {
38
- this.req = req;
39
- this.res = res;
40
-
41
- this.GET = false;
42
- this.POST = false;
43
- this.PUT = false;
44
- this.DELETE = false;
45
- this.PATCH = false;
46
- this.HEAD = false;
47
- this.OPTIONS = false;
48
-
49
- // Request related data
50
- this.ip = undefined;
51
- this.headers = undefined;
52
- this.payload = undefined;
53
- this.method = undefined;
54
-
55
- // URL related data
56
- this.protocol = undefined;
57
- this.hostname = undefined;
58
- this.path = undefined;
59
- this.endpoint = undefined;
60
-
61
- this.fullURL = undefined;
62
-
63
- // Response related data
64
- this.dispatchedData = undefined;
65
- }
66
-
67
- /**
68
- * Enhances the Ammo instance with request data and sets HTTP method flags.
69
- *
70
- * @description
71
- * This method processes the request and sets various properties on the Ammo instance:
72
- * - HTTP method flags (GET, POST, PUT, etc.)
73
- * - Request data (IP, headers, payload, method)
74
- * - URL data (protocol, hostname, path, endpoint, fullURL)
75
- *
76
- * This method should be called before using any other Ammo methods.
77
- *
78
- * @returns {Promise<void>} A promise that resolves when enhancement is complete
79
- *
80
- * @example
81
- * const ammo = new Ammo(req, res);
82
- * await ammo.enhance();
83
- * // Now you can use ammo.GET, ammo.path, etc.
84
- */
85
- async enhance() {
86
- await ammoEnhancer(this);
87
-
88
- this.GET = this.method === 'GET';
89
- this.POST = this.method === 'POST';
90
- this.PUT = this.method === 'PUT';
91
- this.DELETE = this.method === 'DELETE';
92
- this.PATCH = this.method === 'PATCH';
93
- this.HEAD = this.method === 'HEAD';
94
- this.OPTIONS = this.method === 'OPTIONS';
95
- }
96
-
97
- /**
98
- * Sends a response to the client with the specified data and status code.
99
- *
100
- * @param {number|any} [arg1] - If a number, treated as status code. Otherwise treated as data to send.
101
- * @param {any} [arg2] - If arg1 is a number, this is the data to send. Otherwise ignored.
102
- * @param {string} [arg3] - Optional content type override.
103
- *
104
- * @description
105
- * The fire method is flexible and can handle different argument patterns:
106
- *
107
- * 1. No arguments: Sends a 204 No Content response
108
- * 2. Single number: Sends a response with the given status code
109
- * 3. Single non-number: Sends a 200 OK response with the given data
110
- * 4. Two arguments (number, data): Sends a response with the given status code and data
111
- * 5. Three arguments: Sends a response with the given status code, data, and content type
112
- *
113
- * The fire method can be used with any HTTP status code, including error codes (4xx, 5xx).
114
- * For error responses, you can use either fire() or throw(). The main difference is that
115
- * throw() can accept an Error instance and has special handling for it, while fire() only
116
- * accepts status codes, strings, or other data types.
117
- *
118
- * @example
119
- * // Send a 200 OK response with JSON data
120
- * ammo.fire(200, { message: 'Success' });
121
- *
122
- * @example
123
- * // Send a 404 Not Found response with custom message
124
- * ammo.fire(404, 'Resource not found');
125
- *
126
- * @example
127
- * // Send a 500 Internal Server Error response
128
- * ammo.fire(500, 'Something went wrong');
129
- *
130
- * @example
131
- * // Send HTML content with custom content type
132
- * ammo.fire(200, '<html><body>Hello</body></html>', 'text/html');
133
- *
134
- * @example
135
- * // Send just a status code (will use default status message)
136
- * ammo.fire(204);
137
- *
138
- * @example
139
- * // Send just data (will use 200 status code)
140
- * ammo.fire({ message: 'Success' });
141
- * ammo.fire('Hello World');
142
- */
143
- fire() {
144
- const { statusCode, data, contentType } = statusAndData(arguments);
145
- const contentTypeHeader = { 'Content-Type': contentType };
146
-
147
- this.dispatchedData = data;
148
-
149
- this.res.writeHead(statusCode, contentTypeHeader);
150
- this.res.write(data ?? '');
151
- this.res.end();
152
- }
153
-
154
- /**
155
- * Redirects to the specified URL.
156
- *
157
- * @param {string} url - The URL to redirect to
158
- * @param {number} [statusCode=302] - HTTP status code for redirect (default: 302)
159
- *
160
- * @description
161
- * Sends an HTTP redirect response to the specified URL.
162
- * Uses 302 (Found/Temporary Redirect) by default.
163
- * Common status codes:
164
- * - 301: Moved Permanently
165
- * - 302: Found (Temporary Redirect)
166
- * - 303: See Other
167
- * - 307: Temporary Redirect (maintains HTTP method)
168
- * - 308: Permanent Redirect (maintains HTTP method)
169
- *
170
- * @example
171
- * // Temporary redirect (302)
172
- * ammo.redirect('/new-location');
173
- *
174
- * @example
175
- * // Permanent redirect (301)
176
- * ammo.redirect('/new-location', 301);
177
- */
178
- redirect(url, statusCode = 302) {
179
- this.res.writeHead(statusCode, { Location: url });
180
- this.res.end();
181
- }
182
-
183
- /**
184
- * Throws a 404 Not Found error.
185
- *
186
- * @description
187
- * This is a convenience method that throws a 404 Not Found error.
188
- * It's equivalent to calling `throw(404) ` or `fire(404)`.
189
- *
190
- * @throws {TejError} Always throws a TejError with status code 404
191
- *
192
- * @example
193
- * // If resource not found
194
- * if (!resource) {
195
- * ammo.notFound();
196
- * }
197
- */
198
- notFound() {
199
- throw new TejError(404, 'Not Found');
200
- }
201
-
202
- /**
203
- * Throws a 405 Method Not Allowed error.
204
- *
205
- * @description
206
- * This is a convenience method that throws a 405 Method Not Allowed error.
207
- * It's equivalent to calling `throw(405)` or `fire(405)`.
208
- *
209
- * @throws {TejError} Always throws a TejError with status code 405
210
- *
211
- * @example
212
- * // If method not allowed
213
- * if (!allowedMethods.includes(ammo.method)) {
214
- * ammo.notAllowed();
215
- * }
216
- */
217
- notAllowed() {
218
- throw new TejError(405, 'Method Not Allowed');
219
- }
220
-
221
- /**
222
- * Throws a 401 Unauthorized error.
223
- *
224
- * @description
225
- * This is a convenience method that throws a 401 Unauthorized error.
226
- * It's equivalent to calling `throw(401) ` or `fire(401)`.
227
- *
228
- * @throws {TejError} Always throws a TejError with status code 401
229
- *
230
- * @example
231
- * // If user is not authenticated
232
- * if (!user) {
233
- * ammo.unauthorized();
234
- * }
235
- */
236
- unauthorized() {
237
- throw new TejError(401, 'Unauthorized');
238
- }
239
-
240
- /**
241
- * Sends the default entry point HTML.
242
- *
243
- * @description
244
- * This method sends the default HTML entry point for the application.
245
- * It's typically used as a fallback when no specific route is matched.
246
- *
247
- * @example
248
- * // In a catch-all route
249
- * ammo.defaultEntry();
250
- */
251
- defaultEntry() {
252
- this.fire(html);
253
- }
254
-
255
- /**
256
- * Throws an error response with appropriate status code and message.
257
- *
258
- * @param {number|Error|string} [arg1] - Status code, Error object, or error message
259
- * @param {string} [arg2] - Error message (only used when arg1 is a status code)
260
- *
261
- * @description
262
- * The throw method is flexible and can handle different argument patterns:
263
- *
264
- * 1. No arguments: Sends a 500 Internal Server Error response
265
- * 2. Status code: Sends a response with the given status code and default message
266
- * 3. Status code and message: Sends a response with the given status code and message
267
- * 4. Error object: Extracts status code and message from the error
268
- * 5. String: Treats as error message with 500 status code
269
- *
270
- * The key difference between throw() and fire() is that throw() can accept an Error instance
271
- * and has special handling for it. Internally, throw() still calls fire() to send the response.
272
- *
273
- * @example
274
- * // Throw a 404 Not Found error
275
- * ammo.throw(404);
276
- *
277
- * @example
278
- * // Throw a 404 Not Found error with custom message
279
- * ammo.throw(404, 'Resource not found');
280
- *
281
- * @example
282
- * // Throw an error from an Error object
283
- * ammo.throw(new Error('Something went wrong'));
284
- *
285
- * @example
286
- * // Throw an error with a custom message
287
- * ammo.throw('Something went wrong');
288
- */
289
- throw() {
290
- // Handle different argument patterns
291
- const args = Array.from(arguments);
292
-
293
- // Case 1: No arguments provided
294
- if (args.length === 0) {
295
- this.fire(500, 'Internal Server Error');
296
- return;
297
- }
298
-
299
- // Case 2: First argument is a status code
300
- if (isStatusCode(args[0])) {
301
- const statusCode = args[0];
302
- const message = args[1] || toStatusMessage(statusCode);
303
- this.fire(statusCode, message);
304
- return;
305
- }
306
-
307
- // Case 3.1: First argument is an instance of TejError
308
- if (args[0] instanceof TejError) {
309
- const error = args[0];
310
- const statusCode = error.code;
311
- const message = error.message;
312
-
313
- this.fire(statusCode, message);
314
- return;
315
- }
316
-
317
- // Case 3.2: First argument is an Error object
318
- if (args[0] instanceof Error) {
319
- const error = args[0];
320
-
321
- // Check if error message is a numeric status code
322
- if (!isNaN(parseInt(error.message))) {
323
- const statusCode = parseInt(error.message);
324
- const message = toStatusMessage(statusCode) || toStatusMessage(500);
325
- this.fire(statusCode, message);
326
- return;
327
- }
328
-
329
- // Use error message as status code if it's a valid status code string
330
- const statusCode = toStatusCode(error.message);
331
- if (statusCode) {
332
- this.fire(statusCode, error.message);
333
- return;
334
- }
335
-
336
- // Default error handling
337
- this.fire(500, error.message);
338
- return;
339
- }
340
-
341
- // Case 4: First argument is a string or other value
342
- const errorValue = args[0];
343
-
344
- // Check if the string represents a status code
345
- const statusCode = toStatusCode(errorValue);
346
- if (statusCode) {
347
- this.fire(statusCode, toStatusMessage(statusCode));
348
- return;
349
- }
350
-
351
- // Default case: treat as error message
352
- this.fire(500, errorValue.toString());
353
- }
354
- }
355
-
356
- export default Ammo;
1
+ import { statusAndData } from './ammo/dispatch-helper.js';
2
+ import {
3
+ isStatusCode,
4
+ toStatusCode,
5
+ toStatusMessage,
6
+ } from '../utils/status-codes.js';
7
+ import html from '../utils/tejas-entrypoint-html.js';
8
+ import ammoEnhancer from './ammo/enhancer.js';
9
+ import TejError from './error.js';
10
+ import { getErrorsLlmConfig } from '../utils/errors-llm-config.js';
11
+ import { inferErrorFromContext } from './errors/llm-error-service.js';
12
+ import { captureCodeContext } from './errors/code-context.js';
13
+
14
+ /**
15
+ * Detect if the value is a throw() options object (per-call overrides).
16
+ * @param {unknown} v
17
+ * @returns {v is { useLlm?: boolean, messageType?: 'endUser'|'developer' }}
18
+ */
19
+ function isThrowOptions(v) {
20
+ if (!v || typeof v !== 'object' || Array.isArray(v)) return false;
21
+ const o = /** @type {Record<string, unknown>} */ (v);
22
+ const hasUseLlm = 'useLlm' in o;
23
+ const hasMessageType =
24
+ 'messageType' in o &&
25
+ (o.messageType === 'endUser' || o.messageType === 'developer');
26
+ return hasUseLlm || hasMessageType === true;
27
+ }
28
+
29
+ /**
30
+ * Ammo class for handling HTTP requests and responses.
31
+ *
32
+ * @description
33
+ * Ammo is a utility class that simplifies HTTP request handling and response generation.
34
+ * It provides methods for processing requests, sending responses, and handling errors.
35
+ *
36
+ * @example
37
+ *
38
+ * if (ammo.GET) {
39
+ * ammo.fire(200, { message: 'Hello World' });
40
+ * } else {
41
+ * ammo.notAllowed();
42
+ * }
43
+ */
44
+ class Ammo {
45
+ /**
46
+ * Creates a new Ammo instance.
47
+ *
48
+ * @param {http.IncomingMessage} req - The HTTP request object
49
+ * @param {http.ServerResponse} res - The HTTP response object
50
+ *
51
+ * @description
52
+ * Initializes a new Ammo instance with the provided request and response objects.
53
+ * Sets up default values for various properties that will be populated by the enhance method.
54
+ */
55
+ constructor(req, res) {
56
+ this.req = req;
57
+ this.res = res;
58
+
59
+ this.GET = false;
60
+ this.POST = false;
61
+ this.PUT = false;
62
+ this.DELETE = false;
63
+ this.PATCH = false;
64
+ this.HEAD = false;
65
+ this.OPTIONS = false;
66
+
67
+ // Request related data
68
+ this.ip = undefined;
69
+ this.headers = undefined;
70
+ this.payload = undefined;
71
+ this.method = undefined;
72
+
73
+ // URL related data
74
+ this.protocol = undefined;
75
+ this.hostname = undefined;
76
+ this.path = undefined;
77
+ this.endpoint = undefined;
78
+
79
+ this.fullURL = undefined;
80
+
81
+ // Response related data
82
+ this.dispatchedData = undefined;
83
+ }
84
+
85
+ /**
86
+ * Enhances the Ammo instance with request data and sets HTTP method flags.
87
+ *
88
+ * @description
89
+ * This method processes the request and sets various properties on the Ammo instance:
90
+ * - HTTP method flags (GET, POST, PUT, etc.)
91
+ * - Request data (IP, headers, payload, method)
92
+ * - URL data (protocol, hostname, path, endpoint, fullURL)
93
+ *
94
+ * This method should be called before using any other Ammo methods.
95
+ *
96
+ * @returns {Promise<void>} A promise that resolves when enhancement is complete
97
+ *
98
+ * @example
99
+ * const ammo = new Ammo(req, res);
100
+ * await ammo.enhance();
101
+ * // Now you can use ammo.GET, ammo.path, etc.
102
+ */
103
+ async enhance() {
104
+ await ammoEnhancer(this);
105
+
106
+ this.GET = this.method === 'GET' || this.method === 'HEAD';
107
+ this.POST = this.method === 'POST';
108
+ this.PUT = this.method === 'PUT';
109
+ this.DELETE = this.method === 'DELETE';
110
+ this.PATCH = this.method === 'PATCH';
111
+ this.HEAD = this.method === 'HEAD';
112
+ this.OPTIONS = this.method === 'OPTIONS';
113
+ }
114
+
115
+ /**
116
+ * Sends a response to the client with the specified data and status code.
117
+ *
118
+ * @param {number|any} [arg1] - If a number, treated as status code. Otherwise treated as data to send.
119
+ * @param {any} [arg2] - If arg1 is a number, this is the data to send. Otherwise ignored.
120
+ * @param {string} [arg3] - Optional content type override.
121
+ *
122
+ * @description
123
+ * The fire method is flexible and can handle different argument patterns:
124
+ *
125
+ * 1. No arguments: Sends a 204 No Content response
126
+ * 2. Single number: Sends a response with the given status code
127
+ * 3. Single non-number: Sends a 200 OK response with the given data
128
+ * 4. Two arguments (number, data): Sends a response with the given status code and data
129
+ * 5. Three arguments: Sends a response with the given status code, data, and content type
130
+ *
131
+ * The fire method can be used with any HTTP status code, including error codes (4xx, 5xx).
132
+ * For error responses, you can use either fire() or throw(). The main difference is that
133
+ * throw() can accept an Error instance and has special handling for it, while fire() only
134
+ * accepts status codes, strings, or other data types.
135
+ *
136
+ * @example
137
+ * // Send a 200 OK response with JSON data
138
+ * ammo.fire(200, { message: 'Success' });
139
+ *
140
+ * @example
141
+ * // Send a 404 Not Found response with custom message
142
+ * ammo.fire(404, 'Resource not found');
143
+ *
144
+ * @example
145
+ * // Send a 500 Internal Server Error response
146
+ * ammo.fire(500, 'Something went wrong');
147
+ *
148
+ * @example
149
+ * // Send HTML content with custom content type
150
+ * ammo.fire(200, '<html><body>Hello</body></html>', 'text/html');
151
+ *
152
+ * @example
153
+ * // Send just a status code (will use default status message)
154
+ * ammo.fire(204);
155
+ *
156
+ * @example
157
+ * // Send just data (will use 200 status code)
158
+ * ammo.fire({ message: 'Success' });
159
+ * ammo.fire('Hello World');
160
+ */
161
+ fire() {
162
+ const { statusCode, data, contentType } = statusAndData(arguments);
163
+ const contentTypeHeader = { 'Content-Type': contentType };
164
+
165
+ this.dispatchedData = data;
166
+
167
+ this.res.writeHead(statusCode, contentTypeHeader);
168
+ this.res.write(data ?? '');
169
+ this.res.end();
170
+ }
171
+
172
+ /**
173
+ * Redirects to the specified URL.
174
+ *
175
+ * @param {string} url - The URL to redirect to
176
+ * @param {number} [statusCode=302] - HTTP status code for redirect (default: 302)
177
+ *
178
+ * @description
179
+ * Sends an HTTP redirect response to the specified URL.
180
+ * Uses 302 (Found/Temporary Redirect) by default.
181
+ * Common status codes:
182
+ * - 301: Moved Permanently
183
+ * - 302: Found (Temporary Redirect)
184
+ * - 303: See Other
185
+ * - 307: Temporary Redirect (maintains HTTP method)
186
+ * - 308: Permanent Redirect (maintains HTTP method)
187
+ *
188
+ * @example
189
+ * // Temporary redirect (302)
190
+ * ammo.redirect('/new-location');
191
+ *
192
+ * @example
193
+ * // Permanent redirect (301)
194
+ * ammo.redirect('/new-location', 301);
195
+ */
196
+ redirect(url, statusCode = 302) {
197
+ this.res.writeHead(statusCode, { Location: url });
198
+ this.res.end();
199
+ }
200
+
201
+ /**
202
+ * Throws a 404 Not Found error.
203
+ *
204
+ * @description
205
+ * This is a convenience method that throws a 404 Not Found error.
206
+ * It's equivalent to calling `throw(404) ` or `fire(404)`.
207
+ *
208
+ * @throws {TejError} Always throws a TejError with status code 404
209
+ *
210
+ * @example
211
+ * // If resource not found
212
+ * if (!resource) {
213
+ * ammo.notFound();
214
+ * }
215
+ */
216
+ notFound() {
217
+ throw new TejError(404, 'Not Found');
218
+ }
219
+
220
+ /**
221
+ * Throws a 405 Method Not Allowed error.
222
+ *
223
+ * @description
224
+ * This is a convenience method that throws a 405 Method Not Allowed error.
225
+ * It's equivalent to calling `throw(405)` or `fire(405)`.
226
+ *
227
+ * @throws {TejError} Always throws a TejError with status code 405
228
+ *
229
+ * @example
230
+ * // If method not allowed
231
+ * if (!allowedMethods.includes(ammo.method)) {
232
+ * ammo.notAllowed();
233
+ * }
234
+ */
235
+ /**
236
+ * Restricts the handler to the given HTTP method(s). If the request method is not in the list,
237
+ * sets the Allow header and throws 405 Method Not Allowed.
238
+ *
239
+ * @param {...string} methods - Allowed methods (e.g. 'GET', 'POST'). Case-insensitive.
240
+ * @throws {TejError} 405 when the request method is not allowed
241
+ *
242
+ * @example
243
+ * target.register('/health', (ammo) => {
244
+ * ammo.only('GET');
245
+ * ammo.fire({ status: 'ok' });
246
+ * });
247
+ */
248
+ only(...methods) {
249
+ const allowed = methods.map((m) => String(m).toUpperCase());
250
+ const method = this.method ? String(this.method).toUpperCase() : '';
251
+ if (!method || !allowed.includes(method)) {
252
+ this.res.setHeader('Allow', allowed.join(', '));
253
+ throw new TejError(405, 'Method Not Allowed');
254
+ }
255
+ }
256
+
257
+ /**
258
+ * Throws a 405 Method Not Allowed error. Optionally sets the Allow header when allowed methods are provided.
259
+ *
260
+ * @param {...string} [allowedMethods] - Allowed methods for the Allow header (e.g. 'GET', 'POST'). Omit for no header.
261
+ * @throws {TejError} Always throws a TejError with status code 405
262
+ *
263
+ * @example
264
+ * if (!allowedMethods.includes(ammo.method)) {
265
+ * ammo.notAllowed('GET', 'POST');
266
+ * }
267
+ */
268
+ notAllowed(...allowedMethods) {
269
+ if (allowedMethods.length > 0) {
270
+ this.res.setHeader(
271
+ 'Allow',
272
+ allowedMethods.map((m) => String(m).toUpperCase()).join(', '),
273
+ );
274
+ }
275
+ throw new TejError(405, 'Method Not Allowed');
276
+ }
277
+
278
+ /**
279
+ * Throws a 401 Unauthorized error.
280
+ *
281
+ * @description
282
+ * This is a convenience method that throws a 401 Unauthorized error.
283
+ * It's equivalent to calling `throw(401) ` or `fire(401)`.
284
+ *
285
+ * @throws {TejError} Always throws a TejError with status code 401
286
+ *
287
+ * @example
288
+ * // If user is not authenticated
289
+ * if (!user) {
290
+ * ammo.unauthorized();
291
+ * }
292
+ */
293
+ unauthorized() {
294
+ throw new TejError(401, 'Unauthorized');
295
+ }
296
+
297
+ /**
298
+ * Sends the default entry point HTML.
299
+ *
300
+ * @description
301
+ * This method sends the default HTML entry point for the application.
302
+ * It's typically used as a fallback when no specific route is matched.
303
+ *
304
+ * @example
305
+ * // In a catch-all route
306
+ * ammo.defaultEntry();
307
+ */
308
+ defaultEntry() {
309
+ this.fire(html);
310
+ }
311
+
312
+ /**
313
+ * Throws an error response with appropriate status code and message.
314
+ *
315
+ * @param {number|Error|string|object} [arg1] - Status code, Error object, error message, or (when no code) options
316
+ * @param {string|object} [arg2] - Error message (when arg1 is status code) or options (when arg1 is error/empty)
317
+ *
318
+ * @description
319
+ * The throw method is flexible and can handle different argument patterns:
320
+ *
321
+ * 1. No arguments: Sends a 500 Internal Server Error response
322
+ * 2. Status code: Sends a response with the given status code and default message
323
+ * 3. Status code and message: Sends a response with the given status code and message
324
+ * 4. Error object: Extracts status code and message from the error
325
+ * 5. String: Treats as error message with 500 status code
326
+ *
327
+ * When errors.llm.enabled is true and no explicit code/message is given (no args,
328
+ * Error, or string/other), an LLM infers statusCode and message from context.
329
+ * In that case throw() returns a Promise; otherwise it returns undefined.
330
+ *
331
+ * Per-call options (last argument, only when no explicit status code): pass an object
332
+ * with `useLlm` (boolean) and/or `messageType` ('endUser' | 'developer'). Use
333
+ * `useLlm: false` to skip the LLM for this call; use `messageType` to override
334
+ * errors.llm.messageType for this call (end-user-friendly vs developer-friendly message).
335
+ *
336
+ * @example
337
+ * // Throw a 404 Not Found error
338
+ * ammo.throw(404);
339
+ *
340
+ * @example
341
+ * // Throw a 404 Not Found error with custom message
342
+ * ammo.throw(404, 'Resource not found');
343
+ *
344
+ * @example
345
+ * // Throw an error from an Error object
346
+ * ammo.throw(new Error('Something went wrong'));
347
+ *
348
+ * @example
349
+ * // Throw an error with a custom message
350
+ * ammo.throw('Something went wrong');
351
+ *
352
+ * @example
353
+ * // Skip LLM for this call; use default 500
354
+ * ammo.throw(err, { useLlm: false });
355
+ *
356
+ * @example
357
+ * // Force developer-friendly message for this call
358
+ * ammo.throw(err, { messageType: 'developer' });
359
+ *
360
+ * @returns {Promise<void>|void} Promise when LLM path is used; otherwise void
361
+ */
362
+ throw() {
363
+ let args = Array.from(arguments);
364
+ const { enabled: llmEnabled } = getErrorsLlmConfig();
365
+
366
+ // Per-call options: last arg can be { useLlm?, messageType? } when call is LLM-eligible (no explicit code).
367
+ const llmEligible =
368
+ args.length === 0 ||
369
+ (!isStatusCode(args[0]) && !(args[0] instanceof TejError));
370
+ let throwOpts = /** @type {{ useLlm?: boolean, messageType?: 'endUser'|'developer' } | null} */ (null);
371
+ if (llmEligible && args.length > 0 && isThrowOptions(args[args.length - 1])) {
372
+ throwOpts = /** @type {{ useLlm?: boolean, messageType?: 'endUser'|'developer' } } */ (args.pop());
373
+ }
374
+
375
+ const useLlm =
376
+ llmEnabled &&
377
+ llmEligible &&
378
+ throwOpts?.useLlm !== false;
379
+
380
+ if (useLlm) {
381
+ // Use stack from thrown error when available (e.g. handler caught and called ammo.throw(err)) so we capture user code; else current call site.
382
+ const stack =
383
+ args[0] instanceof Error && args[0].stack
384
+ ? args[0].stack
385
+ : new Error().stack;
386
+ return captureCodeContext(stack)
387
+ .then((codeContext) => {
388
+ const context = {
389
+ codeContext,
390
+ method: this.method,
391
+ path: this.path,
392
+ includeDevInsight: true,
393
+ ...(throwOpts?.messageType && { messageType: throwOpts.messageType }),
394
+ };
395
+ if (args[0] !== undefined && args[0] !== null) context.error = args[0];
396
+ return inferErrorFromContext(context);
397
+ })
398
+ .then(({ statusCode, message, devInsight }) => {
399
+ const isProduction = process.env.NODE_ENV === 'production';
400
+ const data =
401
+ !isProduction && devInsight
402
+ ? { message, _dev: devInsight }
403
+ : message;
404
+ this.fire(statusCode, data);
405
+ });
406
+ }
407
+
408
+ // Sync path: explicit code/message or useLlm: false
409
+ if (args.length === 0) {
410
+ this.fire(500, 'Internal Server Error');
411
+ return;
412
+ }
413
+
414
+ if (isStatusCode(args[0])) {
415
+ const statusCode = args[0];
416
+ const message = args[1] || toStatusMessage(statusCode);
417
+ this.fire(statusCode, message);
418
+ return;
419
+ }
420
+
421
+ if (args[0] instanceof TejError) {
422
+ const error = args[0];
423
+ this.fire(error.code, error.message);
424
+ return;
425
+ }
426
+
427
+ if (args[0] instanceof Error) {
428
+ const error = args[0];
429
+ if (!isNaN(parseInt(error.message))) {
430
+ const statusCode = parseInt(error.message);
431
+ const message = toStatusMessage(statusCode) || toStatusMessage(500);
432
+ this.fire(statusCode, message);
433
+ return;
434
+ }
435
+ const statusCode = toStatusCode(error.message);
436
+ if (statusCode) {
437
+ this.fire(statusCode, error.message);
438
+ return;
439
+ }
440
+ this.fire(500, error.message);
441
+ return;
442
+ }
443
+
444
+ const errorValue = args[0];
445
+ const statusCode = toStatusCode(errorValue);
446
+ if (statusCode) {
447
+ this.fire(statusCode, toStatusMessage(statusCode));
448
+ return;
449
+ }
450
+ this.fire(500, errorValue.toString());
451
+ }
452
+ }
453
+
454
+ export default Ammo;