@whatwg-node/server 0.9.57-alpha-20241123125835-498ddbef3a79038cbe04d8cef8c2cc1d83e7573c → 0.9.57-alpha-20241125145154-b2c748560e2a94065f9a25e55132e073f78bea40
Sign up to get free protection for your applications and to get access to all the features.
- package/cjs/createServerAdapter.js +40 -18
- package/cjs/plugins/useContentEncoding.js +6 -4
- package/cjs/utils.js +4 -3
- package/esm/createServerAdapter.js +41 -19
- package/esm/plugins/useContentEncoding.js +6 -4
- package/esm/utils.js +4 -3
- package/package.json +1 -1
- package/typings/plugins/types.d.cts +4 -2
- package/typings/plugins/types.d.ts +4 -2
- package/typings/types.d.cts +1 -0
- package/typings/types.d.ts +1 -0
@@ -41,29 +41,41 @@ function createServerAdapter(serverAdapterBaseObject, options) {
|
|
41
41
|
signal.sendAbort();
|
42
42
|
}
|
43
43
|
});
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
44
|
+
disposableStack.defer(() => {
|
45
|
+
if (waitUntilPromises.size > 0) {
|
46
|
+
return Promise.allSettled(waitUntilPromises).then(() => {
|
47
|
+
waitUntilPromises.clear();
|
48
|
+
}, () => {
|
49
|
+
waitUntilPromises.clear();
|
50
|
+
});
|
51
|
+
}
|
52
|
+
});
|
48
53
|
function waitUntil(promiseLike) {
|
49
54
|
// If it is a Node.js environment, we should register the disposable stack to handle process termination events
|
50
55
|
if (globalThis.process) {
|
51
56
|
(0, utils_js_1.ensureDisposableStackRegisteredForTerminateEvents)(disposableStack);
|
52
57
|
}
|
53
|
-
waitUntilPromises.add(promiseLike
|
58
|
+
waitUntilPromises.add(promiseLike);
|
59
|
+
promiseLike.then(() => {
|
54
60
|
waitUntilPromises.delete(promiseLike);
|
55
61
|
}, err => {
|
56
62
|
console.error(`Unexpected error while waiting: ${err.message || err}`);
|
57
63
|
waitUntilPromises.delete(promiseLike);
|
58
|
-
})
|
64
|
+
});
|
59
65
|
}
|
60
66
|
if (options?.plugins != null) {
|
61
67
|
for (const plugin of options.plugins) {
|
62
|
-
if (plugin
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
68
|
+
if (plugin != null) {
|
69
|
+
if (plugin.onRequest) {
|
70
|
+
onRequestHooks.push(plugin.onRequest);
|
71
|
+
}
|
72
|
+
if (plugin.onResponse) {
|
73
|
+
onResponseHooks.push(plugin.onResponse);
|
74
|
+
}
|
75
|
+
const disposeFn = plugin[disposablestack_1.DisposableSymbols.asyncDispose] || plugin[disposablestack_1.DisposableSymbols.dispose];
|
76
|
+
if (disposeFn != null) {
|
77
|
+
disposableStack.defer(disposeFn);
|
78
|
+
}
|
67
79
|
}
|
68
80
|
}
|
69
81
|
}
|
@@ -74,12 +86,13 @@ function createServerAdapter(serverAdapterBaseObject, options) {
|
|
74
86
|
if (onRequestHooks.length === 0) {
|
75
87
|
return handleEarlyResponse();
|
76
88
|
}
|
77
|
-
let url =
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
89
|
+
let url = request['parsedUrl'] ||
|
90
|
+
new Proxy(EMPTY_OBJECT, {
|
91
|
+
get(_target, prop, _receiver) {
|
92
|
+
url = new fetchAPI.URL(request.url, 'http://localhost');
|
93
|
+
return Reflect.get(url, prop, url);
|
94
|
+
},
|
95
|
+
});
|
83
96
|
const onRequestHooksIteration$ = (0, utils_js_1.iterateAsyncVoid)(onRequestHooks, (onRequestHook, stopEarly) => onRequestHook({
|
84
97
|
request,
|
85
98
|
setRequest(newRequest) {
|
@@ -305,7 +318,16 @@ function createServerAdapter(serverAdapterBaseObject, options) {
|
|
305
318
|
handle: genericRequestHandler,
|
306
319
|
disposableStack,
|
307
320
|
[disposablestack_1.DisposableSymbols.asyncDispose]() {
|
308
|
-
|
321
|
+
if (!disposableStack.disposed) {
|
322
|
+
return disposableStack.disposeAsync();
|
323
|
+
}
|
324
|
+
return (0, uwebsockets_js_1.fakePromise)(undefined);
|
325
|
+
},
|
326
|
+
dispose() {
|
327
|
+
if (!disposableStack.disposed) {
|
328
|
+
return disposableStack.disposeAsync();
|
329
|
+
}
|
330
|
+
return (0, uwebsockets_js_1.fakePromise)(undefined);
|
309
331
|
},
|
310
332
|
};
|
311
333
|
const serverAdapter = new Proxy(genericRequestHandler, {
|
@@ -47,7 +47,6 @@ function useContentEncoding() {
|
|
47
47
|
}
|
48
48
|
},
|
49
49
|
onResponse({ request, response, setResponse, fetchAPI, serverContext }) {
|
50
|
-
const waitUntil = serverContext.waitUntil?.bind(serverContext) || (() => { });
|
51
50
|
// Hack for avoiding to create whatwg-node to create a readable stream until it's needed
|
52
51
|
if (response['bodyInit'] || response.body) {
|
53
52
|
const encodings = encodingMap.get(request);
|
@@ -61,8 +60,10 @@ function useContentEncoding() {
|
|
61
60
|
const bufOfRes = response._buffer;
|
62
61
|
if (bufOfRes) {
|
63
62
|
const writer = compressionStream.writable.getWriter();
|
64
|
-
|
65
|
-
waitUntil(
|
63
|
+
const write$ = writer.write(bufOfRes);
|
64
|
+
serverContext.waitUntil?.(write$);
|
65
|
+
const close$ = writer.close();
|
66
|
+
serverContext.waitUntil?.(close$);
|
66
67
|
const uint8Arrays$ = (0, utils_js_1.isReadable)(compressionStream.readable['readable'])
|
67
68
|
? collectReadableValues(compressionStream.readable['readable'])
|
68
69
|
: (0, utils_js_1.isAsyncIterable)(compressionStream.readable)
|
@@ -80,7 +81,8 @@ function useContentEncoding() {
|
|
80
81
|
});
|
81
82
|
utils_js_1.decompressedResponseMap.set(compressedResponse, response);
|
82
83
|
setResponse(compressedResponse);
|
83
|
-
|
84
|
+
const close$ = compressionStream.writable.close();
|
85
|
+
serverContext.waitUntil?.(close$);
|
84
86
|
});
|
85
87
|
}
|
86
88
|
}
|
package/cjs/utils.js
CHANGED
@@ -557,9 +557,10 @@ function ensureEventListenerForDisposableStacks() {
|
|
557
557
|
eventListenerRegistered = true;
|
558
558
|
for (const event of terminateEvents) {
|
559
559
|
globalThis.process.once(event, function terminateHandler() {
|
560
|
-
return Promise.allSettled([...disposableStacks].map(stack => stack.
|
561
|
-
|
562
|
-
|
560
|
+
return Promise.allSettled([...disposableStacks].map(stack => !stack.disposed &&
|
561
|
+
stack.disposeAsync().catch(e => {
|
562
|
+
console.error('Error while disposing:', e);
|
563
|
+
})));
|
563
564
|
});
|
564
565
|
}
|
565
566
|
}
|
@@ -2,7 +2,7 @@
|
|
2
2
|
import { AsyncDisposableStack, DisposableSymbols } from '@whatwg-node/disposablestack';
|
3
3
|
import * as DefaultFetchAPI from '@whatwg-node/fetch';
|
4
4
|
import { completeAssign, ensureDisposableStackRegisteredForTerminateEvents, handleAbortSignalAndPromiseResponse, handleErrorFromRequestHandler, isFetchEvent, isNodeRequest, isolateObject, isPromise, isRequestInit, isServerResponse, iterateAsyncVoid, nodeRequestResponseMap, normalizeNodeRequest, sendNodeResponse, ServerAdapterRequestAbortSignal, } from './utils.js';
|
5
|
-
import { getRequestFromUWSRequest, isUWSResponse, sendResponseToUwsOpts, } from './uwebsockets.js';
|
5
|
+
import { fakePromise, getRequestFromUWSRequest, isUWSResponse, sendResponseToUwsOpts, } from './uwebsockets.js';
|
6
6
|
// Required for envs like nextjs edge runtime
|
7
7
|
function isRequestAccessible(serverContext) {
|
8
8
|
try {
|
@@ -37,29 +37,41 @@ function createServerAdapter(serverAdapterBaseObject, options) {
|
|
37
37
|
signal.sendAbort();
|
38
38
|
}
|
39
39
|
});
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
disposableStack.defer(() => {
|
41
|
+
if (waitUntilPromises.size > 0) {
|
42
|
+
return Promise.allSettled(waitUntilPromises).then(() => {
|
43
|
+
waitUntilPromises.clear();
|
44
|
+
}, () => {
|
45
|
+
waitUntilPromises.clear();
|
46
|
+
});
|
47
|
+
}
|
48
|
+
});
|
44
49
|
function waitUntil(promiseLike) {
|
45
50
|
// If it is a Node.js environment, we should register the disposable stack to handle process termination events
|
46
51
|
if (globalThis.process) {
|
47
52
|
ensureDisposableStackRegisteredForTerminateEvents(disposableStack);
|
48
53
|
}
|
49
|
-
waitUntilPromises.add(promiseLike
|
54
|
+
waitUntilPromises.add(promiseLike);
|
55
|
+
promiseLike.then(() => {
|
50
56
|
waitUntilPromises.delete(promiseLike);
|
51
57
|
}, err => {
|
52
58
|
console.error(`Unexpected error while waiting: ${err.message || err}`);
|
53
59
|
waitUntilPromises.delete(promiseLike);
|
54
|
-
})
|
60
|
+
});
|
55
61
|
}
|
56
62
|
if (options?.plugins != null) {
|
57
63
|
for (const plugin of options.plugins) {
|
58
|
-
if (plugin
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
64
|
+
if (plugin != null) {
|
65
|
+
if (plugin.onRequest) {
|
66
|
+
onRequestHooks.push(plugin.onRequest);
|
67
|
+
}
|
68
|
+
if (plugin.onResponse) {
|
69
|
+
onResponseHooks.push(plugin.onResponse);
|
70
|
+
}
|
71
|
+
const disposeFn = plugin[DisposableSymbols.asyncDispose] || plugin[DisposableSymbols.dispose];
|
72
|
+
if (disposeFn != null) {
|
73
|
+
disposableStack.defer(disposeFn);
|
74
|
+
}
|
63
75
|
}
|
64
76
|
}
|
65
77
|
}
|
@@ -70,12 +82,13 @@ function createServerAdapter(serverAdapterBaseObject, options) {
|
|
70
82
|
if (onRequestHooks.length === 0) {
|
71
83
|
return handleEarlyResponse();
|
72
84
|
}
|
73
|
-
let url =
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
85
|
+
let url = request['parsedUrl'] ||
|
86
|
+
new Proxy(EMPTY_OBJECT, {
|
87
|
+
get(_target, prop, _receiver) {
|
88
|
+
url = new fetchAPI.URL(request.url, 'http://localhost');
|
89
|
+
return Reflect.get(url, prop, url);
|
90
|
+
},
|
91
|
+
});
|
79
92
|
const onRequestHooksIteration$ = iterateAsyncVoid(onRequestHooks, (onRequestHook, stopEarly) => onRequestHook({
|
80
93
|
request,
|
81
94
|
setRequest(newRequest) {
|
@@ -301,7 +314,16 @@ function createServerAdapter(serverAdapterBaseObject, options) {
|
|
301
314
|
handle: genericRequestHandler,
|
302
315
|
disposableStack,
|
303
316
|
[DisposableSymbols.asyncDispose]() {
|
304
|
-
|
317
|
+
if (!disposableStack.disposed) {
|
318
|
+
return disposableStack.disposeAsync();
|
319
|
+
}
|
320
|
+
return fakePromise(undefined);
|
321
|
+
},
|
322
|
+
dispose() {
|
323
|
+
if (!disposableStack.disposed) {
|
324
|
+
return disposableStack.disposeAsync();
|
325
|
+
}
|
326
|
+
return fakePromise(undefined);
|
305
327
|
},
|
306
328
|
};
|
307
329
|
const serverAdapter = new Proxy(genericRequestHandler, {
|
@@ -44,7 +44,6 @@ export function useContentEncoding() {
|
|
44
44
|
}
|
45
45
|
},
|
46
46
|
onResponse({ request, response, setResponse, fetchAPI, serverContext }) {
|
47
|
-
const waitUntil = serverContext.waitUntil?.bind(serverContext) || (() => { });
|
48
47
|
// Hack for avoiding to create whatwg-node to create a readable stream until it's needed
|
49
48
|
if (response['bodyInit'] || response.body) {
|
50
49
|
const encodings = encodingMap.get(request);
|
@@ -58,8 +57,10 @@ export function useContentEncoding() {
|
|
58
57
|
const bufOfRes = response._buffer;
|
59
58
|
if (bufOfRes) {
|
60
59
|
const writer = compressionStream.writable.getWriter();
|
61
|
-
|
62
|
-
waitUntil(
|
60
|
+
const write$ = writer.write(bufOfRes);
|
61
|
+
serverContext.waitUntil?.(write$);
|
62
|
+
const close$ = writer.close();
|
63
|
+
serverContext.waitUntil?.(close$);
|
63
64
|
const uint8Arrays$ = isReadable(compressionStream.readable['readable'])
|
64
65
|
? collectReadableValues(compressionStream.readable['readable'])
|
65
66
|
: isAsyncIterable(compressionStream.readable)
|
@@ -77,7 +78,8 @@ export function useContentEncoding() {
|
|
77
78
|
});
|
78
79
|
decompressedResponseMap.set(compressedResponse, response);
|
79
80
|
setResponse(compressedResponse);
|
80
|
-
|
81
|
+
const close$ = compressionStream.writable.close();
|
82
|
+
serverContext.waitUntil?.(close$);
|
81
83
|
});
|
82
84
|
}
|
83
85
|
}
|
package/esm/utils.js
CHANGED
@@ -534,9 +534,10 @@ function ensureEventListenerForDisposableStacks() {
|
|
534
534
|
eventListenerRegistered = true;
|
535
535
|
for (const event of terminateEvents) {
|
536
536
|
globalThis.process.once(event, function terminateHandler() {
|
537
|
-
return Promise.allSettled([...disposableStacks].map(stack => stack.
|
538
|
-
|
539
|
-
|
537
|
+
return Promise.allSettled([...disposableStacks].map(stack => !stack.disposed &&
|
538
|
+
stack.disposeAsync().catch(e => {
|
539
|
+
console.error('Error while disposing:', e);
|
540
|
+
})));
|
540
541
|
});
|
541
542
|
}
|
542
543
|
}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@whatwg-node/server",
|
3
|
-
"version": "0.9.57-alpha-
|
3
|
+
"version": "0.9.57-alpha-20241125145154-b2c748560e2a94065f9a25e55132e073f78bea40",
|
4
4
|
"description": "Fetch API compliant HTTP Server adapter",
|
5
5
|
"sideEffects": false,
|
6
6
|
"dependencies": {
|
@@ -1,8 +1,10 @@
|
|
1
1
|
import { FetchAPI, ServerAdapterRequestHandler, type ServerAdapterInitialContext } from '../types.cjs';
|
2
|
-
export
|
2
|
+
export type ServerAdapterPlugin<TServerContext = {}> = {
|
3
3
|
onRequest?: OnRequestHook<TServerContext & ServerAdapterInitialContext>;
|
4
4
|
onResponse?: OnResponseHook<TServerContext & ServerAdapterInitialContext>;
|
5
|
-
|
5
|
+
[Symbol.dispose]?: () => void;
|
6
|
+
[Symbol.asyncDispose]?: () => PromiseLike<void> | void;
|
7
|
+
} | undefined;
|
6
8
|
export type OnRequestHook<TServerContext> = (payload: OnRequestEventPayload<TServerContext>) => Promise<void> | void;
|
7
9
|
export interface OnRequestEventPayload<TServerContext> {
|
8
10
|
request: Request;
|
@@ -1,8 +1,10 @@
|
|
1
1
|
import { FetchAPI, ServerAdapterRequestHandler, type ServerAdapterInitialContext } from '../types.js';
|
2
|
-
export
|
2
|
+
export type ServerAdapterPlugin<TServerContext = {}> = {
|
3
3
|
onRequest?: OnRequestHook<TServerContext & ServerAdapterInitialContext>;
|
4
4
|
onResponse?: OnResponseHook<TServerContext & ServerAdapterInitialContext>;
|
5
|
-
|
5
|
+
[Symbol.dispose]?: () => void;
|
6
|
+
[Symbol.asyncDispose]?: () => PromiseLike<void> | void;
|
7
|
+
} | undefined;
|
6
8
|
export type OnRequestHook<TServerContext> = (payload: OnRequestEventPayload<TServerContext>) => Promise<void> | void;
|
7
9
|
export interface OnRequestEventPayload<TServerContext> {
|
8
10
|
request: Request;
|
package/typings/types.d.cts
CHANGED
@@ -57,6 +57,7 @@ export interface ServerAdapterObject<TServerContext> extends EventListenerObject
|
|
57
57
|
request: Request;
|
58
58
|
} & Partial<TServerContext & ServerAdapterInitialContext>, ...ctx: Partial<TServerContext & ServerAdapterInitialContext>[]): Promise<Response> | Response;
|
59
59
|
disposableStack: AsyncDisposableStack;
|
60
|
+
dispose(): Promise<void> | void;
|
60
61
|
}
|
61
62
|
export interface RequestLike {
|
62
63
|
url: string;
|
package/typings/types.d.ts
CHANGED
@@ -57,6 +57,7 @@ export interface ServerAdapterObject<TServerContext> extends EventListenerObject
|
|
57
57
|
request: Request;
|
58
58
|
} & Partial<TServerContext & ServerAdapterInitialContext>, ...ctx: Partial<TServerContext & ServerAdapterInitialContext>[]): Promise<Response> | Response;
|
59
59
|
disposableStack: AsyncDisposableStack;
|
60
|
+
dispose(): Promise<void> | void;
|
60
61
|
}
|
61
62
|
export interface RequestLike {
|
62
63
|
url: string;
|