fossyl 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +204 -0
- package/dist/index.d.ts +204 -0
- package/dist/index.js +103 -0
- package/dist/index.mjs +78 -0
- package/package.json +46 -0
- package/src/example.ts +31 -0
- package/src/index.ts +22 -0
- package/src/router/router.ts +277 -0
- package/src/router/tmp.sh +15 -0
- package/src/router/types/configuration.types.ts +143 -0
- package/src/router/types/params.types.ts +13 -0
- package/src/router/types/router-creation.types.ts +36 -0
- package/src/router/types/routes.types.ts +124 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
type ExtractUrlParams<Path extends string> = Path extends `${string}/:${infer Param}/${infer Rest}` ? Param | ExtractUrlParams<Rest> : Path extends `${string}/:${infer Param}` ? Param : never;
|
|
2
|
+
type Expand<T> = T extends infer O ? {
|
|
3
|
+
[K in keyof O]: O[K];
|
|
4
|
+
} : never;
|
|
5
|
+
type Params<Path extends string> = Expand<{
|
|
6
|
+
[K in ExtractUrlParams<Path>]: string;
|
|
7
|
+
}>;
|
|
8
|
+
|
|
9
|
+
declare const authBrand: unique symbol;
|
|
10
|
+
type Authentication = {
|
|
11
|
+
readonly [authBrand]: "Auth";
|
|
12
|
+
};
|
|
13
|
+
declare function authWrapper<T>(auth: T): T & Authentication;
|
|
14
|
+
type RestMethod = "GET" | "POST" | "PUT" | "DELETE";
|
|
15
|
+
type OpenRoute<Path extends string, Method extends RestMethod, Res extends unknown, Query extends unknown | undefined = undefined> = {
|
|
16
|
+
type: "open";
|
|
17
|
+
path: Path;
|
|
18
|
+
method: Method;
|
|
19
|
+
validator?: never;
|
|
20
|
+
authenticator?: never;
|
|
21
|
+
handler: (params: {
|
|
22
|
+
url: Params<Path>;
|
|
23
|
+
query: Query;
|
|
24
|
+
}) => Promise<Res>;
|
|
25
|
+
};
|
|
26
|
+
type AuthenticatedRoute<Path extends string, Method extends RestMethod, Res extends unknown, Auth extends Authentication, Query extends unknown | undefined = undefined> = {
|
|
27
|
+
type: "authenticated";
|
|
28
|
+
path: Path;
|
|
29
|
+
method: Method;
|
|
30
|
+
validator?: never;
|
|
31
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
32
|
+
handler: (params: {
|
|
33
|
+
url: Params<Path>;
|
|
34
|
+
query: Query;
|
|
35
|
+
}, auth: Auth) => Promise<Res>;
|
|
36
|
+
};
|
|
37
|
+
type ValidatedRoute<Path extends string, Method extends RestMethod, Res extends unknown, RequestBody extends unknown, Query extends unknown | undefined = undefined> = {
|
|
38
|
+
type: "validated";
|
|
39
|
+
path: Path;
|
|
40
|
+
method: Method;
|
|
41
|
+
validator: ValidatorFunction<RequestBody>;
|
|
42
|
+
authenticator?: never;
|
|
43
|
+
handler: (params: {
|
|
44
|
+
url: Params<Path>;
|
|
45
|
+
query: Query;
|
|
46
|
+
}, body: RequestBody) => Promise<Res>;
|
|
47
|
+
};
|
|
48
|
+
type FullRoute<Path extends string, Method extends RestMethod, Res extends unknown, RequestBody extends unknown, Auth extends Authentication, Query extends unknown | undefined = undefined> = {
|
|
49
|
+
type: "full";
|
|
50
|
+
path: Path;
|
|
51
|
+
method: Method;
|
|
52
|
+
validator: ValidatorFunction<RequestBody>;
|
|
53
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
54
|
+
handler: Query extends undefined ? (params: {
|
|
55
|
+
url: Params<Path>;
|
|
56
|
+
}, auth: Authentication, body: RequestBody) => Promise<Res> : (params: {
|
|
57
|
+
url: Params<Path>;
|
|
58
|
+
query: Query;
|
|
59
|
+
}, auth: Authentication, body: RequestBody) => Promise<Res>;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
type ValidatorFunction<T extends unknown = unknown> = (data: unknown) => T;
|
|
63
|
+
type AuthenticationFunction<T extends Authentication> = (headers: Record<string, string>) => T;
|
|
64
|
+
/**
|
|
65
|
+
* This is the most confusing part of the type system, but also key.
|
|
66
|
+
* This type is used to force type inferenece when creating your endpoints.
|
|
67
|
+
* This is the structure to provide `function overload`s. This is so you can have the same function
|
|
68
|
+
* (in our case get,put,post,delete) but allow differnt configurations.
|
|
69
|
+
*
|
|
70
|
+
* These configurations need to be strongly typed force the developers into using the tools.
|
|
71
|
+
*
|
|
72
|
+
* Want to allow Auth? - Gotta pass in authenticator and the type system handles it
|
|
73
|
+
*
|
|
74
|
+
* Also, this is where the core of the Zod system is. We force the body to be a zod validated input.
|
|
75
|
+
* Luckily, this is pretty easy to replace.
|
|
76
|
+
* PART of me wanted to further abstract it such that you needed a translation function but
|
|
77
|
+
* that seemed overengineerd
|
|
78
|
+
*
|
|
79
|
+
*/
|
|
80
|
+
/**
|
|
81
|
+
* Configuration type for POST/PUT/DELETE endpoints.
|
|
82
|
+
* These methods require a request body, so they always need a validator.
|
|
83
|
+
*
|
|
84
|
+
* Four variants (with/without auth × with/without query):
|
|
85
|
+
* 1. Validated only, no query: validator + handler(params, body)
|
|
86
|
+
* 2. Validated only, with query: validator + queryValidator + handler(params, body)
|
|
87
|
+
* 3. Full (auth + validated), no query: authenticator + validator + handler(params, auth, body)
|
|
88
|
+
* 4. Full (auth + validated), with query: authenticator + validator + queryValidator + handler(params, auth, body)
|
|
89
|
+
*/
|
|
90
|
+
type EndpointCreationFunction<Path extends string, Method extends RestMethod> = {
|
|
91
|
+
<Res extends unknown, RequestBody extends unknown>(config: {
|
|
92
|
+
authenticator?: never;
|
|
93
|
+
validator: ValidatorFunction<RequestBody>;
|
|
94
|
+
queryValidator?: never;
|
|
95
|
+
handler: (params: {
|
|
96
|
+
url: Params<Path>;
|
|
97
|
+
}, body: RequestBody) => Promise<Res>;
|
|
98
|
+
}): ValidatedRoute<Path, Method, Res, RequestBody, undefined>;
|
|
99
|
+
<Res extends unknown, RequestBody extends unknown, Query extends unknown>(config: {
|
|
100
|
+
authenticator?: never;
|
|
101
|
+
validator: ValidatorFunction<RequestBody>;
|
|
102
|
+
queryValidator: ValidatorFunction<Query>;
|
|
103
|
+
handler: (params: {
|
|
104
|
+
url: Params<Path>;
|
|
105
|
+
query: Query;
|
|
106
|
+
}, body: RequestBody) => Promise<Res>;
|
|
107
|
+
}): ValidatedRoute<Path, Method, Res, RequestBody, Query>;
|
|
108
|
+
<Res extends unknown, RequestBody extends unknown, Auth extends Authentication>(config: {
|
|
109
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
110
|
+
validator: ValidatorFunction<RequestBody>;
|
|
111
|
+
queryValidator?: never;
|
|
112
|
+
handler: (params: {
|
|
113
|
+
url: Params<Path>;
|
|
114
|
+
}, auth: Auth, body: RequestBody) => Promise<Res>;
|
|
115
|
+
}): FullRoute<Path, Method, Res, RequestBody, Auth, undefined>;
|
|
116
|
+
<Res extends unknown, RequestBody extends unknown, Auth extends Authentication, Query extends unknown>(config: {
|
|
117
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
118
|
+
validator: ValidatorFunction<RequestBody>;
|
|
119
|
+
queryValidator: ValidatorFunction<Query>;
|
|
120
|
+
handler: (params: {
|
|
121
|
+
url: Params<Path>;
|
|
122
|
+
query: Query;
|
|
123
|
+
}, auth: Auth, body: RequestBody) => Promise<Res>;
|
|
124
|
+
}): FullRoute<Path, Method, Res, RequestBody, Auth, Query>;
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Configuration type for GET/DELETE endpoints.
|
|
128
|
+
* These methods cannot have a request body, so no validator.
|
|
129
|
+
*
|
|
130
|
+
* Four variants (with/without auth × with/without query):
|
|
131
|
+
* 1. Open, no query: just handler(params)
|
|
132
|
+
* 2. Open, with query: queryValidator + handler(params)
|
|
133
|
+
* 3. Authenticated, no query: authenticator + handler(params, auth)
|
|
134
|
+
* 4. Authenticated, with query: authenticator + queryValidator + handler(params, auth)
|
|
135
|
+
*/
|
|
136
|
+
type GetEndpointCreationFunction<Path extends string, Method extends RestMethod> = {
|
|
137
|
+
<Res extends unknown>(config: {
|
|
138
|
+
authenticator?: never;
|
|
139
|
+
queryValidator?: never;
|
|
140
|
+
handler: (params: {
|
|
141
|
+
url: Params<Path>;
|
|
142
|
+
}) => Promise<Res>;
|
|
143
|
+
}): OpenRoute<Path, Method, Res, undefined>;
|
|
144
|
+
<Res extends unknown, Query extends unknown>(config: {
|
|
145
|
+
authenticator?: never;
|
|
146
|
+
queryValidator: ValidatorFunction<Query>;
|
|
147
|
+
handler: (params: {
|
|
148
|
+
url: Params<Path>;
|
|
149
|
+
query: Query;
|
|
150
|
+
}) => Promise<Res>;
|
|
151
|
+
}): OpenRoute<Path, Method, Res, Query>;
|
|
152
|
+
<Res extends unknown, Auth extends Authentication>(config: {
|
|
153
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
154
|
+
queryValidator?: never;
|
|
155
|
+
handler: (params: {
|
|
156
|
+
url: Params<Path>;
|
|
157
|
+
}, auth: Auth) => Promise<Res>;
|
|
158
|
+
}): AuthenticatedRoute<Path, Method, Res, Auth, undefined>;
|
|
159
|
+
<Res extends unknown, Auth extends Authentication, Query extends unknown>(config: {
|
|
160
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
161
|
+
queryValidator: ValidatorFunction<Query>;
|
|
162
|
+
handler: (params: {
|
|
163
|
+
url: Params<Path>;
|
|
164
|
+
query: Query;
|
|
165
|
+
}, auth: Auth) => Promise<Res>;
|
|
166
|
+
}): AuthenticatedRoute<Path, Method, Res, Auth, Query>;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
type Endpoint<Path extends string> = {
|
|
170
|
+
get: GetEndpointCreationFunction<Path, "GET">;
|
|
171
|
+
post: EndpointCreationFunction<Path, "POST">;
|
|
172
|
+
put: EndpointCreationFunction<Path, "PUT">;
|
|
173
|
+
delete: GetEndpointCreationFunction<Path, "DELETE">;
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* Creates a router that ensures all endpoints extend the route.
|
|
177
|
+
* IDK if this is actually useful, but I kinda like it.
|
|
178
|
+
*
|
|
179
|
+
* There def was a part of me that thought about pure generator functions.
|
|
180
|
+
* In fact... they're provided!
|
|
181
|
+
*
|
|
182
|
+
* We can discuss that.
|
|
183
|
+
*/
|
|
184
|
+
type Router<BasePath extends string> = {
|
|
185
|
+
/**
|
|
186
|
+
* Creates an endpoint
|
|
187
|
+
* @param path string, must extend Router's string
|
|
188
|
+
*/
|
|
189
|
+
createEndpoint: <Path extends `${BasePath}${string}`>(path: Path) => Endpoint<Path>;
|
|
190
|
+
/**
|
|
191
|
+
* Creates a subrouter
|
|
192
|
+
* @param path string, must extend Router's string
|
|
193
|
+
*/
|
|
194
|
+
createSubrouter: <Path extends `${BasePath}${string}`>(path: Path) => Router<Path>;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Provides an Ednpoint creater. Would maybe like some other internal stuff? Lowkey... I think we
|
|
199
|
+
* all might just end up using the helper functions of get and del and such.
|
|
200
|
+
* @param _ - this param is still needed because the type is determined at compile time.
|
|
201
|
+
*/
|
|
202
|
+
declare function createRouter<BasePath extends string>(_: BasePath): Router<BasePath>;
|
|
203
|
+
|
|
204
|
+
export { type AuthenticatedRoute, type Authentication, type AuthenticationFunction, type Endpoint, type FullRoute, type OpenRoute, type RestMethod, type Router, type ValidatedRoute, type ValidatorFunction, authWrapper, createRouter };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
type ExtractUrlParams<Path extends string> = Path extends `${string}/:${infer Param}/${infer Rest}` ? Param | ExtractUrlParams<Rest> : Path extends `${string}/:${infer Param}` ? Param : never;
|
|
2
|
+
type Expand<T> = T extends infer O ? {
|
|
3
|
+
[K in keyof O]: O[K];
|
|
4
|
+
} : never;
|
|
5
|
+
type Params<Path extends string> = Expand<{
|
|
6
|
+
[K in ExtractUrlParams<Path>]: string;
|
|
7
|
+
}>;
|
|
8
|
+
|
|
9
|
+
declare const authBrand: unique symbol;
|
|
10
|
+
type Authentication = {
|
|
11
|
+
readonly [authBrand]: "Auth";
|
|
12
|
+
};
|
|
13
|
+
declare function authWrapper<T>(auth: T): T & Authentication;
|
|
14
|
+
type RestMethod = "GET" | "POST" | "PUT" | "DELETE";
|
|
15
|
+
type OpenRoute<Path extends string, Method extends RestMethod, Res extends unknown, Query extends unknown | undefined = undefined> = {
|
|
16
|
+
type: "open";
|
|
17
|
+
path: Path;
|
|
18
|
+
method: Method;
|
|
19
|
+
validator?: never;
|
|
20
|
+
authenticator?: never;
|
|
21
|
+
handler: (params: {
|
|
22
|
+
url: Params<Path>;
|
|
23
|
+
query: Query;
|
|
24
|
+
}) => Promise<Res>;
|
|
25
|
+
};
|
|
26
|
+
type AuthenticatedRoute<Path extends string, Method extends RestMethod, Res extends unknown, Auth extends Authentication, Query extends unknown | undefined = undefined> = {
|
|
27
|
+
type: "authenticated";
|
|
28
|
+
path: Path;
|
|
29
|
+
method: Method;
|
|
30
|
+
validator?: never;
|
|
31
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
32
|
+
handler: (params: {
|
|
33
|
+
url: Params<Path>;
|
|
34
|
+
query: Query;
|
|
35
|
+
}, auth: Auth) => Promise<Res>;
|
|
36
|
+
};
|
|
37
|
+
type ValidatedRoute<Path extends string, Method extends RestMethod, Res extends unknown, RequestBody extends unknown, Query extends unknown | undefined = undefined> = {
|
|
38
|
+
type: "validated";
|
|
39
|
+
path: Path;
|
|
40
|
+
method: Method;
|
|
41
|
+
validator: ValidatorFunction<RequestBody>;
|
|
42
|
+
authenticator?: never;
|
|
43
|
+
handler: (params: {
|
|
44
|
+
url: Params<Path>;
|
|
45
|
+
query: Query;
|
|
46
|
+
}, body: RequestBody) => Promise<Res>;
|
|
47
|
+
};
|
|
48
|
+
type FullRoute<Path extends string, Method extends RestMethod, Res extends unknown, RequestBody extends unknown, Auth extends Authentication, Query extends unknown | undefined = undefined> = {
|
|
49
|
+
type: "full";
|
|
50
|
+
path: Path;
|
|
51
|
+
method: Method;
|
|
52
|
+
validator: ValidatorFunction<RequestBody>;
|
|
53
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
54
|
+
handler: Query extends undefined ? (params: {
|
|
55
|
+
url: Params<Path>;
|
|
56
|
+
}, auth: Authentication, body: RequestBody) => Promise<Res> : (params: {
|
|
57
|
+
url: Params<Path>;
|
|
58
|
+
query: Query;
|
|
59
|
+
}, auth: Authentication, body: RequestBody) => Promise<Res>;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
type ValidatorFunction<T extends unknown = unknown> = (data: unknown) => T;
|
|
63
|
+
type AuthenticationFunction<T extends Authentication> = (headers: Record<string, string>) => T;
|
|
64
|
+
/**
|
|
65
|
+
* This is the most confusing part of the type system, but also key.
|
|
66
|
+
* This type is used to force type inferenece when creating your endpoints.
|
|
67
|
+
* This is the structure to provide `function overload`s. This is so you can have the same function
|
|
68
|
+
* (in our case get,put,post,delete) but allow differnt configurations.
|
|
69
|
+
*
|
|
70
|
+
* These configurations need to be strongly typed force the developers into using the tools.
|
|
71
|
+
*
|
|
72
|
+
* Want to allow Auth? - Gotta pass in authenticator and the type system handles it
|
|
73
|
+
*
|
|
74
|
+
* Also, this is where the core of the Zod system is. We force the body to be a zod validated input.
|
|
75
|
+
* Luckily, this is pretty easy to replace.
|
|
76
|
+
* PART of me wanted to further abstract it such that you needed a translation function but
|
|
77
|
+
* that seemed overengineerd
|
|
78
|
+
*
|
|
79
|
+
*/
|
|
80
|
+
/**
|
|
81
|
+
* Configuration type for POST/PUT/DELETE endpoints.
|
|
82
|
+
* These methods require a request body, so they always need a validator.
|
|
83
|
+
*
|
|
84
|
+
* Four variants (with/without auth × with/without query):
|
|
85
|
+
* 1. Validated only, no query: validator + handler(params, body)
|
|
86
|
+
* 2. Validated only, with query: validator + queryValidator + handler(params, body)
|
|
87
|
+
* 3. Full (auth + validated), no query: authenticator + validator + handler(params, auth, body)
|
|
88
|
+
* 4. Full (auth + validated), with query: authenticator + validator + queryValidator + handler(params, auth, body)
|
|
89
|
+
*/
|
|
90
|
+
type EndpointCreationFunction<Path extends string, Method extends RestMethod> = {
|
|
91
|
+
<Res extends unknown, RequestBody extends unknown>(config: {
|
|
92
|
+
authenticator?: never;
|
|
93
|
+
validator: ValidatorFunction<RequestBody>;
|
|
94
|
+
queryValidator?: never;
|
|
95
|
+
handler: (params: {
|
|
96
|
+
url: Params<Path>;
|
|
97
|
+
}, body: RequestBody) => Promise<Res>;
|
|
98
|
+
}): ValidatedRoute<Path, Method, Res, RequestBody, undefined>;
|
|
99
|
+
<Res extends unknown, RequestBody extends unknown, Query extends unknown>(config: {
|
|
100
|
+
authenticator?: never;
|
|
101
|
+
validator: ValidatorFunction<RequestBody>;
|
|
102
|
+
queryValidator: ValidatorFunction<Query>;
|
|
103
|
+
handler: (params: {
|
|
104
|
+
url: Params<Path>;
|
|
105
|
+
query: Query;
|
|
106
|
+
}, body: RequestBody) => Promise<Res>;
|
|
107
|
+
}): ValidatedRoute<Path, Method, Res, RequestBody, Query>;
|
|
108
|
+
<Res extends unknown, RequestBody extends unknown, Auth extends Authentication>(config: {
|
|
109
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
110
|
+
validator: ValidatorFunction<RequestBody>;
|
|
111
|
+
queryValidator?: never;
|
|
112
|
+
handler: (params: {
|
|
113
|
+
url: Params<Path>;
|
|
114
|
+
}, auth: Auth, body: RequestBody) => Promise<Res>;
|
|
115
|
+
}): FullRoute<Path, Method, Res, RequestBody, Auth, undefined>;
|
|
116
|
+
<Res extends unknown, RequestBody extends unknown, Auth extends Authentication, Query extends unknown>(config: {
|
|
117
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
118
|
+
validator: ValidatorFunction<RequestBody>;
|
|
119
|
+
queryValidator: ValidatorFunction<Query>;
|
|
120
|
+
handler: (params: {
|
|
121
|
+
url: Params<Path>;
|
|
122
|
+
query: Query;
|
|
123
|
+
}, auth: Auth, body: RequestBody) => Promise<Res>;
|
|
124
|
+
}): FullRoute<Path, Method, Res, RequestBody, Auth, Query>;
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Configuration type for GET/DELETE endpoints.
|
|
128
|
+
* These methods cannot have a request body, so no validator.
|
|
129
|
+
*
|
|
130
|
+
* Four variants (with/without auth × with/without query):
|
|
131
|
+
* 1. Open, no query: just handler(params)
|
|
132
|
+
* 2. Open, with query: queryValidator + handler(params)
|
|
133
|
+
* 3. Authenticated, no query: authenticator + handler(params, auth)
|
|
134
|
+
* 4. Authenticated, with query: authenticator + queryValidator + handler(params, auth)
|
|
135
|
+
*/
|
|
136
|
+
type GetEndpointCreationFunction<Path extends string, Method extends RestMethod> = {
|
|
137
|
+
<Res extends unknown>(config: {
|
|
138
|
+
authenticator?: never;
|
|
139
|
+
queryValidator?: never;
|
|
140
|
+
handler: (params: {
|
|
141
|
+
url: Params<Path>;
|
|
142
|
+
}) => Promise<Res>;
|
|
143
|
+
}): OpenRoute<Path, Method, Res, undefined>;
|
|
144
|
+
<Res extends unknown, Query extends unknown>(config: {
|
|
145
|
+
authenticator?: never;
|
|
146
|
+
queryValidator: ValidatorFunction<Query>;
|
|
147
|
+
handler: (params: {
|
|
148
|
+
url: Params<Path>;
|
|
149
|
+
query: Query;
|
|
150
|
+
}) => Promise<Res>;
|
|
151
|
+
}): OpenRoute<Path, Method, Res, Query>;
|
|
152
|
+
<Res extends unknown, Auth extends Authentication>(config: {
|
|
153
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
154
|
+
queryValidator?: never;
|
|
155
|
+
handler: (params: {
|
|
156
|
+
url: Params<Path>;
|
|
157
|
+
}, auth: Auth) => Promise<Res>;
|
|
158
|
+
}): AuthenticatedRoute<Path, Method, Res, Auth, undefined>;
|
|
159
|
+
<Res extends unknown, Auth extends Authentication, Query extends unknown>(config: {
|
|
160
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
161
|
+
queryValidator: ValidatorFunction<Query>;
|
|
162
|
+
handler: (params: {
|
|
163
|
+
url: Params<Path>;
|
|
164
|
+
query: Query;
|
|
165
|
+
}, auth: Auth) => Promise<Res>;
|
|
166
|
+
}): AuthenticatedRoute<Path, Method, Res, Auth, Query>;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
type Endpoint<Path extends string> = {
|
|
170
|
+
get: GetEndpointCreationFunction<Path, "GET">;
|
|
171
|
+
post: EndpointCreationFunction<Path, "POST">;
|
|
172
|
+
put: EndpointCreationFunction<Path, "PUT">;
|
|
173
|
+
delete: GetEndpointCreationFunction<Path, "DELETE">;
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* Creates a router that ensures all endpoints extend the route.
|
|
177
|
+
* IDK if this is actually useful, but I kinda like it.
|
|
178
|
+
*
|
|
179
|
+
* There def was a part of me that thought about pure generator functions.
|
|
180
|
+
* In fact... they're provided!
|
|
181
|
+
*
|
|
182
|
+
* We can discuss that.
|
|
183
|
+
*/
|
|
184
|
+
type Router<BasePath extends string> = {
|
|
185
|
+
/**
|
|
186
|
+
* Creates an endpoint
|
|
187
|
+
* @param path string, must extend Router's string
|
|
188
|
+
*/
|
|
189
|
+
createEndpoint: <Path extends `${BasePath}${string}`>(path: Path) => Endpoint<Path>;
|
|
190
|
+
/**
|
|
191
|
+
* Creates a subrouter
|
|
192
|
+
* @param path string, must extend Router's string
|
|
193
|
+
*/
|
|
194
|
+
createSubrouter: <Path extends `${BasePath}${string}`>(path: Path) => Router<Path>;
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Provides an Ednpoint creater. Would maybe like some other internal stuff? Lowkey... I think we
|
|
199
|
+
* all might just end up using the helper functions of get and del and such.
|
|
200
|
+
* @param _ - this param is still needed because the type is determined at compile time.
|
|
201
|
+
*/
|
|
202
|
+
declare function createRouter<BasePath extends string>(_: BasePath): Router<BasePath>;
|
|
203
|
+
|
|
204
|
+
export { type AuthenticatedRoute, type Authentication, type AuthenticationFunction, type Endpoint, type FullRoute, type OpenRoute, type RestMethod, type Router, type ValidatedRoute, type ValidatorFunction, authWrapper, createRouter };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defProps = Object.defineProperties;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
10
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
11
|
+
var __spreadValues = (a, b) => {
|
|
12
|
+
for (var prop in b || (b = {}))
|
|
13
|
+
if (__hasOwnProp.call(b, prop))
|
|
14
|
+
__defNormalProp(a, prop, b[prop]);
|
|
15
|
+
if (__getOwnPropSymbols)
|
|
16
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
17
|
+
if (__propIsEnum.call(b, prop))
|
|
18
|
+
__defNormalProp(a, prop, b[prop]);
|
|
19
|
+
}
|
|
20
|
+
return a;
|
|
21
|
+
};
|
|
22
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
23
|
+
var __export = (target, all) => {
|
|
24
|
+
for (var name in all)
|
|
25
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
26
|
+
};
|
|
27
|
+
var __copyProps = (to, from, except, desc) => {
|
|
28
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
29
|
+
for (let key of __getOwnPropNames(from))
|
|
30
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
31
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
32
|
+
}
|
|
33
|
+
return to;
|
|
34
|
+
};
|
|
35
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
36
|
+
|
|
37
|
+
// src/index.ts
|
|
38
|
+
var index_exports = {};
|
|
39
|
+
__export(index_exports, {
|
|
40
|
+
authWrapper: () => authWrapper,
|
|
41
|
+
createRouter: () => createRouter
|
|
42
|
+
});
|
|
43
|
+
module.exports = __toCommonJS(index_exports);
|
|
44
|
+
|
|
45
|
+
// src/router/router.ts
|
|
46
|
+
function createEndpoint(path) {
|
|
47
|
+
function get(config) {
|
|
48
|
+
if ("authenticator" in config) {
|
|
49
|
+
return __spreadProps(__spreadValues({}, config), { type: "authenticated", path, method: "GET" });
|
|
50
|
+
} else {
|
|
51
|
+
return __spreadProps(__spreadValues({}, config), { type: "open", path, method: "GET" });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function put(config) {
|
|
55
|
+
if ("authenticator" in config) {
|
|
56
|
+
return __spreadProps(__spreadValues({}, config), { type: "full", path, method: "PUT" });
|
|
57
|
+
} else {
|
|
58
|
+
return __spreadProps(__spreadValues({}, config), { type: "validated", path, method: "PUT" });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function post(config) {
|
|
62
|
+
if ("authenticator" in config) {
|
|
63
|
+
return __spreadProps(__spreadValues({}, config), { type: "full", path, method: "POST" });
|
|
64
|
+
} else {
|
|
65
|
+
return __spreadProps(__spreadValues({}, config), { type: "validated", path, method: "POST" });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function del(config) {
|
|
69
|
+
if ("authenticator" in config) {
|
|
70
|
+
return __spreadProps(__spreadValues({}, config), {
|
|
71
|
+
type: "authenticated",
|
|
72
|
+
path,
|
|
73
|
+
method: "DELETE"
|
|
74
|
+
});
|
|
75
|
+
} else {
|
|
76
|
+
return __spreadProps(__spreadValues({}, config), { type: "open", path, method: "DELETE" });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
get,
|
|
81
|
+
post,
|
|
82
|
+
put,
|
|
83
|
+
delete: del
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function createRouter(_) {
|
|
87
|
+
return {
|
|
88
|
+
createEndpoint: (path) => createEndpoint(path),
|
|
89
|
+
createSubrouter: (path) => createRouter(path)
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/router/types/routes.types.ts
|
|
94
|
+
function authWrapper(auth) {
|
|
95
|
+
return __spreadProps(__spreadValues({}, auth), {
|
|
96
|
+
[authBrand]: "Auth"
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
100
|
+
0 && (module.exports = {
|
|
101
|
+
authWrapper,
|
|
102
|
+
createRouter
|
|
103
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defProps = Object.defineProperties;
|
|
3
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __spreadValues = (a, b) => {
|
|
9
|
+
for (var prop in b || (b = {}))
|
|
10
|
+
if (__hasOwnProp.call(b, prop))
|
|
11
|
+
__defNormalProp(a, prop, b[prop]);
|
|
12
|
+
if (__getOwnPropSymbols)
|
|
13
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
+
if (__propIsEnum.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
}
|
|
17
|
+
return a;
|
|
18
|
+
};
|
|
19
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
+
|
|
21
|
+
// src/router/router.ts
|
|
22
|
+
function createEndpoint(path) {
|
|
23
|
+
function get(config) {
|
|
24
|
+
if ("authenticator" in config) {
|
|
25
|
+
return __spreadProps(__spreadValues({}, config), { type: "authenticated", path, method: "GET" });
|
|
26
|
+
} else {
|
|
27
|
+
return __spreadProps(__spreadValues({}, config), { type: "open", path, method: "GET" });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function put(config) {
|
|
31
|
+
if ("authenticator" in config) {
|
|
32
|
+
return __spreadProps(__spreadValues({}, config), { type: "full", path, method: "PUT" });
|
|
33
|
+
} else {
|
|
34
|
+
return __spreadProps(__spreadValues({}, config), { type: "validated", path, method: "PUT" });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function post(config) {
|
|
38
|
+
if ("authenticator" in config) {
|
|
39
|
+
return __spreadProps(__spreadValues({}, config), { type: "full", path, method: "POST" });
|
|
40
|
+
} else {
|
|
41
|
+
return __spreadProps(__spreadValues({}, config), { type: "validated", path, method: "POST" });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function del(config) {
|
|
45
|
+
if ("authenticator" in config) {
|
|
46
|
+
return __spreadProps(__spreadValues({}, config), {
|
|
47
|
+
type: "authenticated",
|
|
48
|
+
path,
|
|
49
|
+
method: "DELETE"
|
|
50
|
+
});
|
|
51
|
+
} else {
|
|
52
|
+
return __spreadProps(__spreadValues({}, config), { type: "open", path, method: "DELETE" });
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
get,
|
|
57
|
+
post,
|
|
58
|
+
put,
|
|
59
|
+
delete: del
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function createRouter(_) {
|
|
63
|
+
return {
|
|
64
|
+
createEndpoint: (path) => createEndpoint(path),
|
|
65
|
+
createSubrouter: (path) => createRouter(path)
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/router/types/routes.types.ts
|
|
70
|
+
function authWrapper(auth) {
|
|
71
|
+
return __spreadProps(__spreadValues({}, auth), {
|
|
72
|
+
[authBrand]: "Auth"
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
export {
|
|
76
|
+
authWrapper,
|
|
77
|
+
createRouter
|
|
78
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fossyl",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Type-safe REST API framework core - designed for AI-assisted development",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": "./dist/index.mjs",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"src",
|
|
18
|
+
"CLAUDE.md",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"keywords": [
|
|
22
|
+
"typescript",
|
|
23
|
+
"rest-api",
|
|
24
|
+
"type-safe",
|
|
25
|
+
"router",
|
|
26
|
+
"framework",
|
|
27
|
+
"ai-assisted",
|
|
28
|
+
"functional"
|
|
29
|
+
],
|
|
30
|
+
"author": "YoyoSaur",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"tsup": "^8.0.0",
|
|
38
|
+
"typescript": "^5.8.3"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
42
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
43
|
+
"clean": "rm -rf dist",
|
|
44
|
+
"typecheck": "tsc --noEmit"
|
|
45
|
+
}
|
|
46
|
+
}
|
package/src/example.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { createRouter } from "./router/router";
|
|
2
|
+
import { authWrapper } from "./router/types/routes.types";
|
|
3
|
+
|
|
4
|
+
const authenticationMiddleware = (headers: Record<string, string>) => {
|
|
5
|
+
return authWrapper({
|
|
6
|
+
status: headers.authorization,
|
|
7
|
+
});
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const baseRouter = createRouter("/status");
|
|
11
|
+
const endpoint = baseRouter.createEndpoint("/status");
|
|
12
|
+
|
|
13
|
+
const getter = endpoint.get({
|
|
14
|
+
authenticator: authenticationMiddleware,
|
|
15
|
+
handler: async (params, auth) => {
|
|
16
|
+
return {
|
|
17
|
+
status: "ok",
|
|
18
|
+
};
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const poster = endpoint.post({
|
|
23
|
+
validator: (): { a: string } => ({ a: "hello" }),
|
|
24
|
+
queryValidator: (): {b: string} => ({b: "OKAY"}),
|
|
25
|
+
handler: async (params, body) => {
|
|
26
|
+
return {
|
|
27
|
+
status: "ok",
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Router creation
|
|
2
|
+
export { createRouter } from "./router/router";
|
|
3
|
+
|
|
4
|
+
// Type exports
|
|
5
|
+
export type {
|
|
6
|
+
Authentication,
|
|
7
|
+
OpenRoute,
|
|
8
|
+
AuthenticatedRoute,
|
|
9
|
+
ValidatedRoute,
|
|
10
|
+
FullRoute,
|
|
11
|
+
RestMethod,
|
|
12
|
+
} from "./router/types/routes.types";
|
|
13
|
+
|
|
14
|
+
export type {
|
|
15
|
+
ValidatorFunction,
|
|
16
|
+
AuthenticationFunction,
|
|
17
|
+
} from "./router/types/configuration.types";
|
|
18
|
+
|
|
19
|
+
export type { Endpoint, Router } from "./router/types/router-creation.types";
|
|
20
|
+
|
|
21
|
+
// Utility exports
|
|
22
|
+
export { authWrapper } from "./router/types/routes.types";
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Authentication,
|
|
3
|
+
FullRoute,
|
|
4
|
+
ValidatedRoute,
|
|
5
|
+
AuthenticatedRoute,
|
|
6
|
+
OpenRoute,
|
|
7
|
+
} from "./types/routes.types";
|
|
8
|
+
import { Endpoint, Router } from "./types/router-creation.types";
|
|
9
|
+
import { AuthenticationFunction, ValidatorFunction } from "./types/configuration.types";
|
|
10
|
+
import { Params } from "./types/params.types";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Creates an endpoint which can be used to create final routes from.
|
|
14
|
+
* Routes must be fully qualified. This is provided as a helper function
|
|
15
|
+
* @param path
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// Function overloads in TypeScript are only allowed for functions, not for object properties.
|
|
19
|
+
// When you try to declare multiple properties with the same name (like `get`) in an object literal,
|
|
20
|
+
// you get a syntax error: "An object literal cannot have multiple properties with the same name."
|
|
21
|
+
// Additionally, the type signatures for overloaded methods must be declared as overloads on a function, not as separate properties.
|
|
22
|
+
// The correct way is to define a single function for `get` that matches the overload signatures, and then assign it as the property.
|
|
23
|
+
//
|
|
24
|
+
|
|
25
|
+
function createEndpoint<Path extends string>(path: Path): Endpoint<Path> {
|
|
26
|
+
// Open route: no auth, no query
|
|
27
|
+
function get<Res extends unknown>(config: {
|
|
28
|
+
authenticator?: never;
|
|
29
|
+
queryValidator?: never;
|
|
30
|
+
handler: (params: { url: Params<Path> }) => Promise<Res>;
|
|
31
|
+
}): OpenRoute<Path, "GET", Res, undefined>;
|
|
32
|
+
|
|
33
|
+
// Open route: no auth, with query
|
|
34
|
+
function get<Res extends unknown, Query extends unknown>(config: {
|
|
35
|
+
authenticator?: never;
|
|
36
|
+
queryValidator: ValidatorFunction<Query>;
|
|
37
|
+
handler: (params: { url: Params<Path>; query: Query }) => Promise<Res>;
|
|
38
|
+
}): OpenRoute<Path, "GET", Res, Query>;
|
|
39
|
+
|
|
40
|
+
// Authenticated route: auth, no query
|
|
41
|
+
function get<Res extends unknown, Auth extends Authentication>(config: {
|
|
42
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
43
|
+
queryValidator?: never;
|
|
44
|
+
handler: (params: { url: Params<Path> }, auth: Auth) => Promise<Res>;
|
|
45
|
+
}): AuthenticatedRoute<Path, "GET", Res, Auth, undefined>;
|
|
46
|
+
|
|
47
|
+
// Authenticated route: auth, with query
|
|
48
|
+
function get<Res extends unknown, Auth extends Authentication, Query extends unknown>(config: {
|
|
49
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
50
|
+
queryValidator: ValidatorFunction<Query>;
|
|
51
|
+
handler: (params: { url: Params<Path>; query: Query }, auth: Auth) => Promise<Res>;
|
|
52
|
+
}): AuthenticatedRoute<Path, "GET", Res, Auth, Query>;
|
|
53
|
+
|
|
54
|
+
function get(
|
|
55
|
+
config:
|
|
56
|
+
| { handler: (params: { url: Params<Path> }) => Promise<any> }
|
|
57
|
+
| { queryValidator: ValidatorFunction<any>; handler: (params: { url: Params<Path>; query: any }) => Promise<any> }
|
|
58
|
+
| { authenticator: AuthenticationFunction<any>; handler: (params: { url: Params<Path> }, auth: any) => Promise<any> }
|
|
59
|
+
| { authenticator: AuthenticationFunction<any>; queryValidator: ValidatorFunction<any>; handler: (params: { url: Params<Path>; query: any }, auth: any) => Promise<any> }
|
|
60
|
+
): OpenRoute<Path, "GET", any, any> | AuthenticatedRoute<Path, "GET", any, any, any> {
|
|
61
|
+
if ("authenticator" in config) {
|
|
62
|
+
return { ...config, type: "authenticated", path, method: "GET" } satisfies AuthenticatedRoute<
|
|
63
|
+
Path,
|
|
64
|
+
"GET",
|
|
65
|
+
any,
|
|
66
|
+
any,
|
|
67
|
+
any
|
|
68
|
+
>;
|
|
69
|
+
} else {
|
|
70
|
+
return { ...config, type: "open", path, method: "GET" } satisfies OpenRoute<
|
|
71
|
+
Path,
|
|
72
|
+
"GET",
|
|
73
|
+
any,
|
|
74
|
+
any
|
|
75
|
+
>;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Validated route: no auth, no query
|
|
80
|
+
function put<Res extends unknown, RequestBody extends unknown>(config: {
|
|
81
|
+
authenticator?: never;
|
|
82
|
+
validator: ValidatorFunction<RequestBody>;
|
|
83
|
+
queryValidator?: never;
|
|
84
|
+
handler: (params: { url: Params<Path> }, body: RequestBody) => Promise<Res>;
|
|
85
|
+
}): ValidatedRoute<Path, "PUT", Res, RequestBody, undefined>;
|
|
86
|
+
|
|
87
|
+
// Validated route: no auth, with query
|
|
88
|
+
function put<Res extends unknown, RequestBody extends unknown, Query extends unknown>(config: {
|
|
89
|
+
authenticator?: never;
|
|
90
|
+
validator: ValidatorFunction<RequestBody>;
|
|
91
|
+
queryValidator: ValidatorFunction<Query>;
|
|
92
|
+
handler: (params: { url: Params<Path>; query: Query }, body: RequestBody) => Promise<Res>;
|
|
93
|
+
}): ValidatedRoute<Path, "PUT", Res, RequestBody, Query>;
|
|
94
|
+
|
|
95
|
+
// Full route: auth + body validation, no query
|
|
96
|
+
function put<Res extends unknown, RequestBody extends unknown, Auth extends Authentication>(config: {
|
|
97
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
98
|
+
validator: ValidatorFunction<RequestBody>;
|
|
99
|
+
queryValidator?: never;
|
|
100
|
+
handler: (params: { url: Params<Path> }, auth: Auth, body: RequestBody) => Promise<Res>;
|
|
101
|
+
}): FullRoute<Path, "PUT", Res, RequestBody, Auth, undefined>;
|
|
102
|
+
|
|
103
|
+
// Full route: auth + body validation, with query
|
|
104
|
+
function put<
|
|
105
|
+
Res extends unknown,
|
|
106
|
+
RequestBody extends unknown,
|
|
107
|
+
Auth extends Authentication,
|
|
108
|
+
Query extends unknown,
|
|
109
|
+
>(config: {
|
|
110
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
111
|
+
validator: ValidatorFunction<RequestBody>;
|
|
112
|
+
queryValidator: ValidatorFunction<Query>;
|
|
113
|
+
handler: (params: { url: Params<Path>; query: Query }, auth: Auth, body: RequestBody) => Promise<Res>;
|
|
114
|
+
}): FullRoute<Path, "PUT", Res, RequestBody, Auth, Query>;
|
|
115
|
+
|
|
116
|
+
function put(
|
|
117
|
+
config:
|
|
118
|
+
| { validator: ValidatorFunction<any>; handler: (params: { url: Params<Path> }, body: any) => Promise<any> }
|
|
119
|
+
| { validator: ValidatorFunction<any>; queryValidator: ValidatorFunction<any>; handler: (params: { url: Params<Path>; query: any }, body: any) => Promise<any> }
|
|
120
|
+
| { authenticator: AuthenticationFunction<any>; validator: ValidatorFunction<any>; handler: (params: { url: Params<Path> }, auth: any, body: any) => Promise<any> }
|
|
121
|
+
| { authenticator: AuthenticationFunction<any>; validator: ValidatorFunction<any>; queryValidator: ValidatorFunction<any>; handler: (params: { url: Params<Path>; query: any }, auth: any, body: any) => Promise<any> }
|
|
122
|
+
): ValidatedRoute<Path, "PUT", any, any, any> | FullRoute<Path, "PUT", any, any, any, any> {
|
|
123
|
+
if ("authenticator" in config) {
|
|
124
|
+
return { ...config, type: "full", path, method: "PUT" } satisfies FullRoute<
|
|
125
|
+
Path,
|
|
126
|
+
"PUT",
|
|
127
|
+
any,
|
|
128
|
+
any,
|
|
129
|
+
any,
|
|
130
|
+
any
|
|
131
|
+
>;
|
|
132
|
+
} else {
|
|
133
|
+
return { ...config, type: "validated", path, method: "PUT" } satisfies ValidatedRoute<
|
|
134
|
+
Path,
|
|
135
|
+
"PUT",
|
|
136
|
+
any,
|
|
137
|
+
any,
|
|
138
|
+
any
|
|
139
|
+
>;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Validated route: no auth, no query
|
|
144
|
+
function post<Res extends unknown, RequestBody extends unknown>(config: {
|
|
145
|
+
authenticator?: never;
|
|
146
|
+
validator: ValidatorFunction<RequestBody>;
|
|
147
|
+
queryValidator?: never;
|
|
148
|
+
handler: (params: { url: Params<Path> }, body: RequestBody) => Promise<Res>;
|
|
149
|
+
}): ValidatedRoute<Path, "POST", Res, RequestBody, undefined>;
|
|
150
|
+
|
|
151
|
+
// Validated route: no auth, with query
|
|
152
|
+
function post<Res extends unknown, RequestBody extends unknown, Query extends unknown>(config: {
|
|
153
|
+
authenticator?: never;
|
|
154
|
+
validator: ValidatorFunction<RequestBody>;
|
|
155
|
+
queryValidator: ValidatorFunction<Query>;
|
|
156
|
+
handler: (params: { url: Params<Path>; query: Query }, body: RequestBody) => Promise<Res>;
|
|
157
|
+
}): ValidatedRoute<Path, "POST", Res, RequestBody, Query>;
|
|
158
|
+
|
|
159
|
+
// Full route: auth + body validation, no query
|
|
160
|
+
function post<Res extends unknown, RequestBody extends unknown, Auth extends Authentication>(config: {
|
|
161
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
162
|
+
validator: ValidatorFunction<RequestBody>;
|
|
163
|
+
queryValidator?: never;
|
|
164
|
+
handler: (params: { url: Params<Path> }, auth: Auth, body: RequestBody) => Promise<Res>;
|
|
165
|
+
}): FullRoute<Path, "POST", Res, RequestBody, Auth, undefined>;
|
|
166
|
+
|
|
167
|
+
// Full route: auth + body validation, with query
|
|
168
|
+
function post<
|
|
169
|
+
Res extends unknown,
|
|
170
|
+
RequestBody extends unknown,
|
|
171
|
+
Auth extends Authentication,
|
|
172
|
+
Query extends unknown,
|
|
173
|
+
>(config: {
|
|
174
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
175
|
+
validator: ValidatorFunction<RequestBody>;
|
|
176
|
+
queryValidator: ValidatorFunction<Query>;
|
|
177
|
+
handler: (params: { url: Params<Path>; query: Query }, auth: Auth, body: RequestBody) => Promise<Res>;
|
|
178
|
+
}): FullRoute<Path, "POST", Res, RequestBody, Auth, Query>;
|
|
179
|
+
|
|
180
|
+
function post(
|
|
181
|
+
config:
|
|
182
|
+
| { validator: ValidatorFunction<any>; handler: (params: { url: Params<Path> }, body: any) => Promise<any> }
|
|
183
|
+
| { validator: ValidatorFunction<any>; queryValidator: ValidatorFunction<any>; handler: (params: { url: Params<Path>; query: any }, body: any) => Promise<any> }
|
|
184
|
+
| { authenticator: AuthenticationFunction<any>; validator: ValidatorFunction<any>; handler: (params: { url: Params<Path> }, auth: any, body: any) => Promise<any> }
|
|
185
|
+
| { authenticator: AuthenticationFunction<any>; validator: ValidatorFunction<any>; queryValidator: ValidatorFunction<any>; handler: (params: { url: Params<Path>; query: any }, auth: any, body: any) => Promise<any> }
|
|
186
|
+
): ValidatedRoute<Path, "POST", any, any, any> | FullRoute<Path, "POST", any, any, any, any> {
|
|
187
|
+
if ("authenticator" in config) {
|
|
188
|
+
return { ...config, type: "full", path, method: "POST" } satisfies FullRoute<
|
|
189
|
+
Path,
|
|
190
|
+
"POST",
|
|
191
|
+
any,
|
|
192
|
+
any,
|
|
193
|
+
any,
|
|
194
|
+
any
|
|
195
|
+
>;
|
|
196
|
+
} else {
|
|
197
|
+
return { ...config, type: "validated", path, method: "POST" } satisfies ValidatedRoute<
|
|
198
|
+
Path,
|
|
199
|
+
"POST",
|
|
200
|
+
any,
|
|
201
|
+
any,
|
|
202
|
+
any
|
|
203
|
+
>;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Open route: no auth, no query
|
|
208
|
+
function del<Res extends unknown>(config: {
|
|
209
|
+
authenticator?: never;
|
|
210
|
+
queryValidator?: never;
|
|
211
|
+
handler: (params: { url: Params<Path> }) => Promise<Res>;
|
|
212
|
+
}): OpenRoute<Path, "DELETE", Res, undefined>;
|
|
213
|
+
|
|
214
|
+
// Open route: no auth, with query
|
|
215
|
+
function del<Res extends unknown, Query extends unknown>(config: {
|
|
216
|
+
authenticator?: never;
|
|
217
|
+
queryValidator: ValidatorFunction<Query>;
|
|
218
|
+
handler: (params: { url: Params<Path>; query: Query }) => Promise<Res>;
|
|
219
|
+
}): OpenRoute<Path, "DELETE", Res, Query>;
|
|
220
|
+
|
|
221
|
+
// Authenticated route: auth, no query
|
|
222
|
+
function del<Res extends unknown, Auth extends Authentication>(config: {
|
|
223
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
224
|
+
queryValidator?: never;
|
|
225
|
+
handler: (params: { url: Params<Path> }, auth: Auth) => Promise<Res>;
|
|
226
|
+
}): AuthenticatedRoute<Path, "DELETE", Res, Auth, undefined>;
|
|
227
|
+
|
|
228
|
+
// Authenticated route: auth, with query
|
|
229
|
+
function del<Res extends unknown, Auth extends Authentication, Query extends unknown>(config: {
|
|
230
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
231
|
+
queryValidator: ValidatorFunction<Query>;
|
|
232
|
+
handler: (params: { url: Params<Path>; query: Query }, auth: Auth) => Promise<Res>;
|
|
233
|
+
}): AuthenticatedRoute<Path, "DELETE", Res, Auth, Query>;
|
|
234
|
+
|
|
235
|
+
function del(
|
|
236
|
+
config:
|
|
237
|
+
| { handler: (params: { url: Params<Path> }) => Promise<any> }
|
|
238
|
+
| { queryValidator: ValidatorFunction<any>; handler: (params: { url: Params<Path>; query: any }) => Promise<any> }
|
|
239
|
+
| { authenticator: AuthenticationFunction<any>; handler: (params: { url: Params<Path> }, auth: any) => Promise<any> }
|
|
240
|
+
| { authenticator: AuthenticationFunction<any>; queryValidator: ValidatorFunction<any>; handler: (params: { url: Params<Path>; query: any }, auth: any) => Promise<any> }
|
|
241
|
+
): OpenRoute<Path, "DELETE", any, any> | AuthenticatedRoute<Path, "DELETE", any, any, any> {
|
|
242
|
+
if ("authenticator" in config) {
|
|
243
|
+
return {
|
|
244
|
+
...config,
|
|
245
|
+
type: "authenticated",
|
|
246
|
+
path,
|
|
247
|
+
method: "DELETE",
|
|
248
|
+
} satisfies AuthenticatedRoute<Path, "DELETE", any, any, any>;
|
|
249
|
+
} else {
|
|
250
|
+
return { ...config, type: "open", path, method: "DELETE" } satisfies OpenRoute<
|
|
251
|
+
Path,
|
|
252
|
+
"DELETE",
|
|
253
|
+
any,
|
|
254
|
+
any
|
|
255
|
+
>;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
get,
|
|
261
|
+
post,
|
|
262
|
+
put,
|
|
263
|
+
delete: del,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Provides an Ednpoint creater. Would maybe like some other internal stuff? Lowkey... I think we
|
|
269
|
+
* all might just end up using the helper functions of get and del and such.
|
|
270
|
+
* @param _ - this param is still needed because the type is determined at compile time.
|
|
271
|
+
*/
|
|
272
|
+
export function createRouter<BasePath extends string>(_: BasePath): Router<BasePath> {
|
|
273
|
+
return {
|
|
274
|
+
createEndpoint: <Path extends `${BasePath}${string}`>(path: Path) => createEndpoint(path),
|
|
275
|
+
createSubrouter: <Path extends `${BasePath}${string}`>(path: Path) => createRouter(path),
|
|
276
|
+
};
|
|
277
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
|
|
2
|
+
curl \
|
|
3
|
+
--user "euuoxeqo:3351126f-00d2-4964-840f-a8e29fb4f2c5" \
|
|
4
|
+
--digest \
|
|
5
|
+
--header 'Accept: application/vnd.atlas.2024-11-13+json' \
|
|
6
|
+
--header 'Content-Type: application/json' \
|
|
7
|
+
--request GET "https://cloud.mongodb.com/api/atlas/v2/groups/64619e3fea3ebc1bb3d691f3/streams/accountDetails?cloudProvider=aws®ionName=US_EAST_1"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
{
|
|
11
|
+
"awsAccountId":"821274765805",
|
|
12
|
+
"cidrBlock":"192.168.248.0/21",
|
|
13
|
+
"vpcId":"vpc-04a8f4b80f14357ef",
|
|
14
|
+
"cloudProvider":"aws"
|
|
15
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { Params } from "./params.types";
|
|
2
|
+
import {
|
|
3
|
+
Authentication,
|
|
4
|
+
AuthenticatedRoute,
|
|
5
|
+
FullRoute,
|
|
6
|
+
OpenRoute,
|
|
7
|
+
ValidatedRoute,
|
|
8
|
+
} from "./routes.types";
|
|
9
|
+
import { RestMethod } from "./routes.types";
|
|
10
|
+
|
|
11
|
+
export type ValidatorFunction<T extends unknown = unknown> = (data: unknown) => T;
|
|
12
|
+
export type AuthenticationFunction<T extends Authentication> = (
|
|
13
|
+
headers: Record<string, string>
|
|
14
|
+
) => T;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* This is the most confusing part of the type system, but also key.
|
|
18
|
+
* This type is used to force type inferenece when creating your endpoints.
|
|
19
|
+
* This is the structure to provide `function overload`s. This is so you can have the same function
|
|
20
|
+
* (in our case get,put,post,delete) but allow differnt configurations.
|
|
21
|
+
*
|
|
22
|
+
* These configurations need to be strongly typed force the developers into using the tools.
|
|
23
|
+
*
|
|
24
|
+
* Want to allow Auth? - Gotta pass in authenticator and the type system handles it
|
|
25
|
+
*
|
|
26
|
+
* Also, this is where the core of the Zod system is. We force the body to be a zod validated input.
|
|
27
|
+
* Luckily, this is pretty easy to replace.
|
|
28
|
+
* PART of me wanted to further abstract it such that you needed a translation function but
|
|
29
|
+
* that seemed overengineerd
|
|
30
|
+
*
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Configuration type for POST/PUT/DELETE endpoints.
|
|
35
|
+
* These methods require a request body, so they always need a validator.
|
|
36
|
+
*
|
|
37
|
+
* Four variants (with/without auth × with/without query):
|
|
38
|
+
* 1. Validated only, no query: validator + handler(params, body)
|
|
39
|
+
* 2. Validated only, with query: validator + queryValidator + handler(params, body)
|
|
40
|
+
* 3. Full (auth + validated), no query: authenticator + validator + handler(params, auth, body)
|
|
41
|
+
* 4. Full (auth + validated), with query: authenticator + validator + queryValidator + handler(params, auth, body)
|
|
42
|
+
*/
|
|
43
|
+
export type EndpointCreationFunction<Path extends string, Method extends RestMethod> = {
|
|
44
|
+
// Validated route: no auth, no query
|
|
45
|
+
<Res extends unknown, RequestBody extends unknown>(
|
|
46
|
+
config: {
|
|
47
|
+
authenticator?: never;
|
|
48
|
+
validator: ValidatorFunction<RequestBody>;
|
|
49
|
+
queryValidator?: never;
|
|
50
|
+
handler: (params: { url: Params<Path> }, body: RequestBody) => Promise<Res>;
|
|
51
|
+
}
|
|
52
|
+
): ValidatedRoute<Path, Method, Res, RequestBody, undefined>;
|
|
53
|
+
|
|
54
|
+
// Validated route: no auth, with query
|
|
55
|
+
<Res extends unknown, RequestBody extends unknown, Query extends unknown>(
|
|
56
|
+
config: {
|
|
57
|
+
authenticator?: never;
|
|
58
|
+
validator: ValidatorFunction<RequestBody>;
|
|
59
|
+
queryValidator: ValidatorFunction<Query>;
|
|
60
|
+
handler: (
|
|
61
|
+
params: { url: Params<Path>; query: Query },
|
|
62
|
+
body: RequestBody
|
|
63
|
+
) => Promise<Res>;
|
|
64
|
+
}
|
|
65
|
+
): ValidatedRoute<Path, Method, Res, RequestBody, Query>;
|
|
66
|
+
|
|
67
|
+
// Full route: auth + body validation, no query
|
|
68
|
+
<Res extends unknown, RequestBody extends unknown, Auth extends Authentication>(
|
|
69
|
+
config: {
|
|
70
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
71
|
+
validator: ValidatorFunction<RequestBody>;
|
|
72
|
+
queryValidator?: never;
|
|
73
|
+
handler: (params: { url: Params<Path> }, auth: Auth, body: RequestBody) => Promise<Res>;
|
|
74
|
+
}
|
|
75
|
+
): FullRoute<Path, Method, Res, RequestBody, Auth, undefined>;
|
|
76
|
+
|
|
77
|
+
// Full route: auth + body validation, with query
|
|
78
|
+
<
|
|
79
|
+
Res extends unknown,
|
|
80
|
+
RequestBody extends unknown,
|
|
81
|
+
Auth extends Authentication,
|
|
82
|
+
Query extends unknown,
|
|
83
|
+
>(
|
|
84
|
+
config: {
|
|
85
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
86
|
+
validator: ValidatorFunction<RequestBody>;
|
|
87
|
+
queryValidator: ValidatorFunction<Query>;
|
|
88
|
+
handler: (
|
|
89
|
+
params: { url: Params<Path>; query: Query },
|
|
90
|
+
auth: Auth,
|
|
91
|
+
body: RequestBody
|
|
92
|
+
) => Promise<Res>;
|
|
93
|
+
}
|
|
94
|
+
): FullRoute<Path, Method, Res, RequestBody, Auth, Query>;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Configuration type for GET/DELETE endpoints.
|
|
99
|
+
* These methods cannot have a request body, so no validator.
|
|
100
|
+
*
|
|
101
|
+
* Four variants (with/without auth × with/without query):
|
|
102
|
+
* 1. Open, no query: just handler(params)
|
|
103
|
+
* 2. Open, with query: queryValidator + handler(params)
|
|
104
|
+
* 3. Authenticated, no query: authenticator + handler(params, auth)
|
|
105
|
+
* 4. Authenticated, with query: authenticator + queryValidator + handler(params, auth)
|
|
106
|
+
*/
|
|
107
|
+
export type GetEndpointCreationFunction<Path extends string, Method extends RestMethod> = {
|
|
108
|
+
// Open route: no auth, no query
|
|
109
|
+
<Res extends unknown>(
|
|
110
|
+
config: {
|
|
111
|
+
authenticator?: never;
|
|
112
|
+
queryValidator?: never;
|
|
113
|
+
handler: (params: { url: Params<Path> }) => Promise<Res>;
|
|
114
|
+
}
|
|
115
|
+
): OpenRoute<Path, Method, Res, undefined>;
|
|
116
|
+
|
|
117
|
+
// Open route: no auth, with query
|
|
118
|
+
<Res extends unknown, Query extends unknown>(
|
|
119
|
+
config: {
|
|
120
|
+
authenticator?: never;
|
|
121
|
+
queryValidator: ValidatorFunction<Query>;
|
|
122
|
+
handler: (params: { url: Params<Path>; query: Query }) => Promise<Res>;
|
|
123
|
+
}
|
|
124
|
+
): OpenRoute<Path, Method, Res, Query>;
|
|
125
|
+
|
|
126
|
+
// Authenticated route: auth, no query
|
|
127
|
+
<Res extends unknown, Auth extends Authentication>(
|
|
128
|
+
config: {
|
|
129
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
130
|
+
queryValidator?: never;
|
|
131
|
+
handler: (params: { url: Params<Path> }, auth: Auth) => Promise<Res>;
|
|
132
|
+
}
|
|
133
|
+
): AuthenticatedRoute<Path, Method, Res, Auth, undefined>;
|
|
134
|
+
|
|
135
|
+
// Authenticated route: auth, with query
|
|
136
|
+
<Res extends unknown, Auth extends Authentication, Query extends unknown>(
|
|
137
|
+
config: {
|
|
138
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
139
|
+
queryValidator: ValidatorFunction<Query>;
|
|
140
|
+
handler: (params: { url: Params<Path>; query: Query }, auth: Auth) => Promise<Res>;
|
|
141
|
+
}
|
|
142
|
+
): AuthenticatedRoute<Path, Method, Res, Auth, Query>;
|
|
143
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
type ExtractUrlParams<Path extends string> = Path extends `${string}/:${infer Param}/${infer Rest}`
|
|
2
|
+
? Param | ExtractUrlParams<Rest>
|
|
3
|
+
: Path extends `${string}/:${infer Param}`
|
|
4
|
+
? Param
|
|
5
|
+
: never;
|
|
6
|
+
|
|
7
|
+
// Yields the full object. Otherwise, we end up with a bunch of & during type inference
|
|
8
|
+
type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
|
|
9
|
+
|
|
10
|
+
// Helper to use Expand with Path
|
|
11
|
+
export type Params<Path extends string> = Expand<{
|
|
12
|
+
[K in ExtractUrlParams<Path>]: string;
|
|
13
|
+
}>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { GetEndpointCreationFunction } from "./configuration.types";
|
|
2
|
+
import { EndpointCreationFunction } from "./configuration.types";
|
|
3
|
+
|
|
4
|
+
/* Endpoint seems more complex than it is from its type signature.
|
|
5
|
+
* It has 4 Major functions on offer. The different HTTP methods
|
|
6
|
+
*
|
|
7
|
+
* These use the EndpointCreationFunction
|
|
8
|
+
*/
|
|
9
|
+
export type Endpoint<Path extends string> = {
|
|
10
|
+
get: GetEndpointCreationFunction<Path, "GET">;
|
|
11
|
+
post: EndpointCreationFunction<Path, "POST">;
|
|
12
|
+
put: EndpointCreationFunction<Path, "PUT">;
|
|
13
|
+
delete: GetEndpointCreationFunction<Path, "DELETE">;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Creates a router that ensures all endpoints extend the route.
|
|
18
|
+
* IDK if this is actually useful, but I kinda like it.
|
|
19
|
+
*
|
|
20
|
+
* There def was a part of me that thought about pure generator functions.
|
|
21
|
+
* In fact... they're provided!
|
|
22
|
+
*
|
|
23
|
+
* We can discuss that.
|
|
24
|
+
*/
|
|
25
|
+
export type Router<BasePath extends string> = {
|
|
26
|
+
/**
|
|
27
|
+
* Creates an endpoint
|
|
28
|
+
* @param path string, must extend Router's string
|
|
29
|
+
*/
|
|
30
|
+
createEndpoint: <Path extends `${BasePath}${string}`>(path: Path) => Endpoint<Path>;
|
|
31
|
+
/**
|
|
32
|
+
* Creates a subrouter
|
|
33
|
+
* @param path string, must extend Router's string
|
|
34
|
+
*/
|
|
35
|
+
createSubrouter: <Path extends `${BasePath}${string}`>(path: Path) => Router<Path>;
|
|
36
|
+
};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { AuthenticationFunction, ValidatorFunction } from "./configuration.types";
|
|
2
|
+
import { Params } from "./params.types";
|
|
3
|
+
|
|
4
|
+
declare const authBrand: unique symbol;
|
|
5
|
+
export type Authentication = { readonly [authBrand]: "Auth" };
|
|
6
|
+
export function authWrapper<T>(auth: T): T & Authentication {
|
|
7
|
+
return {
|
|
8
|
+
...auth,
|
|
9
|
+
[authBrand]: "Auth",
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type RestMethod = "GET" | "POST" | "PUT" | "DELETE";
|
|
14
|
+
|
|
15
|
+
// Combined Route
|
|
16
|
+
export type BaseRoute<Path extends string> = {
|
|
17
|
+
TRANSACTIONS: boolean;
|
|
18
|
+
path: Path;
|
|
19
|
+
logging?: (input: any) => void;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type OpenRoute<
|
|
23
|
+
Path extends string,
|
|
24
|
+
Method extends RestMethod,
|
|
25
|
+
Res extends unknown,
|
|
26
|
+
Query extends unknown | undefined = undefined,
|
|
27
|
+
> = {
|
|
28
|
+
type: "open";
|
|
29
|
+
path: Path;
|
|
30
|
+
method: Method;
|
|
31
|
+
validator?: never;
|
|
32
|
+
authenticator?: never;
|
|
33
|
+
handler: (params: { url: Params<Path>; query: Query }) => Promise<Res>;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type AuthenticatedRoute<
|
|
37
|
+
Path extends string,
|
|
38
|
+
Method extends RestMethod,
|
|
39
|
+
Res extends unknown,
|
|
40
|
+
Auth extends Authentication,
|
|
41
|
+
Query extends unknown | undefined = undefined,
|
|
42
|
+
> = {
|
|
43
|
+
type: "authenticated";
|
|
44
|
+
path: Path;
|
|
45
|
+
method: Method;
|
|
46
|
+
validator?: never;
|
|
47
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
48
|
+
handler: (
|
|
49
|
+
params: {
|
|
50
|
+
url: Params<Path>;
|
|
51
|
+
query: Query;
|
|
52
|
+
},
|
|
53
|
+
auth: Auth
|
|
54
|
+
) => Promise<Res>;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export type ValidatedRoute<
|
|
58
|
+
Path extends string,
|
|
59
|
+
Method extends RestMethod,
|
|
60
|
+
Res extends unknown,
|
|
61
|
+
RequestBody extends unknown,
|
|
62
|
+
Query extends unknown | undefined = undefined,
|
|
63
|
+
> = {
|
|
64
|
+
type: "validated";
|
|
65
|
+
path: Path;
|
|
66
|
+
method: Method;
|
|
67
|
+
validator: ValidatorFunction<RequestBody>;
|
|
68
|
+
authenticator?: never;
|
|
69
|
+
handler: (
|
|
70
|
+
params: {
|
|
71
|
+
url: Params<Path>;
|
|
72
|
+
query: Query;
|
|
73
|
+
},
|
|
74
|
+
body: RequestBody
|
|
75
|
+
) => Promise<Res>;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export type FullRoute<
|
|
79
|
+
Path extends string,
|
|
80
|
+
Method extends RestMethod,
|
|
81
|
+
Res extends unknown,
|
|
82
|
+
RequestBody extends unknown,
|
|
83
|
+
Auth extends Authentication,
|
|
84
|
+
Query extends unknown | undefined = undefined,
|
|
85
|
+
> = {
|
|
86
|
+
type: "full";
|
|
87
|
+
path: Path;
|
|
88
|
+
method: Method;
|
|
89
|
+
validator: ValidatorFunction<RequestBody>;
|
|
90
|
+
authenticator: AuthenticationFunction<Auth>;
|
|
91
|
+
handler: Query extends undefined
|
|
92
|
+
? (
|
|
93
|
+
params: {
|
|
94
|
+
url: Params<Path>;
|
|
95
|
+
},
|
|
96
|
+
auth: Authentication,
|
|
97
|
+
body: RequestBody
|
|
98
|
+
) => Promise<Res>
|
|
99
|
+
: (
|
|
100
|
+
params: {
|
|
101
|
+
url: Params<Path>;
|
|
102
|
+
query: Query;
|
|
103
|
+
},
|
|
104
|
+
auth: Authentication,
|
|
105
|
+
body: RequestBody
|
|
106
|
+
) => Promise<Res>;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
export type Route<
|
|
110
|
+
Path extends string,
|
|
111
|
+
Res extends unknown,
|
|
112
|
+
Method extends RestMethod,
|
|
113
|
+
RequestBody extends unknown | undefined = undefined,
|
|
114
|
+
Auth extends Authentication | undefined = undefined,
|
|
115
|
+
Query extends unknown | undefined = undefined,
|
|
116
|
+
> = [RequestBody, Auth] extends [undefined, undefined]
|
|
117
|
+
? OpenRoute<Path, Method, Res, Query>
|
|
118
|
+
: [RequestBody, Auth] extends [undefined, infer A extends Authentication]
|
|
119
|
+
? AuthenticatedRoute<Path, Method, Res, A, Query>
|
|
120
|
+
: [RequestBody, Auth] extends [infer B, undefined]
|
|
121
|
+
? ValidatedRoute<Path, Method, Res, B, Query>
|
|
122
|
+
: [RequestBody, Auth] extends [infer B, infer A extends Authentication]
|
|
123
|
+
? FullRoute<Path, Method, Res, B, A, Query>
|
|
124
|
+
: never;
|