-
Notifications
You must be signed in to change notification settings - Fork 0
/
ideas.ts
116 lines (105 loc) · 3.77 KB
/
ideas.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/**
* Here are some ideas for router and route. Handler is already set as an async
* indexed state monad.
*
* ```ts
* export type Handler<D, A, B> = (d: D) => Promise<[A, B]>;
* ```
*/
import type { Option } from "fun/option";
import type { Schema } from "fun/schemable";
import type { PathVars, RouteParser, RouteString } from "./parser.ts";
import type { Handler } from "./handler.ts";
import * as S from "fun/schemable";
/**
* Content is used to create parsers and definitions for requests or responses.
* All
*/
export interface Content<I> {
schema: Schema<I>;
}
/**
* Context is all of the things that are parsed out of the request
*/
export interface Context<S, P, I = never> {
request: Request;
state: S;
variables: P;
input: I;
}
declare const req: Request;
const a = req.blob;
/**
* A full route defines:
*
* * Path: A unique RouteString of the format `VERB /path/:with/vars/*`
* * Input: An extension like fun/schemable.ts#Schema that defines content-type
* and data shape. This `Schema` must be able to output json_schema and a
* decoder for the request body.
* * Output: Like input, this is a `Schema`-like that defines content-type and
* data shape. This is used to construct json_schema for openapi generation as
* well as typing the handler output.
* * Parser: A parser takes in the Path RouteString and returns a filterMap
* function that either parses the path in Request to `Some<PathVars<P>>` or
* `None` if the route doesn't match.
* * Handler: A handler is where the route work is done after path and body
* parsing are complete.
*
* The goal here is to have the kitchen sink of options for defining a route,
* but in actual use I expect to create a handful of combinators over route that
* simplify its use.
*/
export interface Route<S, P extends RouteString, I, O> {
readonly path: P; // The RouteString is unique for each route
readonly input: Schema<I>; // Probably use a more restricted schema here
readonly output: Schema<O>; // Probably use a more restricted schema here
readonly parser: RouteParser<PathVars<P>>; // Parser only types the path params
readonly handler: Handler<Context<S, PathVars<P>, I>, O, unknown>;
}
export function route<S, P extends RouteString, I, O>(
r: Route<S, P, I, O>,
): Route<S, P, I, O> {
return r;
}
/**
* Routes is a dictionary of Route values keyed by their path.
*/
// deno-lint-ignore no-explicit-any
export type Routes<S> = { [K: RouteString]: Route<S, RouteString, any, any> };
/**
* A RouterContext is used by the router when finding the route to use for a
* request.
*/
export interface RouterContext<S, I> {
readonly request: Request;
readonly state: S;
readonly info: I;
}
/**
* An abstract router implementation needs to:
*
* * Add routes and keep track at the type level of the routes that have been
* added to avoid conflicts.
* * Remove routes and keep track of it at the type level.
* * Find a route that matches a given RouterContext.
*
* This allows us to try out different router implementations depending on the
* application. For applications that require extremely fast route mapping we
* can implement a red/black tree. For applications that prefer a fallthrough
* design where many route definitions overlap and a specific order is needed we
* can implement an array.
*/
export interface Router<S, R extends Routes<S> = {}> {
readonly find: <P extends RouteString, I, O>(
ctx: RouterContext<S, Deno.ServeHandlerInfo>,
) => Option<Route<S, P, I, O>>;
readonly addRoute: <P extends RouteString, I, O>(
route: Route<S, Exclude<P, keyof R>, I, O>,
) => Router<
S,
{ [K in (keyof R) | P]: K extends keyof R ? R[K] : typeof route }
>;
readonly removeRoute: <P extends RouteString>(
path: P,
) => Router<S, { [K in Exclude<P, keyof R>]: R[K] }>;
}