@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 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,7 @@
1
+ import { Plugin } from "../plugins.js";
2
+ interface Opts {
3
+ accessControlAllowMethods?: boolean;
4
+ accessControlAllowHeaders?: boolean;
5
+ }
6
+ export declare function createCORSPlugin(allowedOrigins: string[], opts?: Opts): Plugin;
7
+ export {};
@@ -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,2 @@
1
+ import { Plugin } from "../plugins.js";
2
+ export declare function createRateLimiterPlugin(maxRequests: number, windowMs: number): Plugin;
@@ -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
+ }
@@ -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";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wxn0brp/falcon-frame",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "author": "wxn0brP",