@wxn0brp/falcon-frame 0.4.1 → 0.5.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 CHANGED
@@ -4,8 +4,6 @@
4
4
 
5
5
  **FalconFrame** is a minimalist web framework inspired by the middleware-first style, with routing similar to Express, but written in pure TypeScript. It supports static files, data validation, and cookie management, all without external dependencies apart from a logger.
6
6
 
7
- ---
8
-
9
7
  ## ✨ Features
10
8
 
11
9
  - 🚀 Zero-dependency routing engine
@@ -15,8 +13,6 @@
15
13
  - 🍪 Cookie management
16
14
  - 🔍 Debuggable logger
17
15
 
18
- ---
19
-
20
16
  ## 📦 Installation
21
17
 
22
18
  ```bash
@@ -27,16 +23,9 @@ yarn add @wxn0brp/falcon-frame
27
23
 
28
24
  ```ts
29
25
  import FalconFrame from "@wxn0brp/falcon-frame";
30
- import { createCORSPlugin } from "@wxn0brp/falcon-frame/plugins/cors";
31
26
  const app = new FalconFrame();
32
27
 
33
- const pluginSystem = new PluginSystem();
34
-
35
- // Initialize CORS plugin
36
- pluginSystem.register(createCORSPlugin(["http://localhost:3000", "https://example.com"]));
37
-
38
- // Register plugins
39
- app.use(pluginSystem.getRouteHandler());
28
+ app.setOrigin("*");
40
29
 
41
30
  const USERS = {
42
31
  admin: "hunter2",
@@ -112,9 +101,8 @@ app.all("*", (req, res) => {
112
101
  res.status(404).json({ error: "Not found" });
113
102
  });
114
103
 
115
- app.listen(3000, () => {
116
- console.log("Server running on http://localhost:3000");
117
- });
104
+ // Start the server (env.PORT || 3000)
105
+ app.l(3000);
118
106
  ```
119
107
 
120
108
  ## 📜 License
package/dist/cors.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { RouteHandler } from "./types.js";
2
+ export interface Opts {
3
+ accessControlAllowMethods?: boolean;
4
+ accessControlAllowHeaders?: boolean;
5
+ headers?: Record<string, string>;
6
+ }
7
+ export declare function createCORS(allowedOrigins: string[], opts?: Opts): RouteHandler;
package/dist/cors.js ADDED
@@ -0,0 +1,39 @@
1
+ function setHeader(res, opts) {
2
+ if (opts.accessControlAllowMethods)
3
+ res.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
4
+ if (opts.accessControlAllowHeaders)
5
+ res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
6
+ }
7
+ export function createCORS(allowedOrigins, opts = {}) {
8
+ opts = {
9
+ accessControlAllowMethods: true,
10
+ accessControlAllowHeaders: true,
11
+ ...opts,
12
+ };
13
+ return (req, res, next) => {
14
+ if (opts.headers) {
15
+ for (const [key, value] of Object.entries(opts.headers)) {
16
+ res.setHeader(key, value);
17
+ }
18
+ }
19
+ if (allowedOrigins.includes("*")) {
20
+ res.setHeader("Access-Control-Allow-Origin", "*");
21
+ setHeader(res, opts);
22
+ if (req.method === "OPTIONS") {
23
+ res.statusCode = 204;
24
+ return res.end();
25
+ }
26
+ return next();
27
+ }
28
+ const origin = req.headers.origin;
29
+ if (origin && allowedOrigins.includes(origin)) {
30
+ res.setHeader("Access-Control-Allow-Origin", origin);
31
+ setHeader(res, opts);
32
+ if (req.method === "OPTIONS") {
33
+ res.statusCode = 204;
34
+ return res.end();
35
+ }
36
+ }
37
+ next();
38
+ };
39
+ }
package/dist/index.d.ts CHANGED
@@ -1,11 +1,9 @@
1
1
  import { Logger, LoggerOptions } from "@wxn0brp/lucerna-log";
2
2
  import http from "http";
3
- import { PluginSystem } from "./plugin.js";
4
3
  import { renderHTML } from "./render.js";
5
4
  import { FFResponse } from "./res.js";
6
5
  import { Router } from "./router.js";
7
- import type { BeforeHandleRequest, FFRequest, RouteHandler } from "./types.js";
8
- export type EngineCallback = (path: string, options: any, callback: (e: any, rendered?: string) => void) => void;
6
+ import type { BeforeHandleRequest, EngineCallback, FFRequest, RouteHandler } from "./types.js";
9
7
  export interface Opts {
10
8
  loggerOpts?: LoggerOptions;
11
9
  bodyLimit?: string;
@@ -34,8 +32,13 @@ export declare class FalconFrame<Vars extends Record<string, any> = any> extends
34
32
  * app.setOrigin(["http://example.com", "https://example.com"]);
35
33
  */
36
34
  setOrigin(origin?: string[] | string): void;
35
+ /**
36
+ * Listens to the specified port, or the environment variable PORT if available.
37
+ * @param port - The port number to listen to.
38
+ * @returns The server object returned by the listen method.
39
+ */
40
+ l(port: number): http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
37
41
  }
38
42
  export default FalconFrame;
39
43
  export * as Helpers from "./helpers.js";
40
- export * as PluginsEngine from "./plugin.js";
41
- export { FFRequest, FFResponse, PluginSystem, renderHTML, RouteHandler, Router };
44
+ export { FFRequest, FFResponse, renderHTML, RouteHandler, Router };
package/dist/index.js CHANGED
@@ -1,12 +1,11 @@
1
1
  import { Logger } from "@wxn0brp/lucerna-log";
2
2
  import http from "http";
3
- import { PluginSystem } from "./plugin.js";
4
- import { createCORSPlugin } from "./plugins/cors.js";
3
+ import { json, urlencoded } from "./body.js";
4
+ import { createCORS } from "./cors.js";
5
5
  import { renderHTML } from "./render.js";
6
6
  import { handleRequest } from "./req.js";
7
7
  import { FFResponse } from "./res.js";
8
8
  import { Router } from "./router.js";
9
- import { json, urlencoded } from "./body.js";
10
9
  export class FalconFrame extends Router {
11
10
  logger;
12
11
  bodyParsers = [];
@@ -89,10 +88,17 @@ export class FalconFrame extends Router {
89
88
  * app.setOrigin(["http://example.com", "https://example.com"]);
90
89
  */
91
90
  setOrigin(origin = "*") {
92
- this.use(createCORSPlugin(Array.isArray(origin) ? origin : [origin]).process);
91
+ this.use(createCORS(Array.isArray(origin) ? origin : [origin]));
92
+ }
93
+ /**
94
+ * Listens to the specified port, or the environment variable PORT if available.
95
+ * @param port - The port number to listen to.
96
+ * @returns The server object returned by the listen method.
97
+ */
98
+ l(port) {
99
+ return this.listen(+process.env.PORT || port, true);
93
100
  }
94
101
  }
95
102
  export default FalconFrame;
96
103
  export * as Helpers from "./helpers.js";
97
- export * as PluginsEngine from "./plugin.js";
98
- export { FFResponse, PluginSystem, renderHTML, Router };
104
+ export { FFResponse, renderHTML, Router };
package/dist/router.d.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { PluginSystem } from "./plugin.js";
2
1
  import { SSEManager } from "./sse.js";
3
2
  import { Method, Middleware, RouteHandler, StaticServeOptions } from "./types.js";
4
- export type MiddlewareFn = RouteHandler | Router | PluginSystem;
3
+ export type MiddlewareFn = RouteHandler | Router | {
4
+ getRouteHandler(): RouteHandler;
5
+ };
5
6
  export declare class Router {
6
7
  middlewares: Middleware[];
7
8
  addRoute(method: Method, path: string, ...handlers: RouteHandler[]): number;
package/dist/router.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { handleStaticFiles } from "./helpers.js";
2
- import { PluginSystem } from "./plugin.js";
3
2
  import { SSEManager } from "./sse.js";
4
3
  export class Router {
5
4
  middlewares = [];
@@ -9,7 +8,7 @@ export class Router {
9
8
  return this.middlewares.push({ path, middleware: handler, method });
10
9
  }
11
10
  use(path = "/", middlewareFn, method = "all") {
12
- if (typeof path === "function" || path instanceof Router || path instanceof PluginSystem) {
11
+ if (typeof path !== "string") {
13
12
  middlewareFn = path;
14
13
  path = "/";
15
14
  }
@@ -22,7 +21,7 @@ export class Router {
22
21
  if (middlewareFn instanceof Router) {
23
22
  middleware.router = middlewareFn.middlewares;
24
23
  }
25
- else if (middlewareFn instanceof PluginSystem) {
24
+ else if ("getRouteHandler" in middlewareFn) {
26
25
  middleware.middleware = middlewareFn.getRouteHandler();
27
26
  }
28
27
  else {
package/dist/types.d.ts CHANGED
@@ -60,3 +60,4 @@ export interface StaticServeOptions {
60
60
  etag?: boolean;
61
61
  errorIfDirNotFound?: boolean;
62
62
  }
63
+ export type EngineCallback = (path: string, options: any, callback: (e: any, rendered?: string) => void) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wxn0brp/falcon-frame",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "author": "wxn0brP",
package/dist/plugin.d.ts DELETED
@@ -1,54 +0,0 @@
1
- import { RouteHandler } from "./types.js";
2
- export type PluginId = string;
3
- export interface Plugin {
4
- id: PluginId;
5
- process: RouteHandler;
6
- before?: PluginId | PluginId[];
7
- after?: PluginId | PluginId[];
8
- }
9
- export interface PluginOptions {
10
- before?: PluginId | PluginId[];
11
- after?: PluginId | PluginId[];
12
- optional?: boolean;
13
- }
14
- /**
15
- * @deprecated
16
- * The `PluginSystem` class is deprecated and should not be used in new projects.
17
- *
18
- * ⚠️ Notes:
19
- * - It may be refactored in the future, but any refactor will introduce breaking changes.
20
- * - If your system currently relies on it and it works, you may continue using it.
21
- * - If it does not work in your setup, it is recommended to skip it or wait for an update.
22
- */
23
- export declare class PluginSystem {
24
- private plugins;
25
- private executionOrder;
26
- /**
27
- * Registers a new plugin in the system
28
- * @param plugin - Plugin to register
29
- * @param options - Options for positioning plugins
30
- */
31
- register(plugin: Plugin, options?: PluginOptions): void;
32
- /**
33
- * Updates the execution order of plugins
34
- * @param pluginId - ID of the plugin to position
35
- * @param options - Options for positioning
36
- */
37
- private updateExecutionOrder;
38
- /**
39
- * Returns a RouteHandler that executes all registered plugins in the correct order
40
- */
41
- getRouteHandler(): RouteHandler;
42
- /**
43
- * Recursively executes plugins in the correct order
44
- * @param req - Request object
45
- * @param res - Response object
46
- * @param next - Next function
47
- * @param index - Current index in the execution order
48
- */
49
- private executePlugins;
50
- /**
51
- * Returns the list of plugins in the execution order
52
- */
53
- getPluginsInOrder(): Plugin[];
54
- }
package/dist/plugin.js DELETED
@@ -1,101 +0,0 @@
1
- /**
2
- * @deprecated
3
- * The `PluginSystem` class is deprecated and should not be used in new projects.
4
- *
5
- * ⚠️ Notes:
6
- * - It may be refactored in the future, but any refactor will introduce breaking changes.
7
- * - If your system currently relies on it and it works, you may continue using it.
8
- * - If it does not work in your setup, it is recommended to skip it or wait for an update.
9
- */
10
- export class PluginSystem {
11
- plugins = [];
12
- executionOrder = [];
13
- /**
14
- * Registers a new plugin in the system
15
- * @param plugin - Plugin to register
16
- * @param options - Options for positioning plugins
17
- */
18
- register(plugin, options) {
19
- if (this.plugins.some((p) => p.id === plugin.id)) {
20
- throw new Error(`Plugin with id '${plugin.id}' already registered`);
21
- }
22
- this.plugins.push(plugin);
23
- this.updateExecutionOrder(plugin, options);
24
- }
25
- /**
26
- * Updates the execution order of plugins
27
- * @param pluginId - ID of the plugin to position
28
- * @param options - Options for positioning
29
- */
30
- updateExecutionOrder(plugin, options) {
31
- const pluginId = plugin.id;
32
- if (this.executionOrder.includes(pluginId))
33
- return;
34
- const resolveTarget = (target) => {
35
- if (!target)
36
- return null;
37
- const list = Array.isArray(target) ? target : [target];
38
- return list.find((id) => this.executionOrder.includes(id)) || null;
39
- };
40
- const beforeTarget = resolveTarget(options?.before || plugin.before);
41
- const afterTarget = resolveTarget(options?.after || plugin.after);
42
- if (beforeTarget) {
43
- const index = this.executionOrder.indexOf(beforeTarget);
44
- this.executionOrder.splice(index, 0, pluginId);
45
- }
46
- else if (afterTarget) {
47
- const index = this.executionOrder.indexOf(afterTarget);
48
- this.executionOrder.splice(index + 1, 0, pluginId);
49
- }
50
- else if (options?.before || options?.after) {
51
- if (options.optional) {
52
- this.executionOrder.push(pluginId); // fallback: add to the end
53
- }
54
- else {
55
- throw new Error(`Plugin dependency not found for '${pluginId}': ` +
56
- `before=${JSON.stringify(options?.before)}, ` +
57
- `after=${JSON.stringify(options?.after)}`);
58
- }
59
- }
60
- else {
61
- this.executionOrder.push(pluginId);
62
- }
63
- }
64
- /**
65
- * Returns a RouteHandler that executes all registered plugins in the correct order
66
- */
67
- getRouteHandler() {
68
- return (req, res, next) => {
69
- this.executePlugins(req, res, next, 0);
70
- };
71
- }
72
- /**
73
- * Recursively executes plugins in the correct order
74
- * @param req - Request object
75
- * @param res - Response object
76
- * @param next - Next function
77
- * @param index - Current index in the execution order
78
- */
79
- executePlugins(req, res, next, index) {
80
- if (index >= this.executionOrder.length) {
81
- next();
82
- return;
83
- }
84
- const pluginId = this.executionOrder[index];
85
- const plugin = this.plugins.find((p) => p.id === pluginId);
86
- if (!plugin) {
87
- throw new Error(`Plugin '${pluginId}' not found`);
88
- }
89
- plugin.process(req, res, () => {
90
- this.executePlugins(req, res, next, index + 1);
91
- });
92
- }
93
- /**
94
- * Returns the list of plugins in the execution order
95
- */
96
- getPluginsInOrder() {
97
- return this.executionOrder
98
- .map((id) => this.plugins.find((p) => p.id === id))
99
- .filter((p) => p !== undefined);
100
- }
101
- }
@@ -1,8 +0,0 @@
1
- import { Plugin } from "../plugin.js";
2
- interface Opts {
3
- accessControlAllowMethods?: boolean;
4
- accessControlAllowHeaders?: boolean;
5
- headers?: Record<string, string>;
6
- }
7
- export declare function createCORSPlugin(allowedOrigins: string[], opts?: Opts): Plugin;
8
- export {};
@@ -1,42 +0,0 @@
1
- function setHeader(res, opts) {
2
- if (opts.accessControlAllowMethods)
3
- res.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
4
- if (opts.accessControlAllowHeaders)
5
- res.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
6
- }
7
- export function createCORSPlugin(allowedOrigins, opts = {}) {
8
- opts = {
9
- accessControlAllowMethods: true,
10
- accessControlAllowHeaders: true,
11
- ...opts,
12
- };
13
- return {
14
- id: "cors",
15
- process: (req, res, next) => {
16
- if (opts.headers) {
17
- for (const [key, value] of Object.entries(opts.headers)) {
18
- res.setHeader(key, value);
19
- }
20
- }
21
- if (allowedOrigins.includes("*")) {
22
- res.setHeader("Access-Control-Allow-Origin", "*");
23
- setHeader(res, opts);
24
- if (req.method === "OPTIONS") {
25
- res.statusCode = 204;
26
- return res.end();
27
- }
28
- return next();
29
- }
30
- const origin = req.headers.origin;
31
- if (origin && allowedOrigins.includes(origin)) {
32
- res.setHeader("Access-Control-Allow-Origin", origin);
33
- setHeader(res, opts);
34
- if (req.method === "OPTIONS") {
35
- res.statusCode = 204;
36
- return res.end();
37
- }
38
- }
39
- next();
40
- },
41
- };
42
- }
@@ -1,2 +0,0 @@
1
- import { Plugin } from "../plugin.js";
2
- export declare function createRateLimiterPlugin(maxRequests: number, windowMs: number): Plugin;
@@ -1,23 +0,0 @@
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
- }
@@ -1,2 +0,0 @@
1
- import { Plugin } from "../plugin.js";
2
- export declare function createSecurityPlugin(): Plugin;
@@ -1,14 +0,0 @@
1
- export function createSecurityPlugin() {
2
- return {
3
- id: "security",
4
- process: (req, res, next) => {
5
- res.setHeader("X-Content-Type-Options", "nosniff");
6
- res.setHeader("X-Frame-Options", "SAMEORIGIN");
7
- res.setHeader("Referrer-Policy", "no-referrer");
8
- res.setHeader("X-XSS-Protection", "1; mode=block");
9
- res.setHeader("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
10
- next();
11
- },
12
- after: "cors",
13
- };
14
- }