te.js 2.1.0 → 2.1.2
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 -196
- 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 -7
- package/auto-docs/docs-llm/prompts.js +222 -222
- package/auto-docs/docs-llm/provider.js +132 -132
- 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/cors/index.js +71 -0
- 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 -490
- package/docs/auto-docs.md +216 -216
- package/docs/cli.md +152 -152
- package/docs/configuration.md +275 -275
- package/docs/database.md +390 -390
- package/docs/error-handling.md +438 -438
- package/docs/file-uploads.md +333 -333
- package/docs/getting-started.md +214 -214
- package/docs/middleware.md +355 -355
- package/docs/rate-limiting.md +393 -393
- package/docs/routing.md +302 -302
- package/lib/llm/client.js +73 -0
- package/lib/llm/index.js +7 -0
- package/lib/llm/parse.js +89 -0
- package/package.json +64 -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 -415
- package/server/endpoint.js +97 -74
- package/server/error.js +9 -9
- package/server/errors/code-context.js +125 -125
- package/server/errors/llm-error-service.js +140 -140
- package/server/files/helper.js +33 -33
- package/server/files/uploader.js +143 -143
- package/server/handler.js +158 -119
- 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 -402
- package/utils/auto-register.js +17 -17
- package/utils/configuration.js +64 -64
- package/utils/errors-llm-config.js +84 -84
- package/utils/request-logger.js +43 -43
- package/utils/status-codes.js +82 -82
- package/utils/tejas-entrypoint-html.js +18 -18
|
@@ -1,220 +1,220 @@
|
|
|
1
|
-
import { env } from 'tej-env';
|
|
2
|
-
import TejError from '../error.js';
|
|
3
|
-
|
|
4
|
-
async function parseDataBasedOnContentType(req) {
|
|
5
|
-
// If no content-type header, return empty object (for requests without body)
|
|
6
|
-
if (!req.headers['content-type']) {
|
|
7
|
-
return {};
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const contentType = req.headers['content-type'].toLowerCase();
|
|
11
|
-
|
|
12
|
-
// Check if content type is JSON
|
|
13
|
-
if (contentType === 'application/json') {
|
|
14
|
-
return await parseJSONRequestBody(req);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Check if content type is URL encoded
|
|
18
|
-
if (contentType === 'application/x-www-form-urlencoded') {
|
|
19
|
-
return await parseUrlEncodedData(req);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Check if content type is multipart form data
|
|
23
|
-
if (contentType.startsWith('multipart/form-data')) {
|
|
24
|
-
return await parseFormData(req);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
throw new BodyParserError(`Unsupported content type: ${contentType}`, 415);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function parseJSONRequestBody(req) {
|
|
31
|
-
return new Promise((resolve, reject) => {
|
|
32
|
-
let body = '';
|
|
33
|
-
let size = 0;
|
|
34
|
-
const maxSize = env('BODY_MAX_SIZE');
|
|
35
|
-
const timeout = setTimeout(() => {
|
|
36
|
-
reject(new BodyParserError('Request timeout', 408));
|
|
37
|
-
}, env('BODY_TIMEOUT'));
|
|
38
|
-
|
|
39
|
-
req.on('data', (chunk) => {
|
|
40
|
-
size += chunk.length;
|
|
41
|
-
if (size > maxSize) {
|
|
42
|
-
clearTimeout(timeout);
|
|
43
|
-
reject(new BodyParserError('Request entity too large', 413));
|
|
44
|
-
req.destroy();
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
body += chunk.toString();
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
req.on('error', (err) => {
|
|
51
|
-
clearTimeout(timeout);
|
|
52
|
-
reject(new BodyParserError(`Request error: ${err.message}`, 400));
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
req.on('end', () => {
|
|
56
|
-
clearTimeout(timeout);
|
|
57
|
-
try {
|
|
58
|
-
if (!body) {
|
|
59
|
-
resolve({});
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
const jsonData = JSON.parse(body);
|
|
63
|
-
if (typeof jsonData !== 'object') {
|
|
64
|
-
throw new TejError(400, 'Invalid JSON structure');
|
|
65
|
-
}
|
|
66
|
-
resolve(jsonData);
|
|
67
|
-
} catch (err) {
|
|
68
|
-
reject(new TejError(400, `Invalid JSON: ${err.message}`));
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function parseUrlEncodedData(req) {
|
|
75
|
-
return new Promise((resolve, reject) => {
|
|
76
|
-
let body = '';
|
|
77
|
-
let size = 0;
|
|
78
|
-
const maxSize = env('BODY_MAX_SIZE');
|
|
79
|
-
const timeout = setTimeout(() => {
|
|
80
|
-
reject(new BodyParserError('Request timeout', 408));
|
|
81
|
-
}, env('BODY_TIMEOUT'));
|
|
82
|
-
|
|
83
|
-
req.on('data', (chunk) => {
|
|
84
|
-
size += chunk.length;
|
|
85
|
-
if (size > maxSize) {
|
|
86
|
-
clearTimeout(timeout);
|
|
87
|
-
reject(new BodyParserError('Request entity too large', 413));
|
|
88
|
-
req.destroy();
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
body += chunk.toString();
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
req.on('error', (err) => {
|
|
95
|
-
clearTimeout(timeout);
|
|
96
|
-
reject(new BodyParserError(`Request error: ${err.message}`, 400));
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
req.on('end', () => {
|
|
100
|
-
clearTimeout(timeout);
|
|
101
|
-
try {
|
|
102
|
-
if (!body) {
|
|
103
|
-
resolve({});
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
const data = new URLSearchParams(body);
|
|
107
|
-
const parsedData = Object.fromEntries(data);
|
|
108
|
-
resolve(parsedData);
|
|
109
|
-
} catch (err) {
|
|
110
|
-
reject(new BodyParserError('Invalid URL encoded data', 400));
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function parseFormData(req) {
|
|
117
|
-
return new Promise((resolve, reject) => {
|
|
118
|
-
let body = '';
|
|
119
|
-
let size = 0;
|
|
120
|
-
const maxSize = env('BODY_MAX_SIZE');
|
|
121
|
-
const timeout = setTimeout(() => {
|
|
122
|
-
reject(new BodyParserError('Request timeout', 408));
|
|
123
|
-
}, env('BODY_TIMEOUT'));
|
|
124
|
-
|
|
125
|
-
req.on('data', (chunk) => {
|
|
126
|
-
size += chunk.length;
|
|
127
|
-
if (size > maxSize) {
|
|
128
|
-
clearTimeout(timeout);
|
|
129
|
-
reject(new BodyParserError('Request entity too large', 413));
|
|
130
|
-
req.destroy();
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
body += chunk.toString();
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
req.on('error', (err) => {
|
|
137
|
-
clearTimeout(timeout);
|
|
138
|
-
reject(new BodyParserError(`Request error: ${err.message}`, 400));
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
req.on('end', () => {
|
|
142
|
-
clearTimeout(timeout);
|
|
143
|
-
try {
|
|
144
|
-
if (!body.trim()) {
|
|
145
|
-
resolve([]);
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const contentType = req.headers['content-type'];
|
|
150
|
-
const boundaryMatch = contentType.match(
|
|
151
|
-
/boundary=(?:"([^"]+)"|([^;]+))/i,
|
|
152
|
-
);
|
|
153
|
-
|
|
154
|
-
if (!boundaryMatch) {
|
|
155
|
-
throw new TejError(400, 'Missing boundary in content-type');
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const boundary = '--' + (boundaryMatch[1] || boundaryMatch[2]);
|
|
159
|
-
const parts = body
|
|
160
|
-
.split(boundary)
|
|
161
|
-
.filter((part) => part.trim() !== '' && part.trim() !== '--');
|
|
162
|
-
|
|
163
|
-
const parsedData = parts.map((part) => {
|
|
164
|
-
const [headerString, ...contentParts] = part.split('\r\n\r\n');
|
|
165
|
-
if (!headerString || contentParts.length === 0) {
|
|
166
|
-
throw new TejError(400, 'Malformed multipart part');
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const headers = {};
|
|
170
|
-
const headerLines = headerString.trim().split('\r\n');
|
|
171
|
-
|
|
172
|
-
headerLines.forEach((line) => {
|
|
173
|
-
const [key, ...valueParts] = line.split(': ');
|
|
174
|
-
if (!key || valueParts.length === 0) {
|
|
175
|
-
throw new TejError(400, 'Malformed header');
|
|
176
|
-
}
|
|
177
|
-
headers[key.toLowerCase()] = valueParts.join(': ');
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
const value = contentParts.join('\r\n\r\n').replace(/\r\n$/, '');
|
|
181
|
-
|
|
182
|
-
// Parse content-disposition
|
|
183
|
-
const disposition = headers['content-disposition'];
|
|
184
|
-
if (!disposition) {
|
|
185
|
-
throw new TejError(400, 'Missing content-disposition header');
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const nameMatch = disposition.match(/name="([^"]+)"/);
|
|
189
|
-
const filename = disposition.match(/filename="([^"]+)"/);
|
|
190
|
-
|
|
191
|
-
return {
|
|
192
|
-
name: nameMatch ? nameMatch[1] : undefined,
|
|
193
|
-
filename: filename ? filename[1] : undefined,
|
|
194
|
-
headers,
|
|
195
|
-
value,
|
|
196
|
-
};
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
resolve(parsedData);
|
|
200
|
-
} catch (err) {
|
|
201
|
-
reject(
|
|
202
|
-
new BodyParserError(
|
|
203
|
-
`Invalid multipart form data: ${err.message}`,
|
|
204
|
-
400,
|
|
205
|
-
),
|
|
206
|
-
);
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
class BodyParserError extends TejError {
|
|
213
|
-
constructor(message, statusCode = 400) {
|
|
214
|
-
super(statusCode, message);
|
|
215
|
-
this.name = 'BodyParserError';
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
export { BodyParserError };
|
|
220
|
-
export default parseDataBasedOnContentType;
|
|
1
|
+
import { env } from 'tej-env';
|
|
2
|
+
import TejError from '../error.js';
|
|
3
|
+
|
|
4
|
+
async function parseDataBasedOnContentType(req) {
|
|
5
|
+
// If no content-type header, return empty object (for requests without body)
|
|
6
|
+
if (!req.headers['content-type']) {
|
|
7
|
+
return {};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const contentType = req.headers['content-type'].toLowerCase();
|
|
11
|
+
|
|
12
|
+
// Check if content type is JSON
|
|
13
|
+
if (contentType === 'application/json') {
|
|
14
|
+
return await parseJSONRequestBody(req);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Check if content type is URL encoded
|
|
18
|
+
if (contentType === 'application/x-www-form-urlencoded') {
|
|
19
|
+
return await parseUrlEncodedData(req);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Check if content type is multipart form data
|
|
23
|
+
if (contentType.startsWith('multipart/form-data')) {
|
|
24
|
+
return await parseFormData(req);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
throw new BodyParserError(`Unsupported content type: ${contentType}`, 415);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function parseJSONRequestBody(req) {
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
let body = '';
|
|
33
|
+
let size = 0;
|
|
34
|
+
const maxSize = env('BODY_MAX_SIZE');
|
|
35
|
+
const timeout = setTimeout(() => {
|
|
36
|
+
reject(new BodyParserError('Request timeout', 408));
|
|
37
|
+
}, env('BODY_TIMEOUT'));
|
|
38
|
+
|
|
39
|
+
req.on('data', (chunk) => {
|
|
40
|
+
size += chunk.length;
|
|
41
|
+
if (size > maxSize) {
|
|
42
|
+
clearTimeout(timeout);
|
|
43
|
+
reject(new BodyParserError('Request entity too large', 413));
|
|
44
|
+
req.destroy();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
body += chunk.toString();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
req.on('error', (err) => {
|
|
51
|
+
clearTimeout(timeout);
|
|
52
|
+
reject(new BodyParserError(`Request error: ${err.message}`, 400));
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
req.on('end', () => {
|
|
56
|
+
clearTimeout(timeout);
|
|
57
|
+
try {
|
|
58
|
+
if (!body) {
|
|
59
|
+
resolve({});
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const jsonData = JSON.parse(body);
|
|
63
|
+
if (typeof jsonData !== 'object') {
|
|
64
|
+
throw new TejError(400, 'Invalid JSON structure');
|
|
65
|
+
}
|
|
66
|
+
resolve(jsonData);
|
|
67
|
+
} catch (err) {
|
|
68
|
+
reject(new TejError(400, `Invalid JSON: ${err.message}`));
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function parseUrlEncodedData(req) {
|
|
75
|
+
return new Promise((resolve, reject) => {
|
|
76
|
+
let body = '';
|
|
77
|
+
let size = 0;
|
|
78
|
+
const maxSize = env('BODY_MAX_SIZE');
|
|
79
|
+
const timeout = setTimeout(() => {
|
|
80
|
+
reject(new BodyParserError('Request timeout', 408));
|
|
81
|
+
}, env('BODY_TIMEOUT'));
|
|
82
|
+
|
|
83
|
+
req.on('data', (chunk) => {
|
|
84
|
+
size += chunk.length;
|
|
85
|
+
if (size > maxSize) {
|
|
86
|
+
clearTimeout(timeout);
|
|
87
|
+
reject(new BodyParserError('Request entity too large', 413));
|
|
88
|
+
req.destroy();
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
body += chunk.toString();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
req.on('error', (err) => {
|
|
95
|
+
clearTimeout(timeout);
|
|
96
|
+
reject(new BodyParserError(`Request error: ${err.message}`, 400));
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
req.on('end', () => {
|
|
100
|
+
clearTimeout(timeout);
|
|
101
|
+
try {
|
|
102
|
+
if (!body) {
|
|
103
|
+
resolve({});
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const data = new URLSearchParams(body);
|
|
107
|
+
const parsedData = Object.fromEntries(data);
|
|
108
|
+
resolve(parsedData);
|
|
109
|
+
} catch (err) {
|
|
110
|
+
reject(new BodyParserError('Invalid URL encoded data', 400));
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function parseFormData(req) {
|
|
117
|
+
return new Promise((resolve, reject) => {
|
|
118
|
+
let body = '';
|
|
119
|
+
let size = 0;
|
|
120
|
+
const maxSize = env('BODY_MAX_SIZE');
|
|
121
|
+
const timeout = setTimeout(() => {
|
|
122
|
+
reject(new BodyParserError('Request timeout', 408));
|
|
123
|
+
}, env('BODY_TIMEOUT'));
|
|
124
|
+
|
|
125
|
+
req.on('data', (chunk) => {
|
|
126
|
+
size += chunk.length;
|
|
127
|
+
if (size > maxSize) {
|
|
128
|
+
clearTimeout(timeout);
|
|
129
|
+
reject(new BodyParserError('Request entity too large', 413));
|
|
130
|
+
req.destroy();
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
body += chunk.toString();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
req.on('error', (err) => {
|
|
137
|
+
clearTimeout(timeout);
|
|
138
|
+
reject(new BodyParserError(`Request error: ${err.message}`, 400));
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
req.on('end', () => {
|
|
142
|
+
clearTimeout(timeout);
|
|
143
|
+
try {
|
|
144
|
+
if (!body.trim()) {
|
|
145
|
+
resolve([]);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const contentType = req.headers['content-type'];
|
|
150
|
+
const boundaryMatch = contentType.match(
|
|
151
|
+
/boundary=(?:"([^"]+)"|([^;]+))/i,
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
if (!boundaryMatch) {
|
|
155
|
+
throw new TejError(400, 'Missing boundary in content-type');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const boundary = '--' + (boundaryMatch[1] || boundaryMatch[2]);
|
|
159
|
+
const parts = body
|
|
160
|
+
.split(boundary)
|
|
161
|
+
.filter((part) => part.trim() !== '' && part.trim() !== '--');
|
|
162
|
+
|
|
163
|
+
const parsedData = parts.map((part) => {
|
|
164
|
+
const [headerString, ...contentParts] = part.split('\r\n\r\n');
|
|
165
|
+
if (!headerString || contentParts.length === 0) {
|
|
166
|
+
throw new TejError(400, 'Malformed multipart part');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const headers = {};
|
|
170
|
+
const headerLines = headerString.trim().split('\r\n');
|
|
171
|
+
|
|
172
|
+
headerLines.forEach((line) => {
|
|
173
|
+
const [key, ...valueParts] = line.split(': ');
|
|
174
|
+
if (!key || valueParts.length === 0) {
|
|
175
|
+
throw new TejError(400, 'Malformed header');
|
|
176
|
+
}
|
|
177
|
+
headers[key.toLowerCase()] = valueParts.join(': ');
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
const value = contentParts.join('\r\n\r\n').replace(/\r\n$/, '');
|
|
181
|
+
|
|
182
|
+
// Parse content-disposition
|
|
183
|
+
const disposition = headers['content-disposition'];
|
|
184
|
+
if (!disposition) {
|
|
185
|
+
throw new TejError(400, 'Missing content-disposition header');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const nameMatch = disposition.match(/name="([^"]+)"/);
|
|
189
|
+
const filename = disposition.match(/filename="([^"]+)"/);
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
name: nameMatch ? nameMatch[1] : undefined,
|
|
193
|
+
filename: filename ? filename[1] : undefined,
|
|
194
|
+
headers,
|
|
195
|
+
value,
|
|
196
|
+
};
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
resolve(parsedData);
|
|
200
|
+
} catch (err) {
|
|
201
|
+
reject(
|
|
202
|
+
new BodyParserError(
|
|
203
|
+
`Invalid multipart form data: ${err.message}`,
|
|
204
|
+
400,
|
|
205
|
+
),
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
class BodyParserError extends TejError {
|
|
213
|
+
constructor(message, statusCode = 400) {
|
|
214
|
+
super(statusCode, message);
|
|
215
|
+
this.name = 'BodyParserError';
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export { BodyParserError };
|
|
220
|
+
export default parseDataBasedOnContentType;
|
|
@@ -1,103 +1,103 @@
|
|
|
1
|
-
import status from 'statuses';
|
|
2
|
-
|
|
3
|
-
const formattedData = (data) => {
|
|
4
|
-
if (data === null || data === undefined) return '';
|
|
5
|
-
|
|
6
|
-
if (typeof data === 'object') {
|
|
7
|
-
try {
|
|
8
|
-
return JSON.stringify(data);
|
|
9
|
-
} catch (error) {
|
|
10
|
-
return String(data);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
if (typeof data === 'string') return data;
|
|
15
|
-
if (typeof data === 'number') return status[data] || String(data);
|
|
16
|
-
|
|
17
|
-
return String(data);
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const statusAndData = (args) => {
|
|
21
|
-
// Handle no arguments
|
|
22
|
-
if (!args || args.length === 0) {
|
|
23
|
-
return {
|
|
24
|
-
statusCode: 204,
|
|
25
|
-
data: status(204),
|
|
26
|
-
contentType: 'text/plain',
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Handle single argument
|
|
31
|
-
if (args.length === 1) {
|
|
32
|
-
const arg = args[0];
|
|
33
|
-
|
|
34
|
-
// If it's a number, treat as status code
|
|
35
|
-
if (typeof arg === 'number') {
|
|
36
|
-
return {
|
|
37
|
-
statusCode: arg,
|
|
38
|
-
data: status(arg) || String(arg),
|
|
39
|
-
contentType: 'text/plain',
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Otherwise treat as data
|
|
44
|
-
return {
|
|
45
|
-
statusCode: 200,
|
|
46
|
-
data: formattedData(arg),
|
|
47
|
-
contentType: contentType(arg),
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Handle multiple arguments
|
|
52
|
-
let statusCode = 200;
|
|
53
|
-
let data = args[0];
|
|
54
|
-
|
|
55
|
-
// If first argument is a number, treat as status code
|
|
56
|
-
if (typeof args[0] === 'number') {
|
|
57
|
-
statusCode = args[0];
|
|
58
|
-
data = args[1];
|
|
59
|
-
} else {
|
|
60
|
-
// If first argument is not a number, check if second is
|
|
61
|
-
if (typeof args[1] === 'number') {
|
|
62
|
-
statusCode = args[1];
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// If data is undefined, use status message
|
|
67
|
-
if (data === undefined) {
|
|
68
|
-
data = status[statusCode] || String(statusCode);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// If third argument is provided, it's the content type
|
|
72
|
-
const customContentType = args.length > 2 ? args[2] : null;
|
|
73
|
-
|
|
74
|
-
return {
|
|
75
|
-
statusCode,
|
|
76
|
-
data: formattedData(data),
|
|
77
|
-
contentType: customContentType || contentType(data),
|
|
78
|
-
};
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const contentType = (data) => {
|
|
82
|
-
if (data === null || data === undefined) return 'text/plain';
|
|
83
|
-
|
|
84
|
-
switch (typeof data) {
|
|
85
|
-
case 'object':
|
|
86
|
-
return 'application/json';
|
|
87
|
-
case 'string':
|
|
88
|
-
// Check if string is HTML
|
|
89
|
-
if (
|
|
90
|
-
data.trim().toLowerCase().startsWith('<!DOCTYPE') ||
|
|
91
|
-
data.trim().toLowerCase().startsWith('<html')
|
|
92
|
-
) {
|
|
93
|
-
return 'text/html';
|
|
94
|
-
}
|
|
95
|
-
return 'text/plain';
|
|
96
|
-
case 'number':
|
|
97
|
-
return 'text/plain';
|
|
98
|
-
default:
|
|
99
|
-
return 'text/plain';
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
export { statusAndData, contentType, formattedData };
|
|
1
|
+
import status from 'statuses';
|
|
2
|
+
|
|
3
|
+
const formattedData = (data) => {
|
|
4
|
+
if (data === null || data === undefined) return '';
|
|
5
|
+
|
|
6
|
+
if (typeof data === 'object') {
|
|
7
|
+
try {
|
|
8
|
+
return JSON.stringify(data);
|
|
9
|
+
} catch (error) {
|
|
10
|
+
return String(data);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (typeof data === 'string') return data;
|
|
15
|
+
if (typeof data === 'number') return status[data] || String(data);
|
|
16
|
+
|
|
17
|
+
return String(data);
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const statusAndData = (args) => {
|
|
21
|
+
// Handle no arguments
|
|
22
|
+
if (!args || args.length === 0) {
|
|
23
|
+
return {
|
|
24
|
+
statusCode: 204,
|
|
25
|
+
data: status(204),
|
|
26
|
+
contentType: 'text/plain',
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Handle single argument
|
|
31
|
+
if (args.length === 1) {
|
|
32
|
+
const arg = args[0];
|
|
33
|
+
|
|
34
|
+
// If it's a number, treat as status code
|
|
35
|
+
if (typeof arg === 'number') {
|
|
36
|
+
return {
|
|
37
|
+
statusCode: arg,
|
|
38
|
+
data: status(arg) || String(arg),
|
|
39
|
+
contentType: 'text/plain',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Otherwise treat as data
|
|
44
|
+
return {
|
|
45
|
+
statusCode: 200,
|
|
46
|
+
data: formattedData(arg),
|
|
47
|
+
contentType: contentType(arg),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Handle multiple arguments
|
|
52
|
+
let statusCode = 200;
|
|
53
|
+
let data = args[0];
|
|
54
|
+
|
|
55
|
+
// If first argument is a number, treat as status code
|
|
56
|
+
if (typeof args[0] === 'number') {
|
|
57
|
+
statusCode = args[0];
|
|
58
|
+
data = args[1];
|
|
59
|
+
} else {
|
|
60
|
+
// If first argument is not a number, check if second is
|
|
61
|
+
if (typeof args[1] === 'number') {
|
|
62
|
+
statusCode = args[1];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// If data is undefined, use status message
|
|
67
|
+
if (data === undefined) {
|
|
68
|
+
data = status[statusCode] || String(statusCode);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// If third argument is provided, it's the content type
|
|
72
|
+
const customContentType = args.length > 2 ? args[2] : null;
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
statusCode,
|
|
76
|
+
data: formattedData(data),
|
|
77
|
+
contentType: customContentType || contentType(data),
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const contentType = (data) => {
|
|
82
|
+
if (data === null || data === undefined) return 'text/plain';
|
|
83
|
+
|
|
84
|
+
switch (typeof data) {
|
|
85
|
+
case 'object':
|
|
86
|
+
return 'application/json';
|
|
87
|
+
case 'string':
|
|
88
|
+
// Check if string is HTML
|
|
89
|
+
if (
|
|
90
|
+
data.trim().toLowerCase().startsWith('<!DOCTYPE') ||
|
|
91
|
+
data.trim().toLowerCase().startsWith('<html')
|
|
92
|
+
) {
|
|
93
|
+
return 'text/html';
|
|
94
|
+
}
|
|
95
|
+
return 'text/plain';
|
|
96
|
+
case 'number':
|
|
97
|
+
return 'text/plain';
|
|
98
|
+
default:
|
|
99
|
+
return 'text/plain';
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export { statusAndData, contentType, formattedData };
|