alien-middleware 0.6.1 → 0.7.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.
@@ -1,3 +1,11 @@
1
+ // node_modules/.pnpm/radashi@12.5.0-beta.6d5c035/node_modules/radashi/dist/radashi.js
2
+ function noop() {
3
+ }
4
+ var isArray = /* @__PURE__ */ (() => Array.isArray)();
5
+ function isFunction(value) {
6
+ return typeof value === "function";
7
+ }
8
+
1
9
  // src/url.ts
2
10
  var urlDescriptor = {
3
11
  configurable: true,
@@ -14,5 +22,8 @@ function defineParsedURL(context) {
14
22
  }
15
23
 
16
24
  export {
25
+ noop,
26
+ isArray,
27
+ isFunction,
17
28
  defineParsedURL
18
29
  };
@@ -1,5 +1,5 @@
1
+ import { Any, noop } from 'radashi';
1
2
  import { AdapterRequestContext, HattipHandler } from '@hattip/core';
2
- import { Any } from 'radashi';
3
3
 
4
4
  type RequestEnvPlugin = {
5
5
  /**
@@ -55,6 +55,7 @@ type RequestContext<TProperties extends object = never, TEnv extends object = an
55
55
  * When type `T` is `never`, a default context is returned.
56
56
  */
57
57
  type MiddlewareContext<T extends MiddlewareChain> = [T] extends [never] ? RequestContext<{}, {}, unknown> : RequestContext<Properties<T>, Env<T>, Platform<T>>;
58
+ type IsolatedContext<T extends MiddlewareChain> = RequestContext<InputProperties<T>, InputEnv<T>, Platform<T>>;
58
59
  type Awaitable<T> = T | Promise<T>;
59
60
  type RequestMiddleware<T extends MiddlewareChain = MiddlewareChain> = (context: RequestContext<InputProperties<T>, InputEnv<T>, Platform<T>>) => Awaitable<Response | RequestPlugin | void>;
60
61
  type ResponseMiddleware<T extends MiddlewareChain = MiddlewareChain> = (context: RequestContext<InputProperties<T>, InputEnv<T>, Platform<T>>, response: Response) => Awaitable<Response | void>;
@@ -74,7 +75,7 @@ type ExtractMiddleware<T extends MiddlewareChain> = Middleware<Properties<T>, En
74
75
  type Merge<TSource extends object, TOverrides extends object | undefined> = {} & (TOverrides extends object ? {
75
76
  [K in keyof TSource | keyof TOverrides]: K extends keyof TOverrides ? TOverrides[K] : K extends keyof TSource ? TSource[K] : never;
76
77
  } : TSource);
77
- type ApplyRequestPlugin<TParent extends MiddlewareChain, TPlugin extends RequestPlugin> = {
78
+ type ApplyRequestPlugin<TParent extends MiddlewareChain, TPlugin extends RequestPlugin> = {} & {
78
79
  properties: Merge<Properties<TParent>, Omit<TPlugin, keyof RequestEnvPlugin>>;
79
80
  env: Merge<Env<TParent>, TPlugin['env']>;
80
81
  };
@@ -83,7 +84,10 @@ type ApplyRequestPlugin<TParent extends MiddlewareChain, TPlugin extends Request
83
84
  * chain, it's treated as a nested chain, which won't leak its plugins into the
84
85
  * parent chain.
85
86
  */
86
- type ApplyMiddleware<TParent extends MiddlewareChain, TMiddleware> = TMiddleware extends MiddlewareChain ? RequestHandler<Inputs<TParent>, Current<TParent>, Platform<TParent>> : TMiddleware extends () => Awaitable<infer TPlugin extends RequestPlugin> ? RequestHandler<Inputs<TParent>, ApplyRequestPlugin<TParent, TPlugin>, Platform<TParent>> : RequestHandler<Inputs<TParent>, Current<TParent>, Platform<TParent>>;
87
+ type ApplyMiddleware<TFirst extends MiddlewareChain, TSecond extends Middleware<Properties<TFirst>, Env<TFirst>, Platform<TFirst>>> = RequestHandler<Inputs<TFirst>, TSecond extends MiddlewareChain ? {
88
+ properties: Merge<Properties<TFirst>, Properties<TSecond>>;
89
+ env: Merge<Env<TFirst>, Env<TSecond>>;
90
+ } : TSecond extends () => Awaitable<infer TResult> ? TResult extends RequestPlugin ? ApplyRequestPlugin<TFirst, TResult> : Current<TFirst> : Current<TFirst>, Platform<TFirst>>;
87
91
  type EmptyMiddlewareChain = MiddlewareChain<{
88
92
  properties: {};
89
93
  env: {};
@@ -92,10 +96,6 @@ type EmptyMiddlewareChain = MiddlewareChain<{
92
96
  env: {};
93
97
  }, unknown>;
94
98
  type ApplyFirstMiddleware<T extends Middleware> = T extends MiddlewareChain ? T : ApplyMiddleware<EmptyMiddlewareChain, T>;
95
- type MergeMiddleware<TFirst extends MiddlewareChain, TSecond extends Middleware<Properties<TFirst>, Env<TFirst>, Platform<TFirst>>> = RequestHandler<Inputs<TFirst>, TSecond extends MiddlewareChain ? {
96
- properties: Merge<Properties<TFirst>, Properties<TSecond>>;
97
- env: Merge<Env<TFirst>, Env<TSecond>>;
98
- } : TSecond extends () => Awaitable<infer TPlugin extends RequestPlugin> ? ApplyRequestPlugin<TFirst, TPlugin> : Current<TFirst>, Platform<TFirst>>;
99
99
  type RouteMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' | 'HEAD';
100
100
  type RouterContext<T extends MiddlewareChain = any, TPathParams extends object = any, TMethod extends RouteMethod = RouteMethod> = MiddlewareContext<T> & {
101
101
  params: TPathParams;
@@ -127,18 +127,17 @@ TPlatform = any> {
127
127
  * treated as a response middleware. Otherwise, it will be treated as a
128
128
  * request middleware.
129
129
  *
130
+ * If a middleware chain is given, its middlewares will be executed after any
131
+ * existing middlewares in this chain.
132
+ *
130
133
  * @returns a new `MiddlewareChain` instance
131
134
  */
132
135
  use<const TMiddleware extends ExtractMiddleware<this>>(middleware: TMiddleware): ApplyMiddleware<this, TMiddleware>;
133
136
  /**
134
- * Merge two middleware chains. The middlewares from the second chain will be
135
- * executed after the middlewares from the first chain.
136
- *
137
- * For ease of use, this method may be given a middleware function, which
138
- * short-circuits to the `use` method. You should prefer using the `use`
139
- * method directly, if possible.
137
+ * Create a middleware function that encapsulates this middleware chain, so
138
+ * any modifications it makes to the request context are not leaked.
140
139
  */
141
- merge<TMiddleware extends ExtractMiddleware<this>>(chain: TMiddleware): MergeMiddleware<this, TMiddleware>;
140
+ isolate(): ((ctx: IsolatedContext<this>) => Promise<Response | void>) | typeof noop;
142
141
  }
143
142
  declare function chain<TProperties extends object = {}, TEnv extends object = {}, TPlatform = unknown>(): MiddlewareChain<{
144
143
  env: TEnv;
@@ -149,4 +148,4 @@ declare function chain<TProperties extends object = {}, TEnv extends object = {}
149
148
  }, TPlatform>;
150
149
  declare function chain<const T extends Middleware = Middleware>(middleware: T): ApplyFirstMiddleware<T>;
151
150
 
152
- export { type ApplyMiddleware as A, type EmptyMiddlewareChain as E, MiddlewareChain as M, type RouteHandler as R, type MiddlewareContext as a, type RouteMethod as b, type RouterContext as c, chain as d, type ExtractMiddleware as e, type MergeMiddleware as f, type Middleware as g, type RequestContext as h, type RequestHandler as i, type RequestMiddleware as j, type RequestPlugin as k, type ResponseMiddleware as l };
151
+ export { type ApplyMiddleware as A, type EmptyMiddlewareChain as E, MiddlewareChain as M, type RouteHandler as R, type MiddlewareContext as a, type RouteMethod as b, type RouterContext as c, chain as d, type ExtractMiddleware as e, type Middleware as f, type RequestContext as g, type RequestHandler as h, type RequestMiddleware as i, type RequestPlugin as j, type ResponseMiddleware as k };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { A as ApplyMiddleware, e as ExtractMiddleware, f as MergeMiddleware, g as Middleware, M as MiddlewareChain, a as MiddlewareContext, h as RequestContext, i as RequestHandler, j as RequestMiddleware, k as RequestPlugin, l as ResponseMiddleware, d as chain } from './index-CuxbHrbf.js';
2
- import '@hattip/core';
3
1
  import 'radashi';
2
+ export { A as ApplyMiddleware, e as ExtractMiddleware, f as Middleware, M as MiddlewareChain, a as MiddlewareContext, g as RequestContext, h as RequestHandler, i as RequestMiddleware, j as RequestPlugin, k as ResponseMiddleware, d as chain } from './index-DEMeaByn.js';
3
+ import '@hattip/core';
package/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import {
2
- defineParsedURL
3
- } from "./chunk-DEWCXAOT.js";
2
+ defineParsedURL,
3
+ isFunction,
4
+ noop
5
+ } from "./chunk-HZDENULC.js";
4
6
 
5
7
  // src/index.ts
6
8
  var kRequestChain = Symbol("requestChain");
@@ -15,9 +17,18 @@ var MiddlewareChain = class _MiddlewareChain {
15
17
  * treated as a response middleware. Otherwise, it will be treated as a
16
18
  * request middleware.
17
19
  *
20
+ * If a middleware chain is given, its middlewares will be executed after any
21
+ * existing middlewares in this chain.
22
+ *
18
23
  * @returns a new `MiddlewareChain` instance
19
24
  */
20
25
  use(middleware) {
26
+ if (middleware instanceof _MiddlewareChain) {
27
+ return createHandler(
28
+ [...this[kRequestChain], ...middleware[kRequestChain]],
29
+ [...this[kResponseChain], ...middleware[kResponseChain]]
30
+ );
31
+ }
21
32
  let requestChain = this[kRequestChain];
22
33
  let responseChain = this[kResponseChain];
23
34
  if (middleware.length < 2) {
@@ -28,21 +39,11 @@ var MiddlewareChain = class _MiddlewareChain {
28
39
  return createHandler(requestChain, responseChain);
29
40
  }
30
41
  /**
31
- * Merge two middleware chains. The middlewares from the second chain will be
32
- * executed after the middlewares from the first chain.
33
- *
34
- * For ease of use, this method may be given a middleware function, which
35
- * short-circuits to the `use` method. You should prefer using the `use`
36
- * method directly, if possible.
42
+ * Create a middleware function that encapsulates this middleware chain, so
43
+ * any modifications it makes to the request context are not leaked.
37
44
  */
38
- merge(chain2) {
39
- if (chain2 instanceof _MiddlewareChain) {
40
- return createHandler(
41
- [...this[kRequestChain], ...chain2[kRequestChain]],
42
- [...this[kResponseChain], ...chain2[kResponseChain]]
43
- );
44
- }
45
- return this.use(chain2);
45
+ isolate() {
46
+ return isFunction(this) ? (ctx) => this(ctx) : noop;
46
47
  }
47
48
  };
48
49
  function createHandler(requestChain, responseChain) {
@@ -50,6 +51,11 @@ function createHandler(requestChain, responseChain) {
50
51
  const context = Object.create(parentContext);
51
52
  context[kIgnoreNotFound] = true;
52
53
  defineParsedURL(context);
54
+ const { passThrough } = context;
55
+ let shouldPassThrough = false;
56
+ context.passThrough = () => {
57
+ shouldPassThrough = true;
58
+ };
53
59
  const cache = context[kMiddlewareCache] ||= /* @__PURE__ */ new Set();
54
60
  let response;
55
61
  let env;
@@ -62,6 +68,9 @@ function createHandler(requestChain, responseChain) {
62
68
  if (result instanceof Promise) {
63
69
  result = await result;
64
70
  }
71
+ if (shouldPassThrough) {
72
+ break;
73
+ }
65
74
  if (result) {
66
75
  if (result instanceof Response) {
67
76
  response = result;
@@ -87,6 +96,12 @@ function createHandler(requestChain, responseChain) {
87
96
  return;
88
97
  }
89
98
  response = new Response("Not Found", { status: 404 });
99
+ if (shouldPassThrough) {
100
+ passThrough();
101
+ return response;
102
+ }
103
+ } else if (response.type !== "default") {
104
+ response = new Response(response.body, response);
90
105
  }
91
106
  for (const middleware of responseChain) {
92
107
  if (cache.has(middleware)) {
@@ -119,8 +134,8 @@ function chain(middleware) {
119
134
  if (middleware instanceof MiddlewareChain) {
120
135
  return middleware;
121
136
  }
122
- const handler = new MiddlewareChain();
123
- return middleware ? handler.use(middleware) : handler;
137
+ const empty = new MiddlewareChain();
138
+ return middleware ? empty.use(middleware) : empty;
124
139
  }
125
140
  export {
126
141
  MiddlewareChain,
package/dist/router.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { InferParams } from 'pathic';
2
- import { M as MiddlewareChain, E as EmptyMiddlewareChain, a as MiddlewareContext, R as RouteHandler, b as RouteMethod } from './index-CuxbHrbf.js';
3
- export { c as RouterContext } from './index-CuxbHrbf.js';
4
- import '@hattip/core';
2
+ import { M as MiddlewareChain, E as EmptyMiddlewareChain, a as MiddlewareContext, R as RouteHandler, b as RouteMethod } from './index-DEMeaByn.js';
3
+ export { c as RouterContext } from './index-DEMeaByn.js';
5
4
  import 'radashi';
5
+ import '@hattip/core';
6
6
 
7
7
  type OneOrMany<T> = T | readonly T[];
8
8
  type Router<T extends MiddlewareChain = any> = ReturnType<typeof routes<T>>;
package/dist/router.js CHANGED
@@ -1,17 +1,11 @@
1
1
  import {
2
- defineParsedURL
3
- } from "./chunk-DEWCXAOT.js";
2
+ defineParsedURL,
3
+ isArray,
4
+ isFunction
5
+ } from "./chunk-HZDENULC.js";
4
6
 
5
7
  // src/router.ts
6
8
  import { compilePaths } from "pathic";
7
-
8
- // node_modules/.pnpm/radashi@12.5.0-beta.6d5c035/node_modules/radashi/dist/radashi.js
9
- var isArray = /* @__PURE__ */ (() => Array.isArray)();
10
- function isFunction(value) {
11
- return typeof value === "function";
12
- }
13
-
14
- // src/router.ts
15
9
  function routes(middlewares) {
16
10
  const paths = [];
17
11
  const filters = [];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "alien-middleware",
3
3
  "type": "module",
4
- "version": "0.6.1",
4
+ "version": "0.7.1",
5
5
  "exports": {
6
6
  ".": {
7
7
  "types": "./dist/index.d.ts",
@@ -36,7 +36,7 @@
36
36
  "tsc-lint": "^0.1.9",
37
37
  "tsup": "^8.4.0",
38
38
  "typescript": "^5.8.3",
39
- "vitest": "^3.1.2"
39
+ "vitest": "^3.1.3"
40
40
  },
41
41
  "dependencies": {
42
42
  "pathic": "^0.1.6"
package/readme.md CHANGED
@@ -186,45 +186,71 @@ request middlewares, **except** when the middleware chain is nested inside
186
186
  another chain, since the outer chain will still have a chance to return a
187
187
  `Response`.
188
188
 
189
- ### Nesting Chains
189
+ ### Merging a Middleware Chain
190
190
 
191
- You can compose middleware by nesting chains using `.use()`. _Request plugins_ within a nested chain are scoped to that chain and do not affect middleware outside of it.
191
+ By passing a middleware chain to `.use()`, you can merge it with the existing chain. Its middlewares will be executed _after_ any existing middlewares in this chain and _before_ any new middlewares you add later.
192
192
 
193
193
  ```typescript
194
194
  const innerChain = chain((context: RequestContext) => {
195
- console.log('Inner chain start')
196
- return { define: { innerData: 'secret' } } // Only available inside innerChain
197
- }).use((context: RequestContext<{ innerData: string }>) => {
198
- console.log('Accessing inner data:', context.innerData)
195
+ return { helloFromInner: true }
199
196
  })
200
197
 
201
- const outerMiddleware = (context: RequestContext) => {
202
- // context.innerData is not accessible here
203
- console.log('Outer middleware after inner chain')
204
- if (!('innerData' in context)) {
205
- console.log('innerData is correctly scoped.')
206
- }
207
- return new Response('Finished')
208
- }
209
-
210
- const finalApp = chain().use(innerChain).use(outerMiddleware)
211
- // Output when executing the finalApp chain:
212
- // Inner chain start
213
- // Accessing inner data: secret
214
- // Outer middleware after inner chain
215
- // innerData is correctly scoped.
198
+ const app = chain()
199
+ .use(innerChain)
200
+ .use(context => {
201
+ context.helloFromInner // Output: true
202
+ })
216
203
  ```
217
204
 
218
- If a nested chain does not return a `Response`, execution continues with the next middleware in the outer chain.
205
+ ### Isolating a Middleware Chain
219
206
 
220
- ### Merging Chains
207
+ When adding a middleware chain to another, you may use the `isolate()` method to isolate the nested chain from the outer chain.
221
208
 
222
- You can merge two middleware chains using the `merge` method. This is useful if you want to combine middleware from multiple files or modules.
209
+ ```typescript
210
+ const isolatedChain = chain().isolate()
211
+ ```
212
+
213
+ This prevents the nested chain from affecting middleware in the outer chain (e.g. through _request plugins_).
223
214
 
224
215
  ```typescript
225
- const mergedApp = chain().use(middleware1).merge(chain().use(middleware2))
226
- // …is equivalent to…
227
- const mergedApp = chain().use(middleware1).use(middleware2)
216
+ const innerChain = chain()
217
+ .use(() => ({
218
+ foo: true,
219
+ }))
220
+ .use(context => {
221
+ context.foo // Output: true
222
+ })
223
+
224
+ const outerChain = chain()
225
+ .use(innerChain.isolate())
226
+ .use(context => {
227
+ context.foo // Output: undefined
228
+ })
229
+ ```
230
+
231
+ If an isolated chain does not return a `Response`, execution continues with the next middleware in the outer chain.
232
+
233
+ ### Escaping a Middleware Chain
234
+
235
+ To stop processing a request (e.g. skip any remaining middlewares), use the `context.passThrough()` method. The Hattip adapter is responsible for deciding the appropriate action based on the request.
236
+
237
+ In the context of an isolated chain, `context.passThrough()` will skip remaining middlewares in the isolated chain, but the outer chain will continue execution with the next middleware.
238
+
239
+ ```ts
240
+ const app = chain()
241
+ .use(context => {
242
+ if (!context.request.headers.has('Authorization')) {
243
+ // It's best practice to return immediately after
244
+ // calling passThrough()
245
+ return context.passThrough()
246
+ }
247
+ return new Response('Authorized', { status: 200 })
248
+ })
249
+ .use(context => {
250
+ // This will never run, since the previous middleware
251
+ // either returns a Response or calls passThrough()
252
+ throw new Error('This request middleware will never run')
253
+ })
228
254
  ```
229
255
 
230
256
  ### Safe Environment Variables
@@ -338,7 +364,7 @@ import { routes } from 'alien-middleware/router'
338
364
  // Define a middleware that adds a user to the context
339
365
  const addUserMiddleware = (context: RequestContext): RequestPlugin => {
340
366
  const user = { id: 123, name: 'Alice' }
341
- return { define: { user } }
367
+ return { user }
342
368
  }
343
369
 
344
370
  // Create a chain with the middleware