diff --git a/README.md b/README.md index 7f7146f..e30cfd6 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ jobs: - name: Create GitHub Deployment id: defaults continue-on-error: true # Setting to true so the job doesn't fail if updating the board fails. - uses: im-open/create-github-deployment@v2.0.0 # You may also reference just the major or major.minor version + uses: im-open/create-github-deployment@v2.0.1 # You may also reference just the major or major.minor version with: workflow-actor: ${{ github.actor }} # This will add the user who kicked off the workflow to the deployment payload token: ${{ secrets.GITHUB_TOKEN }} # If a different token is used, update github-login with the corresponding account diff --git a/dist/index.js b/dist/index.js index 4d9368c..022329c 100644 --- a/dist/index.js +++ b/dist/index.js @@ -335,6 +335,7 @@ var require_symbols = __commonJS({ kCounter: /* @__PURE__ */ Symbol('socket request counter'), kMaxResponseSize: /* @__PURE__ */ Symbol('max response size'), kHTTP2Session: /* @__PURE__ */ Symbol('http2Session'), + kHTTP2Options: /* @__PURE__ */ Symbol('http2 options'), kHTTP2SessionState: /* @__PURE__ */ Symbol('http2Session state'), kRetryHandlerDefaultRetry: /* @__PURE__ */ Symbol('retry agent default retry'), kConstruct: /* @__PURE__ */ Symbol('constructable'), @@ -938,6 +939,22 @@ var require_errors = __commonJS({ return true; } }; + var kProxyConnectionError = /* @__PURE__ */ Symbol.for('undici.error.UND_ERR_PRX_CONN'); + var ProxyConnectionError = class extends UndiciError { + constructor(cause, message, options = {}) { + super(message, { cause, ...options }); + this.name = 'ProxyConnectionError'; + this.message = message || 'Proxy Connection failed'; + this.code = 'UND_ERR_PRX_CONN'; + this.cause = cause; + } + static [Symbol.hasInstance](instance) { + return instance && instance[kProxyConnectionError] === true; + } + get [kProxyConnectionError]() { + return true; + } + }; var kMaxOriginsReachedError = /* @__PURE__ */ Symbol.for( 'undici.error.UND_ERR_MAX_ORIGINS_REACHED' ); @@ -1003,6 +1020,7 @@ var require_errors = __commonJS({ RequestRetryError, ResponseError, SecureProxyConnectionError, + ProxyConnectionError, MaxOriginsReachedError, Socks5ProxyError, MessageSizeExceededError @@ -1503,7 +1521,9 @@ var require_util = __commonJS({ if (Object.getPrototypeOf(stream2).constructor === IncomingMessage) { stream2.socket = null; } - stream2.destroy(err); + try { + stream2.destroy(err); + } catch {} } else if (err) { queueMicrotask(() => { stream2.emit('error', err); @@ -1858,7 +1878,18 @@ var require_util = __commonJS({ message += ` timeout: ${opts.timeout}ms)`; destroy(socket, new ConnectTimeoutError(message)); } + var lastUrlString = null; + var lastProtocol = null; function getProtocolFromUrlString(urlString) { + if (urlString === lastUrlString) { + return lastProtocol; + } + const protocol = getProtocolFromUrlStringSlow(urlString); + lastUrlString = urlString; + lastProtocol = protocol; + return protocol; + } + function getProtocolFromUrlStringSlow(urlString) { if ( urlString[0] === 'h' && urlString[1] === 't' && @@ -1892,7 +1923,9 @@ var require_util = __commonJS({ post: 'POST', POST: 'POST', put: 'PUT', - PUT: 'PUT' + PUT: 'PUT', + query: 'QUERY', + QUERY: 'QUERY' }; var normalizedMethodRecords = { ...normalizedMethodRecordsBase, @@ -2296,7 +2329,7 @@ var require_request = __commonJS({ this.headersTimeout = headersTimeout; this.bodyTimeout = bodyTimeout; this.method = method; - this.typeOfService = typeOfService ?? 0; + this.typeOfService = typeOfService; this.abort = null; if (body == null) { this.body = null; @@ -2340,7 +2373,10 @@ var require_request = __commonJS({ this.path = query ? serializePathWithQuery(path, query) : path; this.origin = origin; this.protocol = getProtocolFromUrlString(origin); - this.idempotent = idempotent == null ? method === 'HEAD' || method === 'GET' : idempotent; + this.idempotent = + idempotent == null + ? method === 'HEAD' || method === 'GET' || method === 'QUERY' + : idempotent; this.blocking = blocking ?? this.method !== 'HEAD'; this.reset = reset == null ? null : reset; this.host = null; @@ -2537,7 +2573,11 @@ var require_request = __commonJS({ } else if (typeof val[i] === 'object') { throw new InvalidArgumentError(`invalid ${key} header`); } else { - arr.push(`${val[i]}`); + const str = `${val[i]}`; + if (!isValidHeaderValue(str)) { + throw new InvalidArgumentError(`invalid ${key} header`); + } + arr.push(str); } } val = arr; @@ -2549,6 +2589,9 @@ var require_request = __commonJS({ val = ''; } else { val = `${val}`; + if (!isValidHeaderValue(val)) { + throw new InvalidArgumentError(`invalid ${key} header`); + } } if (headerName === 'host') { if (request2.host !== null) { @@ -2899,14 +2942,26 @@ var require_connect = __commonJS({ } else { assert(!httpSocket, 'httpSocket can only be sent on TLS update'); port = port || 80; - socket = net.connect({ + const connectOptions = { highWaterMark: 64 * 1024, // Same as nodejs fs streams. ...options, localAddress, port, host: hostname - }); + }; + const family = net.isIP(hostname); + if (family !== 0 && servername && servername !== hostname) { + connectOptions.host = servername; + connectOptions.lookup = (_hostname, lookupOptions, cb) => { + if (lookupOptions.all) { + cb(null, [{ address: hostname, family }]); + } else { + cb(null, hostname, family); + } + }; + } + socket = net.connect(connectOptions); if (useH2c === true) { socket.alpnProtocol = 'h2'; } @@ -3808,7 +3863,7 @@ var require_constants3 = __commonJS({ ['follow', 'manual', 'error']; var safeMethods = /** @type {const} */ - ['GET', 'HEAD', 'OPTIONS', 'TRACE']; + ['GET', 'HEAD', 'OPTIONS', 'TRACE', 'QUERY']; var safeMethodsSet = new Set(safeMethods); var requestMode = /** @type {const} */ @@ -5605,7 +5660,7 @@ var require_util2 = __commonJS({ if (rangeEndValue === null && rangeStartValue === null) { return 'failure'; } - if (rangeStartValue > rangeEndValue) { + if (rangeStartValue !== null && rangeEndValue !== null && rangeStartValue > rangeEndValue) { return 'failure'; } return { rangeStartValue, rangeEndValue }; @@ -6399,6 +6454,7 @@ var require_body = __commonJS({ var { multipartFormDataParser } = require_formdata_parser(); var { parseJSONFromBytes } = require_infra(); var { utf8DecodeBytes } = require_encoding(); + var { ReadableStreamTee } = require('node:stream/web'); var textEncoder = new TextEncoder(); function noop3() {} var streamRegistry = new FinalizationRegistry(weakRef => { @@ -6545,7 +6601,7 @@ Content-Type: ${value.type || 'application/octet-stream'}\r return extractBody(object, keepalive); } function cloneBody(body) { - const { 0: out1, 1: out2 } = body.stream.tee(); + const { 0: out1, 1: out2 } = ReadableStreamTee?.(body.stream, true) ?? body.stream.tee(); body.stream = out1; return { stream: out2, @@ -6721,6 +6777,7 @@ var require_client_h1 = __commonJS({ RequestContentLengthMismatchError, ResponseContentLengthMismatchError, RequestAbortedError, + InvalidArgumentError, HeadersTimeoutError, HeadersOverflowError, SocketError, @@ -6770,6 +6827,7 @@ var require_client_h1 = __commonJS({ var kIdleSocketValidation = /* @__PURE__ */ Symbol('kIdleSocketValidation'); var kIdleSocketValidationTimeout = /* @__PURE__ */ Symbol('kIdleSocketValidationTimeout'); var kSocketUsed = /* @__PURE__ */ Symbol('kSocketUsed'); + var kTypeOfService = /* @__PURE__ */ Symbol('kTypeOfService'); var extractBody; function lazyllhttp() { const llhttpWasmData = process.env.JEST_WORKER_ID ? require_llhttp_wasm() : void 0; @@ -7022,6 +7080,19 @@ var require_client_h1 = __commonJS({ assert(currentParser === null); assert(this.ptr != null); const { llhttp } = this; + if (this.paused) { + let data; + do { + llhttp.llhttp_resume(this.ptr); + this.paused = false; + data = this.socket.read() || EMPTY_BUF; + this.execute(data); + } while (this.paused && data.length > 0); + if (this.paused) { + llhttp.llhttp_resume(this.ptr); + this.paused = false; + } + } let ret; try { currentParser = this; @@ -7611,6 +7682,23 @@ var require_client_h1 = __commonJS({ method !== 'CONNECT' ); } + function setTypeOfService(socket, request2) { + if (typeof socket.setTypeOfService !== 'function') { + return; + } + const typeOfService = request2.typeOfService; + if (typeOfService === void 0) { + return; + } + const currentTypeOfService = socket[kTypeOfService]; + if (currentTypeOfService === typeOfService) { + return; + } + try { + socket.setTypeOfService(typeOfService); + socket[kTypeOfService] = typeOfService; + } catch {} + } function writeH1(client, request2) { const { method, path, host, upgrade, blocking, reset } = request2; let { body, headers, contentLength } = request2; @@ -7631,8 +7719,20 @@ var require_client_h1 = __commonJS({ } body = bodyStream.stream; contentLength = bodyStream.length; - } else if (util.isBlobLike(body) && request2.contentType == null && body.type) { - headers.push('content-type', body.type); + } else if (util.isBlobLike(body) && request2.contentType == null) { + const contentType = body.type; + if (contentType) { + const contentTypeValue = `${contentType}`; + if (!util.isValidHeaderValue(contentTypeValue)) { + util.errorRequest( + client, + request2, + new InvalidArgumentError('invalid content-type header') + ); + return false; + } + headers.push('content-type', contentTypeValue); + } } if (body && typeof body.read === 'function') { body.read(0); @@ -7690,9 +7790,7 @@ var require_client_h1 = __commonJS({ if (blocking) { socket[kBlocking] = true; } - if (socket.setTypeOfService) { - socket.setTypeOfService(request2.typeOfService); - } + setTypeOfService(socket, request2); let header = `${method} ${path} HTTP/1.1\r `; if (typeof host === 'string') { @@ -8184,10 +8282,7 @@ var require_client_h2 = __commonJS({ kStrictContentLength, kOnError, kMaxConcurrentStreams, - kPingInterval, kHTTP2Session, - kHTTP2InitialWindowSize, - kHTTP2ConnectionWindowSize, kHostAuthority, kResume, kSize, @@ -8199,7 +8294,8 @@ var require_client_h2 = __commonJS({ kEnableConnectProtocol, kRemoteSettings, kHTTP2Stream, - kHTTP2SessionState + kHTTP2SessionState, + kHTTP2Options } = require_symbols(); var { channels } = require_diagnostics(); var kOpenStreams = /* @__PURE__ */ Symbol('open streams'); @@ -8208,6 +8304,9 @@ var require_client_h2 = __commonJS({ var kRequestStreamCleanup = /* @__PURE__ */ Symbol('request stream cleanup'); var kRequestStreamState = /* @__PURE__ */ Symbol('request stream state'); var kReceivedGoAway = /* @__PURE__ */ Symbol('received goaway'); + var kGoAwayReplayAttempts = /* @__PURE__ */ Symbol('goaway replay attempts'); + var kRefusedStreamRetry = /* @__PURE__ */ Symbol('refused stream retry'); + var MAX_GOAWAY_REPLAY_ATTEMPTS = 1; var extractBody; var http2; try { @@ -8300,36 +8399,66 @@ var require_client_h2 = __commonJS({ client[kQueue].splice(client[kPendingIdx] + 1, 0, request2); } function completeRequest(client, request2, resetPendingIdx = false) { - const index = client[kQueue].indexOf(request2, client[kRunningIdx]); + const queue = client[kQueue]; + const runningIdx = client[kRunningIdx]; + if (runningIdx < client[kPendingIdx] && queue[runningIdx] === request2) { + queue[runningIdx] = null; + client[kRunningIdx] = runningIdx + 1; + return; + } + const index = queue.indexOf(request2, runningIdx); if (index === -1 || index >= client[kPendingIdx]) { return; } - client[kQueue].splice(index, 1); + queue.splice(index, 1); client[kPendingIdx]--; if (resetPendingIdx && client[kPendingIdx] < client[kRunningIdx]) { client[kPendingIdx] = client[kRunningIdx]; } } - function canRetryRequestAfterGoAway(request2) { + function canReplayRequest(request2) { const { body } = request2; return body == null || util.isBuffer(body) || util.isBlobLike(body); } - function closeRequestStream(request2, code = NGHTTP2_REFUSED_STREAM) { - const stream = request2[kRequestStream]; - clearRequestStream(request2); + function registerGoAwayRefusal(request2) { + const attempts = (request2[kGoAwayReplayAttempts] ?? 0) + 1; + request2[kGoAwayReplayAttempts] = attempts; + return attempts <= MAX_GOAWAY_REPLAY_ATTEMPTS; + } + function closeStream(stream, code = NGHTTP2_REFUSED_STREAM) { if (stream != null && !stream.destroyed && !stream.closed) { try { stream.close(code); } catch {} } } + function detachRequestStreamForClose(request2) { + const stream = request2[kRequestStream]; + clearRequestStream(request2); + severRequestStream(stream); + return stream; + } + function severRequestStream(stream) { + if (stream == null || stream[kRequestStreamState] == null) { + return; + } + stream[kRequestStreamState] = null; + stream.off('close', completeRequestStream); + stream.off('close', onUpgradeStreamClose); + if (stream[kHTTP2Session] != null) { + closeStreamSession(stream); + } + if (!stream.destroyed && !stream.closed) { + stream.once('error', noop3); + } + } function connectH2(client, socket) { client[kSocket] = socket; - const http2InitialWindowSize = client[kHTTP2InitialWindowSize]; - const http2ConnectionWindowSize = client[kHTTP2ConnectionWindowSize]; + const http2InitialWindowSize = client[kHTTP2Options].sessionOptions?.initialWindowSize; + const http2ConnectionWindowSize = client[kHTTP2Options].connectionWindowSize; const session = http2.connect(client[kUrl], { createConnection: () => socket, - peerMaxConcurrentStreams: client[kMaxConcurrentStreams], + peerMaxConcurrentStreams: client[kHTTP2Options].maxConcurrentStreams, settings: { // TODO(metcoder95): add support for PUSH enablePush: false, @@ -8342,11 +8471,19 @@ var require_client_h2 = __commonJS({ session[kSocket] = socket; session[kHTTP2SessionState] = { idleTimeout: null, + // Armed while the peer advertises MAX_CONCURRENT_STREAMS = 0 and we have + // work that cannot start. See setNoStreamsTimeout. + noStreamsTimeout: null, + // Sockets start out ref'd. Session ref/unref proxies to the socket, so a + // single cached flag lets us skip redundant uv ref/unref calls, provided + // every ref/unref of the session or its socket goes through + // refH2Session/unrefH2Session. + refed: true, ping: { interval: - client[kPingInterval] === 0 + client[kHTTP2Options].pingInterval === 0 ? null - : setInterval(onHttp2SendPing, client[kPingInterval], session).unref() + : setInterval(onHttp2SendPing, client[kHTTP2Options].pingInterval, session).unref() } }; session[kReceivedGoAway] = false; @@ -8361,11 +8498,10 @@ var require_client_h2 = __commonJS({ } util.addListener(session, 'error', onHttp2SessionError); util.addListener(session, 'frameError', onHttp2FrameError); - util.addListener(session, 'end', onHttp2SessionEnd); util.addListener(session, 'goaway', onHttp2SessionGoAway); util.addListener(session, 'close', onHttp2SessionClose); util.addListener(session, 'remoteSettings', onHttp2RemoteSettings); - session.unref(); + unrefH2Session(session); client[kHTTP2Session] = session; socket[kHTTP2Session] = session; util.addListener(socket, 'error', onHttp2SocketError); @@ -8424,13 +8560,6 @@ var require_client_h2 = __commonJS({ session[kRemoteSettings] === false ) return true; - if ( - util.bodyLength(request2.body) !== 0 && - (util.isStream(request2.body) || - util.isAsyncIterable(request2.body) || - util.isFormDataLike(request2.body)) - ) - return true; } else { return ( (request2.upgrade === 'websocket' || request2.method === 'CONNECT') && @@ -8442,23 +8571,80 @@ var require_client_h2 = __commonJS({ } }; } + function refH2Session(session) { + const state = session[kHTTP2SessionState]; + if (state.refed === false) { + state.refed = true; + session.ref(); + } + } + function unrefH2Session(session) { + const state = session[kHTTP2SessionState]; + if (state.refed === true) { + state.refed = false; + session.unref(); + } + } function resumeH2(client) { const socket = client[kSocket]; const session = client[kHTTP2Session]; if (socket?.destroyed === false) { if (client[kSize] === 0 || client[kMaxConcurrentStreams] === 0) { - socket.unref(); - session.unref(); + unrefH2Session(session); } else { - socket.ref(); - session.ref(); + refH2Session(session); } if (client[kSize] === 0 && session[kOpenStreams] === 0) { setHttp2IdleTimeout(session); } else { clearHttp2IdleTimeout(session); } + if (client[kMaxConcurrentStreams] === 0 && client[kRunning] === 0 && client[kPending] > 0) { + setNoStreamsTimeout(session); + } else { + clearNoStreamsTimeout(session); + } + } + } + function clearNoStreamsTimeout(session) { + const state = session[kHTTP2SessionState]; + if (state?.noStreamsTimeout != null) { + clearTimeout(state.noStreamsTimeout); + state.noStreamsTimeout = null; + } + } + function setNoStreamsTimeout(session) { + const client = session[kClient]; + const state = session[kHTTP2SessionState]; + const timeout = client[kHeadersTimeout]; + if (!timeout || state.noStreamsTimeout != null) { + return; + } + state.noStreamsTimeout = setTimeout(onNoStreamsTimeout, timeout, session).unref(); + } + function onNoStreamsTimeout(session) { + const client = session[kClient]; + const state = session[kHTTP2SessionState]; + state.noStreamsTimeout = null; + if ( + client[kHTTP2Session] !== session || + client[kMaxConcurrentStreams] !== 0 || + client[kRunning] !== 0 || + client[kPending] === 0 + ) { + return; + } + const err = new HeadersTimeoutError( + `HTTP/2: server did not accept a new stream within ${client[kHeadersTimeout]}` + ); + const requests = client[kQueue].splice(client[kPendingIdx]); + for (let i = 0; i < requests.length; i++) { + if (requests[i] != null) { + util.errorRequest(client, requests[i], err); + } } + session[kError] = err; + resetHttp2Session(session, err); } function clearHttp2IdleTimeout(session) { const state = session[kHTTP2SessionState]; @@ -8552,10 +8738,16 @@ var require_client_h2 = __commonJS({ function onHttp2SessionError(err) { assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID'); this[kSocket][kError] = err; + if (this[kReceivedGoAway]) { + return; + } this[kClient][kOnError](err); } function onHttp2FrameError(type, code, id) { if (id === 0) { + if (this[kReceivedGoAway]) { + return; + } const err = new InformationalError( `HTTP/2: "frameError" received - type ${type}, code ${code}` ); @@ -8563,11 +8755,6 @@ var require_client_h2 = __commonJS({ this[kClient][kOnError](err); } } - function onHttp2SessionEnd() { - const err = new SocketError('other side closed', util.getSocketInfo(this[kSocket])); - this.destroy(err); - util.destroy(this[kSocket], err); - } function onHttp2SessionGoAway(errorCode, lastStreamID) { if (this[kReceivedGoAway]) { return; @@ -8578,17 +8765,21 @@ var require_client_h2 = __commonJS({ const previousPendingIdx = client[kPendingIdx]; const pendingIdx = getGoAwayPendingIdx(client, lastStreamID); const retriableRequests = []; + const streamsToClose = []; for (let i = pendingIdx; i < previousPendingIdx; i++) { const request2 = client[kQueue][i]; if (request2 != null) { - closeRequestStream(request2); - if (canRetryRequestAfterGoAway(request2)) { + streamsToClose.push(detachRequestStreamForClose(request2)); + if (canReplayRequest(request2) && registerGoAwayRefusal(request2)) { retriableRequests.push(request2); } else { util.errorRequest(client, request2, err); } } } + for (let i = 0; i < streamsToClose.length; i++) { + closeStream(streamsToClose[i]); + } if (pendingIdx !== previousPendingIdx) { const remainingPendingRequests = client[kQueue].slice(previousPendingIdx); client[kQueue].length = pendingIdx; @@ -8600,6 +8791,7 @@ var require_client_h2 = __commonJS({ client[kHTTP2Session] = null; } clearHttp2IdleTimeout(this); + clearNoStreamsTimeout(this); if (!this.closed && !this.destroyed) { this.close(); } @@ -8617,6 +8809,7 @@ var require_client_h2 = __commonJS({ client[kHTTP2Session] = null; } clearHttp2IdleTimeout(this); + clearNoStreamsTimeout(this); if (state.ping.interval != null) { clearInterval(state.ping.interval); state.ping.interval = null; @@ -8658,7 +8851,10 @@ var require_client_h2 = __commonJS({ function onHttp2SocketError(err) { assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID'); this[kError] = err; - this[kClient][kOnError](err); + if (this[kHTTP2Session]?.[kReceivedGoAway]) { + return; + } + this[kHTTP2Session]?.[kClient]?.[kOnError](err); } function onHttp2SocketEnd() { util.destroy(this, new SocketError('other side closed', util.getSocketInfo(this))); @@ -8672,7 +8868,7 @@ var require_client_h2 = __commonJS({ stream[kHTTP2Session] = null; session[kOpenStreams] -= 1; if (session[kOpenStreams] === 0) { - session.unref(); + unrefH2Session(session); setHttp2IdleTimeout(session); } } @@ -8686,17 +8882,22 @@ var require_client_h2 = __commonJS({ ); closeStreamSession(this); } - function onRequestStreamClose() { + function completeRequestStream() { const state = this[kRequestStreamState]; - if (state) { - releaseRequestStream(this); - if (state.pendingEnd && !state.request.aborted && !state.request.completed) { - state.request.onResponseEnd(state.trailers || {}); - state.finalizeRequest(); - } + if (state == null) { + return; } - this.off('data', onData); - this.off('error', noop3); + releaseRequestStream(this); + if (state.pendingEnd && !state.request.aborted && !state.request.completed) { + state.request.onResponseEnd(state.trailers || {}); + } else if (!state.request.aborted && !state.request.completed) { + util.errorRequest( + state.client, + state.request, + new InformationalError('HTTP/2: stream closed before the response was complete') + ); + } + finalizeRequest(state); closeStreamSession(this); this[kRequestStreamState] = null; } @@ -8808,7 +9009,7 @@ var require_client_h2 = __commonJS({ } removeUpgradeStreamListeners(stream); detachRequestFromStream(request2); - state.finalizeRequest(); + finalizeRequest(state); } function setupUpgradeStream(stream, state) { const { request: request2, headersTimeout, session } = state; @@ -8826,6 +9027,35 @@ var require_client_h2 = __commonJS({ ++session[kOpenStreams]; stream.setTimeout(headersTimeout); } + function finalizeRequest(state, resetPendingIdx = false) { + if (state.requestFinalized) { + return; + } + state.requestFinalized = true; + completeRequest(state.client, state.request, resetPendingIdx); + state.client[kResume](); + } + function openStream(client, request2, session, abort, headers, options) { + try { + return session.request(headers, options); + } catch (err) { + if (err?.code === 'ERR_HTTP2_INVALID_SESSION' || err?.code === 'ERR_HTTP2_GOAWAY_SESSION') { + const wrappedErr2 = new SocketError(err.message, util.getSocketInfo(session[kSocket])); + wrappedErr2.cause = err; + session[kError] = wrappedErr2; + resetHttp2Session(session, wrappedErr2); + requeueUnsentRequest(client, request2); + return null; + } + const wrappedErr = new InformationalError(err.message, { cause: err }); + session[kError] = wrappedErr; + session[kSocket][kError] = wrappedErr; + session.destroy(wrappedErr); + util.destroy(session[kSocket], wrappedErr); + abort(wrappedErr); + return null; + } + } function writeH2(client, request2) { const headersTimeout = request2.headersTimeout ?? client[kHeadersTimeout]; const bodyTimeout = request2.bodyTimeout ?? client[kBodyTimeout]; @@ -8840,7 +9070,6 @@ var require_client_h2 = __commonJS({ protocol, headers: reqHeaders } = request2; - let { body } = request2; if (upgrade != null && upgrade !== 'websocket') { util.errorRequest( client, @@ -8850,17 +9079,24 @@ var require_client_h2 = __commonJS({ return false; } const headers = buildRequestHeaders(reqHeaders); - let stream = null; headers[HTTP2_HEADER_AUTHORITY] = host || client[kHostAuthority]; headers[HTTP2_HEADER_METHOD] = method; - let requestFinalized = false; - const finalizeRequest = (resetPendingIdx = false) => { - if (requestFinalized) { - return; - } - requestFinalized = true; - completeRequest(client, request2, resetPendingIdx); - client[kResume](); + const state = { + abort: null, + body: request2.body, + client, + contentLength: null, + expectsPayload: false, + request: request2, + headersTimeout, + bodyTimeout, + requestFinalized: false, + responseReceived: false, + bodySent: false, + pendingEnd: false, + trailers: null, + session, + stream: null }; const abort = (err, resetPendingIdx = false) => { if (request2.aborted || request2.completed) { @@ -8868,35 +9104,20 @@ var require_client_h2 = __commonJS({ } err = err || new RequestAbortedError(); util.errorRequest(client, request2, err); - if (stream != null) { + if (state.stream != null) { clearRequestStream(request2); - stream.close(); + const stream2 = state.stream; + stream2.close(); + if (!stream2.destroyed) { + util.destroy(stream2); + } client[kOnError](err); - finalizeRequest(resetPendingIdx); - } - util.destroy(body, err); - }; - const requestStream = (headers2, options) => { - try { - return session.request(headers2, options); - } catch (err) { - if (err?.code === 'ERR_HTTP2_INVALID_SESSION') { - const wrappedErr2 = new SocketError(err.message, util.getSocketInfo(session[kSocket])); - wrappedErr2.cause = err; - session[kError] = wrappedErr2; - resetHttp2Session(session, wrappedErr2); - requeueUnsentRequest(client, request2); - return null; - } - const wrappedErr = new InformationalError(err.message, { cause: err }); - session[kError] = wrappedErr; - session[kSocket][kError] = wrappedErr; - session.destroy(wrappedErr); - util.destroy(session[kSocket], wrappedErr); - abort(wrappedErr); - return null; + finalizeRequest(state, resetPendingIdx); } + util.destroy(state.body, err); }; + state.abort = abort; + let stream = null; try { request2.onRequestStart(abort, null); } catch (err) { @@ -8906,17 +9127,7 @@ var require_client_h2 = __commonJS({ return false; } if (upgrade || method === 'CONNECT') { - session.ref(); - const upgradeState = { - abort, - finalizeRequest, - request: request2, - headersTimeout, - bodyTimeout, - responseReceived: false, - session, - stream: null - }; + refH2Session(session); if (upgrade === 'websocket') { if (session[kEnableConnectProtocol] === false) { util.errorRequest( @@ -8924,7 +9135,7 @@ var require_client_h2 = __commonJS({ request2, new InformationalError('HTTP/2: Extended CONNECT protocol not supported by server') ); - session.unref(); + unrefH2Session(session); return false; } headers[HTTP2_HEADER_METHOD] = 'CONNECT'; @@ -8935,20 +9146,26 @@ var require_client_h2 = __commonJS({ } else { headers[HTTP2_HEADER_SCHEME] = protocol === 'http:' ? 'http' : 'https'; } - stream = requestStream(headers, { endStream: false, signal }); + stream = openStream(client, request2, session, abort, headers, { + endStream: false, + signal + }); if (stream == null) { - session.unref(); + unrefH2Session(session); return false; } - setupUpgradeStream(stream, upgradeState); + setupUpgradeStream(stream, state); return true; } - stream = requestStream(headers, { endStream: false, signal }); + stream = openStream(client, request2, session, abort, headers, { + endStream: false, + signal + }); if (stream == null) { - session.unref(); + unrefH2Session(session); return false; } - setupUpgradeStream(stream, upgradeState); + setupUpgradeStream(stream, state); return true; } headers[HTTP2_HEADER_PATH] = path; @@ -8960,6 +9177,7 @@ var require_client_h2 = __commonJS({ method === 'QUERY' || method === 'PROPFIND' || method === 'PROPPATCH'; + let body = state.body; if (body && typeof body.read === 'function') { body.read(0); } @@ -8993,7 +9211,7 @@ var require_client_h2 = __commonJS({ assert(body || contentLength === 0, 'no body must not have content length'); headers[HTTP2_HEADER_CONTENT_LENGTH] = `${contentLength}`; } - session.ref(); + refH2Session(session); if (channels.sendHeaders.hasSubscribers) { let header = ''; for (const key in headers) { @@ -9007,24 +9225,16 @@ var require_client_h2 = __commonJS({ }); } const shouldEndStream = body === null || contentLength === 0; - const state = { - abort, - body, - client, - contentLength, - expectsPayload, - finalizeRequest, - request: request2, - headersTimeout, - bodyTimeout, - responseReceived: false, - session, - stream: null - }; + state.body = body; + state.contentLength = contentLength; + state.expectsPayload = expectsPayload; if (expectContinue) { headers[HTTP2_HEADER_EXPECT] = '100-continue'; } - stream = requestStream(headers, { endStream: shouldEndStream, signal }); + stream = openStream(client, request2, session, abort, headers, { + endStream: shouldEndStream, + signal + }); if (stream == null) { return false; } @@ -9033,20 +9243,24 @@ var require_client_h2 = __commonJS({ state.stream = stream; clearHttp2IdleTimeout(session); ++session[kOpenStreams]; - stream.setTimeout(headersTimeout); + if (headersTimeout) { + stream.setTimeout(headersTimeout); + } stream[kHTTP2Session] = session; - stream.once('close', onRequestStreamClose); + stream.on('close', completeRequestStream); bindRequestToStream(request2, stream, releaseRequestStream); if (expectContinue) { stream.once('continue', writeBodyH2); } - stream.once('response', onResponse); - stream.once('end', onEnd); - stream.once('error', onError); - stream.once('frameError', onFrameError); + stream.on('response', onResponse); + stream.on('end', onEnd); + stream.on('error', onError); + stream.on('frameError', onFrameError); stream.on('aborted', onAborted); - stream.on('timeout', onTimeout); - stream.once('trailers', onTrailers); + if (headersTimeout || bodyTimeout) { + stream.on('timeout', onTimeout); + } + stream.on('trailers', onTrailers); if (!expectContinue) { writeBodyH2.call(stream); } @@ -9076,14 +9290,18 @@ var require_client_h2 = __commonJS({ if (request2[kRequestStream] === stream) { detachRequestFromStream(request2); } - removeRequestStreamListeners(stream); if (!stream.destroyed && !stream.closed) { + removeRequestStreamListeners(stream); stream.once('error', noop3); } } function onData(chunk) { const stream = this; - const { request: request2 } = stream[kRequestStreamState]; + const state = stream[kRequestStreamState]; + if (state == null) { + return; + } + const { request: request2 } = state; if (request2.aborted || request2.completed) { return; } @@ -9094,14 +9312,23 @@ var require_client_h2 = __commonJS({ function onResponse(headers) { const stream = this; const state = stream[kRequestStreamState]; + if (state == null) { + return; + } const { request: request2 } = state; stream.off('response', onResponse); + if (state.body != null && !state.bodySent && !stream.writableEnded) { + stream.removeListener('continue', writeBodyH2); + stream.end(); + } const statusCode = headers[HTTP2_HEADER_STATUS]; delete headers[HTTP2_HEADER_STATUS]; request2.onResponseStarted(); state.responseReceived = true; - stream.setTimeout(state.bodyTimeout); - if (request2.aborted) { + if (state.headersTimeout || state.bodyTimeout) { + stream.setTimeout(state.bodyTimeout); + } + if (request2.aborted || request2.completed) { releaseRequestStream(stream); return; } @@ -9116,25 +9343,61 @@ var require_client_h2 = __commonJS({ function onEnd() { const stream = this; const state = stream[kRequestStreamState]; + if (state == null) { + return; + } const { request: request2 } = state; stream.off('end', onEnd); if (state.responseReceived) { if (!request2.aborted && !request2.completed) { state.pendingEnd = true; + completeRequestStream.call(stream); } } else { state.abort(new InformationalError('HTTP/2: stream half-closed (remote)'), true); } } + function retryRefusedStream(stream, state) { + const { client, request: request2 } = state; + if ( + state.responseReceived || + request2.aborted || + request2.completed || + request2[kRefusedStreamRetry] || + !canReplayRequest(request2) + ) { + return false; + } + request2[kRefusedStreamRetry] = true; + detachRequestStreamForClose(request2); + state.stream = null; + state.requestFinalized = true; + completeRequest(client, request2); + client[kQueue].splice(client[kPendingIdx], 0, request2); + client[kResume](); + return true; + } function onError(err) { const stream = this; const state = stream[kRequestStreamState]; + if (state == null) { + return; + } stream.off('error', onError); + if (typeof stream.rstCode === 'number' && stream.rstCode !== NGHTTP2_NO_ERROR) { + err.http2ErrorCode = stream.rstCode; + } + if (stream.rstCode === NGHTTP2_REFUSED_STREAM && retryRefusedStream(stream, state)) { + return; + } state.abort(err); } function onFrameError(type, code) { const stream = this; const state = stream[kRequestStreamState]; + if (state == null) { + return; + } stream.off('frameError', onFrameError); state.abort( new InformationalError(`HTTP/2: "frameError" received - type ${type}, code ${code}`) @@ -9146,6 +9409,9 @@ var require_client_h2 = __commonJS({ function onTimeout() { const stream = this; const state = stream[kRequestStreamState]; + if (state == null) { + return; + } stream.off('timeout', onTimeout); const err = state.responseReceived ? new BodyTimeoutError(`HTTP/2: "stream timeout after ${state.bodyTimeout}"`) @@ -9155,6 +9421,9 @@ var require_client_h2 = __commonJS({ function onTrailers(trailers) { const stream = this; const state = stream[kRequestStreamState]; + if (state == null) { + return; + } const { request: request2 } = state; stream.off('trailers', onTrailers); stream.off('data', onData); @@ -9166,6 +9435,7 @@ var require_client_h2 = __commonJS({ function writeBodyH2() { const stream = this; const state = stream[kRequestStreamState]; + state.bodySent = true; const { abort, body, client, contentLength, expectsPayload, request: request2 } = state; if (!body || contentLength === 0) { writeBuffer( @@ -9434,10 +9704,8 @@ var require_client = __commonJS({ kHTTPContext, kMaxConcurrentStreams, kHostAuthority, - kHTTP2InitialWindowSize, - kHTTP2ConnectionWindowSize, kResume, - kPingInterval + kHTTP2Options } = require_symbols(); var connectH1 = require_client_h1(); var connectH2 = require_client_h2(); @@ -9454,6 +9722,17 @@ var require_client = __commonJS({ function getPipelining(client) { return client[kPipelining] ?? client[kHTTPContext]?.defaultPipelining ?? 1; } + var h2NamespaceOptsWarning = false; + function emitH2OptionsNamespaceWarning(optName) { + if (h2NamespaceOptsWarning === true) return; + process.emitWarning( + `Use h2Options.${optName} instead. ${optName} for H2 will be deprecated in future major.`, + { + code: 'UNDICI-H2-OPTIONS' + } + ); + h2NamespaceOptsWarning = true; + } function getMaxConcurrent(client) { if (client[kHTTPContext]?.version === 'h2') { return client[kMaxConcurrentStreams]; @@ -9499,7 +9778,8 @@ var require_client = __commonJS({ initialWindowSize, connectionWindowSize, pingInterval, - webSocket + webSocket, + h2Options } = {} ) { if (keepAlive !== void 0) { @@ -9589,40 +9869,92 @@ var require_client = __commonJS({ if (allowH2 != null && typeof allowH2 !== 'boolean') { throw new InvalidArgumentError('allowH2 must be a valid boolean value'); } - if ( - maxConcurrentStreams != null && - (typeof maxConcurrentStreams !== 'number' || maxConcurrentStreams < 1) - ) { - throw new InvalidArgumentError( - 'maxConcurrentStreams must be a positive integer, greater than 0' - ); - } - if (useH2c != null && typeof useH2c !== 'boolean') { - throw new InvalidArgumentError('useH2c must be a valid boolean value'); - } - if ( - initialWindowSize != null && - (!Number.isInteger(initialWindowSize) || initialWindowSize < 1) - ) { - throw new InvalidArgumentError( - 'initialWindowSize must be a positive integer, greater than 0' - ); - } - if ( - connectionWindowSize != null && - (!Number.isInteger(connectionWindowSize) || connectionWindowSize < 1) - ) { - throw new InvalidArgumentError( - 'connectionWindowSize must be a positive integer, greater than 0' - ); - } - if ( - pingInterval != null && - (typeof pingInterval !== 'number' || !Number.isInteger(pingInterval) || pingInterval < 0) - ) { - throw new InvalidArgumentError( - 'pingInterval must be a positive integer, greater or equal to 0' - ); + if (allowH2 !== false) { + if (h2Options != null) { + if (h2Options.useH2c != null && typeof h2Options.useH2c !== 'boolean') { + throw new InvalidArgumentError('h2Options.useH2c must be a valid boolean value'); + } + if ( + h2Options.settings?.initialWindowSize != null && + (!Number.isInteger(h2Options.settings.initialWindowSize) || + h2Options.settings.initialWindowSize < 1) + ) { + throw new InvalidArgumentError( + 'h2Options.settings.initialWindowSize must be a positive integer, greater than 0' + ); + } + if ( + h2Options.maxConcurrentStreams != null && + (!Number.isInteger(h2Options.connectionWindowSize) || + h2Options.maxConcurrentStreams < 1) + ) { + throw new InvalidArgumentError( + 'h2Options.maxConcurrentStreams must be a positive integer, greater than 0' + ); + } + if ( + h2Options.connectionWindowSize != null && + (!Number.isInteger(h2Options.connectionWindowSize) || + h2Options.connectionWindowSize < 1) + ) { + throw new InvalidArgumentError( + 'h2Options.connectionWindowSize must be a positive integer, greater than 0' + ); + } + if ( + h2Options.pingInterval != null && + (typeof h2Options.pingInterval !== 'number' || + !Number.isInteger(h2Options.pingInterval) || + h2Options.pingInterval < 0) + ) { + throw new InvalidArgumentError( + 'h2Options.pingInterval must be a positive integer, greater or equal to 0' + ); + } + } else { + if (useH2c != null && typeof useH2c !== 'boolean') { + emitH2OptionsNamespaceWarning('useH2c'); + throw new InvalidArgumentError('useH2c must be a valid boolean value'); + } + if ( + maxConcurrentStreams != null && + (typeof maxConcurrentStreams !== 'number' || maxConcurrentStreams < 1) + ) { + emitH2OptionsNamespaceWarning('maxConcurrentStreams'); + throw new InvalidArgumentError( + 'maxConcurrentStreams must be a positive integer, greater than 0' + ); + } + if ( + initialWindowSize != null && + (!Number.isInteger(initialWindowSize) || initialWindowSize < 1) + ) { + emitH2OptionsNamespaceWarning('initialWindowSize'); + throw new InvalidArgumentError( + 'initialWindowSize must be a positive integer, greater than 0' + ); + } + if ( + connectionWindowSize != null && + (!Number.isInteger(connectionWindowSize) || connectionWindowSize < 1) + ) { + emitH2OptionsNamespaceWarning('connectionWindowSize'); + throw new InvalidArgumentError( + 'connectionWindowSize must be a positive integer, greater than 0' + ); + } + if ( + pingInterval != null && + (typeof pingInterval !== 'number' || + !Number.isInteger(pingInterval) || + pingInterval < 0) + ) { + emitH2OptionsNamespaceWarning('pingInterval'); + throw new InvalidArgumentError( + 'pingInterval must be a positive integer, greater or equal to 0' + ); + } + } } super({ webSocket }); if (typeof connect2 !== 'function') { @@ -9630,8 +9962,8 @@ var require_client = __commonJS({ ...tls, maxCachedSessions, allowH2, - useH2c, socketPath, + useH2c: h2Options?.useH2c ?? useH2c, timeout: connectTimeout, ...(typeof autoSelectFamily === 'boolean' ? { autoSelectFamily, autoSelectFamilyAttemptTimeout } @@ -9675,11 +10007,21 @@ var require_client = __commonJS({ this[kClosedResolve] = null; this[kMaxResponseSize] = maxResponseSize > -1 ? maxResponseSize : -1; this[kHTTPContext] = null; - this[kMaxConcurrentStreams] = maxConcurrentStreams != null ? maxConcurrentStreams : 100; - this[kHTTP2InitialWindowSize] = initialWindowSize != null ? initialWindowSize : 262144; - this[kHTTP2ConnectionWindowSize] = - connectionWindowSize != null ? connectionWindowSize : 524288; - this[kPingInterval] = pingInterval != null ? pingInterval : 6e4; + this[kHTTP2Options] = { + pingInterval: h2Options?.pingInterval ?? pingInterval ?? 6e4, + connectionWindowSize: h2Options?.connectionWindowSize ?? connectionWindowSize ?? 524288, + maxConcurrentStreams: h2Options?.maxConcurrentStreams ?? maxConcurrentStreams ?? 100, + // Max peerConcurrentStreams for a Node h2 server + sessionOptions: { + // HTTP/2 window sizes are set to higher defaults than Node.js core for better performance: + // - initialWindowSize: 262144 (256KB) vs Node.js default 65535 (64KB - 1) + // Allows more data to be sent before requiring acknowledgment, improving throughput + // especially on high-latency networks. This matches common production HTTP/2 servers. + // - connectionWindowSize: 524288 (512KB) vs Node.js default (none set) + // Provides better flow control for the entire connection across multiple streams. + initialWindowSize: h2Options?.initialWindowSize ?? initialWindowSize ?? 262144 + } + }; this[kQueue] = []; this[kRunningIdx] = 0; this[kPendingIdx] = 0; @@ -9975,6 +10317,7 @@ var require_client = __commonJS({ return; } if (!client[kHTTPContext]) { + client[kServerName] = request2.servername; connect(client); return; } @@ -10812,14 +11155,14 @@ var require_agent = __commonJS({ dispatcher.close(); } let hasOrigin = false; - for (const client of this[kClients].values()) { - if (client[kUrl].origin === dispatcher[kUrl].origin) { + for (const k of this[kClients].keys()) { + if (k === origin || k === `${origin}#http1-only`) { hasOrigin = true; break; } } if (!hasOrigin) { - this[kOrigins].delete(dispatcher[kUrl].origin); + this[kOrigins].delete(origin); } }; dispatcher @@ -11477,7 +11820,7 @@ var require_socks5_proxy_agent = __commonJS({ var DispatcherBase = require_dispatcher_base(); var { InvalidArgumentError } = require_errors(); var { Socks5Client, STATES } = require_socks5_client(); - var { kDispatch, kClose, kDestroy } = require_symbols(); + var { kBusy, kConnected, kDispatch, kClose, kDestroy } = require_symbols(); var Pool = require_pool(); var buildConnector = require_connect(); var { debuglog } = require('node:util'); @@ -11640,6 +11983,17 @@ var require_socks5_proxy_agent = __commonJS({ } }); this[kPools].set(originKey, pool); + const closePoolIfUnused = () => { + if (this[kPools].get(originKey) !== pool || pool[kConnected] > 0 || pool[kBusy]) { + return; + } + this[kPools].delete(originKey); + if (!pool.destroyed) { + pool.close(); + } + }; + pool.on('disconnect', closePoolIfUnused); + pool.on('connectionError', closePoolIfUnused); } return pool[kDispatch](opts, handler2); } catch (err) { @@ -11684,8 +12038,12 @@ var require_proxy_agent = __commonJS({ var Agent = require_agent(); var Pool = require_pool(); var DispatcherBase = require_dispatcher_base(); - var { InvalidArgumentError, RequestAbortedError, SecureProxyConnectionError } = - require_errors(); + var { + InvalidArgumentError, + RequestAbortedError, + SecureProxyConnectionError, + ProxyConnectionError + } = require_errors(); var buildConnector = require_connect(); var Client = require_client(); var { channels } = require_diagnostics(); @@ -11712,14 +12070,19 @@ var require_proxy_agent = __commonJS({ } return new Pool(origin, opts); } + function shouldProxyTunnel(requestProtocol, proxyTunnel) { + return proxyTunnel === true || requestProtocol !== 'http:'; + } var Http1ProxyWrapper = class extends DispatcherBase { #client; - constructor(proxyUrl, { headers = {}, connect, factory }) { + #proxyServername; + constructor(proxyUrl, { headers = {}, connect, factory, proxyServername }) { if (!proxyUrl) { throw new InvalidArgumentError('Proxy URL is mandatory'); } super(); this[kProxyHeaders] = headers; + this.#proxyServername = proxyServername; if (factory) { this.#client = factory(proxyUrl, { connect }); } else { @@ -11748,6 +12111,9 @@ var require_proxy_agent = __commonJS({ headers.host = host; } opts.headers = { ...this[kProxyHeaders], ...headers }; + if (this.#proxyServername != null) { + opts.servername = this.#proxyServername; + } return this.#client[kDispatch](opts, handler2); } [kClose]() { @@ -11766,7 +12132,7 @@ var require_proxy_agent = __commonJS({ if (typeof clientFactory !== 'function') { throw new InvalidArgumentError('Proxy opts.clientFactory must be a function.'); } - const { proxyTunnel = true, connectTimeout } = opts; + const { proxyTunnel, connectTimeout } = opts; super(); const url = this.#getUrl(opts); const { href, origin, port, protocol, username, password, hostname: proxyHostname } = url; @@ -11791,6 +12157,11 @@ var require_proxy_agent = __commonJS({ ).toString('base64')}`; } const connect = buildConnector({ timeout: connectTimeout, ...opts.proxyTls }); + const connectHTTP1 = buildConnector({ + timeout: connectTimeout, + ...opts.proxyTls, + allowH2: false + }); this[kConnectEndpoint] = buildConnector({ timeout: connectTimeout, ...opts.requestTls }); this[kConnectEndpointHTTP1] = buildConnector({ timeout: connectTimeout, @@ -11811,11 +12182,26 @@ var require_proxy_agent = __commonJS({ requestTls: opts.requestTls }); } - if (!this[kTunnelProxy] && protocol2 === 'http:' && this[kProxy].protocol === 'http:') { + if (!shouldProxyTunnel(protocol2, this[kTunnelProxy])) { + const forwardConnect = + this[kProxy].protocol === 'https:' + ? (opts2, cb) => + connectHTTP1(opts2, (err, socket) => { + if (err && err.code === 'ERR_TLS_CERT_ALTNAME_INVALID') { + cb(new SecureProxyConnectionError(err)); + } else { + cb(err, socket); + } + }) + : connectHTTP1; return new Http1ProxyWrapper(this[kProxy].uri, { headers: this[kProxyHeaders], - connect, - factory: agentFactory + connect: forwardConnect, + factory: agentFactory, + proxyServername: + this[kProxy].protocol === 'https:' + ? this[kProxyTls]?.servername || proxyHostname + : void 0 }); } return agentFactory(origin2, options); @@ -11888,6 +12274,8 @@ var require_proxy_agent = __commonJS({ } catch (err) { if (err.code === 'ERR_TLS_CERT_ALTNAME_INVALID') { callback(new SecureProxyConnectionError(err)); + } else if (err.code === 'UND_ERR_SOCKET') { + callback(new ProxyConnectionError(err)); } else { callback(err); } @@ -12035,7 +12423,10 @@ var require_env_http_proxy_agent = __commonJS({ } #getProxyAgentForUrl(url) { let { protocol, host: hostname, port } = url; - hostname = hostname.replace(/:\d*$/, '').toLowerCase(); + hostname = hostname + .replace(/:\d*$/, '') + .replace(/^\[(.+)\]$/, '$1') + .toLowerCase(); port = Number.parseInt(port, 10) || DEFAULT_PORTS[protocol] || 0; if (!this.#shouldProxy(hostname, port)) { return this[kNoProxyAgent]; @@ -12078,11 +12469,22 @@ var require_env_http_proxy_agent = __commonJS({ if (!entry) { continue; } - const parsed = entry.match(/^(.+):(\d+)$/); + let hostname, port; + const ipv6WithPort = entry.match(/^\[(.+)\]:(\d+)$/); + if (ipv6WithPort) { + hostname = ipv6WithPort[1]; + port = Number.parseInt(ipv6WithPort[2], 10); + } else { + const unbracketed = entry.replace(/^\[(.+)\]$/, '$1'); + const colonCount = (unbracketed.match(/:/g) || []).length; + const parsed = colonCount === 1 && unbracketed.match(/^(.+):(\d+)$/); + hostname = parsed ? parsed[1] : unbracketed; + port = parsed ? Number.parseInt(parsed[2], 10) : 0; + } noProxyEntries.push({ // strip leading dot or asterisk with dot - hostname: (parsed ? parsed[1] : entry).replace(/^\*?\./, '').toLowerCase(), - port: parsed ? Number.parseInt(parsed[2], 10) : 0 + hostname: hostname.replace(/^\*?\./, '').toLowerCase(), + port }); } this.#noProxyValue = noProxyValue; @@ -12112,8 +12514,54 @@ var require_retry_handler = __commonJS({ var { isDisturbed, parseRangeHeader, wrapRequestBody } = require_util(); function calculateRetryAfterHeader(retryAfter) { const retryTime = new Date(retryAfter).getTime(); - return isNaN(retryTime) ? 0 : retryTime - Date.now(); + return isNaN(retryTime) ? null : retryTime - Date.now(); + } + function validatePartialResponseContentLength(headers, range, statusCode, retryCount) { + const contentLength = headers['content-length']; + if (contentLength == null) { + return; + } + if (!Number.isFinite(range.start) || !Number.isFinite(range.end)) { + return; + } + const length = Number(contentLength); + const expectedLength = range.end - range.start + 1; + if (!Number.isFinite(length) || length !== expectedLength) { + throw new RequestRetryError('Content-Length mismatch', statusCode, { + headers, + data: { count: retryCount } + }); + } } + var RetryController = class { + constructor() { + this.target = null; + } + pause() { + this.target?.pause(); + } + resume() { + this.target?.resume(); + } + abort(reason) { + this.target?.abort(reason); + } + get paused() { + return this.target?.paused ?? false; + } + get aborted() { + return this.target?.aborted ?? false; + } + get reason() { + return this.target?.reason ?? null; + } + get rawHeaders() { + return this.target?.rawHeaders ?? null; + } + get rawTrailers() { + return this.target?.rawTrailers ?? null; + } + }; var RetryHandler = class _RetryHandler { constructor(opts, { dispatch, handler: handler2 }) { const { retryOptions, ...dispatchOpts } = opts; @@ -12146,7 +12594,7 @@ var require_retry_handler = __commonJS({ timeoutFactor: timeoutFactor ?? 2, maxRetries: maxRetries ?? 5, // What errors we should retry - methods: methods ?? ['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE'], + methods: methods ?? ['GET', 'HEAD', 'OPTIONS', 'PUT', 'DELETE', 'TRACE', 'QUERY'], // Indicates which errors to retry statusCodes: statusCodes ?? [500, 502, 503, 504, 429], // List of errors to retry @@ -12170,12 +12618,18 @@ var require_retry_handler = __commonJS({ this.etag = null; this.statusCode = null; this.headers = null; + this.controllerProxy = new RetryController(); } onResponseStartWithRetry(controller, statusCode, headers, statusMessage, err) { if (this.retryOpts.throwOnError) { if (this.retryOpts.statusCodes.includes(statusCode) === false) { this.headersSent = true; - this.handler.onResponseStart?.(controller, statusCode, headers, statusMessage); + this.handler.onResponseStart?.( + this.controllerProxy, + statusCode, + headers, + statusMessage + ); } else { this.error = err; } @@ -12183,13 +12637,18 @@ var require_retry_handler = __commonJS({ } if (isDisturbed(this.opts.body)) { this.headersSent = true; - this.handler.onResponseStart?.(controller, statusCode, headers, statusMessage); + this.handler.onResponseStart?.(this.controllerProxy, statusCode, headers, statusMessage); return; } function shouldRetry(passedErr) { if (passedErr) { this.headersSent = true; - this.handler.onResponseStart?.(controller, statusCode, headers, statusMessage); + this.handler.onResponseStart?.( + this.controllerProxy, + statusCode, + headers, + statusMessage + ); controller.resume(); return; } @@ -12207,12 +12666,13 @@ var require_retry_handler = __commonJS({ ); } onRequestStart(controller, context4) { + this.controllerProxy.target = controller; if (!this.headersSent) { - this.handler.onRequestStart?.(controller, context4); + this.handler.onRequestStart?.(this.controllerProxy, context4); } } - onRequestUpgrade(controller, statusCode, headers, socket) { - this.handler.onRequestUpgrade?.(controller, statusCode, headers, socket); + onRequestUpgrade(_controller, statusCode, headers, socket) { + this.handler.onRequestUpgrade?.(this.controllerProxy, statusCode, headers, socket); } static [kRetryHandlerDefaultRetry](err, { state, opts }, cb) { const { statusCode, code, headers } = err; @@ -12251,12 +12711,18 @@ var require_retry_handler = __commonJS({ : retryAfterHeader * 1e3; } const retryTimeout = - retryAfterHeader > 0 - ? Math.min(retryAfterHeader, maxTimeout) - : Math.min(minTimeout * timeoutFactor ** (counter - 1), maxTimeout); + retryAfterHeader === 0 + ? 0 + : retryAfterHeader > 0 + ? Math.min(retryAfterHeader, maxTimeout) + : Math.min(minTimeout * timeoutFactor ** (counter - 1), maxTimeout); setTimeout(() => cb(null), retryTimeout); } onResponseStart(controller, statusCode, headers, statusMessage) { + if (statusCode < 200) { + this.handler.onResponseStart?.(this.controllerProxy, statusCode, headers, statusMessage); + return; + } this.error = null; this.retryCount += 1; this.statusCode = statusCode; @@ -12295,6 +12761,7 @@ var require_retry_handler = __commonJS({ data: { count: this.retryCount } }); } + validatePartialResponseContentLength(headers, contentRange, statusCode, this.retryCount); const { start, size, end = size ? size - 1 : null } = contentRange; assert(this.start === start, 'content-range mismatch'); assert(this.end == null || this.end === end, 'content-range mismatch'); @@ -12303,18 +12770,24 @@ var require_retry_handler = __commonJS({ if (this.end == null) { if (statusCode === 206) { const range = parseRangeHeader(headers['content-range']); - if (range == null) { + if (range == null || range.end == null) { this.headersSent = true; - this.handler.onResponseStart?.(controller, statusCode, headers, statusMessage); + this.handler.onResponseStart?.( + this.controllerProxy, + statusCode, + headers, + statusMessage + ); return; } + validatePartialResponseContentLength(headers, range, statusCode, this.retryCount); const { start, size, end = size ? size - 1 : null } = range; assert(start != null && Number.isFinite(start), 'content-range mismatch'); assert(end != null && Number.isFinite(end), 'invalid content-length'); this.start = start; this.end = end; } - if (this.end == null) { + if (this.end == null && this.opts.method !== 'HEAD') { const contentLength = headers['content-length']; this.end = contentLength != null ? Number(contentLength) - 1 : null; } @@ -12326,7 +12799,7 @@ var require_retry_handler = __commonJS({ this.etag = null; } this.headersSent = true; - this.handler.onResponseStart?.(controller, statusCode, headers, statusMessage); + this.handler.onResponseStart?.(this.controllerProxy, statusCode, headers, statusMessage); } else { throw new RequestRetryError('Request failed', statusCode, { headers, @@ -12334,14 +12807,14 @@ var require_retry_handler = __commonJS({ }); } } - onResponseData(controller, chunk) { + onResponseData(_controller, chunk) { if (this.error) { return; } this.start += chunk.length; - this.handler.onResponseData?.(controller, chunk); + this.handler.onResponseData?.(this.controllerProxy, chunk); } - onResponseEnd(controller, trailers) { + onResponseEnd(_controller, trailers) { if (this.error && this.retryOpts.throwOnError) { throw this.error; } @@ -12355,11 +12828,11 @@ var require_retry_handler = __commonJS({ } } this.retryCount = 0; - return this.handler.onResponseEnd?.(controller, trailers); + return this.handler.onResponseEnd?.(this.controllerProxy, trailers); } - this.retry(controller); + this.retry(); } - retry(controller) { + retry() { if (this.start !== 0) { const headers = { range: `bytes=${this.start}-${this.end ?? ''}` }; if (this.etag != null) { @@ -12377,20 +12850,20 @@ var require_retry_handler = __commonJS({ this.retryCountCheckpoint = this.retryCount; this.dispatch(this.opts, this); } catch (err) { - this.handler.onResponseError?.(controller, err); + this.handler.onResponseError?.(this.controllerProxy, err); } } onResponseError(controller, err) { if (controller?.aborted || isDisturbed(this.opts.body)) { - this.handler.onResponseError?.(controller, err); + this.handler.onResponseError?.(this.controllerProxy, err); return; } function shouldRetry(returnedErr) { if (!returnedErr) { - this.retry(controller); + this.retry(); return; } - this.handler?.onResponseError?.(controller, returnedErr); + this.handler?.onResponseError?.(this.controllerProxy, returnedErr); } if (this.retryCount - this.retryCountCheckpoint > 0) { this.retryCount = @@ -12754,7 +13227,7 @@ var require_readable = __commonJS({ */ setEncoding(encoding) { if (Buffer.isEncoding(encoding)) { - this._readableState.encoding = encoding; + super.setEncoding(encoding); } return this; } @@ -12817,13 +13290,20 @@ var require_readable = __commonJS({ consumePush(consume2, chunk); } } + const decoder = state.decoder; + if (decoder != null && decoder.lastNeed > 0) { + consumePush( + consume2, + Buffer.from(decoder.lastChar.subarray(0, decoder.lastTotal - decoder.lastNeed)) + ); + } if (state.endEmitted) { - consumeEnd(this[kConsume], this._readableState.encoding); - } else { - consume2.stream.on('end', function () { - consumeEnd(this[kConsume], this._readableState.encoding); - }); + consumeEnd(consume2, state.encoding); + return; } + consume2.stream.on('end', function () { + consumeEnd(this[kConsume], this._readableState.encoding); + }); consume2.stream.resume(); while (consume2.stream.read() != null) {} } @@ -12877,6 +13357,12 @@ var require_readable = __commonJS({ } } function consumePush(consume2, chunk) { + if (consume2.body === null) { + return; + } + if (typeof chunk === 'string') { + chunk = Buffer.from(chunk, consume2.stream._readableState.encoding); + } consume2.length += chunk.length; consume2.body.push(chunk); } @@ -12966,7 +13452,9 @@ var require_api_request = __commonJS({ this.removeAbortListener = util.addAbortListener(signal, () => { this.reason = signal.reason ?? new RequestAbortedError(); if (this.res) { - util.destroy(this.res.on('error', noop3), this.reason); + const res = this.res; + this.res = null; + util.destroy(res.on('error', noop3), this.reason); } else if (this.abort) { this.abort(this.reason); } @@ -13890,6 +14378,7 @@ var require_mock_utils = __commonJS({ types: { isPromise } } = require('node:util'); var { InvalidArgumentError } = require_errors(); + var requestAborted = /* @__PURE__ */ Symbol('request aborted'); function matchValue(match, value) { if (typeof match === 'string') { return match === value; @@ -14003,6 +14492,8 @@ var require_mock_utils = __commonJS({ return data; } else if (data instanceof ArrayBuffer) { return data; + } else if (ArrayBuffer.isView(data)) { + return new Uint8Array(data.buffer, data.byteOffset, data.byteLength); } else if (typeof data === 'object') { return JSON.stringify(data); } else if (data) { @@ -14077,6 +14568,9 @@ var require_mock_utils = __commonJS({ } } function removeTrailingSlash(path) { + if (typeof path !== 'string') { + return path; + } while (path.endsWith('/')) { path = path.slice(0, -1); } @@ -14125,21 +14619,47 @@ var require_mock_utils = __commonJS({ function mockDispatch(opts, handler2) { const key = buildKey(opts); const mockDispatch2 = getMockDispatch(this[kDispatches], key); + const mockDispatches = this[kDispatches]; mockDispatch2.timesInvoked++; - if (mockDispatch2.data.callback) { - mockDispatch2.data = { ...mockDispatch2.data, ...mockDispatch2.data.callback(opts) }; - } - const { - data: { statusCode, data, headers, trailers, error: error2 }, - delay, - persist - } = mockDispatch2; const { timesInvoked, times } = mockDispatch2; - mockDispatch2.consumed = !persist && timesInvoked >= times; + mockDispatch2.consumed = !mockDispatch2.persist && timesInvoked >= times; mockDispatch2.pending = timesInvoked < times; - if (error2 !== null) { - deleteMockDispatch(this[kDispatches], key); - handler2.onResponseError(null, error2); + const hasBodyHooks = + typeof handler2.onBodySent === 'function' || typeof handler2.onRequestSent === 'function'; + if (mockDispatch2.data.callback && (!hasBodyHooks || opts.body == null)) { + const { callback, ...responseDefaults } = mockDispatch2.data; + const callbackResult = callback(opts); + if (isPromise(callbackResult)) { + callbackResult.then( + resolvedData => { + if (resolvedData == null || typeof resolvedData !== 'object') { + handler2.onResponseError( + null, + new InvalidArgumentError('reply options callback must return an object') + ); + return; + } + mockDispatch2.data = { ...responseDefaults, ...resolvedData }; + dispatchMockReply(mockDispatches, mockDispatch2, key, opts, handler2); + }, + error2 => { + handler2.onResponseError(null, error2); + } + ); + return true; + } + if (callbackResult == null || typeof callbackResult !== 'object') { + throw new InvalidArgumentError('reply options callback must return an object'); + } + mockDispatch2.data = { ...responseDefaults, ...callbackResult }; + } + return dispatchMockReply(mockDispatches, mockDispatch2, key, opts, handler2); + } + function dispatchMockReply(mockDispatches, mockDispatch2, key, opts, handler2) { + const { data: response, delay } = mockDispatch2; + if (response.error !== null) { + deleteMockDispatch(mockDispatches, key); + handler2.onResponseError(null, response.error); return true; } let aborted = false; @@ -14166,32 +14686,102 @@ var require_mock_utils = __commonJS({ handler2.onResponseError?.(controller, reason); } }; + let replyOpts = opts; + const dispatches = mockDispatches; handler2.onRequestStart?.(controller, null); - if (typeof delay === 'number' && delay > 0) { - timer = setTimeout(() => { - timer = null; - handleReply(this[kDispatches]); - }, delay); - } else { - handleReply(this[kDispatches]); + if (aborted) { + return true; + } + const requestBody = dispatchRequestBody(opts.body, handler2, controller, () => aborted); + if (isPromise(requestBody)) { + requestBody.then( + body => { + if (body === requestAborted) { + return; + } + if (body !== opts.body) { + replyOpts = { ...opts, body }; + } + sendReply(); + }, + error2 => controller.abort(error2) + ); + return true; + } + if (requestBody === requestAborted) { + return true; + } + if (requestBody !== opts.body) { + replyOpts = { ...opts, body: requestBody }; + } + sendReply(); + function sendReply() { + if (response.callback) { + const { callback, ...responseDefaults } = response; + let callbackResult; + try { + callbackResult = callback(replyOpts); + } catch (err) { + deleteMockDispatch(mockDispatches, key); + handler2.onResponseError(null, err); + return; + } + if (isPromise(callbackResult)) { + callbackResult.then( + resolvedData => { + if (resolvedData == null || typeof resolvedData !== 'object') { + handler2.onResponseError( + null, + new InvalidArgumentError('reply options callback must return an object') + ); + return; + } + mockDispatch2.data = { ...responseDefaults, ...resolvedData }; + handleReply(dispatches, mockDispatch2.data); + }, + err => { + handler2.onResponseError(null, err); + } + ); + return; + } + if (callbackResult == null || typeof callbackResult !== 'object') { + throw new InvalidArgumentError('reply options callback must return an object'); + } + mockDispatch2.data = { ...responseDefaults, ...callbackResult }; + handleReply(dispatches, mockDispatch2.data); + return; + } + if (typeof delay === 'number' && delay > 0) { + timer = setTimeout(() => { + timer = null; + handleReply(dispatches); + }, delay); + } else { + handleReply(dispatches); + } } - function handleReply(mockDispatches, _data = data) { + function handleReply(mockDispatches2, _response = response) { if (aborted) { return; } + const { statusCode, data, headers, trailers } = _response; const optsHeaders = Array.isArray(opts.headers) ? buildHeadersFromArray(opts.headers) : opts.headers; - const body = typeof _data === 'function' ? _data({ ...opts, headers: optsHeaders }) : _data; + const body = + typeof data === 'function' ? data({ ...replyOpts, headers: optsHeaders }) : data; if (isPromise(body)) { - return body.then(newData => handleReply(mockDispatches, newData)); + return body.then(newData => + handleReply(mockDispatches2, { ..._response, data: newData }) + ); } if (aborted) { return; } const responseData = getResponseData2(body); - const responseHeaders = generateKeyValues(headers); - const responseTrailers = generateKeyValues(trailers); + const responseHeaders = generateKeyValues(headers ?? {}); + const responseTrailers = generateKeyValues(trailers ?? {}); controller.rawHeaders = responseHeaders; controller.rawTrailers = responseTrailers; handler2.onResponseStart?.( @@ -14202,10 +14792,90 @@ var require_mock_utils = __commonJS({ ); handler2.onResponseData?.(controller, Buffer.from(responseData)); handler2.onResponseEnd?.(controller, parseHeaders(responseTrailers)); - deleteMockDispatch(mockDispatches, key); + deleteMockDispatch(mockDispatches2, key); } return true; } + function dispatchRequestBody(body, handler2, controller, isAborted) { + if ( + typeof handler2.onBodySent !== 'function' && + typeof handler2.onRequestSent !== 'function' + ) { + return body; + } + if (body == null) { + return callOnRequestSent(handler2, controller, isAborted) ? body : requestAborted; + } + if (body && typeof body[Symbol.asyncIterator] === 'function') { + return dispatchAsyncIterableBody(body, handler2, controller, isAborted); + } + if (isIterableBody(body)) { + const chunks = []; + for (const chunk of body) { + if (isAborted()) { + return requestAborted; + } + chunks.push(chunk); + if (!callOnBodySent(handler2, controller, chunk) || isAborted()) { + return requestAborted; + } + } + return callOnRequestSent(handler2, controller, isAborted) ? chunks : requestAborted; + } + if (isAborted()) { + return requestAborted; + } + if (!callOnBodySent(handler2, controller, body)) { + return requestAborted; + } + return callOnRequestSent(handler2, controller, isAborted) ? body : requestAborted; + } + async function dispatchAsyncIterableBody(body, handler2, controller, isAborted) { + const chunks = []; + for await (const chunk of body) { + if (isAborted()) { + return requestAborted; + } + chunks.push(chunk); + if (!callOnBodySent(handler2, controller, chunk) || isAborted()) { + return requestAborted; + } + } + if (!callOnRequestSent(handler2, controller, isAborted)) { + return requestAborted; + } + return { + async *[Symbol.asyncIterator]() { + yield* chunks; + } + }; + } + function callOnBodySent(handler2, controller, chunk) { + try { + handler2.onBodySent?.(chunk); + return true; + } catch (error2) { + controller.abort(error2); + return false; + } + } + function callOnRequestSent(handler2, controller, isAborted) { + try { + handler2.onRequestSent?.(); + return !isAborted(); + } catch (error2) { + controller.abort(error2); + return false; + } + } + function isIterableBody(body) { + return ( + typeof body !== 'string' && + !Buffer.isBuffer(body) && + !ArrayBuffer.isView(body) && + typeof body[Symbol.iterator] === 'function' + ); + } function buildMockDispatch() { const agent = this[kMockAgent]; const origin = this[kOrigin]; @@ -14333,6 +15003,9 @@ var require_mock_interceptor = __commonJS({ } = require_mock_symbols(); var { InvalidArgumentError } = require_errors(); var { serializePathWithQuery } = require_util(); + var { + types: { isPromise } + } = require('node:util'); var MockScope = class { constructor(mockDispatch) { this[kMockDispatch] = mockDispatch; @@ -14417,8 +15090,7 @@ var require_mock_interceptor = __commonJS({ */ reply(replyOptionsCallbackOrStatusCode) { if (typeof replyOptionsCallbackOrStatusCode === 'function') { - const wrappedDefaultsCallback = opts => { - const resolvedData = replyOptionsCallbackOrStatusCode(opts); + const resolveReplyCallbackData = resolvedData => { if (typeof resolvedData !== 'object' || resolvedData === null) { throw new InvalidArgumentError('reply options callback must return an object'); } @@ -14428,6 +15100,13 @@ var require_mock_interceptor = __commonJS({ ...this.createMockScopeDispatchData(replyParameters2) }; }; + const wrappedDefaultsCallback = opts => { + const resolvedData = replyOptionsCallbackOrStatusCode(opts); + if (isPromise(resolvedData)) { + return resolvedData.then(resolveReplyCallbackData); + } + return resolveReplyCallbackData(resolvedData); + }; const newMockDispatch2 = addMockDispatch( this[kDispatches], this[kDispatchKey], @@ -15962,6 +16641,7 @@ var require_global2 = __commonJS({ var { InvalidArgumentError } = require_errors(); var Agent = require_agent(); var Dispatcher1Wrapper = require_dispatcher1_wrapper(); + var fallbackDispatcher; if (getGlobalDispatcher() === void 0) { setGlobalDispatcher(new Agent()); } @@ -15969,23 +16649,37 @@ var require_global2 = __commonJS({ if (!agent || typeof agent.dispatch !== 'function') { throw new InvalidArgumentError('Argument agent must implement Agent'); } - Object.defineProperty(globalThis, globalDispatcher, { - value: agent, - writable: true, - enumerable: false, - configurable: false - }); - const legacyAgent = - agent instanceof Dispatcher1Wrapper ? agent : new Dispatcher1Wrapper(agent); - Object.defineProperty(globalThis, legacyGlobalDispatcher, { - value: legacyAgent, - writable: true, - enumerable: false, - configurable: false - }); + try { + Object.defineProperty(globalThis, globalDispatcher, { + value: agent, + writable: true, + enumerable: false, + configurable: false + }); + } catch (err) { + if (err instanceof TypeError) { + fallbackDispatcher = agent; + return; + } + throw err; + } + try { + const legacyAgent = + agent instanceof Dispatcher1Wrapper ? agent : new Dispatcher1Wrapper(agent); + Object.defineProperty(globalThis, legacyGlobalDispatcher, { + value: legacyAgent, + writable: true, + enumerable: false, + configurable: false + }); + } catch (err) { + if (!(err instanceof TypeError)) { + throw err; + } + } } function getGlobalDispatcher() { - return globalThis[globalDispatcher]; + return globalThis[globalDispatcher] ?? fallbackDispatcher; } var installedExports = /** @type {const} */ @@ -16126,12 +16820,14 @@ var require_redirect_handler = __commonJS({ if (this.opts.throwOnMaxRedirect && this.history.length >= this.maxRedirections) { throw new Error('max redirects'); } + let removeContentHeaders = statusCode === 303; if ((statusCode === 301 || statusCode === 302) && this.opts.method === 'POST') { this.opts.method = 'GET'; if (util.isStream(this.opts.body)) { util.destroy(this.opts.body.on('error', noop3)); } this.opts.body = null; + removeContentHeaders = true; } if (statusCode === 303 && this.opts.method !== 'HEAD') { this.opts.method = 'GET'; @@ -16167,7 +16863,7 @@ var require_redirect_handler = __commonJS({ } this.opts.headers = cleanRequestHeaders( this.opts.headers, - statusCode === 303, + removeContentHeaders, this.opts.origin !== origin, this.stripHeadersOnRedirect, this.stripHeadersOnCrossOriginRedirect @@ -16986,23 +17682,149 @@ var require_dns = __commonJS({ var require_cache = __commonJS({ 'node_modules/undici/lib/util/cache.js'(exports2, module2) { 'use strict'; - var { safeHTTPMethods, pathHasQueryOrFragment, hasSafeIterator } = require_util(); + var { safeHTTPMethods, pathHasQueryOrFragment, hasSafeIterator, isValidHTTPToken } = + require_util(); var { serializePathWithQuery } = require_util(); - function makeCacheKey(opts) { - if (!opts.origin) { - throw new Error('opts.origin is undefined'); + var MAX_DELTA_SECONDS = 2147483647; + var RESTRICTIVE_DIRECTIVE_NAMES = ['no-store', 'private', 'no-cache']; + var kInvalidCacheControlDirectives = /* @__PURE__ */ Symbol('invalid cache-control directives'); + function trimOWS(value) { + return value.replace(/^[\t ]+|[\t ]+$/g, ''); + } + function arrayIncludes(array, value) { + for (let i = 0; i < array.length; i++) { + if (array[i] === value) { + return true; + } + } + return false; + } + function trimOWSStart(value) { + return value.replace(/^[\t ]+/, ''); + } + function trimOWSEnd(value) { + return value.replace(/[\t ]+$/, ''); + } + function findUnescapedQuote(value, start) { + let escaped = false; + for (let i = start; i < value.length; i++) { + if (escaped) { + escaped = false; + } else if (value[i] === '\\') { + escaped = true; + } else if (value[i] === '"') { + return i; + } + } + return -1; + } + function splitCacheControlHeaderValue(value) { + const directives = []; + let start = 0; + let quoteStart = -1; + let inQuote = false; + let escaped = false; + for (let i = 0; i < value.length; i++) { + if (inQuote) { + if (escaped) { + escaped = false; + } else if (value[i] === '\\') { + escaped = true; + } else if (value[i] === '"') { + inQuote = false; + quoteStart = -1; + } + } else if (value[i] === '"') { + inQuote = true; + quoteStart = i; + } else if (value[i] === ',') { + directives.push({ value: value.substring(start, i), fromMalformedQuote: false }); + start = i + 1; + } + } + if (!inQuote) { + directives.push({ value: value.substring(start), fromMalformedQuote: false }); + return directives; + } + const tail = value.substring(start); + const quoteOffset = quoteStart - start; + let tailStart = 0; + for (let i = 0; i < tail.length; i++) { + if (tail[i] === ',') { + directives.push({ + value: tail.substring(tailStart, i), + fromMalformedQuote: tailStart > quoteOffset + }); + tailStart = i + 1; + } + } + directives.push({ + value: tail.substring(tailStart), + fromMalformedQuote: tailStart > quoteOffset + }); + return directives; + } + function markInvalidCacheControlDirective(directives, key) { + let invalidDirectives = directives[kInvalidCacheControlDirectives]; + if (invalidDirectives === void 0) { + invalidDirectives = /* @__PURE__ */ new Set(); + Object.defineProperty(directives, kInvalidCacheControlDirectives, { + value: invalidDirectives + }); + } + invalidDirectives.add(key); + } + function hasInvalidCacheControlDirective(directives, key) { + return directives[kInvalidCacheControlDirectives]?.has(key) === true; + } + function getMalformedRestrictiveDirectiveName(key) { + for (const directiveName of RESTRICTIVE_DIRECTIVE_NAMES) { + if ( + key.startsWith(directiveName) && + key.length > directiveName.length && + !isValidHTTPToken(key[directiveName.length]) + ) { + return directiveName; + } } + let tokenOnlyKey = ''; + let hasInvalidTokenChar = false; + for (let i = 0; i < key.length; i++) { + if (isValidHTTPToken(key[i])) { + tokenOnlyKey += key[i]; + } else { + hasInvalidTokenChar = true; + } + } + if (hasInvalidTokenChar && arrayIncludes(RESTRICTIVE_DIRECTIVE_NAMES, tokenOnlyKey)) { + return tokenOnlyKey; + } + } + function makeCacheKey(opts) { + const origin = opts.origin ? opts.origin.toString() : ''; let fullPath = opts.path || '/'; if (opts.query && !pathHasQueryOrFragment(fullPath)) { fullPath = serializePathWithQuery(fullPath, opts.query); } return { - origin: opts.origin.toString(), + origin, method: opts.method, path: fullPath, headers: opts.headers }; } + function appendHeader(headers, key, val) { + const headerName = key.toLowerCase(); + const current = headers[headerName]; + const values = Array.isArray(val) ? val : [val]; + if (current === void 0) { + headers[headerName] = Array.isArray(val) ? val.slice() : val; + } else if (Array.isArray(current)) { + current.push(...values); + } else { + headers[headerName] = [current, ...values]; + } + } function normalizeHeaders(opts) { let headers; if (opts.headers == null) { @@ -17010,19 +17832,57 @@ var require_cache = __commonJS({ } else if (typeof opts.headers === 'object') { headers = {}; if (hasSafeIterator(opts.headers)) { - for (const x of opts.headers) { - if (!Array.isArray(x)) { - throw new Error('opts.headers is not a valid header map'); + if (Array.isArray(opts.headers)) { + const first = opts.headers[0]; + if (Array.isArray(first)) { + for (const x of opts.headers) { + if (!Array.isArray(x)) { + throw new Error('opts.headers is not a valid header map'); + } + const [key, val] = x; + if (typeof key !== 'string' || typeof val !== 'string') { + throw new Error('opts.headers is not a valid header map'); + } + appendHeader(headers, key, val); + } + } else { + const len = opts.headers.length; + if (len % 2 !== 0) { + throw new Error('opts.headers is not a valid header map'); + } + for (let i = 0; i < len; i += 2) { + const key = opts.headers[i]; + const val = opts.headers[i + 1]; + if (typeof key !== 'string' || (typeof val !== 'string' && !Array.isArray(val))) { + throw new Error('opts.headers is not a valid header map'); + } + if (typeof val === 'string') { + appendHeader(headers, key, val); + } else { + const mapped = []; + for (let j = 0; j < val.length; j++) { + const v = val[j]; + mapped.push(typeof v === 'string' ? v : v.toString('latin1')); + } + appendHeader(headers, key, mapped); + } + } } - const [key, val] = x; - if (typeof key !== 'string' || typeof val !== 'string') { - throw new Error('opts.headers is not a valid header map'); + } else { + for (const x of opts.headers) { + if (!Array.isArray(x)) { + throw new Error('opts.headers is not a valid header map'); + } + const [key, val] = x; + if (typeof key !== 'string' || typeof val !== 'string') { + throw new Error('opts.headers is not a valid header map'); + } + appendHeader(headers, key, val); } - headers[key.toLowerCase()] = val; } } else { for (const key of Object.keys(opts.headers)) { - headers[key.toLowerCase()] = opts.headers[key]; + appendHeader(headers, key, opts.headers[key]); } } } else { @@ -17071,25 +17931,34 @@ var require_cache = __commonJS({ } function parseCacheControlHeader(header) { const output = {}; - let directives; - if (Array.isArray(header)) { - directives = []; - for (const directive of header) { - directives.push(...directive.split(',')); - } - } else { - directives = header.split(','); - } + const invalidNumericDirectives = /* @__PURE__ */ new Set(); + const invalidNoArgumentDirectives = /* @__PURE__ */ new Set(); + const directives = splitCacheControlHeaderValue( + Array.isArray(header) ? header.join(',') : header + ); for (let i = 0; i < directives.length; i++) { - const directive = directives[i].toLowerCase(); + const directiveRecord = directives[i]; + const directive = directiveRecord.value.toLowerCase(); + const fromMalformedQuote = directiveRecord.fromMalformedQuote; const keyValueDelimiter = directive.indexOf('='); let key; let value; + let keyHasTrailingWhitespace = false; + let valueHasLeadingWhitespace = false; if (keyValueDelimiter !== -1) { - key = directive.substring(0, keyValueDelimiter).trimStart(); - value = directive.substring(keyValueDelimiter + 1); + const rawKey = directive.substring(0, keyValueDelimiter); + const rawValue = directive.substring(keyValueDelimiter + 1); + keyHasTrailingWhitespace = trimOWSEnd(rawKey) !== rawKey; + valueHasLeadingWhitespace = trimOWSStart(rawValue) !== rawValue; + key = trimOWS(rawKey); + value = trimOWSStart(rawValue); } else { - key = directive.trim(); + key = trimOWS(directive); + } + const malformedRestrictiveDirectiveName = getMalformedRestrictiveDirectiveName(key); + if (malformedRestrictiveDirectiveName !== void 0) { + output[malformedRestrictiveDirectiveName] = true; + continue; } switch (key) { case 'min-fresh': @@ -17098,48 +17967,85 @@ var require_cache = __commonJS({ case 's-maxage': case 'stale-while-revalidate': case 'stale-if-error': { - if (value === void 0 || value[0] === ' ') { + if (fromMalformedQuote || invalidNumericDirectives.has(key)) { + continue; + } + if (value === void 0 || keyHasTrailingWhitespace || valueHasLeadingWhitespace) { + delete output[key]; + invalidNumericDirectives.add(key); + markInvalidCacheControlDirective(output, key); continue; } if (value.length >= 2 && value[0] === '"' && value[value.length - 1] === '"') { value = value.substring(1, value.length - 1); } - const parsedValue = parseInt(value, 10); - if (parsedValue !== parsedValue) { + if (!/^[0-9]+$/.test(value)) { + delete output[key]; + invalidNumericDirectives.add(key); + markInvalidCacheControlDirective(output, key); continue; } - if (key === 'max-age' && key in output && output[key] >= parsedValue) { - continue; + const parsedValue = Math.min(parseInt(value, 10), MAX_DELTA_SECONDS); + if (key === 'min-fresh') { + if (!(key in output) || output[key] < parsedValue) { + output[key] = parsedValue; + } + } else if (!(key in output) || output[key] > parsedValue) { + output[key] = parsedValue; } - output[key] = parsedValue; break; } case 'private': case 'no-cache': { + if (fromMalformedQuote) { + output[key] = true; + break; + } + if (value !== void 0 && value.length === 0) { + output[key] = true; + break; + } if (value) { if (value[0] === '"') { - const headers = [value.substring(1)]; - let foundEndingQuote = value[value.length - 1] === '"'; - if (!foundEndingQuote) { + value = trimOWSEnd(value); + let fieldList = ''; + let lastQuotedPart = i; + let foundEndingQuote = false; + const closingQuote = findUnescapedQuote(value, 1); + if (closingQuote !== -1) { + fieldList = value.substring(1, closingQuote); + foundEndingQuote = true; + } else { + const fieldListParts = [value.substring(1)]; for (let j = i + 1; j < directives.length; j++) { - const nextPart = directives[j]; - const nextPartLength = nextPart.length; - headers.push(nextPart.trim()); - if (nextPartLength !== 0 && nextPart[nextPartLength - 1] === '"') { + const nextPart = trimOWS(directives[j].value); + const closingQuote2 = findUnescapedQuote(nextPart, 0); + lastQuotedPart = j; + if (closingQuote2 !== -1) { + fieldListParts.push(nextPart.substring(0, closingQuote2)); foundEndingQuote = true; break; } + fieldListParts.push(nextPart); } + fieldList = fieldListParts.join(','); + } + if (!foundEndingQuote) { + output[key] = true; + break; } - if (foundEndingQuote) { - let lastHeader = headers[headers.length - 1]; - if (lastHeader[lastHeader.length - 1] === '"') { - lastHeader = lastHeader.substring(0, lastHeader.length - 1); - headers[headers.length - 1] = lastHeader; - } - for (let j = 0; j < headers.length; j++) { - headers[j] = headers[j].trim(); + i = lastQuotedPart; + const headers = fieldList.split(','); + let validFieldNames = true; + for (let j = 0; j < headers.length; j++) { + headers[j] = trimOWS(headers[j]); + if (!isValidHTTPToken(headers[j])) { + validFieldNames = false; } + } + if (!validFieldNames) { + output[key] = true; + } else if (output[key] !== true) { if (key in output) { output[key] = output[key].concat(headers); } else { @@ -17147,11 +18053,15 @@ var require_cache = __commonJS({ } } } else { - const fieldName = value.trim(); - if (key in output) { - output[key] = output[key].concat(fieldName); - } else { - output[key] = [fieldName]; + const fieldName = trimOWS(value); + if (!isValidHTTPToken(fieldName)) { + output[key] = true; + } else if (output[key] !== true) { + if (key in output) { + output[key] = output[key].concat(fieldName); + } else { + output[key] = [fieldName]; + } } } break; @@ -17159,38 +18069,77 @@ var require_cache = __commonJS({ } // eslint-disable-next-line no-fallthrough case 'public': - case 'no-store': case 'must-revalidate': case 'proxy-revalidate': case 'immutable': case 'no-transform': case 'must-understand': case 'only-if-cached': - if (value) { + if (fromMalformedQuote || invalidNoArgumentDirectives.has(key)) { + continue; + } + if (value !== void 0) { + delete output[key]; + invalidNoArgumentDirectives.add(key); continue; } output[key] = true; break; + case 'no-store': + output[key] = true; + break; default: continue; } } return output; } + function splitVaryHeader(varyHeader) { + const values = Array.isArray(varyHeader) ? varyHeader : [varyHeader]; + const output = []; + for (let i = 0; i < values.length; i++) { + const parts = values[i].split(','); + for (let j = 0; j < parts.length; j++) { + output.push(parts[j]); + } + } + return output; + } + function hasVaryStar(varyHeader) { + const values = splitVaryHeader(varyHeader); + for (let i = 0; i < values.length; i++) { + if (trimOWS(values[i]).indexOf('*') !== -1) { + return true; + } + } + return false; + } function parseVaryHeader(varyHeader, headers) { - if (typeof varyHeader === 'string' && varyHeader.includes('*')) { + if (hasVaryStar(varyHeader)) { return headers; } const output = /** @type {Record} */ {}; - const varyingHeaders = typeof varyHeader === 'string' ? varyHeader.split(',') : varyHeader; + const varyingHeaders = splitVaryHeader(varyHeader); for (const header of varyingHeaders) { - const trimmedHeader = header.trim().toLowerCase(); - output[trimmedHeader] = headers[trimmedHeader] ?? null; + const trimmedHeader = trimOWS(header).toLowerCase(); + if (trimmedHeader.length === 0) { + continue; + } + if (!isValidHTTPToken(trimmedHeader)) { + return void 0; + } + const headerValue = headers[trimmedHeader]; + output[trimmedHeader] = Array.isArray(headerValue) + ? headerValue.slice() + : headerValue ?? null; } return output; } + function isInvalidOrWildcardVaryHeader(varyHeader) { + return hasVaryStar(varyHeader) || parseVaryHeader(varyHeader, {}) === void 0; + } function isEtagUsable(etag) { if (etag.length <= 2) { return false; @@ -17229,7 +18178,7 @@ var require_cache = __commonJS({ throw new TypeError(`${name} needs to have at least one method`); } for (const method of methods) { - if (!safeHTTPMethods.includes(method)) { + if (!arrayIncludes(safeHTTPMethods, method)) { throw new TypeError( `element of ${name}-array needs to be one of following values: ${safeHTTPMethods.join( ', ' @@ -17257,7 +18206,10 @@ var require_cache = __commonJS({ assertCacheKey, assertCacheValue, parseCacheControlHeader, + hasInvalidCacheControlDirective, parseVaryHeader, + hasVaryStar, + isInvalidOrWildcardVaryHeader, isEtagUsable, assertCacheMethods, assertCacheStore, @@ -17280,6 +18232,21 @@ var require_date = __commonJS({ return parseRfc850Date(date); } } + function makeDate(year, monthIdx, day, hour, minute, second, weekday) { + const result = new Date(Date.UTC(year, monthIdx, day, hour, minute, second)); + if (year >= 0 && year <= 99) { + result.setUTCFullYear(year); + } + return result.getUTCFullYear() === year && + result.getUTCMonth() === monthIdx && + result.getUTCDate() === day && + result.getUTCHours() === hour && + result.getUTCMinutes() === minute && + result.getUTCSeconds() === second && + result.getUTCDay() === weekday + ? result + : void 0; + } function parseImfDate(date) { if ( date.length !== 29 || @@ -17456,8 +18423,7 @@ var require_date = __commonJS({ } second = (code1 - 48) * 10 + (code2 - 48); } - const result = new Date(Date.UTC(year, monthIdx, day, hour, minute, second)); - return result.getUTCDay() === weekday ? result : void 0; + return makeDate(year, monthIdx, day, hour, minute, second, weekday); } function parseAscTimeDate(date) { if (date.length !== 24 || date[7] !== ' ' || date[10] !== ' ' || date[19] !== ' ') { @@ -17623,8 +18589,7 @@ var require_date = __commonJS({ (yearDigit2 - 48) * 100 + (yearDigit3 - 48) * 10 + (yearDigit4 - 48); - const result = new Date(Date.UTC(year, monthIdx, day, hour, minute, second)); - return result.getUTCDay() === weekday ? result : void 0; + return makeDate(year, monthIdx, day, hour, minute, second, weekday); } function parseRfc850Date(date) { let commaIndex = -1; @@ -17887,8 +18852,7 @@ var require_date = __commonJS({ } second = (code1 - 48) * 10 + (code2 - 48); } - const result = new Date(Date.UTC(year, monthIdx, day, hour, minute, second)); - return result.getUTCDay() === weekday ? result : void 0; + return makeDate(year, monthIdx, day, hour, minute, second, weekday); } module2.exports = { parseHttpDate @@ -17901,7 +18865,14 @@ var require_cache_handler = __commonJS({ 'node_modules/undici/lib/handler/cache-handler.js'(exports2, module2) { 'use strict'; var util = require_util(); - var { parseCacheControlHeader, parseVaryHeader, isEtagUsable } = require_cache(); + var { + parseCacheControlHeader, + hasInvalidCacheControlDirective, + parseVaryHeader, + hasVaryStar, + isInvalidOrWildcardVaryHeader, + isEtagUsable + } = require_cache(); var { parseHttpDate } = require_date(); function noop3() {} var HEURISTICALLY_CACHEABLE_STATUS_CODES = [ @@ -17909,6 +18880,79 @@ var require_cache_handler = __commonJS({ ]; var NOT_UNDERSTOOD_STATUS_CODES = [206]; var MAX_RESPONSE_AGE = 2147483647e3; + var REVALIDATION_ONLY_RETENTION = 864e5; + function trimOWS(value) { + return value.replace(/^[\t ]+|[\t ]+$/g, ''); + } + function arrayIncludes(array, value) { + for (let i = 0; i < array.length; i++) { + if (array[i] === value) { + return true; + } + } + return false; + } + function appendConnectionHeaderTokens(headersToRemove, connectionHeader) { + const values = Array.isArray(connectionHeader) ? connectionHeader : [connectionHeader]; + for (let i = 0; i < values.length; i++) { + const tokens = values[i].split(','); + for (let j = 0; j < tokens.length; j++) { + headersToRemove.push(trimOWS(tokens[j]).toLowerCase()); + } + } + } + function getSameOriginPath(cacheKey, location) { + if (typeof location !== 'string') { + return void 0; + } + let originUrl; + let requestUrl; + let locationUrl; + try { + originUrl = new URL(cacheKey.origin); + requestUrl = new URL(cacheKey.path, originUrl); + locationUrl = new URL(location, requestUrl); + } catch { + return void 0; + } + if (locationUrl.origin !== originUrl.origin) { + return void 0; + } + return locationUrl.pathname + locationUrl.search; + } + function deleteCachedUri(store, cacheKey, path) { + deleteCachedValue(store, { + ...cacheKey, + path + }); + for (let i = 0; i < util.safeHTTPMethods.length; i++) { + const method = util.safeHTTPMethods[i]; + if (method !== cacheKey.method) { + deleteCachedValue(store, { + ...cacheKey, + method, + path + }); + } + } + } + function deleteLocationTargets(store, cacheKey, headerValue) { + if (headerValue === void 0) { + return; + } + const values = Array.isArray(headerValue) ? headerValue : [headerValue]; + for (let i = 0; i < values.length; i++) { + const path = getSameOriginPath(cacheKey, values[i]); + if (path !== void 0) { + deleteCachedUri(store, cacheKey, path); + } + } + } + function invalidateUnsafeRequest(store, cacheKey, resHeaders) { + deleteCachedUri(store, cacheKey, cacheKey.path); + deleteLocationTargets(store, cacheKey, resHeaders.location); + deleteLocationTargets(store, cacheKey, resHeaders['content-location']); + } var CacheHandler = class { /** * @type {import('../../types/cache-interceptor.d.ts').default.CacheKey} @@ -17965,24 +19009,30 @@ var require_cache_handler = __commonJS({ this.#handler.onResponseStart?.(controller, statusCode, resHeaders, statusMessage); const handler2 = this; if ( - !util.safeHTTPMethods.includes(this.#cacheKey.method) && + !arrayIncludes(util.safeHTTPMethods, this.#cacheKey.method) && statusCode >= 200 && statusCode <= 399 ) { - try { - this.#store.delete(this.#cacheKey)?.catch?.(noop3); - } catch {} + invalidateUnsafeRequest(this.#store, this.#cacheKey, resHeaders); return downstreamOnHeaders(); } const cacheControlHeader = resHeaders['cache-control']; const heuristicallyCacheable = - resHeaders['last-modified'] && HEURISTICALLY_CACHEABLE_STATUS_CODES.includes(statusCode); + resHeaders['last-modified'] && + arrayIncludes(HEURISTICALLY_CACHEABLE_STATUS_CODES, statusCode); if ( !cacheControlHeader && !resHeaders['expires'] && !heuristicallyCacheable && !this.#cacheByDefault ) { + if ( + statusCode === 304 && + resHeaders.vary && + isInvalidOrWildcardVaryHeader(resHeaders.vary) + ) { + deleteCachedValue(this.#store, this.#cacheKey); + } return downstreamOnHeaders(); } const cacheControlDirectives = cacheControlHeader @@ -17997,15 +19047,35 @@ var require_cache_handler = __commonJS({ this.#cacheKey.headers ) ) { + if ( + statusCode === 304 && + (cacheControlHeader || + revalidationResponseDisallowsCachedReuse( + this.#cacheType, + resHeaders, + cacheControlDirectives + )) + ) { + deleteCachedValue(this.#store, this.#cacheKey); + } return downstreamOnHeaders(); } const now = Date.now(); - const resAge = resHeaders.age ? getAge(resHeaders.age) : void 0; - if (resAge && resAge >= MAX_RESPONSE_AGE) { + const resAge = Object.hasOwn(resHeaders, 'age') ? getAge(resHeaders.age) : void 0; + if (resAge !== void 0 && resAge >= MAX_RESPONSE_AGE) { + deleteCachedValueIfNotModified(statusCode, this.#store, this.#cacheKey); + return downstreamOnHeaders(); + } + const resDate = Object.hasOwn(resHeaders, 'date') ? getDate(resHeaders.date) : void 0; + if (resDate === null) { + deleteCachedValueIfNotModified(statusCode, this.#store, this.#cacheKey); return downstreamOnHeaders(); } - const resDate = - typeof resHeaders.date === 'string' ? parseHttpDate(resHeaders.date) : void 0; + const apparentAge = resDate ? Math.max(0, now - resDate.getTime()) : 0; + const currentAge = Math.max(apparentAge, resAge ?? 0); + const hasValidator = + (typeof resHeaders.etag === 'string' && isEtagUsable(resHeaders.etag)) || + typeof resHeaders['last-modified'] === 'string'; const staleAt = determineStaleAt( this.#cacheType, @@ -18013,14 +19083,20 @@ var require_cache_handler = __commonJS({ resAge, resHeaders, resDate, - cacheControlDirectives + cacheControlDirectives, + hasValidator ) ?? this.#cacheByDefault; - if (staleAt === void 0 || (resAge && resAge > staleAt)) { + const revalidationOnly = staleAt === 0 && hasValidator; + if (staleAt === void 0 || (currentAge >= staleAt && !revalidationOnly)) { + if (cacheControlHeader || staleAt !== void 0) { + deleteCachedValueIfNotModified(statusCode, this.#store, this.#cacheKey); + } return downstreamOnHeaders(); } - const baseTime = resDate ? resDate.getTime() : now; + const baseTime = now - currentAge; const absoluteStaleAt = staleAt + baseTime; - if (now >= absoluteStaleAt) { + if (now >= absoluteStaleAt && !revalidationOnly) { + deleteCachedValueIfNotModified(statusCode, this.#store, this.#cacheKey); return downstreamOnHeaders(); } let varyDirectives; @@ -18030,13 +19106,8 @@ var require_cache_handler = __commonJS({ return downstreamOnHeaders(); } } - const cachedAt = resAge ? now - resAge : now; - const deleteAt = determineDeleteAt( - baseTime, - cachedAt, - cacheControlDirectives, - absoluteStaleAt - ); + const cachedAt = baseTime; + const deleteAt = determineDeleteAt(baseTime, now, cacheControlDirectives, absoluteStaleAt); const strippedHeaders = stripNecessaryHeaders(resHeaders, cacheControlDirectives); const value = { statusCode, @@ -18056,6 +19127,7 @@ var require_cache_handler = __commonJS({ value.statusCode = cachedValue.statusCode; value.statusMessage = cachedValue.statusMessage; value.etag = cachedValue.etag; + value.vary = varyDirectives ?? cachedValue.vary; value.headers = { ...cachedValue.headers, ...strippedHeaders }; downstreamOnHeaders(); this.#writeStream = this.#store.createWriteStream(this.#cacheKey, value); @@ -18157,6 +19229,27 @@ var require_cache_handler = __commonJS({ this.#handler.onResponseError?.(controller, err); } }; + function deleteCachedValue(store, cacheKey) { + try { + store.delete(cacheKey)?.catch?.(noop3); + } catch {} + } + function deleteCachedValueIfNotModified(statusCode, store, cacheKey) { + if (statusCode === 304) { + deleteCachedValue(store, cacheKey); + } + } + function revalidationResponseDisallowsCachedReuse( + cacheType, + resHeaders, + cacheControlDirectives + ) { + return ( + cacheControlDirectives['no-store'] === true || + (cacheType === 'shared' && cacheControlDirectives.private === true) || + (resHeaders.vary ? isInvalidOrWildcardVaryHeader(resHeaders.vary) : false) + ); + } function canCacheResponse( cacheType, statusCode, @@ -18164,11 +19257,11 @@ var require_cache_handler = __commonJS({ cacheControlDirectives, reqHeaders ) { - if (statusCode < 200 || NOT_UNDERSTOOD_STATUS_CODES.includes(statusCode)) { + if (statusCode < 200 || arrayIncludes(NOT_UNDERSTOOD_STATUS_CODES, statusCode)) { return false; } if ( - !HEURISTICALLY_CACHEABLE_STATUS_CODES.includes(statusCode) && + !arrayIncludes(HEURISTICALLY_CACHEABLE_STATUS_CODES, statusCode) && !resHeaders['expires'] && !cacheControlDirectives.public && cacheControlDirectives['max-age'] === void 0 && // RFC 9111: a private response directive, if the cache is not shared @@ -18183,10 +19276,10 @@ var require_cache_handler = __commonJS({ if (cacheType === 'shared' && cacheControlDirectives.private === true) { return false; } - if (resHeaders.vary?.includes('*')) { + if (resHeaders.vary && hasVaryStar(resHeaders.vary)) { return false; } - if (reqHeaders?.authorization) { + if (reqHeaders != null && Object.hasOwn(reqHeaders, 'authorization')) { if ( !cacheControlDirectives.public && !cacheControlDirectives['s-maxage'] && @@ -18199,22 +19292,48 @@ var require_cache_handler = __commonJS({ } if ( Array.isArray(cacheControlDirectives['no-cache']) && - cacheControlDirectives['no-cache'].includes('authorization') + arrayIncludes(cacheControlDirectives['no-cache'], 'authorization') ) { return false; } if ( Array.isArray(cacheControlDirectives['private']) && - cacheControlDirectives['private'].includes('authorization') + arrayIncludes(cacheControlDirectives['private'], 'authorization') ) { return false; } } return true; } + function getDate(dateHeader) { + let dateValue = dateHeader; + if (Array.isArray(dateValue)) { + if (dateValue.length !== 1) { + return null; + } + dateValue = dateValue[0]; + } + if (typeof dateValue !== 'string') { + return null; + } + return parseHttpDate(dateValue); + } function getAge(ageHeader) { - const age = parseInt(Array.isArray(ageHeader) ? ageHeader[0] : ageHeader); - return isNaN(age) ? void 0 : age * 1e3; + let ageValue = ageHeader; + if (Array.isArray(ageValue)) { + if (ageValue.length !== 1) { + return MAX_RESPONSE_AGE; + } + ageValue = ageValue[0]; + } + if (typeof ageValue !== 'string' || !/^[\t ]*[0-9]+[\t ]*$/.test(ageValue)) { + return MAX_RESPONSE_AGE; + } + const age = BigInt(ageValue.replace(/^[\t ]+|[\t ]+$/g, '')); + if (age >= BigInt(MAX_RESPONSE_AGE / 1e3)) { + return MAX_RESPONSE_AGE; + } + return Number(age) * 1e3; } function determineStaleAt( cacheType, @@ -18222,38 +19341,57 @@ var require_cache_handler = __commonJS({ age, resHeaders, responseDate, - cacheControlDirectives + cacheControlDirectives, + hasValidator ) { if (cacheType === 'shared') { + if (hasInvalidCacheControlDirective(cacheControlDirectives, 's-maxage')) { + return 0; + } const sMaxAge = cacheControlDirectives['s-maxage']; if (sMaxAge !== void 0) { - return sMaxAge > 0 ? sMaxAge * 1e3 : void 0; + if (sMaxAge > 0) { + return sMaxAge * 1e3; + } + return 0; } } + if (hasInvalidCacheControlDirective(cacheControlDirectives, 'max-age')) { + return 0; + } const maxAge = cacheControlDirectives['max-age']; if (maxAge !== void 0) { - return maxAge > 0 ? maxAge * 1e3 : void 0; + if (maxAge > 0) { + return maxAge * 1e3; + } + return 0; } - if (typeof resHeaders.expires === 'string') { + if (Object.hasOwn(resHeaders, 'expires')) { + if (typeof resHeaders.expires !== 'string') { + return 0; + } const expiresDate = parseHttpDate(resHeaders.expires); - if (expiresDate) { - if (now >= expiresDate.getTime()) { - return void 0; + if (!expiresDate) { + return 0; + } + if (now >= expiresDate.getTime()) { + return 0; + } + if (responseDate) { + if (responseDate >= expiresDate) { + return 0; } - if (responseDate) { - if (responseDate >= expiresDate) { - return void 0; - } - if (age !== void 0 && age > expiresDate - responseDate) { - return void 0; - } + const freshnessLifetime = expiresDate.getTime() - responseDate.getTime(); + if (age !== void 0 && age >= freshnessLifetime) { + return 0; } - return expiresDate.getTime() - now; + return freshnessLifetime; } + return expiresDate.getTime() - now; } if (typeof resHeaders['last-modified'] === 'string') { - const lastModified = new Date(resHeaders['last-modified']); - if (isValidDate(lastModified)) { + const lastModified = parseHttpDate(resHeaders['last-modified']); + if (lastModified) { if (lastModified.getTime() >= now) { return void 0; } @@ -18264,6 +19402,9 @@ var require_cache_handler = __commonJS({ if (cacheControlDirectives.immutable) { return 31536e6; } + if (cacheControlDirectives['no-cache'] === true && hasValidator) { + return 0; + } return void 0; } function determineDeleteAt(baseTime, cachedAt, cacheControlDirectives, staleAt) { @@ -18289,6 +19430,9 @@ var require_cache_handler = __commonJS({ immutable === -Infinity ) { const freshnessLifetime = staleAt - baseTime; + if (freshnessLifetime <= 0) { + return cachedAt + REVALIDATION_ONLY_RETENTION; + } const datePrecisionPadding = Math.min(Math.max(cachedAt - baseTime, 0), 1e3); return staleAt + freshnessLifetime + datePrecisionPadding; } @@ -18308,11 +19452,7 @@ var require_cache_handler = __commonJS({ 'age' ]; if (resHeaders['connection']) { - if (Array.isArray(resHeaders['connection'])) { - headersToRemove.push(...resHeaders['connection'].map(header => header.trim())); - } else { - headersToRemove.push(...resHeaders['connection'].split(',').map(header => header.trim())); - } + appendConnectionHeaderTokens(headersToRemove, resHeaders['connection']); } if (Array.isArray(cacheControlDirectives['no-cache'])) { headersToRemove.push(...cacheControlDirectives['no-cache']); @@ -18322,16 +19462,13 @@ var require_cache_handler = __commonJS({ } let strippedHeaders; for (const headerName of headersToRemove) { - if (resHeaders[headerName]) { + if (Object.hasOwn(resHeaders, headerName)) { strippedHeaders ??= { ...resHeaders }; delete strippedHeaders[headerName]; } } return strippedHeaders ?? resHeaders; } - function isValidDate(date) { - return date instanceof Date && Number.isFinite(date.valueOf()); - } module2.exports = CacheHandler; } }); @@ -18522,18 +19659,46 @@ var require_memory_cache_store = __commonJS({ } }; function findEntry(key, entries, now) { - return entries.find( - entry => - entry.deleteAt > now && - entry.method === key.method && - (entry.vary == null || - Object.keys(entry.vary).every(headerName => { - if (entry.vary[headerName] === null) { - return key.headers[headerName] === void 0; - } - return entry.vary[headerName] === key.headers[headerName]; - })) - ); + for (let i = 0; i < entries.length; i++) { + const entry = entries[i]; + if (entry.deleteAt > now && entry.method === key.method && varyMatches(key, entry)) { + return entry; + } + } + } + function varyMatches(key, entry) { + if (entry.vary == null) { + return true; + } + for (const headerName in entry.vary) { + if ( + Object.hasOwn(entry.vary, headerName) && + !headerValueEquals(key.headers?.[headerName], entry.vary[headerName]) + ) { + return false; + } + } + return true; + } + function headerValueEquals(lhs, rhs) { + if (lhs == null && rhs == null) { + return true; + } + if ((lhs == null && rhs != null) || (lhs != null && rhs == null)) { + return false; + } + if (Array.isArray(lhs) && Array.isArray(rhs)) { + if (lhs.length !== rhs.length) { + return false; + } + for (let i = 0; i < lhs.length; i++) { + if (lhs[i] !== rhs[i]) { + return false; + } + } + return true; + } + return lhs === rhs; } module2.exports = MemoryCacheStore; } @@ -18547,7 +19712,7 @@ var require_cache_revalidation_handler = __commonJS({ var CacheRevalidationHandler = class { #successful = false; /** - * @type {((boolean, any) => void) | null} + * @type {((success: boolean, context?: any, statusCode?: number, headers?: import('../../types/header.d.ts').IncomingHttpHeaders) => void) | null} */ #callback; /** @@ -18560,7 +19725,7 @@ var require_cache_revalidation_handler = __commonJS({ */ #allowErrorStatusCodes; /** - * @param {(boolean) => void} callback Function to call if the cached value is valid + * @param {(success: boolean, context?: any, statusCode?: number, headers?: import('../../types/header.d.ts').IncomingHttpHeaders) => void} callback Function to call if the cached value is valid * @param {import('../../types/dispatcher.d.ts').default.DispatchHandlers} handler * @param {boolean} allowErrorStatusCodes */ @@ -18584,7 +19749,7 @@ var require_cache_revalidation_handler = __commonJS({ this.#successful = statusCode === 304 || (this.#allowErrorStatusCodes && statusCode >= 500 && statusCode <= 504); - this.#callback(this.#successful, this.#context); + this.#callback(this.#successful, this.#context, statusCode, headers); this.#callback = null; if (this.#successful) { return true; @@ -18609,6 +19774,12 @@ var require_cache_revalidation_handler = __commonJS({ return; } if (this.#callback) { + if (this.#allowErrorStatusCodes) { + this.#successful = true; + this.#callback(true, this.#context); + this.#callback = null; + return; + } this.#callback(false); this.#callback = null; } @@ -18638,9 +19809,11 @@ var require_cache2 = __commonJS({ assertCacheMethods, makeCacheKey, normalizeHeaders, - parseCacheControlHeader + parseCacheControlHeader, + isInvalidOrWildcardVaryHeader } = require_cache(); var { AbortError } = require_errors(); + var { parseHttpDate } = require_date(); function assertCacheOrigins(origins, name) { if (origins === void 0) return; if (!Array.isArray(origins)) { @@ -18656,6 +19829,37 @@ var require_cache2 = __commonJS({ } } var nop = () => {}; + function trimOWS(value) { + return value.replace(/^[\t ]+|[\t ]+$/g, ''); + } + function arrayIncludes(array, value) { + for (let i = 0; i < array.length; i++) { + if (array[i] === value) { + return true; + } + } + return false; + } + function hasPragmaNoCache(headers) { + const pragma = headers?.pragma; + if (!pragma) { + return false; + } + const values = Array.isArray(pragma) ? pragma : [pragma]; + for (let i = 0; i < values.length; i++) { + const value = values[i]; + if (typeof value !== 'string') { + continue; + } + const directives = value.split(','); + for (let j = 0; j < directives.length; j++) { + if (trimOWS(directives[j]).toLowerCase() === 'no-cache') { + return true; + } + } + } + return false; + } function needsRevalidation(result, cacheControlDirectives, { headers = {} }) { if (cacheControlDirectives?.['no-cache']) { return true; @@ -18671,10 +19875,68 @@ var require_cache2 = __commonJS({ } return false; } - function isStale(result, cacheControlDirectives) { + function staleResponseRequiresRevalidation(result, cacheType) { + return ( + result.cacheControlDirectives?.['must-revalidate'] === true || + (cacheType === 'shared' && + (result.cacheControlDirectives?.['proxy-revalidate'] === true || // https://www.rfc-editor.org/rfc/rfc9111.html#section-5.2.2.10 + // s-maxage implies proxy-revalidate for shared caches. + result.cacheControlDirectives?.['s-maxage'] !== void 0)) + ); + } + function revalidationResponseDisallowsCachedReuse(cacheType, headers) { + if (headers.vary && isInvalidOrWildcardVaryHeader(headers.vary)) { + return true; + } + const cacheControl = headers['cache-control']; + if (!cacheControl) { + return false; + } + const cacheControlDirectives = parseCacheControlHeader(cacheControl); + return ( + cacheControlDirectives['no-store'] === true || + (cacheType === 'shared' && cacheControlDirectives.private === true) + ); + } + function revalidationResponseUpdatesCacheControl(headers) { + return headers['cache-control'] !== void 0; + } + function deleteCachedValue(store, cacheKey) { + try { + store.delete(cacheKey)?.catch?.(nop); + } catch {} + } + function getUsableLastModified(headers) { + const lastModified = headers?.['last-modified']; + if (typeof lastModified === 'string' && parseHttpDate(lastModified)) { + return lastModified; + } + } + function makeRevalidationHeaders(opts, result) { + const headers = { + ...opts.headers, + 'if-modified-since': + getUsableLastModified(result.headers) ?? new Date(result.cachedAt).toUTCString() + }; + if (result.etag) { + headers['if-none-match'] = result.etag; + } + if (result.vary) { + for (const key in result.vary) { + if (result.vary[key] != null) { + headers[key] = result.vary[key]; + } + } + } + return headers; + } + function isStale(result, cacheControlDirectives, cacheType) { const now = Date.now(); if (now > result.staleAt) { - if (cacheControlDirectives?.['max-stale']) { + if ( + !staleResponseRequiresRevalidation(result, cacheType) && + cacheControlDirectives?.['max-stale'] + ) { const gracePeriod = result.staleAt + cacheControlDirectives['max-stale'] * 1e3; return now > gracePeriod; } @@ -18687,9 +19949,9 @@ var require_cache2 = __commonJS({ } return false; } - function withinStaleWhileRevalidateWindow(result) { + function withinStaleWhileRevalidateWindow(result, cacheType) { const staleWhileRevalidate = result.cacheControlDirectives?.['stale-while-revalidate']; - if (!staleWhileRevalidate) { + if (!staleWhileRevalidate || staleResponseRequiresRevalidation(result, cacheType)) { return false; } const now = Date.now(); @@ -18815,32 +20077,18 @@ var require_cache2 = __commonJS({ return dispatch(opts, new CacheHandler(globalOpts, cacheKey, handler2)); } const age = Math.round((now - result.cachedAt) / 1e3); - if (reqCacheControl?.['max-age'] && age >= reqCacheControl['max-age']) { - return dispatch(opts, handler2); - } - const stale = isStale(result, reqCacheControl); - const revalidate = needsRevalidation(result, reqCacheControl, opts); + const requestMaxAgeExpired = + reqCacheControl?.['max-age'] !== void 0 && age >= reqCacheControl['max-age']; + const stale = requestMaxAgeExpired || isStale(result, reqCacheControl, globalOpts.type); + const revalidate = requestMaxAgeExpired || needsRevalidation(result, reqCacheControl, opts); if (stale || revalidate) { if (util.isStream(opts.body) && util.bodyLength(opts.body) !== 0) { return dispatch(opts, new CacheHandler(globalOpts, cacheKey, handler2)); } - if (!revalidate && withinStaleWhileRevalidateWindow(result)) { + if (!revalidate && withinStaleWhileRevalidateWindow(result, globalOpts.type)) { sendCachedValue(handler2, opts, result, age, null, true); queueMicrotask(() => { - const headers2 = { - ...opts.headers, - 'if-modified-since': new Date(result.cachedAt).toUTCString() - }; - if (result.etag) { - headers2['if-none-match'] = result.etag; - } - if (result.vary) { - for (const key in result.vary) { - if (result.vary[key] != null) { - headers2[key] = result.vary[key]; - } - } - } + const headers2 = makeRevalidationHeaders(opts, result); dispatch( { ...opts, @@ -18860,33 +20108,34 @@ var require_cache2 = __commonJS({ return true; } let withinStaleIfErrorThreshold = false; - const staleIfErrorExpiry = - result.cacheControlDirectives['stale-if-error'] ?? reqCacheControl?.['stale-if-error']; - if (staleIfErrorExpiry) { - withinStaleIfErrorThreshold = now < result.staleAt + staleIfErrorExpiry * 1e3; - } - const headers = { - ...opts.headers, - 'if-modified-since': new Date(result.cachedAt).toUTCString() - }; - if (result.etag) { - headers['if-none-match'] = result.etag; - } - if (result.vary) { - for (const key in result.vary) { - if (result.vary[key] != null) { - headers[key] = result.vary[key]; - } + if (!staleResponseRequiresRevalidation(result, globalOpts.type)) { + const staleIfErrorExpiry = + result.cacheControlDirectives['stale-if-error'] ?? reqCacheControl?.['stale-if-error']; + if (staleIfErrorExpiry) { + withinStaleIfErrorThreshold = now < result.staleAt + staleIfErrorExpiry * 1e3; } } + const headers = makeRevalidationHeaders(opts, result); return dispatch( { ...opts, headers }, new CacheRevalidationHandler( - (success, context4) => { + (success, context4, statusCode, headers2) => { if (success) { + if (statusCode === 304) { + if (revalidationResponseDisallowsCachedReuse(globalOpts.type, headers2)) { + if (util.isStream(result.body)) { + result.body.on('error', nop).destroy(); + } + deleteCachedValue(globalOpts.store, cacheKey); + return dispatch(opts, new CacheHandler(globalOpts, cacheKey, handler2)); + } + if (revalidationResponseUpdatesCacheControl(headers2)) { + deleteCachedValue(globalOpts.store, cacheKey); + } + } sendCachedValue(handler2, opts, result, age, context4, stale); } else if (util.isStream(result.body)) { result.body.on('error', nop).destroy(); @@ -18934,15 +20183,22 @@ var require_cache2 = __commonJS({ cacheByDefault, type }; - const safeMethodsToNotCache = util.safeHTTPMethods.filter( - method => methods.includes(method) === false - ); + const safeMethodsToNotCache = []; + for (let i = 0; i < util.safeHTTPMethods.length; i++) { + const method = util.safeHTTPMethods[i]; + if (!arrayIncludes(methods, method)) { + safeMethodsToNotCache.push(method); + } + } return dispatch => { return (opts2, handler2) => { - if (!opts2.origin || safeMethodsToNotCache.includes(opts2.method)) { + if (arrayIncludes(safeMethodsToNotCache, opts2.method)) { return dispatch(opts2, handler2); } if (origins !== void 0) { + if (!opts2.origin) { + return dispatch(opts2, handler2); + } const requestOrigin = opts2.origin.toString().toLowerCase(); let isAllowed = false; for (let i = 0; i < origins.length; i++) { @@ -18967,7 +20223,9 @@ var require_cache2 = __commonJS({ }; const reqCacheControl = opts2.headers?.['cache-control'] ? parseCacheControlHeader(opts2.headers['cache-control']) - : void 0; + : hasPragmaNoCache(opts2.headers) + ? { 'no-cache': true } + : void 0; if (reqCacheControl?.['no-store']) { return dispatch(opts2, handler2); } @@ -19032,6 +20290,8 @@ var require_decompress = __commonJS({ var DecompressHandler = class extends DecoratorHandler { /** @type {Transform[]} */ #decompressors = []; + /** @type {Record | undefined} */ + #trailers; /** @type {Readonly} */ #skipStatusCodes; /** @type {boolean} */ @@ -19112,7 +20372,7 @@ var require_decompress = __commonJS({ const decompressor = this.#decompressors[0]; this.#setupDecompressorEvents(decompressor, controller); decompressor.on('end', () => { - super.onResponseEnd(controller, {}); + super.onResponseEnd(controller, this.#trailers); }); } /** @@ -19128,7 +20388,7 @@ var require_decompress = __commonJS({ super.onResponseError(controller, err); return; } - super.onResponseEnd(controller, {}); + super.onResponseEnd(controller, this.#trailers); }); } /** @@ -19208,6 +20468,7 @@ var require_decompress = __commonJS({ */ onResponseEnd(controller, trailers) { if (this.#decompressors.length > 0) { + this.#trailers = trailers; this.#decompressors[0].end(); this.#cleanupDecompressors(); return; @@ -19239,6 +20500,9 @@ var require_decompress = __commonJS({ } return dispatch => { return (opts, handler2) => { + if (opts.method === 'HEAD') { + return dispatch(opts, handler2); + } const decompressHandler = new DecompressHandler(handler2, options); return dispatch(opts, decompressHandler); }; @@ -19665,7 +20929,7 @@ var require_deduplicate = __commonJS({ const pendingRequests = /* @__PURE__ */ new Map(); return dispatch => { return (opts2, handler2) => { - if (!opts2.origin || methods.includes(opts2.method) === false) { + if (opts2.upgrade || methods.includes(opts2.method) === false) { return dispatch(opts2, handler2); } opts2 = { @@ -20094,7 +21358,12 @@ var require_sqlite_cache_store = __commonJS({ if (lhs.length !== rhs.length) { return false; } - return lhs.every((x, i) => x === rhs[i]); + for (let i = 0; i < lhs.length; i++) { + if (lhs[i] !== rhs[i]) { + return false; + } + } + return true; } return lhs === rhs; } @@ -20615,9 +21884,7 @@ var require_response = __commonJS({ // https://fetch.spec.whatwg.org/#dom-response-json static json(data, init = void 0) { webidl.argumentLengthCheck(arguments, 1, 'Response.json'); - if (init !== null) { - init = webidl.converters.ResponseInit(init); - } + init = webidl.converters.ResponseInit(init); const bytes = textEncoder.encode(serializeJavascriptValueToJSONString(data)); const body = extractBody(bytes); const responseObject = fromInnerResponse(makeResponse({}), 'response'); @@ -22677,7 +23944,12 @@ var require_fetch = __commonJS({ httpFetchParams = fetchParams; httpRequest = request2; } else { - httpRequest = cloneRequest(request2); + if (request2.body?.source != null) { + httpRequest = cloneRequest(request2); + } else { + httpRequest = cloneRequest({ ...request2, body: null }); + httpRequest.body = request2.body; + } httpFetchParams = { ...fetchParams }; httpFetchParams.request = httpRequest; } @@ -22742,7 +24014,7 @@ var require_fetch = __commonJS({ } if (!httpRequest.headersList.contains('accept-encoding', true)) { if (urlHasHttpsScheme(requestCurrentURL(httpRequest))) { - httpRequest.headersList.append('accept-encoding', 'br, gzip, deflate', true); + httpRequest.headersList.append('accept-encoding', 'br, gzip, deflate, zstd', true); } else { httpRequest.headersList.append('accept-encoding', 'gzip, deflate', true); } @@ -23995,15 +25267,51 @@ var require_util4 = __commonJS({ const code = path.charCodeAt(i); if ( code < 32 || // exclude CTLs (0-31) - code === 127 || // DEL + code > 126 || // exclude non-ascii and DEL code === 59 ) { throw new Error('Invalid cookie path'); } } } + function isLetterOrDigit(code) { + return ( + (code >= 48 && code <= 57) || // 0-9 + (code >= 65 && code <= 90) || // A-Z + (code >= 97 && code <= 122) + ); + } function validateCookieDomain(domain) { - if (domain.startsWith('-') || domain.endsWith('.') || domain.endsWith('-')) { + if (domain === ' ') { + return; + } + if (domain.length > 255) { + throw new Error('Invalid cookie domain'); + } + let labelLength = 0; + for (let i = 0; i < domain.length; ++i) { + const code = domain.charCodeAt(i); + if (code === 46) { + if (labelLength === 0) { + throw new Error('Invalid cookie domain'); + } + if (domain.charCodeAt(i - 1) === 45) { + throw new Error('Invalid cookie domain'); + } + labelLength = 0; + continue; + } + if (labelLength === 0 && !isLetterOrDigit(code)) { + throw new Error('Invalid cookie domain'); + } + if (!isLetterOrDigit(code) && code !== 45) { + throw new Error('Invalid cookie domain'); + } + if (++labelLength > 63) { + throw new Error('Invalid cookie domain'); + } + } + if (labelLength === 0 || domain.charCodeAt(domain.length - 1) === 45) { throw new Error('Invalid cookie domain'); } } @@ -24084,7 +25392,11 @@ var require_util4 = __commonJS({ throw new Error('Invalid unparsed'); } const [key, ...value] = part.split('='); - out.push(`${key.trim()}=${value.join('=')}`); + const trimmedKey = key.trim(); + const joinedValue = value.join('='); + validateCookieName(trimmedKey); + validateCookieValue(joinedValue); + out.push(`${trimmedKey}=${joinedValue}`); } return out.join('; '); } @@ -24171,7 +25483,9 @@ var require_parse = __commonJS({ const attributeNameLowercase = attributeName.toLowerCase(); if (attributeNameLowercase === 'expires') { const expiryTime = new Date(attributeValue); - cookieAttributeList.expires = expiryTime; + if (!Number.isNaN(expiryTime.getTime())) { + cookieAttributeList.expires = expiryTime; + } } else if (attributeNameLowercase === 'max-age') { const charCode = attributeValue.charCodeAt(0); if ((charCode < 48 || charCode > 57) && attributeValue[0] !== '-') { @@ -25775,6 +27089,8 @@ var require_websocket = __commonJS({ var { SendQueue } = require_sender(); var { WebsocketFrameSend } = require_frame(); var { channels } = require_diagnostics(); + var kRef = /* @__PURE__ */ Symbol.for('nodejs.ref'); + var kUnref = /* @__PURE__ */ Symbol.for('nodejs.unref'); function getSocketAddress(socket) { if (typeof socket?.address === 'function') { return socket.address(); @@ -25794,6 +27110,7 @@ var require_websocket = __commonJS({ #bufferedAmount = 0; #protocol = ''; #extensions = ''; + #refed = true; /** @type {SendQueue} */ #sendQueue; /** @type {Handler} */ @@ -25881,6 +27198,16 @@ var require_websocket = __commonJS({ this.#handler.readyState = _WebSocket.CONNECTING; this.#binaryType = 'blob'; } + [kRef]() { + webidl.brandCheck(this, _WebSocket); + this.#refed = true; + this.#handler.socket?.ref?.(); + } + [kUnref]() { + webidl.brandCheck(this, _WebSocket); + this.#refed = false; + this.#handler.socket?.unref?.(); + } /** * @see https://websockets.spec.whatwg.org/#dom-websocket-close * @param {number|undefined} code @@ -26058,6 +27385,9 @@ var require_websocket = __commonJS({ */ #onConnectionEstablished(response, parsedExtensions) { this.#handler.socket = response.socket; + if (!this.#refed) { + this.#handler.socket.unref?.(); + } const maxFragments = this.#handler.controller.dispatcher?.webSocketOptions?.maxFragments; const maxPayloadSize = this.#handler.controller.dispatcher?.webSocketOptions?.maxPayloadSize; @@ -26695,7 +28025,7 @@ var require_util6 = __commonJS({ destination, mode, credentials: credentialsMode, - useCredentials: true + useURLCredentials: true }); } module2.exports = { diff --git a/package-lock.json b/package-lock.json index 74b86f7..d6f2d37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4664,9 +4664,9 @@ "license": "Apache-2.0" }, "node_modules/brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -5265,16 +5265,16 @@ } }, "node_modules/glob/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/glob/node_modules/minimatch": { @@ -6174,9 +6174,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.1.0.tgz", - "integrity": "sha512-s8VA5jkR8f22S3NAXmhKPFqGUduqZGlsufabVOgN14iTdw/RXcym7bKkbwjxLK9Yw2lEvvmJjFp119+KPeo8Kg==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.3.0.tgz", + "integrity": "sha512-muutsYr+e2+d3rTgUGslq5rxbBlUy3cJ61IsHag2QNDQV+7zXWjkUpmALIajhrlLlrgRUiymj6U3zUr/TMK84Q==", "dev": true, "funding": [ { @@ -6998,9 +6998,9 @@ } }, "node_modules/undici": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-8.5.0.tgz", - "integrity": "sha512-xamtWoB1EshgjpmlXd7GGm2VfdDtw1+rD8uhry8pSNW3If6S8E0m2T2+orSKeZXEn/aPJMviCpDBA65WJt8zhg==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.10.0.tgz", + "integrity": "sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ==", "license": "MIT", "engines": { "node": ">=22.19.0" @@ -10255,9 +10255,9 @@ "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==" }, "brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -10641,9 +10641,9 @@ "dev": true }, "brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "requires": { "balanced-match": "^4.0.2" @@ -11282,9 +11282,9 @@ "dev": true }, "js-yaml": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.1.0.tgz", - "integrity": "sha512-s8VA5jkR8f22S3NAXmhKPFqGUduqZGlsufabVOgN14iTdw/RXcym7bKkbwjxLK9Yw2lEvvmJjFp119+KPeo8Kg==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.3.0.tgz", + "integrity": "sha512-muutsYr+e2+d3rTgUGslq5rxbBlUy3cJ61IsHag2QNDQV+7zXWjkUpmALIajhrlLlrgRUiymj6U3zUr/TMK84Q==", "dev": true, "requires": { "argparse": "^2.0.1" @@ -11815,9 +11815,9 @@ "dev": true }, "undici": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-8.5.0.tgz", - "integrity": "sha512-xamtWoB1EshgjpmlXd7GGm2VfdDtw1+rD8uhry8pSNW3If6S8E0m2T2+orSKeZXEn/aPJMviCpDBA65WJt8zhg==" + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.10.0.tgz", + "integrity": "sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ==" }, "undici-types": { "version": "5.26.5", diff --git a/package.json b/package.json index 878eb99..2c88a63 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,6 @@ "keywords": [], "author": "wtw", "license": "ISC", - "type": "module", "dependencies": { "@actions/core": "^3.0.1", "@actions/github": "^9.1.1",