@wxn0brp/falcon-frame 0.0.14 → 0.0.16

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
@@ -27,8 +27,17 @@ yarn add @wxn0brp/falcon-frame
27
27
 
28
28
  ```ts
29
29
  import FalconFrame from "@wxn0brp/falcon-frame";
30
+ import { createCORSPlugin } from "@wxn0brp/falcon-frame/plugins/cors";
30
31
  const app = new FalconFrame();
31
32
 
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());
40
+
32
41
  const USERS = {
33
42
  admin: "hunter2",
34
43
  };
package/dist/helpers.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Body, Cookies, RouteHandler } from "./types.js";
2
2
  export declare function parseCookies(cookieHeader: string): Cookies;
3
3
  export declare function parseBody(contentType: string, body: string): Body;
4
- export declare function getContentType(filePath: string): string;
5
- export declare function handleStaticFiles(apiPath: string, dirPath: string): RouteHandler;
4
+ export declare function getContentType(filePath: string, utf8?: boolean): string;
5
+ export declare function handleStaticFiles(dirPath: string, utf8?: boolean): RouteHandler;
package/dist/helpers.js CHANGED
@@ -24,7 +24,7 @@ export function parseBody(contentType, body) {
24
24
  }
25
25
  return {};
26
26
  }
27
- export function getContentType(filePath) {
27
+ function _getContentType(filePath) {
28
28
  const ext = path.extname(filePath).toLowerCase();
29
29
  switch (ext) {
30
30
  case ".html":
@@ -44,28 +44,42 @@ export function getContentType(filePath) {
44
44
  return "image/gif";
45
45
  case ".svg":
46
46
  return "image/svg+xml";
47
+ case ".ico":
48
+ return "image/x-icon";
49
+ case ".txt":
50
+ return "text/plain";
51
+ case ".pdf":
52
+ return "application/pdf";
47
53
  default:
48
54
  return "application/octet-stream";
49
55
  }
50
56
  }
51
- export function handleStaticFiles(apiPath, dirPath) {
57
+ export function getContentType(filePath, utf8 = false) {
58
+ let contentType = _getContentType(filePath);
59
+ if (utf8)
60
+ contentType += "; charset=utf-8";
61
+ return contentType;
62
+ }
63
+ export function handleStaticFiles(dirPath, utf8 = true) {
52
64
  return (req, res, next) => {
53
- if (!req.path.startsWith(apiPath))
65
+ if (req.method.toLowerCase() !== "get")
54
66
  return next();
55
- const filePath = path.join(dirPath, req.path.slice(apiPath.length));
67
+ const apiPath = req.middleware.path;
68
+ const filePath = path.join(dirPath, req.path.replace(apiPath, ""));
56
69
  if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
57
- res.setHeader("Content-Type", getContentType(filePath));
70
+ res.ct(getContentType(filePath, utf8));
58
71
  fs.createReadStream(filePath).pipe(res);
59
72
  return true;
60
73
  }
61
- if (req.path.endsWith("/")) {
62
- if (fs.existsSync(filePath) && fs.statSync(filePath).isDirectory()) {
63
- const indexPath = path.join(filePath, "index.html");
64
- if (fs.existsSync(indexPath) && fs.statSync(indexPath).isFile()) {
65
- res.setHeader("Content-Type", getContentType(indexPath));
66
- fs.createReadStream(indexPath).pipe(res);
67
- return true;
74
+ if (fs.existsSync(filePath) && fs.statSync(filePath).isDirectory()) {
75
+ const indexPath = path.join(filePath, "index.html");
76
+ if (fs.existsSync(indexPath) && fs.statSync(indexPath).isFile()) {
77
+ if (!req.path.endsWith("/")) {
78
+ res.redirect(req.path + "/");
68
79
  }
80
+ res.ct(getContentType(indexPath, utf8));
81
+ fs.createReadStream(indexPath).pipe(res);
82
+ return true;
69
83
  }
70
84
  }
71
85
  next();
package/dist/index.d.ts CHANGED
@@ -1,23 +1,18 @@
1
1
  import { Logger, LoggerOptions } from "@wxn0brp/lucerna-log";
2
2
  import http from "http";
3
- import { FFResponse } from "./res.js";
4
- import { AfterHandleRequest as BeforeHandleRequest, FFRequest, Method, Middleware, RouteHandler } from "./types.js";
5
- import { renderHTML } from "./render.js";
6
3
  import { PluginSystem } from "./plugins.js";
7
- export declare class FalconFrame {
8
- middlewares: Middleware[];
4
+ import { renderHTML } from "./render.js";
5
+ import { FFResponse } from "./res.js";
6
+ import { Router } from "./router.js";
7
+ import { BeforeHandleRequest, FFRequest, RouteHandler } from "./types.js";
8
+ export declare class FalconFrame extends Router {
9
9
  logger: Logger;
10
10
  constructor(loggerOpts?: LoggerOptions);
11
- addRoute(method: Method, path: string, ...handlers: RouteHandler[]): void;
12
- use(path?: string | RouteHandler, middleware?: RouteHandler, method?: Method): void;
13
- get(path: string, ...handlers: RouteHandler[]): void;
14
- post(path: string, ...handlers: RouteHandler[]): void;
15
- put(path: string, ...handlers: RouteHandler[]): void;
16
- delete(path: string, ...handlers: RouteHandler[]): void;
17
- all(path: string, ...handlers: RouteHandler[]): void;
18
- static(apiPath: string, dirPath: string): void;
19
11
  listen(port: number, callback?: () => void, beforeHandleRequest?: BeforeHandleRequest): http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
20
12
  getApp(beforeHandleRequest?: BeforeHandleRequest): (req: any, res: any) => Promise<void>;
21
13
  }
22
14
  export default FalconFrame;
23
- export { FFResponse, FFRequest, RouteHandler, renderHTML, PluginSystem, };
15
+ export { FFRequest, FFResponse, PluginSystem, renderHTML, RouteHandler, Router };
16
+ export * as Plugins from "./plugins/index.js";
17
+ export * as PluginsEngine from "./plugins.js";
18
+ export * as Helpers from "./helpers.js";
package/dist/index.js CHANGED
@@ -1,59 +1,20 @@
1
1
  import { Logger } from "@wxn0brp/lucerna-log";
2
2
  import http from "http";
3
- import { handleStaticFiles } from "./helpers.js";
3
+ import { PluginSystem } from "./plugins.js";
4
+ import { renderHTML } from "./render.js";
4
5
  import { handleRequest } from "./req.js";
5
6
  import { FFResponse } from "./res.js";
7
+ import { Router } from "./router.js";
6
8
  import { FFRequest } from "./types.js";
7
- import { renderHTML } from "./render.js";
8
- import { PluginSystem } from "./plugins.js";
9
- export class FalconFrame {
10
- middlewares = [];
9
+ export class FalconFrame extends Router {
11
10
  logger;
12
11
  constructor(loggerOpts) {
12
+ super();
13
13
  this.logger = new Logger({
14
14
  loggerName: "falcon-frame",
15
15
  ...loggerOpts
16
16
  });
17
17
  }
18
- addRoute(method, path, ...handlers) {
19
- const handler = handlers.pop();
20
- handlers.forEach(middleware => this.use(path, middleware));
21
- this.middlewares.push({ path, middleware: handler, method });
22
- }
23
- use(path = "/", middleware, method = "all") {
24
- if (typeof path === "function") {
25
- middleware = path;
26
- path = "/";
27
- }
28
- this.middlewares.push({ path, middleware, method, use: true });
29
- }
30
- get(path, ...handlers) {
31
- this.addRoute("get", path, ...handlers);
32
- }
33
- post(path, ...handlers) {
34
- this.addRoute("post", path, ...handlers);
35
- }
36
- put(path, ...handlers) {
37
- this.addRoute("put", path, ...handlers);
38
- }
39
- delete(path, ...handlers) {
40
- this.addRoute("delete", path, ...handlers);
41
- }
42
- all(path, ...handlers) {
43
- this.addRoute("all", path, ...handlers);
44
- }
45
- static(apiPath, dirPath) {
46
- this.middlewares.push({
47
- path: (apiPath + "/*").replace("//", "/"),
48
- method: "get",
49
- middleware: handleStaticFiles(apiPath, dirPath)
50
- });
51
- this.middlewares.push({
52
- path: apiPath,
53
- method: "get",
54
- middleware: handleStaticFiles(apiPath, dirPath)
55
- });
56
- }
57
18
  listen(port, callback, beforeHandleRequest) {
58
19
  const server = http.createServer(this.getApp(beforeHandleRequest));
59
20
  server.listen(port, callback);
@@ -71,4 +32,7 @@ export class FalconFrame {
71
32
  }
72
33
  }
73
34
  export default FalconFrame;
74
- export { FFResponse, FFRequest, renderHTML, PluginSystem, };
35
+ export { FFRequest, FFResponse, PluginSystem, renderHTML, Router };
36
+ export * as Plugins from "./plugins/index.js";
37
+ export * as PluginsEngine from "./plugins.js";
38
+ export * as Helpers from "./helpers.js";
@@ -0,0 +1,2 @@
1
+ export * from "./cors.js";
2
+ export * from "./rateLimit.js";
@@ -0,0 +1,2 @@
1
+ export * from "./cors.js";
2
+ export * from "./rateLimit.js";
package/dist/req.js CHANGED
@@ -9,7 +9,7 @@ export function handleRequest(req, res, FF) {
9
9
  res._ended = true;
10
10
  return originalEnd.call(res, ...any);
11
11
  };
12
- const { logger, middlewares } = FF;
12
+ const { logger } = FF;
13
13
  try {
14
14
  const [path, params] = (req.url || "").split("?");
15
15
  const normalizedPath = path.replace(/\/{2,}/g, "/");
@@ -26,6 +26,7 @@ export function handleRequest(req, res, FF) {
26
26
  req.params = {};
27
27
  req.valid = (schema) => validate(schema, req.body);
28
28
  logger.info(`Incoming request: ${req.method} ${req.url}`);
29
+ const middlewares = getMiddlewares(FF.middlewares, (req.url + "/").replace(/\/+/g, "/"));
29
30
  let body = "";
30
31
  req.on("data", chunk => (body += chunk.toString()));
31
32
  req.on("end", () => {
@@ -56,6 +57,7 @@ export function handleRequest(req, res, FF) {
56
57
  }
57
58
  }
58
59
  }
60
+ req.middleware = middleware;
59
61
  const result = await middleware.middleware(req, res, next);
60
62
  if (result && !res._ended) {
61
63
  if (typeof result === "string") {
@@ -116,3 +118,25 @@ function matchMiddleware(url, middlewares) {
116
118
  }
117
119
  return matchedMiddlewares;
118
120
  }
121
+ function getMiddlewares(middlewares, matchUrl, basePath = "") {
122
+ const result = [];
123
+ for (const middleware of middlewares) {
124
+ const midPath = (middleware.path || "").replace(/\/+$/, "");
125
+ const fullPath = (basePath + "/" + midPath).replace(/\/+/g, "/");
126
+ const matches = matchUrl === fullPath ||
127
+ (middleware.use && matchUrl.startsWith(fullPath)) ||
128
+ fullPath.includes(":") ||
129
+ fullPath.includes("*") ||
130
+ matchUrl.startsWith(fullPath + "/");
131
+ if (!matches)
132
+ continue;
133
+ if (middleware.router) {
134
+ const nested = getMiddlewares(middleware.router, matchUrl, fullPath);
135
+ result.push(...nested);
136
+ }
137
+ else {
138
+ result.push({ ...middleware, path: fullPath });
139
+ }
140
+ }
141
+ return result;
142
+ }
package/dist/res.d.ts CHANGED
@@ -2,10 +2,18 @@ import http from "http";
2
2
  import { CookieOptions } from "./types.js";
3
3
  export declare class FFResponse extends http.ServerResponse {
4
4
  _ended: boolean;
5
+ /**
6
+ * bind end for compatibility
7
+ */
8
+ send(data: string): void;
9
+ /**
10
+ * Set content type
11
+ */
12
+ ct(contentType?: string): void;
5
13
  json(data: any): void;
6
14
  cookie(name: string, value: string, options?: CookieOptions): this;
7
15
  status(code: number): this;
8
16
  redirect(url: string): this;
9
- sendFile(filePath: string): this;
17
+ sendFile(filePath: string, contentType?: string): this;
10
18
  render(templatePath: string, data: any): this;
11
19
  }
package/dist/res.js CHANGED
@@ -4,6 +4,18 @@ import { createReadStream } from "fs";
4
4
  import { renderHTML } from "./render.js";
5
5
  export class FFResponse extends http.ServerResponse {
6
6
  _ended = false;
7
+ /**
8
+ * bind end for compatibility
9
+ */
10
+ send(data) {
11
+ this.end(data);
12
+ }
13
+ /**
14
+ * Set content type
15
+ */
16
+ ct(contentType = "text/plain") {
17
+ this.setHeader("Content-Type", contentType);
18
+ }
7
19
  json(data) {
8
20
  this.setHeader("Content-Type", "application/json");
9
21
  this.end(JSON.stringify(data));
@@ -32,8 +44,10 @@ export class FFResponse extends http.ServerResponse {
32
44
  this.setHeader("Location", url);
33
45
  return this;
34
46
  }
35
- sendFile(filePath) {
36
- this.setHeader("Content-Type", getContentType(filePath));
47
+ sendFile(filePath, contentType) {
48
+ if (contentType === "utf8")
49
+ contentType = getContentType(filePath);
50
+ this.ct(contentType || getContentType(filePath));
37
51
  createReadStream(filePath).pipe(this);
38
52
  return this;
39
53
  }
@@ -0,0 +1,12 @@
1
+ import { Method, Middleware, RouteHandler } from "./types.js";
2
+ export declare class Router {
3
+ middlewares: Middleware[];
4
+ addRoute(method: Method, path: string, ...handlers: RouteHandler[]): void;
5
+ use(path?: string | RouteHandler | Router, middlewareFn?: RouteHandler | Router, method?: Method): void;
6
+ get(path: string, ...handlers: RouteHandler[]): void;
7
+ post(path: string, ...handlers: RouteHandler[]): void;
8
+ put(path: string, ...handlers: RouteHandler[]): void;
9
+ delete(path: string, ...handlers: RouteHandler[]): void;
10
+ all(path: string, ...handlers: RouteHandler[]): void;
11
+ static(apiPath: string, dirPath?: string, utf8?: boolean): void;
12
+ }
package/dist/router.js ADDED
@@ -0,0 +1,50 @@
1
+ import { handleStaticFiles } from "./helpers.js";
2
+ export class Router {
3
+ middlewares = [];
4
+ addRoute(method, path, ...handlers) {
5
+ const handler = handlers.pop();
6
+ handlers.forEach(middleware => this.use(path, middleware));
7
+ this.middlewares.push({ path, middleware: handler, method });
8
+ }
9
+ use(path = "/", middlewareFn, method = "all") {
10
+ if (typeof path === "function" || path instanceof Router) {
11
+ middlewareFn = path;
12
+ path = "/";
13
+ }
14
+ const middleware = {
15
+ path,
16
+ method,
17
+ middleware: null,
18
+ use: true
19
+ };
20
+ if (middlewareFn instanceof Router) {
21
+ middleware.router = middlewareFn.middlewares;
22
+ }
23
+ else {
24
+ middleware.middleware = middlewareFn;
25
+ }
26
+ this.middlewares.push(middleware);
27
+ }
28
+ get(path, ...handlers) {
29
+ this.addRoute("get", path, ...handlers);
30
+ }
31
+ post(path, ...handlers) {
32
+ this.addRoute("post", path, ...handlers);
33
+ }
34
+ put(path, ...handlers) {
35
+ this.addRoute("put", path, ...handlers);
36
+ }
37
+ delete(path, ...handlers) {
38
+ this.addRoute("delete", path, ...handlers);
39
+ }
40
+ all(path, ...handlers) {
41
+ this.addRoute("all", path, ...handlers);
42
+ }
43
+ static(apiPath, dirPath, utf8 = true) {
44
+ if (!dirPath) {
45
+ dirPath = apiPath;
46
+ apiPath = "/";
47
+ }
48
+ this.use(apiPath, handleStaticFiles(dirPath, utf8));
49
+ }
50
+ }
package/dist/types.d.ts CHANGED
@@ -21,12 +21,14 @@ export declare class FFRequest extends http.IncomingMessage {
21
21
  cookies: Cookies;
22
22
  body: Body;
23
23
  valid: (schema: ValidationSchema) => ValidationResult;
24
+ middleware: Middleware;
24
25
  }
25
26
  export interface Middleware {
26
27
  path: string;
27
28
  method: Method;
28
29
  middleware: RouteHandler;
29
30
  use?: true;
31
+ router?: Middleware[];
30
32
  }
31
33
  export interface CookieOptions {
32
34
  maxAge?: number;
@@ -44,4 +46,4 @@ export interface ValidationResult {
44
46
  [key: string]: string[];
45
47
  };
46
48
  }
47
- export type AfterHandleRequest = (req: http.IncomingMessage, res: http.ServerResponse) => any;
49
+ export type BeforeHandleRequest = (req: http.IncomingMessage, res: http.ServerResponse) => any;
package/dist/types.js CHANGED
@@ -6,4 +6,5 @@ export class FFRequest extends http.IncomingMessage {
6
6
  cookies;
7
7
  body;
8
8
  valid;
9
+ middleware;
9
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wxn0brp/falcon-frame",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "author": "wxn0brP",
package/dist/test.d.ts DELETED
@@ -1 +0,0 @@
1
- export {};
package/dist/test.js DELETED
@@ -1,76 +0,0 @@
1
- import FalconFrame, { PluginSystem } from "./index.js";
2
- const app = new FalconFrame({
3
- logLevel: "INFO",
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" });
20
- app.use((req, res, next) => {
21
- console.log(`[${req.method}] ${req.path}`);
22
- next();
23
- });
24
- app.use(pluginSystem.getRouteHandler());
25
- app.static("/", "public");
26
- app.get("/hello", (req, res) => {
27
- const name = req.query.name || "World";
28
- res.json({
29
- message: `Hello, ${name}?`,
30
- query: req.query,
31
- });
32
- });
33
- app.get("/hello/*", (req, res) => {
34
- res.json({
35
- message: `Hello, ${req.params.name}!`,
36
- query: req.query,
37
- });
38
- });
39
- app.get("/greet/:name", (req, res, next) => {
40
- console.log(req.params);
41
- next();
42
- }, (req, res) => {
43
- res.json({
44
- message: `Hello, ${req.params.name}!`,
45
- });
46
- });
47
- app.post("/submit", (req, res, next) => {
48
- const { validErrors, valid } = req.valid({
49
- login: "required|string",
50
- password: "required|string|min:8",
51
- });
52
- if (!valid) {
53
- res.status(400).json({
54
- status: "error",
55
- errors: validErrors,
56
- });
57
- }
58
- else
59
- next();
60
- }, async (req, res) => {
61
- console.log("run");
62
- res.redirect("/hello?name=" + req.body.login);
63
- return {
64
- status: "success",
65
- data: `Hello ${req.body.login}`,
66
- };
67
- });
68
- app.use((req, res) => {
69
- res.status(404);
70
- res.json({
71
- message: "Not found",
72
- });
73
- });
74
- app.listen(3000, () => {
75
- console.log("Server running on http://localhost:3000");
76
- });