Commit c026621f authored by Grant's avatar Grant
Browse files

remove class

parent 2d80af31
Loading
Loading
Loading
Loading
Loading
+2 −4
Original line number Diff line number Diff line
{
  "name": "@sc07/router",
  "version": "0.0.1",
  "exports": {
    "object": "./dist/object/index.js",
    "class": "./dist/class/index.js"
  },
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "files": [
    "dist"
  ],

src/class/Router.ts

deleted100644 → 0
+0 −137
Original line number Diff line number Diff line
import Express from "express";
import { type PathParams } from "express-serve-static-core";
import { z } from "zod/v4";

type HTTPMethod =
  | "all"
  | "get"
  | "post"
  | "put"
  | "delete"
  | "patch"
  | "options"
  | "head";

type RouterDecorator = ClassMethodDecoratorContext<
  Router,
  (this: Router, req: Express.Request, res: Express.Response) => any
>;

type RouterHandler = (req: Express.Request, res: Express.Response) => any;

export namespace Router {
  export type Request<Body = any> = Omit<Express.Request, "body"> & {
    body: Body;
  };

  export type Response<
    Body = any,
    Errors extends { error: string; details?: any; error_message?: string } = {
      error: string;
      error_message?: string;
      details?: any;
    }
  > = Express.Response<
    | ({ success: true } & Body)
    | ({ success: false } & (
        | {
            error: "ValidationError";
            error_message: string;
            details: z.core.$ZodIssue[];
          }
        | Errors
      ))
  >;
}

export class Router {
  protected _router: Express.Router;
  protected _premiddleware: Express.RequestHandler[] = [];

  constructor(..._args: any[]) {
    this._router = Express.Router();
    // allows for decorators to add middlewares at the beginning
    this._router.use(async (req, res, next) => {
      for (const handler of this._premiddleware) {
        await new Promise((go) => handler(req, res, go));
      }
      next();
    });
  }

  get router() {
    return this._router;
  }

  use(path: PathParams, router: Router) {
    this._router.use(path, router.router);
  }

  static handler = <Method extends HTTPMethod>(
    method: Method,
    route: PathParams,
    env?: NodeJS.ProcessEnv["NODE_ENV"][]
  ) => {
    return function (target: RouterHandler, context: RouterDecorator) {
      if (!env || (env && env.indexOf(process.env.NODE_ENV) > -1))
        context.addInitializer(function () {
          this._router[method](route, target.bind(this));
        });
      return () => {
        throw new Error("RouteHandler called directly");
      };
    };
  };

  /**
   * Validates req.body by specified Zod schema
   *
   * Enforces that `req.body` is the Schema
   */
  static body = <Schema extends z.ZodType>(
    schema: Schema,
    handleError?: RouterHandler
  ) => {
    return function (target: RouterHandler, _context: RouterDecorator) {
      return function (
        this: Router,
        req: Express.Request & { body: z.infer<Schema> },
        res: Express.Response
      ) {
        try {
          schema.parse(req.body);
        } catch (err) {
          if (err instanceof z.ZodError) {
            if (handleError) {
              handleError(req, res);
            } else {
              res.status(400).json({
                success: false,
                error: "ValidationError",
                error_message: z.prettifyError(err),
                details: err.issues,
              });
            }
          }

          return;
        }

        return target.bind(this)(req, res);
      };
    };
  };
}

// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
type ClassMethodDecorator<T extends Function> = (
  target: T,
  context: ClassMethodDecoratorContext
) => any;
type ClassDecorator = <T extends { new (...args: any[]): any }>(
  target: T,
  context: ClassDecoratorContext<T>
) => any;
type Decorator<OnClass extends boolean> = OnClass extends true
  ? ClassDecorator
  : ClassMethodDecorator<RouterHandler>;

src/class/RouterBuilder.test.ts

deleted100644 → 0
+0 −35
Original line number Diff line number Diff line
import Express from "express";
import { RouterBuilder } from "./RouterBuilder";
import type * as Full from "../schema.test";

const { Router, get } = RouterBuilder<Full.paths>();

class TestRouter2 extends Router {
  @get("/events")
  async test(req: Express.Request, res: Express.Response) {
    res.send("sub");
  }
}

class TestRouter extends Router {
  constructor() {
    super();
  }

  @get("/auth")
  async test(req: Express.Request, res: Express.Response) {
    res.send("okie dokie");
  }

  @get("/events")
  async testt(req: Express.Request, res: Express.Response) {
    res.send("main");
  }
}

const app = Express();
app.use(new TestRouter().router);
app.use("/sub", new TestRouter2().router);
app.listen(3000, () => {
  console.log("ok");
});

src/class/RouterBuilder.ts

deleted100644 → 0
+0 −45
Original line number Diff line number Diff line
import Express from "express";

type HTTPMethod =
  | "all"
  | "get"
  | "post"
  | "put"
  | "delete"
  | "patch"
  | "options"
  | "head";

export const RouterBuilder = <Paths extends {}>() => {
  let router: Express.Router;

  class Router {
    constructor() {
      router = Express.Router();
    }

    get router() {
      return router;
    }
  }

  const buildHandler = (method: HTTPMethod) => (path: keyof Paths) => {
    return function (
      target: (req: Express.Request, res: Express.Response) => any,
      context: ClassMethodDecoratorContext
    ) {
      context.addInitializer(function () {
        router[method](path as any, target.bind(this));
      });

      return () => {
        throw new Error("RouteHandler called directly");
      };
    };
  };

  return {
    Router,
    get: buildHandler("get"),
  };
};
Loading