mirror of
https://github.com/dawidd6/action-send-mail.git
synced 2026-02-01 03:40:30 +07:00
Run npm ci --ignore-scripts to update dependencies (#254)
* Initial plan * Run npm ci --ignore-scripts to update dependencies Co-authored-by: dawidd6 <9713907+dawidd6@users.noreply.github.com> * Convert CommonJS to ESM (#255) * Initial plan * Convert CommonJS imports to ESM Co-authored-by: dawidd6 <9713907+dawidd6@users.noreply.github.com> * Use node: protocol prefix for built-in modules Co-authored-by: dawidd6 <9713907+dawidd6@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: dawidd6 <9713907+dawidd6@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: dawidd6 <9713907+dawidd6@users.noreply.github.com>
This commit is contained in:
35
node_modules/undici/lib/handler/DecoratorHandler.js
generated
vendored
35
node_modules/undici/lib/handler/DecoratorHandler.js
generated
vendored
@@ -1,35 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = class DecoratorHandler {
|
||||
constructor (handler) {
|
||||
this.handler = handler
|
||||
}
|
||||
|
||||
onConnect (...args) {
|
||||
return this.handler.onConnect(...args)
|
||||
}
|
||||
|
||||
onError (...args) {
|
||||
return this.handler.onError(...args)
|
||||
}
|
||||
|
||||
onUpgrade (...args) {
|
||||
return this.handler.onUpgrade(...args)
|
||||
}
|
||||
|
||||
onHeaders (...args) {
|
||||
return this.handler.onHeaders(...args)
|
||||
}
|
||||
|
||||
onData (...args) {
|
||||
return this.handler.onData(...args)
|
||||
}
|
||||
|
||||
onComplete (...args) {
|
||||
return this.handler.onComplete(...args)
|
||||
}
|
||||
|
||||
onBodySent (...args) {
|
||||
return this.handler.onBodySent(...args)
|
||||
}
|
||||
}
|
||||
44
node_modules/undici/lib/handler/decorator-handler.js
generated
vendored
Normal file
44
node_modules/undici/lib/handler/decorator-handler.js
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = class DecoratorHandler {
|
||||
#handler
|
||||
|
||||
constructor (handler) {
|
||||
if (typeof handler !== 'object' || handler === null) {
|
||||
throw new TypeError('handler must be an object')
|
||||
}
|
||||
this.#handler = handler
|
||||
}
|
||||
|
||||
onConnect (...args) {
|
||||
return this.#handler.onConnect?.(...args)
|
||||
}
|
||||
|
||||
onError (...args) {
|
||||
return this.#handler.onError?.(...args)
|
||||
}
|
||||
|
||||
onUpgrade (...args) {
|
||||
return this.#handler.onUpgrade?.(...args)
|
||||
}
|
||||
|
||||
onResponseStarted (...args) {
|
||||
return this.#handler.onResponseStarted?.(...args)
|
||||
}
|
||||
|
||||
onHeaders (...args) {
|
||||
return this.#handler.onHeaders?.(...args)
|
||||
}
|
||||
|
||||
onData (...args) {
|
||||
return this.#handler.onData?.(...args)
|
||||
}
|
||||
|
||||
onComplete (...args) {
|
||||
return this.#handler.onComplete?.(...args)
|
||||
}
|
||||
|
||||
onBodySent (...args) {
|
||||
return this.#handler.onBodySent?.(...args)
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
const util = require('../core/util')
|
||||
const { kBodyUsed } = require('../core/symbols')
|
||||
const assert = require('assert')
|
||||
const assert = require('node:assert')
|
||||
const { InvalidArgumentError } = require('../core/errors')
|
||||
const EE = require('events')
|
||||
const EE = require('node:events')
|
||||
|
||||
const redirectableStatusCodes = [300, 301, 302, 303, 307, 308]
|
||||
|
||||
@@ -38,6 +38,7 @@ class RedirectHandler {
|
||||
this.maxRedirections = maxRedirections
|
||||
this.handler = handler
|
||||
this.history = []
|
||||
this.redirectionLimitReached = false
|
||||
|
||||
if (util.isStream(this.opts.body)) {
|
||||
// TODO (fix): Provide some way for the user to cache the file to e.g. /tmp
|
||||
@@ -91,6 +92,16 @@ class RedirectHandler {
|
||||
? null
|
||||
: parseLocation(statusCode, headers)
|
||||
|
||||
if (this.opts.throwOnMaxRedirect && this.history.length >= this.maxRedirections) {
|
||||
if (this.request) {
|
||||
this.request.abort(new Error('max redirects'))
|
||||
}
|
||||
|
||||
this.redirectionLimitReached = true
|
||||
this.abort(new Error('max redirects'))
|
||||
return
|
||||
}
|
||||
|
||||
if (this.opts.origin) {
|
||||
this.history.push(new URL(this.opts.path, this.opts.origin))
|
||||
}
|
||||
@@ -135,7 +146,7 @@ class RedirectHandler {
|
||||
|
||||
For status 300, which is "Multiple Choices", the spec mentions both generating a Location
|
||||
response header AND a response body with the other possible location to follow.
|
||||
Since the spec explicitily chooses not to specify a format for such body and leave it to
|
||||
Since the spec explicitly chooses not to specify a format for such body and leave it to
|
||||
servers and browsers implementors, we ignore the body as there is no specified way to eventually parse it.
|
||||
*/
|
||||
} else {
|
||||
@@ -151,7 +162,7 @@ class RedirectHandler {
|
||||
TLDR: undici always ignores 3xx response trailers as they are not expected in case of redirections
|
||||
and neither are useful if present.
|
||||
|
||||
See comment on onData method above for more detailed informations.
|
||||
See comment on onData method above for more detailed information.
|
||||
*/
|
||||
|
||||
this.location = null
|
||||
@@ -176,7 +187,7 @@ function parseLocation (statusCode, headers) {
|
||||
}
|
||||
|
||||
for (let i = 0; i < headers.length; i += 2) {
|
||||
if (headers[i].toString().toLowerCase() === 'location') {
|
||||
if (headers[i].length === 8 && util.headerNameToString(headers[i]) === 'location') {
|
||||
return headers[i + 1]
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,18 @@
|
||||
const assert = require('assert')
|
||||
'use strict'
|
||||
const assert = require('node:assert')
|
||||
|
||||
const { kRetryHandlerDefaultRetry } = require('../core/symbols')
|
||||
const { RequestRetryError } = require('../core/errors')
|
||||
const { isDisturbed, parseHeaders, parseRangeHeader } = require('../core/util')
|
||||
const {
|
||||
isDisturbed,
|
||||
parseHeaders,
|
||||
parseRangeHeader,
|
||||
wrapRequestBody
|
||||
} = require('../core/util')
|
||||
|
||||
function calculateRetryAfterHeader (retryAfter) {
|
||||
const current = Date.now()
|
||||
const diff = new Date(retryAfter).getTime() - current
|
||||
|
||||
return diff
|
||||
return new Date(retryAfter).getTime() - current
|
||||
}
|
||||
|
||||
class RetryHandler {
|
||||
@@ -30,14 +34,14 @@ class RetryHandler {
|
||||
|
||||
this.dispatch = handlers.dispatch
|
||||
this.handler = handlers.handler
|
||||
this.opts = dispatchOpts
|
||||
this.opts = { ...dispatchOpts, body: wrapRequestBody(opts.body) }
|
||||
this.abort = null
|
||||
this.aborted = false
|
||||
this.retryOpts = {
|
||||
retry: retryFn ?? RetryHandler[kRetryHandlerDefaultRetry],
|
||||
retryAfter: retryAfter ?? true,
|
||||
maxTimeout: maxTimeout ?? 30 * 1000, // 30s,
|
||||
timeout: minTimeout ?? 500, // .5s
|
||||
minTimeout: minTimeout ?? 500, // .5s
|
||||
timeoutFactor: timeoutFactor ?? 2,
|
||||
maxRetries: maxRetries ?? 5,
|
||||
// What errors we should retry
|
||||
@@ -53,11 +57,13 @@ class RetryHandler {
|
||||
'ENETUNREACH',
|
||||
'EHOSTDOWN',
|
||||
'EHOSTUNREACH',
|
||||
'EPIPE'
|
||||
'EPIPE',
|
||||
'UND_ERR_SOCKET'
|
||||
]
|
||||
}
|
||||
|
||||
this.retryCount = 0
|
||||
this.retryCountCheckpoint = 0
|
||||
this.start = 0
|
||||
this.end = null
|
||||
this.etag = null
|
||||
@@ -103,25 +109,17 @@ class RetryHandler {
|
||||
const { method, retryOptions } = opts
|
||||
const {
|
||||
maxRetries,
|
||||
timeout,
|
||||
minTimeout,
|
||||
maxTimeout,
|
||||
timeoutFactor,
|
||||
statusCodes,
|
||||
errorCodes,
|
||||
methods
|
||||
} = retryOptions
|
||||
let { counter, currentTimeout } = state
|
||||
|
||||
currentTimeout =
|
||||
currentTimeout != null && currentTimeout > 0 ? currentTimeout : timeout
|
||||
const { counter } = state
|
||||
|
||||
// Any code that is not a Undici's originated and allowed to retry
|
||||
if (
|
||||
code &&
|
||||
code !== 'UND_ERR_REQ_RETRY' &&
|
||||
code !== 'UND_ERR_SOCKET' &&
|
||||
!errorCodes.includes(code)
|
||||
) {
|
||||
if (code && code !== 'UND_ERR_REQ_RETRY' && !errorCodes.includes(code)) {
|
||||
cb(err)
|
||||
return
|
||||
}
|
||||
@@ -148,10 +146,10 @@ class RetryHandler {
|
||||
return
|
||||
}
|
||||
|
||||
let retryAfterHeader = headers != null && headers['retry-after']
|
||||
let retryAfterHeader = headers?.['retry-after']
|
||||
if (retryAfterHeader) {
|
||||
retryAfterHeader = Number(retryAfterHeader)
|
||||
retryAfterHeader = isNaN(retryAfterHeader)
|
||||
retryAfterHeader = Number.isNaN(retryAfterHeader)
|
||||
? calculateRetryAfterHeader(retryAfterHeader)
|
||||
: retryAfterHeader * 1e3 // Retry-After is in seconds
|
||||
}
|
||||
@@ -159,9 +157,7 @@ class RetryHandler {
|
||||
const retryTimeout =
|
||||
retryAfterHeader > 0
|
||||
? Math.min(retryAfterHeader, maxTimeout)
|
||||
: Math.min(currentTimeout * timeoutFactor ** counter, maxTimeout)
|
||||
|
||||
state.currentTimeout = retryTimeout
|
||||
: Math.min(minTimeout * timeoutFactor ** (counter - 1), maxTimeout)
|
||||
|
||||
setTimeout(() => cb(null), retryTimeout)
|
||||
}
|
||||
@@ -172,21 +168,42 @@ class RetryHandler {
|
||||
this.retryCount += 1
|
||||
|
||||
if (statusCode >= 300) {
|
||||
this.abort(
|
||||
new RequestRetryError('Request failed', statusCode, {
|
||||
headers,
|
||||
count: this.retryCount
|
||||
})
|
||||
)
|
||||
return false
|
||||
if (this.retryOpts.statusCodes.includes(statusCode) === false) {
|
||||
return this.handler.onHeaders(
|
||||
statusCode,
|
||||
rawHeaders,
|
||||
resume,
|
||||
statusMessage
|
||||
)
|
||||
} else {
|
||||
this.abort(
|
||||
new RequestRetryError('Request failed', statusCode, {
|
||||
headers,
|
||||
data: {
|
||||
count: this.retryCount
|
||||
}
|
||||
})
|
||||
)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Checkpoint for resume from where we left it
|
||||
if (this.resume != null) {
|
||||
this.resume = null
|
||||
|
||||
if (statusCode !== 206) {
|
||||
return true
|
||||
// Only Partial Content 206 supposed to provide Content-Range,
|
||||
// any other status code that partially consumed the payload
|
||||
// should not be retry because it would result in downstream
|
||||
// wrongly concatanete multiple responses.
|
||||
if (statusCode !== 206 && (this.start > 0 || statusCode !== 200)) {
|
||||
this.abort(
|
||||
new RequestRetryError('server does not support the range header and the payload was partially consumed', statusCode, {
|
||||
headers,
|
||||
data: { count: this.retryCount }
|
||||
})
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
const contentRange = parseRangeHeader(headers['content-range'])
|
||||
@@ -195,7 +212,7 @@ class RetryHandler {
|
||||
this.abort(
|
||||
new RequestRetryError('Content-Range mismatch', statusCode, {
|
||||
headers,
|
||||
count: this.retryCount
|
||||
data: { count: this.retryCount }
|
||||
})
|
||||
)
|
||||
return false
|
||||
@@ -206,13 +223,13 @@ class RetryHandler {
|
||||
this.abort(
|
||||
new RequestRetryError('ETag mismatch', statusCode, {
|
||||
headers,
|
||||
count: this.retryCount
|
||||
data: { count: this.retryCount }
|
||||
})
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
const { start, size, end = size } = contentRange
|
||||
const { start, size, end = size - 1 } = contentRange
|
||||
|
||||
assert(this.start === start, 'content-range mismatch')
|
||||
assert(this.end == null || this.end === end, 'content-range mismatch')
|
||||
@@ -235,17 +252,12 @@ class RetryHandler {
|
||||
)
|
||||
}
|
||||
|
||||
const { start, size, end = size } = range
|
||||
|
||||
const { start, size, end = size - 1 } = range
|
||||
assert(
|
||||
start != null && Number.isFinite(start) && this.start !== start,
|
||||
start != null && Number.isFinite(start),
|
||||
'content-range mismatch'
|
||||
)
|
||||
assert(Number.isFinite(start))
|
||||
assert(
|
||||
end != null && Number.isFinite(end) && this.end !== end,
|
||||
'invalid content-length'
|
||||
)
|
||||
assert(end != null && Number.isFinite(end), 'invalid content-length')
|
||||
|
||||
this.start = start
|
||||
this.end = end
|
||||
@@ -254,7 +266,7 @@ class RetryHandler {
|
||||
// We make our best to checkpoint the body for further range headers
|
||||
if (this.end == null) {
|
||||
const contentLength = headers['content-length']
|
||||
this.end = contentLength != null ? Number(contentLength) : null
|
||||
this.end = contentLength != null ? Number(contentLength) - 1 : null
|
||||
}
|
||||
|
||||
assert(Number.isFinite(this.start))
|
||||
@@ -266,6 +278,13 @@ class RetryHandler {
|
||||
this.resume = resume
|
||||
this.etag = headers.etag != null ? headers.etag : null
|
||||
|
||||
// Weak etags are not useful for comparison nor cache
|
||||
// for instance not safe to assume if the response is byte-per-byte
|
||||
// equal
|
||||
if (this.etag != null && this.etag.startsWith('W/')) {
|
||||
this.etag = null
|
||||
}
|
||||
|
||||
return this.handler.onHeaders(
|
||||
statusCode,
|
||||
rawHeaders,
|
||||
@@ -276,7 +295,7 @@ class RetryHandler {
|
||||
|
||||
const err = new RequestRetryError('Request failed', statusCode, {
|
||||
headers,
|
||||
count: this.retryCount
|
||||
data: { count: this.retryCount }
|
||||
})
|
||||
|
||||
this.abort(err)
|
||||
@@ -300,10 +319,21 @@ class RetryHandler {
|
||||
return this.handler.onError(err)
|
||||
}
|
||||
|
||||
// We reconcile in case of a mix between network errors
|
||||
// and server error response
|
||||
if (this.retryCount - this.retryCountCheckpoint > 0) {
|
||||
// We count the difference between the last checkpoint and the current retry count
|
||||
this.retryCount =
|
||||
this.retryCountCheckpoint +
|
||||
(this.retryCount - this.retryCountCheckpoint)
|
||||
} else {
|
||||
this.retryCount += 1
|
||||
}
|
||||
|
||||
this.retryOpts.retry(
|
||||
err,
|
||||
{
|
||||
state: { counter: this.retryCount++, currentTimeout: this.retryAfter },
|
||||
state: { counter: this.retryCount },
|
||||
opts: { retryOptions: this.retryOpts, ...this.opts }
|
||||
},
|
||||
onRetry.bind(this)
|
||||
@@ -315,16 +345,24 @@ class RetryHandler {
|
||||
}
|
||||
|
||||
if (this.start !== 0) {
|
||||
const headers = { range: `bytes=${this.start}-${this.end ?? ''}` }
|
||||
|
||||
// Weak etag check - weak etags will make comparison algorithms never match
|
||||
if (this.etag != null) {
|
||||
headers['if-match'] = this.etag
|
||||
}
|
||||
|
||||
this.opts = {
|
||||
...this.opts,
|
||||
headers: {
|
||||
...this.opts.headers,
|
||||
range: `bytes=${this.start}-${this.end ?? ''}`
|
||||
...headers
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
this.retryCountCheckpoint = this.retryCount
|
||||
this.dispatch(this.opts, this)
|
||||
} catch (err) {
|
||||
this.handler.onError(err)
|
||||
Reference in New Issue
Block a user