Skip to content

Commit

Permalink
feat: allow more string "errors" in router
Browse files Browse the repository at this point in the history
  • Loading branch information
npenin committed Jan 30, 2024
1 parent 623cb3b commit 1a9608f
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 28 deletions.
10 changes: 5 additions & 5 deletions packages/core/src/router/composite.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { eachAsync } from '../helpers.js';
import { AnyMiddleware, ErrorMiddleware, Middleware, MiddlewareError, MiddlewarePromise, MiddlewareResult, MiddlewareSuccess, OptionsResponse } from './shared.js';
import { AnyMiddleware, ErrorMiddleware, Middleware, MiddlewareError, MiddlewarePromise, MiddlewareResult, MiddlewareSuccess, OptionsResponse, SpecialNextParam } from './shared.js';


export function convertToMiddleware<T extends unknown[]>(fn: (...args: T) => Promise<unknown>): Middleware<T>
Expand Down Expand Up @@ -36,14 +36,14 @@ export function convertToErrorMiddleware<T extends unknown[]>(fn: (error: Error
}

// eslint-disable-next-line @typescript-eslint/no-empty-interface, @typescript-eslint/no-unused-vars
export interface ExtendableCompositeMiddleware<T extends unknown[]> { }
export interface ExtendableCompositeMiddleware<T extends unknown[], TSpecialNextParam extends string | void = SpecialNextParam> { }

export function isMiddlewareError(x: MiddlewareError<unknown> | MiddlewareSuccess<unknown>): x is MiddlewareError<unknown>
export function isMiddlewareError<TSpecialNextParam extends SpecialNextParam = SpecialNextParam>(x: MiddlewareError<MiddlewareResult<TSpecialNextParam>> | MiddlewareSuccess<unknown>): x is MiddlewareError<MiddlewareResult<TSpecialNextParam>>
{
return typeof x['error'] != 'undefined';
}

export class MiddlewareComposite<T extends unknown[]> implements Middleware<T>, ExtendableCompositeMiddleware<T>
export class MiddlewareComposite<T extends unknown[], TSpecialNextParam extends string | void = SpecialNextParam> implements Middleware<T, TSpecialNextParam>, ExtendableCompositeMiddleware<T, TSpecialNextParam>
{
public readonly name?: string

Expand Down Expand Up @@ -122,7 +122,7 @@ export class MiddlewareComposite<T extends unknown[]> implements Middleware<T>,
}
}

public async handle(...req: T): MiddlewarePromise
public async handle(...req: T): MiddlewarePromise<TSpecialNextParam>
{
let error: Error | OptionsResponse = undefined;
let failed: boolean = undefined;
Expand Down
14 changes: 7 additions & 7 deletions packages/core/src/router/shared.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
/* A middleware promise is the opposite of a promise which will fail when there is a result
* and which will succeed as long as there is no middleware that handled the context
*/
export type MiddlewarePromise = Promise<MiddlewareResult>;
export type MiddlewareResult = Error | SpecialNextParam | OptionsResponse;
export type MiddlewarePromise<T extends string | void = SpecialNextParam> = Promise<MiddlewareResult<T>>;
export type MiddlewareResult<T extends string | void = SpecialNextParam> = Error | T | OptionsResponse;

export interface Middleware<T extends unknown[]>
export interface Middleware<T extends unknown[], TSpecialNextParam extends string | void = SpecialNextParam>
{
handle(...context: T): MiddlewarePromise;
handle(...context: T): MiddlewarePromise<TSpecialNextParam>;
}

export interface ErrorMiddleware<T extends unknown[]>
export interface ErrorMiddleware<T extends unknown[], U extends string | void = SpecialNextParam>
{
handleError(error: Error | OptionsResponse, ...context: T): MiddlewarePromise;
handleError(error: Exclude<MiddlewareResult<U>, U>, ...context: T): MiddlewarePromise<U>;
}

export type AnyMiddleware<T extends unknown[]> = Middleware<T> | ErrorMiddleware<T>;
export type AnyMiddleware<T extends unknown[], TSpecialNextParam extends string | void = SpecialNextParam> = Middleware<T, TSpecialNextParam> | ErrorMiddleware<T, TSpecialNextParam>;

export type OptionsResponse = { allow: string[] };

Expand Down
55 changes: 39 additions & 16 deletions packages/core/src/url-handler.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Middleware, MiddlewareComposite, MiddlewarePromise, Routable, Router } from "./index.js";

export class UrlHandler<T extends [URL, ...unknown[]]> implements Middleware<T>
type MiddlewareError = 'break' | 'loop' | void;

export class UrlHandler<T extends [URL, ...unknown[]], TResult = unknown> implements Middleware<T>
{
protocol: MiddlewareComposite<unknown[]>;
host: MiddlewareComposite<unknown[]>;
protocol: MiddlewareComposite<T, MiddlewareError>;
host: MiddlewareComposite<T>;
router: Router<[Routable, ...T]>;
constructor()
{
Expand All @@ -12,21 +14,38 @@ export class UrlHandler<T extends [URL, ...unknown[]]> implements Middleware<T>
this.router = new Router('path');
}

public process(...context: T)
public process(...context: T): Promise<TResult>
{
return this.handle(...context).then(v => { throw v }, e => e);
}

public useProtocol(protocol: string, action: (...args: T) => Promise<TResult>)
{
const handler = new UrlHandler.Protocol(protocol);
this.protocol.useMiddleware(handler);
return handler.use(action);
}

public useHost(host: string, action: (...args: T) => Promise<TResult>)
{
const handler = new UrlHandler.Host(host);
this.host.useMiddleware(handler);
handler.use(action);
return handler;
}

public async handle(...context: T): MiddlewarePromise
{
let error = this.protocol.handle(context);
let error = await this.protocol.handle(...context);
while (error === 'loop')
error = await this.protocol.handle(...context);
if (error)
return error;
error = this.host.handle(context);
error = await this.host.handle(...context);
if (error)
return error;
let params: Routable['params'];
error = this.router.handle({
error = await this.router.handle({
path: context[0].pathname, get params()
{
if (params)
Expand All @@ -49,34 +68,38 @@ export class UrlHandler<T extends [URL, ...unknown[]]> implements Middleware<T>

export namespace UrlHandler
{
export class Protocol<T extends [URL, ...unknown[]]> extends MiddlewareComposite<T>
export class Protocol<T extends [URL, ...unknown[]]> extends MiddlewareComposite<T, MiddlewareError>
{
constructor(private protocol: string)
constructor(public readonly protocol: string)
{
super();
//safeguarding protocol
if (!protocol.endsWith(':'))
this.protocol = protocol + ':';
if (protocol.endsWith(':'))
this.protocol = protocol.substring(0, protocol.length - 1);
}

handle(...context: T): MiddlewarePromise
async handle(...context: T): MiddlewarePromise<MiddlewareError>
{
if (context[0].protocol === this.protocol)
if (context[0].protocol == this.protocol + ':')
{
return super.handle(...context);
}
else if (context[0].protocol.startsWith(this.protocol + '+'))
{
context[0].protocol = context[0].protocol.substring(this.protocol.length + 2);
return Promise.resolve('loop');
}
return;
}

}
export class Host<T extends [URL, ...unknown[]]> extends MiddlewareComposite<T>
export class Host<T extends [URL, ...unknown[]]> extends MiddlewareComposite<T, MiddlewareError>
{
constructor(private host: string)
{
super();
}

handle(...context: T): MiddlewarePromise
handle(...context: T): MiddlewarePromise<MiddlewareError>
{
if (context[0].host === this.host)
{
Expand Down

0 comments on commit 1a9608f

Please sign in to comment.