@wxn0brp/falcon-frame 0.8.4 → 0.8.6

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
@@ -8,7 +8,6 @@ A minimal, modular TypeScript web library.
8
8
  - Middleware chaining
9
9
  - Static file serving
10
10
  - Request validation via inline schemas
11
- - OpenAPI document generation
12
11
  - Cookie management
13
12
  - Debuggable logger
14
13
 
@@ -28,7 +28,7 @@ export function getContentType(req) {
28
28
  export function getRawBody(req, res, limit) {
29
29
  return new Promise((resolve, reject) => {
30
30
  let body = "";
31
- req.on("data", (chunk) => {
31
+ req.on("data", chunk => {
32
32
  body += chunk.toString();
33
33
  if (limit && body.length > limit) {
34
34
  const error = new Error("Payload Too Large");
@@ -41,7 +41,7 @@ export function getRawBody(req, res, limit) {
41
41
  req.on("end", () => {
42
42
  resolve(body);
43
43
  });
44
- req.on("error", (err) => {
44
+ req.on("error", err => {
45
45
  reject(err);
46
46
  });
47
47
  });
@@ -49,7 +49,8 @@ export function getRawBody(req, res, limit) {
49
49
  export function getStandardBodyParser(type, parser, opts) {
50
50
  const limit = parseLimit(opts.limit || "100k");
51
51
  return async (req, res, next) => {
52
- if ((typeof req.body == "object" && Object.keys(req.body).length) || getContentType(req) !== type) {
52
+ if ((typeof req.body === "object" && Object.keys(req.body).length) ||
53
+ getContentType(req) !== type) {
53
54
  return next();
54
55
  }
55
56
  try {
package/dist/body.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import querystring from "querystring";
2
2
  import { getStandardBodyParser } from "./body-utils.js";
3
3
  export function json(opts = {}) {
4
- return getStandardBodyParser("application/json", (body) => JSON.parse(body), opts);
4
+ return getStandardBodyParser("application/json", body => JSON.parse(body), opts);
5
5
  }
6
6
  export function urlencoded(opts = {}) {
7
- return getStandardBodyParser("application/x-www-form-urlencoded", (body) => querystring.parse(body), opts);
7
+ return getStandardBodyParser("application/x-www-form-urlencoded", body => querystring.parse(body), opts);
8
8
  }
package/dist/compress.js CHANGED
@@ -1,4 +1,18 @@
1
- import { createDeflate, createGzip } from "zlib";
1
+ import { createBrotliCompress, createDeflate, createGzip } from "zlib";
2
+ function isCompressible(contentType) {
3
+ if (!contentType)
4
+ return true;
5
+ const type = contentType.split(";")[0].trim().toLowerCase();
6
+ if (/^image\/(?!svg\+xml)/.test(type))
7
+ return false;
8
+ if (/^(video|audio)\//.test(type))
9
+ return false;
10
+ if (type === "text/event-stream")
11
+ return false;
12
+ if (/^application\/(zip|gzip|x-gzip|x-bzip|x-bzip2|x-xz|x-rar|x-7z|x-tar|x-compress|wasm)$/.test(type))
13
+ return false;
14
+ return true;
15
+ }
2
16
  export function compression(req, res) {
3
17
  if (req._compression)
4
18
  return;
@@ -7,47 +21,69 @@ export function compression(req, res) {
7
21
  if (!encoding || typeof encoding !== "string" || res.headersSent) {
8
22
  return;
9
23
  }
10
- let compressionStream;
24
+ let createCompressor;
11
25
  let encodingType;
12
- if (/\bgzip\b/.test(encoding)) {
26
+ if (/\bbr\b/.test(encoding)) {
27
+ encodingType = "br";
28
+ createCompressor = createBrotliCompress;
29
+ }
30
+ else if (/\bgzip\b/.test(encoding)) {
13
31
  encodingType = "gzip";
14
- compressionStream = createGzip();
32
+ createCompressor = createGzip;
15
33
  }
16
34
  else if (/\bdeflate\b/.test(encoding)) {
17
35
  encodingType = "deflate";
18
- compressionStream = createDeflate();
36
+ createCompressor = createDeflate;
19
37
  }
20
38
  else {
21
39
  return;
22
40
  }
23
- res.setHeader("Content-Encoding", encodingType);
24
- res.removeHeader("Content-Length");
25
41
  const originalWrite = res.write;
26
42
  const originalEnd = res.end;
27
- compressionStream.on("data", (chunk) => {
28
- originalWrite.call(res, chunk);
29
- });
30
- compressionStream.on("end", () => {
31
- originalEnd.call(res);
32
- });
33
- compressionStream.on("error", (err) => {
34
- if (!res.headersSent) {
35
- res.statusCode = 500;
43
+ let compressionStream = null;
44
+ let initialized = false;
45
+ function init() {
46
+ if (initialized)
47
+ return compressionStream !== null;
48
+ initialized = true;
49
+ const contentType = res.getHeader("content-type");
50
+ if (!isCompressible(contentType))
51
+ return false;
52
+ compressionStream = createCompressor();
53
+ res.setHeader("Content-Encoding", encodingType);
54
+ res.removeHeader("Content-Length");
55
+ compressionStream.on("data", chunk => {
56
+ originalWrite.call(res, chunk);
57
+ });
58
+ compressionStream.on("end", () => {
36
59
  originalEnd.call(res);
37
- }
38
- else {
39
- res.destroy(err);
40
- }
41
- });
42
- res.on("close", () => {
43
- compressionStream.destroy();
44
- });
60
+ });
61
+ compressionStream.on("error", err => {
62
+ if (!res.headersSent) {
63
+ res.statusCode = 500;
64
+ originalEnd.call(res);
65
+ }
66
+ else {
67
+ res.destroy(err);
68
+ }
69
+ });
70
+ res.on("close", () => {
71
+ compressionStream.destroy();
72
+ });
73
+ return true;
74
+ }
45
75
  // @ts-ignore
46
76
  res.write = (...args) => {
47
77
  const [chunk, encoding, cb] = args;
48
78
  const callback = typeof encoding === "function" ? encoding : cb;
49
79
  const enc = typeof encoding === "function" ? undefined : encoding;
50
80
  if (!res.headersSent) {
81
+ if (!init()) {
82
+ res.write = originalWrite;
83
+ res.end = originalEnd;
84
+ res.writeHead(res.statusCode);
85
+ return originalWrite.call(res, chunk, enc, callback);
86
+ }
51
87
  res.writeHead(res.statusCode);
52
88
  }
53
89
  return compressionStream.write(chunk, enc, callback);
@@ -68,6 +104,18 @@ export function compression(req, res) {
68
104
  finalCb = encoding;
69
105
  finalEncoding = undefined;
70
106
  }
107
+ if (!res.headersSent) {
108
+ if (!init()) {
109
+ res.write = originalWrite;
110
+ res.end = originalEnd;
111
+ if (finalCb)
112
+ res.once("finish", finalCb);
113
+ if (finalChunk)
114
+ originalWrite.call(res, finalChunk, finalEncoding);
115
+ originalEnd.call(res);
116
+ return res;
117
+ }
118
+ }
71
119
  if (finalCb)
72
120
  res.once("finish", finalCb);
73
121
  if (finalChunk) {
package/dist/cors.js CHANGED
@@ -9,8 +9,7 @@ function parseOrigin(origin) {
9
9
  const url = new URL(origin);
10
10
  return {
11
11
  host: url.hostname,
12
- port: url.port ||
13
- (url.protocol === "https:" ? "443" : "80")
12
+ port: url.port || (url.protocol === "https:" ? "443" : "80"),
14
13
  };
15
14
  }
16
15
  catch {
@@ -68,7 +67,7 @@ export function createCORS(allowedOrigins, opts = {}) {
68
67
  const origin = req.headers.origin;
69
68
  if (allowedOrigins.includes("*"))
70
69
  return handleEnd(req, res, next, origin || "*");
71
- if (origin && allowedOrigins.some((pattern) => matches(origin, pattern)))
70
+ if (origin && allowedOrigins.some(pattern => matches(origin, pattern)))
72
71
  return handleEnd(req, res, next, origin);
73
72
  next();
74
73
  };
package/dist/debug.html CHANGED
@@ -236,6 +236,11 @@
236
236
  text-overflow: ellipsis;
237
237
  }
238
238
 
239
+ .url-copy {
240
+ margin-left: 12px;
241
+ vertical-align: middle;
242
+ }
243
+
239
244
  .detail-row td {
240
245
  padding: 0;
241
246
  background: rgba(22, 27, 34, 0.6);
@@ -253,11 +258,40 @@
253
258
  min-width: 240px;
254
259
  }
255
260
 
261
+ .detail-section-header {
262
+ display: flex;
263
+ justify-content: space-between;
264
+ align-items: center;
265
+ margin-bottom: 4px;
266
+ }
267
+
256
268
  .detail-section h4 {
257
269
  font-size: 11px;
258
270
  text-transform: uppercase;
259
271
  color: var(--text-muted);
260
- margin-bottom: 4px;
272
+ margin: 0;
273
+ }
274
+
275
+ .copy-btn {
276
+ font-family: var(--font);
277
+ font-size: 10px;
278
+ padding: 2px 8px;
279
+ border-radius: var(--radius);
280
+ border: 1px solid var(--border);
281
+ background: transparent;
282
+ color: var(--text-muted);
283
+ cursor: pointer;
284
+ text-transform: uppercase;
285
+ }
286
+
287
+ .copy-btn:hover {
288
+ border-color: var(--accent);
289
+ color: var(--accent);
290
+ }
291
+
292
+ .copy-btn.copied {
293
+ border-color: var(--success);
294
+ color: var(--success);
261
295
  }
262
296
 
263
297
  .detail-section pre {
@@ -333,9 +367,9 @@
333
367
  const methodFilters = document.querySelector("#methodFilters");
334
368
  const emptyRow = logBody.querySelector(".empty");
335
369
  let entries = [];
336
- let seq = 0;
337
- let expanded = new Set();
338
- let activeMethods = new Set(METHODS);
370
+ const seq = 0;
371
+ const expanded = new Set();
372
+ const activeMethods = new Set(METHODS);
339
373
 
340
374
  function statusClass(code) {
341
375
  if (code < 300) return "s2xx";
@@ -358,6 +392,14 @@
358
392
  }
359
393
  }
360
394
 
395
+ function isEmpty(obj) {
396
+ if (!obj || typeof obj !== "object") return true;
397
+ const keys = Object.keys(obj);
398
+ if (keys.length === 0) return true;
399
+ if (keys.length === 1 && keys[0] === "" && obj[""] === "") return true;
400
+ return false;
401
+ }
402
+
361
403
  function esc(str) {
362
404
  const el = document.createElement("span");
363
405
  el.textContent = str;
@@ -414,7 +456,7 @@
414
456
  let html = `<tr data-row="${rowId}">`;
415
457
  html += `<td>${idx}</td>`;
416
458
  html += `<td><span class="method ${e.method.toLowerCase()}">${esc(e.method)}</span></td>`;
417
- html += `<td><span class="url-cell" title="${escUrl}">${escUrl}</span></td>`;
459
+ html += `<td><span class="url-cell" title="${escUrl}">${escUrl}</span><button class="copy-btn url-copy" data-copy-url="${esc(e.url.replace(/"/g, '&quot;'))}">Copy</button></td>`;
418
460
  html += `<td><span class="status ${statusClass(e.status)}">${e.status}</span></td>`;
419
461
  html += `<td><span class="duration ${durationClass(e.duration)}">${dur}</span></td>`;
420
462
  html += `<td class="time">${new Date(e.time).toLocaleTimeString()}</td>`;
@@ -423,10 +465,10 @@
423
465
  if (isExpanded) {
424
466
  html += `<tr class="detail-row"><td colspan="6">`;
425
467
  html += `<div class="detail-inner">`;
426
- html += `<div class="detail-section"><h4>Headers</h4><pre>${esc(formatJSON(e.headers))}</pre></div>`;
427
- html += `<div class="detail-section"><h4>Query</h4><pre>${esc(formatJSON(e.query))}</pre></div>`;
428
- html += `<div class="detail-section"><h4>Cookies</h4><pre>${esc(formatJSON(e.cookies))}</pre></div>`;
429
- html += `<div class="detail-section"><h4>Body</h4><pre>${esc(formatJSON(e.body))}</pre></div>`;
468
+ if (!isEmpty(e.headers)) html += `<div class="detail-section"><div class="detail-section-header"><h4>Headers</h4><button class="copy-btn" data-copy>Copy</button></div><pre>${esc(formatJSON(e.headers))}</pre></div>`;
469
+ if (!isEmpty(e.query)) html += `<div class="detail-section"><div class="detail-section-header"><h4>Query</h4><button class="copy-btn" data-copy>Copy</button></div><pre>${esc(formatJSON(e.query))}</pre></div>`;
470
+ if (!isEmpty(e.cookies)) html += `<div class="detail-section"><div class="detail-section-header"><h4>Cookies</h4><button class="copy-btn" data-copy>Copy</button></div><pre>${esc(formatJSON(e.cookies))}</pre></div>`;
471
+ if (!isEmpty(e.body)) html += `<div class="detail-section"><div class="detail-section-header"><h4>Body</h4><button class="copy-btn" data-copy>Copy</button></div><pre>${esc(formatJSON(e.body))}</pre></div>`;
430
472
  html += `</div></td></tr>`;
431
473
  }
432
474
 
@@ -435,6 +477,35 @@
435
477
  }
436
478
 
437
479
  logBody.addEventListener("click", (e) => {
480
+ const copyBtn = e.target.closest("[data-copy]");
481
+ if (copyBtn) {
482
+ const pre = copyBtn.closest(".detail-section").querySelector("pre");
483
+ navigator.clipboard.writeText(pre.textContent).then(() => {
484
+ copyBtn.textContent = "Copied!";
485
+ copyBtn.classList.add("copied");
486
+ setTimeout(() => {
487
+ copyBtn.textContent = "Copy";
488
+ copyBtn.classList.remove("copied");
489
+ }, 1500);
490
+ });
491
+ return;
492
+ }
493
+
494
+ const urlCopyBtn = e.target.closest("[data-copy-url]");
495
+ if (urlCopyBtn) {
496
+ navigator.clipboard.writeText(urlCopyBtn.dataset.copyUrl).then(() => {
497
+ urlCopyBtn.textContent = "Copied!";
498
+ urlCopyBtn.classList.add("copied");
499
+ setTimeout(() => {
500
+ urlCopyBtn.textContent = "Copy";
501
+ urlCopyBtn.classList.remove("copied");
502
+ }, 1500);
503
+ });
504
+ return;
505
+ }
506
+
507
+ if (e.target.closest(".detail-row")) return;
508
+
438
509
  const row = e.target.closest("tr[data-row]");
439
510
  if (!row) return;
440
511
  const rid = row.dataset.row;
package/dist/debug.js CHANGED
@@ -1,13 +1,19 @@
1
1
  import { join } from "path";
2
2
  import { Router } from "./router.js";
3
3
  export function createDebug(opts = {}) {
4
- const { url = "/debug", hosts = ["localhost", "127.0.0.1", "::1"], } = opts;
4
+ const { url = "/debug", hosts = [
5
+ "localhost",
6
+ "127.0.0.1",
7
+ "::1",
8
+ ], } = opts;
5
9
  if (process.env.NODE_ENV === "production" &&
6
10
  !process.env.FF_DEBUG_PRODUCTION_ENABLED)
7
- return (req, res, next) => { next(); };
11
+ return (req, res, next) => {
12
+ next();
13
+ };
8
14
  const pass = process.env.FF_DEBUG_PASS;
9
15
  function isHostAllowed(req) {
10
- return hosts.some((h) => h === req.socket.remoteAddress);
16
+ return hosts.some(h => h === req.socket.remoteAddress);
11
17
  }
12
18
  function checkAuth(req) {
13
19
  if (!pass)
@@ -31,7 +37,7 @@ export function createDebug(opts = {}) {
31
37
  router.use("/", (req, res, next) => {
32
38
  const start = Date.now();
33
39
  const hook = res.end.bind(res);
34
- res.end = function (...args) {
40
+ res.end = (...args) => {
35
41
  const entry = {
36
42
  method: req.method,
37
43
  url: req.url,
package/dist/helpers.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import path from "path";
2
2
  export function parseCookies(cookieHeader) {
3
3
  const cookies = {};
4
- cookieHeader.split(";").forEach((cookie) => {
4
+ cookieHeader.split(";").forEach(cookie => {
5
5
  const [name, ...valueParts] = cookie.split("=");
6
6
  const value = decodeURIComponent(valueParts.join("=").trim());
7
7
  cookies[name.trim()] = value;
@@ -32,4 +32,4 @@ export function getContentType(filePath, utf8 = false) {
32
32
  contentType += "; charset=utf-8";
33
33
  return contentType;
34
34
  }
35
- export const ensureLeadingDot = (str) => (str.startsWith(".") ? str : "." + str);
35
+ export const ensureLeadingDot = (str) => str.startsWith(".") ? str : "." + str;
package/dist/index.d.ts CHANGED
@@ -29,7 +29,7 @@ export declare class FalconFrame<Vars extends Record<string, any> = {}> extends
29
29
  * @param [origin] - An array of allowed origins. (default: ["*"])
30
30
  * @example
31
31
  * app.setOrigin(["http://example.com", "https://example.com"]);
32
- */
32
+ */
33
33
  setOrigin(origin?: string[] | string): void;
34
34
  /**
35
35
  * Listens to the specified port, or the environment variable PORT if available.
package/dist/index.js CHANGED
@@ -12,7 +12,7 @@ export class FalconFrame extends Router {
12
12
  vars = {};
13
13
  opts = {};
14
14
  engines = {};
15
- _400_formatter = (err) => {
15
+ _400_formatter = err => {
16
16
  return {
17
17
  err: true,
18
18
  msg: "Bad request",
@@ -40,9 +40,13 @@ export class FalconFrame extends Router {
40
40
  ...opts,
41
41
  };
42
42
  if (!this.opts.disableJsonParser)
43
- this.addBodyParser(json({ limit: this.opts.bodyLimit }));
43
+ this.addBodyParser(json({
44
+ limit: this.opts.bodyLimit,
45
+ }));
44
46
  if (!this.opts.disableUrlencodedParser)
45
- this.addBodyParser(urlencoded({ limit: this.opts.bodyLimit }));
47
+ this.addBodyParser(urlencoded({
48
+ limit: this.opts.bodyLimit,
49
+ }));
46
50
  this.engine(".html", (path, data, callback, FF) => {
47
51
  try {
48
52
  const content = renderHTML({
@@ -122,9 +126,13 @@ export class FalconFrame extends Router {
122
126
  * @param [origin] - An array of allowed origins. (default: ["*"])
123
127
  * @example
124
128
  * app.setOrigin(["http://example.com", "https://example.com"]);
125
- */
129
+ */
126
130
  setOrigin(origin = "*") {
127
- this.use(createCORS(Array.isArray(origin) ? origin : [origin]));
131
+ this.use(createCORS(Array.isArray(origin)
132
+ ? origin
133
+ : [
134
+ origin,
135
+ ]));
128
136
  }
129
137
  /**
130
138
  * Listens to the specified port, or the environment variable PORT if available.
@@ -26,7 +26,7 @@ export function matchMiddleware(url, middlewares) {
26
26
  let matches = true;
27
27
  for (let i = 0; i < middlewareParts.length; i++) {
28
28
  if (middlewareParts[i].startsWith(":")) {
29
- continue;
29
+ // continue;
30
30
  }
31
31
  else if (middlewareParts[i] !== urlParts[i]) {
32
32
  matches = false;
@@ -62,7 +62,10 @@ export function getMiddlewares(middlewares, matchUrl, basePath = "") {
62
62
  result.push(...nested);
63
63
  }
64
64
  else {
65
- result.push({ ...middleware, path: fullPath });
65
+ result.push({
66
+ ...middleware,
67
+ path: fullPath,
68
+ });
66
69
  }
67
70
  }
68
71
  return result;
package/dist/render.js CHANGED
@@ -35,7 +35,10 @@ export function renderHTML(options) {
35
35
  const partialPath = path.join(path.dirname(templatePath), partialName + ".html");
36
36
  return renderHTML({
37
37
  templatePath: partialPath,
38
- _renderedPaths: [...renderedPaths, realPath],
38
+ _renderedPaths: [
39
+ ...renderedPaths,
40
+ realPath,
41
+ ],
39
42
  noLayout: true,
40
43
  data,
41
44
  FF,
@@ -67,9 +70,12 @@ export function renderHTML(options) {
67
70
  templatePath: FFLayout,
68
71
  data: {
69
72
  ...data,
70
- body: template
73
+ body: template,
71
74
  },
72
- _renderedPaths: [...renderedPaths, realPath],
75
+ _renderedPaths: [
76
+ ...renderedPaths,
77
+ realPath,
78
+ ],
73
79
  noLayout: true,
74
80
  FF,
75
81
  FFVar,
package/dist/req.js CHANGED
@@ -10,7 +10,7 @@ export function handleRequest(req, res, FF, reRoute = false) {
10
10
  req.FF = FF;
11
11
  res.FF = FF;
12
12
  const originalEnd = res.end;
13
- res.end = function (...any) {
13
+ res.end = (...any) => {
14
14
  res._ended = true;
15
15
  return originalEnd.call(res, ...any);
16
16
  };
@@ -31,7 +31,7 @@ export function handleRequest(req, res, FF, reRoute = false) {
31
31
  req.cookies = parseCookies(req.headers.cookie || "");
32
32
  req.params = {};
33
33
  req.valid = (schema) => validate(schema, req.body);
34
- req.reRoute = (fn) => {
34
+ req.reRoute = fn => {
35
35
  fn(req);
36
36
  handleRequest(req, res, FF, true);
37
37
  };
@@ -49,11 +49,11 @@ export function handleRequest(req, res, FF, reRoute = false) {
49
49
  (req.id ? ` [${req.id}]` : ""));
50
50
  const middlewaresPath = req.path + "/";
51
51
  const middlewares = getMiddlewares(FF.middlewares, middlewaresPath.replace(/\/+/g, "/"));
52
- const matchedTypeMiddlewares = middlewares.filter((middleware) => middleware.method === req.method.toLowerCase() ||
52
+ const matchedTypeMiddlewares = middlewares.filter(middleware => middleware.method === req.method.toLowerCase() ||
53
53
  middleware.method === "all");
54
54
  const matchedMiddlewares = matchMiddleware(req.path, matchedTypeMiddlewares);
55
55
  logger.debug("Matched middlewares: " +
56
- matchedMiddlewares.map((middleware) => middleware.path).join(", "));
56
+ matchedMiddlewares.map(middleware => middleware.path).join(", "));
57
57
  if (matchedMiddlewares.length === 0) {
58
58
  res.status(404);
59
59
  FF._404(req, res);
package/dist/res.js CHANGED
@@ -108,7 +108,7 @@ export class FFResponse extends http.ServerResponse {
108
108
  render(view, data = {}, opts = {}) {
109
109
  const ff = this.FF;
110
110
  const viewsDir = opts.baseDir ??
111
- (opts.notUseViews ? "." : (ff.getVar("views") || "."));
111
+ (opts.notUseViews ? "." : ff.getVar("views") || ".");
112
112
  let engineName = path.extname(view);
113
113
  let filePath = view;
114
114
  if (opts.engine) {
package/dist/router.js CHANGED
@@ -4,8 +4,12 @@ export class Router {
4
4
  middlewares = [];
5
5
  addRoute(method, path, ...handlers) {
6
6
  const handler = handlers.pop();
7
- handlers.forEach((middleware) => this.use(path, middleware));
8
- return this.middlewares.push({ path, middleware: handler, method });
7
+ handlers.forEach(middleware => this.use(path, middleware));
8
+ return this.middlewares.push({
9
+ path,
10
+ middleware: handler,
11
+ method,
12
+ });
9
13
  }
10
14
  use(path = "/", middlewareFn, method = "all") {
11
15
  if (typeof path !== "string") {
package/dist/sse.js CHANGED
@@ -5,7 +5,10 @@ export class SSEManager {
5
5
  return (req, res, next) => {
6
6
  res.sseInit();
7
7
  const id = randomUUID();
8
- this._clients.set(id, { req, res });
8
+ this._clients.set(id, {
9
+ req,
10
+ res,
11
+ });
9
12
  req.on("close", () => {
10
13
  this._clients.delete(id);
11
14
  });
package/dist/static.js CHANGED
@@ -16,11 +16,16 @@ export function handleStaticFiles(dirPath, opts) {
16
16
  const serveFile = (req, res, filePath, stats) => {
17
17
  if (opts.render) {
18
18
  const defaultRenderer = res.FF.getVar("view engine");
19
- const renderStatement = (defaultRenderer && filePath.endsWith(ensureLeadingDot(defaultRenderer))) ||
19
+ const renderStatement = (defaultRenderer &&
20
+ filePath.endsWith(ensureLeadingDot(defaultRenderer))) ||
20
21
  (!opts.notRenderHtml && filePath.endsWith(".html"));
21
22
  if (renderStatement) {
22
- const relativePath = filePath.replace(dirPath + "/", "").replace(".html", "");
23
- res.render(filePath, opts.renderData?.[relativePath] || {}, { notUseViews: true });
23
+ const relativePath = filePath
24
+ .replace(dirPath + "/", "")
25
+ .replace(".html", "");
26
+ res.render(filePath, opts.renderData?.[relativePath] || {}, {
27
+ notUseViews: true,
28
+ });
24
29
  return true;
25
30
  }
26
31
  }
package/dist/types.d.ts CHANGED
@@ -88,7 +88,7 @@ export interface FFOpts {
88
88
  export type FFVars = {
89
89
  "render data": Record<string, any>;
90
90
  "view engine": string;
91
- "views": string;
92
- "layout": string;
91
+ views: string;
92
+ layout: string;
93
93
  };
94
94
  export type CombinedVars<ExtraVars> = ExtraVars & FFVars;
package/dist/valid.js CHANGED
@@ -24,8 +24,7 @@ export function validate(schema, data) {
24
24
  }
25
25
  break;
26
26
  case "min":
27
- if (typeof value === "string" &&
28
- value.length < parseInt(param)) {
27
+ if (typeof value === "string" && value.length < parseInt(param)) {
29
28
  fieldErrors.push(`${key} must be at least ${param} characters long`);
30
29
  }
31
30
  if (typeof value === "number" && value < parseInt(param)) {
@@ -33,20 +32,20 @@ export function validate(schema, data) {
33
32
  }
34
33
  break;
35
34
  case "max":
36
- if (typeof value === "string" &&
37
- value.length > parseInt(param)) {
35
+ if (typeof value === "string" && value.length > parseInt(param)) {
38
36
  fieldErrors.push(`${key} must not exceed ${param} characters`);
39
37
  }
40
38
  if (typeof value === "number" && value > parseInt(param)) {
41
39
  fieldErrors.push(`${key} must not exceed ${param}`);
42
40
  }
43
41
  break;
44
- case "email":
42
+ case "email": {
45
43
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
46
44
  if (!emailRegex.test(value)) {
47
45
  fieldErrors.push(`${key} must be a valid email`);
48
46
  }
49
47
  break;
48
+ }
50
49
  }
51
50
  }
52
51
  if (fieldErrors.length > 0) {
@@ -54,7 +53,10 @@ export function validate(schema, data) {
54
53
  errors[key] = fieldErrors;
55
54
  }
56
55
  }
57
- return { valid: isValid, validErrors: errors };
56
+ return {
57
+ valid: isValid,
58
+ validErrors: errors,
59
+ };
58
60
  }
59
61
  export function validateBody(schema) {
60
62
  return (req, res, next) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wxn0brp/falcon-frame",
3
- "version": "0.8.4",
3
+ "version": "0.8.6",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "author": "wxn0brP",
@@ -24,6 +24,7 @@
24
24
  ],
25
25
  "devDependencies": {
26
26
  "@types/node": "*",
27
+ "@wxn0brp/biome": "*",
27
28
  "tsc-alias": "*",
28
29
  "typescript": "^7"
29
30
  },