@whatwg-node/server
Advanced tools
Comparing version 0.10.0-alpha-20240717150008-1474b9d9b679a0e8f6225f44f11b95a6f4bf24ea to 0.10.0-alpha-20240726141316-c6ce93b3598457ebe73b3b725986723af8f5e609
@@ -57,2 +57,5 @@ "use strict"; | ||
request, | ||
setRequest(newRequest) { | ||
request = newRequest; | ||
}, | ||
serverContext, | ||
@@ -73,3 +76,3 @@ fetchAPI, | ||
function handleResponse(response) { | ||
if (onRequestHooks.length === 0) { | ||
if (onResponseHooks.length === 0) { | ||
return response; | ||
@@ -81,2 +84,6 @@ } | ||
serverContext, | ||
setResponse(newResponse) { | ||
response = newResponse; | ||
}, | ||
fetchAPI, | ||
}; | ||
@@ -105,2 +112,3 @@ const onResponseHooksIteration$ = (0, utils_js_1.iterateAsyncVoid)(onResponseHooks, onResponseHook => onResponseHook(onResponseHookPayload)); | ||
: givenHandleRequest; | ||
// TODO: Remove this on the next major version | ||
function handleNodeRequestAndResponse(nodeRequest, nodeResponseOrContainer, ...ctx) { | ||
@@ -107,0 +115,0 @@ const nodeResponse = nodeResponseOrContainer.raw || nodeResponseOrContainer; |
@@ -11,4 +11,5 @@ "use strict"; | ||
tslib_1.__exportStar(require("./plugins/useErrorHandling.js"), exports); | ||
tslib_1.__exportStar(require("./plugins/useContentEncoding.js"), exports); | ||
tslib_1.__exportStar(require("./uwebsockets.js"), exports); | ||
var fetch_1 = require("@whatwg-node/fetch"); | ||
Object.defineProperty(exports, "Response", { enumerable: true, get: function () { return fetch_1.Response; } }); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ServerAdapterRequestAbortSignal = void 0; | ||
exports.decompressedResponseMap = exports.ServerAdapterRequestAbortSignal = void 0; | ||
exports.isAsyncIterable = isAsyncIterable; | ||
@@ -20,2 +20,4 @@ exports.normalizeNodeRequest = normalizeNodeRequest; | ||
exports.handleAbortSignalAndPromiseResponse = handleAbortSignalAndPromiseResponse; | ||
exports.getSupportedEncodings = getSupportedEncodings; | ||
exports.handleResponseDecompression = handleResponseDecompression; | ||
const fetch_1 = require("@whatwg-node/fetch"); | ||
@@ -116,3 +118,12 @@ function isAsyncIterable(body) { | ||
let signal; | ||
if (nodeResponse.once) { | ||
let normalizedHeaders = nodeRequest.headers; | ||
if (nodeRequest.headers?.[':method']) { | ||
normalizedHeaders = {}; | ||
for (const key in nodeRequest.headers) { | ||
if (!key.startsWith(':')) { | ||
normalizedHeaders[key] = nodeRequest.headers[key]; | ||
} | ||
} | ||
} | ||
if (nodeResponse?.once) { | ||
let sendAbortSignal; | ||
@@ -144,3 +155,3 @@ // If ponyfilled | ||
method: nodeRequest.method, | ||
headers: nodeRequest.headers, | ||
headers: normalizedHeaders, | ||
signal, | ||
@@ -160,3 +171,3 @@ }); | ||
method: nodeRequest.method, | ||
headers: nodeRequest.headers, | ||
headers: normalizedHeaders, | ||
body: maybeParsedBody, | ||
@@ -168,3 +179,3 @@ signal, | ||
method: nodeRequest.method, | ||
headers: nodeRequest.headers, | ||
headers: normalizedHeaders, | ||
signal, | ||
@@ -197,3 +208,3 @@ }); | ||
method: nodeRequest.method, | ||
headers: nodeRequest.headers, | ||
headers: normalizedHeaders, | ||
duplex: 'half', | ||
@@ -222,3 +233,3 @@ body: new ReadableStream({ | ||
method: nodeRequest.method, | ||
headers: nodeRequest.headers, | ||
headers: normalizedHeaders, | ||
body: rawRequest, | ||
@@ -351,3 +362,6 @@ duplex: 'half', | ||
const descriptors = Object.getOwnPropertyNames(source).reduce((descriptors, key) => { | ||
descriptors[key] = Object.getOwnPropertyDescriptor(source, key); | ||
const descriptor = Object.getOwnPropertyDescriptor(source, key); | ||
if (descriptor) { | ||
descriptors[key] = Object.getOwnPropertyDescriptor(source, key); | ||
} | ||
return descriptors; | ||
@@ -358,3 +372,3 @@ }, {}); | ||
const descriptor = Object.getOwnPropertyDescriptor(source, sym); | ||
if (descriptor.enumerable) { | ||
if (descriptor?.enumerable) { | ||
descriptors[sym] = descriptor; | ||
@@ -508,1 +522,53 @@ } | ||
} | ||
exports.decompressedResponseMap = new WeakMap(); | ||
const supportedEncodingsByFetchAPI = new WeakMap(); | ||
function getSupportedEncodings(fetchAPI) { | ||
let supportedEncodings = supportedEncodingsByFetchAPI.get(fetchAPI); | ||
if (!supportedEncodings) { | ||
const possibleEncodings = ['deflate', 'gzip', 'deflate-raw', 'br']; | ||
supportedEncodings = possibleEncodings.filter(encoding => { | ||
// deflate-raw is not supported in Node.js >v20 | ||
if (globalThis.process?.version?.startsWith('v2') && | ||
fetchAPI.DecompressionStream === globalThis.DecompressionStream && | ||
encoding === 'deflate-raw') { | ||
return false; | ||
} | ||
try { | ||
// eslint-disable-next-line no-new | ||
new fetchAPI.DecompressionStream(encoding); | ||
return true; | ||
} | ||
catch { | ||
return false; | ||
} | ||
}); | ||
supportedEncodingsByFetchAPI.set(fetchAPI, supportedEncodings); | ||
} | ||
return supportedEncodings; | ||
} | ||
function handleResponseDecompression(response, fetchAPI) { | ||
const contentEncodingHeader = response?.headers.get('content-encoding'); | ||
if (!contentEncodingHeader || contentEncodingHeader === 'none') { | ||
return response; | ||
} | ||
if (!response?.body) { | ||
return response; | ||
} | ||
let decompressedResponse = exports.decompressedResponseMap.get(response); | ||
if (!decompressedResponse || decompressedResponse.bodyUsed) { | ||
let decompressedBody = response.body; | ||
const contentEncodings = contentEncodingHeader.split(','); | ||
if (!contentEncodings.every(encoding => getSupportedEncodings(fetchAPI).includes(encoding))) { | ||
return new fetchAPI.Response(`Unsupported 'Content-Encoding': ${contentEncodingHeader}`, { | ||
status: 415, | ||
statusText: 'Unsupported Media Type', | ||
}); | ||
} | ||
for (const contentEncoding of contentEncodings) { | ||
decompressedBody = decompressedBody.pipeThrough(new fetchAPI.DecompressionStream(contentEncoding)); | ||
} | ||
decompressedResponse = new fetchAPI.Response(decompressedBody, response); | ||
exports.decompressedResponseMap.set(response, decompressedResponse); | ||
} | ||
return decompressedResponse; | ||
} |
@@ -13,14 +13,38 @@ "use strict"; | ||
if (method !== 'get' && method !== 'head') { | ||
body = new fetchAPI.ReadableStream({}); | ||
let controller; | ||
body = new fetchAPI.ReadableStream({ | ||
start(c) { | ||
controller = c; | ||
}, | ||
}); | ||
const readable = body.readable; | ||
signal.addEventListener('abort', () => { | ||
readable.push(null); | ||
}); | ||
res.onData(function (ab, isLast) { | ||
const chunk = Buffer.from(ab, 0, ab.byteLength); | ||
readable.push(Buffer.from(chunk)); | ||
if (isLast) { | ||
if (readable) { | ||
signal.addEventListener('abort', () => { | ||
readable.push(null); | ||
} | ||
}); | ||
}); | ||
res.onData(function (ab, isLast) { | ||
const chunk = Buffer.from(ab, 0, ab.byteLength); | ||
readable.push(Buffer.from(chunk)); | ||
if (isLast) { | ||
readable.push(null); | ||
} | ||
}); | ||
} | ||
else { | ||
let closed = false; | ||
signal.addEventListener('abort', () => { | ||
if (!closed) { | ||
closed = true; | ||
controller.close(); | ||
} | ||
}); | ||
res.onData(function (ab, isLast) { | ||
const chunk = Buffer.from(ab, 0, ab.byteLength); | ||
controller.enqueue(Buffer.from(chunk)); | ||
if (isLast) { | ||
closed = true; | ||
controller.close(); | ||
} | ||
}); | ||
} | ||
} | ||
@@ -41,2 +65,5 @@ const headers = new fetchAPI.Headers(); | ||
signal, | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore - not in the TS types yet | ||
duplex: 'half', | ||
}); | ||
@@ -43,0 +70,0 @@ } |
@@ -53,2 +53,5 @@ /* eslint-disable @typescript-eslint/ban-types */ | ||
request, | ||
setRequest(newRequest) { | ||
request = newRequest; | ||
}, | ||
serverContext, | ||
@@ -69,3 +72,3 @@ fetchAPI, | ||
function handleResponse(response) { | ||
if (onRequestHooks.length === 0) { | ||
if (onResponseHooks.length === 0) { | ||
return response; | ||
@@ -77,2 +80,6 @@ } | ||
serverContext, | ||
setResponse(newResponse) { | ||
response = newResponse; | ||
}, | ||
fetchAPI, | ||
}; | ||
@@ -101,2 +108,3 @@ const onResponseHooksIteration$ = iterateAsyncVoid(onResponseHooks, onResponseHook => onResponseHook(onResponseHookPayload)); | ||
: givenHandleRequest; | ||
// TODO: Remove this on the next major version | ||
function handleNodeRequestAndResponse(nodeRequest, nodeResponseOrContainer, ...ctx) { | ||
@@ -103,0 +111,0 @@ const nodeResponse = nodeResponseOrContainer.raw || nodeResponseOrContainer; |
@@ -7,3 +7,4 @@ export * from './createServerAdapter.js'; | ||
export * from './plugins/useErrorHandling.js'; | ||
export * from './plugins/useContentEncoding.js'; | ||
export * from './uwebsockets.js'; | ||
export { Response } from '@whatwg-node/fetch'; |
@@ -95,3 +95,12 @@ import { URL } from '@whatwg-node/fetch'; | ||
let signal; | ||
if (nodeResponse.once) { | ||
let normalizedHeaders = nodeRequest.headers; | ||
if (nodeRequest.headers?.[':method']) { | ||
normalizedHeaders = {}; | ||
for (const key in nodeRequest.headers) { | ||
if (!key.startsWith(':')) { | ||
normalizedHeaders[key] = nodeRequest.headers[key]; | ||
} | ||
} | ||
} | ||
if (nodeResponse?.once) { | ||
let sendAbortSignal; | ||
@@ -123,3 +132,3 @@ // If ponyfilled | ||
method: nodeRequest.method, | ||
headers: nodeRequest.headers, | ||
headers: normalizedHeaders, | ||
signal, | ||
@@ -139,3 +148,3 @@ }); | ||
method: nodeRequest.method, | ||
headers: nodeRequest.headers, | ||
headers: normalizedHeaders, | ||
body: maybeParsedBody, | ||
@@ -147,3 +156,3 @@ signal, | ||
method: nodeRequest.method, | ||
headers: nodeRequest.headers, | ||
headers: normalizedHeaders, | ||
signal, | ||
@@ -176,3 +185,3 @@ }); | ||
method: nodeRequest.method, | ||
headers: nodeRequest.headers, | ||
headers: normalizedHeaders, | ||
duplex: 'half', | ||
@@ -201,3 +210,3 @@ body: new ReadableStream({ | ||
method: nodeRequest.method, | ||
headers: nodeRequest.headers, | ||
headers: normalizedHeaders, | ||
body: rawRequest, | ||
@@ -330,3 +339,6 @@ duplex: 'half', | ||
const descriptors = Object.getOwnPropertyNames(source).reduce((descriptors, key) => { | ||
descriptors[key] = Object.getOwnPropertyDescriptor(source, key); | ||
const descriptor = Object.getOwnPropertyDescriptor(source, key); | ||
if (descriptor) { | ||
descriptors[key] = Object.getOwnPropertyDescriptor(source, key); | ||
} | ||
return descriptors; | ||
@@ -337,3 +349,3 @@ }, {}); | ||
const descriptor = Object.getOwnPropertyDescriptor(source, sym); | ||
if (descriptor.enumerable) { | ||
if (descriptor?.enumerable) { | ||
descriptors[sym] = descriptor; | ||
@@ -487,1 +499,53 @@ } | ||
} | ||
export const decompressedResponseMap = new WeakMap(); | ||
const supportedEncodingsByFetchAPI = new WeakMap(); | ||
export function getSupportedEncodings(fetchAPI) { | ||
let supportedEncodings = supportedEncodingsByFetchAPI.get(fetchAPI); | ||
if (!supportedEncodings) { | ||
const possibleEncodings = ['deflate', 'gzip', 'deflate-raw', 'br']; | ||
supportedEncodings = possibleEncodings.filter(encoding => { | ||
// deflate-raw is not supported in Node.js >v20 | ||
if (globalThis.process?.version?.startsWith('v2') && | ||
fetchAPI.DecompressionStream === globalThis.DecompressionStream && | ||
encoding === 'deflate-raw') { | ||
return false; | ||
} | ||
try { | ||
// eslint-disable-next-line no-new | ||
new fetchAPI.DecompressionStream(encoding); | ||
return true; | ||
} | ||
catch { | ||
return false; | ||
} | ||
}); | ||
supportedEncodingsByFetchAPI.set(fetchAPI, supportedEncodings); | ||
} | ||
return supportedEncodings; | ||
} | ||
export function handleResponseDecompression(response, fetchAPI) { | ||
const contentEncodingHeader = response?.headers.get('content-encoding'); | ||
if (!contentEncodingHeader || contentEncodingHeader === 'none') { | ||
return response; | ||
} | ||
if (!response?.body) { | ||
return response; | ||
} | ||
let decompressedResponse = decompressedResponseMap.get(response); | ||
if (!decompressedResponse || decompressedResponse.bodyUsed) { | ||
let decompressedBody = response.body; | ||
const contentEncodings = contentEncodingHeader.split(','); | ||
if (!contentEncodings.every(encoding => getSupportedEncodings(fetchAPI).includes(encoding))) { | ||
return new fetchAPI.Response(`Unsupported 'Content-Encoding': ${contentEncodingHeader}`, { | ||
status: 415, | ||
statusText: 'Unsupported Media Type', | ||
}); | ||
} | ||
for (const contentEncoding of contentEncodings) { | ||
decompressedBody = decompressedBody.pipeThrough(new fetchAPI.DecompressionStream(contentEncoding)); | ||
} | ||
decompressedResponse = new fetchAPI.Response(decompressedBody, response); | ||
decompressedResponseMap.set(response, decompressedResponse); | ||
} | ||
return decompressedResponse; | ||
} |
@@ -8,14 +8,38 @@ export function isUWSResponse(res) { | ||
if (method !== 'get' && method !== 'head') { | ||
body = new fetchAPI.ReadableStream({}); | ||
let controller; | ||
body = new fetchAPI.ReadableStream({ | ||
start(c) { | ||
controller = c; | ||
}, | ||
}); | ||
const readable = body.readable; | ||
signal.addEventListener('abort', () => { | ||
readable.push(null); | ||
}); | ||
res.onData(function (ab, isLast) { | ||
const chunk = Buffer.from(ab, 0, ab.byteLength); | ||
readable.push(Buffer.from(chunk)); | ||
if (isLast) { | ||
if (readable) { | ||
signal.addEventListener('abort', () => { | ||
readable.push(null); | ||
} | ||
}); | ||
}); | ||
res.onData(function (ab, isLast) { | ||
const chunk = Buffer.from(ab, 0, ab.byteLength); | ||
readable.push(Buffer.from(chunk)); | ||
if (isLast) { | ||
readable.push(null); | ||
} | ||
}); | ||
} | ||
else { | ||
let closed = false; | ||
signal.addEventListener('abort', () => { | ||
if (!closed) { | ||
closed = true; | ||
controller.close(); | ||
} | ||
}); | ||
res.onData(function (ab, isLast) { | ||
const chunk = Buffer.from(ab, 0, ab.byteLength); | ||
controller.enqueue(Buffer.from(chunk)); | ||
if (isLast) { | ||
closed = true; | ||
controller.close(); | ||
} | ||
}); | ||
} | ||
} | ||
@@ -36,2 +60,5 @@ const headers = new fetchAPI.Headers(); | ||
signal, | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore - not in the TS types yet | ||
duplex: 'half', | ||
}); | ||
@@ -38,0 +65,0 @@ } |
{ | ||
"name": "@whatwg-node/server", | ||
"version": "0.10.0-alpha-20240717150008-1474b9d9b679a0e8f6225f44f11b95a6f4bf24ea", | ||
"version": "0.10.0-alpha-20240726141316-c6ce93b3598457ebe73b3b725986723af8f5e609", | ||
"description": "Fetch API compliant HTTP Server adapter", | ||
"sideEffects": false, | ||
"dependencies": { | ||
"@whatwg-node/fetch": "^0.9.17", | ||
"@whatwg-node/fetch": "^0.9.19", | ||
"tslib": "^2.6.3" | ||
@@ -9,0 +9,0 @@ }, |
@@ -7,3 +7,4 @@ export * from './createServerAdapter.js'; | ||
export * from './plugins/useErrorHandling.js'; | ||
export * from './plugins/useContentEncoding.js'; | ||
export * from './uwebsockets.js'; | ||
export { Response } from '@whatwg-node/fetch'; |
@@ -9,2 +9,3 @@ import { FetchAPI, ServerAdapterRequestHandler, type ServerAdapterInitialContext } from '../types.js'; | ||
request: Request; | ||
setRequest(newRequest: Request): void; | ||
serverContext: TServerContext; | ||
@@ -22,2 +23,4 @@ fetchAPI: FetchAPI; | ||
response: Response; | ||
setResponse(newResponse: Response): void; | ||
fetchAPI: FetchAPI; | ||
} |
@@ -7,7 +7,7 @@ import { ServerAdapterPlugin } from './types.js'; | ||
headers: HeadersInit; | ||
details?: any | undefined; | ||
details?: any; | ||
name: string; | ||
constructor(status: number, message: string, headers?: HeadersInit, details?: any | undefined); | ||
constructor(status: number, message: string, headers?: HeadersInit, details?: any); | ||
} | ||
export type ErrorHandler<TServerContext> = (e: any, request: Request, ctx: TServerContext) => Response | Promise<Response>; | ||
export declare function useErrorHandling<TServerContext>(onError?: ErrorHandler<TServerContext>): ServerAdapterPlugin<TServerContext>; |
@@ -5,3 +5,3 @@ import type { IncomingMessage, ServerResponse } from 'http'; | ||
import type { Readable } from 'stream'; | ||
import type { FetchEvent } from './types.js'; | ||
import type { FetchAPI, FetchEvent } from './types.js'; | ||
export declare function isAsyncIterable(body: any): body is AsyncIterable<any>; | ||
@@ -54,1 +54,4 @@ export interface NodeRequest { | ||
export declare function handleAbortSignalAndPromiseResponse(response$: Promise<Response> | Response, abortSignal?: AbortSignal | null): Response | Promise<Response>; | ||
export declare const decompressedResponseMap: WeakMap<Response, Response>; | ||
export declare function getSupportedEncodings(fetchAPI: FetchAPI): CompressionFormat[]; | ||
export declare function handleResponseDecompression(response: Response, fetchAPI: FetchAPI): Response; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
138012
39
2819
Updated@whatwg-node/fetch@^0.9.19