@flexireact/core 3.0.0 → 3.0.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 +204 -52
- package/dist/cli/index.js +1514 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/client/index.js +373 -0
- package/dist/core/client/index.js.map +1 -0
- package/dist/core/index.js +6415 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/server/index.js +3094 -0
- package/dist/core/server/index.js.map +1 -0
- package/package.json +80 -80
- package/bin/flexireact.js +0 -23
- package/cli/generators.ts +0 -616
- package/cli/index.ts +0 -1182
- package/core/actions/index.ts +0 -364
- package/core/api.ts +0 -143
- package/core/build/index.ts +0 -425
- package/core/cli/logger.ts +0 -353
- package/core/client/Link.tsx +0 -345
- package/core/client/hydration.ts +0 -147
- package/core/client/index.ts +0 -12
- package/core/client/islands.ts +0 -143
- package/core/client/navigation.ts +0 -212
- package/core/client/runtime.ts +0 -52
- package/core/config.ts +0 -116
- package/core/context.ts +0 -83
- package/core/dev.ts +0 -47
- package/core/devtools/index.ts +0 -644
- package/core/edge/cache.ts +0 -344
- package/core/edge/fetch-polyfill.ts +0 -247
- package/core/edge/handler.ts +0 -248
- package/core/edge/index.ts +0 -81
- package/core/edge/ppr.ts +0 -264
- package/core/edge/runtime.ts +0 -161
- package/core/font/index.ts +0 -306
- package/core/helpers.ts +0 -494
- package/core/image/index.ts +0 -413
- package/core/index.ts +0 -218
- package/core/islands/index.ts +0 -293
- package/core/loader.ts +0 -111
- package/core/logger.ts +0 -242
- package/core/metadata/index.ts +0 -622
- package/core/middleware/index.ts +0 -416
- package/core/plugins/index.ts +0 -373
- package/core/render/index.ts +0 -1243
- package/core/render.ts +0 -136
- package/core/router/index.ts +0 -551
- package/core/router.ts +0 -141
- package/core/rsc/index.ts +0 -199
- package/core/server/index.ts +0 -779
- package/core/server.ts +0 -203
- package/core/ssg/index.ts +0 -346
- package/core/start-dev.ts +0 -6
- package/core/start-prod.ts +0 -6
- package/core/tsconfig.json +0 -30
- package/core/types.ts +0 -239
- package/core/utils.ts +0 -176
package/core/middleware/index.ts
DELETED
|
@@ -1,416 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FlexiReact Middleware System
|
|
3
|
-
*
|
|
4
|
-
* Middlewares run before every request and can:
|
|
5
|
-
* - Modify the request/response
|
|
6
|
-
* - Redirect or rewrite URLs
|
|
7
|
-
* - Add headers
|
|
8
|
-
* - Authenticate users
|
|
9
|
-
* - Log requests
|
|
10
|
-
*
|
|
11
|
-
* Usage:
|
|
12
|
-
* Create a middleware.js file in your project root:
|
|
13
|
-
*
|
|
14
|
-
* export default function middleware(request) {
|
|
15
|
-
* // Return a response to short-circuit
|
|
16
|
-
* // Return NextResponse.next() to continue
|
|
17
|
-
* // Return NextResponse.redirect() to redirect
|
|
18
|
-
* }
|
|
19
|
-
*
|
|
20
|
-
* export const config = {
|
|
21
|
-
* matcher: ['/protected/:path*']
|
|
22
|
-
* };
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
import fs from 'fs';
|
|
26
|
-
import path from 'path';
|
|
27
|
-
import { pathToFileURL } from 'url';
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Middleware response helpers
|
|
31
|
-
*/
|
|
32
|
-
interface MiddlewareResponseOptions {
|
|
33
|
-
type?: string;
|
|
34
|
-
status?: number;
|
|
35
|
-
headers?: Record<string, string>;
|
|
36
|
-
body?: any;
|
|
37
|
-
url?: string | null;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export class MiddlewareResponse {
|
|
41
|
-
type: string;
|
|
42
|
-
status: number;
|
|
43
|
-
headers: Map<string, string>;
|
|
44
|
-
body: any;
|
|
45
|
-
url: string | null;
|
|
46
|
-
|
|
47
|
-
constructor(options: MiddlewareResponseOptions = {}) {
|
|
48
|
-
this.type = options.type || 'next';
|
|
49
|
-
this.status = options.status || 200;
|
|
50
|
-
this.headers = new Map(Object.entries(options.headers || {}));
|
|
51
|
-
this.body = options.body || null;
|
|
52
|
-
this.url = options.url || null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Continue to the next middleware/handler
|
|
57
|
-
*/
|
|
58
|
-
static next(options = {}) {
|
|
59
|
-
return new MiddlewareResponse({ ...options, type: 'next' });
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Redirect to a different URL
|
|
64
|
-
*/
|
|
65
|
-
static redirect(url, status = 302) {
|
|
66
|
-
return new MiddlewareResponse({
|
|
67
|
-
type: 'redirect',
|
|
68
|
-
url,
|
|
69
|
-
status
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Rewrite the request to a different URL (internal)
|
|
75
|
-
*/
|
|
76
|
-
static rewrite(url) {
|
|
77
|
-
return new MiddlewareResponse({
|
|
78
|
-
type: 'rewrite',
|
|
79
|
-
url
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Return a JSON response
|
|
85
|
-
*/
|
|
86
|
-
static json(data: any, options: { status?: number; headers?: Record<string, string> } = {}) {
|
|
87
|
-
return new MiddlewareResponse({
|
|
88
|
-
type: 'response',
|
|
89
|
-
status: options.status || 200,
|
|
90
|
-
headers: { 'Content-Type': 'application/json', ...options.headers },
|
|
91
|
-
body: JSON.stringify(data)
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Return an HTML response
|
|
97
|
-
*/
|
|
98
|
-
static html(content: string, options: { status?: number; headers?: Record<string, string> } = {}) {
|
|
99
|
-
return new MiddlewareResponse({
|
|
100
|
-
type: 'response',
|
|
101
|
-
status: options.status || 200,
|
|
102
|
-
headers: { 'Content-Type': 'text/html', ...options.headers },
|
|
103
|
-
body: content
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Middleware request wrapper
|
|
110
|
-
*/
|
|
111
|
-
export class MiddlewareRequest {
|
|
112
|
-
raw: any;
|
|
113
|
-
method: string;
|
|
114
|
-
url: string;
|
|
115
|
-
headers: Map<string, string>;
|
|
116
|
-
pathname: string;
|
|
117
|
-
searchParams: URLSearchParams;
|
|
118
|
-
query: Record<string, string>;
|
|
119
|
-
cookies: Map<string, string>;
|
|
120
|
-
|
|
121
|
-
constructor(req: any) {
|
|
122
|
-
this.raw = req;
|
|
123
|
-
this.method = req.method;
|
|
124
|
-
this.url = req.url;
|
|
125
|
-
this.headers = new Map(Object.entries(req.headers || {}));
|
|
126
|
-
|
|
127
|
-
// Parse URL
|
|
128
|
-
const parsedUrl = new URL(req.url, `http://${req.headers.host || 'localhost'}`);
|
|
129
|
-
this.pathname = parsedUrl.pathname;
|
|
130
|
-
this.searchParams = parsedUrl.searchParams;
|
|
131
|
-
this.query = Object.fromEntries(parsedUrl.searchParams) as Record<string, string>;
|
|
132
|
-
|
|
133
|
-
// Parse cookies
|
|
134
|
-
this.cookies = this._parseCookies(req.headers.cookie || '');
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
_parseCookies(cookieHeader) {
|
|
138
|
-
const cookies = new Map();
|
|
139
|
-
if (!cookieHeader) return cookies;
|
|
140
|
-
|
|
141
|
-
cookieHeader.split(';').forEach(cookie => {
|
|
142
|
-
const [name, ...rest] = cookie.split('=');
|
|
143
|
-
if (name) {
|
|
144
|
-
cookies.set(name.trim(), rest.join('=').trim());
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
return cookies;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Get a header value
|
|
153
|
-
*/
|
|
154
|
-
header(name) {
|
|
155
|
-
return this.headers.get(name.toLowerCase());
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Get a cookie value
|
|
160
|
-
*/
|
|
161
|
-
cookie(name) {
|
|
162
|
-
return this.cookies.get(name);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Check if request matches a path pattern
|
|
167
|
-
*/
|
|
168
|
-
matches(pattern) {
|
|
169
|
-
return matchPath(this.pathname, pattern);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Loads middleware from project
|
|
175
|
-
*/
|
|
176
|
-
export async function loadMiddleware(projectRoot) {
|
|
177
|
-
const middlewarePath = path.join(projectRoot, 'middleware.js');
|
|
178
|
-
|
|
179
|
-
if (!fs.existsSync(middlewarePath)) {
|
|
180
|
-
return null;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
try {
|
|
184
|
-
const url = pathToFileURL(middlewarePath).href;
|
|
185
|
-
const module = await import(`${url}?t=${Date.now()}`);
|
|
186
|
-
|
|
187
|
-
return {
|
|
188
|
-
handler: module.default,
|
|
189
|
-
config: module.config || {}
|
|
190
|
-
};
|
|
191
|
-
} catch (error) {
|
|
192
|
-
console.error('Failed to load middleware:', error);
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* Runs middleware chain
|
|
199
|
-
*/
|
|
200
|
-
export async function runMiddleware(req, res, middleware) {
|
|
201
|
-
if (!middleware) {
|
|
202
|
-
return { continue: true };
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
const { handler, config } = middleware;
|
|
206
|
-
const request = new MiddlewareRequest(req);
|
|
207
|
-
|
|
208
|
-
// Check if request matches middleware patterns
|
|
209
|
-
if (config.matcher) {
|
|
210
|
-
const patterns = Array.isArray(config.matcher) ? config.matcher : [config.matcher];
|
|
211
|
-
const matches = patterns.some(pattern => matchPath(request.pathname, pattern));
|
|
212
|
-
|
|
213
|
-
if (!matches) {
|
|
214
|
-
return { continue: true };
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
try {
|
|
219
|
-
const response = await handler(request);
|
|
220
|
-
|
|
221
|
-
if (!response || response.type === 'next') {
|
|
222
|
-
// Apply any headers from middleware
|
|
223
|
-
if (response?.headers) {
|
|
224
|
-
for (const [key, value] of response.headers) {
|
|
225
|
-
res.setHeader(key, value);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
return { continue: true };
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (response.type === 'redirect') {
|
|
232
|
-
res.writeHead(response.status, { Location: response.url });
|
|
233
|
-
res.end();
|
|
234
|
-
return { continue: false };
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (response.type === 'rewrite') {
|
|
238
|
-
// Modify the request URL internally
|
|
239
|
-
req.url = response.url;
|
|
240
|
-
return { continue: true, rewritten: true };
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if (response.type === 'response') {
|
|
244
|
-
// Apply headers
|
|
245
|
-
for (const [key, value] of response.headers) {
|
|
246
|
-
res.setHeader(key, value);
|
|
247
|
-
}
|
|
248
|
-
res.writeHead(response.status);
|
|
249
|
-
res.end(response.body);
|
|
250
|
-
return { continue: false };
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
return { continue: true };
|
|
254
|
-
|
|
255
|
-
} catch (error) {
|
|
256
|
-
console.error('Middleware error:', error);
|
|
257
|
-
return { continue: true, error };
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Matches a path against a pattern
|
|
263
|
-
*/
|
|
264
|
-
function matchPath(pathname, pattern) {
|
|
265
|
-
// Convert pattern to regex
|
|
266
|
-
let regex = pattern
|
|
267
|
-
.replace(/\*/g, '.*')
|
|
268
|
-
.replace(/:path\*/g, '.*')
|
|
269
|
-
.replace(/:(\w+)/g, '[^/]+');
|
|
270
|
-
|
|
271
|
-
regex = `^${regex}$`;
|
|
272
|
-
|
|
273
|
-
return new RegExp(regex).test(pathname);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* Compose multiple middleware functions
|
|
278
|
-
*/
|
|
279
|
-
export function composeMiddleware(...middlewares) {
|
|
280
|
-
return async (request) => {
|
|
281
|
-
for (const middleware of middlewares) {
|
|
282
|
-
const response = await middleware(request);
|
|
283
|
-
|
|
284
|
-
if (response && response.type !== 'next') {
|
|
285
|
-
return response;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
return MiddlewareResponse.next();
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* Built-in middleware helpers
|
|
295
|
-
*/
|
|
296
|
-
export const middlewares = {
|
|
297
|
-
/**
|
|
298
|
-
* CORS middleware
|
|
299
|
-
*/
|
|
300
|
-
cors(options: { origin?: string; methods?: string; headers?: string; credentials?: boolean } = {}) {
|
|
301
|
-
const {
|
|
302
|
-
origin = '*',
|
|
303
|
-
methods = 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
|
304
|
-
headers = 'Content-Type,Authorization',
|
|
305
|
-
credentials = false
|
|
306
|
-
} = options;
|
|
307
|
-
|
|
308
|
-
return (request) => {
|
|
309
|
-
const response = MiddlewareResponse.next({
|
|
310
|
-
headers: {
|
|
311
|
-
'Access-Control-Allow-Origin': origin,
|
|
312
|
-
'Access-Control-Allow-Methods': methods,
|
|
313
|
-
'Access-Control-Allow-Headers': headers,
|
|
314
|
-
...(credentials && { 'Access-Control-Allow-Credentials': 'true' })
|
|
315
|
-
}
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
// Handle preflight
|
|
319
|
-
if (request.method === 'OPTIONS') {
|
|
320
|
-
return MiddlewareResponse.json({}, { status: 204, headers: Object.fromEntries(response.headers) });
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
return response;
|
|
324
|
-
};
|
|
325
|
-
},
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* Basic auth middleware
|
|
329
|
-
*/
|
|
330
|
-
basicAuth(options) {
|
|
331
|
-
const { username, password, realm = 'Protected' } = options;
|
|
332
|
-
const expected = Buffer.from(`${username}:${password}`).toString('base64');
|
|
333
|
-
|
|
334
|
-
return (request) => {
|
|
335
|
-
const auth = request.header('authorization');
|
|
336
|
-
|
|
337
|
-
if (!auth || !auth.startsWith('Basic ')) {
|
|
338
|
-
return MiddlewareResponse.html('Unauthorized', {
|
|
339
|
-
status: 401,
|
|
340
|
-
headers: { 'WWW-Authenticate': `Basic realm="${realm}"` }
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
const provided = auth.slice(6);
|
|
345
|
-
if (provided !== expected) {
|
|
346
|
-
return MiddlewareResponse.html('Unauthorized', { status: 401 });
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
return MiddlewareResponse.next();
|
|
350
|
-
};
|
|
351
|
-
},
|
|
352
|
-
|
|
353
|
-
/**
|
|
354
|
-
* Rate limiting middleware
|
|
355
|
-
*/
|
|
356
|
-
rateLimit(options: { windowMs?: number; max?: number } = {}) {
|
|
357
|
-
const { windowMs = 60000, max = 100 } = options;
|
|
358
|
-
const requests = new Map();
|
|
359
|
-
|
|
360
|
-
return (request) => {
|
|
361
|
-
const ip = request.header('x-forwarded-for') || 'unknown';
|
|
362
|
-
const now = Date.now();
|
|
363
|
-
|
|
364
|
-
// Clean old entries
|
|
365
|
-
for (const [key, data] of requests) {
|
|
366
|
-
if (now - data.start > windowMs) {
|
|
367
|
-
requests.delete(key);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
// Check rate limit
|
|
372
|
-
const data = requests.get(ip) || { count: 0, start: now };
|
|
373
|
-
data.count++;
|
|
374
|
-
requests.set(ip, data);
|
|
375
|
-
|
|
376
|
-
if (data.count > max) {
|
|
377
|
-
return MiddlewareResponse.json(
|
|
378
|
-
{ error: 'Too many requests' },
|
|
379
|
-
{ status: 429 }
|
|
380
|
-
);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
return MiddlewareResponse.next({
|
|
384
|
-
headers: {
|
|
385
|
-
'X-RateLimit-Limit': String(max),
|
|
386
|
-
'X-RateLimit-Remaining': String(max - data.count)
|
|
387
|
-
}
|
|
388
|
-
});
|
|
389
|
-
};
|
|
390
|
-
},
|
|
391
|
-
|
|
392
|
-
/**
|
|
393
|
-
* Logging middleware
|
|
394
|
-
*/
|
|
395
|
-
logger(options: { format?: string } = {}) {
|
|
396
|
-
const { format = 'combined' } = options;
|
|
397
|
-
|
|
398
|
-
return (request) => {
|
|
399
|
-
const start = Date.now();
|
|
400
|
-
const { method, pathname } = request;
|
|
401
|
-
|
|
402
|
-
console.log(`→ ${method} ${pathname}`);
|
|
403
|
-
|
|
404
|
-
return MiddlewareResponse.next();
|
|
405
|
-
};
|
|
406
|
-
}
|
|
407
|
-
};
|
|
408
|
-
|
|
409
|
-
export default {
|
|
410
|
-
MiddlewareRequest,
|
|
411
|
-
MiddlewareResponse,
|
|
412
|
-
loadMiddleware,
|
|
413
|
-
runMiddleware,
|
|
414
|
-
composeMiddleware,
|
|
415
|
-
middlewares
|
|
416
|
-
};
|