alien-middleware 0.9.0 → 0.10.0
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/{chunk-OH7VM54L.js → chunk-BBMRIHZ5.js} +37 -34
- package/dist/{index-vRvlpUCy.d.ts → index-B3m3gxNA.d.ts} +78 -40
- package/dist/index.d.ts +1 -2
- package/dist/index.js +5 -3
- package/dist/router.d.ts +2 -3
- package/dist/router.js +1 -1
- package/package.json +1 -1
- package/readme.md +35 -18
|
@@ -21,19 +21,25 @@ function defineParsedURL(context) {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
// src/middleware/filterPlatform.ts
|
|
25
|
+
function filterPlatform(name) {
|
|
26
|
+
return function(ctx) {
|
|
27
|
+
if (ctx.platform.name !== name) {
|
|
28
|
+
return ctx.passThrough();
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
24
34
|
// src/index.ts
|
|
25
35
|
var kRequestChain = Symbol("requestChain");
|
|
26
|
-
var kResponseChain = Symbol("responseChain");
|
|
27
36
|
var kIgnoreNotFound = Symbol("ignoreNotFound");
|
|
28
37
|
var kMiddlewareCache = Symbol("middlewareCache");
|
|
29
38
|
var kResponseHeaders = Symbol("responseHeaders");
|
|
30
39
|
var MiddlewareChain = class _MiddlewareChain {
|
|
31
40
|
[kRequestChain] = [];
|
|
32
|
-
[kResponseChain] = [];
|
|
33
41
|
/**
|
|
34
|
-
*
|
|
35
|
-
* treated as a response middleware. Otherwise, it will be treated as a
|
|
36
|
-
* request middleware.
|
|
42
|
+
* Add a request middleware to the end of the chain.
|
|
37
43
|
*
|
|
38
44
|
* If a middleware chain is given, its middlewares will be executed after any
|
|
39
45
|
* existing middlewares in this chain.
|
|
@@ -41,20 +47,9 @@ var MiddlewareChain = class _MiddlewareChain {
|
|
|
41
47
|
* @returns a new `MiddlewareChain` instance
|
|
42
48
|
*/
|
|
43
49
|
use(middleware) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
[...this[kResponseChain], ...middleware[kResponseChain]]
|
|
48
|
-
);
|
|
49
|
-
}
|
|
50
|
-
let requestChain = this[kRequestChain];
|
|
51
|
-
let responseChain = this[kResponseChain];
|
|
52
|
-
if (middleware.length < 2) {
|
|
53
|
-
requestChain = [...requestChain, middleware];
|
|
54
|
-
} else {
|
|
55
|
-
responseChain = [...responseChain, middleware];
|
|
56
|
-
}
|
|
57
|
-
return createHandler(requestChain, responseChain);
|
|
50
|
+
return createHandler(
|
|
51
|
+
middleware instanceof _MiddlewareChain ? [...this[kRequestChain], ...middleware[kRequestChain]] : [...this[kRequestChain], middleware]
|
|
52
|
+
);
|
|
58
53
|
}
|
|
59
54
|
/**
|
|
60
55
|
* Create a middleware function that encapsulates this middleware chain, so
|
|
@@ -64,7 +59,7 @@ var MiddlewareChain = class _MiddlewareChain {
|
|
|
64
59
|
return isFunction(this) ? (ctx) => this(ctx) : noop;
|
|
65
60
|
}
|
|
66
61
|
};
|
|
67
|
-
function createHandler(requestChain
|
|
62
|
+
function createHandler(requestChain) {
|
|
68
63
|
async function handler(parentContext) {
|
|
69
64
|
const context = Object.create(parentContext);
|
|
70
65
|
context[kIgnoreNotFound] = true;
|
|
@@ -80,7 +75,13 @@ function createHandler(requestChain, responseChain) {
|
|
|
80
75
|
}
|
|
81
76
|
context[kResponseHeaders].set(name, value);
|
|
82
77
|
};
|
|
83
|
-
const
|
|
78
|
+
const responseChain = [];
|
|
79
|
+
context.onResponse = (callback) => {
|
|
80
|
+
responseChain.push(callback);
|
|
81
|
+
};
|
|
82
|
+
const cache = context[kMiddlewareCache] = new Set(
|
|
83
|
+
parentContext[kMiddlewareCache]
|
|
84
|
+
);
|
|
84
85
|
let response;
|
|
85
86
|
let env;
|
|
86
87
|
for (const middleware of requestChain) {
|
|
@@ -102,11 +103,17 @@ function createHandler(requestChain, responseChain) {
|
|
|
102
103
|
}
|
|
103
104
|
for (const key in result) {
|
|
104
105
|
if (key === "env") {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
106
|
+
if (result.env) {
|
|
107
|
+
env ||= createExtendedEnv(context);
|
|
108
|
+
Object.defineProperties(
|
|
109
|
+
env,
|
|
110
|
+
Object.getOwnPropertyDescriptors(result.env)
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
} else if (key === "onResponse") {
|
|
114
|
+
if (result.onResponse) {
|
|
115
|
+
responseChain.push(result.onResponse);
|
|
116
|
+
}
|
|
110
117
|
} else {
|
|
111
118
|
const descriptor = Object.getOwnPropertyDescriptor(result, key);
|
|
112
119
|
descriptor.configurable = false;
|
|
@@ -131,16 +138,12 @@ function createHandler(requestChain, responseChain) {
|
|
|
131
138
|
response.headers.set(name, value);
|
|
132
139
|
});
|
|
133
140
|
context.setHeader = null;
|
|
134
|
-
for (const
|
|
135
|
-
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
cache.add(middleware);
|
|
139
|
-
let result = middleware(context, response);
|
|
141
|
+
for (const plugin of responseChain) {
|
|
142
|
+
let result = plugin(response);
|
|
140
143
|
if (result instanceof Promise) {
|
|
141
144
|
result = await result;
|
|
142
145
|
}
|
|
143
|
-
if (result
|
|
146
|
+
if (result) {
|
|
144
147
|
response = result;
|
|
145
148
|
continue;
|
|
146
149
|
}
|
|
@@ -149,7 +152,6 @@ function createHandler(requestChain, responseChain) {
|
|
|
149
152
|
}
|
|
150
153
|
Object.setPrototypeOf(handler, MiddlewareChain.prototype);
|
|
151
154
|
handler[kRequestChain] = requestChain;
|
|
152
|
-
handler[kResponseChain] = responseChain;
|
|
153
155
|
return handler;
|
|
154
156
|
}
|
|
155
157
|
function createExtendedEnv(context) {
|
|
@@ -170,6 +172,7 @@ export {
|
|
|
170
172
|
isArray,
|
|
171
173
|
isFunction,
|
|
172
174
|
defineParsedURL,
|
|
175
|
+
filterPlatform,
|
|
173
176
|
MiddlewareChain,
|
|
174
177
|
chain
|
|
175
178
|
};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Any, noop } from 'radashi';
|
|
2
1
|
import { AdapterRequestContext, HattipHandler } from '@hattip/core';
|
|
3
2
|
import { InferParams } from 'pathic';
|
|
4
3
|
|
|
@@ -7,10 +6,7 @@ type Eval<T> = {} & {
|
|
|
7
6
|
};
|
|
8
7
|
type Awaitable<T> = T | Promise<T>;
|
|
9
8
|
type OneOrMany<T> = T | readonly T[];
|
|
10
|
-
|
|
11
|
-
* Converts a type `T` to something that can be intersected with an object.
|
|
12
|
-
*/
|
|
13
|
-
type Intersectable<T extends object> = [T] extends [never] ? {} : [T] extends [Any] ? Record<PropertyKey, any> : T;
|
|
9
|
+
type CastNever<T, U> = [T] extends [never] ? U : T;
|
|
14
10
|
|
|
15
11
|
type Keys<T> = T extends any ? keyof T : never;
|
|
16
12
|
type IsOptional<T, K> = K extends keyof T ? T[K] extends Required<T>[K] ? false : true : true;
|
|
@@ -21,16 +17,20 @@ type MergeProperty<TSource, TOverrides, K> = (K extends keyof TOverrides ? Possi
|
|
|
21
17
|
*
|
|
22
18
|
* **FIXME:** Optional properties resolve as `foo: Foo | undefined` instead of `foo?: Foo`.
|
|
23
19
|
*/
|
|
24
|
-
type Merge<TSource extends object, TOverrides extends object | undefined> = Eval<Omit<TSource, Keys<TOverrides>> & {
|
|
25
|
-
[K in Keys<TOverrides>]: TOverrides extends any ? MergeProperty<TSource, TOverrides, K> : never;
|
|
20
|
+
type Merge<TSource extends object, TOverrides extends object | undefined> = Eval<Omit<CastNever<TSource, {}>, Keys<TOverrides>> & {
|
|
21
|
+
[K in Keys<TOverrides>]: TOverrides extends any ? MergeProperty<CastNever<TSource, {}>, TOverrides, K> : never;
|
|
26
22
|
}>;
|
|
27
23
|
|
|
28
|
-
type
|
|
24
|
+
type ReservedProperties = {
|
|
29
25
|
/**
|
|
30
26
|
* Add type-safe environment variables. These are accessed with the `env()`
|
|
31
27
|
* method on the request context.
|
|
32
28
|
*/
|
|
33
29
|
env?: object;
|
|
30
|
+
/**
|
|
31
|
+
* Intercept the response before it's sent to the client.
|
|
32
|
+
*/
|
|
33
|
+
onResponse?: ResponseCallback;
|
|
34
34
|
};
|
|
35
35
|
/**
|
|
36
36
|
* The object returned by a request middleware that is merged into the request
|
|
@@ -40,7 +40,7 @@ type RequestEnvPlugin = {
|
|
|
40
40
|
* May contain special properties:
|
|
41
41
|
* - `env`: Add type-safe environment variables.
|
|
42
42
|
*/
|
|
43
|
-
type RequestPlugin = Record<string, unknown> &
|
|
43
|
+
type RequestPlugin = Record<string, unknown> & ReservedProperties;
|
|
44
44
|
type MiddlewareTypes = {
|
|
45
45
|
/** Values expected by the start of the chain. */
|
|
46
46
|
initial: {
|
|
@@ -67,27 +67,38 @@ type AnyMiddlewareTypes = {
|
|
|
67
67
|
platform: any;
|
|
68
68
|
};
|
|
69
69
|
type AnyMiddlewareChain = MiddlewareChain<AnyMiddlewareTypes>;
|
|
70
|
-
type Inputs<T extends AnyMiddlewareChain> = T['$']['initial'];
|
|
70
|
+
type Inputs<T extends AnyMiddlewareChain> = T['$MiddlewareChain']['initial'];
|
|
71
71
|
type InputProperties<T extends AnyMiddlewareChain> = Inputs<T>['properties'];
|
|
72
72
|
type InputEnv<T extends AnyMiddlewareChain> = Inputs<T>['env'];
|
|
73
|
-
type Current<T extends AnyMiddlewareChain> = T['$']['current'];
|
|
73
|
+
type Current<T extends AnyMiddlewareChain> = T['$MiddlewareChain']['current'];
|
|
74
74
|
type Properties<T extends AnyMiddlewareChain> = Current<T>['properties'];
|
|
75
75
|
type Env<T extends AnyMiddlewareChain> = Current<T>['env'];
|
|
76
|
-
type Platform<T extends AnyMiddlewareChain> = T['$']['platform'];
|
|
76
|
+
type Platform<T extends AnyMiddlewareChain> = T['$MiddlewareChain']['platform'];
|
|
77
|
+
/**
|
|
78
|
+
* The `context.env` method used to access environment variables.
|
|
79
|
+
*/
|
|
80
|
+
type EnvAccessor<TEnv extends object> = {
|
|
81
|
+
<K extends keyof TEnv>(key: Extract<K, string>): TEnv[K];
|
|
82
|
+
(key: never): string | undefined;
|
|
83
|
+
};
|
|
77
84
|
interface HattipContext<TPlatform, TEnv extends object> extends AdapterRequestContext<TPlatform> {
|
|
85
|
+
env: EnvAccessor<TEnv>;
|
|
86
|
+
passThrough(): never;
|
|
78
87
|
/**
|
|
79
88
|
* The `request.url` string parsed into a `URL` object. Parsing is performed
|
|
80
89
|
* on-demand and the result is cached.
|
|
81
90
|
*/
|
|
82
91
|
url: URL;
|
|
83
|
-
env<K extends keyof TEnv>(key: Extract<K, string>): TEnv[K];
|
|
84
|
-
env(key: never): string | undefined;
|
|
85
92
|
/**
|
|
86
93
|
* Set a response header from a request middleware.
|
|
87
94
|
*
|
|
88
95
|
* Response middlewares should use `response.headers.set()` instead.
|
|
89
96
|
*/
|
|
90
97
|
setHeader(name: string, value: string): void;
|
|
98
|
+
/**
|
|
99
|
+
* Add a callback to be called when a response is generated.
|
|
100
|
+
*/
|
|
101
|
+
onResponse(callback: ResponseCallback): void;
|
|
91
102
|
}
|
|
92
103
|
/**
|
|
93
104
|
* An extensible Hattip context object.
|
|
@@ -96,7 +107,7 @@ interface HattipContext<TPlatform, TEnv extends object> extends AdapterRequestCo
|
|
|
96
107
|
* should prefer `RequestContext<any>` over `RequestContext` (no type
|
|
97
108
|
* parameters), as the default type is stricter.
|
|
98
109
|
*/
|
|
99
|
-
type RequestContext<TEnv extends object = any, TProperties extends object = never, TPlatform = any> = HattipContext<TPlatform, TEnv> &
|
|
110
|
+
type RequestContext<TEnv extends object = any, TProperties extends object = never, TPlatform = any> = HattipContext<TPlatform, TEnv> & CastNever<TProperties, unknown>;
|
|
100
111
|
/**
|
|
101
112
|
* Extract a `RequestContext` type from a `MiddlewareChain` type.
|
|
102
113
|
*
|
|
@@ -105,7 +116,7 @@ type RequestContext<TEnv extends object = any, TProperties extends object = neve
|
|
|
105
116
|
type MiddlewareContext<T extends MiddlewareChain> = [T] extends [never] ? RequestContext<{}, never, unknown> : RequestContext<Env<T>, Properties<T>, Platform<T>>;
|
|
106
117
|
type IsolatedContext<T extends MiddlewareChain> = RequestContext<InputProperties<T>, InputEnv<T>, Platform<T>>;
|
|
107
118
|
type RequestMiddleware<T extends MiddlewareChain = MiddlewareChain> = (context: RequestContext<InputProperties<T>, InputEnv<T>, Platform<T>>) => Awaitable<Response | RequestPlugin | void>;
|
|
108
|
-
type
|
|
119
|
+
type ResponseCallback = (response: Response) => Awaitable<Response | void>;
|
|
109
120
|
interface RequestHandler<T extends MiddlewareTypes = any> extends HattipHandler<T['platform']>, MiddlewareChain<T> {
|
|
110
121
|
}
|
|
111
122
|
/**
|
|
@@ -115,11 +126,20 @@ interface RequestHandler<T extends MiddlewareTypes = any> extends HattipHandler<
|
|
|
115
126
|
* response middleware. This means it will run *after* a `Response` is generated
|
|
116
127
|
* by a request middleware.
|
|
117
128
|
*/
|
|
118
|
-
type Middleware<TEnv extends object = any, TProperties extends object = any, TPlatform = any> =
|
|
129
|
+
type Middleware<TEnv extends object = any, TProperties extends object = any, TPlatform = any> = {
|
|
130
|
+
(context: RequestContext<TEnv, TProperties, TPlatform>): Awaitable<Response | RequestPlugin | void>;
|
|
131
|
+
/** This property won't exist at runtime. It contains type information for inference purposes. */
|
|
132
|
+
$Middleware?: MiddlewareTypes & {
|
|
133
|
+
initial: {
|
|
134
|
+
env: TEnv;
|
|
135
|
+
properties: TProperties;
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
};
|
|
119
139
|
/**
|
|
120
140
|
* Extract a `Middleware` type from a `MiddlewareChain` type.
|
|
121
141
|
*/
|
|
122
|
-
type ExtractMiddleware<T extends MiddlewareChain> = Middleware<Env<T>, Properties<T>, Platform<T>>;
|
|
142
|
+
type ExtractMiddleware<T extends MiddlewareChain> = [T] extends [never] ? Middleware<{}, {}, any> : Middleware<Env<T>, Properties<T>, Platform<T>>;
|
|
123
143
|
/**
|
|
124
144
|
* Merge a request plugin into a middleware chain.
|
|
125
145
|
*/
|
|
@@ -127,22 +147,38 @@ type ApplyMiddlewareResult<TParent extends MiddlewareChain, TResult> = Eval<{
|
|
|
127
147
|
env: Merge<Env<TParent>, TResult extends {
|
|
128
148
|
env: infer TEnv extends object | undefined;
|
|
129
149
|
} ? TEnv : undefined>;
|
|
130
|
-
properties: Merge<Properties<TParent>, TResult extends RequestPlugin ? Omit<TResult, keyof
|
|
150
|
+
properties: Merge<Properties<TParent>, TResult extends RequestPlugin ? Omit<TResult, keyof ReservedProperties> : undefined>;
|
|
131
151
|
}>;
|
|
152
|
+
type ApplyMiddlewareOutputs<TFirst extends MiddlewareChain, TSecond extends Middleware> = TSecond extends MiddlewareChain ? {
|
|
153
|
+
env: Merge<Env<TFirst>, Env<TSecond>>;
|
|
154
|
+
properties: Merge<Properties<TFirst>, Properties<TSecond>>;
|
|
155
|
+
} : TSecond extends (...args: any[]) => Awaitable<infer TResult> ? ApplyMiddlewareResult<TFirst, Exclude<TResult, Response>> : Current<TFirst>;
|
|
156
|
+
type MiddlewareInputs<T extends Middleware> = T extends Middleware<infer TEnv, infer TProperties> ? {
|
|
157
|
+
env: TEnv;
|
|
158
|
+
properties: TProperties;
|
|
159
|
+
} : never;
|
|
160
|
+
type MiddlewarePlatform<T extends Middleware> = T extends Middleware<any, any, infer TPlatform> ? TPlatform : never;
|
|
132
161
|
/**
|
|
133
162
|
* This applies a middleware to a chain. If the type `TMiddleware` is itself a
|
|
134
163
|
* chain, it's treated as a nested chain, which won't leak its plugins into the
|
|
135
164
|
* parent chain.
|
|
165
|
+
*
|
|
166
|
+
* The `TFirst` type is allowed to be `never`, which results in the middleware's
|
|
167
|
+
* output types being used as the request handler's input types.
|
|
136
168
|
*/
|
|
137
|
-
type ApplyMiddleware<TFirst extends MiddlewareChain, TSecond extends Middleware
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
initial: Inputs<TFirst>;
|
|
142
|
-
current: TOutputs;
|
|
143
|
-
platform: Platform<TFirst>;
|
|
169
|
+
type ApplyMiddleware<TFirst extends MiddlewareChain, TSecond extends Middleware> = ApplyMiddlewareOutputs<TFirst, TSecond> extends infer TCurrent extends MiddlewareTypes['current'] ? RequestHandler<{
|
|
170
|
+
initial: CastNever<Inputs<TFirst>, MiddlewareInputs<TSecond>>;
|
|
171
|
+
current: TCurrent;
|
|
172
|
+
platform: CastNever<Platform<TFirst>, MiddlewarePlatform<TSecond>>;
|
|
144
173
|
}> : never;
|
|
145
|
-
|
|
174
|
+
/**
|
|
175
|
+
* Apply a list of middlewares to an empty middleware chain.
|
|
176
|
+
*/
|
|
177
|
+
type ApplyMiddlewares<T extends Middleware[]> = T extends [
|
|
178
|
+
...infer TRest extends Middleware[],
|
|
179
|
+
infer TLast extends Middleware
|
|
180
|
+
] ? ApplyMiddleware<ApplyMiddlewares<TRest>, TLast> : ApplyFirstMiddleware<T[0]>;
|
|
181
|
+
type EmptyMiddlewareChain<TPlatform = unknown> = MiddlewareChain<{
|
|
146
182
|
initial: {
|
|
147
183
|
env: {};
|
|
148
184
|
properties: {};
|
|
@@ -151,18 +187,18 @@ type EmptyMiddlewareChain = MiddlewareChain<{
|
|
|
151
187
|
env: {};
|
|
152
188
|
properties: {};
|
|
153
189
|
};
|
|
154
|
-
platform:
|
|
190
|
+
platform: TPlatform;
|
|
155
191
|
}>;
|
|
156
|
-
type ApplyFirstMiddleware<T extends Middleware> = T extends MiddlewareChain ? T : ApplyMiddleware<EmptyMiddlewareChain
|
|
192
|
+
type ApplyFirstMiddleware<T extends Middleware> = T extends MiddlewareChain ? T : ApplyMiddleware<EmptyMiddlewareChain<MiddlewarePlatform<T>>, T>;
|
|
157
193
|
type RouteMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' | 'HEAD' | (string & {});
|
|
158
|
-
type RouteContext<T extends RouterTypes = any, TPathParams extends object = any, TMethod extends RouteMethod = RouteMethod> = MiddlewareContext<ApplyMiddleware<MiddlewareChain<T['$']>, () => {
|
|
194
|
+
type RouteContext<T extends RouterTypes = any, TPathParams extends object = any, TMethod extends RouteMethod = RouteMethod> = MiddlewareContext<ApplyMiddleware<MiddlewareChain<T['$Router']>, () => {
|
|
159
195
|
params: TPathParams;
|
|
160
196
|
method: TMethod;
|
|
161
197
|
}>>;
|
|
162
198
|
type RouteHandler<T extends RouterTypes = any, TPathParams extends object = any, TMethod extends RouteMethod = RouteMethod> = (context: RouteContext<T, TPathParams, TMethod>) => Awaitable<Response | void>;
|
|
163
199
|
declare class RouterTypes<T extends MiddlewareChain = any> extends Function {
|
|
164
200
|
/** This property won't exist at runtime. It contains type information for inference purposes. */
|
|
165
|
-
|
|
201
|
+
$Router: T['$MiddlewareChain'];
|
|
166
202
|
}
|
|
167
203
|
interface Router<T extends MiddlewareChain = any> extends RouterTypes<T> {
|
|
168
204
|
(context: AdapterRequestContext<Platform<T>>): Awaitable<void | Response>;
|
|
@@ -170,19 +206,21 @@ interface Router<T extends MiddlewareChain = any> extends RouterTypes<T> {
|
|
|
170
206
|
use<TPath extends string, TMethod extends RouteMethod = RouteMethod>(method: OneOrMany<TMethod> | '*', path: TPath, handler: RouteHandler<this, InferParams<TPath>, TMethod>): Router;
|
|
171
207
|
}
|
|
172
208
|
|
|
209
|
+
declare function filterPlatform<TPlatform extends {
|
|
210
|
+
name: string;
|
|
211
|
+
}>(name: TPlatform['name']): (ctx: RequestContext) => {
|
|
212
|
+
platform: TPlatform;
|
|
213
|
+
};
|
|
214
|
+
|
|
173
215
|
declare const kRequestChain: unique symbol;
|
|
174
|
-
declare const kResponseChain: unique symbol;
|
|
175
216
|
declare class MiddlewareChain<T extends MiddlewareTypes = any> {
|
|
176
217
|
/** This property won't exist at runtime. It contains type information for inference purposes. */
|
|
177
|
-
|
|
218
|
+
$MiddlewareChain: T;
|
|
178
219
|
/** The number of parameters when called as a function. */
|
|
179
220
|
readonly length: 1;
|
|
180
221
|
protected [kRequestChain]: RequestMiddleware[];
|
|
181
|
-
protected [kResponseChain]: ResponseMiddleware[];
|
|
182
222
|
/**
|
|
183
|
-
*
|
|
184
|
-
* treated as a response middleware. Otherwise, it will be treated as a
|
|
185
|
-
* request middleware.
|
|
223
|
+
* Add a request middleware to the end of the chain.
|
|
186
224
|
*
|
|
187
225
|
* If a middleware chain is given, its middlewares will be executed after any
|
|
188
226
|
* existing middlewares in this chain.
|
|
@@ -194,7 +232,7 @@ declare class MiddlewareChain<T extends MiddlewareTypes = any> {
|
|
|
194
232
|
* Create a middleware function that encapsulates this middleware chain, so
|
|
195
233
|
* any modifications it makes to the request context are not leaked.
|
|
196
234
|
*/
|
|
197
|
-
isolate(): (
|
|
235
|
+
isolate(): (ctx: IsolatedContext<this>) => Awaitable<Response | void>;
|
|
198
236
|
}
|
|
199
237
|
declare function chain<TEnv extends object = {}, TProperties extends object = {}, TPlatform = unknown>(): MiddlewareChain<{
|
|
200
238
|
initial: {
|
|
@@ -207,6 +245,6 @@ declare function chain<TEnv extends object = {}, TProperties extends object = {}
|
|
|
207
245
|
};
|
|
208
246
|
platform: TPlatform;
|
|
209
247
|
}>;
|
|
210
|
-
declare function chain<const T extends Middleware = Middleware
|
|
248
|
+
declare function chain<const T extends Middleware = Middleware<{}, {}, unknown>>(middleware: T): ApplyFirstMiddleware<T>;
|
|
211
249
|
|
|
212
|
-
export { type ApplyMiddleware as A, type EmptyMiddlewareChain as E, type MiddlewareContext as M, type Router as R, MiddlewareChain as a, type RouteContext as b, type RouteHandler as c, chain as d, type
|
|
250
|
+
export { type ApplyMiddleware as A, type EmptyMiddlewareChain as E, type MiddlewareContext as M, type Router as R, MiddlewareChain as a, type RouteContext as b, type RouteHandler as c, chain as d, type ApplyMiddlewares as e, filterPlatform as f, type EnvAccessor as g, type ExtractMiddleware as h, type Middleware as i, type RequestContext as j, type RequestHandler as k, type RequestMiddleware as l, type RequestPlugin as m, type ResponseCallback as n };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
export { A as ApplyMiddleware, e as ExtractMiddleware, f as Middleware, a as MiddlewareChain, M as MiddlewareContext, g as RequestContext, h as RequestHandler, i as RequestMiddleware, j as RequestPlugin, k as ResponseMiddleware, d as chain } from './index-vRvlpUCy.js';
|
|
1
|
+
export { A as ApplyMiddleware, e as ApplyMiddlewares, g as EnvAccessor, h as ExtractMiddleware, i as Middleware, a as MiddlewareChain, M as MiddlewareContext, j as RequestContext, k as RequestHandler, l as RequestMiddleware, m as RequestPlugin, n as ResponseCallback, d as chain, f as filterPlatform } from './index-B3m3gxNA.js';
|
|
3
2
|
import '@hattip/core';
|
|
4
3
|
import 'pathic';
|
package/dist/index.js
CHANGED
package/dist/router.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { R as Router, M as MiddlewareContext, a as MiddlewareChain, E as EmptyMiddlewareChain } from './index-
|
|
2
|
-
export { b as RouteContext, c as RouteHandler } from './index-
|
|
3
|
-
import 'radashi';
|
|
1
|
+
import { R as Router, M as MiddlewareContext, a as MiddlewareChain, E as EmptyMiddlewareChain } from './index-B3m3gxNA.js';
|
|
2
|
+
export { b as RouteContext, c as RouteHandler } from './index-B3m3gxNA.js';
|
|
4
3
|
import '@hattip/core';
|
|
5
4
|
import 'pathic';
|
|
6
5
|
|
package/dist/router.js
CHANGED
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -4,9 +4,17 @@ Reusable middleware chains with top-notch TypeScript support. Built upon [Hattip
|
|
|
4
4
|
|
|
5
5
|
## Philosophy
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
alien-middleware is built on a few key principles:
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
1. **Best-in-class TypeScript support** - Type safety is a first-class citizen. The library provides strong type inference for middleware chains, context extensions, and environment variables.
|
|
10
|
+
|
|
11
|
+
2. **Web Standards** - Built on standard Web APIs like `Request` and `Response`, allowing you to write idiomatic code that follows established patterns and conventions.
|
|
12
|
+
|
|
13
|
+
3. **No vendor lock-in** - Thanks to Hattip's adapter system, your middleware can run anywhere: Node.js, Deno, Bun, Cloudflare Workers, and more.
|
|
14
|
+
|
|
15
|
+
4. **Linear middleware flow** - Unlike Express-style middleware, there's no `next()` function to call. Middleware either returns a `Response` (ending the chain) or doesn't (continuing to the next middleware). This makes the flow easier to reason about and eliminates common bugs like forgetting to call `next()`.
|
|
16
|
+
|
|
17
|
+
5. **Immutable chains** - Middleware chains are immutable, making them easier to compose, extend, and reason about.
|
|
10
18
|
|
|
11
19
|
## Quick Start
|
|
12
20
|
|
|
@@ -34,7 +42,7 @@ const appWithInitial = chain(context => {
|
|
|
34
42
|
|
|
35
43
|
### Adding Middleware with `.use()`
|
|
36
44
|
|
|
37
|
-
Use the `.use()` method to add middleware functions to the chain.
|
|
45
|
+
Use the `.use()` method to add middleware functions to the chain.
|
|
38
46
|
|
|
39
47
|
```typescript
|
|
40
48
|
import type { RequestContext } from 'alien-middleware'
|
|
@@ -156,35 +164,44 @@ Request middleware runs sequentially before a `Response` is generated.
|
|
|
156
164
|
> `.use(…)` call expression, since that requires you to unnecessarily declare
|
|
157
165
|
> the type of the context object. It's better to define them inline.
|
|
158
166
|
|
|
159
|
-
### Response
|
|
167
|
+
### Response Callbacks
|
|
168
|
+
|
|
169
|
+
Request middleware can register a _response callback_ to receive the `Response` object. This is done by either returning an `onResponse` method or by calling `context.onResponse(callback)`. Response callbacks may return a new `Response` object.
|
|
160
170
|
|
|
161
|
-
Response
|
|
171
|
+
Response callbacks are called even if none of your middlewares generate a `Response`. In this case, they receive the default `404 Not Found` response. Note that [isolated middleware chains](#isolating-a-middleware-chain) are an exception to this rule, since a default response is not generated for them.
|
|
162
172
|
|
|
163
173
|
```typescript
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
174
|
+
// Approach 1: Returning an `onResponse` method
|
|
175
|
+
const poweredByMiddleware = (context: RequestContext) => ({
|
|
176
|
+
onResponse(response) {
|
|
177
|
+
response.headers.set('X-Powered-By', 'alien-middleware')
|
|
178
|
+
},
|
|
179
|
+
})
|
|
167
180
|
|
|
168
181
|
const mainHandler = (context: RequestContext) => {
|
|
182
|
+
// Approach 2: Calling `context.onResponse(callback)`
|
|
183
|
+
context.onResponse(response => {
|
|
184
|
+
assert(response instanceof Response)
|
|
185
|
+
})
|
|
186
|
+
|
|
169
187
|
return new Response('Main content')
|
|
170
188
|
}
|
|
171
189
|
|
|
172
|
-
|
|
173
|
-
const app = chain().use(mainHandler).use(poweredByMiddleware)
|
|
190
|
+
const app = chain().use(poweredByMiddleware).use(mainHandler)
|
|
174
191
|
|
|
175
192
|
const response = await app({…})
|
|
176
193
|
console.log(response.headers.get('X-Powered-By')) // Output: alien-middleware
|
|
177
194
|
```
|
|
178
195
|
|
|
179
196
|
> [!NOTE]
|
|
180
|
-
>
|
|
181
|
-
>
|
|
182
|
-
>
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
`Response
|
|
197
|
+
> Remember that request middlewares may not be called if a previous middleware
|
|
198
|
+
> returns a `Response`. In that case, any response callbacks added by the
|
|
199
|
+
> uncalled middleware will not be executed. Therefore, order your middlewares
|
|
200
|
+
> carefully.
|
|
201
|
+
|
|
202
|
+
#### Modifying Response Headers
|
|
203
|
+
|
|
204
|
+
Even if a middleware returns an immutable `Response` (e.g. from a `fetch()` call), your _response callback_ can still modify the headers. We make sure to clone the response before processing it with any response callbacks.
|
|
188
205
|
|
|
189
206
|
### Merging a Middleware Chain
|
|
190
207
|
|