@stacksjs/bun-router 0.1.2 → 0.1.3
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/cli.js +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +13 -13
- package/dist/request/cookie-accessor.d.ts +33 -0
- package/dist/router/middleware-groups.d.ts +19 -14
- package/dist/router/router.d.ts +21 -13
- package/dist/types/registry.d.ts +93 -0
- package/dist/types.d.ts +64 -17
- package/dist/url.d.ts +34 -1
- package/package.json +1 -1
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One `request.cookies`, built one way.
|
|
3
|
+
*
|
|
4
|
+
* There were three constructions of this object - one in the router, one in
|
|
5
|
+
* `testing/test-request`, one in `testing/auth-testing` - and the server built
|
|
6
|
+
* a fourth, different one through a macro accessor. Only the macro's was the
|
|
7
|
+
* real shape: callable, carrying the name→value entries directly, AND holding
|
|
8
|
+
* `get`/`set`/`delete`/`getAll`. The other three were plain method bags, so a
|
|
9
|
+
* handler written against a real request (`req.cookies()`, `req.cookies.session`)
|
|
10
|
+
* broke the moment it met a test request, and the declared type described none
|
|
11
|
+
* of them completely.
|
|
12
|
+
*
|
|
13
|
+
* This is the one builder. The differences that are real - where a read comes
|
|
14
|
+
* from, where a write goes - are parameters.
|
|
15
|
+
*/
|
|
16
|
+
import type { CookieAccessor, CookieOptions } from '../types';
|
|
17
|
+
export interface CookieAccessorSinks {
|
|
18
|
+
/** The current cookie map. Called on every read, so it can stay lazy. */
|
|
19
|
+
read: () => Record<string, string>;
|
|
20
|
+
/** Where a `set` goes. Omitted, `set` is a no-op. */
|
|
21
|
+
write?: (name: string, value: string, options: CookieOptions) => void;
|
|
22
|
+
/** Where a `delete` goes. Omitted, `delete` is a no-op. */
|
|
23
|
+
remove?: (name: string, options: CookieOptions) => void;
|
|
24
|
+
}
|
|
25
|
+
export declare function createCookieAccessor(sinks: CookieAccessorSinks): CookieAccessor;
|
|
26
|
+
/**
|
|
27
|
+
* A cookie accessor backed by a mutable in-memory map.
|
|
28
|
+
*
|
|
29
|
+
* What a test request wants: `set` and `delete` take effect immediately and are
|
|
30
|
+
* visible to the next read, rather than being queued onto a response that does
|
|
31
|
+
* not exist.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createInMemoryCookieAccessor(initial?: Record<string, string>): CookieAccessor;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MiddlewareHandler } from '../types';
|
|
1
|
+
import type { MiddlewareHandler, MiddlewareReference } from '../types';
|
|
2
2
|
/**
|
|
3
3
|
* Middleware Group Registry
|
|
4
4
|
*
|
|
@@ -20,19 +20,19 @@ export declare class MiddlewareGroupRegistry {
|
|
|
20
20
|
/**
|
|
21
21
|
* Define a middleware group
|
|
22
22
|
*/
|
|
23
|
-
define(name: string, middleware: (
|
|
23
|
+
define(name: string, middleware: (MiddlewareReference | MiddlewareHandler)[]): this;
|
|
24
24
|
/**
|
|
25
25
|
* Extend an existing group with additional middleware
|
|
26
26
|
*/
|
|
27
|
-
extend(name: string, middleware: (
|
|
27
|
+
extend(name: string, middleware: (MiddlewareReference | MiddlewareHandler)[]): this;
|
|
28
28
|
/**
|
|
29
29
|
* Prepend middleware to an existing group
|
|
30
30
|
*/
|
|
31
|
-
prepend(name: string, middleware: (
|
|
31
|
+
prepend(name: string, middleware: (MiddlewareReference | MiddlewareHandler)[]): this;
|
|
32
32
|
/**
|
|
33
33
|
* Get middleware for a group
|
|
34
34
|
*/
|
|
35
|
-
get(name: string): (
|
|
35
|
+
get(name: string): (MiddlewareReference | MiddlewareHandler)[];
|
|
36
36
|
/**
|
|
37
37
|
* Check if a group exists
|
|
38
38
|
*/
|
|
@@ -42,21 +42,26 @@ export declare class MiddlewareGroupRegistry {
|
|
|
42
42
|
*/
|
|
43
43
|
remove(name: string): this;
|
|
44
44
|
/**
|
|
45
|
-
* Resolve a middleware reference, expanding group names into their middleware
|
|
45
|
+
* Resolve a middleware reference, expanding group names into their middleware.
|
|
46
|
+
*
|
|
47
|
+
* A group name is a middleware reference as far as a caller is concerned -
|
|
48
|
+
* you write `.middleware('web')` and get the group - so an application that
|
|
49
|
+
* declares its aliases in `RouterTypeRegistry` lists its group names there
|
|
50
|
+
* too.
|
|
46
51
|
*/
|
|
47
|
-
resolve(middleware:
|
|
52
|
+
resolve(middleware: MiddlewareReference | MiddlewareHandler): (MiddlewareReference | MiddlewareHandler)[];
|
|
48
53
|
/**
|
|
49
54
|
* Resolve multiple middleware references, expanding groups
|
|
50
55
|
*/
|
|
51
|
-
resolveAll(middleware: (
|
|
56
|
+
resolveAll(middleware: (MiddlewareReference | MiddlewareHandler)[]): (MiddlewareReference | MiddlewareHandler)[];
|
|
52
57
|
/**
|
|
53
58
|
* Add middleware to the global stack (runs on every request)
|
|
54
59
|
*/
|
|
55
|
-
pushGlobal(...middleware: (
|
|
60
|
+
pushGlobal(...middleware: (MiddlewareReference | MiddlewareHandler)[]): this;
|
|
56
61
|
/**
|
|
57
62
|
* Prepend middleware to the global stack
|
|
58
63
|
*/
|
|
59
|
-
prependGlobal(...middleware: (
|
|
64
|
+
prependGlobal(...middleware: (MiddlewareReference | MiddlewareHandler)[]): this;
|
|
60
65
|
/**
|
|
61
66
|
* Remove middleware from the global stack
|
|
62
67
|
*/
|
|
@@ -64,7 +69,7 @@ export declare class MiddlewareGroupRegistry {
|
|
|
64
69
|
/**
|
|
65
70
|
* Get the global middleware stack
|
|
66
71
|
*/
|
|
67
|
-
getGlobal(): (
|
|
72
|
+
getGlobal(): (MiddlewareReference | MiddlewareHandler)[];
|
|
68
73
|
/**
|
|
69
74
|
* Set execution priority for a middleware (lower = runs first)
|
|
70
75
|
*/
|
|
@@ -72,17 +77,17 @@ export declare class MiddlewareGroupRegistry {
|
|
|
72
77
|
/**
|
|
73
78
|
* Sort middleware by priority
|
|
74
79
|
*/
|
|
75
|
-
sortByPriority(middleware: (
|
|
80
|
+
sortByPriority(middleware: (MiddlewareReference | MiddlewareHandler)[]): (MiddlewareReference | MiddlewareHandler)[];
|
|
76
81
|
/**
|
|
77
82
|
* Filter middleware by applying except/only rules.
|
|
78
83
|
*
|
|
79
84
|
* @param middleware - Full middleware list
|
|
80
85
|
* @param options - except or only filter
|
|
81
86
|
*/
|
|
82
|
-
filter(middleware: (
|
|
87
|
+
filter(middleware: (MiddlewareReference | MiddlewareHandler)[], options: {
|
|
83
88
|
except?: string[];
|
|
84
89
|
only?: string[];
|
|
85
|
-
}): (
|
|
90
|
+
}): (MiddlewareReference | MiddlewareHandler)[];
|
|
86
91
|
/**
|
|
87
92
|
* Clear all groups and global middleware
|
|
88
93
|
*/
|
package/dist/router/router.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Server } from 'bun';
|
|
2
2
|
import type { MiddlewareDependency, MiddlewarePipeline, MiddlewarePipelineStats, MiddlewareSkipCondition } from '../middleware/pipeline';
|
|
3
|
-
import type { ActionHandler, CookieOptions, EnhancedRequest, MiddlewareHandler, Route, RouteGroup, RouteHandler, RouterConfig, WebSocketConfig, WebSocketData } from '../types';
|
|
3
|
+
import type { ActionHandler, CookieOptions, EnhancedRequest, MiddlewareHandler, MiddlewareReference, Route, RouteGroup, RouteHandler, RouterConfig, TypedRouteHandler, WebSocketConfig, WebSocketData } from '../types';
|
|
4
4
|
/**
|
|
5
5
|
* Route compiler interface for pattern matching
|
|
6
6
|
*/
|
|
@@ -95,7 +95,7 @@ export declare class Router {
|
|
|
95
95
|
* Internal method to add a route with full HTTP method support
|
|
96
96
|
* This is the core route registration method used by get/post/put/patch/delete
|
|
97
97
|
*/
|
|
98
|
-
registerRoute(method: string, path: string, handler: ActionHandler, type?: 'api' | 'web', name?: string, middleware?: (
|
|
98
|
+
registerRoute(method: string, path: string, handler: ActionHandler, type?: 'api' | 'web', name?: string, middleware?: (MiddlewareReference | MiddlewareHandler)[]): Router;
|
|
99
99
|
/**
|
|
100
100
|
* Resolve middleware from string or handler (synchronous)
|
|
101
101
|
*/
|
|
@@ -118,43 +118,51 @@ export declare class Router {
|
|
|
118
118
|
* keep working — `ActionHandler<TPath>` is a union that accepts them.
|
|
119
119
|
* See stacksjs/stacks#1851 for the broader typed-request work.
|
|
120
120
|
*/
|
|
121
|
-
get<TPath extends string>(path: TPath, handler:
|
|
121
|
+
get<TPath extends string>(path: TPath, handler: TypedRouteHandler<TPath>, type?: 'api' | 'web', name?: string, middleware?: (MiddlewareReference | MiddlewareHandler)[]): Router;
|
|
122
|
+
get<TPath extends string>(path: TPath, handler: ActionHandler<TPath>, type?: 'api' | 'web', name?: string, middleware?: (MiddlewareReference | MiddlewareHandler)[]): Router;
|
|
122
123
|
/**
|
|
123
124
|
* HTTP POST method. See {@link get} for `TPath`-driven param
|
|
124
125
|
* narrowing on inline handlers.
|
|
125
126
|
*/
|
|
126
|
-
post<TPath extends string>(path: TPath, handler:
|
|
127
|
+
post<TPath extends string>(path: TPath, handler: TypedRouteHandler<TPath>, type?: 'api' | 'web', name?: string, middleware?: (MiddlewareReference | MiddlewareHandler)[]): Router;
|
|
128
|
+
post<TPath extends string>(path: TPath, handler: ActionHandler<TPath>, type?: 'api' | 'web', name?: string, middleware?: (MiddlewareReference | MiddlewareHandler)[]): Router;
|
|
127
129
|
/**
|
|
128
130
|
* HTTP PUT method. See {@link get} for `TPath`-driven param
|
|
129
131
|
* narrowing on inline handlers.
|
|
130
132
|
*/
|
|
131
|
-
put<TPath extends string>(path: TPath, handler:
|
|
133
|
+
put<TPath extends string>(path: TPath, handler: TypedRouteHandler<TPath>, type?: 'api' | 'web', name?: string, middleware?: (MiddlewareReference | MiddlewareHandler)[]): Router;
|
|
134
|
+
put<TPath extends string>(path: TPath, handler: ActionHandler<TPath>, type?: 'api' | 'web', name?: string, middleware?: (MiddlewareReference | MiddlewareHandler)[]): Router;
|
|
132
135
|
/**
|
|
133
136
|
* HTTP PATCH method. See {@link get} for `TPath`-driven param
|
|
134
137
|
* narrowing on inline handlers.
|
|
135
138
|
*/
|
|
136
|
-
patch<TPath extends string>(path: TPath, handler:
|
|
139
|
+
patch<TPath extends string>(path: TPath, handler: TypedRouteHandler<TPath>, type?: 'api' | 'web', name?: string, middleware?: (MiddlewareReference | MiddlewareHandler)[]): Router;
|
|
140
|
+
patch<TPath extends string>(path: TPath, handler: ActionHandler<TPath>, type?: 'api' | 'web', name?: string, middleware?: (MiddlewareReference | MiddlewareHandler)[]): Router;
|
|
137
141
|
/**
|
|
138
142
|
* HTTP DELETE method. See {@link get} for `TPath`-driven param
|
|
139
143
|
* narrowing on inline handlers.
|
|
140
144
|
*/
|
|
141
|
-
delete<TPath extends string>(path: TPath, handler:
|
|
145
|
+
delete<TPath extends string>(path: TPath, handler: TypedRouteHandler<TPath>, type?: 'api' | 'web', name?: string, middleware?: (MiddlewareReference | MiddlewareHandler)[]): Router;
|
|
146
|
+
delete<TPath extends string>(path: TPath, handler: ActionHandler<TPath>, type?: 'api' | 'web', name?: string, middleware?: (MiddlewareReference | MiddlewareHandler)[]): Router;
|
|
142
147
|
/**
|
|
143
148
|
* HTTP OPTIONS method. See {@link get} for `TPath`-driven param
|
|
144
149
|
* narrowing on inline handlers.
|
|
145
150
|
*/
|
|
146
|
-
options<TPath extends string>(path: TPath, handler:
|
|
151
|
+
options<TPath extends string>(path: TPath, handler: TypedRouteHandler<TPath>, type?: 'api' | 'web', name?: string, middleware?: (MiddlewareReference | MiddlewareHandler)[]): Router;
|
|
152
|
+
options<TPath extends string>(path: TPath, handler: ActionHandler<TPath>, type?: 'api' | 'web', name?: string, middleware?: (MiddlewareReference | MiddlewareHandler)[]): Router;
|
|
147
153
|
/**
|
|
148
154
|
* Register the same handler against multiple HTTP methods. The
|
|
149
155
|
* handler is type-narrowed via {@link ActionHandler}'s `TPath`
|
|
150
156
|
* generic, same as the single-method overloads.
|
|
151
157
|
*/
|
|
152
|
-
match<TPath extends string>(methods: string[], path: TPath, handler:
|
|
158
|
+
match<TPath extends string>(methods: string[], path: TPath, handler: TypedRouteHandler<TPath>, type?: 'api' | 'web', name?: string, middleware?: (MiddlewareReference | MiddlewareHandler)[]): Router;
|
|
159
|
+
match<TPath extends string>(methods: string[], path: TPath, handler: ActionHandler<TPath>, type?: 'api' | 'web', name?: string, middleware?: (MiddlewareReference | MiddlewareHandler)[]): Router;
|
|
153
160
|
/**
|
|
154
161
|
* Register the handler against every HTTP method. See {@link get}
|
|
155
162
|
* for `TPath`-driven param narrowing on inline handlers.
|
|
156
163
|
*/
|
|
157
|
-
any<TPath extends string>(path: TPath, handler:
|
|
164
|
+
any<TPath extends string>(path: TPath, handler: TypedRouteHandler<TPath>, type?: 'api' | 'web', name?: string, middleware?: (MiddlewareReference | MiddlewareHandler)[]): Router;
|
|
165
|
+
any<TPath extends string>(path: TPath, handler: ActionHandler<TPath>, type?: 'api' | 'web', name?: string, middleware?: (MiddlewareReference | MiddlewareHandler)[]): Router;
|
|
158
166
|
/**
|
|
159
167
|
* Register redirect route
|
|
160
168
|
*/
|
|
@@ -197,7 +205,7 @@ export declare class Router {
|
|
|
197
205
|
/**
|
|
198
206
|
* Add middleware to the router
|
|
199
207
|
*/
|
|
200
|
-
use(...middleware: (
|
|
208
|
+
use(...middleware: (MiddlewareReference | MiddlewareHandler)[]): Router;
|
|
201
209
|
/**
|
|
202
210
|
* Create a route group with prefix and middleware.
|
|
203
211
|
*
|
|
@@ -208,11 +216,11 @@ export declare class Router {
|
|
|
208
216
|
*/
|
|
209
217
|
group(options: {
|
|
210
218
|
prefix?: string;
|
|
211
|
-
middleware?: (
|
|
219
|
+
middleware?: (MiddlewareReference | MiddlewareHandler)[];
|
|
212
220
|
}, callback: () => Promise<void>): Promise<Router>;
|
|
213
221
|
group(options: {
|
|
214
222
|
prefix?: string;
|
|
215
|
-
middleware?: (
|
|
223
|
+
middleware?: (MiddlewareReference | MiddlewareHandler)[];
|
|
216
224
|
}, callback: () => void): Router;
|
|
217
225
|
/**
|
|
218
226
|
* Add route to the router
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { BuiltInMiddleware } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* What an application tells the router about itself.
|
|
4
|
+
*
|
|
5
|
+
* A router deals in strings that are really identifiers: `'Actions/CreateUser'`
|
|
6
|
+
* names a file, `'auth'` names a middleware, `'users.show'` names a route. The
|
|
7
|
+
* compiler has no idea which of them exist, so a typo in any of the three is a
|
|
8
|
+
* runtime error at best — a silently unprotected endpoint at worst, when the
|
|
9
|
+
* typo is in a middleware alias.
|
|
10
|
+
*
|
|
11
|
+
* The router cannot know them either. The application can, so it says so, by
|
|
12
|
+
* augmenting this interface:
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* // types/router.d.ts
|
|
16
|
+
* declare module '@stacksjs/bun-router' {
|
|
17
|
+
* interface RouterTypeRegistry {
|
|
18
|
+
* actions: 'Actions/CreateUser' | 'Actions/ListUsers'
|
|
19
|
+
* middleware: 'auth' | 'throttle' | 'signed'
|
|
20
|
+
* routes: {
|
|
21
|
+
* 'users.index': '/users'
|
|
22
|
+
* 'users.show': '/users/{id}'
|
|
23
|
+
* }
|
|
24
|
+
* }
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* From then on, `router.post('/users', 'Actions/CreateUsr')` is a compile
|
|
29
|
+
* error, so is `.middleware('atuh')`, and `url('users.show')` demands an `id`
|
|
30
|
+
* because the path says it has one.
|
|
31
|
+
*
|
|
32
|
+
* ## Nothing is required
|
|
33
|
+
*
|
|
34
|
+
* Every key is independent, and every one falls back to exactly the type it had
|
|
35
|
+
* before this existed when it is absent. An application that declares none of
|
|
36
|
+
* them sees no change at all. That is the point: this cannot be a breaking
|
|
37
|
+
* change, and it cannot be a thing you must do before the router is usable.
|
|
38
|
+
*
|
|
39
|
+
* ## Generating it
|
|
40
|
+
*
|
|
41
|
+
* The three unions are facts a build step already knows. A framework on top of
|
|
42
|
+
* this router that scans `app/Actions/` or a middleware alias map can emit the
|
|
43
|
+
* augmentation into a `.d.ts` and the whole surface types itself with nothing
|
|
44
|
+
* hand-written. Writing it by hand works just as well for a smaller app.
|
|
45
|
+
*/
|
|
46
|
+
export interface RouterTypeRegistry {
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Read a key out of the registry, or fall back.
|
|
50
|
+
*
|
|
51
|
+
* `K extends keyof RouterTypeRegistry` is `never extends never` — false — while
|
|
52
|
+
* the interface is empty, which is what makes every fallback the default. The
|
|
53
|
+
* second check keeps a malformed augmentation (say, `actions: number`) from
|
|
54
|
+
* poisoning the type: it falls back rather than producing something unusable.
|
|
55
|
+
*/
|
|
56
|
+
export type FromRegistry<TKey extends string, TFallback> = TKey extends keyof RouterTypeRegistry ? (RouterTypeRegistry[TKey] extends TFallback ? RouterTypeRegistry[TKey] : TFallback) : TFallback;
|
|
57
|
+
/**
|
|
58
|
+
* The action paths this application has, or the shape of one when it has not
|
|
59
|
+
* said.
|
|
60
|
+
*
|
|
61
|
+
* The fallback is the pattern that was here before: anything under `Actions/`
|
|
62
|
+
* ending in `Action`, or a `Controller@method` reference. It catches a shape
|
|
63
|
+
* mistake and nothing else, which is why declaring the real set is worth doing.
|
|
64
|
+
*/
|
|
65
|
+
export type KnownActionPath = FromRegistry<'actions', `Actions/${string}Action` | `actions/${string}Action` | `${string}Controller@${string}`>;
|
|
66
|
+
/**
|
|
67
|
+
* The middleware aliases this application registers, or any string.
|
|
68
|
+
*
|
|
69
|
+
* Middleware group names belong here too: a group name is a middleware
|
|
70
|
+
* reference as far as a call site is concerned - `.middleware('web')` expands
|
|
71
|
+
* to the group - so the union has to contain both or the group form stops
|
|
72
|
+
* type-checking the moment anything is declared.
|
|
73
|
+
*/
|
|
74
|
+
export type KnownMiddlewareName = FromRegistry<'middleware', string>;
|
|
75
|
+
/**
|
|
76
|
+
* A middleware reference: an alias, or an alias with parameters.
|
|
77
|
+
*
|
|
78
|
+
* `'throttle:60,1'` and `'can:view,post'` are how parameters are passed, so a
|
|
79
|
+
* declared alias has to keep accepting its parameterised form.
|
|
80
|
+
*
|
|
81
|
+
* The built-ins are always allowed alongside whatever the application declares.
|
|
82
|
+
* An app listing its own aliases is saying "these are mine", not "the ones this
|
|
83
|
+
* package ships no longer exist" - and the router's own default middleware
|
|
84
|
+
* groups are built from them, so narrowing them away would make the package
|
|
85
|
+
* fail to describe itself.
|
|
86
|
+
*/
|
|
87
|
+
export type MiddlewareReference = BuiltInMiddleware | `${BuiltInMiddleware}:${string}` | KnownMiddlewareName | `${KnownMiddlewareName}:${string}`;
|
|
88
|
+
/** The named routes this application registers, as `name → path`. */
|
|
89
|
+
export type KnownRoutes = FromRegistry<'routes', Record<string, string>>;
|
|
90
|
+
/** The names of those routes, or any string when none are declared. */
|
|
91
|
+
export type KnownRouteName = keyof KnownRoutes extends never ? string : Extract<keyof KnownRoutes, string>;
|
|
92
|
+
/** The path a declared route name resolves to, or any path when it is not declared. */
|
|
93
|
+
export type PathForRouteName<TName extends string> = TName extends keyof KnownRoutes ? Extract<KnownRoutes[TName], string> : string;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Server } from 'bun';
|
|
2
2
|
import type { Router } from './router/router';
|
|
3
|
+
import type { KnownActionPath, MiddlewareReference } from './types/registry';
|
|
3
4
|
import type { SessionManager } from './session/index';
|
|
4
5
|
import type { QueryPreservationConfig } from './utils/query-preservation';
|
|
5
6
|
import type { AuthContext, AuthenticatedUser, BoundModels, CacheAdapter, CacheStats, CookieToDelete, CookieToSet, ErrorMetadata, FallbackError, FileInfo, FlashMessages, FormBody, InputValidationSchemas, InputValue, JsonBodyData, JwtHeader, JwtPayload, MetricEntry, MiddlewareParams, OAuth2Flows, OAuth2Profile, RequestContext, RequestInput, RouteMetadata, SanitizationOptions, SanitizationRule, SessionData, SessionSerializer, SessionStore, SSEConnection, StaticResponseBody, TemplateHelper, TemplateHelpers, TraceAttributes, TraceSpan, TypedServerWebSocket, User, ValidatedBody, ValidatedData, ValidationSchema, WebSocketData } from './types/core';
|
|
@@ -629,8 +630,8 @@ export interface RouterConfig {
|
|
|
629
630
|
*/
|
|
630
631
|
controllersPath?: string;
|
|
631
632
|
defaultMiddleware?: {
|
|
632
|
-
api?: (
|
|
633
|
-
web?: (
|
|
633
|
+
api?: (MiddlewareReference | MiddlewareHandler)[];
|
|
634
|
+
web?: (MiddlewareReference | MiddlewareHandler)[];
|
|
634
635
|
};
|
|
635
636
|
/**
|
|
636
637
|
* View engine configuration
|
|
@@ -678,13 +679,26 @@ export interface CookieOptions {
|
|
|
678
679
|
/**
|
|
679
680
|
* Cookie accessor interface with utility methods
|
|
680
681
|
*/
|
|
682
|
+
/**
|
|
683
|
+
* `request.cookies`, in all three of the shapes it actually has.
|
|
684
|
+
*
|
|
685
|
+
* The server installs a hybrid: a function returning the parsed map, carrying
|
|
686
|
+
* the name→value entries as own properties, with `get`/`set`/`delete`/`getAll`
|
|
687
|
+
* on top. The declaration described only the last of those, so `req.cookies()`
|
|
688
|
+
* and `req.cookies.session` were both type errors against a runtime that
|
|
689
|
+
* supports them, and userland reached for `as any` to get at either.
|
|
690
|
+
*/
|
|
681
691
|
export interface CookieAccessor {
|
|
692
|
+
/** The whole parsed cookie map. */
|
|
693
|
+
(): Record<string, string>;
|
|
694
|
+
/** Direct access by name: `req.cookies.session`. */
|
|
695
|
+
[name: string]: unknown;
|
|
682
696
|
get: (name: string) => string | undefined;
|
|
683
697
|
set: (name: string, value: string, options?: CookieOptions) => void;
|
|
684
698
|
delete: (name: string, options?: CookieOptions) => void;
|
|
685
699
|
getAll: () => Record<string, string>;
|
|
686
700
|
}
|
|
687
|
-
export interface EnhancedRequest extends Request, Omit<RequestMacroMethods, '
|
|
701
|
+
export interface EnhancedRequest extends Request, Omit<RequestMacroMethods, 'cookies' | 'route'> {
|
|
688
702
|
/**
|
|
689
703
|
* Route parameters extracted from the URL
|
|
690
704
|
*/
|
|
@@ -761,13 +775,10 @@ export interface EnhancedRequest extends Request, Omit<RequestMacroMethods, 'ip'
|
|
|
761
775
|
*/
|
|
762
776
|
requestId?: string;
|
|
763
777
|
/**
|
|
764
|
-
*
|
|
765
|
-
|
|
766
|
-
ip?: string;
|
|
767
|
-
/**
|
|
768
|
-
* Cookies parsed from the request with utility methods
|
|
778
|
+
* Cookies parsed from the request. Callable, indexable, and carrying
|
|
779
|
+
* get/set/delete/getAll - see {@link CookieAccessor}.
|
|
769
780
|
*/
|
|
770
|
-
cookies
|
|
781
|
+
cookies: CookieAccessor;
|
|
771
782
|
/**
|
|
772
783
|
* Flash messages (temporary messages for the next request)
|
|
773
784
|
*/
|
|
@@ -833,11 +844,23 @@ export interface ActionHandlerClass {
|
|
|
833
844
|
handle: (request: EnhancedRequest) => Promise<Response>;
|
|
834
845
|
}
|
|
835
846
|
/**
|
|
836
|
-
* Action handler path
|
|
847
|
+
* Action handler path.
|
|
848
|
+
*
|
|
849
|
+
* The real set of actions when the application declares one through
|
|
850
|
+
* {@link RouterTypeRegistry}, and the shape of an action path otherwise — which
|
|
851
|
+
* catches `'Acton/CreateUser'` and nothing else. See `./types/registry.ts`.
|
|
837
852
|
*/
|
|
838
|
-
export type ActionPath =
|
|
853
|
+
export type ActionPath = KnownActionPath;
|
|
839
854
|
/**
|
|
840
|
-
* Strongly typed action handler
|
|
855
|
+
* Strongly typed action handler: every form a route will accept.
|
|
856
|
+
*
|
|
857
|
+
* Note that the route-registering methods do NOT take this as a single
|
|
858
|
+
* parameter. A parameter typed as a union containing two call signatures gives
|
|
859
|
+
* TypeScript nothing to contextually type an inline arrow with, so
|
|
860
|
+
* `router.get('/users/{id}', req => …)` left `req` implicitly `any` - the exact
|
|
861
|
+
* opposite of what the path generic exists for. They declare
|
|
862
|
+
* {@link TypedRouteHandler} as a first overload and this as the fallback, which
|
|
863
|
+
* types the inline case and keeps every other form working.
|
|
841
864
|
*/
|
|
842
865
|
export type ActionHandler<TPath extends string = string> = ActionPath | TypedRouteHandler<TPath> | RouteHandler | (new () => ActionHandlerClass) | Response;
|
|
843
866
|
/**
|
|
@@ -935,7 +958,7 @@ export interface AuthMiddlewareConfig {
|
|
|
935
958
|
}
|
|
936
959
|
export interface RouteGroup {
|
|
937
960
|
prefix?: string;
|
|
938
|
-
middleware?: (
|
|
961
|
+
middleware?: (MiddlewareReference | MiddlewareHandler)[];
|
|
939
962
|
}
|
|
940
963
|
export interface Route {
|
|
941
964
|
path: string;
|
|
@@ -1112,7 +1135,21 @@ export type SecurityHeader = 'Content-Security-Policy' | 'X-Frame-Options' | 'X-
|
|
|
1112
1135
|
/**
|
|
1113
1136
|
* Built-in middleware names - extremely narrow
|
|
1114
1137
|
*/
|
|
1115
|
-
|
|
1138
|
+
/**
|
|
1139
|
+
* The middleware this package ships, by the name it is actually registered
|
|
1140
|
+
* under.
|
|
1141
|
+
*
|
|
1142
|
+
* This list used to be a guess and disagreed with the package in several
|
|
1143
|
+
* places: it offered `json`, `rateLimiter`, `requestId`, `compress`, `static`
|
|
1144
|
+
* and `logger`, none of which exist, while the real `json_body`, `rate_limit`,
|
|
1145
|
+
* `request_id`, `security` and `ddos_protection` - the ones the default
|
|
1146
|
+
* middleware groups in `config.ts` are built from - were missing. Anything
|
|
1147
|
+
* typed against it was being checked against fiction.
|
|
1148
|
+
*
|
|
1149
|
+
* Every name here is a module under `src/middleware/`, plus the three aliases
|
|
1150
|
+
* `Router` registers itself.
|
|
1151
|
+
*/
|
|
1152
|
+
export type BuiltInMiddleware = 'auth' | 'content_security_policy' | 'cors' | 'csrf' | 'ddos_protection' | 'file_security' | 'file_upload' | 'helmet' | 'input_validation' | 'json_body' | 'performance_alerting' | 'performance_dashboard' | 'performance_monitor' | 'rate_limit' | 'request_id' | 'request_signing' | 'request_tracer' | 'response_cache' | 'security' | 'security_suite' | 'session' | 'throttle';
|
|
1116
1153
|
/**
|
|
1117
1154
|
* Middleware with parameters
|
|
1118
1155
|
*/
|
|
@@ -1175,10 +1212,19 @@ export type ContentType = 'application/json' | 'application/xml' | 'text/html' |
|
|
|
1175
1212
|
/**
|
|
1176
1213
|
* Strongly typed route method signatures
|
|
1177
1214
|
*/
|
|
1215
|
+
/**
|
|
1216
|
+
* A request whose `params` are exactly the ones its path declares.
|
|
1217
|
+
*
|
|
1218
|
+
* `Omit` first, because `EnhancedRequest.params` is `Record<string, string>`
|
|
1219
|
+
* and an intersection with it widens the narrowed keyset straight back:
|
|
1220
|
+
* `req.params.slugTypo` type-checked happily and returned `undefined` at
|
|
1221
|
+
* runtime, which is the failure a typed router exists to prevent.
|
|
1222
|
+
*/
|
|
1223
|
+
export type RequestFor<TPath extends string> = Omit<EnhancedRequest, 'params'> & {
|
|
1224
|
+
params: ExtractRouteParams<TPath>;
|
|
1225
|
+
};
|
|
1178
1226
|
export interface TypedRouteHandler<TPath extends string> {
|
|
1179
|
-
(req:
|
|
1180
|
-
params: ExtractRouteParams<TPath>;
|
|
1181
|
-
}): Response | Promise<Response>;
|
|
1227
|
+
(req: RequestFor<TPath>): Response | Promise<Response>;
|
|
1182
1228
|
}
|
|
1183
1229
|
/**
|
|
1184
1230
|
* Strongly typed route definition for specific HTTP methods
|
|
@@ -1860,3 +1906,4 @@ export interface EnhancedLaravelModelBindingMethods {
|
|
|
1860
1906
|
*/
|
|
1861
1907
|
scopedBindings: <Parent extends string, Child extends string>(bindings: Record<Child, Parent>) => MiddlewareHandler;
|
|
1862
1908
|
}
|
|
1909
|
+
export type { FromRegistry, KnownActionPath, KnownMiddlewareName, KnownRouteName, KnownRoutes, MiddlewareReference, PathForRouteName, RouterTypeRegistry, } from './types/registry';
|
package/dist/url.d.ts
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
* resolves a registered name into a URL with path parameters substituted
|
|
7
7
|
* and unmatched params appended as a query string.
|
|
8
8
|
*/
|
|
9
|
+
import type { ExtractRouteParams } from './types';
|
|
10
|
+
import type { KnownRouteName, PathForRouteName } from './types/registry';
|
|
9
11
|
/**
|
|
10
12
|
* Register a named-route path. Called by Router/FluentRouter when a route
|
|
11
13
|
* gets a `.name()`.
|
|
@@ -25,6 +27,36 @@ export declare function getNamedRoutes(): ReadonlyMap<string, string>;
|
|
|
25
27
|
* Clear the registry. Provided for tests; production code should not need it.
|
|
26
28
|
*/
|
|
27
29
|
export declare function clearNamedRoutes(): void;
|
|
30
|
+
/**
|
|
31
|
+
* The params a named route needs, plus anything else you want in the query.
|
|
32
|
+
*
|
|
33
|
+
* The required half comes from the path the name resolves to, so
|
|
34
|
+
* `url('users.show')` with no `id` is a compile error once the application has
|
|
35
|
+
* declared its routes. Extra keys stay allowed on purpose: the implementation
|
|
36
|
+
* appends whatever it did not consume as a query string, which is a real
|
|
37
|
+
* feature and not a mistake to be typed away.
|
|
38
|
+
*/
|
|
39
|
+
export type UrlParams<TName extends string> = ParamValues<ExtractRouteParams<PathForRouteName<TName>>> & Record<string, string | number | boolean>;
|
|
40
|
+
/**
|
|
41
|
+
* Path params are strings on the wire, but `url()` stringifies whatever it is
|
|
42
|
+
* given - so `url('users.show', { id: 42 })` is correct and should type as
|
|
43
|
+
* correct. Homomorphic, so an optional `{slug?}` stays optional.
|
|
44
|
+
*/
|
|
45
|
+
type ParamValues<TParams> = {
|
|
46
|
+
[K in keyof TParams]: string | number | boolean;
|
|
47
|
+
};
|
|
48
|
+
/** The keys of `T` that are not optional. */
|
|
49
|
+
type RequiredKeys<T> = {
|
|
50
|
+
[K in keyof T]-?: object extends Pick<T, K> ? never : K;
|
|
51
|
+
}[keyof T];
|
|
52
|
+
/**
|
|
53
|
+
* `url()` takes no second argument when the route needs no parameters.
|
|
54
|
+
*
|
|
55
|
+
* Keyed on REQUIRED params, not all of them: a path whose only parameter is
|
|
56
|
+
* optional (`/posts/{slug?}`) is reachable with nothing at all, and demanding
|
|
57
|
+
* an empty object for it would be the type getting in the way of the truth.
|
|
58
|
+
*/
|
|
59
|
+
type RequiresParams<TName extends string> = [RequiredKeys<ExtractRouteParams<PathForRouteName<TName>>>] extends [never] ? false : true;
|
|
28
60
|
export interface UrlOptions {
|
|
29
61
|
/**
|
|
30
62
|
* Return a fully-qualified URL using `process.env.APP_URL` (falling back to
|
|
@@ -53,4 +85,5 @@ export interface UrlOptions {
|
|
|
53
85
|
* // → 'https://app.example/users/42' (when APP_URL=https://app.example)
|
|
54
86
|
* ```
|
|
55
87
|
*/
|
|
56
|
-
export declare function url(name:
|
|
88
|
+
export declare function url<TName extends KnownRouteName>(name: TName, ...rest: RequiresParams<TName> extends true ? [params: UrlParams<TName>, options?: UrlOptions] : [params?: UrlParams<TName>, options?: UrlOptions]): string;
|
|
89
|
+
export {};
|