Skip to content

Commit

Permalink
chore
Browse files Browse the repository at this point in the history
  • Loading branch information
magne4000 committed Dec 17, 2024
1 parent 105940c commit e66b1f1
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 9 deletions.
20 changes: 11 additions & 9 deletions packages/core/src/pipe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,17 @@ type AnyMiddleware<In extends Universal.Context = any, Out extends Universal.Con

type ExtractUF<T> = T extends UniversalFn<infer _, infer Fn> ? Fn : never;

type ComposeReturnType<T extends AnyMiddleware[]> = Last<T> extends UniversalHandler<any>
? UniversalHandler<In<First<T>>>
: Last<T> extends UniversalMiddleware<any, any>
? UniversalMiddleware<In<First<T>>, In<Last<T>>>
: Last<T> extends UniversalFn<UniversalHandler<any>, infer _>
? UniversalFn<UniversalHandler<In<First<T>>>, ExtractUF<Last<T>>>
: Last<T> extends UniversalFn<UniversalMiddleware<any, any>, infer _>
? UniversalFn<UniversalMiddleware<In<First<T>>, In<Last<T>>>, ExtractUF<Last<T>>>
: never;
type ComposeReturnType<T extends AnyMiddleware[]> = Last<T> extends never
? T[number]
: Last<T> extends UniversalHandler<any>
? UniversalHandler<In<First<T>>>
: Last<T> extends UniversalMiddleware<any, any>
? UniversalMiddleware<In<First<T>>, In<Last<T>>>
: Last<T> extends UniversalFn<UniversalHandler<any>, infer _>
? UniversalFn<UniversalHandler<In<First<T>>>, ExtractUF<Last<T>>>
: Last<T> extends UniversalFn<UniversalMiddleware<any, any>, infer _>
? UniversalFn<UniversalMiddleware<In<First<T>>, In<Last<T>>>, ExtractUF<Last<T>>>
: never;

type Cast<
T extends AnyMiddleware,
Expand Down
30 changes: 30 additions & 0 deletions packages/router/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "@universal-middleware/router",
"version": "0.1.0",
"type": "module",
"description": "Universal router",
"files": [
"dist"
],
"exports": {
".": "./dist/index.js"
},
"author": "Joël Charles <[email protected]>",
"repository": "https://github.com/magne4000/universal-middleware",
"license": "MIT",
"scripts": {
"build": "rimraf dist && tsup",
"test": "vitest --typecheck run",
"prepack": "pnpm build",
"test:typecheck": "tsc -p tsconfig.json --noEmit"
},
"sideEffects": false,
"dependencies": {
"@universal-middleware/core": "workspace:^",
"@universal-middleware/hono": "workspace:^",
"rou3": "^0.5.1"
},
"devDependencies": {
"hono": "catalog:"
}
}
118 changes: 118 additions & 0 deletions packages/router/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import type { UniversalHandler, UniversalMiddleware } from "@universal-middleware/core";
import { pipe, universalSymbol } from "@universal-middleware/core";
import { createHandler, createMiddleware } from "@universal-middleware/hono";
import type { Hono } from "hono";
import { type RouterContext, addRoute, createRouter, findRoute } from "rou3";

export interface RouteDefinition {
method: "get";
path: string;
handler: UniversalHandler;
}

export type MiddlewareDefinition = [middleware: UniversalMiddleware, order: number];

export interface UniversalRouterInterface {
use(middleware: UniversalMiddleware, order?: number): this;
route(method: RouteDefinition["method"], path: string, handler: UniversalHandler): this;
}

export class UniversalRouter implements UniversalRouterInterface {
public router: RouterContext<UniversalHandler>;
#middlewares: MiddlewareDefinition[];
#computedMiddleware?: UniversalMiddleware;

constructor() {
this.router = createRouter<UniversalHandler>();
this.#middlewares = [];
}

use(middleware: UniversalMiddleware, order?: number) {
this.#computedMiddleware = undefined;
this.#middlewares.push([middleware, order ?? 0]);
return this;
}

// TODO https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods
route(method: RouteDefinition["method"], path: string, handler: UniversalHandler) {
addRoute(this.router, method.toLocaleUpperCase() as Uppercase<RouteDefinition["method"]>, path, handler);
return this;
}

// TODO? routes to adapter?
// Or UniversalRouter extends per adapter, e.g. UniversalHonoRouter
// routes() {
// this.router.root;
// }
//
// middlewares() {
// return ordered(this.#middlewares);
// }

get [universalSymbol](): UniversalMiddleware {
if (!this.#computedMiddleware && this.#middlewares.length > 0) {
this.#computedMiddleware = pipe(...ordered(this.#middlewares));
}
return (request, ctx, runtime) => {
// TODO core helper to cache the result
const url = new URL(request.url);
const router = findRoute(this.router, request.method, url.pathname);

if (router) {
const handler = this.#computedMiddleware ? pipe(this.#computedMiddleware, router.data) : router.data;
return handler(request, ctx, runtime);
}
if (this.#computedMiddleware) {
return this.#computedMiddleware(request, ctx, runtime);
}
// else do nothing
};
}
}

export class UniversalHonoRouter implements UniversalRouterInterface {
#app: Hono;

constructor(app: Hono) {
this.#app = app;
}

use(middleware: UniversalMiddleware) {
this.#app.use(createMiddleware(() => middleware)());
return this;
}

route(method: RouteDefinition["method"], path: string, handler: UniversalHandler) {
this.#app[method](path, createHandler(() => handler)());
return this;
}
}

export function apply(
router: UniversalRouterInterface,
routes?: RouteDefinition[],
middlewares?: MiddlewareDefinition[],
) {
if (middlewares) {
const ms = ordered(middlewares);
for (const m of ms) {
router.use(m);
}
}
if (routes) {
for (const r of routes) {
router.route(r.method, r.path, r.handler);
}
}
}

function ordered(middlewares: MiddlewareDefinition[]) {
return Array.from(middlewares)
.sort((a, b) => a[1] - b[1])
.map((x) => x[0]);
}

// middlewares
// order
// routes
// handlers
3 changes: 3 additions & 0 deletions packages/router/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "../../tsconfig.json"
}
21 changes: 21 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit e66b1f1

Please sign in to comment.