yinzerflow 0.1.18 → 0.2.0
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 +0 -296
- package/YinzerFlow.d.ts +565 -0
- package/YinzerFlow.js +24 -0
- package/YinzerFlow.js.map +42 -0
- package/docs/advanced-configuration-options.md +175 -0
- package/docs/body-parsing.md +294 -0
- package/docs/cors.md +187 -0
- package/docs/ip-security.md +232 -0
- package/docs/request.md +145 -0
- package/docs/response.md +251 -0
- package/docs/start-here.MD +116 -0
- package/example/index.ts +109 -53
- package/package.json +15 -17
- package/constants/index.d.ts +0 -86
- package/constants/index.js +0 -3
- package/constants/index.js.map +0 -13
- package/docs/README.md +0 -327
- package/docs/content-types.md +0 -390
- package/docs/error-handling.md +0 -266
- package/docs/file-parsers.md +0 -276
- package/docs/hooks.md +0 -289
- package/docs/routing.md +0 -204
- package/example/bun.lock +0 -866
- package/example/hooks/authentication.middleware.ts +0 -77
- package/example/package.json +0 -61
- package/example/routes/authentication.routes.ts +0 -243
- package/example/routes/content-types.ts +0 -116
- package/example/tsconfig.json +0 -32
- package/index.d.ts +0 -395
- package/index.js +0 -23
- package/index.js.map +0 -33
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Authentication Middleware
|
|
3
|
-
*
|
|
4
|
-
* This middleware checks for a valid authentication token in the request headers.
|
|
5
|
-
* If no token is provided or the token is invalid, it returns an unauthorized response.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { HttpStatusCode } from 'yinzerflow';
|
|
9
|
-
import type { Context, THttpStatusCode, TResponseBody } from 'yinzerflow';
|
|
10
|
-
|
|
11
|
-
// Define user interface
|
|
12
|
-
interface User {
|
|
13
|
-
id: string;
|
|
14
|
-
name: string;
|
|
15
|
-
role: string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Define response interface
|
|
19
|
-
interface AuthResponse {
|
|
20
|
-
success: boolean;
|
|
21
|
-
message: string;
|
|
22
|
-
error?: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Extend the request type to include user property
|
|
26
|
-
declare module 'yinzerflow' {
|
|
27
|
-
interface HttpRequest {
|
|
28
|
-
user?: User;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
// Simulated user database for demo purposes
|
|
33
|
-
const USERS: Record<string, User> = {
|
|
34
|
-
'user123-token': { id: 'user123', name: 'John Doe', role: 'user' },
|
|
35
|
-
'admin456-token': { id: 'admin456', name: 'Jane Smith', role: 'admin' },
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Authentication middleware function
|
|
40
|
-
* Validates the auth token and attaches user data to the request if valid
|
|
41
|
-
*/
|
|
42
|
-
export default ({ request, response }: Context): TResponseBody<AuthResponse> | void => {
|
|
43
|
-
// Get the authorization header
|
|
44
|
-
const authHeader = request.headers['authorization'];
|
|
45
|
-
|
|
46
|
-
// Check if the authorization header exists and has the correct format
|
|
47
|
-
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
48
|
-
response.setStatus(HttpStatusCode.UNAUTHORIZED as THttpStatusCode);
|
|
49
|
-
return {
|
|
50
|
-
success: false,
|
|
51
|
-
message: 'Authentication required',
|
|
52
|
-
error: 'Missing or invalid authorization header',
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Extract the token
|
|
57
|
-
const token = authHeader.split(' ')[1];
|
|
58
|
-
|
|
59
|
-
// Check if the token exists in our simulated database
|
|
60
|
-
const user = USERS[token];
|
|
61
|
-
|
|
62
|
-
if (!user) {
|
|
63
|
-
response.setStatus(HttpStatusCode.UNAUTHORIZED as THttpStatusCode);
|
|
64
|
-
return {
|
|
65
|
-
success: false,
|
|
66
|
-
message: 'Authentication failed',
|
|
67
|
-
error: 'Invalid token',
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// If we reach here, the token is valid
|
|
72
|
-
// Attach the user data to the request for use in route handlers
|
|
73
|
-
request.user = user;
|
|
74
|
-
|
|
75
|
-
// Continue to the next middleware or route handler
|
|
76
|
-
// By not returning anything, we allow the request to proceed
|
|
77
|
-
};
|
package/example/package.json
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "yinzerflow",
|
|
3
|
-
"version": "0.1.10",
|
|
4
|
-
"author": "Patrick Rizzardi <patrick@redact.digital> (https://redact.digital)",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"homepage": "https://github.com/yinzers/YinzerFlow.git",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/yinzers/YinzerFlow.git"
|
|
10
|
-
},
|
|
11
|
-
"description": "A simple, fast, and lightweight web framework for Node and Bun",
|
|
12
|
-
"keywords": [
|
|
13
|
-
"http",
|
|
14
|
-
"rest",
|
|
15
|
-
"server",
|
|
16
|
-
"router",
|
|
17
|
-
"app",
|
|
18
|
-
"api",
|
|
19
|
-
"web",
|
|
20
|
-
"framework",
|
|
21
|
-
"node",
|
|
22
|
-
"bun",
|
|
23
|
-
"yinzerflow"
|
|
24
|
-
],
|
|
25
|
-
"types": "index.d.ts",
|
|
26
|
-
"main": "index.js",
|
|
27
|
-
"type": "module",
|
|
28
|
-
"scripts": {
|
|
29
|
-
"test": "bun test --watch --coverage --ignore-coverage-for-files=app/core/__mocks__",
|
|
30
|
-
"test:production": "bun test --timeout 20 --rerun-each 10 --bail 10",
|
|
31
|
-
"clean": "rm -rf lib && rm -rf storage && echo 'Done.'",
|
|
32
|
-
"build": "bun clean; bun build.ts",
|
|
33
|
-
"start": "bun --watch local/index.ts",
|
|
34
|
-
"lint": "eslint . --fix --quiet --color --cache",
|
|
35
|
-
"lint:format": "prettier --check .",
|
|
36
|
-
"lint:spelling": "cspell . --no-progress --unique",
|
|
37
|
-
"lint:size": "gzip-size lib/index.js --include-original",
|
|
38
|
-
"format": "prettier --write app"
|
|
39
|
-
},
|
|
40
|
-
"dependencies": {
|
|
41
|
-
"yinzerflow": "file:../lib"
|
|
42
|
-
},
|
|
43
|
-
"devDependencies": {
|
|
44
|
-
"@eslint/compat": "^1.2.7",
|
|
45
|
-
"@types/ip": "^1.1.3",
|
|
46
|
-
"bun-plugin-dts": "^0.3.0",
|
|
47
|
-
"bun-types": "1.0.3",
|
|
48
|
-
"cspell": "^8.17.5",
|
|
49
|
-
"eslint": "^9.22.0",
|
|
50
|
-
"eslint-import-resolver-typescript": "^3.8.5",
|
|
51
|
-
"eslint-plugin-import": "^2.31.0",
|
|
52
|
-
"globals": "^15.15.0",
|
|
53
|
-
"gzip-size-cli": "^5.1.0",
|
|
54
|
-
"prettier": "^3.5.3",
|
|
55
|
-
"typescript": "^5.8.2",
|
|
56
|
-
"typescript-eslint": "^8.26.1"
|
|
57
|
-
},
|
|
58
|
-
"peerDependencies": {
|
|
59
|
-
"typescript": "^5.0.0"
|
|
60
|
-
}
|
|
61
|
-
}
|
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Authentication Routes
|
|
3
|
-
*
|
|
4
|
-
* This file defines routes for user authentication including:
|
|
5
|
-
* - User registration
|
|
6
|
-
* - User login
|
|
7
|
-
* - User logout
|
|
8
|
-
* - User profile retrieval
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { HttpStatusCode } from 'yinzerflow';
|
|
12
|
-
import type { Context, IRoute, THttpStatusCode, TResponseBody } from 'yinzerflow';
|
|
13
|
-
|
|
14
|
-
// Define interfaces for better type safety
|
|
15
|
-
interface User {
|
|
16
|
-
id: string;
|
|
17
|
-
email: string;
|
|
18
|
-
password: string;
|
|
19
|
-
name: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
interface UserResponse {
|
|
23
|
-
id: string;
|
|
24
|
-
email: string;
|
|
25
|
-
name: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
interface AuthResponse {
|
|
29
|
-
success: boolean;
|
|
30
|
-
message: string;
|
|
31
|
-
user?: UserResponse;
|
|
32
|
-
token?: string;
|
|
33
|
-
error?: string;
|
|
34
|
-
errors?: Record<string, string | null>;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Mock database for demonstration
|
|
38
|
-
const users: User[] = [
|
|
39
|
-
{
|
|
40
|
-
id: '1',
|
|
41
|
-
email: 'user@example.com',
|
|
42
|
-
password: 'password123', // In a real app, this would be hashed
|
|
43
|
-
name: 'Example User',
|
|
44
|
-
},
|
|
45
|
-
];
|
|
46
|
-
|
|
47
|
-
// Mock tokens for demonstration
|
|
48
|
-
const tokens: Record<string, string> = {};
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Authentication routes
|
|
52
|
-
*/
|
|
53
|
-
export default <IRoute[]>[
|
|
54
|
-
// Register a new user
|
|
55
|
-
{
|
|
56
|
-
path: '/register',
|
|
57
|
-
method: 'POST',
|
|
58
|
-
handler: ({ request, response }: Context): TResponseBody<AuthResponse> => {
|
|
59
|
-
const { email, password, name } = request.body as {
|
|
60
|
-
email?: string;
|
|
61
|
-
password?: string;
|
|
62
|
-
name?: string;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
// Validate input
|
|
66
|
-
const errors: Record<string, string | null> = {
|
|
67
|
-
email: !email ? 'Email is required' : null,
|
|
68
|
-
password: !password ? 'Password is required' : null,
|
|
69
|
-
name: !name ? 'Name is required' : null,
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
// Check if any errors exist
|
|
73
|
-
const hasErrors = Object.values(errors).some((error) => error !== null);
|
|
74
|
-
if (hasErrors) {
|
|
75
|
-
response.setStatus(HttpStatusCode.BAD_REQUEST as THttpStatusCode);
|
|
76
|
-
return {
|
|
77
|
-
success: false,
|
|
78
|
-
message: 'Validation failed',
|
|
79
|
-
errors: Object.fromEntries(Object.entries(errors).filter(([_, value]) => value !== null)),
|
|
80
|
-
} as TResponseBody<AuthResponse>;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// Check if user already exists
|
|
84
|
-
const existingUser = users.find((u) => u.email === email);
|
|
85
|
-
if (existingUser) {
|
|
86
|
-
response.setStatus(HttpStatusCode.BAD_REQUEST as THttpStatusCode);
|
|
87
|
-
return {
|
|
88
|
-
success: false,
|
|
89
|
-
message: 'User already exists',
|
|
90
|
-
error: 'Email already in use',
|
|
91
|
-
} as TResponseBody<AuthResponse>;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Create new user
|
|
95
|
-
const newUser: User = {
|
|
96
|
-
id: (users.length + 1).toString(),
|
|
97
|
-
email: email!,
|
|
98
|
-
password: password!, // In a real app, this would be hashed
|
|
99
|
-
name: name!,
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
users.push(newUser);
|
|
103
|
-
|
|
104
|
-
// Generate token
|
|
105
|
-
const token = `token_${Date.now()}_${newUser.id}`;
|
|
106
|
-
tokens[newUser.id] = token;
|
|
107
|
-
|
|
108
|
-
response.setStatus(HttpStatusCode.CREATED as THttpStatusCode);
|
|
109
|
-
return {
|
|
110
|
-
success: true,
|
|
111
|
-
message: 'User registered successfully',
|
|
112
|
-
user: {
|
|
113
|
-
id: newUser.id,
|
|
114
|
-
email: newUser.email,
|
|
115
|
-
name: newUser.name,
|
|
116
|
-
},
|
|
117
|
-
token,
|
|
118
|
-
} as TResponseBody<AuthResponse>;
|
|
119
|
-
},
|
|
120
|
-
},
|
|
121
|
-
|
|
122
|
-
// Login user
|
|
123
|
-
{
|
|
124
|
-
path: '/login',
|
|
125
|
-
method: 'POST',
|
|
126
|
-
handler: ({ request, response }: Context): TResponseBody<AuthResponse> => {
|
|
127
|
-
const { email, password } = request.body as {
|
|
128
|
-
email?: string;
|
|
129
|
-
password?: string;
|
|
130
|
-
};
|
|
131
|
-
|
|
132
|
-
// Validate input
|
|
133
|
-
if (!email || !password) {
|
|
134
|
-
response.setStatus(HttpStatusCode.BAD_REQUEST as THttpStatusCode);
|
|
135
|
-
return {
|
|
136
|
-
success: false,
|
|
137
|
-
message: 'Email and password are required',
|
|
138
|
-
} as TResponseBody<AuthResponse>;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Find user
|
|
142
|
-
const user = users.find((u) => u.email === email && u.password === password);
|
|
143
|
-
if (!user) {
|
|
144
|
-
response.setStatus(HttpStatusCode.UNAUTHORIZED as THttpStatusCode);
|
|
145
|
-
return {
|
|
146
|
-
success: false,
|
|
147
|
-
message: 'Invalid credentials',
|
|
148
|
-
} as TResponseBody<AuthResponse>;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Generate token
|
|
152
|
-
const token = `token_${Date.now()}_${user.id}`;
|
|
153
|
-
tokens[user.id] = token;
|
|
154
|
-
|
|
155
|
-
return {
|
|
156
|
-
success: true,
|
|
157
|
-
message: 'Login successful',
|
|
158
|
-
user: {
|
|
159
|
-
id: user.id,
|
|
160
|
-
email: user.email,
|
|
161
|
-
name: user.name,
|
|
162
|
-
},
|
|
163
|
-
token,
|
|
164
|
-
} as TResponseBody<AuthResponse>;
|
|
165
|
-
},
|
|
166
|
-
},
|
|
167
|
-
|
|
168
|
-
// Get user profile
|
|
169
|
-
{
|
|
170
|
-
path: '/profile/:id',
|
|
171
|
-
method: 'GET',
|
|
172
|
-
handler: ({ request, response }: Context): TResponseBody<AuthResponse> => {
|
|
173
|
-
const { id } = <{ id: string }>request.params;
|
|
174
|
-
const authHeader = request.headers.Authorization;
|
|
175
|
-
|
|
176
|
-
// Check if user exists
|
|
177
|
-
const user = users.find((u) => u.id === id);
|
|
178
|
-
if (!user) {
|
|
179
|
-
response.setStatus(HttpStatusCode.NOT_FOUND as THttpStatusCode);
|
|
180
|
-
return {
|
|
181
|
-
success: false,
|
|
182
|
-
message: 'User not found',
|
|
183
|
-
} as TResponseBody<AuthResponse>;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// Validate token
|
|
187
|
-
const expectedToken = tokens[id];
|
|
188
|
-
if (!authHeader || !authHeader.startsWith('Bearer ') || authHeader.split(' ')[1] !== expectedToken) {
|
|
189
|
-
response.setStatus(HttpStatusCode.UNAUTHORIZED as THttpStatusCode);
|
|
190
|
-
return {
|
|
191
|
-
success: false,
|
|
192
|
-
message: 'Unauthorized',
|
|
193
|
-
} as TResponseBody<AuthResponse>;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return {
|
|
197
|
-
success: true,
|
|
198
|
-
message: 'Profile retrieved successfully',
|
|
199
|
-
user: {
|
|
200
|
-
id: user.id,
|
|
201
|
-
email: user.email,
|
|
202
|
-
name: user.name,
|
|
203
|
-
},
|
|
204
|
-
} as TResponseBody<AuthResponse>;
|
|
205
|
-
},
|
|
206
|
-
},
|
|
207
|
-
|
|
208
|
-
// Logout user
|
|
209
|
-
{
|
|
210
|
-
path: '/logout',
|
|
211
|
-
method: 'POST',
|
|
212
|
-
handler: ({ request, response }: Context): TResponseBody<AuthResponse> => {
|
|
213
|
-
const authHeader = request.headers.Authorization;
|
|
214
|
-
|
|
215
|
-
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
216
|
-
response.setStatus(HttpStatusCode.BAD_REQUEST as THttpStatusCode);
|
|
217
|
-
return {
|
|
218
|
-
success: false,
|
|
219
|
-
message: 'No token provided',
|
|
220
|
-
} as TResponseBody<AuthResponse>;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const token = authHeader.split(' ')[1];
|
|
224
|
-
const userId = Object.keys(tokens).find((id) => tokens[id] === token);
|
|
225
|
-
|
|
226
|
-
if (!userId) {
|
|
227
|
-
response.setStatus(HttpStatusCode.UNAUTHORIZED as THttpStatusCode);
|
|
228
|
-
return {
|
|
229
|
-
success: false,
|
|
230
|
-
message: 'Invalid token',
|
|
231
|
-
} as TResponseBody<AuthResponse>;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// Remove token
|
|
235
|
-
delete tokens[userId];
|
|
236
|
-
|
|
237
|
-
return {
|
|
238
|
-
success: true,
|
|
239
|
-
message: 'Logout successful',
|
|
240
|
-
} as TResponseBody<AuthResponse>;
|
|
241
|
-
},
|
|
242
|
-
},
|
|
243
|
-
];
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { IMultipartFormData, isJsonData, isMultipartFormData, TJsonData, YinzerFlow } from 'yinzerflow';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* This file demonstrates different approaches to handle various content types:
|
|
5
|
-
* 1. Fast approach using type casting (no runtime checks)
|
|
6
|
-
* 2. Safe approach using type guards (with runtime validation)
|
|
7
|
-
*/
|
|
8
|
-
export default function setupContentHandlers(app: YinzerFlow) {
|
|
9
|
-
//=============================================================================
|
|
10
|
-
// JSON Data Handlers
|
|
11
|
-
//=============================================================================
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Fast approach for JSON data (using type casting)
|
|
15
|
-
*/
|
|
16
|
-
app.post('/api/json/fast', ({ request }) => {
|
|
17
|
-
// Use type assertion - fast but no runtime validation
|
|
18
|
-
const body = request.body as TJsonData<{ name: string; email: string; preferences: Record<string, unknown> }>;
|
|
19
|
-
|
|
20
|
-
// Access properties directly
|
|
21
|
-
const { name, email, preferences } = body;
|
|
22
|
-
|
|
23
|
-
return {
|
|
24
|
-
success: true,
|
|
25
|
-
message: `Processed JSON data for ${name}`,
|
|
26
|
-
emailConfirmed: !!email,
|
|
27
|
-
preferencesCount: preferences ? Object.keys(preferences).length : 0,
|
|
28
|
-
};
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Safe approach for JSON data (using type guards)
|
|
33
|
-
*/
|
|
34
|
-
app.post('/api/json/safe', ({ request, response }) => {
|
|
35
|
-
// Runtime type checking
|
|
36
|
-
if (!isJsonData<{ name: string; email: string; preferences: Record<string, unknown> }>(request.body)) {
|
|
37
|
-
response.setStatus(400);
|
|
38
|
-
return {
|
|
39
|
-
success: false,
|
|
40
|
-
message: 'Expected JSON data',
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// TypeScript knows request.body is JsonData here
|
|
45
|
-
const body = request.body;
|
|
46
|
-
|
|
47
|
-
// Validate required fields
|
|
48
|
-
if (!body.name || typeof body.name !== 'string') {
|
|
49
|
-
response.setStatus(400);
|
|
50
|
-
return {
|
|
51
|
-
success: false,
|
|
52
|
-
message: 'Missing or invalid required field: name',
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return {
|
|
57
|
-
success: true,
|
|
58
|
-
message: `Processed JSON data for ${body.name}`,
|
|
59
|
-
receivedFields: Object.keys(body),
|
|
60
|
-
};
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
//=============================================================================
|
|
64
|
-
// Multipart Form Data Handlers
|
|
65
|
-
//=============================================================================
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Fast approach for multipart form data (using type casting)
|
|
69
|
-
*/
|
|
70
|
-
app.post('/api/multipart/fast', ({ request }) => {
|
|
71
|
-
// Use type assertion - fast but no runtime validation
|
|
72
|
-
const body = request.body as IMultipartFormData;
|
|
73
|
-
|
|
74
|
-
// Access form fields and files
|
|
75
|
-
const { fields, files } = body;
|
|
76
|
-
|
|
77
|
-
return {
|
|
78
|
-
success: true,
|
|
79
|
-
message: 'Processed multipart form data',
|
|
80
|
-
fieldCount: Object.keys(fields).length,
|
|
81
|
-
fileCount: Object.keys(files).length,
|
|
82
|
-
fileNames: Object.keys(files),
|
|
83
|
-
};
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Safe approach for multipart form data (using type guards)
|
|
88
|
-
*/
|
|
89
|
-
app.post('/api/multipart/safe', ({ request, response }) => {
|
|
90
|
-
// Runtime type checking
|
|
91
|
-
if (!isMultipartFormData(request.body)) {
|
|
92
|
-
response.setStatus(400);
|
|
93
|
-
return {
|
|
94
|
-
success: false,
|
|
95
|
-
message: 'Expected multipart form data',
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// TypeScript knows request.body is MultipartFormData here
|
|
100
|
-
const { fields, files } = request.body;
|
|
101
|
-
|
|
102
|
-
// Process files
|
|
103
|
-
const fileInfo = Object.entries(files).map(([name, file]) => ({
|
|
104
|
-
name,
|
|
105
|
-
size: file.size,
|
|
106
|
-
contentType: file.contentType,
|
|
107
|
-
}));
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
success: true,
|
|
111
|
-
message: 'Processed multipart form data',
|
|
112
|
-
fields,
|
|
113
|
-
files: fileInfo,
|
|
114
|
-
};
|
|
115
|
-
});
|
|
116
|
-
}
|
package/example/tsconfig.json
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ESNext",
|
|
4
|
-
"module": "NodeNext",
|
|
5
|
-
"moduleResolution": "NodeNext",
|
|
6
|
-
"esModuleInterop": true,
|
|
7
|
-
"strict": true,
|
|
8
|
-
"skipLibCheck": true,
|
|
9
|
-
"forceConsistentCasingInFileNames": true,
|
|
10
|
-
"noEmit": true,
|
|
11
|
-
"allowImportingTsExtensions": true,
|
|
12
|
-
"outDir": "dist",
|
|
13
|
-
"declaration": true,
|
|
14
|
-
"sourceMap": true,
|
|
15
|
-
"types": [
|
|
16
|
-
"bun-types"
|
|
17
|
-
],
|
|
18
|
-
"baseUrl": ".",
|
|
19
|
-
"paths": {
|
|
20
|
-
"yinzerflow": [
|
|
21
|
-
"../lib",
|
|
22
|
-
"../lib/index.d.ts"
|
|
23
|
-
]
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
"include": [
|
|
27
|
-
"./**/*.ts"
|
|
28
|
-
],
|
|
29
|
-
"exclude": [
|
|
30
|
-
"node_modules"
|
|
31
|
-
]
|
|
32
|
-
}
|