Skip to content

Commit

Permalink
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 7 deletions.
4 changes: 2 additions & 2 deletions src/request.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export interface IncomingCloudflareProperties {
timezone?: string;
}

export declare class ServerRequest {
export declare class ServerRequest<P extends Params = Params> {
constructor(event: FetchEvent);
url: string;
path: string;
Expand All @@ -131,7 +131,7 @@ export declare class ServerRequest {
extend: FetchEvent['waitUntil'];
cf: IncomingCloudflareProperties;
headers: Headers;
params: Params;
params: P;
body: {
<T>(): Promise<T|void>;
json<T=any>(): Promise<T>;
Expand Down
18 changes: 16 additions & 2 deletions src/router.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,24 @@ export function reply(handler: ResponseHandler): FetchHandler;
export function listen(handler: ResponseHandler): void;

export type Route = { params: Params; handler: Handler | false };
export type Handler = (req: ServerRequest, res: ServerResponse) => Promisable<Response|void>;
export type Handler<P extends Params = Params> = (req: ServerRequest<P>, res: ServerResponse) => Promisable<Response|void>;

// TODO: wildcard
export type RouteParams<T extends string> =
T extends `${infer S}:${infer P}?/${infer Rest}`
? { [K in P]?: string } & RouteParams<Rest>
: T extends `${infer S}:${infer P}/${infer Rest}`
? { [K in P | keyof RouteParams<Rest>]: string }
: T extends `${infer S}:${infer P}?`
? { [K in P]?: string }
: T extends `${infer S}:${infer P}`
? { [K in P]: string }
: {};

export declare class Router {
add(method: string, route: RegExp | string, handler: Handler): void;
add<T extends RegExp>(method: string, route: T, handler: Handler<Params>): void;
add<T extends string>(method: string, route: T, handler: Handler<RouteParams<T>>): void;

find(method: string, pathname: string): Route;
run(event: FetchEvent): Promise<Response>;
onerror(req: ServerRequest, res: ServerResponse, status?: number, error?: Error): Promisable<Response>;
Expand Down
2 changes: 1 addition & 1 deletion src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export function Router(): RR {
let $: RR, tree: Tree = {};

return $ = {
add(method, route, handler) {
add(method: string, route: RegExp | string, handler: Handler) {
let dict = tree[method];

if (dict === void 0) {
Expand Down
87 changes: 85 additions & 2 deletions types/check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { byteLength, HEX, uid, uuid } from 'worktop/utils';
import { listen, reply, Router, STATUS_CODES } from 'worktop';

import type { KV } from 'worktop/kv';
import type { FetchHandler, Route } from 'worktop';
import type { ServerRequest, IncomingCloudflareProperties } from 'worktop/request';
import type { FetchHandler, Route, RouteParams } from 'worktop';
import type { Params, ServerRequest, IncomingCloudflareProperties } from 'worktop/request';

declare function assert<T>(value: T): void;

Expand Down Expand Up @@ -199,6 +199,89 @@ Cache.reply(API.run);
Cache.listen(reply(API.run));
Cache.listen(API.run);

/**
* WORKTOP/ROUTER
* > PATTERNS & PARAMS
*/

let foo='', bar='';

// @ts-expect-error
assert<RouteParams<'/:foo'>>({ /*empty*/ });
assert<RouteParams<'/:foo'>>({ foo });
assert<RouteParams<'/:foo?'>>({ foo });
assert<RouteParams<'/:foo?'>>({ /*empty*/ });
assert<RouteParams<'/foo'>>({ /*empty*/ });

// @ts-expect-error
assert<RouteParams<'/foo/:bar'>>({ /*empty*/ });
assert<RouteParams<'/foo/:bar'>>({ bar });
// @ts-expect-error
assert<RouteParams<'/:foo/:bar'>>({ bar });
assert<RouteParams<'/:foo/:bar'>>({ foo, bar });
// @ts-expect-error
assert<RouteParams<'/:foo?/:bar'>>({ /*empty*/ });
assert<RouteParams<'/:foo?/:bar'>>({ foo, bar });
assert<RouteParams<'/:foo?/:bar'>>({ bar });
assert<RouteParams<'/:foo?/:bar?'>>({ /*empty*/ });

// @ts-expect-error
assert<RouteParams<'/foo/:bar/baz'>>({ /*empty*/ });
assert<RouteParams<'/foo/:bar/baz'>>({ bar });
assert<RouteParams<'/:foo/:bar/baz'>>({ foo, bar});
assert<RouteParams<'/:foo?/:bar/baz'>>({ foo, bar });
assert<RouteParams<'/:foo?/:bar/baz'>>({ bar });
assert<RouteParams<'/:foo?/:bar?/baz'>>({ /*empty*/ });
assert<RouteParams<'/:foo?/:bar?/baz'>>({ foo, bar });
assert<RouteParams<'/:foo?/:bar?/baz'>>({ bar });

// @ts-expect-error
assert<RouteParams<'/foo/baz/:bar'>>({ /*empty*/ });
assert<RouteParams<'/foo/baz/:bar'>>({ bar });
// @ts-expect-error
assert<RouteParams<'/:foo/baz/:bar'>>({ /*empty*/ });
// @ts-expect-error
assert<RouteParams<'/:foo/baz/:bar'>>({ foo });
assert<RouteParams<'/:foo/baz/:bar'>>({ foo, bar });
// @ts-expect-error
assert<RouteParams<'/:foo?/baz/:bar'>>({ /*empty*/ });
assert<RouteParams<'/:foo?/baz/:bar'>>({ foo, bar });
assert<RouteParams<'/:foo?/baz/:bar'>>({ bar });
// @ts-expect-error
assert<RouteParams<'/:foo?/baz/:bar?'>>({ hello: 'x' });
assert<RouteParams<'/:foo?/baz/:bar?'>>({ /*empty*/ });
assert<RouteParams<'/:foo?/baz/:bar?'>>({ foo, bar });
assert<RouteParams<'/:foo?/baz/:bar?'>>({ bar });

API.add('GET', '/foo', (req) => {
assert<{}>(req.params);
// @ts-expect-error
req.params.anything;
});

API.add('GET', '/foo/:bar', (req) => {
assert<{ bar: string }>(req.params);
assert<string>(req.params.bar);
// @ts-expect-error
req.params.hello;
});

API.add('GET', '/foo/:bar?/:baz', (req) => {
assert<{ bar?: string, baz: string }>(req.params);
assert<string|undefined>(req.params.bar);
assert<string>(req.params.baz);
// @ts-expect-error
assert<string>(req.params.bar);
// @ts-expect-error
req.params.hello;
});

API.add('GET', /^[/]foobar[/]?/, (req) => {
assert<{}>(req.params);
assert<Params>(req.params);
assert<string>(req.params.anything);
});


/**
* WORKTOP/CACHE
Expand Down

0 comments on commit b048388

Please sign in to comment.