@wxn0brp/falcon-frame 0.2.1 → 0.3.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/dist/body-utils.d.ts +6 -0
- package/dist/body-utils.js +66 -0
- package/dist/body.d.ts +3 -2
- package/dist/body.js +5 -49
- package/dist/index.d.ts +5 -2
- package/dist/index.js +12 -2
- package/dist/req.js +18 -9
- package/dist/router.d.ts +1 -0
- package/dist/router.js +5 -0
- package/dist/types.d.ts +4 -4
- package/package.json +1 -1
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import FalconFrame from "./index.js";
|
|
2
|
+
import type { FFRequest, ParseBodyFunction, RouteHandler, StandardBodyParserOptions } from "./types.js";
|
|
3
|
+
export declare function parseLimit(limit: string | number): number;
|
|
4
|
+
export declare function getContentType(req: FFRequest): string | undefined;
|
|
5
|
+
export declare function getRawBody(req: FFRequest, limit: number): Promise<string>;
|
|
6
|
+
export declare function getStandardBodyParser(type: string, parser: ParseBodyFunction, FF: FalconFrame, opts: StandardBodyParserOptions): RouteHandler;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export function parseLimit(limit) {
|
|
2
|
+
if (!limit)
|
|
3
|
+
return 0;
|
|
4
|
+
if (typeof limit === "number")
|
|
5
|
+
return limit;
|
|
6
|
+
if (typeof limit !== "string")
|
|
7
|
+
return 0;
|
|
8
|
+
limit = limit.toLowerCase().replace("b", "");
|
|
9
|
+
const match = limit.match(/^(\d+)([kmg])?$/i);
|
|
10
|
+
if (!match)
|
|
11
|
+
return 0;
|
|
12
|
+
const num = parseInt(match[1], 10);
|
|
13
|
+
const unit = match[2]?.toLowerCase();
|
|
14
|
+
switch (unit) {
|
|
15
|
+
case "k":
|
|
16
|
+
return num * 1024;
|
|
17
|
+
case "m":
|
|
18
|
+
return num * 1024 * 1024;
|
|
19
|
+
case "g":
|
|
20
|
+
return num * 1024 * 1024 * 1024;
|
|
21
|
+
default:
|
|
22
|
+
return num;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function getContentType(req) {
|
|
26
|
+
return req.headers["content-type"]?.split(";")[0].toLowerCase();
|
|
27
|
+
}
|
|
28
|
+
export function getRawBody(req, limit) {
|
|
29
|
+
return new Promise((resolve, reject) => {
|
|
30
|
+
let body = "";
|
|
31
|
+
req.on("data", (chunk) => {
|
|
32
|
+
body += chunk.toString();
|
|
33
|
+
if (limit && body.length > limit) {
|
|
34
|
+
const error = new Error("Payload Too Large");
|
|
35
|
+
// @ts-ignore
|
|
36
|
+
error.statusCode = 413;
|
|
37
|
+
req.destroy();
|
|
38
|
+
return reject(error);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
req.on("end", () => {
|
|
42
|
+
resolve(body);
|
|
43
|
+
});
|
|
44
|
+
req.on("error", (err) => {
|
|
45
|
+
reject(err);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
export function getStandardBodyParser(type, parser, FF, opts) {
|
|
50
|
+
const limit = parseLimit(opts.limit || "100k");
|
|
51
|
+
return async (req, res, next) => {
|
|
52
|
+
if ((typeof req.body == "object" && Object.keys(req.body).length) || getContentType(req) !== type) {
|
|
53
|
+
return next();
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const body = await getRawBody(req, limit);
|
|
57
|
+
req.body = await parser(body, req, FF);
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
req.body = {};
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
next();
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
package/dist/body.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
import FalconFrame from "./index.js";
|
|
2
|
-
import {
|
|
3
|
-
export declare function
|
|
2
|
+
import { RouteHandler, StandardBodyParserOptions } from "./types.js";
|
|
3
|
+
export declare function json(FF: FalconFrame, opts?: StandardBodyParserOptions): RouteHandler;
|
|
4
|
+
export declare function urlencoded(FF: FalconFrame, opts?: StandardBodyParserOptions): RouteHandler;
|
package/dist/body.js
CHANGED
|
@@ -1,52 +1,8 @@
|
|
|
1
1
|
import querystring from "querystring";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
"application/
|
|
5
|
-
body: querystring.parse(body),
|
|
6
|
-
}),
|
|
7
|
-
};
|
|
8
|
-
export async function parseBody(req, body, FF) {
|
|
9
|
-
const funcs = Object.assign({}, parseBodyFunctions, FF.customParsers || {});
|
|
10
|
-
const limit = parseLimit(FF.opts.bodyLimit);
|
|
11
|
-
try {
|
|
12
|
-
if (limit && body.length > limit) {
|
|
13
|
-
await FF.logger.warn(`Body size exceeds limit of ${limit} bytes`);
|
|
14
|
-
return {};
|
|
15
|
-
}
|
|
16
|
-
const type = req.headers["content-type"] || "";
|
|
17
|
-
const func = funcs[type];
|
|
18
|
-
if (!func)
|
|
19
|
-
return {};
|
|
20
|
-
const data = await func(body, req, FF);
|
|
21
|
-
if (!data || typeof data !== "object")
|
|
22
|
-
return {};
|
|
23
|
-
return data;
|
|
24
|
-
}
|
|
25
|
-
catch (e) {
|
|
26
|
-
await FF.logger.warn(`Error parsing body: ${e}`);
|
|
27
|
-
return {};
|
|
28
|
-
}
|
|
2
|
+
import { getStandardBodyParser } from "./body-utils.js";
|
|
3
|
+
export function json(FF, opts = {}) {
|
|
4
|
+
return getStandardBodyParser("application/json", (body) => JSON.parse(body), FF, opts);
|
|
29
5
|
}
|
|
30
|
-
function
|
|
31
|
-
|
|
32
|
-
return 0;
|
|
33
|
-
if (typeof limit === "number")
|
|
34
|
-
return limit;
|
|
35
|
-
if (typeof limit !== "string")
|
|
36
|
-
return 0;
|
|
37
|
-
const match = limit.match(/^(\d+)([kmg])?$/i);
|
|
38
|
-
if (!match)
|
|
39
|
-
return 0;
|
|
40
|
-
const num = parseInt(match[1], 10);
|
|
41
|
-
const unit = match[2]?.toLowerCase();
|
|
42
|
-
switch (unit) {
|
|
43
|
-
case "k":
|
|
44
|
-
return num * 1024;
|
|
45
|
-
case "m":
|
|
46
|
-
return num * 1024 * 1024;
|
|
47
|
-
case "g":
|
|
48
|
-
return num * 1024 * 1024 * 1024;
|
|
49
|
-
default:
|
|
50
|
-
return num;
|
|
51
|
-
}
|
|
6
|
+
export function urlencoded(FF, opts = {}) {
|
|
7
|
+
return getStandardBodyParser("application/x-www-form-urlencoded", (body) => querystring.parse(body), FF, opts);
|
|
52
8
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,19 +4,22 @@ import { PluginSystem } from "./plugin.js";
|
|
|
4
4
|
import { renderHTML } from "./render.js";
|
|
5
5
|
import { FFResponse } from "./res.js";
|
|
6
6
|
import { Router } from "./router.js";
|
|
7
|
-
import type { BeforeHandleRequest, FFRequest,
|
|
7
|
+
import type { BeforeHandleRequest, FFRequest, RouteHandler } from "./types.js";
|
|
8
8
|
export type EngineCallback = (path: string, options: any, callback: (e: any, rendered?: string) => void) => void;
|
|
9
9
|
export interface Opts {
|
|
10
10
|
loggerOpts?: LoggerOptions;
|
|
11
11
|
bodyLimit?: string;
|
|
12
|
+
disableJsonParser?: boolean;
|
|
13
|
+
disableUrlencodedParser?: boolean;
|
|
12
14
|
}
|
|
13
15
|
export declare class FalconFrame<Vars extends Record<string, any> = any> extends Router {
|
|
14
16
|
logger: Logger;
|
|
15
|
-
|
|
17
|
+
bodyParsers: RouteHandler[];
|
|
16
18
|
vars: Vars;
|
|
17
19
|
opts: Opts;
|
|
18
20
|
engines: Record<string, EngineCallback>;
|
|
19
21
|
constructor(opts?: Partial<Opts>);
|
|
22
|
+
addBodyParser(parser: RouteHandler): this;
|
|
20
23
|
listen(port: number | string, callback?: (() => void) | boolean, beforeHandleRequest?: BeforeHandleRequest): http.Server<typeof http.IncomingMessage, typeof http.ServerResponse>;
|
|
21
24
|
getApp(beforeHandleRequest?: BeforeHandleRequest): (req: any, res: any) => Promise<void>;
|
|
22
25
|
engine(ext: string, callback: EngineCallback): this;
|
package/dist/index.js
CHANGED
|
@@ -6,22 +6,28 @@ 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";
|
|
9
10
|
export class FalconFrame extends Router {
|
|
10
11
|
logger;
|
|
11
|
-
|
|
12
|
+
bodyParsers = [];
|
|
12
13
|
vars = {};
|
|
13
14
|
opts = {};
|
|
14
15
|
engines = {};
|
|
15
16
|
constructor(opts = {}) {
|
|
16
17
|
super();
|
|
18
|
+
const loggerOpts = opts?.loggerOpts || {};
|
|
17
19
|
this.logger = new Logger({
|
|
18
20
|
loggerName: "falcon-frame",
|
|
19
|
-
...
|
|
21
|
+
...loggerOpts,
|
|
20
22
|
});
|
|
21
23
|
this.opts = {
|
|
22
24
|
bodyLimit: "10m",
|
|
23
25
|
...opts,
|
|
24
26
|
};
|
|
27
|
+
if (!this.opts.disableJsonParser)
|
|
28
|
+
this.addBodyParser(json(this, { limit: this.opts.bodyLimit }));
|
|
29
|
+
if (!this.opts.disableUrlencodedParser)
|
|
30
|
+
this.addBodyParser(urlencoded(this, { limit: this.opts.bodyLimit }));
|
|
25
31
|
this.engine(".html", (path, options, callback) => {
|
|
26
32
|
try {
|
|
27
33
|
const content = renderHTML(path, options);
|
|
@@ -32,6 +38,10 @@ export class FalconFrame extends Router {
|
|
|
32
38
|
}
|
|
33
39
|
});
|
|
34
40
|
}
|
|
41
|
+
addBodyParser(parser) {
|
|
42
|
+
this.bodyParsers.push(parser);
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
35
45
|
listen(port, callback, beforeHandleRequest) {
|
|
36
46
|
const server = http.createServer(this.getApp(beforeHandleRequest));
|
|
37
47
|
if (typeof callback === "boolean") {
|
package/dist/req.js
CHANGED
|
@@ -3,7 +3,6 @@ import { parseCookies } from "./helpers.js";
|
|
|
3
3
|
import { FFResponse } from "./res.js";
|
|
4
4
|
import { validate } from "./valid.js";
|
|
5
5
|
import { getMiddlewares, matchMiddleware } from "./middleware.js";
|
|
6
|
-
import { parseBody } from "./body.js";
|
|
7
6
|
export function handleRequest(req, res, FF) {
|
|
8
7
|
Object.setPrototypeOf(res, FFResponse.prototype);
|
|
9
8
|
res.FF = FF;
|
|
@@ -71,6 +70,12 @@ export function handleRequest(req, res, FF) {
|
|
|
71
70
|
}
|
|
72
71
|
}
|
|
73
72
|
}
|
|
73
|
+
const hasCustomParserEndpoint = matchedMiddlewares.some(middleware => middleware.customParser);
|
|
74
|
+
if (hasCustomParserEndpoint) {
|
|
75
|
+
req.body = {};
|
|
76
|
+
next();
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
74
79
|
if (req.method === "GET" ||
|
|
75
80
|
req.method === "HEAD" ||
|
|
76
81
|
req.method === "OPTIONS") {
|
|
@@ -78,12 +83,16 @@ export function handleRequest(req, res, FF) {
|
|
|
78
83
|
next();
|
|
79
84
|
return;
|
|
80
85
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
req.body = {};
|
|
87
|
+
let bodyParserIndex = 0;
|
|
88
|
+
function nextBodyParser() {
|
|
89
|
+
if (bodyParserIndex >= FF.bodyParsers.length) {
|
|
90
|
+
logger.debug("No more body parsers. Executing middlewares");
|
|
91
|
+
return next();
|
|
92
|
+
}
|
|
93
|
+
logger.debug(`Executing body parser ${bodyParserIndex} of ${FF.bodyParsers.length}`);
|
|
94
|
+
const bodyParser = FF.bodyParsers[bodyParserIndex++];
|
|
95
|
+
bodyParser(req, res, nextBodyParser);
|
|
96
|
+
}
|
|
97
|
+
nextBodyParser();
|
|
89
98
|
}
|
package/dist/router.d.ts
CHANGED
|
@@ -12,4 +12,5 @@ export declare class Router {
|
|
|
12
12
|
all(path: string, ...handlers: RouteHandler[]): this;
|
|
13
13
|
static(apiPath: string, dirPath?: string, opts?: StaticServeOptions): this;
|
|
14
14
|
sse(path: string, ...handlers: RouteHandler[]): this;
|
|
15
|
+
customParser(path: string, handler: RouteHandler, method?: Method): this;
|
|
15
16
|
}
|
package/dist/router.js
CHANGED
|
@@ -63,4 +63,9 @@ export class Router {
|
|
|
63
63
|
this.middlewares[index - 1].sse = true;
|
|
64
64
|
return this;
|
|
65
65
|
}
|
|
66
|
+
customParser(path, handler, method = "post") {
|
|
67
|
+
const index = this.addRoute(method, path, handler);
|
|
68
|
+
this.middlewares[index - 1].customParser = true;
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
66
71
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -15,11 +15,10 @@ export interface Query {
|
|
|
15
15
|
export interface Body {
|
|
16
16
|
[key: string]: any;
|
|
17
17
|
}
|
|
18
|
-
export
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
export type ParseBodyFunction = (body: string, req: FFRequest, FF: FalconFrame) => Promise<Record<string, any>>;
|
|
19
|
+
export interface StandardBodyParserOptions {
|
|
20
|
+
limit?: string | number;
|
|
21
21
|
}
|
|
22
|
-
export type ParseBodyFunction = (body: string, req: FFRequest, FF: FalconFrame) => Promise<ParseBody>;
|
|
23
22
|
export declare class FFRequest extends http.IncomingMessage {
|
|
24
23
|
path: string;
|
|
25
24
|
query: Query;
|
|
@@ -36,6 +35,7 @@ export interface Middleware {
|
|
|
36
35
|
use?: true;
|
|
37
36
|
router?: Middleware[];
|
|
38
37
|
sse?: true;
|
|
38
|
+
customParser?: true;
|
|
39
39
|
}
|
|
40
40
|
export interface CookieOptions {
|
|
41
41
|
maxAge?: number;
|