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.
- package/README.md +197 -187
- package/auto-docs/analysis/handler-analyzer.js +58 -58
- package/auto-docs/analysis/source-resolver.js +101 -101
- package/auto-docs/constants.js +37 -37
- package/auto-docs/docs-llm/index.js +7 -0
- package/auto-docs/{llm → docs-llm}/prompts.js +222 -222
- package/auto-docs/{llm → docs-llm}/provider.js +132 -187
- package/auto-docs/index.js +146 -146
- package/auto-docs/openapi/endpoint-processor.js +277 -277
- package/auto-docs/openapi/generator.js +107 -107
- package/auto-docs/openapi/level3.js +131 -131
- package/auto-docs/openapi/spec-builders.js +244 -244
- package/auto-docs/ui/docs-ui.js +186 -186
- package/auto-docs/utils/logger.js +17 -17
- package/auto-docs/utils/strip-usage.js +10 -10
- package/cli/docs-command.js +315 -315
- package/cli/fly-command.js +71 -71
- package/cli/index.js +56 -56
- package/database/index.js +165 -165
- package/database/mongodb.js +146 -146
- package/database/redis.js +201 -201
- package/docs/README.md +36 -36
- package/docs/ammo.md +362 -362
- package/docs/api-reference.md +490 -489
- package/docs/auto-docs.md +216 -215
- package/docs/cli.md +152 -152
- package/docs/configuration.md +275 -233
- package/docs/database.md +390 -391
- package/docs/error-handling.md +438 -417
- package/docs/file-uploads.md +333 -334
- package/docs/getting-started.md +214 -215
- package/docs/middleware.md +355 -356
- package/docs/rate-limiting.md +393 -394
- package/docs/routing.md +302 -302
- package/package.json +62 -62
- package/rate-limit/algorithms/fixed-window.js +141 -141
- package/rate-limit/algorithms/sliding-window.js +147 -147
- package/rate-limit/algorithms/token-bucket.js +115 -115
- package/rate-limit/base.js +165 -165
- package/rate-limit/index.js +147 -147
- package/rate-limit/storage/base.js +104 -104
- package/rate-limit/storage/memory.js +101 -101
- package/rate-limit/storage/redis.js +88 -88
- package/server/ammo/body-parser.js +220 -220
- package/server/ammo/dispatch-helper.js +103 -103
- package/server/ammo/enhancer.js +57 -57
- package/server/ammo.js +454 -356
- package/server/endpoint.js +97 -74
- package/server/error.js +9 -9
- package/server/errors/code-context.js +125 -0
- package/server/errors/llm-error-service.js +140 -0
- package/server/files/helper.js +33 -33
- package/server/files/uploader.js +143 -143
- package/server/handler.js +158 -113
- package/server/target.js +185 -175
- package/server/targets/middleware-validator.js +22 -22
- package/server/targets/path-validator.js +21 -21
- package/server/targets/registry.js +160 -160
- package/server/targets/shoot-validator.js +21 -21
- package/te.js +428 -363
- package/utils/auto-register.js +17 -17
- package/utils/configuration.js +64 -64
- package/utils/errors-llm-config.js +84 -0
- package/utils/request-logger.js +43 -43
- package/utils/status-codes.js +82 -82
- package/utils/tejas-entrypoint-html.js +18 -18
- package/auto-docs/llm/index.js +0 -6
- package/auto-docs/llm/parse.js +0 -88
package/docs/ammo.md
CHANGED
|
@@ -1,362 +1,362 @@
|
|
|
1
|
-
# Ammo - Request & Response
|
|
2
|
-
|
|
3
|
-
The `Ammo` class is Tejas's unified request/response object. It wraps Node.js's `req` and `res` objects, providing a clean API for handling HTTP interactions.
|
|
4
|
-
|
|
5
|
-
## Overview
|
|
6
|
-
|
|
7
|
-
Every route handler receives an `ammo` object:
|
|
8
|
-
|
|
9
|
-
```javascript
|
|
10
|
-
target.register('/example', (ammo) => {
|
|
11
|
-
// ammo contains everything you need
|
|
12
|
-
console.log(ammo.method); // 'GET'
|
|
13
|
-
console.log(ammo.payload); // { query: 'params', body: 'data' }
|
|
14
|
-
ammo.fire({ success: true });
|
|
15
|
-
});
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Properties
|
|
19
|
-
|
|
20
|
-
### HTTP Method Flags
|
|
21
|
-
|
|
22
|
-
Boolean flags for quick method checking:
|
|
23
|
-
|
|
24
|
-
```javascript
|
|
25
|
-
ammo.GET // true if GET request
|
|
26
|
-
ammo.POST // true if POST request
|
|
27
|
-
ammo.PUT // true if PUT request
|
|
28
|
-
ammo.DELETE // true if DELETE request
|
|
29
|
-
ammo.PATCH // true if PATCH request
|
|
30
|
-
ammo.HEAD // true if HEAD request
|
|
31
|
-
ammo.OPTIONS // true if OPTIONS request
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
**Usage:**
|
|
35
|
-
|
|
36
|
-
```javascript
|
|
37
|
-
target.register('/resource', (ammo) => {
|
|
38
|
-
if (ammo.GET) {
|
|
39
|
-
return ammo.fire({ items: [] });
|
|
40
|
-
}
|
|
41
|
-
if (ammo.POST) {
|
|
42
|
-
return ammo.fire(201, { created: true });
|
|
43
|
-
}
|
|
44
|
-
ammo.notAllowed();
|
|
45
|
-
});
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
### Request Data
|
|
49
|
-
|
|
50
|
-
| Property | Type | Description |
|
|
51
|
-
|----------|------|-------------|
|
|
52
|
-
| `ammo.method` | string | HTTP method (`'GET'`, `'POST'`, etc.) |
|
|
53
|
-
| `ammo.payload` | object | Combined body, query params, and route params |
|
|
54
|
-
| `ammo.headers` | object | Request headers (lowercase keys) |
|
|
55
|
-
| `ammo.ip` | string | Client IP address |
|
|
56
|
-
|
|
57
|
-
### URL Data
|
|
58
|
-
|
|
59
|
-
| Property | Type | Description |
|
|
60
|
-
|----------|------|-------------|
|
|
61
|
-
| `ammo.path` | string | Full URL path with query string |
|
|
62
|
-
| `ammo.endpoint` | string | Path without query string |
|
|
63
|
-
| `ammo.protocol` | string | `'http'` or `'https'` |
|
|
64
|
-
| `ammo.hostname` | string | Request hostname |
|
|
65
|
-
| `ammo.fullURL` | string | Complete URL |
|
|
66
|
-
|
|
67
|
-
### Raw Objects
|
|
68
|
-
|
|
69
|
-
| Property | Type | Description |
|
|
70
|
-
|----------|------|-------------|
|
|
71
|
-
| `ammo.req` | IncomingMessage | Node.js request object |
|
|
72
|
-
| `ammo.res` | ServerResponse | Node.js response object |
|
|
73
|
-
|
|
74
|
-
### Response Data
|
|
75
|
-
|
|
76
|
-
| Property | Type | Description |
|
|
77
|
-
|----------|------|-------------|
|
|
78
|
-
| `ammo.dispatchedData` | any | The data sent via the most recent `fire()` call. `undefined` until `fire()` is called |
|
|
79
|
-
|
|
80
|
-
## The Payload Object
|
|
81
|
-
|
|
82
|
-
`ammo.payload` is a merged object containing data from three sources, applied in this order (later sources override earlier ones for the same key):
|
|
83
|
-
|
|
84
|
-
1. **Query parameters** — From the URL query string
|
|
85
|
-
2. **Request body** — Parsed JSON, URL-encoded form data, or multipart form data
|
|
86
|
-
3. **Route parameters** — From parameterized routes (`:id`) — highest priority
|
|
87
|
-
|
|
88
|
-
```javascript
|
|
89
|
-
// Request: POST /users/123?notify=true
|
|
90
|
-
// Body: { "name": "John", "email": "john@example.com" }
|
|
91
|
-
|
|
92
|
-
target.register('/users/:id', (ammo) => {
|
|
93
|
-
console.log(ammo.payload);
|
|
94
|
-
// {
|
|
95
|
-
// id: '123', // Route param
|
|
96
|
-
// notify: 'true', // Query param
|
|
97
|
-
// name: 'John', // Body
|
|
98
|
-
// email: 'john@example.com' // Body
|
|
99
|
-
// }
|
|
100
|
-
});
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
## Methods
|
|
104
|
-
|
|
105
|
-
### fire() — Send Response
|
|
106
|
-
|
|
107
|
-
The primary method for sending responses:
|
|
108
|
-
|
|
109
|
-
```javascript
|
|
110
|
-
// Send JSON with 200 status (default)
|
|
111
|
-
ammo.fire({ message: 'Success' });
|
|
112
|
-
|
|
113
|
-
// Send with specific status code
|
|
114
|
-
ammo.fire(201, { id: 1, created: true });
|
|
115
|
-
|
|
116
|
-
// Send just a status code
|
|
117
|
-
ammo.fire(204);
|
|
118
|
-
|
|
119
|
-
// Send plain text
|
|
120
|
-
ammo.fire('Hello, World!');
|
|
121
|
-
|
|
122
|
-
// Send HTML with custom content type
|
|
123
|
-
ammo.fire(200, '<h1>Hello</h1>', 'text/html');
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
**All signatures:**
|
|
127
|
-
|
|
128
|
-
| Call | Status | Body | Content-Type |
|
|
129
|
-
|------|--------|------|-------------|
|
|
130
|
-
| `fire()` | 204 | *(empty)* | — |
|
|
131
|
-
| `fire("text")` | 200 | `text` | `text/plain` |
|
|
132
|
-
| `fire({ json })` | 200 | JSON string | `application/json` |
|
|
133
|
-
| `fire(201)` | 201 | status message | `text/plain` |
|
|
134
|
-
| `fire(201, data)` | 201 | `data` | auto-detected |
|
|
135
|
-
| `fire(200, html, "text/html")` | 200 | `html` | `text/html` |
|
|
136
|
-
|
|
137
|
-
After `fire()` is called, the sent data is available as `ammo.dispatchedData`.
|
|
138
|
-
|
|
139
|
-
### throw() — Send Error Response
|
|
140
|
-
|
|
141
|
-
For intentional error
|
|
142
|
-
|
|
143
|
-
```javascript
|
|
144
|
-
//
|
|
145
|
-
ammo.throw();
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
throw
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
| `throw(
|
|
165
|
-
| `throw(404
|
|
166
|
-
| `throw(
|
|
167
|
-
| `throw(new
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
ammo.
|
|
189
|
-
ammo.
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
ammo.res.setHeader('
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
|
227
|
-
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
ammo.fire(200,
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
//
|
|
247
|
-
users
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
//
|
|
270
|
-
//
|
|
271
|
-
users
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
import
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
ammo.
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
ammo.res.setHeader('
|
|
324
|
-
ammo.res.setHeader('
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
ammo.
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
|
|
1
|
+
# Ammo - Request & Response
|
|
2
|
+
|
|
3
|
+
The `Ammo` class is Tejas's unified request/response object. It wraps Node.js's `req` and `res` objects, providing a clean API for handling HTTP interactions.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Every route handler receives an `ammo` object:
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
target.register('/example', (ammo) => {
|
|
11
|
+
// ammo contains everything you need
|
|
12
|
+
console.log(ammo.method); // 'GET'
|
|
13
|
+
console.log(ammo.payload); // { query: 'params', body: 'data' }
|
|
14
|
+
ammo.fire({ success: true });
|
|
15
|
+
});
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Properties
|
|
19
|
+
|
|
20
|
+
### HTTP Method Flags
|
|
21
|
+
|
|
22
|
+
Boolean flags for quick method checking:
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
ammo.GET // true if GET request
|
|
26
|
+
ammo.POST // true if POST request
|
|
27
|
+
ammo.PUT // true if PUT request
|
|
28
|
+
ammo.DELETE // true if DELETE request
|
|
29
|
+
ammo.PATCH // true if PATCH request
|
|
30
|
+
ammo.HEAD // true if HEAD request
|
|
31
|
+
ammo.OPTIONS // true if OPTIONS request
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Usage:**
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
target.register('/resource', (ammo) => {
|
|
38
|
+
if (ammo.GET) {
|
|
39
|
+
return ammo.fire({ items: [] });
|
|
40
|
+
}
|
|
41
|
+
if (ammo.POST) {
|
|
42
|
+
return ammo.fire(201, { created: true });
|
|
43
|
+
}
|
|
44
|
+
ammo.notAllowed();
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Request Data
|
|
49
|
+
|
|
50
|
+
| Property | Type | Description |
|
|
51
|
+
|----------|------|-------------|
|
|
52
|
+
| `ammo.method` | string | HTTP method (`'GET'`, `'POST'`, etc.) |
|
|
53
|
+
| `ammo.payload` | object | Combined body, query params, and route params |
|
|
54
|
+
| `ammo.headers` | object | Request headers (lowercase keys) |
|
|
55
|
+
| `ammo.ip` | string | Client IP address |
|
|
56
|
+
|
|
57
|
+
### URL Data
|
|
58
|
+
|
|
59
|
+
| Property | Type | Description |
|
|
60
|
+
|----------|------|-------------|
|
|
61
|
+
| `ammo.path` | string | Full URL path with query string |
|
|
62
|
+
| `ammo.endpoint` | string | Path without query string |
|
|
63
|
+
| `ammo.protocol` | string | `'http'` or `'https'` |
|
|
64
|
+
| `ammo.hostname` | string | Request hostname |
|
|
65
|
+
| `ammo.fullURL` | string | Complete URL |
|
|
66
|
+
|
|
67
|
+
### Raw Objects
|
|
68
|
+
|
|
69
|
+
| Property | Type | Description |
|
|
70
|
+
|----------|------|-------------|
|
|
71
|
+
| `ammo.req` | IncomingMessage | Node.js request object |
|
|
72
|
+
| `ammo.res` | ServerResponse | Node.js response object |
|
|
73
|
+
|
|
74
|
+
### Response Data
|
|
75
|
+
|
|
76
|
+
| Property | Type | Description |
|
|
77
|
+
|----------|------|-------------|
|
|
78
|
+
| `ammo.dispatchedData` | any | The data sent via the most recent `fire()` call. `undefined` until `fire()` is called |
|
|
79
|
+
|
|
80
|
+
## The Payload Object
|
|
81
|
+
|
|
82
|
+
`ammo.payload` is a merged object containing data from three sources, applied in this order (later sources override earlier ones for the same key):
|
|
83
|
+
|
|
84
|
+
1. **Query parameters** — From the URL query string
|
|
85
|
+
2. **Request body** — Parsed JSON, URL-encoded form data, or multipart form data
|
|
86
|
+
3. **Route parameters** — From parameterized routes (`:id`) — highest priority
|
|
87
|
+
|
|
88
|
+
```javascript
|
|
89
|
+
// Request: POST /users/123?notify=true
|
|
90
|
+
// Body: { "name": "John", "email": "john@example.com" }
|
|
91
|
+
|
|
92
|
+
target.register('/users/:id', (ammo) => {
|
|
93
|
+
console.log(ammo.payload);
|
|
94
|
+
// {
|
|
95
|
+
// id: '123', // Route param
|
|
96
|
+
// notify: 'true', // Query param
|
|
97
|
+
// name: 'John', // Body
|
|
98
|
+
// email: 'john@example.com' // Body
|
|
99
|
+
// }
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Methods
|
|
104
|
+
|
|
105
|
+
### fire() — Send Response
|
|
106
|
+
|
|
107
|
+
The primary method for sending responses:
|
|
108
|
+
|
|
109
|
+
```javascript
|
|
110
|
+
// Send JSON with 200 status (default)
|
|
111
|
+
ammo.fire({ message: 'Success' });
|
|
112
|
+
|
|
113
|
+
// Send with specific status code
|
|
114
|
+
ammo.fire(201, { id: 1, created: true });
|
|
115
|
+
|
|
116
|
+
// Send just a status code
|
|
117
|
+
ammo.fire(204);
|
|
118
|
+
|
|
119
|
+
// Send plain text
|
|
120
|
+
ammo.fire('Hello, World!');
|
|
121
|
+
|
|
122
|
+
// Send HTML with custom content type
|
|
123
|
+
ammo.fire(200, '<h1>Hello</h1>', 'text/html');
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**All signatures:**
|
|
127
|
+
|
|
128
|
+
| Call | Status | Body | Content-Type |
|
|
129
|
+
|------|--------|------|-------------|
|
|
130
|
+
| `fire()` | 204 | *(empty)* | — |
|
|
131
|
+
| `fire("text")` | 200 | `text` | `text/plain` |
|
|
132
|
+
| `fire({ json })` | 200 | JSON string | `application/json` |
|
|
133
|
+
| `fire(201)` | 201 | status message | `text/plain` |
|
|
134
|
+
| `fire(201, data)` | 201 | `data` | auto-detected |
|
|
135
|
+
| `fire(200, html, "text/html")` | 200 | `html` | `text/html` |
|
|
136
|
+
|
|
137
|
+
After `fire()` is called, the sent data is available as `ammo.dispatchedData`.
|
|
138
|
+
|
|
139
|
+
### throw() — Send Error Response
|
|
140
|
+
|
|
141
|
+
**One mechanism** for error responses: you don't log the error and send the response separately — `ammo.throw()` takes care of everything. The framework uses the same `ammo.throw()` when it catches an error, so one config, one behaviour. For intentional errors, call `ammo.throw()` (or pass an error); when [LLM-inferred errors](./error-handling.md#llm-inferred-errors) are enabled, call with no arguments and an LLM infers status and message from code context. Explicit code/message always override. See [Error Handling](./error-handling.md) and per-call options (e.g. `messageType`).
|
|
142
|
+
|
|
143
|
+
```javascript
|
|
144
|
+
// Explicit: status code and/or message
|
|
145
|
+
ammo.throw(404);
|
|
146
|
+
ammo.throw(404, 'User not found');
|
|
147
|
+
ammo.throw(new TejError(400, 'Invalid input'));
|
|
148
|
+
|
|
149
|
+
// When errors.llm.enabled: no args — LLM infers from code context (surrounding + upstream/downstream)
|
|
150
|
+
ammo.throw();
|
|
151
|
+
|
|
152
|
+
// Optional: pass caught error for secondary signal; LLM still uses code context (error stack) as primary
|
|
153
|
+
ammo.throw(caughtErr);
|
|
154
|
+
|
|
155
|
+
// Per-call: skip LLM or override message type
|
|
156
|
+
ammo.throw({ useLlm: false });
|
|
157
|
+
ammo.throw({ messageType: 'developer' });
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**All `throw()` signatures:**
|
|
161
|
+
|
|
162
|
+
| Call | Status | Message |
|
|
163
|
+
|------|--------|---------|
|
|
164
|
+
| `throw()` | 500 or LLM-inferred | Default or LLM-derived from **code context** (see [LLM-inferred errors](./error-handling.md#llm-inferred-errors)) |
|
|
165
|
+
| `throw(404)` | 404 | Default message for that status code |
|
|
166
|
+
| `throw(404, "msg")` | 404 | `"msg"` |
|
|
167
|
+
| `throw(new TejError(code, msg))` | `code` | `msg` |
|
|
168
|
+
| `throw(error)` (optional) | LLM-inferred | LLM-derived from code context (error stack used to find call site) |
|
|
169
|
+
|
|
170
|
+
> **Note:** You don't need try-catch blocks in your handlers! Tejas automatically catches all errors and converts them to appropriate HTTP responses. Use `throw()` or `TejError` only for intentional, expected error conditions. See [Error Handling](./error-handling.md) for details.
|
|
171
|
+
|
|
172
|
+
### redirect() — HTTP Redirect
|
|
173
|
+
|
|
174
|
+
```javascript
|
|
175
|
+
// Temporary redirect (302)
|
|
176
|
+
ammo.redirect('/new-location');
|
|
177
|
+
|
|
178
|
+
// Permanent redirect (301)
|
|
179
|
+
ammo.redirect('/new-location', 301);
|
|
180
|
+
|
|
181
|
+
// External redirect
|
|
182
|
+
ammo.redirect('https://example.com');
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Convenience Error Methods
|
|
186
|
+
|
|
187
|
+
```javascript
|
|
188
|
+
ammo.notFound(); // Throws 404 Not Found
|
|
189
|
+
ammo.notAllowed(); // Throws 405 Method Not Allowed
|
|
190
|
+
ammo.unauthorized(); // Throws 401 Unauthorized
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Working with Headers
|
|
194
|
+
|
|
195
|
+
### Reading Headers
|
|
196
|
+
|
|
197
|
+
```javascript
|
|
198
|
+
target.register('/example', (ammo) => {
|
|
199
|
+
const authHeader = ammo.headers['authorization'];
|
|
200
|
+
const contentType = ammo.headers['content-type'];
|
|
201
|
+
const userAgent = ammo.headers['user-agent'];
|
|
202
|
+
|
|
203
|
+
ammo.fire({ userAgent });
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Setting Response Headers
|
|
208
|
+
|
|
209
|
+
Use the underlying `res` object:
|
|
210
|
+
|
|
211
|
+
```javascript
|
|
212
|
+
target.register('/example', (ammo) => {
|
|
213
|
+
ammo.res.setHeader('X-Custom-Header', 'value');
|
|
214
|
+
ammo.res.setHeader('Cache-Control', 'max-age=3600');
|
|
215
|
+
|
|
216
|
+
ammo.fire({ data: 'with headers' });
|
|
217
|
+
});
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Content Types
|
|
221
|
+
|
|
222
|
+
`fire()` automatically sets `Content-Type` based on the data:
|
|
223
|
+
|
|
224
|
+
| Data Type | Content-Type |
|
|
225
|
+
|-----------|--------------|
|
|
226
|
+
| Object/Array | `application/json` |
|
|
227
|
+
| String | `text/plain` |
|
|
228
|
+
| Buffer | `application/octet-stream` |
|
|
229
|
+
|
|
230
|
+
Override with the third parameter:
|
|
231
|
+
|
|
232
|
+
```javascript
|
|
233
|
+
ammo.fire(200, htmlString, 'text/html');
|
|
234
|
+
ammo.fire(200, xmlString, 'application/xml');
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Examples
|
|
238
|
+
|
|
239
|
+
### REST API Resource
|
|
240
|
+
|
|
241
|
+
```javascript
|
|
242
|
+
import { Target, TejError } from 'te.js';
|
|
243
|
+
|
|
244
|
+
const users = new Target('/users');
|
|
245
|
+
|
|
246
|
+
// GET /users - List all
|
|
247
|
+
// POST /users - Create new
|
|
248
|
+
users.register('/', async (ammo) => {
|
|
249
|
+
if (ammo.GET) {
|
|
250
|
+
const { page = 1, limit = 10 } = ammo.payload;
|
|
251
|
+
const users = await getUsers(page, limit);
|
|
252
|
+
return ammo.fire({ users, page, limit });
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (ammo.POST) {
|
|
256
|
+
const { name, email } = ammo.payload;
|
|
257
|
+
|
|
258
|
+
if (!name || !email) {
|
|
259
|
+
throw new TejError(400, 'Name and email are required');
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const user = await createUser({ name, email });
|
|
263
|
+
return ammo.fire(201, user);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
ammo.notAllowed();
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// GET /users/:id - Get one
|
|
270
|
+
// PUT /users/:id - Update
|
|
271
|
+
// DELETE /users/:id - Delete
|
|
272
|
+
users.register('/:id', async (ammo) => {
|
|
273
|
+
const { id } = ammo.payload;
|
|
274
|
+
|
|
275
|
+
if (ammo.GET) {
|
|
276
|
+
const user = await getUser(id);
|
|
277
|
+
if (!user) throw new TejError(404, 'User not found');
|
|
278
|
+
return ammo.fire(user);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (ammo.PUT) {
|
|
282
|
+
const { name, email } = ammo.payload;
|
|
283
|
+
const user = await updateUser(id, { name, email });
|
|
284
|
+
return ammo.fire(user);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (ammo.DELETE) {
|
|
288
|
+
await deleteUser(id);
|
|
289
|
+
return ammo.fire(204);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
ammo.notAllowed();
|
|
293
|
+
});
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### File Download
|
|
297
|
+
|
|
298
|
+
```javascript
|
|
299
|
+
import fs from 'fs';
|
|
300
|
+
import path from 'path';
|
|
301
|
+
|
|
302
|
+
target.register('/download/:filename', (ammo) => {
|
|
303
|
+
const { filename } = ammo.payload;
|
|
304
|
+
const filepath = path.join('uploads', filename);
|
|
305
|
+
|
|
306
|
+
if (!fs.existsSync(filepath)) {
|
|
307
|
+
throw new TejError(404, 'File not found');
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const file = fs.readFileSync(filepath);
|
|
311
|
+
|
|
312
|
+
ammo.res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
|
|
313
|
+
ammo.fire(200, file, 'application/octet-stream');
|
|
314
|
+
});
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Streaming Response
|
|
318
|
+
|
|
319
|
+
For large responses, use the raw `res` object:
|
|
320
|
+
|
|
321
|
+
```javascript
|
|
322
|
+
target.register('/stream', (ammo) => {
|
|
323
|
+
ammo.res.setHeader('Content-Type', 'text/event-stream');
|
|
324
|
+
ammo.res.setHeader('Cache-Control', 'no-cache');
|
|
325
|
+
ammo.res.setHeader('Connection', 'keep-alive');
|
|
326
|
+
|
|
327
|
+
let count = 0;
|
|
328
|
+
const interval = setInterval(() => {
|
|
329
|
+
ammo.res.write(`data: ${JSON.stringify({ count: ++count })}\n\n`);
|
|
330
|
+
|
|
331
|
+
if (count >= 10) {
|
|
332
|
+
clearInterval(interval);
|
|
333
|
+
ammo.res.end();
|
|
334
|
+
}
|
|
335
|
+
}, 1000);
|
|
336
|
+
});
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## Adding Custom Properties
|
|
340
|
+
|
|
341
|
+
Extend `ammo` in middleware:
|
|
342
|
+
|
|
343
|
+
```javascript
|
|
344
|
+
// In middleware
|
|
345
|
+
const authMiddleware = async (ammo, next) => {
|
|
346
|
+
const token = ammo.headers.authorization;
|
|
347
|
+
const user = await verifyToken(token);
|
|
348
|
+
|
|
349
|
+
ammo.user = user; // Add user
|
|
350
|
+
ammo.isAdmin = user.role === 'admin';
|
|
351
|
+
|
|
352
|
+
next();
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
// In handler
|
|
356
|
+
target.register('/profile', authMiddleware, (ammo) => {
|
|
357
|
+
ammo.fire({
|
|
358
|
+
user: ammo.user,
|
|
359
|
+
isAdmin: ammo.isAdmin
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
```
|