@wxn0brp/falcon-frame 0.9.1 → 0.9.3

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.d.ts CHANGED
@@ -4,5 +4,4 @@ export declare const urlencoded: ParseBodyFunction;
4
4
  export declare const yaml: ParseBodyFunction;
5
5
  export declare const toml: ParseBodyFunction;
6
6
  export declare const json5: ParseBodyFunction;
7
- export declare const xml: ParseBodyFunction;
8
7
  export declare const text: ParseBodyFunction;
package/dist/body.js CHANGED
@@ -4,7 +4,6 @@ export const urlencoded = body => querystring.parse(body);
4
4
  export const yaml = body => Bun.YAML.parse(body);
5
5
  export const toml = body => Bun.TOML.parse(body);
6
6
  export const json5 = body => Bun.JSON5.parse(body);
7
- export const xml = body => new DOMParser().parseFromString(body, "text/xml");
8
7
  export const text = body => ({
9
8
  data: body,
10
9
  });
package/dist/debug.js CHANGED
@@ -13,7 +13,8 @@ export function createDebug(opts = {}) {
13
13
  };
14
14
  const pass = process.env.FF_DEBUG_PASS;
15
15
  function isHostAllowed(req) {
16
- return hosts.some(h => h === req.socket.remoteAddress);
16
+ const ip = req.ip || req.socket.remoteAddress || "";
17
+ return hosts.some(h => h === ip);
17
18
  }
18
19
  function checkAuth(req) {
19
20
  if (!pass)
@@ -0,0 +1,19 @@
1
+ import { FalconFrame } from "./index.js";
2
+ export interface OfflineRequest {
3
+ method?: string;
4
+ url: string;
5
+ headers?: Record<string, string>;
6
+ body?: any;
7
+ cookies?: Record<string, string>;
8
+ }
9
+ export interface OfflineResponse {
10
+ status: number;
11
+ headers: Record<string, string>;
12
+ body: any;
13
+ }
14
+ export declare class OfflineEngine {
15
+ private ff;
16
+ constructor(ff: FalconFrame<any>);
17
+ handle(request: OfflineRequest): Promise<OfflineResponse>;
18
+ private buildResponse;
19
+ }
package/dist/engine.js ADDED
@@ -0,0 +1,146 @@
1
+ import { getContentType } from "./body-utils.js";
2
+ import { parseCookies } from "./helpers.js";
3
+ import { extractParams, getMiddlewares, matchMiddleware } from "./middleware.js";
4
+ import { validate } from "./valid.js";
5
+ export class OfflineEngine {
6
+ ff;
7
+ constructor(ff) {
8
+ this.ff = ff;
9
+ }
10
+ async handle(request) {
11
+ const urlParts = request.url.split("?");
12
+ const path = decodeURIComponent(urlParts[0]);
13
+ const query = urlParts[1]
14
+ ? Object.fromEntries(new URLSearchParams(urlParts[1]))
15
+ : {};
16
+ request.method ??= "GET";
17
+ const cookies = request.cookies
18
+ ? {
19
+ ...request.cookies,
20
+ ...parseCookies(request.cookies.cookie || ""),
21
+ }
22
+ : parseCookies(request.headers?.cookie || "");
23
+ const res = {
24
+ statusCode: 200,
25
+ headers: {},
26
+ body: "",
27
+ _ended: false,
28
+ FF: this.ff,
29
+ json: (data) => {
30
+ res.headers["content-type"] = "application/json";
31
+ res.body = data;
32
+ res._ended = true;
33
+ },
34
+ send: (data) => {
35
+ res.body = data;
36
+ res._ended = true;
37
+ },
38
+ end: (data) => {
39
+ res.body = data;
40
+ res._ended = true;
41
+ },
42
+ status: (code) => {
43
+ res.statusCode = code;
44
+ return res;
45
+ },
46
+ setHeader: (name, value) => {
47
+ res.headers[name.toLowerCase()] = value;
48
+ },
49
+ ct: (type) => {
50
+ res.headers["content-type"] = type;
51
+ return res;
52
+ },
53
+ cookie: (name, value, opts = {}) => {
54
+ let c = `${name}=${encodeURIComponent(value)}`;
55
+ if (opts.maxAge !== undefined)
56
+ c += `; Max-Age=${opts.maxAge}`;
57
+ if (opts.path)
58
+ c += `; Path=${opts.path}`;
59
+ if (opts.httpOnly)
60
+ c += `; HttpOnly`;
61
+ if (opts.secure)
62
+ c += `; Secure`;
63
+ const existing = res.headers["set-cookie"];
64
+ res.headers["set-cookie"] = existing ? `${existing}, ${c}` : c;
65
+ },
66
+ };
67
+ const req = {
68
+ path,
69
+ query,
70
+ params: {},
71
+ body: {},
72
+ cookies,
73
+ headers: request.headers || {},
74
+ isBodyParsed: false,
75
+ FF: this.ff,
76
+ header: (name) => request.headers?.[name.toLowerCase()] || "",
77
+ valid: (schema, regexRules) => validate({
78
+ schema,
79
+ data: req.body,
80
+ regexRules,
81
+ isBodyParsed: req.isBodyParsed,
82
+ }),
83
+ };
84
+ if (request.body !== undefined && request.body !== null) {
85
+ const ct = getContentType(req) || "application/json";
86
+ const entry = this.ff.bodyParsers[ct];
87
+ if (entry) {
88
+ const raw = typeof request.body === "string"
89
+ ? request.body
90
+ : JSON.stringify(request.body);
91
+ try {
92
+ req.body = (await entry.parse(raw, req, res)) ?? {};
93
+ req.isBodyParsed = true;
94
+ }
95
+ catch {
96
+ req.body = {};
97
+ }
98
+ }
99
+ else {
100
+ req.body = request.body;
101
+ req.isBodyParsed = true;
102
+ }
103
+ }
104
+ const middlewares = getMiddlewares(this.ff.middlewares, path + "/");
105
+ const matched = matchMiddleware(path, middlewares.filter(m => m.method === request.method.toLowerCase() || m.method === "all"));
106
+ if (matched.length === 0) {
107
+ res.statusCode = 404;
108
+ this.ff._404(req, res);
109
+ return this.buildResponse(res);
110
+ }
111
+ let i = 0;
112
+ const next = async () => {
113
+ if (i >= matched.length) {
114
+ res.statusCode = 404;
115
+ this.ff._404(req, res);
116
+ return;
117
+ }
118
+ const mw = matched[i++];
119
+ req.params = extractParams(mw.path, path);
120
+ try {
121
+ const result = await mw.middleware(req, res, next);
122
+ if (result && !res._ended) {
123
+ typeof result === "string" ? res.send(result) : res.json(result);
124
+ }
125
+ }
126
+ catch (err) {
127
+ if (!res._ended) {
128
+ res.statusCode = 500;
129
+ this.ff._500(err, req, res);
130
+ }
131
+ }
132
+ };
133
+ await next();
134
+ return this.buildResponse(res);
135
+ }
136
+ buildResponse(res) {
137
+ const ct = res.headers["content-type"] || "";
138
+ return {
139
+ status: res.statusCode,
140
+ headers: res.headers,
141
+ body: ct.includes("application/json") && res.body
142
+ ? res.body
143
+ : (res.body ?? ""),
144
+ };
145
+ }
146
+ }
package/dist/helpers.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import path from "path";
2
2
  export function parseCookies(cookieHeader) {
3
- const cookies = {};
3
+ const cookies = Object.create(null);
4
4
  cookieHeader.split(";").forEach(cookie => {
5
5
  const [name, ...valueParts] = cookie.split("=");
6
6
  const value = decodeURIComponent(valueParts.join("=").trim());
package/dist/index.d.ts CHANGED
@@ -43,6 +43,7 @@ export declare class FalconFrame<Vars extends Record<string, any> = {}> extends
43
43
  set500(handler: ErrorHandler): void;
44
44
  }
45
45
  export default FalconFrame;
46
+ export { OfflineEngine, OfflineRequest, OfflineResponse } from "./engine.js";
46
47
  export * as Helpers from "./helpers.js";
47
48
  export type { FFOpts as Opts } from "./types.js";
48
49
  export { validateBody } from "./valid.js";
package/dist/index.js CHANGED
@@ -68,10 +68,6 @@ export class FalconFrame extends Router {
68
68
  fn: bodyParser.toml,
69
69
  ct: "application/toml",
70
70
  },
71
- xml: {
72
- fn: bodyParser.xml,
73
- ct: "application/xml",
74
- },
75
71
  text: {
76
72
  fn: bodyParser.text,
77
73
  ct: "text/plain",
@@ -196,6 +192,7 @@ export class FalconFrame extends Router {
196
192
  }
197
193
  }
198
194
  export default FalconFrame;
195
+ export { OfflineEngine } from "./engine.js";
199
196
  export * as Helpers from "./helpers.js";
200
197
  export { validateBody } from "./valid.js";
201
198
  export { FFResponse, renderHTML, Router };
@@ -1,3 +1,4 @@
1
1
  import { Middleware } from "./types.js";
2
2
  export declare function matchMiddleware(url: string, middlewares: Middleware[]): Middleware[];
3
3
  export declare function getMiddlewares(middlewares: Middleware[], matchUrl: string, basePath?: string): Middleware[];
4
+ export declare function extractParams(routePath: string, requestPath: string): Record<string, string>;
@@ -70,3 +70,16 @@ export function getMiddlewares(middlewares, matchUrl, basePath = "") {
70
70
  }
71
71
  return result;
72
72
  }
73
+ export function extractParams(routePath, requestPath) {
74
+ const params = {};
75
+ if (!routePath.includes(":"))
76
+ return params;
77
+ const routeParts = routePath.split("/");
78
+ const pathParts = requestPath.split("/");
79
+ for (let i = 0; i < routeParts.length; i++) {
80
+ if (routeParts[i].startsWith(":")) {
81
+ params[routeParts[i].slice(1)] = pathParts[i];
82
+ }
83
+ }
84
+ return params;
85
+ }
package/dist/req.js CHANGED
@@ -2,7 +2,7 @@ import { randomUUID } from "crypto";
2
2
  import { URL } from "url";
3
3
  import { getContentType, getRawBody } from "./body-utils.js";
4
4
  import { getIP, parseCookies } from "./helpers.js";
5
- import { getMiddlewares, matchMiddleware } from "./middleware.js";
5
+ import { extractParams, getMiddlewares, matchMiddleware } from "./middleware.js";
6
6
  import { FFResponse } from "./res.js";
7
7
  import { validate } from "./valid.js";
8
8
  export function handleRequest(req, res, FF, reRoute = false) {
@@ -77,17 +77,7 @@ export function handleRequest(req, res, FF, reRoute = false) {
77
77
  }
78
78
  const middleware = matchedMiddlewares[middlewareIndex++];
79
79
  logger.debug(`Executing middleware ${middlewareIndex} of ${matchedMiddlewares.length} matched for path [${middleware.path}]`);
80
- if (middleware.path.includes(":")) {
81
- const middlewareParts = middleware.path.split("/");
82
- const reqPathParts = req.path.split("/");
83
- req.params = {};
84
- for (let i = 0; i < middlewareParts.length; i++) {
85
- if (middlewareParts[i].startsWith(":")) {
86
- const paramName = middlewareParts[i].slice(1);
87
- req.params[paramName] = reqPathParts[i];
88
- }
89
- }
90
- }
80
+ req.params = extractParams(middleware.path, req.path);
91
81
  req.middleware = middleware;
92
82
  try {
93
83
  const result = await middleware.middleware(req, res, next);
package/dist/static.js CHANGED
@@ -38,11 +38,16 @@ export function handleStaticFiles(dirPath, opts) {
38
38
  res.setHeader("ETag", etag);
39
39
  }
40
40
  res.ct(getContentType(filePath, opts.utf8));
41
+ if (req.method === "HEAD") {
42
+ res.setHeader("Content-Length", stats.size);
43
+ res.end();
44
+ return true;
45
+ }
41
46
  fs.createReadStream(filePath).pipe(res);
42
47
  return true;
43
48
  };
44
49
  return (req, res, next) => {
45
- if (req.method.toLowerCase() !== "get")
50
+ if (req.method !== "GET" && req.method !== "HEAD")
46
51
  return next();
47
52
  const apiPath = req.middleware.path;
48
53
  const unsafePath = req.path.replace(apiPath, "");
package/dist/types.d.ts CHANGED
@@ -94,7 +94,6 @@ export interface FFOpts {
94
94
  json5?: boolean;
95
95
  yaml?: boolean;
96
96
  toml?: boolean;
97
- xml?: boolean;
98
97
  text?: boolean;
99
98
  };
100
99
  xRequestId?: "auto" | "disable" | "manual";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wxn0brp/falcon-frame",
3
- "version": "0.9.1",
3
+ "version": "0.9.3",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "author": "wxn0brP",
@@ -29,7 +29,7 @@
29
29
  "typescript": "^7"
30
30
  },
31
31
  "dependencies": {
32
- "@wxn0brp/lucerna-log": "^0.2.4"
32
+ "@wxn0brp/lucerna-log": "^0.2.5"
33
33
  },
34
34
  "files": [
35
35
  "dist"