te.js 1.3.1 → 2.0.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 (80) hide show
  1. package/.cursor/plans/ai_native_framework_features_5bb1a20a.plan.md +234 -0
  2. package/.cursor/plans/auto_error_fix_agent_e68979c5.plan.md +356 -0
  3. package/.cursor/plans/tejas_framework_test_suite_5e3c6fad.plan.md +168 -0
  4. package/.prettierignore +31 -0
  5. package/README.md +181 -14
  6. package/auto-docs/analysis/handler-analyzer.js +58 -0
  7. package/auto-docs/analysis/source-resolver.js +101 -0
  8. package/auto-docs/constants.js +37 -0
  9. package/auto-docs/index.js +146 -0
  10. package/auto-docs/llm/index.js +6 -0
  11. package/auto-docs/llm/parse.js +88 -0
  12. package/auto-docs/llm/prompts.js +222 -0
  13. package/auto-docs/llm/provider.js +187 -0
  14. package/auto-docs/openapi/endpoint-processor.js +277 -0
  15. package/auto-docs/openapi/generator.js +107 -0
  16. package/auto-docs/openapi/level3.js +131 -0
  17. package/auto-docs/openapi/spec-builders.js +244 -0
  18. package/auto-docs/ui/docs-ui.js +186 -0
  19. package/auto-docs/utils/logger.js +17 -0
  20. package/auto-docs/utils/strip-usage.js +10 -0
  21. package/cli/docs-command.js +315 -0
  22. package/cli/fly-command.js +71 -0
  23. package/cli/index.js +57 -0
  24. package/database/index.js +163 -5
  25. package/database/mongodb.js +146 -0
  26. package/database/redis.js +201 -0
  27. package/docs/README.md +36 -0
  28. package/docs/ammo.md +362 -0
  29. package/docs/api-reference.md +489 -0
  30. package/docs/auto-docs.md +215 -0
  31. package/docs/cli.md +152 -0
  32. package/docs/configuration.md +233 -0
  33. package/docs/database.md +391 -0
  34. package/docs/error-handling.md +417 -0
  35. package/docs/file-uploads.md +334 -0
  36. package/docs/getting-started.md +215 -0
  37. package/docs/middleware.md +356 -0
  38. package/docs/rate-limiting.md +394 -0
  39. package/docs/routing.md +302 -0
  40. package/example/API_OVERVIEW.md +77 -0
  41. package/example/README.md +155 -0
  42. package/example/index.js +27 -2
  43. package/example/openapi.json +390 -0
  44. package/example/package.json +5 -2
  45. package/example/services/cache.service.js +25 -0
  46. package/example/services/user.service.js +42 -0
  47. package/example/start-redis.js +2 -0
  48. package/example/targets/cache.target.js +35 -0
  49. package/example/targets/index.target.js +11 -2
  50. package/example/targets/users.target.js +60 -0
  51. package/example/tejas.config.json +13 -1
  52. package/package.json +20 -5
  53. package/rate-limit/algorithms/fixed-window.js +141 -0
  54. package/rate-limit/algorithms/sliding-window.js +147 -0
  55. package/rate-limit/algorithms/token-bucket.js +115 -0
  56. package/rate-limit/base.js +165 -0
  57. package/rate-limit/index.js +147 -0
  58. package/rate-limit/storage/base.js +104 -0
  59. package/rate-limit/storage/memory.js +102 -0
  60. package/rate-limit/storage/redis.js +88 -0
  61. package/server/ammo/body-parser.js +152 -25
  62. package/server/ammo/enhancer.js +6 -2
  63. package/server/ammo.js +356 -327
  64. package/server/endpoint.js +21 -0
  65. package/server/handler.js +113 -87
  66. package/server/target.js +50 -9
  67. package/server/targets/registry.js +160 -57
  68. package/te.js +363 -137
  69. package/tests/auto-docs/handler-analyzer.test.js +44 -0
  70. package/tests/auto-docs/openapi-generator.test.js +103 -0
  71. package/tests/auto-docs/parse.test.js +63 -0
  72. package/tests/auto-docs/source-resolver.test.js +58 -0
  73. package/tests/helpers/index.js +37 -0
  74. package/tests/helpers/mock-http.js +342 -0
  75. package/tests/helpers/test-utils.js +446 -0
  76. package/tests/setup.test.js +148 -0
  77. package/utils/configuration.js +13 -10
  78. package/vitest.config.js +54 -0
  79. package/database/mongo.js +0 -67
  80. package/example/targets/user/user.target.js +0 -17
@@ -0,0 +1,334 @@
1
+ # File Uploads
2
+
3
+ Tejas provides a built-in `TejFileUploader` class for handling file uploads with ease.
4
+
5
+ ## Quick Start
6
+
7
+ ```javascript
8
+ import { Target, TejFileUploader } from 'te.js';
9
+
10
+ const upload = new TejFileUploader({
11
+ destination: 'uploads/',
12
+ maxFileSize: 5 * 1024 * 1024 // 5MB
13
+ });
14
+
15
+ const target = new Target('/files');
16
+
17
+ target.register('/upload', upload.file('avatar'), (ammo) => {
18
+ ammo.fire({ file: ammo.payload.avatar });
19
+ });
20
+ ```
21
+
22
+ ## Configuration
23
+
24
+ ```javascript
25
+ const upload = new TejFileUploader({
26
+ destination: 'public/uploads', // Where to save files
27
+ name: 'custom-name', // Optional: custom filename
28
+ maxFileSize: 10 * 1024 * 1024 // Max file size in bytes (10MB)
29
+ });
30
+ ```
31
+
32
+ ### Options
33
+
34
+ | Option | Type | Description |
35
+ |--------|------|-------------|
36
+ | `destination` | string | Directory to save uploaded files |
37
+ | `name` | string | Optional custom filename |
38
+ | `maxFileSize` | number | Maximum file size in bytes |
39
+
40
+ ## Single File Upload
41
+
42
+ Use `upload.file()` for single file uploads:
43
+
44
+ ```javascript
45
+ // Expects a file field named 'avatar'
46
+ target.register('/avatar', upload.file('avatar'), (ammo) => {
47
+ const file = ammo.payload.avatar;
48
+
49
+ ammo.fire({
50
+ filename: file.filename,
51
+ path: file.path.relative,
52
+ mimetype: file.mimetype,
53
+ size: file.size
54
+ });
55
+ });
56
+ ```
57
+
58
+ ### File Object Structure
59
+
60
+ When a file is uploaded, `ammo.payload[fieldName]` contains:
61
+
62
+ ```javascript
63
+ {
64
+ filename: 'photo.jpg', // Original filename
65
+ extension: 'jpg', // File extension
66
+ path: {
67
+ absolute: '/var/www/uploads/photo.jpg', // Absolute path on disk
68
+ relative: '\\uploads\\photo.jpg' // Relative to cwd
69
+ },
70
+ mimetype: 'image/jpeg', // MIME type
71
+ size: { // From the filesize library
72
+ value: 245, // Numeric value
73
+ symbol: 'KB' // Unit (B, KB, MB, etc.)
74
+ }
75
+ }
76
+ ```
77
+
78
+ The `size` object is produced by the [filesize](https://www.npmjs.com/package/filesize) library. Use `${size.value} ${size.symbol}` for display (e.g. "245 KB").
79
+
80
+ ## Multiple File Upload
81
+
82
+ Use `upload.files()` for multiple files:
83
+
84
+ ```javascript
85
+ // Expects files in 'photos' and 'documents' fields
86
+ target.register('/documents', upload.files('photos', 'documents'), (ammo) => {
87
+ const { photos, documents } = ammo.payload;
88
+
89
+ ammo.fire({
90
+ photos: photos || [], // Array of file objects
91
+ documents: documents || [] // Array of file objects
92
+ });
93
+ });
94
+ ```
95
+
96
+ ### Multiple Files Response
97
+
98
+ Each field contains an array of file objects:
99
+
100
+ ```javascript
101
+ {
102
+ photos: [
103
+ { filename: 'photo1.jpg', path: {...}, mimetype: 'image/jpeg', size: {...} },
104
+ { filename: 'photo2.jpg', path: {...}, mimetype: 'image/jpeg', size: {...} }
105
+ ],
106
+ documents: [
107
+ { filename: 'doc.pdf', path: {...}, mimetype: 'application/pdf', size: {...} }
108
+ ]
109
+ }
110
+ ```
111
+
112
+ ## Mixed Fields (Files + Data)
113
+
114
+ File uploads can include regular form fields:
115
+
116
+ ```javascript
117
+ target.register('/profile', upload.file('avatar'), (ammo) => {
118
+ const { avatar, name, bio } = ammo.payload;
119
+
120
+ ammo.fire({
121
+ name, // Regular form field
122
+ bio, // Regular form field
123
+ avatar: avatar // File object
124
+ });
125
+ });
126
+ ```
127
+
128
+ ## File Size Limits
129
+
130
+ When a file exceeds `maxFileSize`, a `413 Payload Too Large` error is thrown automatically. The error message includes the human-readable limit (e.g. "File size exceeds 2 MB"):
131
+
132
+ ```javascript
133
+ const upload = new TejFileUploader({
134
+ destination: 'uploads/',
135
+ maxFileSize: 2 * 1024 * 1024 // 2MB limit
136
+ });
137
+
138
+ target.register('/upload', upload.file('file'), (ammo) => {
139
+ // If file > 2MB, this handler never runs
140
+ // Client receives: 413 "File size exceeds 2 MB"
141
+ ammo.fire({ success: true });
142
+ });
143
+ ```
144
+
145
+ Note that the overall request body is also subject to the global `body.max_size` limit (default 10 MB). See [Configuration](./configuration.md).
146
+
147
+ ## Client-Side Examples
148
+
149
+ ### HTML Form
150
+
151
+ ```html
152
+ <form action="/files/upload" method="POST" enctype="multipart/form-data">
153
+ <input type="file" name="avatar" />
154
+ <input type="text" name="username" />
155
+ <button type="submit">Upload</button>
156
+ </form>
157
+ ```
158
+
159
+ ### JavaScript (Fetch)
160
+
161
+ ```javascript
162
+ const formData = new FormData();
163
+ formData.append('avatar', fileInput.files[0]);
164
+ formData.append('username', 'john');
165
+
166
+ const response = await fetch('/files/upload', {
167
+ method: 'POST',
168
+ body: formData
169
+ });
170
+ ```
171
+
172
+ ### JavaScript (Multiple Files)
173
+
174
+ ```javascript
175
+ const formData = new FormData();
176
+
177
+ // Add multiple files to same field
178
+ for (const file of fileInput.files) {
179
+ formData.append('photos', file);
180
+ }
181
+
182
+ const response = await fetch('/files/documents', {
183
+ method: 'POST',
184
+ body: formData
185
+ });
186
+ ```
187
+
188
+ ## Complete Example
189
+
190
+ ```javascript
191
+ import { Target, TejFileUploader, TejError } from 'te.js';
192
+ import fs from 'fs';
193
+ import path from 'path';
194
+
195
+ const target = new Target('/api/files');
196
+
197
+ // Configure uploader
198
+ const imageUpload = new TejFileUploader({
199
+ destination: 'public/images',
200
+ maxFileSize: 5 * 1024 * 1024 // 5MB
201
+ });
202
+
203
+ const documentUpload = new TejFileUploader({
204
+ destination: 'private/documents',
205
+ maxFileSize: 20 * 1024 * 1024 // 20MB
206
+ });
207
+
208
+ // Upload profile image
209
+ target.register('/profile-image', imageUpload.file('image'), (ammo) => {
210
+ if (!ammo.POST) return ammo.notAllowed();
211
+
212
+ const { image } = ammo.payload;
213
+
214
+ if (!image) {
215
+ throw new TejError(400, 'No image provided');
216
+ }
217
+
218
+ // Validate image type
219
+ if (!image.mimetype.startsWith('image/')) {
220
+ // Delete uploaded file
221
+ fs.unlinkSync(image.path.absolute);
222
+ throw new TejError(400, 'File must be an image');
223
+ }
224
+
225
+ ammo.fire({
226
+ message: 'Profile image uploaded',
227
+ url: `/images/${image.filename}`
228
+ });
229
+ });
230
+
231
+ // Upload multiple documents
232
+ target.register('/documents', documentUpload.files('files'), (ammo) => {
233
+ if (!ammo.POST) return ammo.notAllowed();
234
+
235
+ const { files } = ammo.payload;
236
+
237
+ if (!files || files.length === 0) {
238
+ throw new TejError(400, 'No files provided');
239
+ }
240
+
241
+ ammo.fire({
242
+ message: `${files.length} files uploaded`,
243
+ files: files.map(f => ({
244
+ name: f.filename,
245
+ size: `${f.size.value} ${f.size.symbol}`
246
+ }))
247
+ });
248
+ });
249
+
250
+ // Delete a file
251
+ target.register('/delete/:filename', (ammo) => {
252
+ if (!ammo.DELETE) return ammo.notAllowed();
253
+
254
+ const { filename } = ammo.payload;
255
+ const filepath = path.join('public/images', filename);
256
+
257
+ if (!fs.existsSync(filepath)) {
258
+ throw new TejError(404, 'File not found');
259
+ }
260
+
261
+ fs.unlinkSync(filepath);
262
+ ammo.fire({ message: 'File deleted' });
263
+ });
264
+ ```
265
+
266
+ ## Validation Middleware
267
+
268
+ Create reusable validation middleware:
269
+
270
+ ```javascript
271
+ // middleware/validate-image.js
272
+ import { TejError } from 'te.js';
273
+ import fs from 'fs';
274
+
275
+ const allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
276
+
277
+ export const validateImage = (fieldName) => (ammo, next) => {
278
+ const file = ammo.payload[fieldName];
279
+
280
+ if (!file) {
281
+ throw new TejError(400, `${fieldName} is required`);
282
+ }
283
+
284
+ if (!allowedTypes.includes(file.mimetype)) {
285
+ fs.unlinkSync(file.path.absolute);
286
+ throw new TejError(400, 'Only JPEG, PNG, GIF, and WebP images are allowed');
287
+ }
288
+
289
+ next();
290
+ };
291
+
292
+ // Usage
293
+ target.register('/avatar',
294
+ upload.file('avatar'),
295
+ validateImage('avatar'),
296
+ (ammo) => {
297
+ ammo.fire({ success: true });
298
+ }
299
+ );
300
+ ```
301
+
302
+ ## Serving Uploaded Files
303
+
304
+ Tejas doesn't include a static file server, but you can serve files manually:
305
+
306
+ ```javascript
307
+ import fs from 'fs';
308
+ import path from 'path';
309
+ import mime from 'mime';
310
+
311
+ target.register('/images/:filename', (ammo) => {
312
+ const { filename } = ammo.payload;
313
+ const filepath = path.join('public/images', filename);
314
+
315
+ if (!fs.existsSync(filepath)) {
316
+ return ammo.notFound();
317
+ }
318
+
319
+ const file = fs.readFileSync(filepath);
320
+ const contentType = mime.getType(filepath) || 'application/octet-stream';
321
+
322
+ ammo.fire(200, file, contentType);
323
+ });
324
+ ```
325
+
326
+ ## Best Practices
327
+
328
+ 1. **Validate file types** — Don't trust client-reported MIME types
329
+ 2. **Set size limits** — Prevent disk exhaustion attacks
330
+ 3. **Use unique filenames** — Avoid overwrites with UUID or timestamps
331
+ 4. **Store outside web root** — For sensitive files, store in private directories
332
+ 5. **Clean up on errors** — Delete uploaded files if validation fails
333
+ 6. **Scan for malware** — For production systems, integrate virus scanning
334
+
@@ -0,0 +1,215 @@
1
+ # Getting Started with Tejas
2
+
3
+ Tejas is a lightweight Node.js framework for building powerful backend services. It features an intuitive API with aviation-inspired naming conventions.
4
+
5
+ ## Why Tejas?
6
+
7
+ - **AI-Native** — MCP server gives your AI assistant full framework knowledge for correct code generation
8
+ - **Zero-Config Error Handling** — No try-catch needed! Tejas catches all errors automatically
9
+ - **Clean, Readable Code** — Aviation-inspired naming makes code self-documenting
10
+ - **Express Compatible** — Use your existing Express middleware
11
+ - **Built-in Features** — Rate limiting, file uploads, database connections out of the box
12
+
13
+ ## AI-Assisted Setup (MCP) — Recommended
14
+
15
+ The fastest way to start building with Tejas is through your AI assistant. The **Tejas MCP server** (`tejas-mcp`) gives AI tools full access to framework documentation, validated code examples, and purpose-built tools that scaffold projects and generate correct te.js code.
16
+
17
+ ### Setup
18
+
19
+ **Cursor** — create or edit `.cursor/mcp.json` in your workspace:
20
+
21
+ ```json
22
+ {
23
+ "mcpServers": {
24
+ "tejas": {
25
+ "command": "npx",
26
+ "args": ["-y", "tejas-mcp"]
27
+ }
28
+ }
29
+ }
30
+ ```
31
+
32
+ **Other MCP-compatible IDEs** — run `npx tejas-mcp` as the stdio server command. No API keys required.
33
+
34
+ ### What you can do
35
+
36
+ Once connected, prompt your assistant naturally:
37
+
38
+ - *"Scaffold a new te.js project called my-api on port 5000"*
39
+ - *"Create a REST API with user CRUD routes using te.js"*
40
+ - *"Add a /health endpoint that returns system uptime"*
41
+
42
+ The MCP server provides these tools: `scaffold_project`, `generate_target`, `generate_app_entry`, `generate_config`, `get_documentation`, and `search_docs`.
43
+
44
+ ---
45
+
46
+ ## Prerequisites
47
+
48
+ - Node.js 18.x or higher
49
+ - npm or yarn
50
+
51
+ ## Installation
52
+
53
+ ```bash
54
+ npm install te.js
55
+ ```
56
+
57
+ ## Quick Start
58
+
59
+ ### 1. Create Your Application
60
+
61
+ Create an `index.js` file:
62
+
63
+ ```javascript
64
+ import Tejas from 'te.js';
65
+
66
+ const app = new Tejas();
67
+
68
+ app.takeoff();
69
+ ```
70
+
71
+ ### 2. Create Your First Route
72
+
73
+ Create a `targets` directory and add `hello.target.js`:
74
+
75
+ ```javascript
76
+ import { Target } from 'te.js';
77
+
78
+ const target = new Target('/hello');
79
+
80
+ target.register('/', (ammo) => {
81
+ ammo.fire({ message: 'Hello, World!' });
82
+ });
83
+
84
+ target.register('/greet/:name', (ammo) => {
85
+ const { name } = ammo.payload;
86
+ ammo.fire({ message: `Hello, ${name}!` });
87
+ });
88
+ ```
89
+
90
+ ### 3. Run Your Application
91
+
92
+ ```bash
93
+ node index.js
94
+ ```
95
+
96
+ Your server is now running on `http://localhost:1403`
97
+
98
+ ## Core Concepts
99
+
100
+ ### Terminology
101
+
102
+ Tejas uses aviation-inspired naming:
103
+
104
+ | Term | Express Equivalent | Description |
105
+ |------|-------------------|-------------|
106
+ | `Tejas` | `express()` | Main application instance |
107
+ | `Target` | `Router` | Route grouping |
108
+ | `Ammo` | `req` + `res` | Request/response wrapper |
109
+ | `fire()` | `res.send()` | Send response |
110
+ | `throw()` | Error response | Send error |
111
+ | `midair()` | `use()` | Register middleware |
112
+ | `takeoff()` | `listen()` | Start server |
113
+
114
+ ### Basic Structure
115
+
116
+ ```
117
+ my-app/
118
+ ├── index.js # Application entry point
119
+ ├── tejas.config.json # Optional configuration
120
+ ├── .env # Environment variables
121
+ ├── targets/ # Route definitions (auto-discovered)
122
+ │ ├── user.target.js
123
+ │ ├── auth.target.js
124
+ │ └── api/
125
+ │ └── v1.target.js
126
+ ├── services/ # Business logic
127
+ │ └── user.service.js
128
+ └── middleware/ # Custom middleware
129
+ └── auth.js
130
+ ```
131
+
132
+ ## Automatic Error Handling
133
+
134
+ One of Tejas's most powerful features is that **you don't need to write any error handling code**. The framework catches all errors automatically:
135
+
136
+ ```javascript
137
+ // ✅ No try-catch needed — if anything throws, Tejas handles it
138
+ target.register('/data', async (ammo) => {
139
+ const data = await riskyDatabaseCall();
140
+ const processed = await anotherAsyncOperation(data);
141
+ ammo.fire(processed);
142
+ });
143
+ ```
144
+
145
+ Your application never crashes from unhandled exceptions, and clients always receive proper error responses. Learn more in [Error Handling](./error-handling.md).
146
+
147
+ ## Next Steps
148
+
149
+ - [Configuration](./configuration.md) — All configuration options and sources
150
+ - [Routing](./routing.md) — Deep dive into the Target-based routing system
151
+ - [Ammo](./ammo.md) — Master request/response handling
152
+ - [Middleware](./middleware.md) — Global, target, and route-level middleware
153
+ - [Database](./database.md) — Connect to MongoDB or Redis
154
+ - [Error Handling](./error-handling.md) — Zero-config error handling
155
+ - [CLI Reference](./cli.md) — `tejas fly` and doc generation commands
156
+ - [Auto-Documentation](./auto-docs.md) — Generate OpenAPI specs from your code
157
+
158
+ ## Example Application
159
+
160
+ Here's a more complete example:
161
+
162
+ ```javascript
163
+ import Tejas from 'te.js';
164
+
165
+ const app = new Tejas({
166
+ port: 3000,
167
+ log: {
168
+ http_requests: true,
169
+ exceptions: true
170
+ }
171
+ });
172
+
173
+ // Global middleware
174
+ app.midair((ammo, next) => {
175
+ console.log(`${ammo.method} ${ammo.path}`);
176
+ next();
177
+ });
178
+
179
+ // Rate limiting (in-memory)
180
+ app.withRateLimit({
181
+ maxRequests: 100,
182
+ timeWindowSeconds: 60
183
+ });
184
+
185
+ // Start with optional Redis
186
+ app.takeoff({
187
+ withRedis: { url: 'redis://localhost:6379' }
188
+ });
189
+ ```
190
+
191
+ ```javascript
192
+ // targets/api.target.js
193
+ import { Target } from 'te.js';
194
+
195
+ const api = new Target('/api');
196
+
197
+ // GET /api/status
198
+ api.register('/status', (ammo) => {
199
+ if (ammo.GET) {
200
+ ammo.fire({ status: 'operational', timestamp: Date.now() });
201
+ } else {
202
+ ammo.notAllowed();
203
+ }
204
+ });
205
+
206
+ // POST /api/echo
207
+ api.register('/echo', (ammo) => {
208
+ if (ammo.POST) {
209
+ ammo.fire(ammo.payload);
210
+ } else {
211
+ ammo.notAllowed();
212
+ }
213
+ });
214
+ ```
215
+