alien-middleware 0.3.0 → 0.4.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/index.d.ts CHANGED
@@ -39,7 +39,7 @@ type RequestContext<TProperties extends object = any, TEnv extends object = any,
39
39
  type Awaitable<T> = T | PromiseLike<T>;
40
40
  type RequestMiddleware<T extends MiddlewareChain = MiddlewareChain> = (context: RequestContext<InputProperties<T>, InputEnv<T>, Platform<T>>) => Awaitable<Response | RequestPlugin | void>;
41
41
  type ResponseMiddleware<T extends MiddlewareChain = MiddlewareChain> = (context: RequestContext<InputProperties<T>, InputEnv<T>, Platform<T>>, response: Response) => Awaitable<Response | void>;
42
- type RequestHandler<TInputs extends MiddlewareTypes, TCurrent extends MiddlewareTypes, TPlatform> = HattipHandler<TPlatform> & MiddlewareChain<TInputs, TCurrent, TPlatform>;
42
+ type RequestHandler<TInputs extends MiddlewareTypes = any, TCurrent extends MiddlewareTypes = any, TPlatform = any> = HattipHandler<TPlatform> & MiddlewareChain<TInputs, TCurrent, TPlatform>;
43
43
  /**
44
44
  * Either a request middleware or a response middleware.
45
45
  *
@@ -62,6 +62,10 @@ type ApplyFirstMiddleware<T extends Middleware> = ApplyMiddleware<MiddlewareChai
62
62
  properties: {};
63
63
  env: {};
64
64
  }, unknown>, T>;
65
+ type MergeMiddlewareChains<TFirst extends MiddlewareChain, TSecond extends MiddlewareChain<Current<TFirst>, any, Platform<TFirst>>> = RequestHandler<Inputs<TFirst>, {
66
+ properties: Merge<Properties<TFirst>, Properties<TSecond>>;
67
+ env: Merge<Env<TFirst>, Env<TSecond>>;
68
+ }, Platform<TFirst>>;
65
69
 
66
70
  declare const kRequestChain: unique symbol;
67
71
  declare const kResponseChain: unique symbol;
@@ -90,6 +94,11 @@ TPlatform = any> {
90
94
  * @returns a new `MiddlewareChain` instance
91
95
  */
92
96
  use<const TMiddleware extends Middleware<TCurrent['properties'], TCurrent['env'], TPlatform>>(middleware: TMiddleware): ApplyMiddleware<this, TMiddleware>;
97
+ /**
98
+ * Merge two middleware chains. The middlewares from the second chain will be
99
+ * executed after the middlewares from the first chain.
100
+ */
101
+ merge<TChain extends MiddlewareChain<TCurrent, any, TPlatform>>(chain: TChain): MergeMiddlewareChains<this, TChain>;
93
102
  }
94
103
  declare function chain<TProperties extends object = {}, TEnv extends object = {}, TPlatform = unknown>(): MiddlewareChain<{
95
104
  env: TEnv;
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ var urlDescriptor = {
23
23
  return url;
24
24
  }
25
25
  };
26
- var MiddlewareChain = class _MiddlewareChain {
26
+ var MiddlewareChain = class {
27
27
  [kRequestChain] = [];
28
28
  [kResponseChain] = [];
29
29
  /**
@@ -41,66 +41,84 @@ var MiddlewareChain = class _MiddlewareChain {
41
41
  } else {
42
42
  responseChain = [...responseChain, middleware];
43
43
  }
44
- async function handler(parentContext) {
45
- const context = Object.create(parentContext);
46
- context[kIgnoreNotFound] = true;
47
- if (!("url" in context)) {
48
- Object.defineProperty(context, "url", urlDescriptor);
44
+ return createHandler(requestChain, responseChain);
45
+ }
46
+ /**
47
+ * Merge two middleware chains. The middlewares from the second chain will be
48
+ * executed after the middlewares from the first chain.
49
+ */
50
+ merge(chain2) {
51
+ return createHandler(
52
+ [...this[kRequestChain], ...chain2[kRequestChain]],
53
+ [...this[kResponseChain], ...chain2[kResponseChain]]
54
+ );
55
+ }
56
+ };
57
+ function createHandler(requestChain, responseChain) {
58
+ async function handler(parentContext) {
59
+ const context = Object.create(parentContext);
60
+ context[kIgnoreNotFound] = true;
61
+ if (!("url" in context)) {
62
+ Object.defineProperty(context, "url", urlDescriptor);
63
+ }
64
+ const cache = context[kMiddlewareCache] ||= /* @__PURE__ */ new Set();
65
+ let response;
66
+ let env;
67
+ for (const middleware of requestChain) {
68
+ if (cache.has(middleware)) {
69
+ continue;
49
70
  }
50
- const cache = context[kMiddlewareCache] ||= /* @__PURE__ */ new Set();
51
- let response;
52
- let env;
53
- for (const middleware2 of requestChain) {
54
- if (cache.has(middleware2)) {
55
- continue;
56
- }
57
- cache.add(middleware2);
58
- let result = middleware2(context);
59
- if (isPromise(result)) {
60
- result = await result;
71
+ cache.add(middleware);
72
+ let result = middleware(context);
73
+ if (isPromise(result)) {
74
+ result = await result;
75
+ }
76
+ if (result) {
77
+ if (result instanceof Response) {
78
+ response = result;
79
+ break;
61
80
  }
62
- if (result) {
63
- if (result instanceof Response) {
64
- response = result;
65
- break;
66
- }
67
- if (result.define) {
68
- Object.assign(context, result.define);
69
- }
70
- if (result.env) {
71
- env ||= createExtendedEnv(context);
72
- Object.assign(env, result.env);
81
+ if (result.define) {
82
+ for (const key in result.define) {
83
+ Object.defineProperty(context, key, {
84
+ ...Object.getOwnPropertyDescriptor(result.define, key),
85
+ configurable: false
86
+ });
73
87
  }
74
88
  }
75
- }
76
- if (!response) {
77
- if (parentContext[kIgnoreNotFound]) {
78
- return;
89
+ if (result.env) {
90
+ env ||= createExtendedEnv(context);
91
+ Object.assign(env, result.env);
79
92
  }
80
- response = new Response("Not Found", { status: 404 });
81
93
  }
82
- for (const middleware2 of responseChain) {
83
- if (cache.has(middleware2)) {
84
- continue;
85
- }
86
- cache.add(middleware2);
87
- let result = middleware2(context, response);
88
- if (isPromise(result)) {
89
- result = await result;
90
- }
91
- if (result && result instanceof Response) {
92
- response = result;
93
- continue;
94
- }
94
+ }
95
+ if (!response) {
96
+ if (parentContext[kIgnoreNotFound]) {
97
+ return;
95
98
  }
96
- return response;
99
+ response = new Response("Not Found", { status: 404 });
97
100
  }
98
- Object.setPrototypeOf(handler, _MiddlewareChain.prototype);
99
- handler[kRequestChain] = requestChain;
100
- handler[kResponseChain] = responseChain;
101
- return handler;
101
+ for (const middleware of responseChain) {
102
+ if (cache.has(middleware)) {
103
+ continue;
104
+ }
105
+ cache.add(middleware);
106
+ let result = middleware(context, response);
107
+ if (isPromise(result)) {
108
+ result = await result;
109
+ }
110
+ if (result && result instanceof Response) {
111
+ response = result;
112
+ continue;
113
+ }
114
+ }
115
+ return response;
102
116
  }
103
- };
117
+ Object.setPrototypeOf(handler, MiddlewareChain.prototype);
118
+ handler[kRequestChain] = requestChain;
119
+ handler[kResponseChain] = responseChain;
120
+ return handler;
121
+ }
104
122
  function createExtendedEnv(context) {
105
123
  const env = /* @__PURE__ */ Object.create(null);
106
124
  const superEnv = context.env;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "alien-middleware",
3
3
  "type": "module",
4
- "version": "0.3.0",
4
+ "version": "0.4.0",
5
5
  "exports": {
6
6
  "types": "./dist/index.d.ts",
7
7
  "default": "./dist/index.js"
package/readme.md CHANGED
@@ -82,6 +82,14 @@ server.listen(3000, () => {
82
82
  > If no middleware in the chain returns a `Response`, a `404 Not Found` response
83
83
  > is automatically returned.
84
84
 
85
+ #### Middlewares are deduplicated.
86
+
87
+ If you add the same middleware multiple times, it will only run once. This is a safety measure that allows you to use the same middleware in different places without worrying about it running multiple times.
88
+
89
+ ```typescript
90
+ const app = chain().use(myMiddleware).use(myMiddleware)
91
+ ```
92
+
85
93
  ### Request Middleware
86
94
 
87
95
  Request middleware runs sequentially before a `Response` is generated.
@@ -203,6 +211,16 @@ const finalApp = chain().use(innerChain).use(outerMiddleware)
203
211
 
204
212
  If a nested chain does not return a `Response`, execution continues with the next middleware in the outer chain.
205
213
 
214
+ ### Merging Chains
215
+
216
+ You can merge two middleware chains using the `merge` method. This is useful if you want to combine middleware from multiple files or modules.
217
+
218
+ ```typescript
219
+ const mergedApp = chain().use(middleware1).merge(chain().use(middleware2))
220
+ // …is equivalent to…
221
+ const mergedApp = chain().use(middleware1).use(middleware2)
222
+ ```
223
+
206
224
  ### Safe Environment Variables
207
225
 
208
226
  When writing a Hattip handler without this package, the `context.env()` method is inherently unsafe. Its return type is always `string | undefined`, which means you either need to write defensive checks or use type assertions. Neither is ideal.