@wxn0brp/falcon-frame 0.0.5 → 0.0.7
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/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/plugins/cors.d.ts +7 -0
- package/dist/plugins/cors.js +25 -0
- package/dist/plugins/rateLimit.d.ts +2 -0
- package/dist/plugins/rateLimit.js +23 -0
- package/dist/plugins.d.ts +44 -0
- package/dist/plugins.js +93 -0
- package/dist/test.js +17 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import http from "http";
|
|
|
3
3
|
import { FFResponse } from "./res.js";
|
|
4
4
|
import { FFRequest, Method, Middleware, RouteHandler } from "./types.js";
|
|
5
5
|
import { renderHTML } from "./render.js";
|
|
6
|
+
import { PluginSystem } from "./plugins.js";
|
|
6
7
|
export declare class FalconFrame {
|
|
7
8
|
middlewares: Middleware[];
|
|
8
9
|
logger: Logger;
|
|
@@ -19,4 +20,4 @@ export declare class FalconFrame {
|
|
|
19
20
|
getApp(): (req: any, res: any) => void;
|
|
20
21
|
}
|
|
21
22
|
export default FalconFrame;
|
|
22
|
-
export { FFResponse, FFRequest, RouteHandler, renderHTML, };
|
|
23
|
+
export { FFResponse, FFRequest, RouteHandler, renderHTML, PluginSystem, };
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { handleRequest } from "./req.js";
|
|
|
5
5
|
import { FFResponse } from "./res.js";
|
|
6
6
|
import { FFRequest } from "./types.js";
|
|
7
7
|
import { renderHTML } from "./render.js";
|
|
8
|
+
import { PluginSystem } from "./plugins.js";
|
|
8
9
|
export class FalconFrame {
|
|
9
10
|
middlewares = [];
|
|
10
11
|
logger;
|
|
@@ -67,4 +68,4 @@ export class FalconFrame {
|
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
70
|
export default FalconFrame;
|
|
70
|
-
export { FFResponse, FFRequest, renderHTML, };
|
|
71
|
+
export { FFResponse, FFRequest, renderHTML, PluginSystem, };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function createCORSPlugin(allowedOrigins, opts = {}) {
|
|
2
|
+
opts = {
|
|
3
|
+
accessControlAllowMethods: true,
|
|
4
|
+
accessControlAllowHeaders: true,
|
|
5
|
+
...opts,
|
|
6
|
+
};
|
|
7
|
+
return {
|
|
8
|
+
id: "cors",
|
|
9
|
+
process: (req, res, next) => {
|
|
10
|
+
const origin = req.headers.origin;
|
|
11
|
+
if (origin && allowedOrigins.includes(origin)) {
|
|
12
|
+
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
13
|
+
if (opts.accessControlAllowMethods)
|
|
14
|
+
res.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
|
|
15
|
+
if (opts.accessControlAllowHeaders)
|
|
16
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
|
|
17
|
+
}
|
|
18
|
+
if (req.method === "OPTIONS") {
|
|
19
|
+
res.statusCode = 204;
|
|
20
|
+
return res.end();
|
|
21
|
+
}
|
|
22
|
+
next();
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export function createRateLimiterPlugin(maxRequests, windowMs) {
|
|
2
|
+
const rateLimitMap = new Map();
|
|
3
|
+
return {
|
|
4
|
+
id: "rateLimiter",
|
|
5
|
+
process: (req, res, next) => {
|
|
6
|
+
const ip = req.socket.remoteAddress ?? "unknown";
|
|
7
|
+
const now = Date.now();
|
|
8
|
+
const record = rateLimitMap.get(ip);
|
|
9
|
+
if (!record || now - record.lastRequest > windowMs) {
|
|
10
|
+
rateLimitMap.set(ip, { count: 1, lastRequest: now });
|
|
11
|
+
return next();
|
|
12
|
+
}
|
|
13
|
+
if (record.count >= maxRequests) {
|
|
14
|
+
res.statusCode = 429;
|
|
15
|
+
return res.end("Too Many Requests");
|
|
16
|
+
}
|
|
17
|
+
record.count++;
|
|
18
|
+
record.lastRequest = now;
|
|
19
|
+
rateLimitMap.set(ip, record);
|
|
20
|
+
next();
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { RouteHandler } from "./types.js";
|
|
2
|
+
export type PluginId = string;
|
|
3
|
+
export interface Plugin {
|
|
4
|
+
id: PluginId;
|
|
5
|
+
process: RouteHandler;
|
|
6
|
+
priority?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface PluginOptions {
|
|
9
|
+
before?: PluginId | PluginId[];
|
|
10
|
+
after?: PluginId | PluginId[];
|
|
11
|
+
optional?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare class PluginSystem {
|
|
14
|
+
private plugins;
|
|
15
|
+
private executionOrder;
|
|
16
|
+
/**
|
|
17
|
+
* Registers a new plugin in the system
|
|
18
|
+
* @param plugin - Plugin to register
|
|
19
|
+
* @param options - Options for positioning plugins
|
|
20
|
+
*/
|
|
21
|
+
register(plugin: Plugin, options?: PluginOptions): void;
|
|
22
|
+
/**
|
|
23
|
+
* Updates the execution order of plugins
|
|
24
|
+
* @param pluginId - ID of the plugin to position
|
|
25
|
+
* @param options - Options for positioning
|
|
26
|
+
*/
|
|
27
|
+
private updateExecutionOrder;
|
|
28
|
+
/**
|
|
29
|
+
* Returns a RouteHandler that executes all registered plugins in the correct order
|
|
30
|
+
*/
|
|
31
|
+
getRouteHandler(): RouteHandler;
|
|
32
|
+
/**
|
|
33
|
+
* Recursively executes plugins in the correct order
|
|
34
|
+
* @param req - Request object
|
|
35
|
+
* @param res - Response object
|
|
36
|
+
* @param next - Next function
|
|
37
|
+
* @param index - Current index in the execution order
|
|
38
|
+
*/
|
|
39
|
+
private executePlugins;
|
|
40
|
+
/**
|
|
41
|
+
* Returns the list of plugins in the execution order
|
|
42
|
+
*/
|
|
43
|
+
getPluginsInOrder(): Plugin[];
|
|
44
|
+
}
|
package/dist/plugins.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
export class PluginSystem {
|
|
2
|
+
plugins = [];
|
|
3
|
+
executionOrder = [];
|
|
4
|
+
/**
|
|
5
|
+
* Registers a new plugin in the system
|
|
6
|
+
* @param plugin - Plugin to register
|
|
7
|
+
* @param options - Options for positioning plugins
|
|
8
|
+
*/
|
|
9
|
+
register(plugin, options) {
|
|
10
|
+
if (this.plugins.some(p => p.id === plugin.id)) {
|
|
11
|
+
throw new Error(`Plugin with id '${plugin.id}' already registered`);
|
|
12
|
+
}
|
|
13
|
+
// Add plugin to the list
|
|
14
|
+
this.plugins.push(plugin);
|
|
15
|
+
// Update the execution order
|
|
16
|
+
this.updateExecutionOrder(plugin.id, options);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Updates the execution order of plugins
|
|
20
|
+
* @param pluginId - ID of the plugin to position
|
|
21
|
+
* @param options - Options for positioning
|
|
22
|
+
*/
|
|
23
|
+
updateExecutionOrder(pluginId, options) {
|
|
24
|
+
if (this.executionOrder.includes(pluginId))
|
|
25
|
+
return;
|
|
26
|
+
const resolveTarget = (target) => {
|
|
27
|
+
if (!target)
|
|
28
|
+
return null;
|
|
29
|
+
const list = Array.isArray(target) ? target : [target];
|
|
30
|
+
return list.find(id => this.executionOrder.includes(id)) || null;
|
|
31
|
+
};
|
|
32
|
+
const beforeTarget = resolveTarget(options?.before);
|
|
33
|
+
const afterTarget = resolveTarget(options?.after);
|
|
34
|
+
if (beforeTarget) {
|
|
35
|
+
const index = this.executionOrder.indexOf(beforeTarget);
|
|
36
|
+
this.executionOrder.splice(index, 0, pluginId);
|
|
37
|
+
}
|
|
38
|
+
else if (afterTarget) {
|
|
39
|
+
const index = this.executionOrder.indexOf(afterTarget);
|
|
40
|
+
this.executionOrder.splice(index + 1, 0, pluginId);
|
|
41
|
+
}
|
|
42
|
+
else if (options?.before || options?.after) {
|
|
43
|
+
if (options.optional) {
|
|
44
|
+
this.executionOrder.push(pluginId); // fallback: add to the end
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
throw new Error(`Plugin dependency not found for '${pluginId}': ` +
|
|
48
|
+
`before=${JSON.stringify(options?.before)}, ` +
|
|
49
|
+
`after=${JSON.stringify(options?.after)}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
this.executionOrder.push(pluginId);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Returns a RouteHandler that executes all registered plugins in the correct order
|
|
58
|
+
*/
|
|
59
|
+
getRouteHandler() {
|
|
60
|
+
return (req, res, next) => {
|
|
61
|
+
this.executePlugins(req, res, next, 0);
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Recursively executes plugins in the correct order
|
|
66
|
+
* @param req - Request object
|
|
67
|
+
* @param res - Response object
|
|
68
|
+
* @param next - Next function
|
|
69
|
+
* @param index - Current index in the execution order
|
|
70
|
+
*/
|
|
71
|
+
executePlugins(req, res, next, index) {
|
|
72
|
+
if (index >= this.executionOrder.length) {
|
|
73
|
+
next();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const pluginId = this.executionOrder[index];
|
|
77
|
+
const plugin = this.plugins.find(p => p.id === pluginId);
|
|
78
|
+
if (!plugin) {
|
|
79
|
+
throw new Error(`Plugin '${pluginId}' not found`);
|
|
80
|
+
}
|
|
81
|
+
plugin.process(req, res, () => {
|
|
82
|
+
this.executePlugins(req, res, next, index + 1);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Returns the list of plugins in the execution order
|
|
87
|
+
*/
|
|
88
|
+
getPluginsInOrder() {
|
|
89
|
+
return this.executionOrder
|
|
90
|
+
.map(id => this.plugins.find(p => p.id === id))
|
|
91
|
+
.filter((p) => p !== undefined);
|
|
92
|
+
}
|
|
93
|
+
}
|
package/dist/test.js
CHANGED
|
@@ -1,11 +1,27 @@
|
|
|
1
|
-
import FalconFrame from "./index.js";
|
|
1
|
+
import FalconFrame, { PluginSystem } from "./index.js";
|
|
2
2
|
const app = new FalconFrame({
|
|
3
3
|
logLevel: "INFO",
|
|
4
4
|
});
|
|
5
|
+
const pluginSystem = new PluginSystem();
|
|
6
|
+
pluginSystem.register({
|
|
7
|
+
id: "logger",
|
|
8
|
+
process: (req, res, next) => {
|
|
9
|
+
console.log(`Request to ${req.url} with body ${JSON.stringify(req.body)}`);
|
|
10
|
+
next();
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
pluginSystem.register({
|
|
14
|
+
id: "logger2",
|
|
15
|
+
process: (req, res, next) => {
|
|
16
|
+
req.body.test = "test";
|
|
17
|
+
next();
|
|
18
|
+
}
|
|
19
|
+
}, { before: "logger" });
|
|
5
20
|
app.use((req, res, next) => {
|
|
6
21
|
console.log(`[${req.method}] ${req.path}`);
|
|
7
22
|
next();
|
|
8
23
|
});
|
|
24
|
+
app.use(pluginSystem.getRouteHandler());
|
|
9
25
|
app.static("/", "public");
|
|
10
26
|
app.get("/hello", (req, res) => {
|
|
11
27
|
const name = req.query.name || "World";
|