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.
@@ -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
- };
@@ -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
- }
@@ -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
- }