te.js 2.0.1 → 2.0.3
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/package.json +13 -2
- package/.cursor/plans/ai_native_framework_features_5bb1a20a.plan.md +0 -234
- package/.cursor/plans/auto_error_fix_agent_e68979c5.plan.md +0 -356
- package/.cursor/plans/tejas_framework_test_suite_5e3c6fad.plan.md +0 -168
- package/.prettierignore +0 -31
- package/.prettierrc +0 -5
- package/example/API_OVERVIEW.md +0 -77
- package/example/README.md +0 -155
- package/example/index.js +0 -29
- package/example/middlewares/auth.js +0 -9
- package/example/middlewares/global.midair.js +0 -6
- package/example/openapi.json +0 -390
- package/example/package.json +0 -23
- package/example/services/cache.service.js +0 -25
- package/example/services/user.service.js +0 -42
- package/example/start-redis.js +0 -2
- package/example/targets/cache.target.js +0 -35
- package/example/targets/index.target.js +0 -16
- package/example/targets/users.target.js +0 -60
- package/example/tejas.config.json +0 -22
- package/tests/auto-docs/handler-analyzer.test.js +0 -44
- package/tests/auto-docs/openapi-generator.test.js +0 -103
- package/tests/auto-docs/parse.test.js +0 -63
- package/tests/auto-docs/source-resolver.test.js +0 -58
- package/tests/helpers/index.js +0 -37
- package/tests/helpers/mock-http.js +0 -342
- package/tests/helpers/test-utils.js +0 -446
- package/tests/setup.test.js +0 -148
- package/vitest.config.js +0 -54
package/example/openapi.json
DELETED
|
@@ -1,390 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"openapi": "3.0.3",
|
|
3
|
-
"info": {
|
|
4
|
-
"title": "API",
|
|
5
|
-
"version": "1.0.0",
|
|
6
|
-
"description": "# API Documentation\n\n**Version:** 1.0.0\n\n## Project Summary\n\nThis API provides a lightweight service combining user account management, a Redis-backed caching layer, and system introspection utilities. It supports full CRUD operations on user resources—including file uploads for profile images and bulk documents—offers key-value caching with optional TTL, and exposes health-check and route discovery endpoints for operational monitoring and API introspection. User data is maintained in-memory for demonstration purposes, and uploaded files are stored on disk with configurable size limits.\n\n---\n\n## APIs Available\n\n### Users\nManages user accounts with full CRUD operations: create, retrieve, update, and delete users. Additionally supports profile image uploads and bulk document uploads, with files stored on disk and configurable size limits.\n\n### Cache\nProvides a key-value caching layer backed by Redis. Store arbitrary values with an optional time-to-live (TTL) and retrieve them by key. A middleware guard ensures the Redis service is available before processing any cache request.\n\n### System & Discovery\nCore application utilities including a default landing endpoint, a health-check endpoint reporting server status with a current timestamp, and a route discovery endpoint that lists all registered endpoints grouped by source for API introspection.\n\n---\n\n## Key Endpoints\n\n### Getting Started\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `GET` | `/` | Default landing entry point for the API. |\n| `GET` | `/health` | Health check — returns server status and current timestamp. |\n| `GET` | `/routes` | Route discovery — lists all registered endpoints grouped by source. |\n\n### User Management\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `GET` | `/users` | Retrieve a list of all users. |\n| `POST` | `/users` | Create a new user. |\n| `GET` | `/users/{id}` | Retrieve a specific user by ID. |\n| `PUT` | `/users/{id}` | Update an existing user by ID. |\n| `DELETE` | `/users/{id}` | Delete a user by ID. |\n| `GET` | `/users/{id}/updateProfileImage` | Upload or update a user's profile image. |\n| `GET` | `/users/{id}/uploadDocuments` | Bulk upload documents for a user. |\n\n### Cache Operations\n\n| Method | Path | Description |\n|--------|------|-------------|\n| `POST` | `/cache` | Store a value in the cache with an optional TTL. |\n| `GET` | `/cache/{key}` | Retrieve a cached value by its key. |\n\n---\n\n## Additional Notes\n\n### Data Storage\n- **User data** is maintained in-memory and is not persisted across server restarts (demonstration mode).\n- **Uploaded files** (profile images, documents) are stored on disk with configurable size limits.\n\n### Cache Availability\nAll cache endpoints are protected by a middleware guard that verifies the Redis service is reachable before processing requests. If Redis is unavailable, cache requests will be rejected before reaching the handler.\n\n### File Uploads\n- **Profile images**: Single-file upload tied to a specific user via `/users/{id}/updateProfileImage`.\n- **Bulk documents**: Multi-file upload supported via `/users/{id}/uploadDocuments`.\n\nFile size limits are configurable at the application level.\n\n---\n\n## Quick Start\n\n1. **Verify the API is running** — call `GET /health` and confirm a successful status response with a timestamp.\n2. **Explore available routes** — call `GET /routes` to discover all registered endpoints.\n3. **Create a user** — send a `POST` request to `/users` with the required user payload.\n4. **Store and retrieve cache entries** — use `POST /cache` to set a key-value pair, then `GET /cache/{key}` to retrieve it."
|
|
7
|
-
},
|
|
8
|
-
"tags": [
|
|
9
|
-
{
|
|
10
|
-
"name": "Users",
|
|
11
|
-
"description": "Manages user accounts with full CRUD operations including creation, retrieval, updating, and deletion. Also supports profile image uploads and bulk document uploads, storing files on disk with configurable size limits. User data is maintained in-memory for demonstration purposes."
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
"name": "Cache",
|
|
15
|
-
"description": "Provides a key-value caching layer backed by Redis. Consumers can store arbitrary values with an optional time-to-live and retrieve them by key. A middleware guard ensures the Redis-backed cache service is available before processing any request."
|
|
16
|
-
},
|
|
17
|
-
{
|
|
18
|
-
"name": "System & Discovery",
|
|
19
|
-
"description": "Provides core application utilities: a default landing entry point, a health-check endpoint that reports server status with a current timestamp, and a route discovery endpoint that lists all registered endpoints grouped by their source, enabling API introspection."
|
|
20
|
-
}
|
|
21
|
-
],
|
|
22
|
-
"paths": {
|
|
23
|
-
"/cache/{key}": {
|
|
24
|
-
"get": {
|
|
25
|
-
"summary": "Get cached value by key",
|
|
26
|
-
"description": "Retrieves a cached value from Redis by its key. Requires the cache service (Redis) to be available.",
|
|
27
|
-
"parameters": [
|
|
28
|
-
{
|
|
29
|
-
"name": "key",
|
|
30
|
-
"in": "path",
|
|
31
|
-
"required": true,
|
|
32
|
-
"description": "Path parameter: key",
|
|
33
|
-
"schema": {
|
|
34
|
-
"type": "string"
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
],
|
|
38
|
-
"responses": {
|
|
39
|
-
"200": {
|
|
40
|
-
"description": "Cached value found and returned successfully"
|
|
41
|
-
},
|
|
42
|
-
"404": {
|
|
43
|
-
"description": "No cached value found for the given key"
|
|
44
|
-
},
|
|
45
|
-
"405": {
|
|
46
|
-
"description": "Method not allowed (non-GET request made to this endpoint)"
|
|
47
|
-
},
|
|
48
|
-
"503": {
|
|
49
|
-
"description": "Cache service unavailable. Redis is not configured or reachable."
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
"tags": [
|
|
53
|
-
"Cache"
|
|
54
|
-
]
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
"/cache": {
|
|
58
|
-
"post": {
|
|
59
|
-
"summary": "Store a value in the cache",
|
|
60
|
-
"description": "Sets a key-value pair in the Redis cache with an optional TTL (time-to-live) in seconds. Requires the cache service (Redis) to be available.",
|
|
61
|
-
"requestBody": {
|
|
62
|
-
"required": true,
|
|
63
|
-
"content": {
|
|
64
|
-
"application/json": {
|
|
65
|
-
"schema": {
|
|
66
|
-
"type": "object",
|
|
67
|
-
"properties": {
|
|
68
|
-
"key": {
|
|
69
|
-
"type": "string",
|
|
70
|
-
"description": "The cache key under which to store the value"
|
|
71
|
-
},
|
|
72
|
-
"value": {
|
|
73
|
-
"type": "any",
|
|
74
|
-
"description": "The value to cache; can be any type but must not be undefined"
|
|
75
|
-
},
|
|
76
|
-
"ttl": {
|
|
77
|
-
"type": "integer",
|
|
78
|
-
"description": "Time-to-live in seconds for the cached entry; if omitted, the entry does not expire automatically"
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
"required": [
|
|
82
|
-
"key",
|
|
83
|
-
"value"
|
|
84
|
-
]
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
},
|
|
89
|
-
"responses": {
|
|
90
|
-
"201": {
|
|
91
|
-
"description": "Value cached successfully. Returns an object with a confirmation message and the cache key."
|
|
92
|
-
},
|
|
93
|
-
"400": {
|
|
94
|
-
"description": "Validation error: 'key' and 'value' are required fields"
|
|
95
|
-
},
|
|
96
|
-
"405": {
|
|
97
|
-
"description": "Method not allowed. Only POST is accepted on this endpoint."
|
|
98
|
-
},
|
|
99
|
-
"503": {
|
|
100
|
-
"description": "Cache service unavailable. Redis is not configured or not reachable (REDIS_URL not set)."
|
|
101
|
-
}
|
|
102
|
-
},
|
|
103
|
-
"tags": [
|
|
104
|
-
"Cache"
|
|
105
|
-
]
|
|
106
|
-
}
|
|
107
|
-
},
|
|
108
|
-
"/": {
|
|
109
|
-
"get": {
|
|
110
|
-
"summary": "Submit data to the default entry endpoint",
|
|
111
|
-
"description": "Handles POST requests to the root endpoint by returning the default entry response. No specific body processing is performed.\n\nAccepts any HTTP method: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS.",
|
|
112
|
-
"responses": {
|
|
113
|
-
"200": {
|
|
114
|
-
"description": "Default entry content returned successfully"
|
|
115
|
-
}
|
|
116
|
-
},
|
|
117
|
-
"tags": [
|
|
118
|
-
"System & Discovery"
|
|
119
|
-
]
|
|
120
|
-
}
|
|
121
|
-
},
|
|
122
|
-
"/health": {
|
|
123
|
-
"get": {
|
|
124
|
-
"summary": "Post health check request",
|
|
125
|
-
"description": "Accepts a POST request and returns the current health status and timestamp. No request body is required; any provided body is ignored.\n\nAccepts any HTTP method: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS.",
|
|
126
|
-
"responses": {
|
|
127
|
-
"200": {
|
|
128
|
-
"description": "Service is healthy. Returns an object with `status` ('ok') and `timestamp` (ISO 8601 string)."
|
|
129
|
-
}
|
|
130
|
-
},
|
|
131
|
-
"tags": [
|
|
132
|
-
"System & Discovery"
|
|
133
|
-
]
|
|
134
|
-
}
|
|
135
|
-
},
|
|
136
|
-
"/routes": {
|
|
137
|
-
"get": {
|
|
138
|
-
"summary": "Create or trigger route listing via POST",
|
|
139
|
-
"description": "Returns the same grouped listing of all registered endpoints. The handler does not differentiate by HTTP method, so POST behaves identically to GET.\n\nAccepts any HTTP method: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS.",
|
|
140
|
-
"responses": {
|
|
141
|
-
"200": {
|
|
142
|
-
"description": "A grouped object of all registered endpoints and their supported methods"
|
|
143
|
-
}
|
|
144
|
-
},
|
|
145
|
-
"tags": [
|
|
146
|
-
"System & Discovery"
|
|
147
|
-
]
|
|
148
|
-
}
|
|
149
|
-
},
|
|
150
|
-
"/users": {
|
|
151
|
-
"get": {
|
|
152
|
-
"summary": "List all users",
|
|
153
|
-
"description": "Returns an array of all users currently stored in the system.",
|
|
154
|
-
"responses": {
|
|
155
|
-
"200": {
|
|
156
|
-
"description": "An array of user objects. Returns an empty array if no users exist."
|
|
157
|
-
}
|
|
158
|
-
},
|
|
159
|
-
"tags": [
|
|
160
|
-
"Users"
|
|
161
|
-
]
|
|
162
|
-
},
|
|
163
|
-
"post": {
|
|
164
|
-
"summary": "Create a new user",
|
|
165
|
-
"description": "Creates a new user with the provided name and email. Returns the newly created user object with a generated id and createdAt timestamp.",
|
|
166
|
-
"requestBody": {
|
|
167
|
-
"required": true,
|
|
168
|
-
"content": {
|
|
169
|
-
"application/json": {
|
|
170
|
-
"schema": {
|
|
171
|
-
"type": "object",
|
|
172
|
-
"properties": {
|
|
173
|
-
"name": {
|
|
174
|
-
"type": "string"
|
|
175
|
-
},
|
|
176
|
-
"email": {
|
|
177
|
-
"type": "string",
|
|
178
|
-
"format": "email"
|
|
179
|
-
}
|
|
180
|
-
},
|
|
181
|
-
"required": [
|
|
182
|
-
"name",
|
|
183
|
-
"email"
|
|
184
|
-
]
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
},
|
|
189
|
-
"responses": {
|
|
190
|
-
"201": {
|
|
191
|
-
"description": "User created successfully. Returns the new user object including id, name, email, and createdAt fields."
|
|
192
|
-
},
|
|
193
|
-
"400": {
|
|
194
|
-
"description": "Validation error. Both name and email are required."
|
|
195
|
-
}
|
|
196
|
-
},
|
|
197
|
-
"tags": [
|
|
198
|
-
"Users"
|
|
199
|
-
]
|
|
200
|
-
}
|
|
201
|
-
},
|
|
202
|
-
"/users/{id}": {
|
|
203
|
-
"get": {
|
|
204
|
-
"summary": "Get user by id",
|
|
205
|
-
"description": "Retrieves a single user by their unique identifier.",
|
|
206
|
-
"parameters": [
|
|
207
|
-
{
|
|
208
|
-
"name": "id",
|
|
209
|
-
"in": "path",
|
|
210
|
-
"required": true,
|
|
211
|
-
"description": "Path parameter: id",
|
|
212
|
-
"schema": {
|
|
213
|
-
"type": "string"
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
],
|
|
217
|
-
"responses": {
|
|
218
|
-
"200": {
|
|
219
|
-
"description": "User object returned successfully"
|
|
220
|
-
},
|
|
221
|
-
"404": {
|
|
222
|
-
"description": "User not found"
|
|
223
|
-
}
|
|
224
|
-
},
|
|
225
|
-
"tags": [
|
|
226
|
-
"Users"
|
|
227
|
-
]
|
|
228
|
-
},
|
|
229
|
-
"put": {
|
|
230
|
-
"summary": "Update user",
|
|
231
|
-
"description": "Updates an existing user's name and/or email by their unique identifier.",
|
|
232
|
-
"parameters": [
|
|
233
|
-
{
|
|
234
|
-
"name": "id",
|
|
235
|
-
"in": "path",
|
|
236
|
-
"required": true,
|
|
237
|
-
"description": "Path parameter: id",
|
|
238
|
-
"schema": {
|
|
239
|
-
"type": "string"
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
],
|
|
243
|
-
"requestBody": {
|
|
244
|
-
"required": true,
|
|
245
|
-
"content": {
|
|
246
|
-
"application/json": {
|
|
247
|
-
"schema": {
|
|
248
|
-
"type": "object",
|
|
249
|
-
"properties": {
|
|
250
|
-
"name": {
|
|
251
|
-
"type": "string"
|
|
252
|
-
},
|
|
253
|
-
"email": {
|
|
254
|
-
"type": "string",
|
|
255
|
-
"format": "email"
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
},
|
|
262
|
-
"responses": {
|
|
263
|
-
"200": {
|
|
264
|
-
"description": "User updated successfully"
|
|
265
|
-
},
|
|
266
|
-
"404": {
|
|
267
|
-
"description": "User not found"
|
|
268
|
-
}
|
|
269
|
-
},
|
|
270
|
-
"tags": [
|
|
271
|
-
"Users"
|
|
272
|
-
]
|
|
273
|
-
},
|
|
274
|
-
"delete": {
|
|
275
|
-
"summary": "Delete user",
|
|
276
|
-
"description": "Deletes a user by their unique identifier.",
|
|
277
|
-
"parameters": [
|
|
278
|
-
{
|
|
279
|
-
"name": "id",
|
|
280
|
-
"in": "path",
|
|
281
|
-
"required": true,
|
|
282
|
-
"description": "Path parameter: id",
|
|
283
|
-
"schema": {
|
|
284
|
-
"type": "string"
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
],
|
|
288
|
-
"responses": {
|
|
289
|
-
"204": {
|
|
290
|
-
"description": "User deleted successfully"
|
|
291
|
-
},
|
|
292
|
-
"404": {
|
|
293
|
-
"description": "User not found"
|
|
294
|
-
}
|
|
295
|
-
},
|
|
296
|
-
"tags": [
|
|
297
|
-
"Users"
|
|
298
|
-
]
|
|
299
|
-
}
|
|
300
|
-
},
|
|
301
|
-
"/users/{id}/updateProfileImage": {
|
|
302
|
-
"get": {
|
|
303
|
-
"summary": "Upload a profile image for a user",
|
|
304
|
-
"description": "Uploads a single profile image file for the specified user. Uses multipart/form-data with a file field named 'image'. Maximum file size is 5MB. Files are stored in 'public/uploads'.\n\nAccepts any HTTP method: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS.",
|
|
305
|
-
"parameters": [
|
|
306
|
-
{
|
|
307
|
-
"name": "id",
|
|
308
|
-
"in": "path",
|
|
309
|
-
"required": true,
|
|
310
|
-
"description": "Path parameter: id",
|
|
311
|
-
"schema": {
|
|
312
|
-
"type": "string"
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
],
|
|
316
|
-
"requestBody": {
|
|
317
|
-
"required": true,
|
|
318
|
-
"content": {
|
|
319
|
-
"application/json": {
|
|
320
|
-
"schema": {
|
|
321
|
-
"type": "object",
|
|
322
|
-
"properties": {
|
|
323
|
-
"image": {
|
|
324
|
-
"type": "file",
|
|
325
|
-
"description": "The profile image file to upload (max 5MB)"
|
|
326
|
-
}
|
|
327
|
-
},
|
|
328
|
-
"required": [
|
|
329
|
-
"image"
|
|
330
|
-
]
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
},
|
|
335
|
-
"responses": {
|
|
336
|
-
"200": {
|
|
337
|
-
"description": "Profile image uploaded successfully. Returns a message and the uploaded file metadata."
|
|
338
|
-
}
|
|
339
|
-
},
|
|
340
|
-
"tags": [
|
|
341
|
-
"Users"
|
|
342
|
-
]
|
|
343
|
-
}
|
|
344
|
-
},
|
|
345
|
-
"/users/{id}/uploadDocuments": {
|
|
346
|
-
"get": {
|
|
347
|
-
"summary": "Upload documents for a user",
|
|
348
|
-
"description": "Uploads multiple document files for the user identified by :id. Files are processed by the TejFileUploader middleware with a max file size of 5MB each and stored in 'public/uploads'.\n\nAccepts any HTTP method: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS.",
|
|
349
|
-
"parameters": [
|
|
350
|
-
{
|
|
351
|
-
"name": "id",
|
|
352
|
-
"in": "path",
|
|
353
|
-
"required": true,
|
|
354
|
-
"description": "Path parameter: id",
|
|
355
|
-
"schema": {
|
|
356
|
-
"type": "string"
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
],
|
|
360
|
-
"requestBody": {
|
|
361
|
-
"required": true,
|
|
362
|
-
"content": {
|
|
363
|
-
"application/json": {
|
|
364
|
-
"schema": {
|
|
365
|
-
"type": "object",
|
|
366
|
-
"properties": {
|
|
367
|
-
"documents": {
|
|
368
|
-
"type": "array",
|
|
369
|
-
"description": "Array of document files to upload. Field name must be 'documents'. Each file must not exceed 5MB."
|
|
370
|
-
}
|
|
371
|
-
},
|
|
372
|
-
"required": [
|
|
373
|
-
"documents"
|
|
374
|
-
]
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
},
|
|
379
|
-
"responses": {
|
|
380
|
-
"200": {
|
|
381
|
-
"description": "Documents uploaded successfully. Returns a message indicating the number of documents uploaded and an array of file metadata."
|
|
382
|
-
}
|
|
383
|
-
},
|
|
384
|
-
"tags": [
|
|
385
|
-
"Users"
|
|
386
|
-
]
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
}
|
package/example/package.json
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "example",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"description": "Comprehensive te.js example - routing, middleware, file uploads, services",
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"scripts": {
|
|
8
|
-
"start": "node index.js",
|
|
9
|
-
"start:redis": "node start-redis.js",
|
|
10
|
-
"test": "echo \"Error: no test specified\" && exit 1",
|
|
11
|
-
"prettify": "prettier --write --ignore-unknown"
|
|
12
|
-
},
|
|
13
|
-
"keywords": [],
|
|
14
|
-
"author": "",
|
|
15
|
-
"license": "ISC",
|
|
16
|
-
"dependencies": {
|
|
17
|
-
"cors": "^2.8.5",
|
|
18
|
-
"mongoose": "^8.3.1",
|
|
19
|
-
"redis": "^4.7.0",
|
|
20
|
-
"te.js": "file:..",
|
|
21
|
-
"tej-env": "^1.0.2"
|
|
22
|
-
}
|
|
23
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cache service - Redis wrapper for key-value storage.
|
|
3
|
-
* Requires Redis connection via app.takeoff({ withRedis: { url } }).
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import dbManager from 'te.js/database/index.js';
|
|
7
|
-
|
|
8
|
-
export function isAvailable() {
|
|
9
|
-
const { exists } = dbManager.hasConnection('redis', {});
|
|
10
|
-
return exists;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export async function get(key) {
|
|
14
|
-
const redis = dbManager.getConnection('redis');
|
|
15
|
-
return redis.get(key);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export async function set(key, value, ttlSeconds) {
|
|
19
|
-
const redis = dbManager.getConnection('redis');
|
|
20
|
-
if (ttlSeconds) {
|
|
21
|
-
await redis.setEx(key, ttlSeconds, String(value));
|
|
22
|
-
} else {
|
|
23
|
-
await redis.set(key, String(value));
|
|
24
|
-
}
|
|
25
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* User service - in-memory CRUD for demo purposes.
|
|
3
|
-
* Demonstrates business logic extraction from route handlers.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
const store = [];
|
|
7
|
-
let nextId = 1;
|
|
8
|
-
|
|
9
|
-
export default {
|
|
10
|
-
list() {
|
|
11
|
-
return [...store];
|
|
12
|
-
},
|
|
13
|
-
|
|
14
|
-
getById(id) {
|
|
15
|
-
return store.find((u) => String(u.id) === String(id));
|
|
16
|
-
},
|
|
17
|
-
|
|
18
|
-
create({ name, email }) {
|
|
19
|
-
if (!name || !email) {
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
const user = { id: nextId++, name, email, createdAt: new Date().toISOString() };
|
|
23
|
-
store.push(user);
|
|
24
|
-
return user;
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
update(id, { name, email }) {
|
|
28
|
-
const user = this.getById(id);
|
|
29
|
-
if (!user) return null;
|
|
30
|
-
if (name !== undefined) user.name = name;
|
|
31
|
-
if (email !== undefined) user.email = email;
|
|
32
|
-
user.updatedAt = new Date().toISOString();
|
|
33
|
-
return user;
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
delete(id) {
|
|
37
|
-
const index = store.findIndex((u) => String(u.id) === String(id));
|
|
38
|
-
if (index === -1) return false;
|
|
39
|
-
store.splice(index, 1);
|
|
40
|
-
return true;
|
|
41
|
-
},
|
|
42
|
-
};
|
package/example/start-redis.js
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { Target, TejError } from 'te.js';
|
|
2
|
-
import * as cacheService from '../services/cache.service.js';
|
|
3
|
-
|
|
4
|
-
const cache = new Target('/cache');
|
|
5
|
-
|
|
6
|
-
// Middleware: require Redis to be available
|
|
7
|
-
cache.midair((ammo, next) => {
|
|
8
|
-
if (!cacheService.isAvailable()) {
|
|
9
|
-
throw new TejError(503, 'Cache service unavailable. Set REDIS_URL to enable Redis.');
|
|
10
|
-
}
|
|
11
|
-
next();
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
cache.register('/:key', async (ammo) => {
|
|
15
|
-
if (!ammo.GET) return ammo.notAllowed();
|
|
16
|
-
|
|
17
|
-
const { key } = ammo.payload;
|
|
18
|
-
const value = await cacheService.get(key);
|
|
19
|
-
|
|
20
|
-
if (value === null) return ammo.notFound();
|
|
21
|
-
|
|
22
|
-
ammo.fire({ key, value });
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
cache.register('/', async (ammo) => {
|
|
26
|
-
if (!ammo.POST) return ammo.notAllowed();
|
|
27
|
-
|
|
28
|
-
const { key, value, ttl } = ammo.payload;
|
|
29
|
-
if (!key || value === undefined) {
|
|
30
|
-
throw new TejError(400, 'key and value are required');
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
await cacheService.set(key, value, ttl ? parseInt(ttl, 10) : undefined);
|
|
34
|
-
ammo.fire(201, { message: 'Cached successfully', key });
|
|
35
|
-
});
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { Target, listAllEndpoints } from 'te.js';
|
|
2
|
-
|
|
3
|
-
const target = new Target();
|
|
4
|
-
|
|
5
|
-
target.register('/', (ammo) => {
|
|
6
|
-
ammo.defaultEntry();
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
target.register('/health', (ammo) => {
|
|
10
|
-
ammo.fire({ status: 'ok', timestamp: new Date().toISOString() });
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
target.register('/routes', (ammo) => {
|
|
14
|
-
const grouped = listAllEndpoints(true);
|
|
15
|
-
ammo.fire(grouped);
|
|
16
|
-
});
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { Target, TejError, TejFileUploader } from 'te.js';
|
|
2
|
-
import userService from '../services/user.service.js';
|
|
3
|
-
|
|
4
|
-
const users = new Target('/users');
|
|
5
|
-
|
|
6
|
-
const uploader = new TejFileUploader({
|
|
7
|
-
destination: 'public/uploads',
|
|
8
|
-
maxFileSize: 5 * 1024 * 1024, // 5MB
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
users.register('/', (ammo) => {
|
|
12
|
-
if (ammo.GET) {
|
|
13
|
-
return ammo.fire(userService.list());
|
|
14
|
-
}
|
|
15
|
-
if (ammo.POST) {
|
|
16
|
-
const user = userService.create(ammo.payload);
|
|
17
|
-
if (!user) {
|
|
18
|
-
throw new TejError(400, 'name and email are required');
|
|
19
|
-
}
|
|
20
|
-
return ammo.fire(201, user);
|
|
21
|
-
}
|
|
22
|
-
ammo.notAllowed();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
users.register('/:id', (ammo) => {
|
|
26
|
-
const { id } = ammo.payload;
|
|
27
|
-
const user = userService.getById(id);
|
|
28
|
-
|
|
29
|
-
if (ammo.GET) {
|
|
30
|
-
if (!user) return ammo.notFound();
|
|
31
|
-
return ammo.fire(user);
|
|
32
|
-
}
|
|
33
|
-
if (ammo.PUT) {
|
|
34
|
-
if (!user) return ammo.notFound();
|
|
35
|
-
const updated = userService.update(id, ammo.payload);
|
|
36
|
-
return ammo.fire(updated);
|
|
37
|
-
}
|
|
38
|
-
if (ammo.DELETE) {
|
|
39
|
-
if (!userService.delete(id)) return ammo.notFound();
|
|
40
|
-
return ammo.fire(204);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
ammo.notAllowed();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
users.register('/:id/updateProfileImage', uploader.file('image'), (ammo) => {
|
|
47
|
-
const { image } = ammo.payload;
|
|
48
|
-
ammo.fire(200, {
|
|
49
|
-
message: 'Profile image updated successfully!',
|
|
50
|
-
file: image,
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
users.register('/:id/uploadDocuments', uploader.files('documents'), (ammo) => {
|
|
55
|
-
const { documents } = ammo.payload;
|
|
56
|
-
ammo.fire(200, {
|
|
57
|
-
message: `${documents?.length ?? 0} document(s) uploaded`,
|
|
58
|
-
files: documents,
|
|
59
|
-
});
|
|
60
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"port": 1403,
|
|
3
|
-
"log": {
|
|
4
|
-
"http_requests": true,
|
|
5
|
-
"exceptions": true
|
|
6
|
-
},
|
|
7
|
-
"dir": {
|
|
8
|
-
"targets": "targets"
|
|
9
|
-
},
|
|
10
|
-
"body": {
|
|
11
|
-
"max_size": 5242880,
|
|
12
|
-
"timeout": 15000
|
|
13
|
-
},
|
|
14
|
-
"docs": {
|
|
15
|
-
"dirTargets": "targets",
|
|
16
|
-
"output": "./openapi.json",
|
|
17
|
-
"title": "Example API",
|
|
18
|
-
"version": "1.0.0",
|
|
19
|
-
"level": 1,
|
|
20
|
-
"productionBranch": "main"
|
|
21
|
-
}
|
|
22
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unit tests for auto-docs handler-analyzer (detectMethods, analyzeHandler).
|
|
3
|
-
*/
|
|
4
|
-
import { describe, it, expect } from 'vitest';
|
|
5
|
-
import { detectMethods, analyzeHandler, ALL_METHODS } from '../../auto-docs/analysis/handler-analyzer.js';
|
|
6
|
-
|
|
7
|
-
describe('handler-analyzer', () => {
|
|
8
|
-
describe('detectMethods', () => {
|
|
9
|
-
it('returns all methods when handler is not a function', () => {
|
|
10
|
-
expect(detectMethods(null)).toEqual(ALL_METHODS);
|
|
11
|
-
expect(detectMethods(undefined)).toEqual(ALL_METHODS);
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('detects GET when handler uses ammo.GET', () => {
|
|
15
|
-
const handler = (ammo) => { if (ammo.GET) ammo.fire(200, {}); };
|
|
16
|
-
expect(detectMethods(handler)).toContain('GET');
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('detects POST and GET when handler checks both', () => {
|
|
20
|
-
const handler = (ammo) => {
|
|
21
|
-
if (ammo.GET) ammo.fire(200, {});
|
|
22
|
-
if (ammo.POST) ammo.fire(201, {});
|
|
23
|
-
};
|
|
24
|
-
const detected = detectMethods(handler);
|
|
25
|
-
expect(detected).toContain('GET');
|
|
26
|
-
expect(detected).toContain('POST');
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it('returns all methods when no method checks found (method-agnostic)', () => {
|
|
30
|
-
const handler = () => {};
|
|
31
|
-
expect(detectMethods(handler)).toEqual(ALL_METHODS);
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
describe('analyzeHandler', () => {
|
|
36
|
-
it('returns object with methods array', () => {
|
|
37
|
-
const handler = (ammo) => { if (ammo.GET) ammo.fire(200); };
|
|
38
|
-
const result = analyzeHandler(handler);
|
|
39
|
-
expect(result).toHaveProperty('methods');
|
|
40
|
-
expect(Array.isArray(result.methods)).toBe(true);
|
|
41
|
-
expect(result.methods).toContain('GET');
|
|
42
|
-
});
|
|
43
|
-
});
|
|
44
|
-
});
|