Skip to content
This repository has been archived by the owner on Sep 15, 2024. It is now read-only.

Commit

Permalink
Add next.js middler and Update code
Browse files Browse the repository at this point in the history
Signed-off-by: Matheus Sampaio Queiroga <[email protected]>
  • Loading branch information
Sirherobrine23 committed Dec 16, 2023
1 parent b5d2777 commit c27791b
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 125 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"editor.minimap.enabled": false,
"editor.detectIndentation": false,
"editor.codeActionsOnSave": {
"source.organizeImports": true
"source.organizeImports": "explicit",
"source.fixAll": "never"
},
"files.exclude": {
"**/node_modules/": true,
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
![npm](https://img.shields.io/npm/dw/neste) [![Teste](https://github.com/Sirherobrine23/neste/actions/workflows/test.yml/badge.svg)](https://github.com/Sirherobrine23/neste/actions/workflows/test.yml)
![npm](https://img.shields.io/npm/dw/neste) [![Teste](https://sirherobrine23.org/utils/neste/actions/workflows/test.yml/badge.svg)](https://sirherobrine23.org/utils/neste/actions/workflows/test.yml)

A fork of [express](https://github.com/expressjs/express) with patches and improvements for new integrations, fixes and more.

Expand Down
21 changes: 12 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
"types": "./src/index.d.ts",
"repository": {
"type": "git",
"url": "git+https://github.com/Sirherobrine23/neste.git"
"url": "git+https://sirherobrine23.org/utils/neste.git"
},
"bugs": {
"url": "https://github.com/Sirherobrine23/neste/issues"
"url": "https://sirherobrine23.org/utils/neste/issues"
},
"keywords": [
"express",
Expand All @@ -33,18 +33,21 @@
"postpack": "tsc --build --clean"
},
"devDependencies": {
"@types/busboy": "^1.5.2",
"@types/busboy": "^1.5.3",
"@types/cookie": "^0.6.0",
"@types/node": "^20.8.9",
"@types/ws": "^8.5.8",
"ts-node": "^10.9.1",
"typescript": "^5.2.2"
"@types/node": "^20.10.4",
"@types/ws": "^8.5.10",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
},
"dependencies": {
"busboy": "^1.6.0",
"cookie": "^0.6.0",
"path-to-regexp": "^6.2.1",
"ws": "^8.14.2",
"yaml": "^2.3.3"
"ws": "^8.15.1",
"yaml": "^2.3.4"
},
"optionalDependencies": {
"next": "*"
}
}
104 changes: 65 additions & 39 deletions src/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ import { WebSocket, WebSocketServer } from "ws";
import { ErrorRequestHandler, Handlers, Layer, NextFunction, RequestHandler, WsRequestHandler, WsResponse, assignRequest, assignResponse, assignWsResponse } from "./handler.js";
import { Methods, methods } from "./util.js";

export type RouterSettingsConfig = Record<string, any>;
export type RouterSettingsConfig = Partial<Record<"app path"|"env", string>> & Record<string, any> & {
"path resolve"?: boolean;
"json replacer"?: (key: string, value: any) => any;
"json escape"?: boolean;
"json space"?: string|number;
};

export class RouterSettings extends Map<string, any> {
constructor(sets?: RouterSettingsConfig) {
super();
Expand Down Expand Up @@ -71,6 +77,52 @@ export class Router extends Function {
settings: RouterSettings;
wsRooms: WsRoom = new WsRoom();

use(...fn: RequestHandler[]): this;
use(path: string|RegExp, ...fn: RequestHandler[]): this;
use() {
let p: [string|RegExp, Handlers[]];
if (!(arguments[0] instanceof RegExp || typeof arguments[0] === "string" && arguments[0].trim())) p = ["(.*)", Array.from(arguments)];
else p = [arguments[0], Array.from(arguments).slice(1)];
for (const fn of p[1]) {
if (typeof fn !== "function") throw new Error(util.format("Invalid middleare, require function, recived %s", typeof fn));
this.layers.push(new Layer(p[0], fn, { isRoute: true, strict: false, end: false }));
}
return this;
}

useError(...fn: ErrorRequestHandler[]): this;
useError(path: string|RegExp, ...fn: ErrorRequestHandler[]): this;
useError() {
this.use(...arguments);
return this;
}

all(path: string|RegExp, ...handlers: RequestHandler[]) {
if (!(path instanceof RegExp || typeof path === "string" && path.trim())) throw new Error("Set path");
for (const fn of handlers) {
const layerHand = new Layer(path, fn);
this.layers.push(layerHand);
}
return this;
}

/**
* Generic set router with request methods
*
* @param method - Method - GET, POST, PUT, etc.
* @param path - Request path
* @param handlers - Callbacks
*/
__method(method: Methods, path: string|RegExp, ...handlers: RequestHandler[]) {
if (!(path instanceof RegExp || typeof path === "string" && path.trim())) throw new Error("Set path");
for (const fn of handlers) {
const layerHand = new Layer(path, fn);
layerHand.method = method;
this.layers.push(layerHand);
}
return this;
}

handler(req: IncomingMessage, res: WebSocket|ServerResponse, next?: NextFunction) {
if (typeof next !== "function") next = (err?: any) => {
if (err && !(err === "router" || err === "route") && this.settings.get("env") !== "production") console.error(err);
Expand All @@ -89,10 +141,9 @@ export class Router extends Function {
}

const { layers } = this, method = (res instanceof WebSocket ? "ws" : (String(req.method||"").toLowerCase())), saveParms = Object.freeze(req["params"] || {});
let originalPath: string = req["path"]||(parse(req.url)).pathname;
let originalPath: string = req["path"]||(parse(req.url)).pathname, layersIndex = 0;
if (this.settings.get("path resolve")) originalPath = path.posix.resolve("/", originalPath);
if (this.settings.has("app path") && typeof this.settings.get("app path") === "string" && originalPath.startsWith(this.settings.get("app path"))) originalPath = path.posix.resolve("/", originalPath.slice(path.posix.resolve("/", this.settings.get("app path")).length));
let layersIndex = 0;

const nextHandler = async (err?: any) => {
req["path"] = originalPath;
Expand All @@ -106,18 +157,26 @@ export class Router extends Function {
if (!layerMatch) return nextHandler(err);
req["path"] = layerMatch.path;
if (err && layer.handler.length !== 4) return nextHandler(err);
const reqMod = assignRequest(this, req, method, Object.assign({}, saveParms, layerMatch.params));
if (!req["__set_cookie"]) {
req["__set_cookie"] = true;
Object.defineProperty(req.headers, "set-cookie", {
set(v) { reqMod.Cookies.fromString(v); },
get() { return reqMod.Cookies.toString(); },
});
}
try {
if (err) {
if (res instanceof WebSocket) return nextHandler(err);
const fn = layer.handler as ErrorRequestHandler;
await fn(err, assignRequest(this, req, method, Object.assign({}, saveParms, layerMatch.params)), assignResponse(this, res), nextHandler);
await fn(err, reqMod, assignResponse(this, res), nextHandler);
} else {
if (res instanceof WebSocket) {
const fn = layer.handler as WsRequestHandler;
await fn(assignRequest(this, req, method, Object.assign({}, saveParms, layerMatch.params)), assignWsResponse(this, res), nextHandler);
await fn(reqMod, assignWsResponse(this, res), nextHandler);
} else {
const fn = layer.handler as RequestHandler;
await fn(assignRequest(this, req, method, Object.assign({}, saveParms, layerMatch.params)), assignResponse(this, res), nextHandler);
await fn(reqMod, assignResponse(this, res), nextHandler);
}
}
} catch (err) {
Expand All @@ -126,39 +185,6 @@ export class Router extends Function {
}
nextHandler().catch(next);
}


use(...fn: RequestHandler[]): this;
use(path: string|RegExp, ...fn: RequestHandler[]): this;
use() {
let p: [string|RegExp, Handlers[]];
if (!(arguments[0] instanceof RegExp || typeof arguments[0] === "string" && arguments[0].trim())) p = ["(.*)", Array.from(arguments)];
else p = [arguments[0], Array.from(arguments).slice(1)];
for (const fn of p[1]) {
if (typeof fn !== "function") throw new Error(util.format("Invalid middleare, require function, recived %s", typeof fn));
this.layers.push(new Layer(p[0], fn, { isRoute: true, strict: false, end: false }));
}
return this;
}

all(path: string|RegExp, ...handlers: RequestHandler[]) {
if (!(path instanceof RegExp || typeof path === "string" && path.trim())) throw new Error("Set path");
for (const fn of handlers) {
const layerHand = new Layer(path, fn);
this.layers.push(layerHand);
}
return this;
}

__method(method: Methods, path: string|RegExp, ...handlers: RequestHandler[]) {
if (!(path instanceof RegExp || typeof path === "string" && path.trim())) throw new Error("Set path");
for (const fn of handlers) {
const layerHand = new Layer(path, fn);
layerHand.method = method;
this.layers.push(layerHand);
}
return this;
}
};

methods.forEach(method => Router.prototype[method] = function(this: Router) { return this.__method.apply(this, ([method] as any[]).concat(Array.from(arguments))) } as any)
Expand Down
90 changes: 16 additions & 74 deletions src/handler.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
import { MatchFunction, ParseOptions, RegexpToFunctionOptions, TokensToRegexpOptions, match as regexMatch } from "path-to-regexp";
import { IncomingMessage, ServerResponse } from "http";
import { WebSocket } from "ws";
import cookie from "cookie";
import { parse } from "url";
import { IncomingMessage, ServerResponse, STATUS_CODES } from "http";
import { isIP } from "net";
import { defineProperties, mixin } from "./util.js";
import { posix } from "path";
import { MatchFunction, ParseOptions, RegexpToFunctionOptions, TokensToRegexpOptions, match as regexMatch } from "path-to-regexp";
import stream from "stream";
import { parse } from "url";
import { WebSocket } from "ws";
import yaml from "yaml";
import * as ranger from "./ranger.js";
import { Router } from "./application.js";
import { posix } from "path";
import * as ranger from "./ranger.js";
import { defineProperties, mixin } from "./util.js";

export class CookieManeger extends Map<string, [string, cookie.CookieSerializeOptions]> {
constructor(public initialCookies: string) {
super();
if (!initialCookies) return;
const parsed = cookie.parse(initialCookies);
Object.keys(parsed).forEach(k => this.set(k, parsed[k]));
this.fromString(initialCookies);
}

// @ts-ignore
Expand All @@ -30,6 +29,12 @@ export class CookieManeger extends Map<string, [string, cookie.CookieSerializeOp
return Array.from(this.entries()).filter(([key, [value]]) => parsed[key] && parsed[key] !== value).map(([key, [value, opt]]) => cookie.serialize(key, value, opt)).join("; ");
}

fromString(cookieHead: string): this {
const parsed = cookie.parse(cookieHead);
Object.keys(parsed).forEach(k => this.set(k, parsed[k]));
return this;
}

get sizeDiff() {
const parsed = cookie.parse(this.initialCookies);
return Array.from(this.entries()).filter(([key, [value]]) => parsed[key] && parsed[key] !== value).length;
Expand All @@ -42,6 +47,7 @@ export class CookieManeger extends Map<string, [string, cookie.CookieSerializeOp

export interface Request extends IncomingMessage {
app: Router;
res: Response;
protocol: "https"|"http";
secure: boolean;
path: string;
Expand Down Expand Up @@ -133,71 +139,7 @@ export class Request {
}
}

export const codes = {
"100": "Continue",
"101": "Switching Protocols",
"102": "Processing",
"103": "Early Hints",
"200": "OK",
"201": "Created",
"202": "Accepted",
"203": "Non-Authoritative Information",
"204": "No Content",
"205": "Reset Content",
"206": "Partial Content",
"207": "Multi-Status",
"208": "Already Reported",
"226": "IM Used",
"300": "Multiple Choices",
"301": "Moved Permanently",
"302": "Found",
"303": "See Other",
"304": "Not Modified",
"305": "Use Proxy",
"307": "Temporary Redirect",
"308": "Permanent Redirect",
"400": "Bad Request",
"401": "Unauthorized",
"402": "Payment Required",
"403": "Forbidden",
"404": "Not Found",
"405": "Method Not Allowed",
"406": "Not Acceptable",
"407": "Proxy Authentication Required",
"408": "Request Timeout",
"409": "Conflict",
"410": "Gone",
"411": "Length Required",
"412": "Precondition Failed",
"413": "Payload Too Large",
"414": "URI Too Long",
"415": "Unsupported Media Type",
"416": "Range Not Satisfiable",
"417": "Expectation Failed",
"418": "I'm a Teapot",
"421": "Misdirected Request",
"422": "Unprocessable Entity",
"423": "Locked",
"424": "Failed Dependency",
"425": "Too Early",
"426": "Upgrade Required",
"428": "Precondition Required",
"429": "Too Many Requests",
"431": "Request Header Fields Too Large",
"451": "Unavailable For Legal Reasons",
"500": "Internal Server Error",
"501": "Not Implemented",
"502": "Bad Gateway",
"503": "Service Unavailable",
"504": "Gateway Timeout",
"505": "HTTP Version Not Supported",
"506": "Variant Also Negotiates",
"507": "Insufficient Storage",
"508": "Loop Detected",
"509": "Bandwidth Limit Exceeded",
"510": "Not Extended",
"511": "Network Authentication Required"
}
export const codes = STATUS_CODES;

export interface Response extends Omit<ServerResponse, "req"> {
req: Request;
Expand Down
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ import { Neste, Router, RouterSettings, WsRoom } from "./application.js";

export { staticFile } from "./middles/staticFile.js";
export { parseBody } from "./middles/bodyParse.js";
export { nextjsPages } from "./middles/nextjs.js";

export type { FileStorage, LocalFile, ParseBodyOptions } from "./middles/bodyParse.js";

export type { Handlers, ErrorRequestHandler, RequestHandler, WsRequestHandler, NextFunction } from "./handler.js";
export type { RouterSettingsConfig } from "./application.js";

function router() { return new Router(); }
function neste() { return new Neste(); }

export { neste, router, CookieManeger, Layer, Request, Response, WsResponse, Neste, Router, RouterSettings, WsRoom };
export default neste;
40 changes: 40 additions & 0 deletions src/middles/nextjs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { NextServer, NextServerOptions } from "next/dist/server/next.js";

Check failure on line 1 in src/middles/nextjs.ts

View workflow job for this annotation

GitHub Actions / Test in node 17.x

Cannot find module 'next/dist/server/next.js' or its corresponding type declarations.

Check failure on line 1 in src/middles/nextjs.ts

View workflow job for this annotation

GitHub Actions / Test in node 16.x

Cannot find module 'next/dist/server/next.js' or its corresponding type declarations.

Check failure on line 1 in src/middles/nextjs.ts

View workflow job for this annotation

GitHub Actions / Test in node 15.x

Cannot find module 'next/dist/server/next.js' or its corresponding type declarations.
import type { RequestHandler } from "../handler.js";
import { defineProperties } from "../util.js";

/**
* Create config to Nextjs pages
*
* in config set full pages path `dir`, and current middle path
*
* @example
* {
* dir: "/root/nextjs_example",
* conf: {
* basePath: "/foo/bar/page_next"
* }
* }
*
* @param config - Next config
* @returns
*/
export async function nextjsPages(config: Omit<NextServerOptions, "customServer">): Promise<RequestHandler> {
const app = ((await import("next")).default as any as (options: NextServerOptions) => NextServer)({

Check failure on line 22 in src/middles/nextjs.ts

View workflow job for this annotation

GitHub Actions / Test in node 17.x

Cannot find module 'next' or its corresponding type declarations.

Check failure on line 22 in src/middles/nextjs.ts

View workflow job for this annotation

GitHub Actions / Test in node 16.x

Cannot find module 'next' or its corresponding type declarations.

Check failure on line 22 in src/middles/nextjs.ts

View workflow job for this annotation

GitHub Actions / Test in node 15.x

Cannot find module 'next' or its corresponding type declarations.
...config,
customServer: true,
dir: "/root/nextjs_example",
conf: {
basePath: "/page_next"
}
});
await app.prepare();
const handler = await app.getRequestHandler();
return (req, res) => handler(defineProperties(req, {
"url": {
writable: true,
configurable: true,
enumerable: true,
value: String().concat(req.path, ...(Object.keys(req.query).length > 0 ? [ "?", Object.keys(req.query).map(k => String().concat(k, "=", req.query[k])).join("&") ] : []))
}
}), res);
}
2 changes: 1 addition & 1 deletion src/util.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const __methods = [ "ws", "get", "post", "put", "delete", "head", "connect", "options", "trace" ] as const;
const __methods = [ "ws", "acl", "bind", "checkout", "connect", "copy", "delete", "get", "head", "link", "lock", "m-search", "merge", "mkactivity", "mkcalendar", "mkcol", "move", "notify", "options", "patch", "post", "propfind", "proppatch", "purge", "put", "rebind", "report", "search", "source", "subscribe", "trace", "unbind", "unlink", "unlock", "unsubscribe" ] as const;
export type Methods = typeof __methods[number];
export const methods: Methods[] = Object.freeze(__methods) as any;

Expand Down

0 comments on commit c27791b

Please sign in to comment.