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/routing.md
CHANGED
|
@@ -1,302 +1,302 @@
|
|
|
1
|
-
# Routing with Targets
|
|
2
|
-
|
|
3
|
-
Tejas uses a **Target-based** routing system. A Target is similar to an Express Router—it groups related endpoints under a common base path.
|
|
4
|
-
|
|
5
|
-
## Creating a Target
|
|
6
|
-
|
|
7
|
-
```javascript
|
|
8
|
-
import { Target } from 'te.js';
|
|
9
|
-
|
|
10
|
-
const target = new Target('/api');
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Registering Endpoints
|
|
14
|
-
|
|
15
|
-
Use `register()` to add endpoints to a target:
|
|
16
|
-
|
|
17
|
-
```javascript
|
|
18
|
-
target.register('/users', (ammo) => {
|
|
19
|
-
ammo.fire([{ id: 1, name: 'John' }]);
|
|
20
|
-
});
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
This creates a route at `GET /api/users`.
|
|
24
|
-
|
|
25
|
-
## Method Handling
|
|
26
|
-
|
|
27
|
-
Tejas routes are **method-agnostic** by default. Use the method flags on `ammo` to handle different HTTP methods:
|
|
28
|
-
|
|
29
|
-
```javascript
|
|
30
|
-
target.register('/users', (ammo) => {
|
|
31
|
-
if (ammo.GET) {
|
|
32
|
-
// Handle GET /api/users
|
|
33
|
-
ammo.fire([{ id: 1, name: 'John' }]);
|
|
34
|
-
} else if (ammo.POST) {
|
|
35
|
-
// Handle POST /api/users
|
|
36
|
-
const { name, email } = ammo.payload;
|
|
37
|
-
ammo.fire(201, { id: 2, name, email });
|
|
38
|
-
} else {
|
|
39
|
-
ammo.notAllowed();
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
### Available Method Flags
|
|
45
|
-
|
|
46
|
-
- `ammo.GET`
|
|
47
|
-
- `ammo.POST`
|
|
48
|
-
- `ammo.PUT`
|
|
49
|
-
- `ammo.DELETE`
|
|
50
|
-
- `ammo.PATCH`
|
|
51
|
-
- `ammo.HEAD`
|
|
52
|
-
- `ammo.OPTIONS`
|
|
53
|
-
|
|
54
|
-
## Parameterized Routes
|
|
55
|
-
|
|
56
|
-
Use `:param` syntax for dynamic route segments:
|
|
57
|
-
|
|
58
|
-
```javascript
|
|
59
|
-
target.register('/users/:id', (ammo) => {
|
|
60
|
-
const { id } = ammo.payload;
|
|
61
|
-
ammo.fire({ userId: id });
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
target.register('/users/:userId/posts/:postId', (ammo) => {
|
|
65
|
-
const { userId, postId } = ammo.payload;
|
|
66
|
-
ammo.fire({ userId, postId });
|
|
67
|
-
});
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
Route parameters are automatically extracted and added to `ammo.payload`.
|
|
71
|
-
|
|
72
|
-
## Query Parameters
|
|
73
|
-
|
|
74
|
-
Query parameters are also available in `ammo.payload`:
|
|
75
|
-
|
|
76
|
-
```javascript
|
|
77
|
-
// Request: GET /api/users?page=2&limit=10
|
|
78
|
-
|
|
79
|
-
target.register('/users', (ammo) => {
|
|
80
|
-
const { page, limit } = ammo.payload;
|
|
81
|
-
ammo.fire({ page, limit }); // { page: "2", limit: "10" }
|
|
82
|
-
});
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
## Route Priority
|
|
86
|
-
|
|
87
|
-
Routes are matched in the following order:
|
|
88
|
-
|
|
89
|
-
1. **Exact matches** (most specific)
|
|
90
|
-
2. **Parameterized routes** (in registration order)
|
|
91
|
-
|
|
92
|
-
```javascript
|
|
93
|
-
// These don't conflict:
|
|
94
|
-
target.register('/users/me', handler); // Exact match for /users/me
|
|
95
|
-
target.register('/users/:id', handler); // Matches /users/123, /users/john
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
## Target-Level Middleware
|
|
99
|
-
|
|
100
|
-
Apply middleware to all routes in a target:
|
|
101
|
-
|
|
102
|
-
```javascript
|
|
103
|
-
const api = new Target('/api');
|
|
104
|
-
|
|
105
|
-
// This middleware runs for ALL /api/* routes
|
|
106
|
-
api.midair((ammo, next) => {
|
|
107
|
-
console.log('API request:', ammo.path);
|
|
108
|
-
next();
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
api.register('/users', handler);
|
|
112
|
-
api.register('/posts', handler);
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
## Route-Specific Middleware
|
|
116
|
-
|
|
117
|
-
Apply middleware to individual routes:
|
|
118
|
-
|
|
119
|
-
```javascript
|
|
120
|
-
import { authMiddleware } from './middleware/auth.js';
|
|
121
|
-
|
|
122
|
-
target.register('/public', (ammo) => {
|
|
123
|
-
ammo.fire({ public: true });
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
// Auth middleware only for this route
|
|
127
|
-
target.register('/private', authMiddleware, (ammo) => {
|
|
128
|
-
ammo.fire({ private: true, user: ammo.user });
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
// Multiple middleware
|
|
132
|
-
target.register('/admin', authMiddleware, adminMiddleware, (ammo) => {
|
|
133
|
-
ammo.fire({ admin: true });
|
|
134
|
-
});
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
## File Organization
|
|
138
|
-
|
|
139
|
-
### Recommended Structure
|
|
140
|
-
|
|
141
|
-
```
|
|
142
|
-
targets/
|
|
143
|
-
├── index.target.js # Root routes (/)
|
|
144
|
-
├── user.target.js # User routes (/user)
|
|
145
|
-
├── auth.target.js # Auth routes (/auth)
|
|
146
|
-
└── api/
|
|
147
|
-
├── v1.target.js # API v1 routes (/api/v1)
|
|
148
|
-
└── v2.target.js # API v2 routes (/api/v2)
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
### Example: user.target.js
|
|
152
|
-
|
|
153
|
-
```javascript
|
|
154
|
-
import { Target } from 'te.js';
|
|
155
|
-
import { authMiddleware } from '../middleware/auth.js';
|
|
156
|
-
|
|
157
|
-
const users = new Target('/users');
|
|
158
|
-
|
|
159
|
-
// Public route
|
|
160
|
-
users.register('/register', (ammo) => {
|
|
161
|
-
if (!ammo.POST) return ammo.notAllowed();
|
|
162
|
-
|
|
163
|
-
const { email, password, name } = ammo.payload;
|
|
164
|
-
// ... create user
|
|
165
|
-
ammo.fire(201, { message: 'User created' });
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
// Protected routes
|
|
169
|
-
users.midair(authMiddleware);
|
|
170
|
-
|
|
171
|
-
users.register('/profile', (ammo) => {
|
|
172
|
-
if (ammo.GET) {
|
|
173
|
-
ammo.fire({ user: ammo.user });
|
|
174
|
-
} else if (ammo.PUT) {
|
|
175
|
-
// ... update profile
|
|
176
|
-
ammo.fire({ message: 'Profile updated' });
|
|
177
|
-
} else {
|
|
178
|
-
ammo.notAllowed();
|
|
179
|
-
}
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
users.register('/:id', (ammo) => {
|
|
183
|
-
if (!ammo.GET) return ammo.notAllowed();
|
|
184
|
-
|
|
185
|
-
const { id } = ammo.payload;
|
|
186
|
-
// ... fetch user by id
|
|
187
|
-
ammo.fire({ id, name: 'John Doe' });
|
|
188
|
-
});
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
## Endpoint Metadata
|
|
192
|
-
|
|
193
|
-
You can optionally pass a metadata object as the second argument to `register()`. This metadata is used by the [auto-documentation](./auto-docs.md) system to generate richer OpenAPI specs:
|
|
194
|
-
|
|
195
|
-
```javascript
|
|
196
|
-
target.register('/users', {
|
|
197
|
-
summary: 'User operations',
|
|
198
|
-
description: 'Create and list users',
|
|
199
|
-
methods: ['GET', 'POST'],
|
|
200
|
-
request: {
|
|
201
|
-
name: { type: 'string', required: true },
|
|
202
|
-
email: { type: 'string', required: true }
|
|
203
|
-
},
|
|
204
|
-
response: {
|
|
205
|
-
200: { description: 'User list' },
|
|
206
|
-
201: { description: 'User created' }
|
|
207
|
-
}
|
|
208
|
-
}, (ammo) => {
|
|
209
|
-
if (ammo.GET) return ammo.fire(getUsers());
|
|
210
|
-
if (ammo.POST) return ammo.fire(201, createUser(ammo.payload));
|
|
211
|
-
ammo.notAllowed();
|
|
212
|
-
});
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
When metadata is omitted, the auto-docs LLM infers everything from the handler source code.
|
|
216
|
-
|
|
217
|
-
## Method-Agnostic Handlers
|
|
218
|
-
|
|
219
|
-
If a handler does not check any method flags (`ammo.GET`, `ammo.POST`, etc.), it is treated as accepting **all HTTP methods**. This is useful for simple endpoints:
|
|
220
|
-
|
|
221
|
-
```javascript
|
|
222
|
-
target.register('/health', (ammo) => {
|
|
223
|
-
ammo.fire({ status: 'ok' });
|
|
224
|
-
});
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
## Listing All Routes
|
|
228
|
-
|
|
229
|
-
Get all registered endpoints programmatically:
|
|
230
|
-
|
|
231
|
-
```javascript
|
|
232
|
-
import { listAllEndpoints } from 'te.js';
|
|
233
|
-
|
|
234
|
-
// Get flat list of paths
|
|
235
|
-
const routes = listAllEndpoints();
|
|
236
|
-
// ['/api/users', '/api/posts', '/auth/login', ...]
|
|
237
|
-
|
|
238
|
-
// Get grouped by first path segment
|
|
239
|
-
const grouped = listAllEndpoints(true);
|
|
240
|
-
// {
|
|
241
|
-
// api: ['/api/users', '/api/posts'],
|
|
242
|
-
// auth: ['/auth/login', '/auth/register']
|
|
243
|
-
// }
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
## Complete Example
|
|
247
|
-
|
|
248
|
-
```javascript
|
|
249
|
-
// targets/products.target.js
|
|
250
|
-
import { Target, TejError } from 'te.js';
|
|
251
|
-
import { authMiddleware, adminMiddleware } from '../middleware/index.js';
|
|
252
|
-
|
|
253
|
-
const products = new Target('/products');
|
|
254
|
-
|
|
255
|
-
// Public: List all products
|
|
256
|
-
products.register('/', (ammo) => {
|
|
257
|
-
if (!ammo.GET) return ammo.notAllowed();
|
|
258
|
-
|
|
259
|
-
const { category, page = 1, limit = 10 } = ammo.payload;
|
|
260
|
-
// ... fetch products
|
|
261
|
-
ammo.fire({ products: [], page, limit });
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
// Public: Get single product
|
|
265
|
-
products.register('/:id', (ammo) => {
|
|
266
|
-
if (!ammo.GET) return ammo.notAllowed();
|
|
267
|
-
|
|
268
|
-
const { id } = ammo.payload;
|
|
269
|
-
const product = findProduct(id);
|
|
270
|
-
|
|
271
|
-
if (!product) {
|
|
272
|
-
throw new TejError(404, 'Product not found');
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
ammo.fire(product);
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
// Protected: Create product (admin only)
|
|
279
|
-
products.register('/create', authMiddleware, adminMiddleware, (ammo) => {
|
|
280
|
-
if (!ammo.POST) return ammo.notAllowed();
|
|
281
|
-
|
|
282
|
-
const { name, price, description } = ammo.payload;
|
|
283
|
-
// ... create product
|
|
284
|
-
ammo.fire(201, { id: 'new-id', name, price });
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
// Protected: Update product (admin only)
|
|
288
|
-
products.register('/:id/update', authMiddleware, adminMiddleware, (ammo) => {
|
|
289
|
-
if (!ammo.PUT) return ammo.notAllowed();
|
|
290
|
-
|
|
291
|
-
const { id, ...updates } = ammo.payload;
|
|
292
|
-
// ... update product
|
|
293
|
-
ammo.fire({ message: 'Product updated' });
|
|
294
|
-
});
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
## Next Steps
|
|
298
|
-
|
|
299
|
-
- [Ammo](./ammo.md) — Handle requests and send responses
|
|
300
|
-
- [Middleware](./middleware.md) — Global, target, and route-level middleware
|
|
301
|
-
- [Auto-Documentation](./auto-docs.md) — Endpoint metadata for OpenAPI generation
|
|
302
|
-
|
|
1
|
+
# Routing with Targets
|
|
2
|
+
|
|
3
|
+
Tejas uses a **Target-based** routing system. A Target is similar to an Express Router—it groups related endpoints under a common base path.
|
|
4
|
+
|
|
5
|
+
## Creating a Target
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
import { Target } from 'te.js';
|
|
9
|
+
|
|
10
|
+
const target = new Target('/api');
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Registering Endpoints
|
|
14
|
+
|
|
15
|
+
Use `register()` to add endpoints to a target:
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
target.register('/users', (ammo) => {
|
|
19
|
+
ammo.fire([{ id: 1, name: 'John' }]);
|
|
20
|
+
});
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
This creates a route at `GET /api/users`.
|
|
24
|
+
|
|
25
|
+
## Method Handling
|
|
26
|
+
|
|
27
|
+
Tejas routes are **method-agnostic** by default. Use the method flags on `ammo` to handle different HTTP methods:
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
target.register('/users', (ammo) => {
|
|
31
|
+
if (ammo.GET) {
|
|
32
|
+
// Handle GET /api/users
|
|
33
|
+
ammo.fire([{ id: 1, name: 'John' }]);
|
|
34
|
+
} else if (ammo.POST) {
|
|
35
|
+
// Handle POST /api/users
|
|
36
|
+
const { name, email } = ammo.payload;
|
|
37
|
+
ammo.fire(201, { id: 2, name, email });
|
|
38
|
+
} else {
|
|
39
|
+
ammo.notAllowed();
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Available Method Flags
|
|
45
|
+
|
|
46
|
+
- `ammo.GET`
|
|
47
|
+
- `ammo.POST`
|
|
48
|
+
- `ammo.PUT`
|
|
49
|
+
- `ammo.DELETE`
|
|
50
|
+
- `ammo.PATCH`
|
|
51
|
+
- `ammo.HEAD`
|
|
52
|
+
- `ammo.OPTIONS`
|
|
53
|
+
|
|
54
|
+
## Parameterized Routes
|
|
55
|
+
|
|
56
|
+
Use `:param` syntax for dynamic route segments:
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
target.register('/users/:id', (ammo) => {
|
|
60
|
+
const { id } = ammo.payload;
|
|
61
|
+
ammo.fire({ userId: id });
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
target.register('/users/:userId/posts/:postId', (ammo) => {
|
|
65
|
+
const { userId, postId } = ammo.payload;
|
|
66
|
+
ammo.fire({ userId, postId });
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Route parameters are automatically extracted and added to `ammo.payload`.
|
|
71
|
+
|
|
72
|
+
## Query Parameters
|
|
73
|
+
|
|
74
|
+
Query parameters are also available in `ammo.payload`:
|
|
75
|
+
|
|
76
|
+
```javascript
|
|
77
|
+
// Request: GET /api/users?page=2&limit=10
|
|
78
|
+
|
|
79
|
+
target.register('/users', (ammo) => {
|
|
80
|
+
const { page, limit } = ammo.payload;
|
|
81
|
+
ammo.fire({ page, limit }); // { page: "2", limit: "10" }
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Route Priority
|
|
86
|
+
|
|
87
|
+
Routes are matched in the following order:
|
|
88
|
+
|
|
89
|
+
1. **Exact matches** (most specific)
|
|
90
|
+
2. **Parameterized routes** (in registration order)
|
|
91
|
+
|
|
92
|
+
```javascript
|
|
93
|
+
// These don't conflict:
|
|
94
|
+
target.register('/users/me', handler); // Exact match for /users/me
|
|
95
|
+
target.register('/users/:id', handler); // Matches /users/123, /users/john
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Target-Level Middleware
|
|
99
|
+
|
|
100
|
+
Apply middleware to all routes in a target:
|
|
101
|
+
|
|
102
|
+
```javascript
|
|
103
|
+
const api = new Target('/api');
|
|
104
|
+
|
|
105
|
+
// This middleware runs for ALL /api/* routes
|
|
106
|
+
api.midair((ammo, next) => {
|
|
107
|
+
console.log('API request:', ammo.path);
|
|
108
|
+
next();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
api.register('/users', handler);
|
|
112
|
+
api.register('/posts', handler);
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Route-Specific Middleware
|
|
116
|
+
|
|
117
|
+
Apply middleware to individual routes:
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
import { authMiddleware } from './middleware/auth.js';
|
|
121
|
+
|
|
122
|
+
target.register('/public', (ammo) => {
|
|
123
|
+
ammo.fire({ public: true });
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Auth middleware only for this route
|
|
127
|
+
target.register('/private', authMiddleware, (ammo) => {
|
|
128
|
+
ammo.fire({ private: true, user: ammo.user });
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Multiple middleware
|
|
132
|
+
target.register('/admin', authMiddleware, adminMiddleware, (ammo) => {
|
|
133
|
+
ammo.fire({ admin: true });
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## File Organization
|
|
138
|
+
|
|
139
|
+
### Recommended Structure
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
targets/
|
|
143
|
+
├── index.target.js # Root routes (/)
|
|
144
|
+
├── user.target.js # User routes (/user)
|
|
145
|
+
├── auth.target.js # Auth routes (/auth)
|
|
146
|
+
└── api/
|
|
147
|
+
├── v1.target.js # API v1 routes (/api/v1)
|
|
148
|
+
└── v2.target.js # API v2 routes (/api/v2)
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Example: user.target.js
|
|
152
|
+
|
|
153
|
+
```javascript
|
|
154
|
+
import { Target } from 'te.js';
|
|
155
|
+
import { authMiddleware } from '../middleware/auth.js';
|
|
156
|
+
|
|
157
|
+
const users = new Target('/users');
|
|
158
|
+
|
|
159
|
+
// Public route
|
|
160
|
+
users.register('/register', (ammo) => {
|
|
161
|
+
if (!ammo.POST) return ammo.notAllowed();
|
|
162
|
+
|
|
163
|
+
const { email, password, name } = ammo.payload;
|
|
164
|
+
// ... create user
|
|
165
|
+
ammo.fire(201, { message: 'User created' });
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Protected routes
|
|
169
|
+
users.midair(authMiddleware);
|
|
170
|
+
|
|
171
|
+
users.register('/profile', (ammo) => {
|
|
172
|
+
if (ammo.GET) {
|
|
173
|
+
ammo.fire({ user: ammo.user });
|
|
174
|
+
} else if (ammo.PUT) {
|
|
175
|
+
// ... update profile
|
|
176
|
+
ammo.fire({ message: 'Profile updated' });
|
|
177
|
+
} else {
|
|
178
|
+
ammo.notAllowed();
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
users.register('/:id', (ammo) => {
|
|
183
|
+
if (!ammo.GET) return ammo.notAllowed();
|
|
184
|
+
|
|
185
|
+
const { id } = ammo.payload;
|
|
186
|
+
// ... fetch user by id
|
|
187
|
+
ammo.fire({ id, name: 'John Doe' });
|
|
188
|
+
});
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Endpoint Metadata
|
|
192
|
+
|
|
193
|
+
You can optionally pass a metadata object as the second argument to `register()`. This metadata is used by the [auto-documentation](./auto-docs.md) system to generate richer OpenAPI specs:
|
|
194
|
+
|
|
195
|
+
```javascript
|
|
196
|
+
target.register('/users', {
|
|
197
|
+
summary: 'User operations',
|
|
198
|
+
description: 'Create and list users',
|
|
199
|
+
methods: ['GET', 'POST'],
|
|
200
|
+
request: {
|
|
201
|
+
name: { type: 'string', required: true },
|
|
202
|
+
email: { type: 'string', required: true }
|
|
203
|
+
},
|
|
204
|
+
response: {
|
|
205
|
+
200: { description: 'User list' },
|
|
206
|
+
201: { description: 'User created' }
|
|
207
|
+
}
|
|
208
|
+
}, (ammo) => {
|
|
209
|
+
if (ammo.GET) return ammo.fire(getUsers());
|
|
210
|
+
if (ammo.POST) return ammo.fire(201, createUser(ammo.payload));
|
|
211
|
+
ammo.notAllowed();
|
|
212
|
+
});
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
When metadata is omitted, the auto-docs LLM infers everything from the handler source code.
|
|
216
|
+
|
|
217
|
+
## Method-Agnostic Handlers
|
|
218
|
+
|
|
219
|
+
If a handler does not check any method flags (`ammo.GET`, `ammo.POST`, etc.), it is treated as accepting **all HTTP methods**. This is useful for simple endpoints:
|
|
220
|
+
|
|
221
|
+
```javascript
|
|
222
|
+
target.register('/health', (ammo) => {
|
|
223
|
+
ammo.fire({ status: 'ok' });
|
|
224
|
+
});
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Listing All Routes
|
|
228
|
+
|
|
229
|
+
Get all registered endpoints programmatically:
|
|
230
|
+
|
|
231
|
+
```javascript
|
|
232
|
+
import { listAllEndpoints } from 'te.js';
|
|
233
|
+
|
|
234
|
+
// Get flat list of paths
|
|
235
|
+
const routes = listAllEndpoints();
|
|
236
|
+
// ['/api/users', '/api/posts', '/auth/login', ...]
|
|
237
|
+
|
|
238
|
+
// Get grouped by first path segment
|
|
239
|
+
const grouped = listAllEndpoints(true);
|
|
240
|
+
// {
|
|
241
|
+
// api: ['/api/users', '/api/posts'],
|
|
242
|
+
// auth: ['/auth/login', '/auth/register']
|
|
243
|
+
// }
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Complete Example
|
|
247
|
+
|
|
248
|
+
```javascript
|
|
249
|
+
// targets/products.target.js
|
|
250
|
+
import { Target, TejError } from 'te.js';
|
|
251
|
+
import { authMiddleware, adminMiddleware } from '../middleware/index.js';
|
|
252
|
+
|
|
253
|
+
const products = new Target('/products');
|
|
254
|
+
|
|
255
|
+
// Public: List all products
|
|
256
|
+
products.register('/', (ammo) => {
|
|
257
|
+
if (!ammo.GET) return ammo.notAllowed();
|
|
258
|
+
|
|
259
|
+
const { category, page = 1, limit = 10 } = ammo.payload;
|
|
260
|
+
// ... fetch products
|
|
261
|
+
ammo.fire({ products: [], page, limit });
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// Public: Get single product
|
|
265
|
+
products.register('/:id', (ammo) => {
|
|
266
|
+
if (!ammo.GET) return ammo.notAllowed();
|
|
267
|
+
|
|
268
|
+
const { id } = ammo.payload;
|
|
269
|
+
const product = findProduct(id);
|
|
270
|
+
|
|
271
|
+
if (!product) {
|
|
272
|
+
throw new TejError(404, 'Product not found');
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
ammo.fire(product);
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// Protected: Create product (admin only)
|
|
279
|
+
products.register('/create', authMiddleware, adminMiddleware, (ammo) => {
|
|
280
|
+
if (!ammo.POST) return ammo.notAllowed();
|
|
281
|
+
|
|
282
|
+
const { name, price, description } = ammo.payload;
|
|
283
|
+
// ... create product
|
|
284
|
+
ammo.fire(201, { id: 'new-id', name, price });
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// Protected: Update product (admin only)
|
|
288
|
+
products.register('/:id/update', authMiddleware, adminMiddleware, (ammo) => {
|
|
289
|
+
if (!ammo.PUT) return ammo.notAllowed();
|
|
290
|
+
|
|
291
|
+
const { id, ...updates } = ammo.payload;
|
|
292
|
+
// ... update product
|
|
293
|
+
ammo.fire({ message: 'Product updated' });
|
|
294
|
+
});
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## Next Steps
|
|
298
|
+
|
|
299
|
+
- [Ammo](./ammo.md) — Handle requests and send responses
|
|
300
|
+
- [Middleware](./middleware.md) — Global, target, and route-level middleware
|
|
301
|
+
- [Auto-Documentation](./auto-docs.md) — Endpoint metadata for OpenAPI generation
|
|
302
|
+
|